ci: reorganize project scripts and E2E workflows by type

This commit is contained in:
neon
2026-04-12 17:12:36 -06:00
parent 67ec10f703
commit 19f81faf4d
14 changed files with 21 additions and 17 deletions
+3
View File
@@ -0,0 +1,3 @@
import { listE2ESuites } from "./suite-registry.mjs";
process.stdout.write(JSON.stringify(listE2ESuites().map(({ id, name, timeout }) => ({ id, name, timeout }))));
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT_DIR"
E2E_DATABASE_URL="${E2E_DATABASE_URL:-postgres://projectx:projectx@localhost:5432/projectx_e2e}"
echo "Preparing dedicated e2e database..."
DATABASE_URL="$E2E_DATABASE_URL" pnpm --filter server e2e:db:prepare
echo "Applying migrations to e2e database..."
DATABASE_URL="$E2E_DATABASE_URL" pnpm --filter server e2e:db:migrate
+159
View File
@@ -0,0 +1,159 @@
#!/usr/bin/env node
import { spawn } from "node:child_process";
import { mkdirSync } from "node:fs";
import { dirname, join } from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
import { getE2ESuite, listE2ESuites } from "./suite-registry.mjs";
const DEFAULT_E2E_DATABASE_URL = "postgres://projectx:projectx@localhost:5432/projectx_e2e";
const scriptDir = dirname(fileURLToPath(import.meta.url));
const rootDir = join(scriptDir, "..", "..");
const resultsDir = join(rootDir, "test-results", "server");
function isCiEnvironment() {
return (process.env.CI ?? "").toLowerCase() === "true";
}
function trimLeadingSeparator(args) {
if (args[0] === "--") {
return args.slice(1);
}
return args;
}
function printUsage() {
const usage = [
"Usage:",
" pnpm run e2e:run -- <suite-id> [vitest args...]",
"",
"Examples:",
" pnpm run e2e:run -- app-smoke",
" pnpm run e2e:run -- all",
" pnpm run e2e:run -- metadata-write",
' pnpm run e2e:run -- metadata-write --testNamePattern="concurrency hardening"',
" pnpm run e2e:run -- metadata-write --shard=1/2",
" pnpm run e2e:run -- scanner-scenarios --testNamePattern=book-per-folder-disc-folder-flattening",
" pnpm run e2e:run -- --list",
" pnpm run e2e:list",
];
console.log(usage.join("\n"));
}
function printSuiteList() {
const suites = listE2ESuites();
console.log("Available e2e suites:");
console.log("- all [composite] - Run all suites sequentially");
for (const suite of suites) {
const dbMode = suite.prepareDedicatedDatabase ? "dedicated-db" : "default-db";
console.log(`- ${suite.id} [${dbMode}] - ${suite.description}`);
}
}
async function runCommand(command, args, envOverrides = {}) {
await new Promise((resolve, reject) => {
const child = spawn(command, args, {
cwd: rootDir,
env: { ...process.env, ...envOverrides },
stdio: "inherit",
});
child.once("error", reject);
child.once("exit", (code, signal) => {
if (code === 0) {
resolve(undefined);
return;
}
const reason = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
reject(new Error(`Command failed (${reason}): ${command} ${args.join(" ")}`));
});
});
}
async function main() {
const cliArgs = trimLeadingSeparator(process.argv.slice(2));
if (cliArgs.length === 0 || cliArgs.includes("--help") || cliArgs.includes("-h")) {
printUsage();
process.exit(cliArgs.length === 0 ? 1 : 0);
}
if (cliArgs.includes("--list")) {
printSuiteList();
return;
}
const [suiteId, ...remainingArgs] = cliArgs;
const vitestArgs = trimLeadingSeparator(remainingArgs);
const suites = suiteId === "all" ? listE2ESuites() : [getE2ESuite(suiteId)];
if (suiteId !== "all" && !suites[0]) {
console.error(`Unknown e2e suite "${suiteId}".`);
printSuiteList();
process.exit(1);
}
if (suiteId === "all" && suites.length === 0) {
console.error("No e2e suites are configured.");
process.exit(1);
}
if (suiteId === "all" && process.env.JUNIT_OUTPUT) {
console.error("JUNIT_OUTPUT is only supported when running a single suite. Unset JUNIT_OUTPUT or run one suite id.");
process.exit(1);
}
mkdirSync(resultsDir, { recursive: true });
const e2eDatabaseUrl = process.env.E2E_DATABASE_URL ?? DEFAULT_E2E_DATABASE_URL;
if (!isCiEnvironment() && suites.some((suite) => suite.prepareDedicatedDatabase)) {
console.log("Starting local PostgreSQL (dev compose)...");
await runCommand("pnpm", ["run", "db:up"]);
}
for (const [index, suite] of suites.entries()) {
// E2E_DATABASE_URL is not in .env so pnpm's auto-loading cannot override it.
// vitest.config.e2e.ts reads this and injects it as DATABASE_URL into all
// test workers, ensuring NestJS never connects to the dev database.
const runEnvironment = { E2E_DATABASE_URL: e2eDatabaseUrl };
if (suite.prepareDedicatedDatabase) {
console.log(`Resetting and migrating dedicated e2e database for suite ${suite.id}...`);
await runCommand("pnpm", ["run", "e2e:db:prepare"], { E2E_DATABASE_URL: e2eDatabaseUrl });
}
const junitOutput = process.env.JUNIT_OUTPUT ?? suite.junitOutput;
if (suites.length > 1) {
console.log(`Running e2e suite [${index + 1}/${suites.length}]: ${suite.id}...`);
} else {
console.log(`Running e2e suite: ${suite.id}...`);
}
await runCommand(
"pnpm",
[
"--filter",
"server",
"exec",
"vitest",
"run",
"--config",
"vitest.config.e2e.ts",
suite.vitestTarget,
"--reporter=default",
"--reporter=junit",
`--outputFile.junit=${junitOutput}`,
...vitestArgs,
],
runEnvironment,
);
}
}
void main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT_DIR"
node ./scripts/e2e/run-suite.mjs "$@"
+85
View File
@@ -0,0 +1,85 @@
import { execFileSync } from "node:child_process";
import { matchesGlob } from "node:path";
import process from "node:process";
import { E2E_GLOBAL_CHANGE_PATHS, listE2ESuites } from "./suite-registry.mjs";
const ZERO_SHA_PATTERN = /^0+$/;
function git(args) {
return execFileSync("git", args, { encoding: "utf8" }).trim();
}
function hasCommit(sha) {
if (!sha || ZERO_SHA_PATTERN.test(sha)) {
return false;
}
try {
git(["cat-file", "-e", `${sha}^{commit}`]);
return true;
} catch {
return false;
}
}
function getChangedFiles(eventName, baseSha, headSha) {
if (eventName !== "pull_request") {
return null;
}
if (!hasCommit(baseSha) || !hasCommit(headSha)) {
return null;
}
const diffRange = `${baseSha}...${headSha}`;
const output = git(["diff", "--name-only", diffRange]);
return output === "" ? [] : output.split("\n").filter(Boolean);
}
function fileMatchesAnyPattern(filePath, patterns) {
return patterns.some((pattern) => matchesGlob(filePath, pattern));
}
function suiteMatchesChangedFiles(suite, changedFiles) {
if (!Array.isArray(suite.changedPaths) || suite.changedPaths.length === 0) {
return false;
}
return changedFiles.some((filePath) => fileMatchesAnyPattern(filePath, suite.changedPaths));
}
function toMatrixItems(suites) {
return suites.map(({ id, name, timeout }) => ({ id, name, timeout }));
}
function selectSuitesForEvent(eventName, changedFiles) {
const suites = listE2ESuites();
if (eventName === "workflow_dispatch" || eventName === "push" || eventName === "schedule") {
return toMatrixItems(suites);
}
if (eventName !== "pull_request") {
return toMatrixItems(suites);
}
const smokeSuites = suites.filter((suite) => suite.lane === "smoke");
if (!changedFiles || changedFiles.length === 0) {
return changedFiles === null ? toMatrixItems(smokeSuites) : [];
}
if (changedFiles.some((filePath) => fileMatchesAnyPattern(filePath, E2E_GLOBAL_CHANGE_PATHS))) {
return toMatrixItems(smokeSuites);
}
return toMatrixItems(smokeSuites.filter((suite) => suiteMatchesChangedFiles(suite, changedFiles)));
}
const eventName = process.env.GITHUB_EVENT_NAME ?? "";
const baseSha = process.env.GITHUB_BASE_SHA ?? "";
const headSha = process.env.GITHUB_HEAD_SHA ?? "";
const changedFiles = getChangedFiles(eventName, baseSha, headSha);
const selectedSuites = selectSuitesForEvent(eventName, changedFiles);
process.stdout.write(JSON.stringify(selectedSuites));
+438
View File
@@ -0,0 +1,438 @@
const TEST_RESULTS_DIR = "../test-results/server";
export const E2E_GLOBAL_CHANGE_PATHS = Object.freeze([
".github/workflows/ci.yml",
".github/workflows/e2e.yml",
".github/workflows/e2e-runner.yml",
"Dockerfile",
"docker-compose*.yml",
"docker/**",
]);
const SHARED_DB_AND_HELPER_PATHS = Object.freeze([
"server/src/db/schema/**",
"scripts/e2e/**",
"scripts/db/**",
]);
const SHARED_AUTH_TYPE_PATHS = Object.freeze(["packages/types/src/auth.ts", "packages/types/src/permissions.ts"]);
const SHARED_TYPES_PATHS = Object.freeze(["packages/types/src/**"]);
export const E2E_SUITES = Object.freeze({
"guard-mechanics": {
id: "guard-mechanics",
name: "Guard Mechanics",
timeout: 30,
lane: "smoke",
description: "Guard mechanics suite - verifies @Public/@RequirePermission wiring without a real DB",
vitestTarget: "test/guard-mechanics.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/guard-mechanics-e2e-junit.xml`,
prepareDedicatedDatabase: false,
useDedicatedDatabase: false,
changedPaths: [
"server/src/common/guards/**",
"server/src/common/decorators/**",
"server/src/common/types/**",
"server/src/modules/auth/**",
"server/src/modules/user/**",
"server/src/modules/role/**",
"server/src/**",
"server/test/guard-mechanics.e2e-spec.ts",
"server/test/e2e/**",
...SHARED_AUTH_TYPE_PATHS,
...SHARED_DB_AND_HELPER_PATHS,
],
},
"scanner-scenarios": {
id: "scanner-scenarios",
name: "Scanner Scenarios",
timeout: 45,
lane: "full",
description: "Scanner scenario matrix",
vitestTarget: "test/scanner-scenarios.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/scanner-scenarios-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/scanner/**",
"server/test/scanner-scenarios.e2e-spec.ts",
"server/test/e2e/scanner/**",
".github/workflows/e2e-runner.yml",
...SHARED_DB_AND_HELPER_PATHS,
],
},
"scanner-file-operations": {
id: "scanner-file-operations",
name: "Scanner File Operations",
timeout: 60,
lane: "full",
description: "Scanner file operation matrix",
vitestTarget: "test/scanner-file-operations.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/scanner-file-operations-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/scanner/**",
"server/test/scanner-file-operations.e2e-spec.ts",
"server/test/e2e/scanner/**",
".github/workflows/e2e-runner.yml",
...SHARED_DB_AND_HELPER_PATHS,
],
},
"auth-session-security": {
id: "auth-session-security",
name: "Auth Session Security",
timeout: 50,
lane: "full",
description: "Auth session security suite",
vitestTarget: "test/auth-session-security.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/auth-session-security-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/auth/**",
"server/src/modules/user/**",
"server/src/modules/role/**",
"server/src/common/guards/**",
"server/src/common/decorators/**",
"server/src/common/types/**",
"server/test/auth-session-security.e2e-spec.ts",
"server/test/e2e/app-harness.ts",
".github/workflows/e2e-runner.yml",
...SHARED_AUTH_TYPE_PATHS,
...SHARED_DB_AND_HELPER_PATHS,
],
},
"auth-recovery-oidc-logout": {
id: "auth-recovery-oidc-logout",
name: "Auth Recovery OIDC Logout",
timeout: 50,
lane: "full",
description: "Auth recovery and OIDC logout suite",
vitestTarget: "test/auth-recovery-oidc-logout.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/auth-recovery-oidc-logout-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/auth/**",
"server/src/modules/user/**",
"server/src/modules/email/**",
"server/src/modules/metadata/**",
"server/test/auth-recovery-oidc-logout.e2e-spec.ts",
"server/test/e2e/app-harness.ts",
".github/workflows/e2e-runner.yml",
"packages/types/src/auth.ts",
...SHARED_DB_AND_HELPER_PATHS,
],
},
"book-bucket-ingest-finalize": {
id: "book-bucket-ingest-finalize",
name: "Book Bucket Ingest Finalize",
timeout: 45,
lane: "full",
description: "Book Bucket ingest and finalize suite",
vitestTarget: "test/book-bucket-ingest-finalize.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/book-bucket-ingest-finalize-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/book-bucket/**",
"server/src/modules/upload/**",
"server/src/modules/metadata/**",
"server/test/book-bucket-ingest-finalize.e2e-spec.ts",
"server/test/e2e/book-bucket/**",
...SHARED_DB_AND_HELPER_PATHS,
],
},
"metadata-write": {
id: "metadata-write",
name: "Metadata Write",
timeout: 60,
lane: "full",
description: "Metadata write operations suite",
vitestTarget: "test/metadata-write.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/metadata-write-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/book/**",
"server/src/modules/library/**",
"server/src/modules/file-write/**",
"server/src/modules/metadata/**",
"server/src/modules/app-settings/**",
"server/test/metadata-write.e2e-spec.ts",
"server/test/e2e/metadata-write/**",
"packages/types/src/file-write.ts",
...SHARED_DB_AND_HELPER_PATHS,
],
},
"metadata-lock": {
id: "metadata-lock",
name: "Metadata Lock",
timeout: 60,
lane: "full",
description: "Metadata lock end-to-end suite",
vitestTarget: "test/metadata-lock.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/metadata-lock-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/book/**",
"server/src/modules/book-metadata-lock/**",
"server/src/modules/book-metadata-fetch/**",
"server/src/modules/metadata-fetch/**",
"server/src/modules/metadata-preferences/**",
"server/src/modules/cover/**",
"server/src/modules/scanner/**",
"server/src/modules/library/**",
"server/src/modules/auth/**",
"server/src/modules/app-settings/**",
"server/test/metadata-lock.e2e-spec.ts",
"server/test/e2e/metadata-lock/**",
".github/workflows/e2e-runner.yml",
...SHARED_TYPES_PATHS,
...SHARED_DB_AND_HELPER_PATHS,
],
},
"migration-booklore": {
id: "migration-booklore",
name: "Migration Booklore",
timeout: 70,
lane: "full",
description: "Booklore migration end-to-end suite",
vitestTarget: "test/migration-booklore.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/migration-booklore-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/migration/**",
"server/test/migration-booklore.e2e-spec.ts",
"server/test/e2e/migration-booklore/**",
".github/workflows/e2e-runner.yml",
...SHARED_DB_AND_HELPER_PATHS,
],
},
"authorization-matrix": {
id: "authorization-matrix",
name: "Authorization Matrix",
timeout: 60,
lane: "full",
description: "Authorization matrix suite",
vitestTarget: "test/authorization-matrix.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/authorization-matrix-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/**",
"server/test/authorization-matrix.e2e-spec.ts",
"server/test/e2e/authorization-matrix/**",
".github/workflows/e2e-runner.yml",
...SHARED_TYPES_PATHS,
...SHARED_DB_AND_HELPER_PATHS,
],
},
"book-api-contract": {
id: "book-api-contract",
name: "Book API Contract",
timeout: 60,
lane: "full",
description: "Book API contract suite",
vitestTarget: "test/book-api-contract.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/book-api-contract-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/book/**",
"server/src/modules/library/**",
"server/src/modules/collection/**",
"server/src/modules/auth/**",
"server/src/modules/user/**",
"server/test/book-api-contract.e2e-spec.ts",
"server/test/e2e/book-api-contract/**",
...SHARED_TYPES_PATHS,
...SHARED_DB_AND_HELPER_PATHS,
],
},
"app-settings-oidc-contract": {
id: "app-settings-oidc-contract",
name: "App Settings OIDC Contract",
timeout: 60,
lane: "full",
description: "App settings OIDC contract suite",
vitestTarget: "test/app-settings-oidc-contract.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/app-settings-oidc-contract-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/app-settings/**",
"server/src/modules/auth/**",
"server/src/modules/user/**",
"server/src/modules/metadata/**",
"server/test/app-settings-oidc-contract.e2e-spec.ts",
"server/test/e2e/app-settings/**",
...SHARED_TYPES_PATHS,
...SHARED_DB_AND_HELPER_PATHS,
],
},
"library-admin-workflows": {
id: "library-admin-workflows",
name: "Library Admin Workflows",
timeout: 60,
lane: "full",
description: "Library admin workflows suite",
vitestTarget: "test/library-admin-workflows.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/library-admin-workflows-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/library/**",
"server/src/modules/auth/**",
"server/src/modules/user/**",
"server/src/modules/role/**",
"server/test/library-admin-workflows.e2e-spec.ts",
"server/test/e2e/library/**",
...SHARED_TYPES_PATHS,
...SHARED_DB_AND_HELPER_PATHS,
],
},
"reader-format-delivery": {
id: "reader-format-delivery",
name: "Reader Format Delivery",
timeout: 60,
lane: "full",
description: "Reader format delivery suite",
vitestTarget: "test/reader-format-delivery.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/reader-format-delivery-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/book/**",
"server/src/modules/library/**",
"server/src/modules/reader-preferences/**",
"server/src/modules/annotation/**",
"server/src/modules/bookmark/**",
"server/test/reader-format-delivery.e2e-spec.ts",
"server/test/e2e/reader-format-delivery/**",
"packages/types/src/reader-settings.ts",
...SHARED_DB_AND_HELPER_PATHS,
],
},
"opds-auth-catalog": {
id: "opds-auth-catalog",
name: "OPDS Auth Catalog",
timeout: 45,
lane: "full",
description: "OPDS auth and catalog suite",
vitestTarget: "test/opds-auth-catalog.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/opds-auth-catalog-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/opds/**",
"server/src/modules/book/**",
"server/src/modules/library/**",
"server/src/modules/collection/**",
"server/src/modules/lens/**",
"server/src/modules/user/**",
"server/src/modules/auth/**",
"server/src/modules/app-settings/**",
"server/test/opds-auth-catalog.e2e-spec.ts",
"server/test/e2e/opds/**",
"server/test/e2e/**",
".github/workflows/e2e-runner.yml",
...SHARED_TYPES_PATHS,
...SHARED_DB_AND_HELPER_PATHS,
],
},
"email-lifecycle": {
id: "email-lifecycle",
name: "Email Lifecycle",
timeout: 45,
lane: "full",
description: "Email lifecycle suite",
vitestTarget: "test/email-lifecycle.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/email-lifecycle-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/email/**",
"server/src/modules/book/**",
"server/src/modules/library/**",
"server/src/modules/auth/**",
"server/src/modules/cover/**",
"server/src/modules/scanner/**",
"server/src/modules/seed/**",
"server/test/email-lifecycle.e2e-spec.ts",
"server/test/e2e/email/**",
"server/test/e2e/**",
"client/src/features/email/**",
"client/src/router/index.ts",
"client/src/features/settings/SettingsHeader.vue",
"client/src/views/HomeView.vue",
"client/src/views/CollectionView.vue",
"client/src/views/LensView.vue",
"client/src/features/book/components/BookCoverCard.vue",
"client/src/features/book/components/BookListRow.vue",
...SHARED_TYPES_PATHS,
...SHARED_DB_AND_HELPER_PATHS,
],
},
"reader-state-isolation": {
id: "reader-state-isolation",
name: "Reader State Isolation",
timeout: 45,
lane: "full",
description: "Reader state isolation suite",
vitestTarget: "test/reader-state-isolation.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/reader-state-isolation-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/bookmark/**",
"server/src/modules/annotation/**",
"server/src/modules/reader-preferences/**",
"server/src/modules/reading-session/**",
"server/src/modules/book/**",
"server/src/modules/library/**",
"server/src/modules/scanner/**",
"server/src/modules/user-book-status/**",
"server/src/scripts/db-prepare-e2e.ts",
"server/test/reader-state-isolation.e2e-spec.ts",
"server/test/e2e/reader-state-isolation/**",
"packages/types/src/reader-settings.ts",
...SHARED_DB_AND_HELPER_PATHS,
],
},
"users-admin-lifecycle": {
id: "users-admin-lifecycle",
name: "Users Admin Lifecycle",
timeout: 50,
lane: "full",
description: "Users admin lifecycle suite",
vitestTarget: "test/users-admin-lifecycle.e2e-spec.ts",
junitOutput: `${TEST_RESULTS_DIR}/users-admin-lifecycle-e2e-junit.xml`,
prepareDedicatedDatabase: true,
useDedicatedDatabase: true,
changedPaths: [
"server/src/modules/user/**",
"server/src/modules/auth/**",
"server/src/common/guards/**",
"server/src/common/decorators/**",
"server/src/common/types/**",
"server/test/users-admin-lifecycle.e2e-spec.ts",
"server/test/e2e/users-admin-lifecycle/**",
".github/workflows/e2e-runner.yml",
...SHARED_AUTH_TYPE_PATHS,
...SHARED_DB_AND_HELPER_PATHS,
],
},
});
export function listE2ESuites() {
return Object.values(E2E_SUITES);
}
export function getE2ESuite(suiteId) {
return E2E_SUITES[suiteId] ?? null;
}