@venturekit/testing 0.0.0-dev.20260816114351 → 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.
- package/README.md +105 -39
- package/bin/ensure-db.mjs +64 -0
- package/dist/ensure-database.d.ts +43 -0
- package/dist/ensure-database.d.ts.map +1 -0
- package/dist/ensure-database.js +76 -0
- package/dist/ensure-database.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/playwright/auth-setup.d.ts +58 -0
- package/dist/playwright/auth-setup.d.ts.map +1 -0
- package/dist/playwright/auth-setup.js +79 -0
- package/dist/playwright/auth-setup.js.map +1 -0
- package/dist/playwright/fixtures.d.ts +39 -0
- package/dist/playwright/fixtures.d.ts.map +1 -0
- package/dist/playwright/fixtures.js +46 -0
- package/dist/playwright/fixtures.js.map +1 -0
- package/dist/playwright/index.d.ts +24 -0
- package/dist/playwright/index.d.ts.map +1 -0
- package/dist/playwright/index.js +21 -0
- package/dist/playwright/index.js.map +1 -0
- package/dist/playwright/storage-state.d.ts +28 -0
- package/dist/playwright/storage-state.d.ts.map +1 -0
- package/dist/playwright/storage-state.js +40 -0
- package/dist/playwright/storage-state.js.map +1 -0
- package/dist/playwright/web-servers.d.ts +135 -0
- package/dist/playwright/web-servers.d.ts.map +1 -0
- package/dist/playwright/web-servers.js +122 -0
- package/dist/playwright/web-servers.js.map +1 -0
- package/dist/stack.d.ts +20 -0
- package/dist/stack.d.ts.map +1 -1
- package/dist/stack.js +12 -0
- package/dist/stack.js.map +1 -1
- package/dist/stage-db.d.ts +88 -0
- package/dist/stage-db.d.ts.map +1 -0
- package/dist/stage-db.js +96 -0
- package/dist/stage-db.js.map +1 -0
- 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: `
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
for
|
|
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: './
|
|
45
|
-
globalSetup: './
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
//
|
|
65
|
-
import {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
85
|
-
import {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
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
|
|
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"}
|