@vitest/browser 2.0.0-beta.10 → 2.0.0-beta.12

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.
@@ -1,18 +1,18 @@
1
- const moduleCache = new Map()
1
+ const moduleCache = new Map();
2
2
 
3
3
  function wrapModule(module) {
4
- if (typeof module === 'function') {
4
+ if (typeof module === "function") {
5
5
  const promise = new Promise((resolve, reject) => {
6
- if (typeof __vitest_mocker__ === 'undefined')
7
- return module().then(resolve, reject)
6
+ if (typeof __vitest_mocker__ === "undefined")
7
+ return module().then(resolve, reject);
8
8
  __vitest_mocker__.prepare().finally(() => {
9
- module().then(resolve, reject)
10
- })
11
- })
12
- moduleCache.set(promise, { promise, evaluated: false })
13
- return promise.finally(() => moduleCache.delete(promise))
9
+ module().then(resolve, reject);
10
+ });
11
+ });
12
+ moduleCache.set(promise, { promise, evaluated: false });
13
+ return promise.finally(() => moduleCache.delete(promise));
14
14
  }
15
- return module
15
+ return module;
16
16
  }
17
17
 
18
18
  window.__vitest_browser_runner__ = {
@@ -23,25 +23,26 @@ window.__vitest_browser_runner__ = {
23
23
  files: { __VITEST_FILES__ },
24
24
  type: { __VITEST_TYPE__ },
25
25
  contextId: { __VITEST_CONTEXT_ID__ },
26
- }
26
+ provider: { __VITEST_PROVIDER__ },
27
+ providedContext: { __VITEST_PROVIDED_CONTEXT__ },
28
+ };
27
29
 
28
- const config = __vitest_browser_runner__.config
30
+ const config = __vitest_browser_runner__.config;
29
31
 
30
32
  if (config.testNamePattern)
31
- config.testNamePattern = parseRegexp(config.testNamePattern)
33
+ config.testNamePattern = parseRegexp(config.testNamePattern);
32
34
 
33
35
  function parseRegexp(input) {
34
36
  // Parse input
35
- const m = input.match(/(\/?)(.+)\1([a-z]*)/i)
37
+ const m = input.match(/(\/?)(.+)\1([a-z]*)/i);
36
38
 
37
39
  // match nothing
38
- if (!m)
39
- return /$^/
40
+ if (!m) return /$^/;
40
41
 
41
42
  // Invalid flags
42
43
  if (m[3] && !/^(?!.*?(.).*?\1)[gmixXsuUAJ]+$/.test(m[3]))
43
- return RegExp(input)
44
+ return RegExp(input);
44
45
 
45
46
  // Create the regular expression
46
- return new RegExp(m[2], m[3])
47
+ return new RegExp(m[2], m[3]);
47
48
  }
@@ -25,8 +25,8 @@
25
25
  </style>
26
26
  <script>{__VITEST_INJECTOR__}</script>
27
27
  {__VITEST_SCRIPTS__}
28
- <script type="module" crossorigin src="/__vitest_browser__/orchestrator-BCpOi5ot.js"></script>
29
- <link rel="modulepreload" crossorigin href="/__vitest_browser__/rpc-CImfI7bO.js">
28
+ <script type="module" crossorigin src="/__vitest_browser__/orchestrator-DJ6H4qlM.js"></script>
29
+ <link rel="modulepreload" crossorigin href="/__vitest_browser__/client-Dz5Ebwug.js">
30
30
  </head>
31
31
  <body>
32
32
  <div id="vitest-tester"></div>
@@ -17,11 +17,20 @@
17
17
  }
18
18
  </style>
19
19
  <script>{__VITEST_INJECTOR__}</script>
20
+ <script>{__VITEST_STATE__}</script>
20
21
  {__VITEST_SCRIPTS__}
