@opice/harness 0.1.1 → 0.2.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 +22 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/scenario.d.ts.map +1 -1
- package/dist/scenario.js +8 -1
- package/dist/scenario.js.map +1 -1
- package/dist/setup.d.ts +32 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +35 -0
- package/dist/setup.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +3 -0
- package/src/scenario.ts +7 -1
- package/src/setup.ts +54 -0
package/README.md
CHANGED
|
@@ -111,6 +111,28 @@ import { fullEnum } from '../browser-tools'
|
|
|
111
111
|
await call(fullEnum, { label: 'Typ', option: 'Faktura' })
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
### Context setup (user-land)
|
|
115
|
+
|
|
116
|
+
Export `setup(context)` from `<repo>/browser-setup.ts` to configure the browser
|
|
117
|
+
**context** once, **before the first navigation** — on both faces (the test
|
|
118
|
+
harness runs it in `beforeAll` before `page.goto`; the `opice-browser` server
|
|
119
|
+
runs it after connecting, before navigating to the launch URL). Because it runs
|
|
120
|
+
pre-navigation, an `addInitScript` here fires before the app's own scripts on
|
|
121
|
+
first paint — the place to seed storage/cookies, grant permissions, or set a
|
|
122
|
+
boot-time flag (e.g. "automated run — skip dev-only chrome"). Keep it
|
|
123
|
+
idempotent.
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
// browser-setup.ts
|
|
127
|
+
import type { BrowserSetup } from '@opice/harness'
|
|
128
|
+
|
|
129
|
+
export const setup: BrowserSetup = async (context) => {
|
|
130
|
+
await context.addInitScript(() => {
|
|
131
|
+
try { localStorage.setItem('app:e2e', '1') } catch {}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
114
136
|
### Misc
|
|
115
137
|
|
|
116
138
|
- `screenshot(path?)` — saves a PNG, returns the path (default under `/tmp/`).
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export { parseOpiceDsn } from './dsn.js';
|
|
|
10
10
|
export type { OpiceDsn } from './dsn.js';
|
|
11
11
|
export { command, call, runCommand, makeCtx, loadUserCommands, findUserCommandsFile, z } from './command.js';
|
|
12
12
|
export type { Command, CommandCtx } from './command.js';
|
|
13
|
+
export { loadUserSetup, findUserSetupFile } from './setup.js';
|
|
14
|
+
export type { BrowserSetup } from './setup.js';
|
|
13
15
|
export { expect } from '@playwright/test';
|
|
14
16
|
export type { Locator } from 'playwright';
|
|
15
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEzD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEtF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAEvD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC1E,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEvG,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAC5G,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEzD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEtF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAEvD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC1E,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEvG,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAC5G,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC7D,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAI9C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAGzC,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { browserTest, step } from './scenario.js';
|
|
|
6
6
|
export { getReporter, setReporter, configureFromEnv } from './reporter.js';
|
|
7
7
|
export { parseOpiceDsn } from './dsn.js';
|
|
8
8
|
export { command, call, runCommand, makeCtx, loadUserCommands, findUserCommandsFile, z } from './command.js';
|
|
9
|
+
export { loadUserSetup, findUserSetupFile } from './setup.js';
|
|
9
10
|
// Playwright's web-first `expect` (retrying locator matchers + generic matchers)
|
|
10
11
|
// works under `bun:test`; re-export it so tests use a single `expect`.
|
|
11
12
|
export { expect } from '@playwright/test';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEzD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEtF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAGjD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAG1E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAG5G,iFAAiF;AACjF,uEAAuE;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEzD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEtF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAGjD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAG1E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAG5G,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAG7D,iFAAiF;AACjF,uEAAuE;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/scenario.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenario.d.ts","sourceRoot":"","sources":["../src/scenario.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scenario.d.ts","sourceRoot":"","sources":["../src/scenario.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,kBAAkB;IAClC,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACrB;AAuCD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,EAAE,OAAO,GAAE,kBAAkB,GAAG,MAAW,GAAG,IAAI,CAyDzG;AAED;;;;;;GAMG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCtF"}
|
package/dist/scenario.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { closePage, launchPage } from './context.js';
|
|
3
|
+
import { closePage, getContext, launchPage } from './context.js';
|
|
4
4
|
import { screenshot } from './element.js';
|
|
5
5
|
import { getReporter } from './reporter.js';
|
|
6
|
+
import { loadUserSetup } from './setup.js';
|
|
6
7
|
/**
|
|
7
8
|
* `bun:test` is resolved lazily, at the moment `browserTest` registers a
|
|
8
9
|
* scenario — never at module load. That keeps `@opice/harness` importable
|
|
@@ -78,6 +79,12 @@ export function browserTest(name, fn, options = {}) {
|
|
|
78
79
|
currentScenarioId = null;
|
|
79
80
|
}
|
|
80
81
|
const page = await launchPage();
|
|
82
|
+
// Repo-level context setup (browser-setup.ts) runs before the first
|
|
83
|
+
// navigation, so an addInitScript it registers fires before the app's
|
|
84
|
+
// own scripts on first paint.
|
|
85
|
+
const setup = await loadUserSetup();
|
|
86
|
+
if (setup)
|
|
87
|
+
await setup(getContext());
|
|
81
88
|
const base = opts.url ?? PLAYGROUND_URL;
|
|
82
89
|
const url = opts.hash ? `${base}#${opts.hash}` : base;
|
|
83
90
|
await page.goto(url);
|
package/dist/scenario.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenario.js","sourceRoot":"","sources":["../src/scenario.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"scenario.js","sourceRoot":"","sources":["../src/scenario.ts"],"names":[],"mappings":"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"}
|
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { BrowserContext } from 'playwright';
|
|
2
|
+
/**
|
|
3
|
+
* Repo-level browser context setup — the context analog of `browser-tools.ts`.
|
|
4
|
+
*
|
|
5
|
+
* A repo may export a `setup(context)` from `browser-setup.ts`; opice runs it
|
|
6
|
+
* against the freshly-created `BrowserContext` **before the first navigation**,
|
|
7
|
+
* on both faces:
|
|
8
|
+
*
|
|
9
|
+
* - **tests** — `browserTest` runs it in `beforeAll`, after launching the
|
|
10
|
+
* context but before `page.goto`,
|
|
11
|
+
* - **authoring** — the `opice-browser` server runs it once after connecting,
|
|
12
|
+
* before navigating to the launch URL.
|
|
13
|
+
*
|
|
14
|
+
* Because it runs pre-navigation, an `addInitScript` registered here executes
|
|
15
|
+
* before the app's own scripts on the very first paint — the right place to
|
|
16
|
+
* seed storage/cookies, grant permissions, or set a flag the app reads at boot
|
|
17
|
+
* (e.g. "this is an automated run — don't render dev-only chrome"). Keep the
|
|
18
|
+
* body idempotent: it may run more than once over a context's life.
|
|
19
|
+
*/
|
|
20
|
+
export type BrowserSetup = (context: BrowserContext) => void | Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Locate a repo's `browser-setup.ts` (or `.js`/`.mjs`), walking up from `from`.
|
|
23
|
+
* Returns the absolute path, or null if none is found before the filesystem root.
|
|
24
|
+
*/
|
|
25
|
+
export declare function findUserSetupFile(from?: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Load a repo's `browser-setup.ts` and return its setup function (the `setup`
|
|
28
|
+
* named export, or the default export), or null if there is no such file or it
|
|
29
|
+
* doesn't export a function.
|
|
30
|
+
*/
|
|
31
|
+
export declare function loadUserSetup(from?: string): Promise<BrowserSetup | null>;
|
|
32
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAE5E;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAW7E;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAM/E"}
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
/**
|
|
5
|
+
* Locate a repo's `browser-setup.ts` (or `.js`/`.mjs`), walking up from `from`.
|
|
6
|
+
* Returns the absolute path, or null if none is found before the filesystem root.
|
|
7
|
+
*/
|
|
8
|
+
export function findUserSetupFile(from = process.cwd()) {
|
|
9
|
+
let dir = path.resolve(from);
|
|
10
|
+
for (;;) {
|
|
11
|
+
for (const name of ['browser-setup.ts', 'browser-setup.js', 'browser-setup.mjs']) {
|
|
12
|
+
const candidate = path.join(dir, name);
|
|
13
|
+
if (existsSync(candidate))
|
|
14
|
+
return candidate;
|
|
15
|
+
}
|
|
16
|
+
const parent = path.dirname(dir);
|
|
17
|
+
if (parent === dir)
|
|
18
|
+
return null;
|
|
19
|
+
dir = parent;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load a repo's `browser-setup.ts` and return its setup function (the `setup`
|
|
24
|
+
* named export, or the default export), or null if there is no such file or it
|
|
25
|
+
* doesn't export a function.
|
|
26
|
+
*/
|
|
27
|
+
export async function loadUserSetup(from) {
|
|
28
|
+
const file = findUserSetupFile(from);
|
|
29
|
+
if (!file)
|
|
30
|
+
return null;
|
|
31
|
+
const mod = (await import(pathToFileURL(file).href));
|
|
32
|
+
const fn = mod['setup'] ?? mod['default'];
|
|
33
|
+
return typeof fn === 'function' ? fn : null;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAuBxC;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe,OAAO,CAAC,GAAG,EAAE;IAC7D,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5B,SAAS,CAAC;QACT,KAAK,MAAM,IAAI,IAAI,CAAC,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,EAAE,CAAC;YAClF,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACtC,IAAI,UAAU,CAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAA;QAC5C,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAChC,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAA;QAC/B,GAAG,GAAG,MAAM,CAAA;IACb,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAa;IAChD,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAA;IACpC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IACtB,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAA4B,CAAA;IAC/E,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,CAAA;IACzC,OAAO,OAAO,EAAE,KAAK,UAAU,CAAC,CAAC,CAAE,EAAmB,CAAC,CAAC,CAAC,IAAI,CAAA;AAC9D,CAAC"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -18,6 +18,9 @@ export type { OpiceDsn } from './dsn.js'
|
|
|
18
18
|
export { command, call, runCommand, makeCtx, loadUserCommands, findUserCommandsFile, z } from './command.js'
|
|
19
19
|
export type { Command, CommandCtx } from './command.js'
|
|
20
20
|
|
|
21
|
+
export { loadUserSetup, findUserSetupFile } from './setup.js'
|
|
22
|
+
export type { BrowserSetup } from './setup.js'
|
|
23
|
+
|
|
21
24
|
// Playwright's web-first `expect` (retrying locator matchers + generic matchers)
|
|
22
25
|
// works under `bun:test`; re-export it so tests use a single `expect`.
|
|
23
26
|
export { expect } from '@playwright/test'
|
package/src/scenario.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
import { closePage, launchPage } from './context.js'
|
|
3
|
+
import { closePage, getContext, launchPage } from './context.js'
|
|
4
4
|
import { screenshot } from './element.js'
|
|
5
5
|
import { getReporter } from './reporter.js'
|
|
6
|
+
import { loadUserSetup } from './setup.js'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* `bun:test` is resolved lazily, at the moment `browserTest` registers a
|
|
@@ -94,6 +95,11 @@ export function browserTest(name: string, fn: () => void, options: BrowserTestOp
|
|
|
94
95
|
currentScenarioId = null
|
|
95
96
|
}
|
|
96
97
|
const page = await launchPage()
|
|
98
|
+
// Repo-level context setup (browser-setup.ts) runs before the first
|
|
99
|
+
// navigation, so an addInitScript it registers fires before the app's
|
|
100
|
+
// own scripts on first paint.
|
|
101
|
+
const setup = await loadUserSetup()
|
|
102
|
+
if (setup) await setup(getContext())
|
|
97
103
|
const base = opts.url ?? PLAYGROUND_URL
|
|
98
104
|
const url = opts.hash ? `${base}#${opts.hash}` : base
|
|
99
105
|
await page.goto(url)
|
package/src/setup.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { pathToFileURL } from 'node:url'
|
|
4
|
+
import type { BrowserContext } from 'playwright'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Repo-level browser context setup — the context analog of `browser-tools.ts`.
|
|
8
|
+
*
|
|
9
|
+
* A repo may export a `setup(context)` from `browser-setup.ts`; opice runs it
|
|
10
|
+
* against the freshly-created `BrowserContext` **before the first navigation**,
|
|
11
|
+
* on both faces:
|
|
12
|
+
*
|
|
13
|
+
* - **tests** — `browserTest` runs it in `beforeAll`, after launching the
|
|
14
|
+
* context but before `page.goto`,
|
|
15
|
+
* - **authoring** — the `opice-browser` server runs it once after connecting,
|
|
16
|
+
* before navigating to the launch URL.
|
|
17
|
+
*
|
|
18
|
+
* Because it runs pre-navigation, an `addInitScript` registered here executes
|
|
19
|
+
* before the app's own scripts on the very first paint — the right place to
|
|
20
|
+
* seed storage/cookies, grant permissions, or set a flag the app reads at boot
|
|
21
|
+
* (e.g. "this is an automated run — don't render dev-only chrome"). Keep the
|
|
22
|
+
* body idempotent: it may run more than once over a context's life.
|
|
23
|
+
*/
|
|
24
|
+
export type BrowserSetup = (context: BrowserContext) => void | Promise<void>
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Locate a repo's `browser-setup.ts` (or `.js`/`.mjs`), walking up from `from`.
|
|
28
|
+
* Returns the absolute path, or null if none is found before the filesystem root.
|
|
29
|
+
*/
|
|
30
|
+
export function findUserSetupFile(from: string = process.cwd()): string | null {
|
|
31
|
+
let dir = path.resolve(from)
|
|
32
|
+
for (;;) {
|
|
33
|
+
for (const name of ['browser-setup.ts', 'browser-setup.js', 'browser-setup.mjs']) {
|
|
34
|
+
const candidate = path.join(dir, name)
|
|
35
|
+
if (existsSync(candidate)) return candidate
|
|
36
|
+
}
|
|
37
|
+
const parent = path.dirname(dir)
|
|
38
|
+
if (parent === dir) return null
|
|
39
|
+
dir = parent
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load a repo's `browser-setup.ts` and return its setup function (the `setup`
|
|
45
|
+
* named export, or the default export), or null if there is no such file or it
|
|
46
|
+
* doesn't export a function.
|
|
47
|
+
*/
|
|
48
|
+
export async function loadUserSetup(from?: string): Promise<BrowserSetup | null> {
|
|
49
|
+
const file = findUserSetupFile(from)
|
|
50
|
+
if (!file) return null
|
|
51
|
+
const mod = (await import(pathToFileURL(file).href)) as Record<string, unknown>
|
|
52
|
+
const fn = mod['setup'] ?? mod['default']
|
|
53
|
+
return typeof fn === 'function' ? (fn as BrowserSetup) : null
|
|
54
|
+
}
|