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

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.
@@ -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-DQ4hbmZ_.js"></script>
29
- <link rel="modulepreload" crossorigin href="/__vitest_browser__/rpc-D6HtJ5Rb.js">
28
+ <script type="module" crossorigin src="/__vitest_browser__/orchestrator-x0A1t8rC.js"></script>
29
+ <link rel="modulepreload" crossorigin href="/__vitest_browser__/client-dLyjuL0K.js">
30
30
  </head>
31
31
  <body>
32
32
  <div id="vitest-tester"></div>
@@ -19,8 +19,8 @@
19
19
  <script>{__VITEST_INJECTOR__}</script>
20
20
  <script>{__VITEST_STATE__}</script>
21
21
  {__VITEST_SCRIPTS__}
22
- <script type="module" crossorigin src="/__vitest_browser__/tester-IF8AbWCS.js"></script>
23
- <link rel="modulepreload" crossorigin href="/__vitest_browser__/rpc-D6HtJ5Rb.js">
22
+ <script type="module" crossorigin src="/__vitest_browser__/tester-BdcP5piS.js"></script>
23
+ <link rel="modulepreload" crossorigin href="/__vitest_browser__/client-dLyjuL0K.js">
24
24
  </head>
25
25
  <body
26
26
  data-vitest-body
package/dist/context.js CHANGED
@@ -1,31 +1,3 @@
1
- function convertElementToXPath(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
- }
7
- return getPathTo(element);
8
- }
9
- function getPathTo(element) {
10
- if (element.id !== "") {
11
- return `id("${element.id}")`;
12
- }
13
- if (!element.parentNode || element === document.documentElement) {
14
- return element.tagName;
15
- }
16
- let ix = 0;
17
- const siblings = element.parentNode.childNodes;
18
- for (let i = 0; i < siblings.length; i++) {
19
- const sibling = siblings[i];
20
- if (sibling === element) {
21
- return `${getPathTo(element.parentNode)}/${element.tagName}[${ix + 1}]`;
22
- }
23
- if (sibling.nodeType === 1 && sibling.tagName === element.tagName) {
24
- ix++;
25
- }
26
- }
27
- return "invalid xpath";
28
- }
29
1
  const state = () => __vitest_worker__;
30
2
  const runner = () => __vitest_browser_runner__;