21
- <script type="module" crossorigin src="/__vitest_browser__/tester-e70VCOgC.js"></script>
22
- <link rel="modulepreload" crossorigin href="/__vitest_browser__/rpc-CImfI7bO.js">
22
+ <script type="module" crossorigin src="/__vitest_browser__/tester-DHXll_4H.js"></script>
23
+ <link rel="modulepreload" crossorigin href="/__vitest_browser__/client-Dz5Ebwug.js">
23
24
  </head>
24
- <body style="width: 100%; height: 100%; transform: scale(1); transform-origin: left top;">
25
+ <body
26
+ data-vitest-body
27
+ style="
28
+ width: 100%;
29
+ height: 100%;
30
+ transform: scale(1);
31
+ transform-origin: left top;
32
+ "
33
+ >
25
34
  {__VITEST_APPEND__}
26
35
  </body>
27
36
  </html>
package/dist/context.js CHANGED
@@ -1,39 +1,133 @@
1
1
  function convertElementToXPath(element) {
2
- if (!element || !(element instanceof Element))
3
- throw new Error(`Expected DOM element to be an instance of Element, received ${typeof element}`);
2
+ if (!element || !(element instanceof Element)) {
3
+ throw new Error(
4
+ `Expected DOM element to be an instance of Element, received ${typeof element}`
5
+ );
6
+ }
4
7
  return getPathTo(element);
5
8
  }
6
9
  function getPathTo(element) {
7
- if (element.id !== "")
10
+ if (element.id !== "") {
8
11
  return `id("${element.id}")`;
9
- if (!element.parentNode || element === document.documentElement)
12
+ }
13
+ if (!element.parentNode || element === document.documentElement) {
10
14
  return element.tagName;
15
+ }
11
16
  let ix = 0;
12
17
  const siblings = element.parentNode.childNodes;
13
18
  for (let i = 0; i < siblings.length; i++) {
14
19
  const sibling = siblings[i];
15
- if (sibling === element)
20
+ if (sibling === element) {
16
21
  return `${getPathTo(element.parentNode)}/${element.tagName}[${ix + 1}]`;
17
- if (sibling.nodeType === 1 && sibling.tagName === element.tagName)
22
+ }
23
+ if (sibling.nodeType === 1 && sibling.tagName === element.tagName) {
18
24
  ix++;
25
+ }
19
26
  }
20
27
  return "invalid xpath";
21
28
  }
22
29
  const state = () => __vitest_worker__;
23
30
  const runner = () => __vitest_browser_runner__;
24
- const filepath = () => state().filepath || state().current?.file?.filepath || void 0;
31
+ function filepath() {
32
+ return state().filepath || state().current?.file?.filepath || void 0;
33
+ }
25
34
  const rpc = () => state().rpc;
26
35
  const contextId = runner().contextId;
27
36
  const channel = new BroadcastChannel(`vitest:${contextId}`);
28
37
  function triggerCommand(command, ...args) {
29
38
  return rpc().triggerCommand(contextId, command, filepath(), args);
30
39
  }
