@venturekit/testing 0.0.0-dev.20260815191300 → 0.0.0-dev.20260816162655

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.
Files changed (39) hide show
  1. package/README.md +105 -39
  2. package/bin/ensure-db.mjs +64 -0
  3. package/dist/ensure-database.d.ts +43 -0
  4. package/dist/ensure-database.d.ts.map +1 -0
  5. package/dist/ensure-database.js +76 -0
  6. package/dist/ensure-database.js.map +1 -0
  7. package/dist/index.d.ts +8 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +6 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/playwright/auth-setup.d.ts +58 -0
  12. package/dist/playwright/auth-setup.d.ts.map +1 -0
  13. package/dist/playwright/auth-setup.js +79 -0
  14. package/dist/playwright/auth-setup.js.map +1 -0
  15. package/dist/playwright/fixtures.d.ts +39 -0
  16. package/dist/playwright/fixtures.d.ts.map +1 -0
  17. package/dist/playwright/fixtures.js +46 -0
  18. package/dist/playwright/fixtures.js.map +1 -0
  19. package/dist/playwright/index.d.ts +24 -0
  20. package/dist/playwright/index.d.ts.map +1 -0
  21. package/dist/playwright/index.js +21 -0
  22. package/dist/playwright/index.js.map +1 -0
  23. package/dist/playwright/storage-state.d.ts +28 -0
  24. package/dist/playwright/storage-state.d.ts.map +1 -0
  25. package/dist/playwright/storage-state.js +40 -0
  26. package/dist/playwright/storage-state.js.map +1 -0
  27. package/dist/playwright/web-servers.d.ts +135 -0
  28. package/dist/playwright/web-servers.d.ts.map +1 -0
  29. package/dist/playwright/web-servers.js +122 -0
  30. package/dist/playwright/web-servers.js.map +1 -0
  31. package/dist/stack.d.ts +20 -0
  32. package/dist/stack.d.ts.map +1 -1
  33. package/dist/stack.js +12 -0
  34. package/dist/stack.js.map +1 -1
  35. package/dist/stage-db.d.ts +88 -0
  36. package/dist/stage-db.d.ts.map +1 -0
  37. package/dist/stage-db.js +96 -0
  38. package/dist/stage-db.js.map +1 -0
  39. package/package.json +24 -5
package/README.md CHANGED
@@ -27,69 +27,114 @@ vitest. The scenarios (selectors, flows, assertions) stay in your project.
27
27
  ```bash
28
28
  pnpm add -D @venturekit/testing
29
29
  # Optional peers (only if you use the matching helpers):
30
+ # pg -> ensureStageDatabase (creates the stage DB)
31
+ # @playwright/test -> the /playwright subpath
30
32
  # @venturekit/data -> truncate* DB helpers
31
33
  # @aws-sdk/client-cognito-identity-provider -> signInWithPassword / loginAs
32
34
  ```
33
35
 
34
- ## Playwright: `globalSetup` pattern
35
-
36
- Let Playwright's `webServer` own the `vk dev` process and use the harness
37
- for migration, auth seeding, and readiness:
36
+ ## Playwright: `vkWebServers`
37
+
38
+ The `@venturekit/testing/playwright` subpath builds the `webServer` block
39
+ for you. It exists because the ordering is unforgiving and every mistake
40
+ fails somewhere far from the cause:
41
+
42
+ - **The database has to be created first.** Neither `vk migrate` nor
43
+ `vk dev` creates it, and `globalSetup` is too late — Playwright starts
44
+ `webServer` entries as config *plugins*, which run before `globalSetup`.
45
+ So creation is chained into the command, ahead of the migration.
46
+ - **`vk migrate` and `vk dev` must agree on the database.** `vk dev`
47
+ derives `<database>_<stage>` from `vk.config.ts` and ignores an inherited
48
+ `DATABASE_URL`; `vk migrate` honours it. Pick your own name and you
49
+ migrate one database while the API serves another empty one — the suite
50
+ then fails with `relation "…" does not exist`.
51
+ - **Crons must be off.** They share the single-process dev server with the
52
+ requests under test, write to the rows the specs assert on, and can spend
53
+ real provider quota.
38
54
 
