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

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-DQ4hbmZ_.js"></script>
29
+ <link rel="modulepreload" crossorigin href="/__vitest_browser__/rpc-D6HtJ5Rb.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-IF8AbWCS.js"></script>
23
+ <link rel="modulepreload" crossorigin href="/__vitest_browser__/rpc-D6HtJ5Rb.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,130 @@
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
+ }
37
128
  const screenshotIds = {};
38
129
  const page = {
39
130
  get config() {
@@ -57,8 +148,9 @@ const page = {
57
148
  },
58
149
  async screenshot(options = {}) {
59
150
  const currentTest = state().current;
60
- if (!currentTest)
151
+ if (!currentTest) {
61
152
  throw new Error("Cannot take a screenshot outside of a test.");
153
+ }
62
154
  if (currentTest.concurrent) {
63
155
  throw new Error(
64
156
  "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,14 +162,10 @@ const page = {
70
162
  screenshotIds[repeatCount] ??= {};
71
163
  screenshotIds[repeatCount][taskName] = number + 1;
72
164
  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
- );
165
+ return triggerCommand("__vitest_screenshot", name, {
166
+ ...options,
167
+ element: options.element ? convertElementToXPath(options.element) : void 0
168
+ });
81
169
  }
82
170
  };
83
171
  function getTaskFullName(task) {
package/dist/index.d.ts CHANGED
@@ -1,9 +1,105 @@
1
- import { BrowserCommand, WorkspaceProject } from 'vitest/node';
2
- export { BrowserCommand } from 'vitest/node';
1
+ import { 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
+ }
50
+ interface WebSocketBrowserEvents {
51
+ onCancel: (reason: CancelReason) => void;
52
+ createTesters: (files: string[]) => Promise<void>;
53
+ }
54
+ type WebSocketBrowserRPC = BirpcReturn<WebSocketBrowserEvents, WebSocketBrowserHandlers>;
55
+ interface SourceMap {
56
+ file: string;
57
+ mappings: string;
58
+ names: string[];
59
+ sources: string[];
60
+ sourcesContent?: string[];
61
+ version: number;
62
+ toString: () => string;
63
+ toUrl: () => string;
64
+ }
8
65
 
9
- export { _default as default, defineBrowserCommand };
66
+ declare class BrowserServerState implements BrowserServerState$1 {
67
+ orchestrators: Map<string, WebSocketBrowserRPC>;
68
+ testers: Map<string, WebSocketBrowserRPC>;
69
+ private contexts;
70
+ getContext(contextId: string): BrowserServerStateContext | undefined;
71
+ createAsyncContext(contextId: string, files: string[]): Promise<void>;
72
+ }
73
+
74
+ declare class BrowserServer implements BrowserServer$1 {
75
+ project: WorkspaceProject;
76
+ base: string;
77
+ faviconUrl: string;
78
+ prefixTesterUrl: string;
79
+ orchestratorScripts: string | undefined;
80
+ testerScripts: string | undefined;
81
+ manifest: Promise<Vite.Manifest> | Vite.Manifest;
82
+ testerHtml: Promise<string> | string;
83
+ orchestratorHtml: Promise<string> | string;
84
+ injectorJs: Promise<string> | string;
85
+ stateJs: Promise<string> | string;
86
+ state: BrowserServerState;
87
+ provider: BrowserProvider;
88
+ vite: Vite.ViteDevServer;
89
+ constructor(project: WorkspaceProject, base: string);
90
+ setServer(server: Vite.ViteDevServer): void;
91
+ getSerializableConfig(): ResolvedConfig;
92
+ resolveTesterUrl(pathname: string): {
93
+ contextId: string;
94
+ testFile: string;
95
+ };
96
+ formatScripts(scripts: BrowserScript[] | undefined): Promise<string>;
97
+ initBrowserProvider(): Promise<void>;
98
+ close(): Promise<void>;
99
+ }
100
+
101
+ declare function createBrowserPool(ctx: Vitest): ProcessPool;
102
+
103
+ declare function createBrowserServer(project: WorkspaceProject, configFile: string | undefined, prePlugins?: Plugin[], postPlugins?: Plugin[]): Promise<BrowserServer>;
104
+
105
+ export { BrowserServer, createBrowserPool, createBrowserServer };