40
+ const provider = runner().provider;
31
41
  const userEvent = {
42
+ // TODO: actually setup userEvent with config options
43
+ setup() {
44
+ return userEvent;
45
+ },
32
46
  click(element, options = {}) {
33
47
  const xpath = convertElementToXPath(element);
34
48
  return triggerCommand("__vitest_click", xpath, options);
49
+ },
50
+ dblClick(element, options = {}) {
51
+ const xpath = convertElementToXPath(element);
52
+ return triggerCommand("__vitest_dblClick", xpath, options);
53
+ },
54
+ selectOptions(element, value) {
55
+ const values = provider === "webdriverio" ? getWebdriverioSelectOptions(element, value) : getSimpleSelectOptions(element, value);
56
+ return triggerCommand("__vitest_selectOptions", convertElementToXPath(element), values);
57
+ },
58
+ type(element, text, options = {}) {
59
+ const xpath = convertElementToXPath(element);
60
+ return triggerCommand("__vitest_type", xpath, text, options);
61
+ },
62
+ clear(element) {
63
+ const xpath = convertElementToXPath(element);
64
+ return triggerCommand("__vitest_clear", xpath);
65
+ },
66
+ tab(options = {}) {
67
+ return triggerCommand("__vitest_tab", options);
68
+ },
69
+ keyboard(text) {
70
+ return triggerCommand("__vitest_keyboard", text);
71
+ },
72
+ hover(element) {
73
+ const xpath = convertElementToXPath(element);
74
+ return triggerCommand("__vitest_hover", xpath);
75
+ },
76
+ unhover(element) {
77
+ const xpath = convertElementToXPath(element.ownerDocument.body);
78
+ return triggerCommand("__vitest_hover", xpath);
79
+ },
80
+ // non userEvent events, but still useful
81
+ fill(element, text, options) {
82
+ const xpath = convertElementToXPath(element);
83
+ return triggerCommand("__vitest_fill", xpath, text, options);
84
+ },
85
+ dragAndDrop(source, target, options = {}) {
86
+ const sourceXpath = convertElementToXPath(source);
87
+ const targetXpath = convertElementToXPath(target);
88
+ return triggerCommand("__vitest_dragAndDrop", sourceXpath, targetXpath, options);
35
89
  }
36
90
  };
91
+ function getWebdriverioSelectOptions(element, value) {
92
+ const options = [...element.querySelectorAll("option")];
93
+ const arrayValues = Array.isArray(value) ? value : [value];
94
+ if (!arrayValues.length) {
95
+ return [];
96
+ }
97
+ if (arrayValues.length > 1) {
98
+ throw new Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);
99
+ }
100
+ const optionValue = arrayValues[0];
101
+ if (typeof optionValue !== "string") {
102
+ const index = options.indexOf(optionValue);
103
+ if (index === -1) {
104
+ throw new Error(`The element ${convertElementToXPath(optionValue)} was not found in the "select" options.`);
105
+ }
106
+ return [{ index }];
107
+ }
108
+ const valueIndex = options.findIndex((option) => option.value === optionValue);
109
+ if (valueIndex !== -1) {
110
+ return [{ index: valueIndex }];
111
+ }
112
+ const labelIndex = options.findIndex(
113
+ (option) => option.textContent?.trim() === optionValue || option.ariaLabel === optionValue
114
+ );
115
+ if (labelIndex === -1) {
116
+ throw new Error(`The option "${optionValue}" was not found in the "select" options.`);
117
+ }
118
+ return [{ index: labelIndex }];
119
+ }
120
+ function getSimpleSelectOptions(element, value) {
121
+ return (Array.isArray(value) ? value : [value]).map((v) => {
122
+ if (typeof v !== "string") {
123
+ return { element: convertElementToXPath(v) };
124
+ }
125
+ return v;
126
+ });
127
+ }
128
+ function cdp() {
129
+ return runner().cdp;
130
+ }
37
131
  const screenshotIds = {};