39
55
  ```ts
40
56
  // playwright.config.ts
41
57
  import { defineConfig } from '@playwright/test';
58
+ import { vkWebServers, storageStatePath } from '@venturekit/testing/playwright';
42
59
 
43
60
  export default defineConfig({
44
- testDir: './e2e',
45
- globalSetup: './e2e/global-setup.ts',
46
- use: { baseURL: 'http://localhost:3005' }, // your UI
47
- webServer: [
48
- {
49
- command: 'vk dev --stage test --port 4001 --no-watch',
50
- url: 'http://localhost:4001/_dev/health',
51
- reuseExistingServer: !process.env.CI,
52
- timeout: 120_000,
53
- },
61
+ testDir: './tests',
62
+ globalSetup: './setup/global-setup.ts',
63
+ projects: [
64
+ { name: 'setup', testMatch: /.*\.setup\.ts/ },
54
65
  {
55
- command: 'pnpm --filter @your/admin dev',
56
- url: 'http://localhost:3005',
57
- reuseExistingServer: !process.env.CI,
66
+ name: 'api',
67
+ dependencies: ['setup'],
68
+ use: { baseURL: 'http://127.0.0.1:4100', storageState: storageStatePath('admin') },
58
69
  },
59
70
  ],
71
+ webServer: vkWebServers({
72
+ stage: 'test',
73
+ // `infrastructure.databases[].name` from vk.config.ts. The stage suffix
74
+ // is added for you, so this run uses `acme_app_test`.
75
+ database: 'acme_app',
76
+ api: { filter: '@acme/api', port: 4100 },
77
+ web: {
78
+ name: 'Admin',
79
+ command: 'pnpm --filter @acme/admin exec next dev -p 3100',
80
+ url: 'http://127.0.0.1:3100',
81
+ env: { NEXT_PUBLIC_API_BASE: 'http://127.0.0.1:4100' },
82
+ },
83
+ }),
60
84
  });
61
85
  ```
62
86
 
87
+ That single call creates `acme_app_test`, migrates and seeds it, boots
88
+ `vk dev --no-watch --no-crons`, gates on `/_dev/health`, names both servers
89
+ so a timeout says which one hung, and pipes their output.
90
+
91
+ ### Roles once, not per spec
92
+
93
+ `establishRoles` provisions each cognito-local user, logs it in, and writes
94
+ the cookie jar. Specs adopt a role by pointing at the file:
95
+
63
96
  ```ts
64
- // e2e/global-setup.ts
65
- import { createTestUser, truncateAllTables, waitForReady } from '@venturekit/testing';
97
+ // setup/roles.setup.ts
98
+ import { test as setup } from '@playwright/test';
99
+ import { establishRoles } from '@venturekit/testing/playwright';
100
+
101
+ setup('establish roles', async () => {
102
+ await establishRoles({
103
+ baseUrl: 'http://127.0.0.1:4100',
104
+ roles: {
105
+ admin: { email: 'admin@example.com', password: 'Passw0rd!', attributes: { tenantId: 'dev' } },
106
+ viewer: { email: 'viewer@example.com', password: 'Passw0rd!', attributes: { tenantId: 'dev' } },
107
+ },
108
+ });
109
+ });
110
+ ```
66
111
 
67
- const API = 'http://localhost:4001';
112
+ ```ts
113
+ // setup/global-setup.ts
114
+ import { wipeAuthDir } from '@venturekit/testing/playwright';
68
115
 
116
+ // A jar from a previous run points at users the fresh database no longer
117
+ // has; the resulting 401s look like an auth bug.
69
118
  export default async function globalSetup() {
70
- await waitForReady({ baseUrl: API });
71
- await truncateAllTables(); // needs DATABASE_URL / DB_* in env
72
- await createTestUser({
73
- baseUrl: API,
74
- email: 'admin@example.com',
75
- password: 'Passw0rd!',
76
- attributes: { tenantId: 'global' },
77
- });
119
+ await wipeAuthDir();
78
120
  }
79
121
  ```
80
122
 
81
- Your specs then log in through the real UI (the most faithful E2E):
123
+ For an authz matrix, `createVkTest` adds an `asRole()` fixture that opens
124
+ (and disposes) a context per role inside one spec:
82
125
 
83
126
  ```ts
84
- // e2e/login.spec.ts
85
- import { test, expect } from '@playwright/test';
86
-
87
- test('logs in and lands on the dashboard', async ({ page }) => {
88
- await page.goto('/login');
89
- await page.getByLabel('Email').fill('admin@example.com');
90
- await page.getByLabel('Password').fill('Passw0rd!');
91
- await page.getByRole('button', { name: 'Sign in' }).click();
92
- await expect(page).toHaveURL(/\/(inbox|dashboard)/);
127
+ // tests/api/fixtures.ts
128
+ import { createVkTest } from '@venturekit/testing/playwright';
129
+ export const test = createVkTest();
130
+
131
+ // tests/api/authz.spec.ts
132
+ import { expect } from '@playwright/test';
133
+ import { test } from './fixtures.js';
134
+
135
+ test('viewers cannot publish', async ({ asRole }) => {
136
+ const viewer = await asRole('viewer');
137
+ expect((await viewer.post('/content/1/publish')).status()).toBe(403);
93
138
  });
94
139
  ```
