@molecule/app-e2e-fixtures-default 1.0.0 → 1.0.2

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.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The console-error guard fixture: every test subscribes to the page's
3
+ * `pageerror` and `console.error` events and fails at teardown if any were
4
+ * seen. It works over both bonds — the preview client forwards console output
5
+ * and errors, so the guard sees the same events a real browser emits.
6
+ *
7
+ * @module
8
+ */
9
+ import type { PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType } from '@playwright/test';
10
+ type BaseTest = TestType<PlaywrightTestArgs & PlaywrightTestOptions, PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
11
+ /** One captured browser error. */
12
+ export interface ConsoleErrorEntry {
13
+ type: 'pageerror' | 'console.error';
14
+ text: string;
15
+ location?: string;
16
+ }
17
+ /**
18
+ * Substrings that are always ignored — browser noise that doesn't reflect app
19
+ * bugs.
20
+ */
21
+ export declare const ALWAYS_IGNORE: readonly RegExp[];
22
+ /** The fixture object `withConsoleGuard` adds to a test. */
23
+ export interface ConsoleGuardFixtures {
24
+ consoleGuard: void;
25
+ }
26
+ /**
27
+ * Extend a Playwright `test` with the auto-attached console-error guard.
28
+ *
29
+ * To intentionally let a known error through (rare — almost always a smell
30
+ * that should be fixed in the app), use
31
+ * `test.info().annotations.push({ type: 'allow-console-error', description: 'why' })`
32
+ * inside the test body BEFORE the error fires. The `description` is matched
33
+ * against the error text as a regular expression; if it is not a valid regex
34
+ * it is matched as a plain substring instead. An annotation with no
35
+ * description allows every error — always provide one.
36
+ */
37
+ export declare const withConsoleGuard: (base: BaseTest) => TestType<PlaywrightTestArgs & PlaywrightTestOptions & ConsoleGuardFixtures, PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
38
+ export {};
39
+ //# sourceMappingURL=console-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-guard.d.ts","sourceRoot":"","sources":["../src/console-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAEV,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACT,MAAM,kBAAkB,CAAA;AAEzB,KAAK,QAAQ,GAAG,QAAQ,CACtB,kBAAkB,GAAG,qBAAqB,EAC1C,oBAAoB,GAAG,uBAAuB,CAC/C,CAAA;AAED,kCAAkC;AAClC,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,WAAW,GAAG,eAAe,CAAA;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EA6B1C,CAAA;AAED,4DAA4D;AAC5D,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,IAAI,CAAA;CACnB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAM,QAAQ,KACb,QAAQ,CACT,kBAAkB,GAAG,qBAAqB,GAAG,oBAAoB,EACjE,oBAAoB,GAAG,uBAAuB,CAiE5C,CAAA"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The console-error guard fixture: every test subscribes to the page's
3
+ * `pageerror` and `console.error` events and fails at teardown if any were
4
+ * seen. It works over both bonds — the preview client forwards console output
5
+ * and errors, so the guard sees the same events a real browser emits.
6
+ *
7
+ * @module
8
+ */
9
+ /**
10
+ * Substrings that are always ignored — browser noise that doesn't reflect app
11
+ * bugs.
12
+ */
13
+ export const ALWAYS_IGNORE = [
14
+ // Vite HMR dev-only websocket reconnect warnings — happen on the
15
+ // test runner's first poll before vite is fully booted; harmless.
16
+ /\[vite\].*connecting/i,
17
+ /\[vite\].*server connection lost/i,
18
+ // Service worker registration failures in headless Chrome — VitePWA
19
+ // tries to register at /sw.js but our smoke build emits to /workbox-*
20
+ // and Playwright fixtures the user-agent. Cosmetic.
21
+ /service worker.*404/i,
22
+ // Chrome DevTools' own probe for source maps on third-party CDN bundles
23
+ // we don't ship maps for (Stripe, Google Maps, etc). Two bugs fixed here,
24
+ // both verified against a headless Chromium probe + the real reported
25
+ // message text (not guessed): (1) the verb was wrong — real Chrome output
26
+ // is "DevTools failed to load SourceMap: Could not load content for
27
+ // <url>: ...", not "failed to fetch source map", so the old pattern never
28
+ // matched real Chrome text at all; (2) the host check (`.cdn.`) doesn't
29
+ // match real vendor hosts (js.stripe.com, maps.googleapis.com contain no
30
+ // '.cdn.' substring). Now matches any `https://`-hosted source map
31
+ // (third-party CDN bundles are always TLS; the app's own dev/preview
32
+ // server is plain `http://localhost`, so same-origin source-map issues
33
+ // are never accidentally silenced by this pattern).
34
+ // NOTE (verified, not assumed): a probe with a broken `sourceMappingURL`
35
+ // produced ZERO console messages via `page.on('console')` under plain
36
+ // Playwright automation (with and without tracing) — Chrome only fetches
37
+ // source maps when a DevTools Sources panel is actually attached, which a
38
+ // headless Playwright run never does. So this entry currently matches
39
+ // nothing observed in practice; it is defense-in-depth against a future
40
+ // Chrome/Playwright behavior change, not an active filter today.
41
+ /devtools failed to load source ?map.*https:\/\//i,
42
+ ];
43
+ /**
44
+ * Extend a Playwright `test` with the auto-attached console-error guard.
45
+ *
46
+ * To intentionally let a known error through (rare — almost always a smell
47
+ * that should be fixed in the app), use
48
+ * `test.info().annotations.push({ type: 'allow-console-error', description: 'why' })`
49
+ * inside the test body BEFORE the error fires. The `description` is matched
50
+ * against the error text as a regular expression; if it is not a valid regex
51
+ * it is matched as a plain substring instead. An annotation with no
52
+ * description allows every error — always provide one.
53
+ */
54
+ export const withConsoleGuard = (base) => base.extend({
55
+ consoleGuard: [
56
+ async ({ page }, use, testInfo) => {
57
+ const buffer = [];
58
+ const matchesAllowed = (text, description) => {
59
+ if (description === undefined)
60
+ return true; // no description = allow everything
61
+ try {
62
+ return new RegExp(description).test(text);
63
+ }
64
+ catch (_error) {
65
+ // Not a valid regex: the annotation holds literal error text — match it as a substring.
66
+ return text.includes(description);
67
+ }
68
+ };
69
+ const shouldIgnore = (text) => {
70
+ if (ALWAYS_IGNORE.some((re) => re.test(text)))
71
+ return true;
72
+ return testInfo.annotations
73
+ .filter((a) => a.type === 'allow-console-error')
74
+ .some((a) => matchesAllowed(text, a.description));
75
+ };
76
+ const onPageError = (err) => {
77
+ const text = err.message || String(err);
78
+ if (shouldIgnore(text))
79
+ return;
80
+ buffer.push({ type: 'pageerror', text });
81
+ };
82
+ const onConsole = (msg) => {
83
+ if (msg.type() !== 'error')
84
+ return;
85
+ const text = msg.text();
86
+ if (shouldIgnore(text))
87
+ return;
88
+ const loc = msg.location();
89
+ buffer.push({
90
+ type: 'console.error',
91
+ text,
92
+ location: loc?.url ? `${loc.url}:${loc.lineNumber}` : undefined,
93
+ });
94
+ };
95
+ page.on('pageerror', onPageError);
96
+ page.on('console', onConsole);
97
+ try {
98
+ await use();
99
+ }
100
+ finally {
101
+ page.off('pageerror', onPageError);
102
+ page.off('console', onConsole);
103
+ }
104
+ if (buffer.length > 0) {
105
+ const lines = buffer
106
+ .map((e) => ` - [${e.type}] ${e.text}${e.location ? ` (${e.location})` : ''}`)
107
+ .join('\n');
108
+ throw new Error(`Browser console error(s) during test (${buffer.length}):\n${lines}\n\n` +
109
+ `If this error is genuinely expected, add\n` +
110
+ ` test.info().annotations.push({ type: 'allow-console-error', description: '<regex>' })\n` +
111
+ `to the test body BEFORE the error fires.`);
112
+ }
113
+ },
114
+ { auto: true },
115
+ ],
116
+ });
117
+ //# sourceMappingURL=console-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-guard.js","sourceRoot":"","sources":["../src/console-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `expect` that routes locators and pages built by {@link createEvaluatePage}
3
+ * to polling matchers of their own, and everything else — values, real
4
+ * Playwright locators — to Playwright's `expect` untouched.
5
+ *
6
+ * @module
7
+ */
8
+ import { expect as playwrightExpect } from '@playwright/test';
9
+ type AnyExpect = typeof playwrightExpect;
10
+ /**
11
+ * Playwright's `expect`, plus polling matchers for pages and locators built
12
+ * over the preview. `expect(value)`, `expect.soft`, `expect.poll`,
13
+ * `expect.configure` and the asymmetric matchers behave exactly as in
14
+ * Playwright.
15
+ */
16
+ export declare const expect: AnyExpect;
17
+ export {};
18
+ //# sourceMappingURL=expect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expect.d.ts","sourceRoot":"","sources":["../src/expect.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,IAAI,gBAAgB,EAA0B,MAAM,kBAAkB,CAAA;AAqWrF,KAAK,SAAS,GAAG,OAAO,gBAAgB,CAAA;AAuCxC;;;;;GAKG;AACH,eAAO,MAAM,MAAM,EAAE,SAAsC,CAAA"}
package/dist/expect.js ADDED
@@ -0,0 +1,233 @@
1
+ /**
2
+ * `expect` that routes locators and pages built by {@link createEvaluatePage}
3
+ * to polling matchers of their own, and everything else — values, real
4
+ * Playwright locators — to Playwright's `expect` untouched.
5
+ *
6
+ * @module
7
+ */
8
+ import { expect as playwrightExpect, test as playwrightTest } from '@playwright/test';
9
+ import { E2EStrictModeError } from '@molecule/app-e2e';
10
+ import { isE2ELocator, isE2EPage, textMatch, textMatchFull, } from './page.js';
11
+ const DEFAULT_TIMEOUT = 5_000;
12
+ const pretty = (value) => typeof value === 'string' ? JSON.stringify(value) : (JSON.stringify(value) ?? String(value));
13
+ const expectationText = (expected) => {
14
+ if (expected instanceof RegExp)
15
+ return String(expected);
16
+ if (Array.isArray(expected))
17
+ return JSON.stringify(expected.map((e) => (e instanceof RegExp ? String(e) : e)));
18
+ return pretty(expected);
19
+ };
20
+ const failure = (subject, matcher, describeSubject, expected, received, timeout, negate, message) => {
21
+ const lines = [
22
+ `${message ?? `expect(${subject}).${negate ? 'not.' : ''}${matcher}() failed`}`,
23
+ '',
24
+ `${subject === 'page' ? 'Page' : 'Locator'}: ${describeSubject}`,
25
+ `Expected: ${negate ? 'not ' : ''}${expected}`,
26
+ `Received: ${pretty(received)}`,
27
+ `Timeout: ${timeout}ms`,
28
+ ];
29
+ const err = new Error(lines.join('\n'));
30
+ err.name = 'Error';
31
+ return err;
32
+ };
33
+ const record = (soft, err) => {
34
+ if (!soft)
35
+ throw err;
36
+ let info;
37
+ try {
38
+ info = playwrightTest.info();
39
+ }
40
+ catch (_error) {
41
+ info = null;
42
+ }
43
+ if (info && typeof info._failWithError === 'function')
44
+ info._failWithError(err);
45
+ else
46
+ throw err;
47
+ };
48
+ const toMatchArg = (expected, full, opts) => Array.isArray(expected)
49
+ ? expected.map((e) => (full ? textMatchFull(e, opts) : textMatch(e, opts)))
50
+ : full
51
+ ? textMatchFull(expected, opts)
52
+ : textMatch(expected, opts);
53
+ const locatorMatchers = (locator, defaults, negate = false) => {
54
+ const run = async (matcher, args, expected, opts) => {
55
+ const timeout = opts?.timeout ?? defaults.timeout ?? DEFAULT_TIMEOUT;
56
+ const deadline = Date.now() + timeout;
57
+ let last;
58
+ let delay = 100;
59
+ for (;;) {
60
+ const res = await locator.probe(matcher, args);
61
+ if (res.strict) {
62
+ record(!!defaults.soft, new E2EStrictModeError(locator.describe(), Number(res.count)));
63
+ return;
64
+ }
65
+ if (res.error) {
66
+ record(!!defaults.soft, new Error(`expect(locator).${matcher}(): ${String(res.error)}`));
67
+ return;
68
+ }
69
+ last = res.received;
70
+ if (Boolean(res.pass) !== negate)
71
+ return;
72
+ if (Date.now() > deadline) {
73
+ record(!!defaults.soft, failure('locator', matcher, locator.describe(), expected, last, timeout, negate, defaults.message));
74
+ return;
75
+ }
76
+ await new Promise((r) => setTimeout(r, delay));
77
+ delay = Math.min(delay * 2, 500);
78
+ }
79
+ };
80
+ const timeoutOf = (opts) => opts && typeof opts.timeout === 'number' ? { timeout: opts.timeout } : undefined;
81
+ const matchers = {
82
+ toBeVisible: (opts) => run('toBeVisible', {}, 'visible', timeoutOf(opts)),
83
+ toBeHidden: (opts) => run('toBeHidden', {}, 'hidden', timeoutOf(opts)),
84
+ toBeAttached: (opts) => run('toBeAttached', {}, 'attached', timeoutOf(opts)),
85
+ toHaveCount: (n, opts) => run('toHaveCount', { n }, String(n), timeoutOf(opts)),
86
+ toHaveText: (expected, opts) => run('toHaveText', {
87
+ expected: toMatchArg(expected, true, {
88
+ ignoreCase: opts?.ignoreCase,
89
+ }),
90
+ useInnerText: opts?.useInnerText,
91
+ }, expectationText(expected), timeoutOf(opts)),
92
+ toContainText: (expected, opts) => run('toContainText', {
93
+ expected: toMatchArg(expected, false, {
94
+ ignoreCase: opts?.ignoreCase,
95
+ }),
96
+ useInnerText: opts?.useInnerText,
97
+ }, `containing ${expectationText(expected)}`, timeoutOf(opts)),
98
+ toHaveAttribute: (name, expected, opts) => {
99
+ const valueGiven = expected !== undefined && !(typeof expected === 'object' && !(expected instanceof RegExp));
100
+ const options = (valueGiven ? opts : expected) ?? {};
101
+ return run('toHaveAttribute', {
102
+ name,
103
+ expected: valueGiven
104
+ ? textMatchFull(expected, {
105
+ ignoreCase: options.ignoreCase,
106
+ })
107
+ : undefined,
108
+ }, valueGiven ? `${name}=${expectationText(expected)}` : `attribute ${name}`, timeoutOf(options));
109
+ },
110
+ toHaveClass: (expected, opts) => run('toHaveClass', { expected: toMatchArg(expected, true) }, `class ${expectationText(expected)}`, timeoutOf(opts)),
111
+ toContainClass: (expected, opts) => run('toContainClass', { expected }, `classes ${expectationText(expected)}`, timeoutOf(opts)),
112
+ toHaveCSS: (name, expected, opts) => run('toHaveCSS', { name, expected: textMatchFull(expected) }, `${name}: ${expectationText(expected)}`, timeoutOf(opts)),
113
+ toHaveValue: (expected, opts) => run('toHaveValue', { expected: textMatchFull(expected) }, `value ${expectationText(expected)}`, timeoutOf(opts)),
114
+ toHaveValues: (expected, opts) => run('toHaveValues', { expected: toMatchArg(expected, true) }, `values ${expectationText(expected)}`, timeoutOf(opts)),
115
+ toHaveId: (expected, opts) => run('toHaveId', { expected: textMatchFull(expected) }, `id ${expectationText(expected)}`, timeoutOf(opts)),
116
+ toHaveJSProperty: (name, expected, opts) => run('toHaveJSProperty', { name, expected }, `${name} = ${expectationText(expected)}`, timeoutOf(opts)),
117
+ toBeChecked: (opts) => run('toBeChecked', { checked: opts?.checked }, opts?.checked === false ? 'unchecked' : 'checked', timeoutOf(opts)),
118
+ toBeEnabled: (opts) => run('toBeEnabled', {}, 'enabled', timeoutOf(opts)),
119
+ toBeDisabled: (opts) => run('toBeDisabled', {}, 'disabled', timeoutOf(opts)),
120
+ toBeEditable: (opts) => run('toBeEditable', {}, 'editable', timeoutOf(opts)),
121
+ toBeEmpty: (opts) => run('toBeEmpty', {}, 'empty', timeoutOf(opts)),
122
+ toBeFocused: (opts) => run('toBeFocused', {}, 'focused', timeoutOf(opts)),
123
+ toBeInViewport: (opts) => run('toBeInViewport', { ratio: opts?.ratio }, 'in viewport', timeoutOf(opts)),
124
+ toHaveAccessibleName: (expected, opts) => run('toHaveAccessibleName', {
125
+ expected: textMatchFull(expected, {
126
+ ignoreCase: opts?.ignoreCase,
127
+ }),
128
+ }, `accessible name ${expectationText(expected)}`, timeoutOf(opts)),
129
+ toHaveRole: (expected, opts) => run('toHaveRole', { expected }, `role ${expectationText(expected)}`, timeoutOf(opts)),
130
+ };
131
+ if (!negate)
132
+ matchers.not = locatorMatchers(locator, defaults, true);
133
+ return new Proxy(matchers, {
134
+ get(target, prop) {
135
+ if (typeof prop === 'symbol' || prop in target)
136
+ return target[prop];
137
+ if (prop === 'then')
138
+ return undefined;
139
+ return () => {
140
+ throw new Error(`expect(locator).${String(prop)}() is not supported by the e2e core over the preview. Supported: ${Object.keys(matchers)
141
+ .filter((k) => k !== 'not')
142
+ .join(', ')}. For anything else read the value (textContent, boundingBox, evaluate) and assert on it.`);
143
+ };
144
+ },
145
+ });
146
+ };
147
+ const pageMatchers = (page, defaults, negate = false) => {
148
+ const poll = async (matcher, expected, describe, read, test, opts) => {
149
+ const timeout = opts?.timeout ?? defaults.timeout ?? DEFAULT_TIMEOUT;
150
+ const deadline = Date.now() + timeout;
151
+ let last;
152
+ for (;;) {
153
+ last = await read();
154
+ if (test(last) !== negate)
155
+ return;
156
+ if (Date.now() > deadline) {
157
+ record(!!defaults.soft, failure('page', matcher, describe, expectationText(expected), last, timeout, negate, defaults.message));
158
+ return;
159
+ }
160
+ await new Promise((r) => setTimeout(r, 100));
161
+ }
162
+ };
163
+ const matchers = {
164
+ toHaveTitle: (expected, opts) => poll('toHaveTitle', expected, page.url(), () => page.title(), (t) => expected instanceof RegExp
165
+ ? expected.test(t)
166
+ : t.replace(/\s+/g, ' ').trim() === expected.replace(/\s+/g, ' ').trim(), opts),
167
+ toHaveURL: (expected, opts) => poll('toHaveURL', expected, page.url(), async () => String((await page.rt('info', [])).url ?? page.url()), (current) => {
168
+ if (expected instanceof RegExp)
169
+ return expected.test(current);
170
+ if (/^[a-z]+:/i.test(expected))
171
+ return (current === expected || current.replace(/\/$/, '') === expected.replace(/\/$/, ''));
172
+ const u = new URL(current);
173
+ const path = u.pathname + u.search + u.hash;
174
+ return (path === expected ||
175
+ u.pathname === expected ||
176
+ u.pathname.replace(/\/$/, '') === expected.replace(/\/$/, ''));
177
+ }, opts),
178
+ };
179
+ if (!negate)
180
+ matchers.not = pageMatchers(page, defaults, true);
181
+ return new Proxy(matchers, {
182
+ get(target, prop) {
183
+ if (typeof prop === 'symbol' || prop in target)
184
+ return target[prop];
185
+ if (prop === 'then')
186
+ return undefined;
187
+ return () => {
188
+ throw new Error(`expect(page).${String(prop)}() is not supported over the preview. Supported: toHaveTitle, toHaveURL. Screenshots need a real browser; assert layout with locator.boundingBox() and page.evaluate().`);
189
+ };
190
+ },
191
+ });
192
+ };
193
+ const wrap = (base, defaults) => new Proxy(base, {
194
+ apply(target, thisArg, args) {
195
+ const [actual, messageOrOptions] = args;
196
+ const message = typeof messageOrOptions === 'string'
197
+ ? messageOrOptions
198
+ : messageOrOptions?.message;
199
+ if (isE2ELocator(actual))
200
+ return locatorMatchers(actual, { ...defaults, message: message ?? defaults.message });
201
+ if (isE2EPage(actual))
202
+ return pageMatchers(actual, { ...defaults, message: message ?? defaults.message });
203
+ return Reflect.apply(target, thisArg, args);
204
+ },
205
+ get(target, prop, receiver) {
206
+ if (prop === 'soft') {
207
+ const soft = Reflect.get(target, prop, receiver);
208
+ return (actual, messageOrOptions) => {
209
+ const message = typeof messageOrOptions === 'string'
210
+ ? messageOrOptions
211
+ : messageOrOptions?.message;
212
+ if (isE2ELocator(actual))
213
+ return locatorMatchers(actual, { ...defaults, soft: true, message });
214
+ if (isE2EPage(actual))
215
+ return pageMatchers(actual, { ...defaults, soft: true, message });
216
+ return soft(actual, messageOrOptions);
217
+ };
218
+ }
219
+ if (prop === 'configure') {
220
+ const configure = Reflect.get(target, prop, receiver);
221
+ return (opts) => wrap(configure(opts), { ...defaults, ...opts });
222
+ }
223
+ return Reflect.get(target, prop, receiver);
224
+ },
225
+ });
226
+ /**
227
+ * Playwright's `expect`, plus polling matchers for pages and locators built
228
+ * over the preview. `expect(value)`, `expect.soft`, `expect.poll`,
229
+ * `expect.configure` and the asymmetric matchers behave exactly as in
230
+ * Playwright.
231
+ */
232
+ export const expect = wrap(playwrightExpect, {});
233
+ //# sourceMappingURL=expect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expect.js","sourceRoot":"","sources":["../src/expect.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,67 +1,114 @@
1
1
  /**
2
- * Shared Playwright `test` + `expect` with auto-attached browser
3
- * console-error / pageerror guard.
2
+ * The `@playwright/test` drop-in for molecule apps: Playwright's `test`,
3
+ * `expect` and `page`, with the browser supplied by the bonded e2e provider
4
+ * and a console-error guard on every test.
4
5
  *
5
- * Every fleet app's e2e specs import `test` and `expect` from this
6
- * module (re-exported through their per-app `_helpers.ts`) instead of
7
- * `@playwright/test` directly. The custom `test` includes a
8
- * `consoleGuard` fixture with `{ auto: true }`, so every test
9
- * automatically subscribes to the browser's `pageerror` event and
10
- * `console.error` messages, then asserts the buffer is empty at test
11
- * teardown.
6
+ * Import `test` and `expect` from here instead of `@playwright/test` and
7
+ * write ordinary Playwright specs. Which browser runs them is the e2e bond
8
+ * (`@molecule/app-e2e`):
12
9
  *
13
- * This catches the failure mode where React (or any other client-side
14
- * module) throws on mount but the spec only asserts against
15
- * `page.request.get/post`, leaving the test green while the rendered
16
- * page is blank. A single quill-delta ESM/CJS interop error sat for
17
- * four days like this before we noticed — the replay videos were
18
- * 17 KB of nothing and the JSON reported PASS.
10
+ * - **`@molecule/app-e2e-preview`** drives the LIVE PREVIEW the molecule.dev
11
+ * IDE is already showing the page the person is looking at, in their own
12
+ * browser over a WebSocket through the dev server. No browser binary in
13
+ * the sandbox, nothing to download. This is what a molecule sandbox uses.
14
+ * - **`@molecule/app-e2e-playwright`** launches real Playwright browsers. This
15
+ * is what your own machine and CI use.
19
16
  *
20
- * To intentionally let a known error through (rare almost always a
21
- * smell that should be fixed in the app), use
22
- * `test.info().annotations.push({ type: 'allow-console-error', description: 'why' })`
23
- * inside the test body BEFORE the error fires. The `description` is
24
- * matched against the error text as a regular expression; if it is not
25
- * a valid regex it is matched as a plain substring instead (so literal
26
- * error text with `[`/`(` can be pasted verbatim). An annotation with
27
- * no description allows every error — always provide one.
17
+ * The same spec file runs unchanged in both. The provider is picked by
18
+ * `resolveE2EProviderName()`: `MOL_E2E_PROVIDER` when set, otherwise `preview`
19
+ * inside a molecule sandbox (the `/etc/mol/app-root` marker exists) and
20
+ * `playwright` everywhere else. The scaffolded `e2e/bonds.ts` bonds the
21
+ * matching provider (and `test` bonds it by name as a fallback); `test` reads
22
+ * the same answer to decide whether to launch a browser.
28
23
  *
29
- * A small, fixed set of browser-noise patterns are ALWAYS ignored regardless
30
- * of `allow-console-error` (Vite HMR reconnect chatter, service-worker 404s
31
- * in headless Chrome, and a real Chrome DevTools "failed to load SourceMap"
32
- * message for any `https://`-hosted bundle — e.g. Stripe/Google Maps CDN
33
- * scripts shipped without source maps). That last pattern is verified
34
- * against the actual Chrome message text and constrained to `https://` so it
35
- * can never silence a genuinely broken source map in the app's OWN bundle
36
- * (served over plain `http://localhost` in dev/preview).
24
+ * Every test also carries the console-error guard: the page's `pageerror`
25
+ * and `console.error` events fail the test at teardown, so a spec that only
26
+ * asserts on `page.request` cannot stay green while the rendered page is
27
+ * blank. Allow a known error with
28
+ * `test.info().annotations.push({ type: 'allow-console-error', description: '<regex>' })`.
37
29
  *
38
30
  * @example
39
31
  * ```ts
40
- * // In fleet apps, the per-app `./_helpers.ts` re-exports these:
41
- * import { test, expect } from '@molecule/app-e2e-fixtures-default'
32
+ * // e2e/bonds.ts scaffolded; wires the provider for THIS environment
33
+ * import { resolveE2EProviderName, setProvider } from '@molecule/app-e2e'
34
+ * import { provider as playwright } from '@molecule/app-e2e-playwright'
35
+ * import { provider as preview } from '@molecule/app-e2e-preview'
42
36
  *
43
- * test('login lands on dashboard', async ({ page }) => {
44
- * await page.goto('/login')
45
- * await page.getByLabel(/email/i).fill('user@example.com')
46
- * // ...
37
+ * setProvider(resolveE2EProviderName() === 'preview' ? preview : playwright)
38
+ *
39
+ * // e2e/post.spec.ts — an ordinary Playwright spec
40
+ * import { expect, test } from '@molecule/app-e2e-fixtures-default'
41
+ *
42
+ * import './bonds.js'
43
+ *
44
+ * test('the phone layout keeps the prose large', async ({ page }) => {
45
+ * await page.setViewportSize({ width: 390, height: 844 })
46
+ * await page.goto('/blog/hello/')
47
+ * const prose = page.locator('article p').first()
48
+ * const size = await prose.evaluate((el) => parseFloat(getComputedStyle(el).fontSize))
49
+ * expect(size).toBeGreaterThanOrEqual(18)
50
+ * await expect(page.getByRole('switch', { name: /summar/i })).toBeVisible()
47
51
  * })
48
52
  * ```
49
53
  *
54
+ * @remarks
55
+ * - **What works over the preview** (the `@molecule/app-e2e-preview` bond):
56
+ * `page.goto/reload/goBack/goForward/url/title/content`,
57
+ * `page.evaluate/$eval/$$eval`, `page.locator` and every `getBy*` (role
58
+ * with name/level/checked/pressed/expanded/selected, text, label,
59
+ * placeholder, title, alt text, test id), `first/last/nth/filter/count/all`,
60
+ * `click/dblclick/hover/tap/fill/clear/type/press/check/uncheck/setChecked/
61
+ * selectOption/focus/blur/dispatchEvent/scrollIntoViewIfNeeded`,
62
+ * `textContent/innerText/innerHTML/inputValue/getAttribute/isVisible/isHidden/
63
+ * isEnabled/isDisabled/isEditable/isChecked/boundingBox/evaluate/evaluateAll/
64
+ * waitFor`, `page.setViewportSize/viewportSize`, `page.mouse.*`,
65
+ * `page.keyboard.*`, `page.request.get/post/put/patch/delete/fetch` (runs
66
+ * `fetch` inside the page, cookies included), `waitForSelector/waitForURL/
67
+ * waitForFunction/waitForLoadState/waitForTimeout`, `page.on('console' |
68
+ * 'pageerror' | 'dialog' | 'close')`, and `expect(locator)` with
69
+ * `toBeVisible/toBeHidden/toBeAttached/toHaveCount/toHaveText/toContainText/
70
+ * toHaveAttribute/toHaveClass/toContainClass/toHaveCSS/toHaveValue/toHaveId/
71
+ * toBeChecked/toBeEnabled/toBeDisabled/toBeEditable/toBeEmpty/toBeFocused/
72
+ * toBeInViewport/toHaveAccessibleName/toHaveRole` (+ `.not`, `expect.soft`,
73
+ * `expect.configure`), `expect(page).toHaveTitle/toHaveURL`.
74
+ * - **The escape hatch is `page.evaluate()`.** Anything the list above does
75
+ * not cover — a computed style, a scroll position, `matchMedia`, a
76
+ * `fetch` — is one `evaluate` away; the function runs inside the real page
77
+ * and returns JSON.
78
+ * - **Not available over the preview**, and the method THROWS naming the
79
+ * alternative: screenshots and `toHaveScreenshot` (assert layout with
80
+ * `boundingBox()` and computed styles instead), `page.route/waitForResponse/
81
+ * waitForRequest` (read the response with `page.request` or `fetch` in
82
+ * `evaluate`), element handles (`$`, `$$`, `elementHandle` — use locators),
83
+ * `setInputFiles`, `dragTo`, iframes inside the preview, `emulateMedia`,
84
+ * `addInitScript/exposeFunction`, `context.cookies/storageState` (read
85
+ * `document.cookie`/`localStorage` in `evaluate`). The playwright bond
86
+ * supports all of them.
87
+ * - **Viewport.** `page.setViewportSize` asks the IDE to resize the preview
88
+ * frame and throws if the host did not (a preview opened in a plain tab
89
+ * keeps the tab's width). Test phone layouts at 390×844 this way.
90
+ * - **Events.** `page.on('response')`/`'request'` never fire over the preview
91
+ * (a one-time warning says so); `'console'` and `'pageerror'` do, so the
92
+ * console-error guard works there too.
93
+ * - **The person's browser is the renderer.** If every tab showing the
94
+ * preview is closed or asleep, actions wait for a page to reconnect and
95
+ * then time out with a message saying so. Keep the IDE tab open (the IDE
96
+ * holds a screen wake lock while a build runs) or open the preview URL in
97
+ * any other tab — any connected viewer will do.
98
+ * - `createEvaluatePage()` is how a bond that can only run code inside a page
99
+ * (an `E2ETransport`) gets the whole Playwright-shaped page; the preview
100
+ * bond uses it, and so can any future one.
101
+ * - `@playwright/test` is a peer dependency: it supplies the runner
102
+ * (`npx playwright test`), `expect` for plain values, and the `Page` types.
103
+ * It never downloads browsers on install; only the playwright bond needs
104
+ * `npx playwright install`.
105
+ *
50
106
  * @module
51
107
  */
52
- import { expect } from '@playwright/test';
53
- interface ConsoleErrorEntry {
54
- type: 'pageerror' | 'console.error';
55
- text: string;
56
- location?: string;
57
- }
58
- /**
59
- * Custom Playwright `test` with an auto-attached browser console-error
60
- * guard. Drop-in replacement for `import { test } from '@playwright/test'`.
61
- */
62
- export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
63
- consoleGuard: void;
64
- }, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
65
- export { expect };
66
- export type { ConsoleErrorEntry };
108
+ export * from './console-guard.js';
109
+ export * from './expect.js';
110
+ export * from './page.js';
111
+ export * from './playwright.js';
112
+ export * from './runtime.js';
113
+ export * from './test.js';
67
114
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH,OAAO,EAAuB,MAAM,EAAgB,MAAM,kBAAkB,CAAA;AAE5E,UAAU,iBAAiB;IACzB,IAAI,EAAE,WAAW,GAAG,eAAe,CAAA;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAkCD;;;GAGG;AACH,eAAO,MAAM,IAAI;kBAA+B,IAAI;wGAkElD,CAAA;AAEF,OAAO,EAAE,MAAM,EAAE,CAAA;AACjB,YAAY,EAAE,iBAAiB,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0GG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA"}