@aamini/config 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/msw.d.mts +3 -0
- package/dist/msw.mjs +3 -0
- package/dist/playwright.shared.config.d.mts +8 -0
- package/dist/playwright.shared.config.mjs +47 -0
- package/dist/playwright.shared.config.mjs.map +1 -0
- package/dist/setup/styles.d.mts +1 -0
- package/dist/setup/styles.mjs +3 -0
- package/dist/test/browser.d.mts +15 -0
- package/dist/test/browser.mjs +26 -0
- package/dist/test/browser.mjs.map +1 -0
- package/dist/test/database.d.mts +22 -0
- package/dist/test/database.mjs +18 -0
- package/dist/test/database.mjs.map +1 -0
- package/dist/test/db.d.mts +21 -0
- package/dist/test/db.mjs +53 -0
- package/dist/test/db.mjs.map +1 -0
- package/dist/test/playwright.d.mts +7 -0
- package/dist/test/playwright.mjs +15 -0
- package/dist/test/playwright.mjs.map +1 -0
- package/dist/test/postgres.d.mts +14 -0
- package/dist/test/postgres.mjs +25 -0
- package/dist/test/postgres.mjs.map +1 -0
- package/dist/test/server.d.mts +15 -0
- package/dist/test/server.mjs +23 -0
- package/dist/test/server.mjs.map +1 -0
- package/dist/test/visual-page.d.mts +36 -0
- package/dist/test/visual-page.mjs +30 -0
- package/dist/test/visual-page.mjs.map +1 -0
- package/dist/vite.shared.d.mts +14 -0
- package/dist/vite.shared.mjs +232 -0
- package/dist/vite.shared.mjs.map +1 -0
- package/dist/vitest.shared.d.mts +12 -0
- package/dist/vitest.shared.mjs +122 -0
- package/dist/vitest.shared.mjs.map +1 -0
- package/msw.ts +5 -0
- package/package.json +99 -0
- package/playwright.shared.config.ts +71 -0
- package/setup/styles.ts +4 -0
- package/test/browser.ts +31 -0
- package/test/database.ts +38 -0
- package/test/db.ts +80 -0
- package/test/playwright.ts +17 -0
- package/test/postgres.ts +43 -0
- package/test/server.ts +31 -0
- package/test/visual-page.ts +57 -0
- package/tsconfig/tsconfig.astro.json +34 -0
- package/tsconfig/tsconfig.lib.json +19 -0
- package/tsconfig/tsconfig.react.json +16 -0
- package/tsconfig/tsconfig.strict.json +16 -0
- package/tsconfig/tsconfig.tanstack.json +17 -0
- package/virtual-modules.d.ts +6 -0
- package/vite.config.ts +53 -0
- package/vite.shared.ts +298 -0
- package/vitest.shared.ts +183 -0
package/dist/msw.d.mts
ADDED
package/dist/msw.mjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PlaywrightTestConfig } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
//#region playwright.shared.config.d.ts
|
|
4
|
+
/** See https://playwright.dev/docs/test-configuration. */
|
|
5
|
+
declare const baseConfig: (overrides?: PlaywrightTestConfig) => Promise<PlaywrightTestConfig<{}, {}>>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { baseConfig };
|
|
8
|
+
//# sourceMappingURL=playwright.shared.config.d.mts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineConfig, devices } from "@playwright/test";
|
|
2
|
+
//#region playwright.shared.config.ts
|
|
3
|
+
/** See https://playwright.dev/docs/test-configuration. */
|
|
4
|
+
const baseConfig = async (overrides) => {
|
|
5
|
+
return defineConfig({
|
|
6
|
+
testDir: "./e2e",
|
|
7
|
+
outputDir: ".playwright/test-results",
|
|
8
|
+
fullyParallel: true,
|
|
9
|
+
workers: process.env.CI ? 1 : "50%",
|
|
10
|
+
forbidOnly: !!process.env.CI,
|
|
11
|
+
retries: process.env.CI ? 3 : 0,
|
|
12
|
+
reporter: [["html", {
|
|
13
|
+
open: "never",
|
|
14
|
+
outputFolder: ".playwright/report"
|
|
15
|
+
}]],
|
|
16
|
+
use: {
|
|
17
|
+
colorScheme: "dark",
|
|
18
|
+
baseURL: process.env.BASE_URL,
|
|
19
|
+
trace: "retain-on-first-failure",
|
|
20
|
+
screenshot: "on",
|
|
21
|
+
video: "retain-on-failure"
|
|
22
|
+
},
|
|
23
|
+
timeout: 15e3,
|
|
24
|
+
expect: { timeout: 5e3 },
|
|
25
|
+
projects: [{
|
|
26
|
+
name: "chromium",
|
|
27
|
+
use: {
|
|
28
|
+
...devices["Desktop Chrome"],
|
|
29
|
+
launchOptions: { args: ["--disable-lcd-text"] }
|
|
30
|
+
}
|
|
31
|
+
}, {
|
|
32
|
+
name: "mobile",
|
|
33
|
+
use: {
|
|
34
|
+
...devices["Desktop Chrome"],
|
|
35
|
+
viewport: {
|
|
36
|
+
width: 320,
|
|
37
|
+
height: 800
|
|
38
|
+
},
|
|
39
|
+
launchOptions: { args: ["--disable-lcd-text"] }
|
|
40
|
+
}
|
|
41
|
+
}]
|
|
42
|
+
}, overrides ?? {});
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { baseConfig };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=playwright.shared.config.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.shared.config.mjs","names":[],"sources":["../playwright.shared.config.ts"],"sourcesContent":["import {\n\tdefineConfig,\n\tdevices,\n\ttype PlaywrightTestConfig,\n} from '@playwright/test'\n\n/** See https://playwright.dev/docs/test-configuration. */\nexport const baseConfig = async (overrides?: PlaywrightTestConfig) => {\n\treturn defineConfig(\n\t\t{\n\t\t\ttestDir: './e2e',\n\t\t\toutputDir: '.playwright/test-results',\n\t\t\t/* Run tests in files in parallel */\n\t\t\tfullyParallel: true,\n\t\t\t// Opt out of parallel tests on CI.\n\t\t\tworkers: process.env.CI ? 1 : '50%',\n\t\t\t/* Fail the build on CI if you accidentally left test.only in the source code. */\n\t\t\tforbidOnly: !!process.env.CI,\n\t\t\tretries: process.env.CI ? 3 : 0,\n\t\t\t/* Reporter to use. See https://playwright.dev/docs/test-reporters */\n\t\t\treporter: [\n\t\t\t\t['html', { open: 'never', outputFolder: '.playwright/report' }],\n\t\t\t],\n\n\t\t\t/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n\t\t\tuse: {\n\t\t\t\tcolorScheme: 'dark',\n\n\t\t\t\t/* Base URL to use in actions like `await page.goto('/')`. */\n\t\t\t\tbaseURL: process.env.BASE_URL,\n\n\t\t\t\t/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */\n\t\t\t\ttrace: 'retain-on-first-failure',\n\t\t\t\tscreenshot: 'on',\n\t\t\t\tvideo: 'retain-on-failure',\n\t\t\t},\n\n\t\t\ttimeout: 15_000,\n\t\t\texpect: {\n\t\t\t\ttimeout: 5_000,\n\t\t\t},\n\n\t\t\t/* Configure projects for major browsers */\n\t\t\tprojects: [\n\t\t\t\t{\n\t\t\t\t\tname: 'chromium',\n\t\t\t\t\tuse: {\n\t\t\t\t\t\t...devices['Desktop Chrome'],\n\t\t\t\t\t\tlaunchOptions: {\n\t\t\t\t\t\t\targs: ['--disable-lcd-text'],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: 'mobile',\n\t\t\t\t\tuse: {\n\t\t\t\t\t\t...devices['Desktop Chrome'],\n\t\t\t\t\t\tviewport: {\n\t\t\t\t\t\t\twidth: 320,\n\t\t\t\t\t\t\theight: 800,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tlaunchOptions: {\n\t\t\t\t\t\t\targs: ['--disable-lcd-text'],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\toverrides ?? {},\n\t)\n}\n"],"mappings":";;;AAOA,MAAa,aAAa,OAAO,cAAqC;CACrE,OAAO,aACN;EACC,SAAS;EACT,WAAW;EAEX,eAAe;EAEf,SAAS,QAAQ,IAAI,KAAK,IAAI;EAE9B,YAAY,CAAC,CAAC,QAAQ,IAAI;EAC1B,SAAS,QAAQ,IAAI,KAAK,IAAI;EAE9B,UAAU,CACT,CAAC,QAAQ;GAAE,MAAM;GAAS,cAAc;GAAsB,CAAC,CAC/D;EAGD,KAAK;GACJ,aAAa;GAGb,SAAS,QAAQ,IAAI;GAGrB,OAAO;GACP,YAAY;GACZ,OAAO;GACP;EAED,SAAS;EACT,QAAQ,EACP,SAAS,KACT;EAGD,UAAU,CACT;GACC,MAAM;GACN,KAAK;IACJ,GAAG,QAAQ;IACX,eAAe,EACd,MAAM,CAAC,qBAAqB,EAC5B;IACD;GACD,EACD;GACC,MAAM;GACN,KAAK;IACJ,GAAG,QAAQ;IACX,UAAU;KACT,OAAO;KACP,QAAQ;KACR;IACD,eAAe,EACd,MAAM,CAAC,qBAAqB,EAC5B;IACD;GACD,CACD;EACD,EACD,aAAa,EAAE,CACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SetupWorker } from "msw/browser";
|
|
2
|
+
import * as _$vitest from "vitest";
|
|
3
|
+
|
|
4
|
+
//#region test/browser.d.ts
|
|
5
|
+
interface MswBrowserFixture {
|
|
6
|
+
worker: SetupWorker;
|
|
7
|
+
_cleanup: void;
|
|
8
|
+
}
|
|
9
|
+
declare const test: _$vitest.TestAPI<{
|
|
10
|
+
worker: SetupWorker;
|
|
11
|
+
_cleanup: void;
|
|
12
|
+
}>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { MswBrowserFixture, test };
|
|
15
|
+
//# sourceMappingURL=browser.d.mts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { setupWorker } from "msw/browser";
|
|
2
|
+
import handlers from "@test/handlers";
|
|
3
|
+
import { test as test$1 } from "vitest";
|
|
4
|
+
//#region test/browser.ts
|
|
5
|
+
const client = setupWorker(...handlers);
|
|
6
|
+
const test = test$1.extend({
|
|
7
|
+
worker: [async ({}, use) => {
|
|
8
|
+
await client.start({
|
|
9
|
+
quiet: true,
|
|
10
|
+
onUnhandledRequest: "bypass"
|
|
11
|
+
});
|
|
12
|
+
await use(client);
|
|
13
|
+
client.stop();
|
|
14
|
+
}, {
|
|
15
|
+
auto: true,
|
|
16
|
+
scope: "worker"
|
|
17
|
+
}],
|
|
18
|
+
_cleanup: [async ({ worker }, use) => {
|
|
19
|
+
await use();
|
|
20
|
+
worker.resetHandlers();
|
|
21
|
+
}, { auto: true }]
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
export { test };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=browser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.mjs","names":["baseTest"],"sources":["../../test/browser.ts"],"sourcesContent":["// oxlint-disable typescript-eslint/triple-slash-reference\n/// <reference path=\"../virtual-modules.d.ts\" />\n// oxlint-disable no-empty-pattern\nimport handlers from '@test/handlers'\nimport { setupWorker, type SetupWorker } from 'msw/browser'\nimport { test as baseTest } from 'vitest'\n\nexport interface MswBrowserFixture {\n\tworker: SetupWorker\n\t_cleanup: void\n}\n\nconst client = setupWorker(...handlers)\n\nexport const test = baseTest.extend<MswBrowserFixture>({\n\tworker: [\n\t\tasync ({}, use) => {\n\t\t\tawait client.start({ quiet: true, onUnhandledRequest: 'bypass' })\n\t\t\tawait use(client)\n\t\t\tclient.stop()\n\t\t},\n\t\t{ auto: true, scope: 'worker' },\n\t],\n\t_cleanup: [\n\t\tasync ({ worker }, use) => {\n\t\t\tawait use()\n\t\t\tworker.resetHandlers()\n\t\t},\n\t\t{ auto: true },\n\t],\n})\n"],"mappings":";;;;AAYA,MAAM,SAAS,YAAY,GAAG,SAAS;AAEvC,MAAa,OAAOA,OAAS,OAA0B;CACtD,QAAQ,CACP,OAAO,IAAI,QAAQ;EAClB,MAAM,OAAO,MAAM;GAAE,OAAO;GAAM,oBAAoB;GAAU,CAAC;EACjE,MAAM,IAAI,OAAO;EACjB,OAAO,MAAM;IAEd;EAAE,MAAM;EAAM,OAAO;EAAU,CAC/B;CACD,UAAU,CACT,OAAO,EAAE,UAAU,QAAQ;EAC1B,MAAM,KAAK;EACX,OAAO,eAAe;IAEvB,EAAE,MAAM,MAAM,CACd;CACD,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
|
+
import { Pool } from "pg";
|
|
3
|
+
|
|
4
|
+
//#region test/database.d.ts
|
|
5
|
+
type Database = NodePgDatabase & {
|
|
6
|
+
$client: Pool;
|
|
7
|
+
};
|
|
8
|
+
interface SeedDatabaseOptions {
|
|
9
|
+
schemaPath: string;
|
|
10
|
+
migrationsFolder: string;
|
|
11
|
+
seedFunction?: (db: Database) => Promise<void>;
|
|
12
|
+
extensions?: string[];
|
|
13
|
+
}
|
|
14
|
+
declare function seedDatabase(db: Database, {
|
|
15
|
+
schemaPath,
|
|
16
|
+
migrationsFolder,
|
|
17
|
+
seedFunction,
|
|
18
|
+
extensions
|
|
19
|
+
}: SeedDatabaseOptions): Promise<void>;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { Database, SeedDatabaseOptions, seedDatabase };
|
|
22
|
+
//# sourceMappingURL=database.d.mts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region test/database.ts
|
|
2
|
+
const DEFAULT_EXTENSIONS = ["pg_trgm"];
|
|
3
|
+
async function seedDatabase(db, { schemaPath, migrationsFolder, seedFunction, extensions = DEFAULT_EXTENSIONS }) {
|
|
4
|
+
const { migrate } = await import("drizzle-orm/node-postgres/migrator");
|
|
5
|
+
const { reset } = await import("drizzle-seed");
|
|
6
|
+
const schema = await import(
|
|
7
|
+
/* @vite-ignore */
|
|
8
|
+
schemaPath
|
|
9
|
+
);
|
|
10
|
+
for (const ext of extensions) await db.execute(`CREATE EXTENSION IF NOT EXISTS "${ext}"`);
|
|
11
|
+
await migrate(db, { migrationsFolder });
|
|
12
|
+
await reset(db, schema);
|
|
13
|
+
if (seedFunction) await seedFunction(db);
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { seedDatabase };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=database.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.mjs","names":[],"sources":["../../test/database.ts"],"sourcesContent":["import type { NodePgDatabase } from 'drizzle-orm/node-postgres'\nimport type { Pool } from 'pg'\n\n// Convention: schema at src/db/tables.ts, migrations at src/db/migrations/\nconst DEFAULT_EXTENSIONS = ['pg_trgm']\n\nexport type Database = NodePgDatabase & {\n\t$client: Pool\n}\n\nexport interface SeedDatabaseOptions {\n\tschemaPath: string\n\tmigrationsFolder: string\n\tseedFunction?: (db: Database) => Promise<void>\n\textensions?: string[]\n}\n\nexport async function seedDatabase(\n\tdb: Database,\n\t{\n\t\tschemaPath,\n\t\tmigrationsFolder,\n\t\tseedFunction,\n\t\textensions = DEFAULT_EXTENSIONS,\n\t}: SeedDatabaseOptions,\n) {\n\tconst { migrate } = await import('drizzle-orm/node-postgres/migrator')\n\tconst { reset } = await import('drizzle-seed')\n\n\tconst schema = await import(/* @vite-ignore */ schemaPath)\n\n\tfor (const ext of extensions) {\n\t\tawait db.execute(`CREATE EXTENSION IF NOT EXISTS \"${ext}\"`)\n\t}\n\tawait migrate(db, { migrationsFolder })\n\tawait reset(db, schema)\n\tif (seedFunction) await seedFunction(db)\n}\n"],"mappings":";AAIA,MAAM,qBAAqB,CAAC,UAAU;AAatC,eAAsB,aACrB,IACA,EACC,YACA,kBACA,cACA,aAAa,sBAEb;CACD,MAAM,EAAE,YAAY,MAAM,OAAO;CACjC,MAAM,EAAE,UAAU,MAAM,OAAO;CAE/B,MAAM,SAAS,MAAM;;EAA0B;;CAE/C,KAAK,MAAM,OAAO,YACjB,MAAM,GAAG,QAAQ,mCAAmC,IAAI,GAAG;CAE5D,MAAM,QAAQ,IAAI,EAAE,kBAAkB,CAAC;CACvC,MAAM,MAAM,IAAI,OAAO;CACvB,IAAI,cAAc,MAAM,aAAa,GAAG"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Database } from "./database.mjs";
|
|
2
|
+
import * as _$vitest from "vitest";
|
|
3
|
+
import { SetupServer } from "msw/node";
|
|
4
|
+
|
|
5
|
+
//#region test/db.d.ts
|
|
6
|
+
interface DbFixture {
|
|
7
|
+
server: SetupServer;
|
|
8
|
+
_cleanup: void;
|
|
9
|
+
db: Database;
|
|
10
|
+
seedFunction: (db: Database) => Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
declare const test: _$vitest.TestAPI<{
|
|
13
|
+
server: SetupServer;
|
|
14
|
+
_cleanup: void;
|
|
15
|
+
db: Database;
|
|
16
|
+
seedFunction: (db: Database) => Promise<void>;
|
|
17
|
+
}>;
|
|
18
|
+
declare function initDb(seedFunction: (db: Database) => Promise<void>): void;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { DbFixture, initDb, test };
|
|
21
|
+
//# sourceMappingURL=db.d.mts.map
|
package/dist/test/db.mjs
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { seedDatabase } from "./database.mjs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import handlers from "@test/handlers";
|
|
4
|
+
import { test as test$1 } from "vitest";
|
|
5
|
+
import { setupServer } from "msw/node";
|
|
6
|
+
//#region test/db.ts
|
|
7
|
+
const DEFAULT_SCHEMA_PATH = "src/db/tables.ts";
|
|
8
|
+
const DEFAULT_MIGRATIONS_PATH = "src/db/migrations";
|
|
9
|
+
const DEFAULT_IMAGE = "postgres:17";
|
|
10
|
+
const mswServer = setupServer(...handlers);
|
|
11
|
+
const test = test$1.extend({
|
|
12
|
+
server: [async ({}, use) => {
|
|
13
|
+
mswServer.listen({ onUnhandledRequest: "bypass" });
|
|
14
|
+
await use(mswServer);
|
|
15
|
+
mswServer.close();
|
|
16
|
+
}, {
|
|
17
|
+
auto: true,
|
|
18
|
+
scope: "worker"
|
|
19
|
+
}],
|
|
20
|
+
_cleanup: [async ({ server }, use) => {
|
|
21
|
+
await use();
|
|
22
|
+
server.resetHandlers();
|
|
23
|
+
}, { auto: true }],
|
|
24
|
+
seedFunction: [async ({}, use) => use(async () => {}), { scope: "file" }],
|
|
25
|
+
db: [async ({ seedFunction }, use) => {
|
|
26
|
+
const { PostgreSqlContainer } = await import("@testcontainers/postgresql");
|
|
27
|
+
const { drizzle } = await import("drizzle-orm/node-postgres");
|
|
28
|
+
const { Wait } = await import("testcontainers");
|
|
29
|
+
const { Pool } = await import("pg");
|
|
30
|
+
const schemaPath = resolve(process.cwd(), DEFAULT_SCHEMA_PATH);
|
|
31
|
+
const migrationsFolder = resolve(process.cwd(), DEFAULT_MIGRATIONS_PATH);
|
|
32
|
+
const container = await new PostgreSqlContainer(DEFAULT_IMAGE).withWaitStrategy(Wait.forHealthCheck()).start();
|
|
33
|
+
const db = drizzle({ client: new Pool({ connectionString: container.getConnectionUri() }) });
|
|
34
|
+
try {
|
|
35
|
+
await seedDatabase(db, {
|
|
36
|
+
schemaPath,
|
|
37
|
+
migrationsFolder,
|
|
38
|
+
seedFunction
|
|
39
|
+
});
|
|
40
|
+
await use(db);
|
|
41
|
+
} finally {
|
|
42
|
+
await db.$client.end();
|
|
43
|
+
await container.stop();
|
|
44
|
+
}
|
|
45
|
+
}, { scope: "file" }]
|
|
46
|
+
});
|
|
47
|
+
function initDb(seedFunction) {
|
|
48
|
+
test.override({ seedFunction: [async ({}, use) => use(seedFunction), { scope: "file" }] });
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { initDb, test };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=db.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.mjs","names":["baseTest"],"sources":["../../test/db.ts"],"sourcesContent":["// oxlint-disable typescript-eslint/triple-slash-reference\n/// <reference path=\"../virtual-modules.d.ts\" />\n// oxlint-disable no-empty-pattern\nimport handlers from '@test/handlers'\nimport { setupServer, SetupServer } from 'msw/node'\nimport { resolve } from 'node:path'\nimport { test as baseTest } from 'vitest'\nimport type { Database } from './database'\nimport { seedDatabase } from './database'\n\nconst DEFAULT_SCHEMA_PATH = 'src/db/tables.ts'\nconst DEFAULT_MIGRATIONS_PATH = 'src/db/migrations'\nconst DEFAULT_IMAGE = 'postgres:17'\n\nexport interface DbFixture {\n\tserver: SetupServer\n\t_cleanup: void\n\tdb: Database\n\tseedFunction: (db: Database) => Promise<void>\n}\n\nconst mswServer = setupServer(...handlers)\n\nexport const test = baseTest.extend<DbFixture>({\n\t// MSW Server fixture\n\tserver: [\n\t\tasync ({}, use) => {\n\t\t\tmswServer.listen({ onUnhandledRequest: 'bypass' })\n\t\t\tawait use(mswServer)\n\t\t\tmswServer.close()\n\t\t},\n\t\t{ auto: true, scope: 'worker' },\n\t],\n\t_cleanup: [\n\t\tasync ({ server }, use) => {\n\t\t\tawait use()\n\t\t\tserver.resetHandlers()\n\t\t},\n\t\t{ auto: true },\n\t],\n\n\t// Database fixtures\n\tseedFunction: [async ({}, use) => use(async () => {}), { scope: 'file' }],\n\tdb: [\n\t\tasync ({ seedFunction }: Pick<DbFixture, 'seedFunction'>, use) => {\n\t\t\tconst { PostgreSqlContainer } = await import('@testcontainers/postgresql')\n\t\t\tconst { drizzle } = await import('drizzle-orm/node-postgres')\n\t\t\tconst { Wait } = await import('testcontainers')\n\t\t\tconst { Pool } = await import('pg')\n\n\t\t\t// Auto-discover schema\n\t\t\tconst schemaPath = resolve(process.cwd(), DEFAULT_SCHEMA_PATH)\n\n\t\t\t// Resolve migrations folder relative to cwd (app root)\n\t\t\tconst migrationsFolder = resolve(process.cwd(), DEFAULT_MIGRATIONS_PATH)\n\n\t\t\tconst container = await new PostgreSqlContainer(DEFAULT_IMAGE)\n\t\t\t\t.withWaitStrategy(Wait.forHealthCheck())\n\t\t\t\t.start()\n\t\t\tconst db = drizzle({\n\t\t\t\tclient: new Pool({ connectionString: container.getConnectionUri() }),\n\t\t\t}) as Database\n\n\t\t\ttry {\n\t\t\t\tawait seedDatabase(db, { schemaPath, migrationsFolder, seedFunction })\n\t\t\t\tawait use(db)\n\t\t\t} finally {\n\t\t\t\tawait db.$client.end()\n\t\t\t\tawait container.stop()\n\t\t\t}\n\t\t},\n\t\t{ scope: 'file' },\n\t],\n})\n\nexport function initDb(seedFunction: (db: Database) => Promise<void>) {\n\ttest.override({\n\t\tseedFunction: [async ({}, use) => use(seedFunction), { scope: 'file' }],\n\t})\n}\n"],"mappings":";;;;;;AAUA,MAAM,sBAAsB;AAC5B,MAAM,0BAA0B;AAChC,MAAM,gBAAgB;AAStB,MAAM,YAAY,YAAY,GAAG,SAAS;AAE1C,MAAa,OAAOA,OAAS,OAAkB;CAE9C,QAAQ,CACP,OAAO,IAAI,QAAQ;EAClB,UAAU,OAAO,EAAE,oBAAoB,UAAU,CAAC;EAClD,MAAM,IAAI,UAAU;EACpB,UAAU,OAAO;IAElB;EAAE,MAAM;EAAM,OAAO;EAAU,CAC/B;CACD,UAAU,CACT,OAAO,EAAE,UAAU,QAAQ;EAC1B,MAAM,KAAK;EACX,OAAO,eAAe;IAEvB,EAAE,MAAM,MAAM,CACd;CAGD,cAAc,CAAC,OAAO,IAAI,QAAQ,IAAI,YAAY,GAAG,EAAE,EAAE,OAAO,QAAQ,CAAC;CACzE,IAAI,CACH,OAAO,EAAE,gBAAiD,QAAQ;EACjE,MAAM,EAAE,wBAAwB,MAAM,OAAO;EAC7C,MAAM,EAAE,YAAY,MAAM,OAAO;EACjC,MAAM,EAAE,SAAS,MAAM,OAAO;EAC9B,MAAM,EAAE,SAAS,MAAM,OAAO;EAG9B,MAAM,aAAa,QAAQ,QAAQ,KAAK,EAAE,oBAAoB;EAG9D,MAAM,mBAAmB,QAAQ,QAAQ,KAAK,EAAE,wBAAwB;EAExE,MAAM,YAAY,MAAM,IAAI,oBAAoB,cAAc,CAC5D,iBAAiB,KAAK,gBAAgB,CAAC,CACvC,OAAO;EACT,MAAM,KAAK,QAAQ,EAClB,QAAQ,IAAI,KAAK,EAAE,kBAAkB,UAAU,kBAAkB,EAAE,CAAC,EACpE,CAAC;EAEF,IAAI;GACH,MAAM,aAAa,IAAI;IAAE;IAAY;IAAkB;IAAc,CAAC;GACtE,MAAM,IAAI,GAAG;YACJ;GACT,MAAM,GAAG,QAAQ,KAAK;GACtB,MAAM,UAAU,MAAM;;IAGxB,EAAE,OAAO,QAAQ,CACjB;CACD,CAAC;AAEF,SAAgB,OAAO,cAA+C;CACrE,KAAK,SAAS,EACb,cAAc,CAAC,OAAO,IAAI,QAAQ,IAAI,aAAa,EAAE,EAAE,OAAO,QAAQ,CAAC,EACvE,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
//#region test/playwright.ts
|
|
3
|
+
async function mockJsonRoute(page, url, fixtureUrl) {
|
|
4
|
+
await page.route(url, async (route) => {
|
|
5
|
+
const body = await readFile(fixtureUrl, "utf8");
|
|
6
|
+
await route.fulfill({
|
|
7
|
+
contentType: "application/json",
|
|
8
|
+
body
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { mockJsonRoute };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=playwright.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.mjs","names":[],"sources":["../../test/playwright.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises'\nimport type { Page } from '@playwright/test'\n\nexport async function mockJsonRoute(\n\tpage: Page,\n\turl: string | RegExp,\n\tfixtureUrl: URL,\n) {\n\tawait page.route(url, async (route) => {\n\t\tconst body = await readFile(fixtureUrl, 'utf8')\n\n\t\tawait route.fulfill({\n\t\t\tcontentType: 'application/json',\n\t\t\tbody,\n\t\t})\n\t})\n}\n"],"mappings":";;AAGA,eAAsB,cACrB,MACA,KACA,YACC;CACD,MAAM,KAAK,MAAM,KAAK,OAAO,UAAU;EACtC,MAAM,OAAO,MAAM,SAAS,YAAY,OAAO;EAE/C,MAAM,MAAM,QAAQ;GACnB,aAAa;GACb;GACA,CAAC;GACD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { StartedPostgreSqlContainer } from "@testcontainers/postgresql";
|
|
2
|
+
|
|
3
|
+
//#region test/postgres.d.ts
|
|
4
|
+
type State = {
|
|
5
|
+
container: StartedPostgreSqlContainer;
|
|
6
|
+
databaseUrl: string;
|
|
7
|
+
};
|
|
8
|
+
declare function ensurePostgresContainer(): Promise<State | {
|
|
9
|
+
databaseUrl: string;
|
|
10
|
+
}>;
|
|
11
|
+
declare function stopPostgresContainer(): Promise<void>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ensurePostgresContainer, stopPostgresContainer };
|
|
14
|
+
//# sourceMappingURL=postgres.d.mts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { PostgreSqlContainer } from "@testcontainers/postgresql";
|
|
2
|
+
import { Wait } from "testcontainers";
|
|
3
|
+
//#region test/postgres.ts
|
|
4
|
+
let state;
|
|
5
|
+
async function ensurePostgresContainer() {
|
|
6
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
7
|
+
if (databaseUrl) return { databaseUrl };
|
|
8
|
+
if (!state) {
|
|
9
|
+
const container = await new PostgreSqlContainer("postgres:17").withDatabase("imdbgraph").withUsername("imdbgraph").withPassword("imdbgraph").withWaitStrategy(Wait.forHealthCheck()).start();
|
|
10
|
+
state = {
|
|
11
|
+
container,
|
|
12
|
+
databaseUrl: container.getConnectionUri()
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return state;
|
|
16
|
+
}
|
|
17
|
+
async function stopPostgresContainer() {
|
|
18
|
+
if (!state) return;
|
|
19
|
+
await state.container.stop();
|
|
20
|
+
state = void 0;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { ensurePostgresContainer, stopPostgresContainer };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=postgres.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.mjs","names":[],"sources":["../../test/postgres.ts"],"sourcesContent":["import {\n\tPostgreSqlContainer,\n\ttype StartedPostgreSqlContainer,\n} from '@testcontainers/postgresql'\nimport { Wait } from 'testcontainers'\n\ntype State = {\n\tcontainer: StartedPostgreSqlContainer\n\tdatabaseUrl: string\n}\n\nlet state: State | undefined\n\nexport async function ensurePostgresContainer() {\n\tconst databaseUrl = process.env.DATABASE_URL\n\tif (databaseUrl) {\n\t\treturn {\n\t\t\tdatabaseUrl,\n\t\t}\n\t}\n\n\tif (!state) {\n\t\tconst container = await new PostgreSqlContainer('postgres:17')\n\t\t\t.withDatabase('imdbgraph')\n\t\t\t.withUsername('imdbgraph')\n\t\t\t.withPassword('imdbgraph')\n\t\t\t.withWaitStrategy(Wait.forHealthCheck())\n\t\t\t.start()\n\n\t\tstate = {\n\t\t\tcontainer,\n\t\t\tdatabaseUrl: container.getConnectionUri(),\n\t\t}\n\t}\n\n\treturn state\n}\n\nexport async function stopPostgresContainer() {\n\tif (!state) return\n\tawait state.container.stop()\n\tstate = undefined\n}\n"],"mappings":";;;AAWA,IAAI;AAEJ,eAAsB,0BAA0B;CAC/C,MAAM,cAAc,QAAQ,IAAI;CAChC,IAAI,aACH,OAAO,EACN,aACA;CAGF,IAAI,CAAC,OAAO;EACX,MAAM,YAAY,MAAM,IAAI,oBAAoB,cAAc,CAC5D,aAAa,YAAY,CACzB,aAAa,YAAY,CACzB,aAAa,YAAY,CACzB,iBAAiB,KAAK,gBAAgB,CAAC,CACvC,OAAO;EAET,QAAQ;GACP;GACA,aAAa,UAAU,kBAAkB;GACzC;;CAGF,OAAO;;AAGR,eAAsB,wBAAwB;CAC7C,IAAI,CAAC,OAAO;CACZ,MAAM,MAAM,UAAU,MAAM;CAC5B,QAAQ,KAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as _$vitest from "vitest";
|
|
2
|
+
import { SetupServer } from "msw/node";
|
|
3
|
+
|
|
4
|
+
//#region test/server.d.ts
|
|
5
|
+
interface MswServerFixture {
|
|
6
|
+
server: SetupServer;
|
|
7
|
+
_cleanup: void;
|
|
8
|
+
}
|
|
9
|
+
declare const test: _$vitest.TestAPI<{
|
|
10
|
+
server: SetupServer;
|
|
11
|
+
_cleanup: void;
|
|
12
|
+
}>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { MswServerFixture, test };
|
|
15
|
+
//# sourceMappingURL=server.d.mts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import handlers from "@test/handlers";
|
|
2
|
+
import { test as test$1 } from "vitest";
|
|
3
|
+
import { setupServer } from "msw/node";
|
|
4
|
+
//#region test/server.ts
|
|
5
|
+
const mswServer = setupServer(...handlers);
|
|
6
|
+
const test = test$1.extend({
|
|
7
|
+
server: [async ({}, use) => {
|
|
8
|
+
mswServer.listen({ onUnhandledRequest: "bypass" });
|
|
9
|
+
await use(mswServer);
|
|
10
|
+
mswServer.close();
|
|
11
|
+
}, {
|
|
12
|
+
auto: true,
|
|
13
|
+
scope: "worker"
|
|
14
|
+
}],
|
|
15
|
+
_cleanup: [async ({ server }, use) => {
|
|
16
|
+
await use();
|
|
17
|
+
server.resetHandlers();
|
|
18
|
+
}, { auto: true }]
|
|
19
|
+
});
|
|
20
|
+
//#endregion
|
|
21
|
+
export { test };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=server.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.mjs","names":["baseTest"],"sources":["../../test/server.ts"],"sourcesContent":["// oxlint-disable typescript-eslint/triple-slash-reference\n/// <reference path=\"../virtual-modules.d.ts\" />\n// oxlint-disable no-empty-pattern\nimport handlers from '@test/handlers'\nimport { SetupServer, setupServer } from 'msw/node'\nimport { test as baseTest } from 'vitest'\n\nexport interface MswServerFixture {\n\tserver: SetupServer\n\t_cleanup: void\n}\n\nconst mswServer = setupServer(...handlers)\n\nexport const test = baseTest.extend<MswServerFixture>({\n\tserver: [\n\t\tasync ({}, use) => {\n\t\t\tmswServer.listen({ onUnhandledRequest: 'bypass' })\n\t\t\tawait use(mswServer)\n\t\t\tmswServer.close()\n\t\t},\n\t\t{ auto: true, scope: 'worker' },\n\t],\n\t_cleanup: [\n\t\tasync ({ server }, use) => {\n\t\t\tawait use()\n\t\t\tserver.resetHandlers()\n\t\t},\n\t\t{ auto: true },\n\t],\n})\n"],"mappings":";;;;AAYA,MAAM,YAAY,YAAY,GAAG,SAAS;AAE1C,MAAa,OAAOA,OAAS,OAAyB;CACrD,QAAQ,CACP,OAAO,IAAI,QAAQ;EAClB,UAAU,OAAO,EAAE,oBAAoB,UAAU,CAAC;EAClD,MAAM,IAAI,UAAU;EACpB,UAAU,OAAO;IAElB;EAAE,MAAM;EAAM,OAAO;EAAU,CAC/B;CACD,UAAU,CACT,OAAO,EAAE,UAAU,QAAQ;EAC1B,MAAM,KAAK;EACX,OAAO,eAAe;IAEvB,EAAE,MAAM,MAAM,CACd;CACD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ComponentType, ReactNode } from "react";
|
|
2
|
+
import { Locator } from "vitest/browser";
|
|
3
|
+
import { RenderResult } from "vitest-browser-react";
|
|
4
|
+
|
|
5
|
+
//#region test/visual-page.d.ts
|
|
6
|
+
interface Viewport {
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
}
|
|
10
|
+
interface ExpectComponentScreenshotOptions {
|
|
11
|
+
component: ComponentType;
|
|
12
|
+
name: string;
|
|
13
|
+
waitFor: (screen: RenderResult) => Locator;
|
|
14
|
+
prepare?: ((screen: RenderResult) => void | Promise<void>) | undefined;
|
|
15
|
+
target?: ((screen: RenderResult) => Locator) | undefined;
|
|
16
|
+
setup?: (() => void | Promise<void>) | undefined;
|
|
17
|
+
wrapper?: ComponentType<{
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
}> | undefined;
|
|
20
|
+
viewport?: Viewport | undefined;
|
|
21
|
+
fullPage?: boolean | undefined;
|
|
22
|
+
}
|
|
23
|
+
declare function expectComponentScreenshot({
|
|
24
|
+
component,
|
|
25
|
+
name,
|
|
26
|
+
waitFor,
|
|
27
|
+
prepare,
|
|
28
|
+
target,
|
|
29
|
+
setup,
|
|
30
|
+
wrapper,
|
|
31
|
+
viewport,
|
|
32
|
+
fullPage
|
|
33
|
+
}: ExpectComponentScreenshotOptions): Promise<void>;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { expectComponentScreenshot };
|
|
36
|
+
//# sourceMappingURL=visual-page.d.mts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { expect } from "vitest";
|
|
2
|
+
import { createElement } from "react";
|
|
3
|
+
import { page } from "vitest/browser";
|
|
4
|
+
import { render } from "vitest-browser-react";
|
|
5
|
+
//#region test/visual-page.ts
|
|
6
|
+
async function expectComponentScreenshot({ component, name, waitFor, prepare, target = (screen) => screen.locator, setup, wrapper, viewport = {
|
|
7
|
+
width: 1280,
|
|
8
|
+
height: 720
|
|
9
|
+
}, fullPage = true }) {
|
|
10
|
+
localStorage.clear();
|
|
11
|
+
document.body.replaceChildren();
|
|
12
|
+
await setup?.();
|
|
13
|
+
await page.viewport(viewport.width, viewport.height);
|
|
14
|
+
const container = document.body.appendChild(document.createElement("div"));
|
|
15
|
+
container.style.width = "100vw";
|
|
16
|
+
container.style.minHeight = "100vh";
|
|
17
|
+
const screen = await render(createElement(component), wrapper ? {
|
|
18
|
+
container,
|
|
19
|
+
wrapper
|
|
20
|
+
} : { container });
|
|
21
|
+
await prepare?.(screen);
|
|
22
|
+
await expect.element(waitFor(screen)).toBeVisible();
|
|
23
|
+
await document.fonts.ready;
|
|
24
|
+
if (fullPage) await page.viewport(viewport.width, Math.ceil(container.scrollHeight));
|
|
25
|
+
await expect.element(target(screen)).toMatchScreenshot(`${name}.png`, { screenshotOptions: { scale: "css" } });
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { expectComponentScreenshot };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=visual-page.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-page.mjs","names":[],"sources":["../../test/visual-page.ts"],"sourcesContent":["import { createElement, type ComponentType, type ReactNode } from 'react'\nimport { expect } from 'vitest'\nimport { page, type Locator } from 'vitest/browser'\nimport { render, type RenderResult } from 'vitest-browser-react'\n\ninterface Viewport {\n\twidth: number\n\theight: number\n}\n\ninterface ExpectComponentScreenshotOptions {\n\tcomponent: ComponentType\n\tname: string\n\twaitFor: (screen: RenderResult) => Locator\n\tprepare?: ((screen: RenderResult) => void | Promise<void>) | undefined\n\ttarget?: ((screen: RenderResult) => Locator) | undefined\n\tsetup?: (() => void | Promise<void>) | undefined\n\twrapper?: ComponentType<{ children: ReactNode }> | undefined\n\tviewport?: Viewport | undefined\n\tfullPage?: boolean | undefined\n}\n\nexport async function expectComponentScreenshot({\n\tcomponent,\n\tname,\n\twaitFor,\n\tprepare,\n\ttarget = (screen) => screen.locator,\n\tsetup,\n\twrapper,\n\tviewport = { width: 1280, height: 720 },\n\tfullPage = true,\n}: ExpectComponentScreenshotOptions) {\n\tlocalStorage.clear()\n\tdocument.body.replaceChildren()\n\tawait setup?.()\n\tawait page.viewport(viewport.width, viewport.height)\n\n\tconst container = document.body.appendChild(document.createElement('div'))\n\tcontainer.style.width = '100vw'\n\tcontainer.style.minHeight = '100vh'\n\n\tconst screen = await render(\n\t\tcreateElement(component),\n\t\twrapper ? { container, wrapper } : { container },\n\t)\n\n\tawait prepare?.(screen)\n\tawait expect.element(waitFor(screen)).toBeVisible()\n\tawait document.fonts.ready\n\tif (fullPage) {\n\t\tawait page.viewport(viewport.width, Math.ceil(container.scrollHeight))\n\t}\n\tawait expect.element(target(screen)).toMatchScreenshot(`${name}.png`, {\n\t\tscreenshotOptions: { scale: 'css' },\n\t})\n}\n"],"mappings":";;;;;AAsBA,eAAsB,0BAA0B,EAC/C,WACA,MACA,SACA,SACA,UAAU,WAAW,OAAO,SAC5B,OACA,SACA,WAAW;CAAE,OAAO;CAAM,QAAQ;CAAK,EACvC,WAAW,QACyB;CACpC,aAAa,OAAO;CACpB,SAAS,KAAK,iBAAiB;CAC/B,MAAM,SAAS;CACf,MAAM,KAAK,SAAS,SAAS,OAAO,SAAS,OAAO;CAEpD,MAAM,YAAY,SAAS,KAAK,YAAY,SAAS,cAAc,MAAM,CAAC;CAC1E,UAAU,MAAM,QAAQ;CACxB,UAAU,MAAM,YAAY;CAE5B,MAAM,SAAS,MAAM,OACpB,cAAc,UAAU,EACxB,UAAU;EAAE;EAAW;EAAS,GAAG,EAAE,WAAW,CAChD;CAED,MAAM,UAAU,OAAO;CACvB,MAAM,OAAO,QAAQ,QAAQ,OAAO,CAAC,CAAC,aAAa;CACnD,MAAM,SAAS,MAAM;CACrB,IAAI,UACH,MAAM,KAAK,SAAS,SAAS,OAAO,KAAK,KAAK,UAAU,aAAa,CAAC;CAEvE,MAAM,OAAO,QAAQ,OAAO,OAAO,CAAC,CAAC,kBAAkB,GAAG,KAAK,OAAO,EACrE,mBAAmB,EAAE,OAAO,OAAO,EACnC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { TestProjectConfiguration, TestProjectInlineConfiguration } from "vitest/config";
|
|
2
|
+
import * as _$_voidzero_dev_vite_plus_core0 from "@voidzero-dev/vite-plus-core";
|
|
3
|
+
|
|
4
|
+
//#region vite.shared.d.ts
|
|
5
|
+
interface ProjectOverrides {
|
|
6
|
+
server?: TestProjectConfiguration;
|
|
7
|
+
browser?: TestProjectConfiguration;
|
|
8
|
+
}
|
|
9
|
+
declare function createAppConfig(root: string, projectOverrides?: ProjectOverrides, overrides?: TestProjectInlineConfiguration): Record<string, any>;
|
|
10
|
+
declare function baseConfigFor(configUrl: string | URL): Record<string, any>;
|
|
11
|
+
declare const workspaceConfig: _$_voidzero_dev_vite_plus_core0.UserConfig;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { baseConfigFor, createAppConfig, workspaceConfig };
|
|
14
|
+
//# sourceMappingURL=vite.shared.d.mts.map
|