31
3
  function filepath() {
@@ -38,30 +10,86 @@ function triggerCommand(command, ...args) {
38
10
  return rpc().triggerCommand(contextId, command, filepath(), args);
39
11
  }
40
12
  const provider = runner().provider;
13
+ function convertElementToCssSelector(element) {
14
+ if (!element || !(element instanceof Element)) {
15
+ throw new Error(
16
+ `Expected DOM element to be an instance of Element, received ${typeof element}`
17
+ );
18
+ }
19
+ return getUniqueCssSelector(element);
20
+ }
21
+ function getUniqueCssSelector(el) {
22
+ const path = [];
23
+ let parent;
24
+ let hasShadowRoot = false;
25
+ while (parent = getParent(el)) {
26
+ if (parent.shadowRoot) {
27
+ hasShadowRoot = true;
28
+ }
29
+ const tag = el.tagName;
30
+ if (el.id) {
31
+ path.push(`#${el.id}`);
32
+ } else if (!el.nextElementSibling && !el.previousElementSibling) {
33
+ path.push(tag);
34
+ } else {
35
+ let index = 0;
36
+ let sameTagSiblings = 0;
37
+ let elementIndex = 0;
38
+ for (const sibling of parent.children) {
39
+ index++;
40
+ if (sibling.tagName === tag) {
41
+ sameTagSiblings++;
42
+ }
43
+ if (sibling === el) {
44
+ elementIndex = index;
45
+ }
46
+ }
47
+ if (sameTagSiblings > 1) {
48
+ path.push(`${tag}:nth-child(${elementIndex})`);
49
+ } else {
50
+ path.push(tag);
51
+ }
52
+ }
53
+ el = parent;
54
+ }
55
+ return `${provider === "webdriverio" && hasShadowRoot ? ">>>" : ""}${path.reverse().join(" > ")}`.toLowerCase();
56
+ }
57
+ function getParent(el) {
58
+ const parent = el.parentNode;
59
+ if (parent instanceof ShadowRoot) {
60
+ return parent.host;
61
+ }
62
+ return parent;
63
+ }
41
64
  const userEvent = {
42
65
  // TODO: actually setup userEvent with config options
43
66
  setup() {
44
67
  return userEvent;
45
68
  },
46
69
  click(element, options = {}) {
47
- const xpath = convertElementToXPath(element);
48
- return triggerCommand("__vitest_click", xpath, options);
70
+ const css = convertElementToCssSelector(element);
71
+ return triggerCommand("__vitest_click", css, options);
49
72
  },
50
73
  dblClick(element, options = {}) {
51
- const xpath = convertElementToXPath(element);
52
- return triggerCommand("__vitest_dblClick", xpath, options);
74
+ const css = convertElementToCssSelector(element);
75
+ return triggerCommand("__vitest_dblClick", css, options);
76
+ },
77
+ tripleClick(element, options = {}) {
78
+ const css = convertElementToCssSelector(element);
79
+ return triggerCommand("__vitest_tripleClick", css, options);
53
80
  },
54
81
  selectOptions(element, value) {
55
82
  const values = provider === "webdriverio" ? getWebdriverioSelectOptions(element, value) : getSimpleSelectOptions(element, value);
56
- return triggerCommand("__vitest_selectOptions", convertElementToXPath(element), values);
83
+ const css = convertElementToCssSelector(element);
84
+ return triggerCommand("__vitest_selectOptions", css, values);
57
85
  },
58
86
  type(element, text, options = {}) {
59
- const xpath = convertElementToXPath(element);
60
- return triggerCommand("__vitest_type", xpath, text, options);
87
+ const css = convertElementToCssSelector(element);
88
+ return triggerCommand("__vitest_type", css, text, options);
61
89
  },
62
90
  clear(element) {
63
- const xpath = convertElementToXPath(element);
64
- return triggerCommand("__vitest_clear", xpath);
91
+ const css = convertElementToCssSelector(element);
92
+ return triggerCommand("__vitest_clear", css);
65
93
  },
66
94
  tab(options = {}) {
67
95
  return triggerCommand("__vitest_tab", options);
@@ -70,22 +98,22 @@ const userEvent = {
70
98
  return triggerCommand("__vitest_keyboard", text);
71
99
  },
72
100
  hover(element) {
73
- const xpath = convertElementToXPath(element);
74
- return triggerCommand("__vitest_hover", xpath);
101
+ const css = convertElementToCssSelector(element);
102
+ return triggerCommand("__vitest_hover", css);
75
103
  },
76
104
  unhover(element) {
77
- const xpath = convertElementToXPath(element.ownerDocument.body);
78
- return triggerCommand("__vitest_hover", xpath);
105
+ const css = convertElementToCssSelector(element.ownerDocument.body);
106
+ return triggerCommand("__vitest_hover", css);
79
107
  },
80
108
  // non userEvent events, but still useful
81
109
  fill(element, text, options) {
82
- const xpath = convertElementToXPath(element);
83
- return triggerCommand("__vitest_fill", xpath, text, options);
110
+ const css = convertElementToCssSelector(element);
111
+ return triggerCommand("__vitest_fill", css, text, options);
84
112
  },
85
113
  dragAndDrop(source, target, options = {}) {
86
- const sourceXpath = convertElementToXPath(source);
87
- const targetXpath = convertElementToXPath(target);
88
- return triggerCommand("__vitest_dragAndDrop", sourceXpath, targetXpath, options);
114
+ const sourceCss = convertElementToCssSelector(source);
115
+ const targetCss = convertElementToCssSelector(target);
116
+ return triggerCommand("__vitest_dragAndDrop", sourceCss, targetCss, options);
89
117
  }
90
118
  };
91
119
  function getWebdriverioSelectOptions(element, value) {
@@ -101,7 +129,7 @@ function getWebdriverioSelectOptions(element, value) {
101
129
  if (typeof optionValue !== "string") {
102
130
  const index = options.indexOf(optionValue);
103
131
  if (index === -1) {
104
- throw new Error(`The element ${convertElementToXPath(optionValue)} was not found in the "select" options.`);
132
+ throw new Error(`The element ${convertElementToCssSelector(optionValue)} was not found in the "select" options.`);
105
133
  }
106
134
  return [{ index }];
107
135
  }
@@ -120,11 +148,14 @@ function getWebdriverioSelectOptions(element, value) {
120
148
  function getSimpleSelectOptions(element, value) {
121
149
  return (Array.isArray(value) ? value : [value]).map((v) => {
122
150
  if (typeof v !== "string") {
123
- return { element: convertElementToXPath(v) };
151
+ return { element: convertElementToCssSelector(v) };
124
152
  }
125
153
  return v;
126
154
  });
127
155
  }
156
+ function cdp() {
157
+ return runner().cdp;
158
+ }
128
159
  const screenshotIds = {};
129
160
  const page = {
130
161
  get config() {
@@ -161,10 +192,10 @@ const page = {
161
192
  const number = screenshotIds[repeatCount]?.[taskName] ?? 1;
162
193
  screenshotIds[repeatCount] ??= {};
163
194
  screenshotIds[repeatCount][taskName] = number + 1;
164
- const name = options.path || `${taskName.replace(/[^a-z0-9]/g, "-")}-${number}.png`;
195
+ const name = options.path || `${taskName.replace(/[^a-z0-9]/gi, "-")}-${number}.png`;
165
196
  return triggerCommand("__vitest_screenshot", name, {
166
197
  ...options,
167
- element: options.element ? convertElementToXPath(options.element) : void 0
198
+ element: options.element ? convertElementToCssSelector(options.element) : void 0
168
199
  });
169
200
  }
170
201
  };
@@ -172,4 +203,4 @@ function getTaskFullName(task) {
172
203
  return task.suite ? `${getTaskFullName(task.suite)} ${task.name}` : task.name;
173
204
  }
174
205
 
175
- export { page, userEvent };
206
+ export { cdp, page, userEvent };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
- import { BrowserServerState as BrowserServerState$1, BrowserServerStateContext, BrowserServer as BrowserServer$1, WorkspaceProject, Vite, BrowserProvider, BrowserScript, Vitest, ProcessPool } from 'vitest/node';
1
+ import { CDPSession, BrowserServerState as BrowserServerState$1, BrowserServerStateContext, BrowserServer as BrowserServer$1, WorkspaceProject, Vite, BrowserProvider, BrowserScript, Vitest, ProcessPool } from 'vitest/node';
2
2
  import { Plugin } from 'vitest/config';
3
+ import * as vitest from 'vitest';
3
4
  import { File, TaskResultPack, AfterSuiteRunMeta, CancelReason, UserConsoleLog, SnapshotResult, ResolvedConfig } from 'vitest';
5
+ import { ErrorWithDiff } from '@vitest/utils';
6
+ import { StackTraceParserOptions } from '@vitest/utils/source-map';
4
7
 
5
8
  type ArgumentsType<T> = T extends (...args: infer A) => any ? A : never;
6
9
  type ReturnType<T> = T extends (...args: any) => infer R ? R : never;
@@ -41,15 +44,19 @@ interface WebSocketBrowserHandlers {
41
44
  type: 'factory' | 'redirect' | 'automock';
42
45
  mockPath?: string | null;
43
46
  resolvedId: string;
47
+ needsInterop?: boolean;
44
48
  }>;
45
49
  invalidate: (ids: string[]) => void;
46
50
  getBrowserFileSourceMap: (id: string) => SourceMap | null | {
47
51
  mappings: '';
48
52
  } | undefined;
53
+ sendCdpEvent: (contextId: string, event: string, payload?: Record<string, unknown>) => unknown;
54
+ trackCdpEvent: (contextId: string, type: 'on' | 'once' | 'off', event: string, listenerId: string) => void;
49
55
  }
50
56
  interface WebSocketBrowserEvents {
51
57
  onCancel: (reason: CancelReason) => void;
52
58
  createTesters: (files: string[]) => Promise<void>;
59
+ cdpEvent: (event: string, payload: unknown) => void;
53
60
  }
54
61
  type WebSocketBrowserRPC = BirpcReturn<WebSocketBrowserEvents, WebSocketBrowserHandlers>;
55
62
  interface SourceMap {
@@ -63,12 +70,26 @@ interface SourceMap {
63
70
  toUrl: () => string;
64
71
  }
65
72
 
73
+ declare class BrowserServerCDPHandler {
74
+ private session;
75
+ private tester;
76
+ private listenerIds;
77
+ private listeners;
78
+ constructor(session: CDPSession, tester: WebSocketBrowserRPC);
79
+ send(method: string, params?: Record<string, unknown>): Promise<unknown>;
80
+ on(event: string, id: string, once?: boolean): void;
81
+ off(event: string, id: string): void;
82
+ once(event: string, listener: string): void;
83
+ }
84
+
66
85
  declare class BrowserServerState implements BrowserServerState$1 {
67
- orchestrators: Map<string, WebSocketBrowserRPC>;
68
- testers: Map<string, WebSocketBrowserRPC>;
86
+ readonly orchestrators: Map<string, WebSocketBrowserRPC>;
87
+ readonly testers: Map<string, WebSocketBrowserRPC>;
88
+ readonly cdps: Map<string, BrowserServerCDPHandler>;
69
89
  private contexts;
70
90
  getContext(contextId: string): BrowserServerStateContext | undefined;
71
- createAsyncContext(contextId: string, files: string[]): Promise<void>;
91
+ createAsyncContext(method: 'run' | 'collect', contextId: string, files: string[]): Promise<void>;
92
+ removeCDPHandler(sessionId: string): Promise<void>;
72
93
  }
73
94
 
74
95
  declare class BrowserServer implements BrowserServer$1 {
@@ -86,6 +107,7 @@ declare class BrowserServer implements BrowserServer$1 {
86
107
  state: BrowserServerState;
87
108
  provider: BrowserProvider;
88
109
  vite: Vite.ViteDevServer;
110
+ private stackTraceOptions;
89
111
  constructor(project: WorkspaceProject, base: string);
90
112
  setServer(server: Vite.ViteDevServer): void;
91
113
  getSerializableConfig(): ResolvedConfig;
@@ -95,6 +117,10 @@ declare class BrowserServer implements BrowserServer$1 {
95
117
  };
96
118
  formatScripts(scripts: BrowserScript[] | undefined): Promise<string>;
97
119
  initBrowserProvider(): Promise<void>;
120
+ parseErrorStacktrace(e: ErrorWithDiff, options?: StackTraceParserOptions): vitest.ParsedStack[];
121
+ parseStacktrace(trace: string, options?: StackTraceParserOptions): vitest.ParsedStack[];
122
+ private cdpSessions;
123
+ ensureCDPHandler(contextId: string, sessionId: string): Promise<BrowserServerCDPHandler>;
98
124
  close(): Promise<void>;
99
125
  }
100
126