@rstest/browser 0.8.5 → 0.9.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/LICENSE-APACHE-2.0 +202 -0
- package/NOTICE +11 -0
- package/dist/361.js +8 -0
- package/dist/augmentExpect.d.ts +73 -0
- package/dist/browser-container/container-static/css/index.5c72297783.css +1 -0
- package/dist/browser-container/container-static/js/{565.226c9ef5.js → 101.36a8ccdf84.js} +4024 -3856
- package/dist/browser-container/container-static/js/101.36a8ccdf84.js.LICENSE.txt +1 -0
- package/dist/browser-container/container-static/js/{index.c1d17467.js → index.0687a8142a.js} +742 -692
- package/dist/browser-container/container-static/js/{lib-react.97ee79b0.js → lib-react.dcf2a5e57a.js} +10 -10
- package/dist/browser-container/container-static/js/lib-react.dcf2a5e57a.js.LICENSE.txt +1 -0
- package/dist/browser-container/index.html +1 -1
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +583 -0
- package/dist/browserRpcRegistry.d.ts +18 -0
- package/dist/client/api.d.ts +3 -0
- package/dist/client/browserRpc.d.ts +2 -0
- package/dist/client/dispatchTransport.d.ts +11 -0
- package/dist/client/entry.d.ts +1 -5
- package/dist/client/locator.d.ts +125 -0
- package/dist/client/snapshot.d.ts +0 -6
- package/dist/concurrency.d.ts +12 -0
- package/dist/dispatchCapabilities.d.ts +34 -0
- package/dist/dispatchRouter.d.ts +20 -0
- package/dist/headlessLatestRerunScheduler.d.ts +19 -0
- package/dist/headlessTransport.d.ts +12 -0
- package/dist/index.js +1580 -258
- package/dist/protocol.d.ts +44 -33
- package/dist/providers/index.d.ts +79 -0
- package/dist/providers/playwright/compileLocator.d.ts +3 -0
- package/dist/providers/playwright/dispatchBrowserRpc.d.ts +13 -0
- package/dist/providers/playwright/expectUtils.d.ts +24 -0
- package/dist/providers/playwright/implementation.d.ts +2 -0
- package/dist/providers/playwright/index.d.ts +1 -0
- package/dist/providers/playwright/runtime.d.ts +5 -0
- package/dist/providers/playwright/textMatcher.d.ts +8 -0
- package/dist/rpcProtocol.d.ts +145 -0
- package/dist/runSession.d.ts +33 -0
- package/dist/sessionRegistry.d.ts +34 -0
- package/dist/sourceMap/sourceMapLoader.d.ts +14 -0
- package/dist/watchRerunPlanner.d.ts +21 -0
- package/package.json +15 -10
- package/src/AGENTS.md +128 -0
- package/src/augmentExpect.ts +62 -0
- package/src/browser.ts +3 -0
- package/src/browserRpcRegistry.ts +57 -0
- package/src/client/AGENTS.md +82 -0
- package/src/client/api.ts +213 -0
- package/src/client/browserRpc.ts +86 -0
- package/src/client/dispatchTransport.ts +178 -0
- package/src/client/entry.ts +96 -33
- package/src/client/locator.ts +452 -0
- package/src/client/snapshot.ts +32 -97
- package/src/client/sourceMapSupport.ts +26 -37
- package/src/concurrency.ts +62 -0
- package/src/dispatchCapabilities.ts +162 -0
- package/src/dispatchRouter.ts +82 -0
- package/src/env.d.ts +8 -1
- package/src/headlessLatestRerunScheduler.ts +76 -0
- package/src/headlessTransport.ts +28 -0
- package/src/hostController.ts +1292 -367
- package/src/protocol.ts +66 -31
- package/src/providers/index.ts +103 -0
- package/src/providers/playwright/compileLocator.ts +130 -0
- package/src/providers/playwright/dispatchBrowserRpc.ts +372 -0
- package/src/providers/playwright/expectUtils.ts +57 -0
- package/src/providers/playwright/implementation.ts +33 -0
- package/src/providers/playwright/index.ts +1 -0
- package/src/providers/playwright/runtime.ts +32 -0
- package/src/providers/playwright/textMatcher.ts +10 -0
- package/src/rpcProtocol.ts +220 -0
- package/src/runSession.ts +110 -0
- package/src/sessionRegistry.ts +89 -0
- package/src/sourceMap/sourceMapLoader.ts +96 -0
- package/src/watchRerunPlanner.ts +77 -0
- package/dist/browser-container/container-static/css/index.5a71c757.css +0 -1
- package/dist/browser-container/container-static/js/565.226c9ef5.js.LICENSE.txt +0 -1
- package/dist/browser-container/container-static/js/lib-react.97ee79b0.js.LICENSE.txt +0 -1
- package/dist/browser-container/container-static/js/scheduler.5accca0c.js +0 -407
- package/dist/browser-container/scheduler.html +0 -19
package/src/protocol.ts
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
import type { DevicePreset } from '@rstest/core/browser';
|
|
2
2
|
import type {
|
|
3
3
|
RuntimeConfig,
|
|
4
|
-
Test,
|
|
5
4
|
TestFileResult,
|
|
5
|
+
TestInfo,
|
|
6
6
|
TestResult,
|
|
7
7
|
} from '@rstest/core/browser-runtime';
|
|
8
8
|
import type { SnapshotUpdateState } from '@vitest/snapshot';
|
|
9
9
|
|
|
10
|
+
export type {
|
|
11
|
+
BrowserLocatorIR,
|
|
12
|
+
BrowserLocatorStep,
|
|
13
|
+
BrowserLocatorText,
|
|
14
|
+
BrowserRpcRequest,
|
|
15
|
+
BrowserRpcResponse,
|
|
16
|
+
SnapshotRpcRequest,
|
|
17
|
+
SnapshotRpcResponse,
|
|
18
|
+
} from './rpcProtocol';
|
|
19
|
+
export { validateBrowserRpcRequest } from './rpcProtocol';
|
|
20
|
+
|
|
21
|
+
export const DISPATCH_MESSAGE_TYPE = '__rstest_dispatch__';
|
|
22
|
+
export const DISPATCH_RESPONSE_TYPE = '__rstest_dispatch_response__';
|
|
23
|
+
export const DISPATCH_RPC_BRIDGE_NAME = '__rstest_dispatch_rpc__';
|
|
24
|
+
export const DISPATCH_RPC_REQUEST_TYPE = 'dispatch-rpc-request';
|
|
25
|
+
export const RSTEST_CONFIG_MESSAGE_TYPE = 'RSTEST_CONFIG';
|
|
26
|
+
|
|
27
|
+
export const DISPATCH_NAMESPACE_RUNNER = 'runner';
|
|
28
|
+
export const DISPATCH_NAMESPACE_BROWSER = 'browser';
|
|
29
|
+
export const DISPATCH_NAMESPACE_SNAPSHOT = 'snapshot';
|
|
30
|
+
export const DISPATCH_METHOD_RPC = 'rpc';
|
|
31
|
+
|
|
10
32
|
export type SerializedRuntimeConfig = RuntimeConfig;
|
|
11
33
|
|
|
12
34
|
export type BrowserViewport =
|
|
@@ -47,6 +69,11 @@ export type BrowserHostConfig = {
|
|
|
47
69
|
updateSnapshot: SnapshotUpdateState;
|
|
48
70
|
};
|
|
49
71
|
testFile?: string; // Optional: if provided, only run this specific test file
|
|
72
|
+
/**
|
|
73
|
+
* Per-run identifier assigned by the container.
|
|
74
|
+
* Used by browser RPC calls to prevent stale requests from previous reruns.
|
|
75
|
+
*/
|
|
76
|
+
runId?: string;
|
|
50
77
|
/**
|
|
51
78
|
* Base URL for runner (iframe) pages.
|
|
52
79
|
*/
|
|
@@ -96,46 +123,54 @@ export type BrowserClientMessage =
|
|
|
96
123
|
// Collect mode messages
|
|
97
124
|
| {
|
|
98
125
|
type: 'collect-result';
|
|
99
|
-
payload: { testPath: string; project: string; tests:
|
|
126
|
+
payload: { testPath: string; project: string; tests: TestInfo[] };
|
|
100
127
|
}
|
|
101
128
|
| { type: 'collect-complete' }
|
|
102
|
-
//
|
|
129
|
+
// Unified RPC envelope for all runner -> container/host capability calls.
|
|
130
|
+
// Snapshot already uses this path via namespace "snapshot". Future PR #948
|
|
131
|
+
// capabilities can add new namespaces instead of adding new message types.
|
|
103
132
|
| {
|
|
104
|
-
type:
|
|
105
|
-
payload:
|
|
133
|
+
type: typeof DISPATCH_RPC_REQUEST_TYPE;
|
|
134
|
+
payload: BrowserDispatchRequest;
|
|
106
135
|
};
|
|
107
136
|
|
|
108
137
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
138
|
+
* Transport-agnostic envelope used by host routing.
|
|
139
|
+
* `namespace + method + args + target` describes an operation independent of
|
|
140
|
+
* the underlying message channel, and `runToken` provides run-level isolation.
|
|
111
141
|
*/
|
|
112
|
-
export type
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
args: { filepath: string; content: string };
|
|
127
|
-
}
|
|
128
|
-
| {
|
|
129
|
-
id: string;
|
|
130
|
-
method: 'removeSnapshotFile';
|
|
131
|
-
args: { filepath: string };
|
|
132
|
-
};
|
|
142
|
+
export type BrowserDispatchRequest = {
|
|
143
|
+
requestId: string;
|
|
144
|
+
// Optional so headed/container paths can adopt the same envelope even when
|
|
145
|
+
// run-token isolation is only enforced in headless scheduling today.
|
|
146
|
+
runToken?: number;
|
|
147
|
+
namespace: string;
|
|
148
|
+
method: string;
|
|
149
|
+
args?: unknown;
|
|
150
|
+
target?: {
|
|
151
|
+
testFile?: string;
|
|
152
|
+
sessionId?: string;
|
|
153
|
+
projectName?: string;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
133
156
|
|
|
134
157
|
/**
|
|
135
|
-
*
|
|
158
|
+
* Dispatch response envelope.
|
|
159
|
+
* `stale: true` signals a safe drop from an older run generation, not a failure.
|
|
136
160
|
*/
|
|
137
|
-
export type
|
|
138
|
-
|
|
161
|
+
export type BrowserDispatchResponse = {
|
|
162
|
+
requestId: string;
|
|
163
|
+
runToken?: number;
|
|
139
164
|
result?: unknown;
|
|
140
165
|
error?: string;
|
|
166
|
+
stale?: boolean;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export type BrowserDispatchResponseEnvelope = {
|
|
170
|
+
type: typeof DISPATCH_RESPONSE_TYPE;
|
|
171
|
+
payload: BrowserDispatchResponse;
|
|
141
172
|
};
|
|
173
|
+
|
|
174
|
+
export type BrowserDispatchHandler = (
|
|
175
|
+
request: BrowserDispatchRequest,
|
|
176
|
+
) => Promise<unknown>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { BrowserRpcRequest } from '../rpcProtocol';
|
|
2
|
+
import { playwrightProviderImplementation } from './playwright';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Browser provider contract hub.
|
|
6
|
+
*
|
|
7
|
+
* When adding a new built-in provider, implement `BrowserProviderImplementation`
|
|
8
|
+
* and register it in `providerImplementations` below.
|
|
9
|
+
*/
|
|
10
|
+
export type BrowserProvider = 'playwright';
|
|
11
|
+
|
|
12
|
+
/** Minimal console shape needed by host logging bridge. */
|
|
13
|
+
export type BrowserConsoleMessage = {
|
|
14
|
+
text: () => string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Minimal page API surface required by hostController.
|
|
19
|
+
*
|
|
20
|
+
* This is a structural type (shape interface), NOT a direct Playwright import.
|
|
21
|
+
* It currently mirrors a subset of Playwright's Page API because that is the
|
|
22
|
+
* only provider. When adding a second provider whose page primitive diverges
|
|
23
|
+
* (e.g. WebDriver BiDi), consider pushing page-level orchestration (goto,
|
|
24
|
+
* exposeFunction, addInitScript, event listeners) into provider-specific
|
|
25
|
+
* implementations so hostController only calls high-level semantic methods.
|
|
26
|
+
*/
|
|
27
|
+
export type BrowserProviderPage = {
|
|
28
|
+
goto: (url: string, options?: { waitUntil?: 'load' }) => Promise<unknown>;
|
|
29
|
+
exposeFunction: (name: string, fn: (...args: any[]) => any) => Promise<void>;
|
|
30
|
+
addInitScript: (script: string) => Promise<void>;
|
|
31
|
+
on: {
|
|
32
|
+
(event: 'popup', listener: (page: BrowserProviderPage) => void): void;
|
|
33
|
+
(
|
|
34
|
+
event: 'console',
|
|
35
|
+
listener: (message: BrowserConsoleMessage) => void,
|
|
36
|
+
): void;
|
|
37
|
+
};
|
|
38
|
+
close: () => Promise<void>;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** Minimal browser context API surface required by hostController. */
|
|
42
|
+
export type BrowserProviderContext = {
|
|
43
|
+
newPage: () => Promise<BrowserProviderPage>;
|
|
44
|
+
on: (event: 'page', listener: (page: BrowserProviderPage) => void) => void;
|
|
45
|
+
close: () => Promise<void>;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Minimal browser API surface required by hostController. */
|
|
49
|
+
export type BrowserProviderBrowser = {
|
|
50
|
+
close: () => Promise<void>;
|
|
51
|
+
newContext: (options: {
|
|
52
|
+
viewport: { width: number; height: number } | null;
|
|
53
|
+
}) => Promise<BrowserProviderContext>;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Provider launch result consumed by hostController. */
|
|
57
|
+
export type BrowserProviderRuntime = {
|
|
58
|
+
browser: BrowserProviderBrowser;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** Input contract for browser launch. */
|
|
62
|
+
export type LaunchBrowserInput = {
|
|
63
|
+
browserName: 'chromium' | 'firefox' | 'webkit';
|
|
64
|
+
headless: boolean | undefined;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/** Input contract for provider-side browser RPC dispatch. */
|
|
68
|
+
export type DispatchBrowserRpcInput = {
|
|
69
|
+
containerPage?: BrowserProviderPage;
|
|
70
|
+
runnerPage?: BrowserProviderPage;
|
|
71
|
+
request: BrowserRpcRequest;
|
|
72
|
+
timeoutFallbackMs: number;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Core provider implementation contract.
|
|
77
|
+
*
|
|
78
|
+
* Any new built-in provider must:
|
|
79
|
+
* - launch browser runtime for test execution
|
|
80
|
+
* - execute browser RPC requests (locator actions + assertions)
|
|
81
|
+
*/
|
|
82
|
+
export type BrowserProviderImplementation = {
|
|
83
|
+
name: BrowserProvider;
|
|
84
|
+
launchRuntime: (input: LaunchBrowserInput) => Promise<BrowserProviderRuntime>;
|
|
85
|
+
dispatchRpc: (input: DispatchBrowserRpcInput) => Promise<unknown>;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const providerImplementations: Record<
|
|
89
|
+
BrowserProvider,
|
|
90
|
+
BrowserProviderImplementation
|
|
91
|
+
> = {
|
|
92
|
+
playwright: playwrightProviderImplementation,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export function getBrowserProviderImplementation(
|
|
96
|
+
provider: BrowserProvider,
|
|
97
|
+
): BrowserProviderImplementation {
|
|
98
|
+
const implementation = providerImplementations[provider];
|
|
99
|
+
if (!implementation) {
|
|
100
|
+
throw new Error(`Unsupported browser provider: ${String(provider)}`);
|
|
101
|
+
}
|
|
102
|
+
return implementation;
|
|
103
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { FrameLocator, Locator, Page } from 'playwright';
|
|
2
|
+
import type { BrowserLocatorIR } from '../../protocol';
|
|
3
|
+
import { reviveBrowserLocatorText } from './textMatcher';
|
|
4
|
+
|
|
5
|
+
export const compilePlaywrightLocator = (
|
|
6
|
+
frame: FrameLocator | Page,
|
|
7
|
+
locatorIR: BrowserLocatorIR,
|
|
8
|
+
): Locator => {
|
|
9
|
+
const compileFromFrame = (ir: BrowserLocatorIR): Locator => {
|
|
10
|
+
let current: FrameLocator | Page | Locator = frame;
|
|
11
|
+
|
|
12
|
+
const ensureLocator = (): Locator => {
|
|
13
|
+
if ((current as any).filter) {
|
|
14
|
+
return current as Locator;
|
|
15
|
+
}
|
|
16
|
+
// Convert FrameLocator to a Locator within the frame.
|
|
17
|
+
current = (current as FrameLocator).locator(':root');
|
|
18
|
+
return current as Locator;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
for (const step of ir.steps as any[]) {
|
|
22
|
+
switch (step.type) {
|
|
23
|
+
case 'getByRole': {
|
|
24
|
+
const name = step.options?.name
|
|
25
|
+
? reviveBrowserLocatorText(step.options.name)
|
|
26
|
+
: undefined;
|
|
27
|
+
const options = step.options ? { ...step.options, name } : undefined;
|
|
28
|
+
current = (current as any).getByRole(step.role, options);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case 'locator':
|
|
32
|
+
current = (current as any).locator(step.selector);
|
|
33
|
+
break;
|
|
34
|
+
case 'getByText':
|
|
35
|
+
current = (current as any).getByText(
|
|
36
|
+
reviveBrowserLocatorText(step.text),
|
|
37
|
+
step.options,
|
|
38
|
+
);
|
|
39
|
+
break;
|
|
40
|
+
case 'getByLabel':
|
|
41
|
+
current = (current as any).getByLabel(
|
|
42
|
+
reviveBrowserLocatorText(step.text),
|
|
43
|
+
step.options,
|
|
44
|
+
);
|
|
45
|
+
break;
|
|
46
|
+
case 'getByPlaceholder':
|
|
47
|
+
current = (current as any).getByPlaceholder(
|
|
48
|
+
reviveBrowserLocatorText(step.text),
|
|
49
|
+
step.options,
|
|
50
|
+
);
|
|
51
|
+
break;
|
|
52
|
+
case 'getByAltText':
|
|
53
|
+
current = (current as any).getByAltText(
|
|
54
|
+
reviveBrowserLocatorText(step.text),
|
|
55
|
+
step.options,
|
|
56
|
+
);
|
|
57
|
+
break;
|
|
58
|
+
case 'getByTitle':
|
|
59
|
+
current = (current as any).getByTitle(
|
|
60
|
+
reviveBrowserLocatorText(step.text),
|
|
61
|
+
step.options,
|
|
62
|
+
);
|
|
63
|
+
break;
|
|
64
|
+
case 'getByTestId':
|
|
65
|
+
current = (current as any).getByTestId(
|
|
66
|
+
reviveBrowserLocatorText(step.text) as any,
|
|
67
|
+
);
|
|
68
|
+
break;
|
|
69
|
+
case 'filter': {
|
|
70
|
+
const locator = ensureLocator();
|
|
71
|
+
const options: {
|
|
72
|
+
hasText?: string | RegExp;
|
|
73
|
+
hasNotText?: string | RegExp;
|
|
74
|
+
has?: Locator;
|
|
75
|
+
hasNot?: Locator;
|
|
76
|
+
} = {};
|
|
77
|
+
if (step.options?.hasText) {
|
|
78
|
+
options.hasText = reviveBrowserLocatorText(step.options.hasText);
|
|
79
|
+
}
|
|
80
|
+
if (step.options?.hasNotText) {
|
|
81
|
+
options.hasNotText = reviveBrowserLocatorText(
|
|
82
|
+
step.options.hasNotText,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (step.options?.has) {
|
|
86
|
+
options.has = compileFromFrame(step.options.has);
|
|
87
|
+
}
|
|
88
|
+
if (step.options?.hasNot) {
|
|
89
|
+
options.hasNot = compileFromFrame(step.options.hasNot);
|
|
90
|
+
}
|
|
91
|
+
current = locator.filter(options);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case 'and': {
|
|
95
|
+
const locator = ensureLocator();
|
|
96
|
+
const other = compileFromFrame(step.locator);
|
|
97
|
+
current = locator.and(other);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
case 'or': {
|
|
101
|
+
const locator = ensureLocator();
|
|
102
|
+
const other = compileFromFrame(step.locator);
|
|
103
|
+
current = locator.or(other);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
case 'nth': {
|
|
107
|
+
const locator = ensureLocator();
|
|
108
|
+
current = locator.nth(step.index);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case 'first': {
|
|
112
|
+
const locator = ensureLocator();
|
|
113
|
+
current = locator.first();
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case 'last': {
|
|
117
|
+
const locator = ensureLocator();
|
|
118
|
+
current = locator.last();
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
default:
|
|
122
|
+
throw new Error(`Unknown locator step: ${String(step?.type)}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return ensureLocator();
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return compileFromFrame(locatorIR);
|
|
130
|
+
};
|