@cosmicdrift/kumiko-testing 0.315.0 → 0.316.0
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 +2 -1
- package/package.json +4 -4
- package/src/changes.json +18 -0
- package/src/e2e/define-app-e2e-config.ts +8 -1
- package/src/e2e/seed-contract.ts +3 -0
- package/src/e2e/seed-route.ts +6 -1
- package/src/e2e/seeded-tenant-fixture.ts +12 -2
- package/src/integration-runner.ts +4 -1
- package/src/seed-tenant.ts +6 -2
- package/src/seed-types.ts +4 -1
package/README.md
CHANGED
|
@@ -18,7 +18,8 @@ await tenant.api.writeOk("my-feature:write:thing:create", { title: "x" });
|
|
|
18
18
|
`bunfig.integration.toml` and `bunfig.real.toml` (plus `bunfig.dom.toml`). `--dom` needs your own
|
|
19
19
|
`./test-setup/dom.preload.ts`.
|
|
20
20
|
- `kumiko-testing integration [--parallel N] [--timings <file>]`: runs `*.integration.test.ts`
|
|
21
|
-
with the 15s budget.
|
|
21
|
+
with the 15s budget. `--parallel` only when you ask for it, and then with `--no-isolate`:
|
|
22
|
+
bun 1.4.0's implicit `--isolate` leaks native memory per file until CI workers are OOM-killed.
|
|
22
23
|
|
|
23
24
|
## E2E (`./e2e`, `./e2e/seed-route`)
|
|
24
25
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.316.0",
|
|
4
4
|
"description": "Test template for Kumiko apps: per-flow seedTenant, app test stack, canonical test preloads, bunfig generator and integration runner.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"kumiko-testing": "./bin/kumiko-testing.ts"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
67
|
-
"@cosmicdrift/kumiko-dev-server": "0.
|
|
68
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
66
|
+
"@cosmicdrift/kumiko-bundled-features": "0.316.0",
|
|
67
|
+
"@cosmicdrift/kumiko-dev-server": "0.316.0",
|
|
68
|
+
"@cosmicdrift/kumiko-framework": "0.316.0",
|
|
69
69
|
"zod": "^4.4.3"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.316.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Parallel integration runs no longer OOM-kill their workers",
|
|
6
|
+
"detail": "bun 1.4.0's --parallel implies --isolate, which leaks native memory per\ntest file while the JS heap stays flat: about 4 MB per file for a bare\nsetupTestStack, about 40 MB with bundled-features. In publicstatus\n(--parallel 4) each worker grew from ~290 to ~1000 MiB and the run died\nwith exit 137 on the 3 GiB CI runner. With --no-isolate the same run\npeaks at 380 MiB and finishes in 28.9 s instead of 49.9 s, with no new\nfailures. Without --parallel bun never isolated files, so the tests\nalready isolate through data (seedTenant per flow, queue prefix per\nstack), not through processes."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.316.0",
|
|
10
|
+
"type": "fix",
|
|
11
|
+
"title": "A dedicated screenshots Playwright config without SCREENSHOT_DIR now fails with a clear message",
|
|
12
|
+
"detail": "A dedicated screenshots config (testDir: \"./e2e/screenshots\", used by\nphronexsis and publicstatus) had its entire testDir excluded by\nscreenshotSpecsIgnore()'s \"**/screenshots/**\" pattern whenever\nSCREENSHOT_DIR was unset, so Playwright reported the generic \"No tests\nfound\" with no mention of the missing env var — even though\nrequireScreenshotDir() already existed with a clear message, just never\ncalled on this path. defineAppE2eConfig now calls it up front for any\ntestDir ending in \"screenshots\" outside a screenshot run."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.316.0",
|
|
16
|
+
"type": "improvement",
|
|
17
|
+
"title": "tenant.addUser accepts an optional displayName/email identity",
|
|
18
|
+
"detail": "seedTenant()'s admin already accepted `admin: { displayName, email }`, but\n`tenant.addUser(roles)` (the seed-user route and the in-process seedTenant\nalike) had no such parameter and always fell back to `Seed <hash>` /\n`user-<uuid>@example.test`, which then leaked into screenshots and docs\n(solon, money-horse). `addUser(roles, { displayName, email })` now threads\nan optional identity through to the created user row, the same\n`SeedAdminIdentity` shape the admin already uses. Also: the seedTenant\nfixture logging the shared Playwright `context` in as the tenant admin was\npreviously documented only in a code comment in auth-kit.ts — it is now on\nthe `SeedTenantFixture` type's own JSDoc."
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"version": "0.314.0",
|
|
4
22
|
"type": "improvement",
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
SEED_TOKEN_ENV,
|
|
21
21
|
STYLESHEET_WATCH_ENV,
|
|
22
22
|
} from "./constants";
|
|
23
|
-
import { isScreenshotRun, screenshotSpecsIgnore } from "./screenshot-dir";
|
|
23
|
+
import { isScreenshotRun, requireScreenshotDir, screenshotSpecsIgnore } from "./screenshot-dir";
|
|
24
24
|
import { E2E_TIMEOUT_MS } from "./timeouts";
|
|
25
25
|
|
|
26
26
|
// Apps pick Meilisearch over their in-memory search adapter just because
|
|
@@ -146,6 +146,13 @@ export function defineAppE2eConfig(input: AppE2eConfigInput): PlaywrightTestConf
|
|
|
146
146
|
} = input;
|
|
147
147
|
assertNoReservedEnv(env);
|
|
148
148
|
for (const project of projects) assertTemplateOwnedKeysUnset(project);
|
|
149
|
+
// A dedicated screenshots config (testDir: "./e2e/screenshots") has its
|
|
150
|
+
// entire testDir excluded by screenshotSpecsIgnore() until SCREENSHOT_DIR
|
|
151
|
+
// is set, which Playwright reports as a generic "No tests found" with no
|
|
152
|
+
// mention of the missing env var.
|
|
153
|
+
if (!isScreenshotRun() && /(?:^|\/)screenshots\/?$/.test(testDir)) {
|
|
154
|
+
requireScreenshotDir();
|
|
155
|
+
}
|
|
149
156
|
const baseURL = `http://localhost:${port}`;
|
|
150
157
|
const realRun = isRealProviderRun();
|
|
151
158
|
|
package/src/e2e/seed-contract.ts
CHANGED
|
@@ -60,6 +60,9 @@ export function createSeedUserRequestSchema(extraRoles: readonly string[] = [])
|
|
|
60
60
|
)
|
|
61
61
|
.min(1)
|
|
62
62
|
.max(seedable.length),
|
|
63
|
+
displayName: z.string().min(1).max(MAX_DISPLAY_NAME_LENGTH).optional(),
|
|
64
|
+
// May contain the literal "{tenantId}" placeholder, same as seedAdminIdentitySchema.
|
|
65
|
+
email: z.string().min(1).max(MAX_EMAIL_LENGTH).optional(),
|
|
63
66
|
});
|
|
64
67
|
}
|
|
65
68
|
|
package/src/e2e/seed-route.ts
CHANGED
|
@@ -206,7 +206,12 @@ export function createE2eSeedRoutes(
|
|
|
206
206
|
const write: SeedWriter = async (handlerQn, payload, tenantId) =>
|
|
207
207
|
unwrapSavedRow(handlerQn, await deps.dispatchSystemWrite({ handlerQn, payload, tenantId }));
|
|
208
208
|
try {
|
|
209
|
-
return c.json(
|
|
209
|
+
return c.json(
|
|
210
|
+
await persistUserRows(write, verified.tenantId, verified.roles, {
|
|
211
|
+
displayName: verified.displayName,
|
|
212
|
+
email: verified.email,
|
|
213
|
+
}),
|
|
214
|
+
);
|
|
210
215
|
} catch (error) {
|
|
211
216
|
return c.json({ error: error instanceof Error ? error.message : "seed failed" }, 500);
|
|
212
217
|
}
|
|
@@ -36,6 +36,11 @@ export type E2eSeededTenant = SeededTenant & {
|
|
|
36
36
|
readonly seed: (seeder: string, body?: unknown) => Promise<unknown>;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Seeds a tenant over HTTP and logs the shared `context` in as its admin, so
|
|
41
|
+
* a `page` opened from that context is already authenticated — call
|
|
42
|
+
* `loginAs` to switch it to another seeded user instead.
|
|
43
|
+
*/
|
|
39
44
|
export type SeedTenantFixture = (opts?: E2eSeedTenantOptions) => Promise<E2eSeededTenant>;
|
|
40
45
|
|
|
41
46
|
type E2eFixtures = {
|
|
@@ -125,12 +130,17 @@ export async function provideSeedTenant(
|
|
|
125
130
|
name: seeded.name,
|
|
126
131
|
admin,
|
|
127
132
|
members: seeded.members.map((member) => toUser(member, [ROLES.Member])),
|
|
128
|
-
addUser: async (roles = [ROLES.Member]) =>
|
|
133
|
+
addUser: async (roles = [ROLES.Member], identity = {}) =>
|
|
129
134
|
toUser(
|
|
130
135
|
await postSeedRoute(
|
|
131
136
|
request,
|
|
132
137
|
SEED_ROUTES.seedUser,
|
|
133
|
-
{
|
|
138
|
+
{
|
|
139
|
+
tenantId,
|
|
140
|
+
roles: [...roles],
|
|
141
|
+
displayName: identity.displayName,
|
|
142
|
+
email: identity.email,
|
|
143
|
+
} satisfies SeedUserRequest,
|
|
134
144
|
seedUserResponseSchema,
|
|
135
145
|
),
|
|
136
146
|
roles,
|
|
@@ -29,7 +29,10 @@ export function buildIntegrationTestArgs(opts: IntegrationRunOptions): string[]
|
|
|
29
29
|
"test",
|
|
30
30
|
`--config=${BUNFIG_FILES.integration}`,
|
|
31
31
|
`--timeout=${TEST_TIMEOUT_MS.integration}`,
|
|
32
|
-
|
|
32
|
+
// bun 1.4.0's --parallel implies --isolate, which leaks native memory per test file
|
|
33
|
+
// (~40-60 MB with bundled-features) until the workers blow the CI runner's limit.
|
|
34
|
+
// Tests isolate through data (seedTenant per flow, queue prefix per stack), not processes.
|
|
35
|
+
...(opts.parallel !== undefined ? [`--parallel=${opts.parallel}`, "--no-isolate"] : []),
|
|
33
36
|
...(opts.timings !== undefined ? [`--timings=${opts.timings}`] : []),
|
|
34
37
|
...(opts.updateTimings === true ? ["--update-timings"] : []),
|
|
35
38
|
...opts.files,
|
package/src/seed-tenant.ts
CHANGED
|
@@ -184,9 +184,13 @@ export async function seedTenant(
|
|
|
184
184
|
name: rows.name,
|
|
185
185
|
admin,
|
|
186
186
|
members: rows.members.map((member) => withSession(member, id, [ROLES.Member])),
|
|
187
|
-
addUser: async (roles = [ROLES.Member]) =>
|
|
187
|
+
addUser: async (roles = [ROLES.Member], identity = {}) =>
|
|
188
188
|
withSession(
|
|
189
|
-
persist
|
|
189
|
+
persist
|
|
190
|
+
? await persistUserRows(write, id, roles, identity)
|
|
191
|
+
: lightCredentials(
|
|
192
|
+
identity.email !== undefined ? substituteTenantId(identity.email, id) : undefined,
|
|
193
|
+
),
|
|
190
194
|
id,
|
|
191
195
|
roles,
|
|
192
196
|
),
|
package/src/seed-types.ts
CHANGED
|
@@ -45,7 +45,10 @@ export type SeededTenant = {
|
|
|
45
45
|
readonly name: string;
|
|
46
46
|
readonly admin: SeededUser;
|
|
47
47
|
readonly members: readonly SeededUser[];
|
|
48
|
-
readonly addUser: (
|
|
48
|
+
readonly addUser: (
|
|
49
|
+
roles?: readonly string[],
|
|
50
|
+
identity?: SeedAdminIdentity,
|
|
51
|
+
) => Promise<SeededUser>;
|
|
49
52
|
readonly api: BoundApi;
|
|
50
53
|
readonly apiAs: (user: SeededUser) => BoundApi;
|
|
51
54
|
};
|