95
140
 
@@ -136,11 +181,32 @@ test('GET /tenant returns the current tenant', async () => {
136
181
  | `signInWithPassword(opts)` / `loginAs(opts)` | Mint real JWTs for a user (needs the Cognito SDK peer). |
137
182
  | `truncateAllTables(opts)` / `truncateTables` / `listTables` | Reset the DB between specs (needs `@venturekit/data`). |
138
183
  | `buildTruncateSql` / `filterTruncatableTables` / `quoteIdent` | Pure SQL builders (reusable / testable). |
184
+ | `ensureStageDatabase(opts)` | Create `<database>_<stage>` if absent (needs `pg`). Idempotent and race-safe. |
185
+ | `stageDatabaseName` / `stageDatabaseUrl` / `adminDatabaseUrl` | Mirror the CLI's database derivation so migrate and `vk dev` agree. |
186
+
187
+ From `@venturekit/testing/playwright` (needs `@playwright/test`):
188
+
189
+ | Export | Purpose |
190
+ | --- | --- |
191
+ | `vkWebServers(opts)` | Build the `webServer` block: create DB → migrate → `vk dev --no-crons`. |
192
+ | `establishRoles(opts)` | Provision + log in every role, writing per-role `storageState`. |
193
+ | `wipeAuthDir(dir?)` | Clear stale storage state from `globalSetup`. |
194
+ | `storageStatePath(role, dir?)` | Where a role's jar lives (default `.auth/<role>.json`). |
195
+ | `createVkTest(opts?)` | `test` object with an `asRole()` fixture for authz specs. |
196
+ | `ensureDbScriptPath()` | Absolute path to the bundled creation script (also exposed as the `vk-ensure-db` bin). |
139
197
 
140
198
  ## Notes
141
199
 
142
200
  - `startTestStack` defaults to stage `test`, which targets a separate local
143
- database (`<dbname>_test`) so your tests never clobber `vk dev` data.
201
+ database (`<dbname>_test`) so your tests never clobber `vk dev` data. That
202
+ name is derived by the CLI from `<databases[].name>_<stage>` and an
203
+ inherited `DATABASE_URL` does **not** override it — point `vk migrate` and
204
+ your own DB helpers at the same name, or the API will serve an unmigrated
205
+ database.
206
+ - Pass `--no-crons` whenever a test suite owns the stack. Scheduled tasks
207
+ otherwise fire against the database under assertion, and a cron that calls
208
+ an LLM provider spends real quota on every tick. Invoke them explicitly
209
+ with `POST /_dev/invoke/cron/{name}` when a spec needs one to run.
144
210
  - `vk dev` does **not** auto-migrate — `startTestStack` runs `vk migrate`
145
211
  for you (pass `migrate: false` to skip, `seed: true` to also seed).
146
212
  - DB helpers read the same `DATABASE_URL` / `DB_*` env the app uses.
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Create the stage database, then exit.
4
+ *
5
+ * Chained ahead of `vk migrate` inside a Playwright `webServer` command,
6
+ * because that is the only hook guaranteed to run before the migration:
7
+ * `webServer` entries start as config *plugins*, and plugin setup runs
8
+ * before `globalSetup`. Creating the database in `globalSetup` therefore
9
+ * always loses the race and a cold run dies with
10
+ * `database "…" does not exist`.
11
+ *
12
+ * Plain `.mjs` on purpose — it runs under bare `node`, with no TS loader.
13
+ * Configuration arrives through the environment (set for you by
14
+ * `vkWebServers()` in @venturekit/testing/playwright):
15
+ *
16
+ * VK_TEST_DB_NAME the database to create
17
+ * VK_TEST_ADMIN_DATABASE_URL maintenance connection that issues the DDL
18
+ *
19
+ * `--name` / `--admin-url` argv flags override both, for one-off use.
20
+ */
21
+
22
+ import { ensureStageDatabase } from '../dist/ensure-database.js';
23
+
24
+ const arg = (flag) => {
25
+ const i = process.argv.indexOf(flag);
26
+ return i === -1 ? undefined : process.argv[i + 1];
27
+ };
28
+
29
+ const name = arg('--name') ?? process.env.VK_TEST_DB_NAME;
30
+ const adminUrl = arg('--admin-url') ?? process.env.VK_TEST_ADMIN_DATABASE_URL;
31
+
32
+ if (!name) {
33
+ console.error(
34
+ '[vk-testing] VK_TEST_DB_NAME (or --name) is required. It is normally set by the\n' +
35
+ ' webServer env block that vkWebServers() generates.',
36
+ );
37
+ process.exit(1);
38
+ }
39
+
40
+ function parse(url) {
41
+ try {
42
+ const u = new URL(url);
43
+ return {
44
+ host: u.hostname || undefined,
45
+ port: u.port ? Number(u.port) : undefined,
46
+ user: decodeURIComponent(u.username) || undefined,
47
+ password: decodeURIComponent(u.password) || undefined,
48
+ };
49
+ } catch {
50
+ console.error(`[vk-testing] VK_TEST_ADMIN_DATABASE_URL is not a valid URL: ${url}`);
51
+ process.exit(1);
52
+ }
53
+ }
54
+
55
+ // The admin URL is optional: ensureStageDatabase falls back to the local
56
+ // Postgres defaults `vk dev` uses.
57
+ const connection = adminUrl ? parse(adminUrl) : {};
58
+
59
+ try {
60
+ await ensureStageDatabase({ name, ...connection });
61
+ } catch (err) {
62
+ console.error(String(err instanceof Error ? err.message : err));
63
+ process.exit(1);
64
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Create the stage database before migrations run.
3
+ *
4
+ * Neither `vk migrate` nor `vk dev` creates the database it connects to, so
5
+ * a cold machine or a fresh CI runner dies on the first migration with
6
+ * `database "…" does not exist`. This closes that gap.
7
+ *
8
+ * IO-bound by nature (it needs a live Postgres), so the pure naming and
9
+ * validation logic it builds on lives in ./stage-db.ts and is unit-tested
10
+ * there. See the coverage exclusions in the root vitest.config.ts.
11
+ */
12
+ import { type PgConnection, type StageDatabaseTarget } from './stage-db.js';
13
+ export interface EnsureStageDatabaseOptions extends Partial<StageDatabaseTarget>, PgConnection {
14
+ /**
15
+ * Explicit database name, bypassing derivation. Prefer `database` +
16
+ * `stage` so the name always tracks what the CLI will actually serve.
17
+ */
18
+ name?: string;
19
+ /** Where to write progress lines. Pass `null` to stay silent. */
20
+ logger?: ((message: string) => void) | null;
21
+ }
22
+ export interface EnsureStageDatabaseResult {
23
+ /** The database name that now exists. */
24
+ name: string;
25
+ /** Connection string for it. */
26
+ url: string;
27
+ /** False when it was already present. */
28
+ created: boolean;
29
+ }
30
+ /**
31
+ * Create the stage database if it does not exist yet.
32
+ *
33
+ * Idempotent and safe to run concurrently: a `42P04` (duplicate_database)
34
+ * raised by a racing run is the desired end state, not a failure.
35
+ *
36
+ * Run this BEFORE `vk migrate`. Under Playwright that means chaining it into
37
+ * the `webServer` command rather than doing it in `globalSetup` —
38
+ * `webServer` entries start as config plugins, and plugin setup runs first,
39
+ * so `globalSetup` always loses the race to the migration.
40
+ * `vkWebServers()` from `@venturekit/testing/playwright` wires that for you.
41
+ */
42
+ export declare function ensureStageDatabase(options: EnsureStageDatabaseOptions): Promise<EnsureStageDatabaseResult>;
43
+ //# sourceMappingURL=ensure-database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure-database.d.ts","sourceRoot":"","sources":["../src/ensure-database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAKL,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACzB,MAAM,eAAe,CAAC;AAevB,MAAM,WAAW,0BAA2B,SAAQ,OAAO,CAAC,mBAAmB,CAAC,EAAE,YAAY;IAC5F;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,yBAAyB;IACxC,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,yBAAyB,CAAC,CA6CpC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Create the stage database before migrations run.
3
+ *
4
+ * Neither `vk migrate` nor `vk dev` creates the database it connects to, so
5
+ * a cold machine or a fresh CI runner dies on the first migration with
6
+ * `database "…" does not exist`. This closes that gap.
7
+ *
8
+ * IO-bound by nature (it needs a live Postgres), so the pure naming and
9
+ * validation logic it builds on lives in ./stage-db.ts and is unit-tested
10
+ * there. See the coverage exclusions in the root vitest.config.ts.
11
+ */
12
+ import { adminDatabaseUrl, pgConnectionString, redactUrl, resolveStageDatabaseName, } from './stage-db.js';
13
+ async function loadPg() {
14
+ try {
15
+ return (await import('pg'));
16
+ }
17
+ catch {
18
+ throw new Error("@venturekit/testing: ensureStageDatabase requires the optional peer 'pg'. " +
19
+ 'Install it in your test package: `pnpm add -D pg`.');
20
+ }
21
+ }
22
+ /**
23
+ * Create the stage database if it does not exist yet.
24
+ *
25
+ * Idempotent and safe to run concurrently: a `42P04` (duplicate_database)
26
+ * raised by a racing run is the desired end state, not a failure.
27
+ *
28
+ * Run this BEFORE `vk migrate`. Under Playwright that means chaining it into
29
+ * the `webServer` command rather than doing it in `globalSetup` —
30
+ * `webServer` entries start as config plugins, and plugin setup runs first,
31
+ * so `globalSetup` always loses the race to the migration.
32
+ * `vkWebServers()` from `@venturekit/testing/playwright` wires that for you.
33
+ */
34
+ export async function ensureStageDatabase(options) {
35
+ const name = resolveStageDatabaseName(options);
36
+ const log = options.logger === null
37
+ ? () => { }
38
+ : (options.logger ?? ((m) => console.log(`[vk-testing] ${m}`)));
39
+ // Connect to the maintenance database: CREATE DATABASE can run neither
40
+ // inside the target database nor inside a transaction.
41
+ const adminUrl = adminDatabaseUrl(options);
42
+ const url = pgConnectionString(options, name);
43
+ const { Client } = await loadPg();
44
+ const client = new Client({ connectionString: adminUrl });
45
+ try {
46
+ await client.connect();
47
+ }
48
+ catch (err) {
49
+ throw new Error(`@venturekit/testing: cannot reach Postgres at ${redactUrl(adminUrl)} — is it running? ` +
50
+ 'Start the local stack (`vk dev`) once, or `docker compose up -d postgres`. ' +
51
+ `Cause: ${String(err)}`);
52
+ }
53
+ try {
54
+ const { rowCount } = await client.query('SELECT 1 FROM pg_database WHERE datname = $1', [name]);
55
+ if (rowCount) {
56
+ log(`stage database ready: ${name}`);
57
+ return { name, url, created: false };
58
+ }
59
+ await client.query(`CREATE DATABASE "${name}"`);
60
+ log(`stage database created: ${name}`);
61
+ return { name, url, created: true };
62
+ }
63
+ catch (err) {
64
+ // 42P04 = duplicate_database: a concurrent run won the race between the
65
+ // SELECT and the CREATE. That is exactly the state we wanted.
66
+ if (err?.code === '42P04') {
67
+ log(`stage database ready: ${name}`);
68
+ return { name, url, created: false };
69
+ }
70
+ throw new Error(`@venturekit/testing: failed to create database ${name}: ${String(err)}`);
71
+ }
72
+ finally {
73
+ await client.end().catch(() => { });
74
+ }
75
+ }
76
+ //# sourceMappingURL=ensure-database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure-database.js","sourceRoot":"","sources":["../src/ensure-database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,SAAS,EACT,wBAAwB,GAGzB,MAAM,eAAe,CAAC;AAIvB,KAAK,UAAU,MAAM;IACnB,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAwB,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,4EAA4E;YAC1E,oDAAoD,CACvD,CAAC;IACJ,CAAC;AACH,CAAC;AAqBD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAAmC;IAEnC,MAAM,IAAI,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAC/C,MAAM,GAAG,GACP,OAAO,CAAC,MAAM,KAAK,IAAI;QACrB,CAAC,CAAC,GAAG,EAAE,GAAE,CAAC;QACV,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAE5E,uEAAuE;IACvE,uDAAuD;IACvD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAE9C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC,CAAC;IAE1D,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,iDAAiD,SAAS,CAAC,QAAQ,CAAC,oBAAoB;YACtF,6EAA6E;YAC7E,UAAU,MAAM,CAAC,GAAG,CAAC,EAAE,CAC1B,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,8CAA8C,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QAChG,IAAI,QAAQ,EAAE,CAAC;YACb,GAAG,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;YACrC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACvC,CAAC;QACD,MAAM,MAAM,CAAC,KAAK,CAAC,oBAAoB,IAAI,GAAG,CAAC,CAAC;QAChD,GAAG,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAC;QACvC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,wEAAwE;QACxE,8DAA8D;QAC9D,IAAK,GAAyB,EAAE,IAAI,KAAK,OAAO,EAAE,CAAC;YACjD,GAAG,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;YACrC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACvC,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,kDAAkD,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5F,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrC,CAAC;AACH,CAAC"}
package/dist/index.d.ts CHANGED
@@ -3,11 +3,15 @@
3
3
  *
4
4
  * Integration & end-to-end test harness for VentureKit apps.
5
5
  *
6
+ * - Create the stage database (`ensureStageDatabase`) — the CLI does not.
6
7
  * - Launch a full local stack (`startTestStack`) — migrate + `vk dev`.
7
8
  * - Wait for readiness (`waitForReady`) against `/_dev/health`.
8
9
  * - Seed cognito-local users (`createTestUser`) and mint JWTs (`loginAs`).
9
10
  * - Reset the database between specs (`truncateAllTables`).
10
11
  * - Drive the API with a typed client (`createApiClient`).
12
+ *
13
+ * Playwright-specific pieces (`webServer` wiring, role storage state,
14
+ * fixtures) live in the `@venturekit/testing/playwright` subpath.
11
15
  */
12
16
  export { ApiError } from './errors.js';
13
17
  export type { ApiErrorInit } from './errors.js';
@@ -21,6 +25,10 @@ export { signInWithPassword, loginAs, DEFAULT_COGNITO_ENDPOINT, DEFAULT_COGNITO_
21
25
  export type { TokenSet, SignInInput, LoginAsInput } from './auth-tokens.js';
22
26
  export { startTestStack } from './stack.js';
23
27
  export type { StartTestStackOptions, TestStack } from './stack.js';
28
+ export { stageDatabaseName, stageDatabaseUrl, adminDatabaseUrl, pgConnectionString, resolveStageDatabaseName, isSafeDatabaseName, redactUrl, LOCAL_PG, } from './stage-db.js';
29
+ export type { StageDatabaseTarget, PgConnection } from './stage-db.js';
30
+ export { ensureStageDatabase } from './ensure-database.js';
31
+ export type { EnsureStageDatabaseOptions, EnsureStageDatabaseResult, } from './ensure-database.js';
24
32
  export { truncateAllTables, truncateTables, listTables, } from './db.js';
25
33
  export type { TruncateOptions } from './db.js';
26
34
  export { buildTruncateSql, filterTruncatableTables, quoteIdent, VK_TRACKING_TABLES, } from './sql.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5D,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EACL,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,OAAO,EACP,cAAc,GACf,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,kBAAkB,EAClB,OAAO,EACP,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE5E,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEnE,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,UAAU,EACV,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5D,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EACL,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,OAAO,EACP,cAAc,GACf,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,kBAAkB,EAClB,OAAO,EACP,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE5E,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEnE,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,SAAS,EACT,QAAQ,GACT,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,YAAY,EACV,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,UAAU,EACV,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -3,11 +3,15 @@
3
3
  *
4
4
  * Integration & end-to-end test harness for VentureKit apps.
5
5
  *
6
+ * - Create the stage database (`ensureStageDatabase`) — the CLI does not.
6
7
  * - Launch a full local stack (`startTestStack`) — migrate + `vk dev`.
7
8
  * - Wait for readiness (`waitForReady`) against `/_dev/health`.
8
9
  * - Seed cognito-local users (`createTestUser`) and mint JWTs (`loginAs`).
9
10
  * - Reset the database between specs (`truncateAllTables`).
10
11
  * - Drive the API with a typed client (`createApiClient`).
12
+ *
13
+ * Playwright-specific pieces (`webServer` wiring, role storage state,
14
+ * fixtures) live in the `@venturekit/testing/playwright` subpath.
11
15
  */
12
16
  export { ApiError } from './errors.js';
13
17
  export { createApiClient, buildUrl } from './client.js';
@@ -15,6 +19,8 @@ export { waitForReady, vkDevServerReady } from './ready.js';
15
19
  export { createTestUser, deleteTestUser, setTestUserAttributes, getDevPools, } from './cognito.js';
16
20
  export { signInWithPassword, loginAs, DEFAULT_COGNITO_ENDPOINT, DEFAULT_COGNITO_REGION, } from './auth-tokens.js';
17
21
  export { startTestStack } from './stack.js';
22
+ export { stageDatabaseName, stageDatabaseUrl, adminDatabaseUrl, pgConnectionString, resolveStageDatabaseName, isSafeDatabaseName, redactUrl, LOCAL_PG, } from './stage-db.js';
23
+ export { ensureStageDatabase } from './ensure-database.js';
18
24
  export { truncateAllTables, truncateTables, listTables, } from './db.js';
19
25
  export { buildTruncateSql, filterTruncatableTables, quoteIdent, VK_TRACKING_TABLES, } from './sql.js';
20
26
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQxD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG5D,OAAO,EACL,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,WAAW,GACZ,MAAM,cAAc,CAAC;AAQtB,OAAO,EACL,kBAAkB,EAClB,OAAO,EACP,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,UAAU,GACX,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,UAAU,EACV,kBAAkB,GACnB,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQxD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG5D,OAAO,EACL,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,WAAW,GACZ,MAAM,cAAc,CAAC;AAQtB,OAAO,EACL,kBAAkB,EAClB,OAAO,EACP,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,SAAS,EACT,QAAQ,GACT,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAM3D,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,UAAU,GACX,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,UAAU,EACV,kBAAkB,GACnB,MAAM,UAAU,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Log every role in once, up front, and persist its cookie jar.
3
+ *
4
+ * Run from a Playwright `setup` project that the other projects depend on.
5
+ * Specs then adopt a role by pointing `storageState` at a file — no
6
+ * per-spec login, no token plumbing, and the authz matrix costs one line.
7
+ *
8
+ * Assumes cookie-based sessions, which is what `@venturekit/auth`'s
9
+ * `/auth/login` route issues: the response `Set-Cookie` lands in the
10
+ * request context's jar, and `storageState()` writes it out.
11
+ *
12
+ * IO-bound, so the pure path math lives in ./storage-state.ts.
13
+ */
14
+ export interface VkRoleSpec {
15
+ email: string;
16
+ password: string;
17
+ /**
18
+ * Cognito attributes. Unprefixed custom names get `custom:` added by the
19
+ * dev server, e.g. `{ tenantId: 'dev' }` → `custom:tenantId`.
20
+ */
21
+ attributes?: Record<string, string>;
22
+ }
23
+ export interface EstablishRolesOptions {
24
+ /** Role key → credentials. The key becomes the storage-state filename. */
25
+ roles: Record<string, VkRoleSpec>;
26
+ /** API base URL — serves both the cognito-local admin API and the login route. */
27
+ baseUrl: string;
28
+ /** Auth intent id, when the project declares more than one. */
29
+ intent?: string;
30
+ /** Login route. Defaults to the `@venturekit/auth` route. */
31
+ loginPath?: string;
32
+ /** Directory for the storage-state files. Defaults to `.auth`. */
33
+ authDir?: string;
34
+ /**
35
+ * Create each user in cognito-local first. On by default and idempotent —
36
+ * an existing user just has its password reset, so runs stay deterministic.
37
+ */
38
+ provision?: boolean;
39
+ /** Build the login body. Defaults to `{ email, password }`. */
40
+ loginBody?: (role: VkRoleSpec) => Record<string, unknown>;
41
+ /** Where to write progress lines. Pass `null` to stay silent. */
42
+ logger?: ((message: string) => void) | null;
43
+ }
44
+ /**
45
+ * Delete the storage-state directory.
46
+ *
47
+ * Call from `globalSetup`: a jar left over from a previous run points at
48
+ * users that no longer exist in the freshly created database, and the
49
+ * resulting 401s look like an auth bug rather than stale state.
50
+ */
51
+ export declare function wipeAuthDir(dir?: string): Promise<void>;
52
+ /**
53
+ * Provision each role, log it in, and write its `storageState`.
54
+ *
55
+ * Returns role key → file path, ready to drop into a project's `use`.
56
+ */
57
+ export declare function establishRoles(options: EstablishRolesOptions): Promise<Record<string, string>>;
58
+ //# sourceMappingURL=auth-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-setup.d.ts","sourceRoot":"","sources":["../../src/playwright/auth-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1D,iEAAiE;IACjE,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7C;AAYD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,GAAG,GAAE,MAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/E;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAyDjC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Log every role in once, up front, and persist its cookie jar.
3
+ *
4
+ * Run from a Playwright `setup` project that the other projects depend on.
5
+ * Specs then adopt a role by pointing `storageState` at a file — no
6
+ * per-spec login, no token plumbing, and the authz matrix costs one line.
7
+ *
8
+ * Assumes cookie-based sessions, which is what `@venturekit/auth`'s
9
+ * `/auth/login` route issues: the response `Set-Cookie` lands in the
10
+ * request context's jar, and `storageState()` writes it out.
11
+ *
12
+ * IO-bound, so the pure path math lives in ./storage-state.ts.
13
+ */
14
+ import { mkdir, rm } from 'node:fs/promises';
15
+ import { dirname } from 'node:path';
16
+ import { createTestUser } from '../cognito.js';
17
+ import { DEFAULT_AUTH_DIR, storageStatePath } from './storage-state.js';
18
+ async function loadPlaywright() {
19
+ try {
20
+ return await import('@playwright/test');
21
+ }
22
+ catch {
23
+ throw new Error("@venturekit/testing: this helper requires the optional peer '@playwright/test'.");
24
+ }
25
+ }
26
+ /**
27
+ * Delete the storage-state directory.
28
+ *
29
+ * Call from `globalSetup`: a jar left over from a previous run points at
30
+ * users that no longer exist in the freshly created database, and the
31
+ * resulting 401s look like an auth bug rather than stale state.
32
+ */
33
+ export async function wipeAuthDir(dir = DEFAULT_AUTH_DIR) {
34
+ await rm(dir, { recursive: true, force: true });
35
+ }
36
+ /**
37
+ * Provision each role, log it in, and write its `storageState`.
38
+ *
39
+ * Returns role key → file path, ready to drop into a project's `use`.
40
+ */
41
+ export async function establishRoles(options) {
42
+ const { roles, baseUrl, intent, loginPath = '/auth/login', authDir = DEFAULT_AUTH_DIR, provision = true, loginBody = (role) => ({ email: role.email, password: role.password }), } = options;
43
+ const log = options.logger === null
44
+ ? () => { }
45
+ : (options.logger ?? ((m) => console.log(`[vk-testing] ${m}`)));
46
+ const { request } = await loadPlaywright();
47
+ const written = {};
48
+ for (const [key, role] of Object.entries(roles)) {
49
+ const path = storageStatePath(key, authDir);
50
+ if (provision) {
51
+ await createTestUser({ baseUrl, intent, ...role });
52
+ }
53
+ const context = await request.newContext({ baseURL: baseUrl });
54
+ try {
55
+ const response = await context.post(loginPath, { data: loginBody(role) });
56
+ if (!response.ok()) {
57
+ const body = await response.text().catch(() => '');
58
+ throw new Error(`@venturekit/testing: login failed for role "${key}" (${role.email}) — ` +
59
+ `POST ${loginPath} returned ${response.status()}. ${body.slice(0, 400)}`);
60
+ }
61
+ // A 200 with no cookie writes a jar that authenticates nothing, and
62
+ // every later spec then fails with a 401 far from the cause.
63
+ const state = await context.storageState();
64
+ if (state.cookies.length === 0) {
65
+ throw new Error(`@venturekit/testing: login for role "${key}" succeeded but set no cookies. ` +
66
+ `Is ${loginPath} the right route, and does it issue a session cookie?`);
67
+ }
68
+ await mkdir(dirname(path), { recursive: true });
69
+ await context.storageState({ path });
70
+ written[key] = path;
71
+ log(`role ready: ${key} → ${path}`);
72
+ }
73
+ finally {
74
+ await context.dispose();
75
+ }
76
+ }
77
+ return written;
78
+ }
79
+ //# sourceMappingURL=auth-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-setup.js","sourceRoot":"","sources":["../../src/playwright/auth-setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Per-role API contexts as a Playwright fixture.
3
+ *
4
+ * A project pins one role through `use.storageState`, which covers the happy
5
+ * path. Authz tests need the others in the same spec — this exposes them on
6
+ * demand and disposes them at teardown, so no spec leaks a context.
7
+ *
8
+ * Requires the roles to have been established first; see ./auth-setup.ts.
9
+ */
10
+ import { type APIRequestContext } from '@playwright/test';
11
+ export interface VkFixtures {
12
+ /**
13
+ * Open an API request context authenticated as `role`.
14
+ *
15
+ * ```ts
16
+ * const viewer = await asRole('viewer');
17
+ * expect((await viewer.post('/content')).status()).toBe(403);
18
+ * ```
19
+ */
20
+ asRole: (role: string) => Promise<APIRequestContext>;
21
+ }
22
+ export interface CreateVkTestOptions {
23
+ /** Directory holding the storage-state files. Defaults to `.auth`. */
24
+ authDir?: string;
25
+ /** Override the project's `baseURL` for these contexts. */
26
+ baseURL?: string;
27
+ /** Extra headers for every role context. Defaults to `Accept: application/json`. */
28
+ extraHTTPHeaders?: Record<string, string>;
29
+ }
30
+ /**
31
+ * Build a `test` object carrying the `asRole` fixture.
32
+ *
33
+ * ```ts
34
+ * // tests/api/fixtures.ts
35
+ * export const test = createVkTest();
36
+ * ```
37
+ */
38
+ export declare function createVkTest(options?: CreateVkTestOptions): import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & VkFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
39
+ //# sourceMappingURL=fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/playwright/fixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAyB,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGjF,MAAM,WAAW,UAAU;IACzB;;;;;;;OAOG;IACH,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,4PA6B7D"}