38
132
  const page = {
39
133
  get config() {
@@ -57,8 +151,9 @@ const page = {
57
151
  },
58
152
  async screenshot(options = {}) {
59
153
  const currentTest = state().current;
60
- if (!currentTest)
154
+ if (!currentTest) {
61
155
  throw new Error("Cannot take a screenshot outside of a test.");
156
+ }
62
157
  if (currentTest.concurrent) {
63
158
  throw new Error(
64
159
  "Cannot take a screenshot in a concurrent test because concurrent tests run at the same time in the same iframe and affect each other's environment. Use a non-concurrent test to take a screenshot."
@@ -70,18 +165,14 @@ const page = {
70
165
  screenshotIds[repeatCount] ??= {};
71
166
  screenshotIds[repeatCount][taskName] = number + 1;
72
167
  const name = options.path || `${taskName.replace(/[^a-z0-9]/g, "-")}-${number}.png`;
73
- return triggerCommand(
74
- "__vitest_screenshot",
75
- name,
76
- {
77
- ...options,
78
- element: options.element ? convertElementToXPath(options.element) : void 0
79
- }
80
- );
168
+ return triggerCommand("__vitest_screenshot", name, {
169
+ ...options,
170
+ element: options.element ? convertElementToXPath(options.element) : void 0
171
+ });
81
172
  }
82
173
  };
83
174
  function getTaskFullName(task) {
84
175
  return task.suite ? `${getTaskFullName(task.suite)} ${task.name}` : task.name;
85
176
  }
86
177
 
87
- export { page, userEvent };
178
+ export { cdp, page, userEvent };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,124 @@
1
- import { BrowserCommand, WorkspaceProject } from 'vitest/node';
2
- export { BrowserCommand } from 'vitest/node';
1
+ import { CDPSession, BrowserServerState as BrowserServerState$1, BrowserServerStateContext, BrowserServer as BrowserServer$1, WorkspaceProject, Vite, BrowserProvider, BrowserScript, Vitest, ProcessPool } from 'vitest/node';
3
2
  import { Plugin } from 'vitest/config';
3
+ import { File, TaskResultPack, AfterSuiteRunMeta, CancelReason, UserConsoleLog, SnapshotResult, ResolvedConfig } from 'vitest';
4
4
 
5
- declare function defineBrowserCommand<T extends unknown[]>(fn: BrowserCommand<T>): BrowserCommand<T>;
5
+ type ArgumentsType<T> = T extends (...args: infer A) => any ? A : never;
6
+ type ReturnType<T> = T extends (...args: any) => infer R ? R : never;
7
+ type PromisifyFn<T> = ReturnType<T> extends Promise<any> ? T : (...args: ArgumentsType<T>) => Promise<Awaited<ReturnType<T>>>;
8
+ type BirpcFn<T> = PromisifyFn<T> & {
9
+ /**
10
+ * Send event without asking for response
11
+ */
12
+ asEvent: (...args: ArgumentsType<T>) => void;
13
+ };
14
+ type BirpcReturn<RemoteFunctions, LocalFunctions = Record<string, never>> = {
15
+ [K in keyof RemoteFunctions]: BirpcFn<RemoteFunctions[K]>;
16
+ } & {
17
+ $functions: LocalFunctions;
18
+ };
6
19
 
7
- declare const _default: (project: WorkspaceProject, base?: string) => Plugin[];
20
+ interface WebSocketBrowserHandlers {
21
+ resolveSnapshotPath: (testPath: string) => string;
22
+ resolveSnapshotRawPath: (testPath: string, rawPath: string) => string;
23
+ onUnhandledError: (error: unknown, type: string) => Promise<void>;
24
+ onCollected: (files?: File[]) => Promise<void>;
25
+ onTaskUpdate: (packs: TaskResultPack[]) => void;
26
+ onAfterSuiteRun: (meta: AfterSuiteRunMeta) => void;
27
+ onCancel: (reason: CancelReason) => void;
28
+ getCountOfFailedTests: () => number;
29
+ readSnapshotFile: (id: string) => Promise<string | null>;
30
+ saveSnapshotFile: (id: string, content: string) => Promise<void>;
31
+ removeSnapshotFile: (id: string) => Promise<void>;
32
+ sendLog: (log: UserConsoleLog) => void;
33
+ finishBrowserTests: (contextId: string) => void;
34
+ snapshotSaved: (snapshot: SnapshotResult) => void;
35
+ debug: (...args: string[]) => void;
36
+ resolveId: (id: string, importer?: string) => Promise<{
37
+ id: string;
38
+ } | null>;
39
+ triggerCommand: <T>(contextId: string, command: string, testPath: string | undefined, payload: unknown[]) => Promise<T>;
40
+ resolveMock: (id: string, importer: string, hasFactory: boolean) => Promise<{
41
+ type: 'factory' | 'redirect' | 'automock';
42
+ mockPath?: string | null;
43
+ resolvedId: string;
44
+ }>;
45
+ invalidate: (ids: string[]) => void;
46
+ getBrowserFileSourceMap: (id: string) => SourceMap | null | {
47
+ mappings: '';
48
+ } | undefined;
49
+ sendCdpEvent: (contextId: string, event: string, payload?: Record<string, unknown>) => unknown;
50
+ trackCdpEvent: (contextId: string, type: 'on' | 'once' | 'off', event: string, listenerId: string) => void;
51
+ }
52
+ interface WebSocketBrowserEvents {
53
+ onCancel: (reason: CancelReason) => void;
54
+ createTesters: (files: string[]) => Promise<void>;
55
+ cdpEvent: (event: string, payload: unknown) => void;
56
+ }
57
+ type WebSocketBrowserRPC = BirpcReturn<WebSocketBrowserEvents, WebSocketBrowserHandlers>;
58
+ interface SourceMap {
59
+ file: string;
60
+ mappings: string;
61
+ names: string[];
62
+ sources: string[];
63
+ sourcesContent?: string[];
64
+ version: number;
65
+ toString: () => string;
66
+ toUrl: () => string;
67
+ }
8
68
 
9
- export { _default as default, defineBrowserCommand };
69
+ declare class BrowserServerCDPHandler {
70
+ private session;
71
+ private tester;
72
+ private listenerIds;
73
+ private listeners;
74
+ constructor(session: CDPSession, tester: WebSocketBrowserRPC);
75
+ send(method: string, params?: Record<string, unknown>): Promise<unknown>;
76
+ on(event: string, id: string, once?: boolean): void;
77
+ off(event: string, id: string): void;
78
+ once(event: string, listener: string): void;
79
+ }
80
+
81
+ declare class BrowserServerState implements BrowserServerState$1 {
82
+ readonly orchestrators: Map<string, WebSocketBrowserRPC>;
83
+ readonly testers: Map<string, WebSocketBrowserRPC>;
84
+ readonly cdps: Map<string, BrowserServerCDPHandler>;
85
+ private contexts;
86
+ getContext(contextId: string): BrowserServerStateContext | undefined;
87
+ createAsyncContext(contextId: string, files: string[]): Promise<void>;
88
+ removeCDPHandler(sessionId: string): Promise<void>;
89
+ }
90
+
91
+ declare class BrowserServer implements BrowserServer$1 {
92
+ project: WorkspaceProject;
93
+ base: string;
94
+ faviconUrl: string;
95
+ prefixTesterUrl: string;
96
+ orchestratorScripts: string | undefined;
97
+ testerScripts: string | undefined;
98
+ manifest: Promise<Vite.Manifest> | Vite.Manifest;
99
+ testerHtml: Promise<string> | string;
100
+ orchestratorHtml: Promise<string> | string;
101
+ injectorJs: Promise<string> | string;
102
+ stateJs: Promise<string> | string;
103
+ state: BrowserServerState;
104
+ provider: BrowserProvider;
105
+ vite: Vite.ViteDevServer;
106
+ constructor(project: WorkspaceProject, base: string);
107
+ setServer(server: Vite.ViteDevServer): void;
108
+ getSerializableConfig(): ResolvedConfig;
109
+ resolveTesterUrl(pathname: string): {
110
+ contextId: string;
111
+ testFile: string;
112
+ };
113
+ formatScripts(scripts: BrowserScript[] | undefined): Promise<string>;
114
+ initBrowserProvider(): Promise<void>;
115
+ private cdpSessions;
116
+ ensureCDPHandler(contextId: string, sessionId: string): Promise<BrowserServerCDPHandler>;
117
+ close(): Promise<void>;
118
+ }
119
+
120
+ declare function createBrowserPool(ctx: Vitest): ProcessPool;
121
+
122
+ declare function createBrowserServer(project: WorkspaceProject, configFile: string | undefined, prePlugins?: Plugin[], postPlugins?: Plugin[]): Promise<BrowserServer>;
123
+
124
+ export { BrowserServer, createBrowserPool, createBrowserServer };