@specific.dev/spectest 0.110.0 → 0.111.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/dist/daemon.js +4 -3
- package/dist/index.d.ts +12 -7
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/src/daemon.ts +4 -3
- package/src/index.ts +9 -8
- package/src/interactive-setup.test.ts +2 -2
package/dist/daemon.js
CHANGED
|
@@ -2932,7 +2932,7 @@ async function bootstrapInner(prepared, identity) {
|
|
|
2932
2932
|
let interactiveSetupCompleted = false;
|
|
2933
2933
|
/**
|
|
2934
2934
|
* Run a project setup hook. Normal `setup` runs before the shared warm
|
|
2935
|
-
* snapshot; `
|
|
2935
|
+
* snapshot; `interactive.seed` runs only after it, in a fresh user start.
|
|
2936
2936
|
* Both use the same context and boot-log capture.
|
|
2937
2937
|
*
|
|
2938
2938
|
* Unlike test runs, this is NOT instrumented — no recorder, no event
|
|
@@ -2951,7 +2951,8 @@ async function runProjectSetup(hook = "setup") {
|
|
|
2951
2951
|
}
|
|
2952
2952
|
}
|
|
2953
2953
|
async function runProjectSetupInner(hook) {
|
|
2954
|
-
const
|
|
2954
|
+
const project = requireLoaded().project;
|
|
2955
|
+
const setup = hook === "setup" ? project.setup : project.interactive?.seed;
|
|
2955
2956
|
if (!setup || (hook === "setupInteractive" && interactiveSetupCompleted)) {
|
|
2956
2957
|
return { ran: false, durationMs: 0 };
|
|
2957
2958
|
}
|
|
@@ -5143,7 +5144,7 @@ export function harnessMethods(state) {
|
|
|
5143
5144
|
capabilities: ["prepared-images-v1"],
|
|
5144
5145
|
// Optional discovery: old SDKs omit this field and are never sent the
|
|
5145
5146
|
// new method. Check the loaded project, including after warm restore.
|
|
5146
|
-
setupInteractive: typeof loaded?.project.
|
|
5147
|
+
setupInteractive: typeof loaded?.project.interactive?.seed === "function",
|
|
5147
5148
|
replayEnvironmentMode: true,
|
|
5148
5149
|
}),
|
|
5149
5150
|
// Live bootstrap progress, polled by the control plane during
|
package/dist/index.d.ts
CHANGED
|
@@ -1627,21 +1627,24 @@ export interface Project<S extends ServicesMap = ServicesMap, F extends FakesMap
|
|
|
1627
1627
|
/**
|
|
1628
1628
|
* Prepare a new interactive environment, after normal setup and the shared
|
|
1629
1629
|
* warm-template snapshot. Runs on both cold starts and warm restores from
|
|
1630
|
-
* the dashboard or `spectest env start`, before
|
|
1630
|
+
* the dashboard (seed checkbox on) or `spectest env start --seed`, before
|
|
1631
|
+
* the environment is ready. Skipped when seeding is not requested.
|
|
1631
1632
|
* Never runs for tests, snapshot forks, or resuming a stopped environment.
|
|
1632
1633
|
* Uses the same context as `setup`; throw to fail the start.
|
|
1633
1634
|
*/
|
|
1634
|
-
|
|
1635
|
+
interactive?: {
|
|
1636
|
+
seed?: ProjectSetupFn<S, F>;
|
|
1637
|
+
};
|
|
1635
1638
|
tests?: TestSuite<S, F>;
|
|
1636
1639
|
}
|
|
1637
1640
|
/**
|
|
1638
|
-
* Context handed to the project-level `setup` and `
|
|
1641
|
+
* Context handed to the project-level `setup` and `interactive.seed` hooks: the full
|
|
1639
1642
|
* {@link SpectestContext}, minus only what a timeline gives a test
|
|
1640
1643
|
* (testName, parent, browser/mobile/terminal). Everything a test can do
|
|
1641
1644
|
* to the environment — exec, fetch, `svc`/`fakes` helpers, `poll`,
|
|
1642
1645
|
* `dnsName`, `certificate`, `startService`, and project-file access via
|
|
1643
1646
|
* `projectRoot`/`readProjectFile` — is available here too. Normal `setup`
|
|
1644
|
-
* state is captured into the shared warm template; `
|
|
1647
|
+
* state is captured into the shared warm template; `interactive.seed`
|
|
1645
1648
|
* state belongs only to the new interactive environment.
|
|
1646
1649
|
*/
|
|
1647
1650
|
export interface ProjectSetupContext<S extends ServicesMap = ServicesMap, F extends FakesMap = FakesMap> extends SpectestContext<S, F> {
|
|
@@ -1681,8 +1684,10 @@ export type ProjectSetupFn<S extends ServicesMap = ServicesMap, F extends FakesM
|
|
|
1681
1684
|
export interface ProjectOpts<S extends ServicesMap = ServicesMap, F extends FakesMap = FakesMap> {
|
|
1682
1685
|
/** Optional project-level setup. See `Project.setup`. */
|
|
1683
1686
|
setup?: ProjectSetupFn<S, F>;
|
|
1684
|
-
/** Optional interactive-only setup. See `Project.
|
|
1685
|
-
|
|
1687
|
+
/** Optional interactive-only setup. See `Project.interactive`. */
|
|
1688
|
+
interactive?: {
|
|
1689
|
+
seed?: ProjectSetupFn<S, F>;
|
|
1690
|
+
};
|
|
1686
1691
|
/** Test cases for this project's suite. */
|
|
1687
1692
|
tests?: TestCase<unknown, S, F>[];
|
|
1688
1693
|
}
|
|
@@ -1736,7 +1741,7 @@ export interface DefinedEnvironment<S extends ServicesMap, F extends FakesMap =
|
|
|
1736
1741
|
group<P = undefined>(name: string, opts?: GroupOpts<P, S, F>): TestGroup<P, S, F>;
|
|
1737
1742
|
/** Bundle this environment with a test suite into the project default
|
|
1738
1743
|
* export. Pass tests as a plain array for the common case, or an
|
|
1739
|
-
* options bag to attach `setup` or `
|
|
1744
|
+
* options bag to attach `setup` or `interactive.seed`. (Fakes are declared on
|
|
1740
1745
|
* `defineEnvironment`, not here.) */
|
|
1741
1746
|
project(tests?: TestCase<unknown, S, F>[]): Project<S, F>;
|
|
1742
1747
|
project(opts: ProjectOpts<S, F>): Project<S, F>;
|
package/dist/index.js
CHANGED
|
@@ -484,8 +484,8 @@ export function defineEnvironment(input) {
|
|
|
484
484
|
const proj = { environment: config };
|
|
485
485
|
if (opts.setup)
|
|
486
486
|
proj.setup = opts.setup;
|
|
487
|
-
if (opts.
|
|
488
|
-
proj.
|
|
487
|
+
if (opts.interactive)
|
|
488
|
+
proj.interactive = opts.interactive;
|
|
489
489
|
if (fakes)
|
|
490
490
|
proj.fakes = fakes;
|
|
491
491
|
if (opts.tests && opts.tests.length > 0) {
|
package/package.json
CHANGED
package/src/daemon.ts
CHANGED
|
@@ -3481,7 +3481,7 @@ let interactiveSetupCompleted = false;
|
|
|
3481
3481
|
|
|
3482
3482
|
/**
|
|
3483
3483
|
* Run a project setup hook. Normal `setup` runs before the shared warm
|
|
3484
|
-
* snapshot; `
|
|
3484
|
+
* snapshot; `interactive.seed` runs only after it, in a fresh user start.
|
|
3485
3485
|
* Both use the same context and boot-log capture.
|
|
3486
3486
|
*
|
|
3487
3487
|
* Unlike test runs, this is NOT instrumented — no recorder, no event
|
|
@@ -3500,7 +3500,8 @@ async function runProjectSetup(hook: "setup" | "setupInteractive" = "setup"): Pr
|
|
|
3500
3500
|
}
|
|
3501
3501
|
|
|
3502
3502
|
async function runProjectSetupInner(hook: "setup" | "setupInteractive"): Promise<ProjectSetupResult> {
|
|
3503
|
-
const
|
|
3503
|
+
const project = requireLoaded().project;
|
|
3504
|
+
const setup = hook === "setup" ? project.setup : project.interactive?.seed;
|
|
3504
3505
|
if (!setup || (hook === "setupInteractive" && interactiveSetupCompleted)) {
|
|
3505
3506
|
return { ran: false, durationMs: 0 };
|
|
3506
3507
|
}
|
|
@@ -6161,7 +6162,7 @@ export function harnessMethods(state: RouteState): MethodTable {
|
|
|
6161
6162
|
capabilities: ["prepared-images-v1"],
|
|
6162
6163
|
// Optional discovery: old SDKs omit this field and are never sent the
|
|
6163
6164
|
// new method. Check the loaded project, including after warm restore.
|
|
6164
|
-
setupInteractive: typeof loaded?.project.
|
|
6165
|
+
setupInteractive: typeof loaded?.project.interactive?.seed === "function",
|
|
6165
6166
|
replayEnvironmentMode: true,
|
|
6166
6167
|
}),
|
|
6167
6168
|
|
package/src/index.ts
CHANGED
|
@@ -2380,22 +2380,23 @@ export interface Project<
|
|
|
2380
2380
|
/**
|
|
2381
2381
|
* Prepare a new interactive environment, after normal setup and the shared
|
|
2382
2382
|
* warm-template snapshot. Runs on both cold starts and warm restores from
|
|
2383
|
-
* the dashboard or `spectest env start`, before
|
|
2383
|
+
* the dashboard (seed checkbox on) or `spectest env start --seed`, before
|
|
2384
|
+
* the environment is ready. Skipped when seeding is not requested.
|
|
2384
2385
|
* Never runs for tests, snapshot forks, or resuming a stopped environment.
|
|
2385
2386
|
* Uses the same context as `setup`; throw to fail the start.
|
|
2386
2387
|
*/
|
|
2387
|
-
|
|
2388
|
+
interactive?: { seed?: ProjectSetupFn<S, F> };
|
|
2388
2389
|
tests?: TestSuite<S, F>;
|
|
2389
2390
|
}
|
|
2390
2391
|
|
|
2391
2392
|
/**
|
|
2392
|
-
* Context handed to the project-level `setup` and `
|
|
2393
|
+
* Context handed to the project-level `setup` and `interactive.seed` hooks: the full
|
|
2393
2394
|
* {@link SpectestContext}, minus only what a timeline gives a test
|
|
2394
2395
|
* (testName, parent, browser/mobile/terminal). Everything a test can do
|
|
2395
2396
|
* to the environment — exec, fetch, `svc`/`fakes` helpers, `poll`,
|
|
2396
2397
|
* `dnsName`, `certificate`, `startService`, and project-file access via
|
|
2397
2398
|
* `projectRoot`/`readProjectFile` — is available here too. Normal `setup`
|
|
2398
|
-
* state is captured into the shared warm template; `
|
|
2399
|
+
* state is captured into the shared warm template; `interactive.seed`
|
|
2399
2400
|
* state belongs only to the new interactive environment.
|
|
2400
2401
|
*/
|
|
2401
2402
|
export interface ProjectSetupContext<
|
|
@@ -2445,8 +2446,8 @@ export interface ProjectOpts<
|
|
|
2445
2446
|
> {
|
|
2446
2447
|
/** Optional project-level setup. See `Project.setup`. */
|
|
2447
2448
|
setup?: ProjectSetupFn<S, F>;
|
|
2448
|
-
/** Optional interactive-only setup. See `Project.
|
|
2449
|
-
|
|
2449
|
+
/** Optional interactive-only setup. See `Project.interactive`. */
|
|
2450
|
+
interactive?: { seed?: ProjectSetupFn<S, F> };
|
|
2450
2451
|
/** Test cases for this project's suite. */
|
|
2451
2452
|
tests?: TestCase<unknown, S, F>[];
|
|
2452
2453
|
}
|
|
@@ -2511,7 +2512,7 @@ export interface DefinedEnvironment<
|
|
|
2511
2512
|
): TestGroup<P, S, F>;
|
|
2512
2513
|
/** Bundle this environment with a test suite into the project default
|
|
2513
2514
|
* export. Pass tests as a plain array for the common case, or an
|
|
2514
|
-
* options bag to attach `setup` or `
|
|
2515
|
+
* options bag to attach `setup` or `interactive.seed`. (Fakes are declared on
|
|
2515
2516
|
* `defineEnvironment`, not here.) */
|
|
2516
2517
|
project(tests?: TestCase<unknown, S, F>[]): Project<S, F>;
|
|
2517
2518
|
project(opts: ProjectOpts<S, F>): Project<S, F>;
|
|
@@ -2663,7 +2664,7 @@ export function defineEnvironment<
|
|
|
2663
2664
|
: (arg ?? {});
|
|
2664
2665
|
const proj: Project<S, F> = { environment: config };
|
|
2665
2666
|
if (opts.setup) proj.setup = opts.setup;
|
|
2666
|
-
if (opts.
|
|
2667
|
+
if (opts.interactive) proj.interactive = opts.interactive;
|
|
2667
2668
|
if (fakes) proj.fakes = fakes;
|
|
2668
2669
|
if (opts.tests && opts.tests.length > 0) {
|
|
2669
2670
|
// Explicit suite (single-file / legacy layout): the tests are listed
|
|
@@ -22,7 +22,7 @@ for (const withHook of [false, true]) {
|
|
|
22
22
|
}});
|
|
23
23
|
export default env.project({
|
|
24
24
|
setup: () => { calls.push("baseline"); },
|
|
25
|
-
${withHook ? `
|
|
25
|
+
${withHook ? `interactive: { seed: async (ctx) => {
|
|
26
26
|
calls.push("interactive");
|
|
27
27
|
console.log("seeding demo data");
|
|
28
28
|
await globalThis.seedGate;
|
|
@@ -35,7 +35,7 @@ for (const withHook of [false, true]) {
|
|
|
35
35
|
if (typeof result.exitCode !== "number" || !result.stdout.includes("seed demo")) {
|
|
36
36
|
throw new Error("exec result not plain: " + JSON.stringify(result));
|
|
37
37
|
}
|
|
38
|
-
},` : ""}
|
|
38
|
+
} },` : ""}
|
|
39
39
|
});
|
|
40
40
|
`);
|
|
41
41
|
const script = `
|