@vitest/browser 5.0.0-beta.1 → 5.0.0-beta.3
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/context.d.ts +40 -1
- package/dist/client/.vite/manifest.json +8 -8
- package/dist/client/__vitest__/assets/index-BlLo6Q_D.css +1 -0
- package/dist/client/__vitest__/assets/index-O8gheoYf.js +89 -0
- package/dist/client/__vitest__/index.html +2 -2
- package/dist/client/__vitest_browser__/defineProperty-C3k2g8Sk.js +267 -0
- package/dist/client/__vitest_browser__/orchestrator-B44yH1M4.js +343 -0
- package/dist/client/__vitest_browser__/rrweb-snapshot-iZCFA2to.js +4388 -0
- package/dist/client/__vitest_browser__/tester-Byk-s_d6.js +5088 -0
- package/dist/client/orchestrator.html +2 -2
- package/dist/client/tester/locators.d.ts +69 -0
- package/dist/client/tester/tester.html +2 -2
- package/dist/client/tester/trace.d.ts +11 -7
- package/dist/client.js +10 -1
- package/dist/context.js +73 -44
- package/dist/expect-element.js +30 -30
- package/dist/index.d.ts +6 -22
- package/dist/index.js +99 -4573
- package/dist/locators-DUkyvRhY.js +5 -0
- package/dist/locators.d.ts +9 -1
- package/dist/locators.js +1 -1
- package/dist/shared/screenshotMatcher/types.d.ts +4 -3
- package/dist/state.js +64 -14
- package/dist/types.d.ts +2 -0
- package/jest-dom.d.ts +1 -0
- package/matchers.d.ts +2 -1
- package/package.json +10 -7
- package/dist/client/__vitest__/assets/index-BmuVn2L3.js +0 -136
- package/dist/client/__vitest__/assets/index-CxYquQyv.css +0 -1
- package/dist/client/__vitest__/bg.png +0 -0
- package/dist/client/__vitest_browser__/orchestrator-pTEf6o0n.js +0 -383
- package/dist/client/__vitest_browser__/rrweb-snapshot-xhvrgOHx.js +0 -5476
- package/dist/client/__vitest_browser__/tester-CIKiUsoz.js +0 -2431
- package/dist/client/__vitest_browser__/utils-BYUpz6v6.js +0 -3379
- package/dist/index-BlWsE3ij.js +0 -5
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
{__VITEST_INJECTOR__}
|
|
27
27
|
{__VITEST_ERROR_CATCHER__}
|
|
28
28
|
{__VITEST_SCRIPTS__}
|
|
29
|
-
<script type="module" crossorigin src="/__vitest_browser__/orchestrator-
|
|
30
|
-
<link rel="modulepreload" crossorigin href="/__vitest_browser__/
|
|
29
|
+
<script type="module" crossorigin src="/__vitest_browser__/orchestrator-B44yH1M4.js"></script>
|
|
30
|
+
<link rel="modulepreload" crossorigin href="/__vitest_browser__/defineProperty-C3k2g8Sk.js">
|
|
31
31
|
</head>
|
|
32
32
|
<body>
|
|
33
33
|
<div id="vitest-tester"></div>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { LocatorByRoleOptions, LocatorOptions, LocatorScreenshotOptions, MarkOptions, SelectorOptions, UserEventClearOptions, UserEventClickOptions, UserEventDragAndDropOptions, UserEventFillOptions, UserEventHoverOptions, UserEventSelectOptions, UserEventUploadOptions, UserEventWheelOptions } from "vitest/browser";
|
|
2
|
+
import { Ivya } from "ivya";
|
|
3
|
+
export { ensureAwaited } from "../utils.js";
|
|
4
|
+
export { convertElementToCssSelector, getIframeScale, processTimeoutOptions } from "./tester-utils.js";
|
|
5
|
+
export { getByAltTextSelector, getByLabelSelector, getByPlaceholderSelector, getByRoleSelector, getByTestIdSelector, getByTextSelector, getByTitleSelector } from "ivya";
|
|
6
|
+
export declare const selectorEngine: Ivya;
|
|
7
|
+
export declare abstract class Locator {
|
|
8
|
+
abstract selector: string;
|
|
9
|
+
private _parsedSelector;
|
|
10
|
+
protected _container?: Element | undefined;
|
|
11
|
+
protected _pwSelector?: string | undefined;
|
|
12
|
+
protected _pwLocator?: string | undefined;
|
|
13
|
+
protected _errorSource?: Error;
|
|
14
|
+
constructor();
|
|
15
|
+
click(options?: UserEventClickOptions): Promise<void>;
|
|
16
|
+
dblClick(options?: UserEventClickOptions): Promise<void>;
|
|
17
|
+
tripleClick(options?: UserEventClickOptions): Promise<void>;
|
|
18
|
+
wheel(options: UserEventWheelOptions): Promise<void>;
|
|
19
|
+
clear(options?: UserEventClearOptions): Promise<void>;
|
|
20
|
+
hover(options?: UserEventHoverOptions): Promise<void>;
|
|
21
|
+
unhover(options?: UserEventHoverOptions): Promise<void>;
|
|
22
|
+
fill(text: string, options?: UserEventFillOptions): Promise<void>;
|
|
23
|
+
upload(files: string | string[] | File | File[], options?: UserEventUploadOptions): Promise<void>;
|
|
24
|
+
dropTo(target: Locator, options?: UserEventDragAndDropOptions): Promise<void>;
|
|
25
|
+
selectOptions(value: HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[], options?: UserEventSelectOptions): Promise<void>;
|
|
26
|
+
screenshot(options: Omit<LocatorScreenshotOptions, "base64"> & {
|
|
27
|
+
base64: true;
|
|
28
|
+
}): Promise<{
|
|
29
|
+
path: string;
|
|
30
|
+
base64: string;
|
|
31
|
+
}>;
|
|
32
|
+
screenshot(options?: LocatorScreenshotOptions): Promise<string>;
|
|
33
|
+
mark(name: string, options?: MarkOptions): Promise<void>;
|
|
34
|
+
protected abstract locator(selector: string): Locator;
|
|
35
|
+
protected abstract elementLocator(element: Element): Locator;
|
|
36
|
+
getByRole(role: string, options?: LocatorByRoleOptions): Locator;
|
|
37
|
+
getByAltText(text: string | RegExp, options?: LocatorOptions): Locator;
|
|
38
|
+
getByLabelText(text: string | RegExp, options?: LocatorOptions): Locator;
|
|
39
|
+
getByPlaceholder(text: string | RegExp, options?: LocatorOptions): Locator;
|
|
40
|
+
getByTestId(testId: string | RegExp): Locator;
|
|
41
|
+
getByText(text: string | RegExp, options?: LocatorOptions): Locator;
|
|
42
|
+
getByTitle(title: string | RegExp, options?: LocatorOptions): Locator;
|
|
43
|
+
filter(filter: LocatorOptions): Locator;
|
|
44
|
+
and(locator: Locator): Locator;
|
|
45
|
+
or(locator: Locator): Locator;
|
|
46
|
+
query(): HTMLElement | SVGElement | null;
|
|
47
|
+
element(): HTMLElement | SVGElement;
|
|
48
|
+
elements(): (HTMLElement | SVGElement)[];
|
|
49
|
+
get length(): number;
|
|
50
|
+
all(): Locator[];
|
|
51
|
+
nth(index: number): Locator;
|
|
52
|
+
first(): Locator;
|
|
53
|
+
last(): Locator;
|
|
54
|
+
toString(): string;
|
|
55
|
+
serialize(): SerializedLocator;
|
|
56
|
+
asLocator(): string;
|
|
57
|
+
toJSON(): SerializedLocator;
|
|
58
|
+
findElement(options_?: SelectorOptions): Promise<HTMLElement | SVGElement>;
|
|
59
|
+
protected triggerCommand<T>(command: string, ...args: any[]): Promise<T>;
|
|
60
|
+
}
|
|
61
|
+
export declare function triggerCommandWithTrace<T>(options: {
|
|
62
|
+
name: string;
|
|
63
|
+
arguments: unknown[];
|
|
64
|
+
errorSource?: Error | undefined;
|
|
65
|
+
}): Promise<T>;
|
|
66
|
+
export interface SerializedLocator {
|
|
67
|
+
selector: string;
|
|
68
|
+
locator: string;
|
|
69
|
+
}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" href="{__VITEST_FAVICON__}" type="image/svg+xml">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>Vitest Browser Tester</title>
|
|
8
|
-
<script type="module" crossorigin src="/__vitest_browser__/tester-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/__vitest_browser__/
|
|
8
|
+
<script type="module" crossorigin src="/__vitest_browser__/tester-Byk-s_d6.js"></script>
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/__vitest_browser__/defineProperty-C3k2g8Sk.js">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
</body>
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import type { Task } from "@vitest/runner";
|
|
2
|
+
import type { BrowserTraceEntryKind } from "vitest/browser";
|
|
3
|
+
import type { SerializedLocator } from "./locators.js";
|
|
2
4
|
export interface BrowserTraceData {
|
|
3
5
|
retry: number;
|
|
4
6
|
repeats: number;
|
|
5
7
|
recordCanvas: boolean;
|
|
6
8
|
entries: BrowserTraceEntry[];
|
|
7
9
|
}
|
|
8
|
-
export type BrowserTraceEntryKind = "action" | "expect" | "mark" | "lifecycle";
|
|
9
10
|
export type BrowserTraceEntryStatus = "pass" | "fail";
|
|
11
|
+
export type BrowserTraceEntryRangePhase = "start" | "end";
|
|
10
12
|
export type BrowserTraceSelectorResolution = "matched" | "missing" | "error";
|
|
13
|
+
export interface BrowserTraceEntryRange {
|
|
14
|
+
id: string;
|
|
15
|
+
phase: BrowserTraceEntryRangePhase;
|
|
16
|
+
}
|
|
11
17
|
export interface BrowserTraceEntry {
|
|
12
18
|
name: string;
|
|
13
19
|
kind: BrowserTraceEntryKind;
|
|
20
|
+
range?: BrowserTraceEntryRange;
|
|
14
21
|
status?: BrowserTraceEntryStatus;
|
|
15
22
|
startTime: number;
|
|
16
23
|
duration?: number;
|
|
@@ -20,7 +27,7 @@ export interface BrowserTraceEntry {
|
|
|
20
27
|
line: number;
|
|
21
28
|
column: number;
|
|
22
29
|
};
|
|
23
|
-
|
|
30
|
+
element?: SerializedLocator;
|
|
24
31
|
snapshot: TraceSnapshot;
|
|
25
32
|
}
|
|
26
33
|
interface TraceSnapshot {
|
|
@@ -40,14 +47,11 @@ interface TraceSnapshot {
|
|
|
40
47
|
}
|
|
41
48
|
declare const PSEUDO_CLASS_NAMES: readonly [":hover", ":active", ":focus", ":focus-visible", ":focus-within"];
|
|
42
49
|
type PseudoClassName = (typeof PSEUDO_CLASS_NAMES)[number];
|
|
43
|
-
export type BrowserTraceState = Record<string, BrowserTraceData>;
|
|
44
50
|
export interface BrowserTraceAttempt {
|
|
45
51
|
retry: number;
|
|
46
52
|
repeats: number;
|
|
47
53
|
startTime: number;
|
|
48
54
|
}
|
|
49
|
-
export declare function
|
|
50
|
-
|
|
51
|
-
}): void;
|
|
52
|
-
export declare function getBrowserTrace(testId: string, repeats: number, retry: number): BrowserTraceData | undefined;
|
|
55
|
+
export declare function createBrowserTraceRangeId(): string;
|
|
56
|
+
export declare function recordBrowserTraceEntry(task: Task, options: Omit<BrowserTraceEntry, "snapshot" | "startTime">): Promise<void>;
|
|
53
57
|
export {};
|
package/dist/client.js
CHANGED
|
@@ -337,6 +337,10 @@ const onCancelCallbacks = [];
|
|
|
337
337
|
function onCancel(callback) {
|
|
338
338
|
onCancelCallbacks.push(callback);
|
|
339
339
|
}
|
|
340
|
+
let pageMarkHandler = null;
|
|
341
|
+
function registerPageMarkHandler(handler) {
|
|
342
|
+
pageMarkHandler = handler;
|
|
343
|
+
}
|
|
340
344
|
// ws connection can be established before the orchestrator is fully loaded
|
|
341
345
|
// in very rare cases in the preview provider
|
|
342
346
|
function waitForOrchestrator() {
|
|
@@ -385,6 +389,11 @@ function createClient() {
|
|
|
385
389
|
}
|
|
386
390
|
cdp.emit(event, payload);
|
|
387
391
|
},
|
|
392
|
+
async pageMark(name, options) {
|
|
393
|
+
if (pageMarkHandler) {
|
|
394
|
+
await pageMarkHandler(name, options);
|
|
395
|
+
}
|
|
396
|
+
},
|
|
388
397
|
async resolveManualMock(url) {
|
|
389
398
|
// @ts-expect-error not typed global API
|
|
390
399
|
const mocker = globalThis.__vitest_mocker__;
|
|
@@ -461,4 +470,4 @@ function createClient() {
|
|
|
461
470
|
}
|
|
462
471
|
const client = createClient();
|
|
463
472
|
|
|
464
|
-
export { ENTRY_URL, HOST, PORT, RPC_ID, channel, client, globalChannel, onCancel };
|
|
473
|
+
export { ENTRY_URL, HOST, PORT, RPC_ID, channel, client, globalChannel, onCancel, registerPageMarkHandler };
|
package/dist/context.js
CHANGED
|
@@ -66,27 +66,13 @@ const PSEUDO_CLASS_NAMES = [
|
|
|
66
66
|
":focus-visible",
|
|
67
67
|
":focus-within"
|
|
68
68
|
];
|
|
69
|
-
function
|
|
70
|
-
return
|
|
69
|
+
function createBrowserTraceRangeId() {
|
|
70
|
+
return Math.random().toString(36).slice(2);
|
|
71
71
|
}
|
|
72
|
-
function
|
|
73
|
-
return `${testId}:${repeats}:${retry}`;
|
|
74
|
-
}
|
|
75
|
-
// TODO: should we avoid accumulating? send and immediately clear each entry to save memory?
|
|
76
|
-
function recordBrowserTraceEntry(task, options) {
|
|
77
|
-
// TODO: trace-view currently receives selectors after locator/action resolution,
|
|
78
|
-
// so provider-specific lowered selectors can leak into snapshot lookup. Preserve
|
|
79
|
-
// the original locator selector, or record the target node id before lowering.
|
|
80
|
-
// for example, this causes shadow dom selectors to fail with `>>>` marker.
|
|
81
|
-
// For now, remove trivial `html >` prefix generated by convertElementToCssSelector.
|
|
82
|
-
// this is also necessary to `engine.querySelector + document.documentElement`
|
|
83
|
-
// to find an element on webdriverio
|
|
84
|
-
if (options.selector?.startsWith("html >")) {
|
|
85
|
-
options.selector = options.selector.slice(6);
|
|
86
|
-
}
|
|
72
|
+
async function recordBrowserTraceEntry(task, options) {
|
|
87
73
|
const attemptInfo = getBrowserState().browserTraceAttempts.get(task.id);
|
|
88
|
-
const relativeStartTime =
|
|
89
|
-
const snapshot = takeSnapshot(options.
|
|
74
|
+
const relativeStartTime = now() - attemptInfo.startTime;
|
|
75
|
+
const snapshot = takeSnapshot(options.element);
|
|
90
76
|
const entry = {
|
|
91
77
|
...options,
|
|
92
78
|
startTime: relativeStartTime,
|
|
@@ -94,15 +80,21 @@ function recordBrowserTraceEntry(task, options) {
|
|
|
94
80
|
};
|
|
95
81
|
const { retry, repeats } = attemptInfo;
|
|
96
82
|
const { recordCanvas } = getBrowserState().config.browser.traceView;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
83
|
+
// An async lane could defer artifact recording and flush it at test-attempt end,
|
|
84
|
+
// but the synchronous snapshot work is already a comparable cost, and this path
|
|
85
|
+
// is mostly data passing after that.
|
|
86
|
+
// Keep it simple unless measurements show artifact recording is a bottleneck.
|
|
87
|
+
const data = {
|
|
100
88
|
retry,
|
|
101
89
|
repeats,
|
|
102
90
|
recordCanvas,
|
|
103
|
-
entries: []
|
|
91
|
+
entries: [entry]
|
|
104
92
|
};
|
|
105
|
-
|
|
93
|
+
const rpc = getWorkerState().rpc;
|
|
94
|
+
await rpc.triggerCommand(getBrowserState().sessionId, "__vitest_recordBrowserTrace", undefined, [{
|
|
95
|
+
testId: task.id,
|
|
96
|
+
data
|
|
97
|
+
}]);
|
|
106
98
|
}
|
|
107
99
|
// Resolve ivya selector to a DOM element and take a snapshot with rrweb Mirror
|
|
108
100
|
// so we can store the nodeId for provider-agnostic element highlighting in the viewer.
|
|
@@ -110,7 +102,7 @@ function recordBrowserTraceEntry(task, options) {
|
|
|
110
102
|
// selector engine inside the snapshot iframe at view time via injected script.
|
|
111
103
|
// Our approach resolves at collection time (same moment as snapshot) — simpler but
|
|
112
104
|
// requires Mirror plumbing. nodeId-based lookup also works across shadow DOM, unlike querySelector.
|
|
113
|
-
function takeSnapshot(
|
|
105
|
+
function takeSnapshot(serializedLocator) {
|
|
114
106
|
const { snapshot, createMirror } = getBrowserState().browserTraceDomSnapshot;
|
|
115
107
|
const traceView = getBrowserState().config.browser.traceView;
|
|
116
108
|
const engine = getBrowserState().selectorEngine;
|
|
@@ -137,9 +129,9 @@ function takeSnapshot(selector) {
|
|
|
137
129
|
const ids = Array.from(elements, (el) => mirror.getId(el)).filter((id) => id !== -1);
|
|
138
130
|
result.pseudoClassIds[className] = ids;
|
|
139
131
|
}
|
|
140
|
-
if (
|
|
132
|
+
if (serializedLocator) {
|
|
141
133
|
try {
|
|
142
|
-
const el = engine.querySelector(engine.parseSelector(selector), document.documentElement, false);
|
|
134
|
+
const el = engine.querySelector(engine.parseSelector(serializedLocator._pwSelector ?? serializedLocator.selector), document.documentElement, false);
|
|
143
135
|
if (!el) {
|
|
144
136
|
result.selectorResolution = "missing";
|
|
145
137
|
} else {
|
|
@@ -262,19 +254,28 @@ function processTimeoutOptions(options_) {
|
|
|
262
254
|
}
|
|
263
255
|
const provider$1 = getBrowserState().provider;
|
|
264
256
|
const kElementLocator = Symbol.for("$$vitest:locator-resolved");
|
|
265
|
-
async function
|
|
257
|
+
async function serializeElement(elementOrLocator, options) {
|
|
266
258
|
if (!elementOrLocator) {
|
|
267
259
|
throw new Error("Expected element or locator to be defined.");
|
|
268
260
|
}
|
|
269
261
|
if (elementOrLocator instanceof Element) {
|
|
270
|
-
|
|
262
|
+
const selector = convertElementToCssSelector(elementOrLocator);
|
|
263
|
+
return {
|
|
264
|
+
selector,
|
|
265
|
+
locator: __INTERNAL._asLocator("javascript", selector)
|
|
266
|
+
};
|
|
271
267
|
}
|
|
272
268
|
if (isLocator(elementOrLocator)) {
|
|
273
269
|
if (provider$1 === "playwright" || kElementLocator in elementOrLocator) {
|
|
274
|
-
return elementOrLocator.
|
|
270
|
+
return elementOrLocator.serialize();
|
|
275
271
|
}
|
|
276
272
|
const element = await elementOrLocator.findElement(options);
|
|
277
|
-
|
|
273
|
+
const selector = convertElementToCssSelector(element);
|
|
274
|
+
const locator = __INTERNAL._asLocator("javascript", selector);
|
|
275
|
+
return {
|
|
276
|
+
selector,
|
|
277
|
+
locator
|
|
278
|
+
};
|
|
278
279
|
}
|
|
279
280
|
throw new Error("Expected element or locator to be an instance of Element or Locator.");
|
|
280
281
|
}
|
|
@@ -380,9 +381,9 @@ function createUserEvent(__tl_user_event_base__, options) {
|
|
|
380
381
|
},
|
|
381
382
|
type(element, text, options) {
|
|
382
383
|
return ensureAwaited(async (error) => {
|
|
383
|
-
const
|
|
384
|
+
const serializedElement = await serializeElement(element, options);
|
|
384
385
|
const { unreleased } = await triggerCommand("__vitest_type", [
|
|
385
|
-
|
|
386
|
+
serializedElement,
|
|
386
387
|
text,
|
|
387
388
|
{
|
|
388
389
|
...options,
|
|
@@ -566,7 +567,7 @@ const page = {
|
|
|
566
567
|
screenshotIds[repeatCount] ??= {};
|
|
567
568
|
screenshotIds[repeatCount][taskName] = number + 1;
|
|
568
569
|
const name = options.path || `${taskName.replace(/[^a-z0-9]/gi, "-")}-${number}.png`;
|
|
569
|
-
const [element, ...mask] = await Promise.all([options.element ?
|
|
570
|
+
const [element, ...mask] = await Promise.all([options.element ? serializeElement(options.element, options) : undefined, ..."mask" in options ? options.mask.map((el) => serializeElement(el, options)) : []]);
|
|
570
571
|
const normalizedOptions = "mask" in options ? {
|
|
571
572
|
...options,
|
|
572
573
|
mask
|
|
@@ -586,13 +587,24 @@ const page = {
|
|
|
586
587
|
if (typeof bodyOrOptions === "function") {
|
|
587
588
|
return ensureAwaited(async (error) => {
|
|
588
589
|
let status = "pass";
|
|
589
|
-
const
|
|
590
|
+
const traceRangeId = hasActiveTraceView ? createBrowserTraceRangeId() : undefined;
|
|
590
591
|
if (hasActiveTrace) {
|
|
591
592
|
await triggerCommand("__vitest_groupTraceStart", [{
|
|
592
593
|
name,
|
|
593
594
|
stack: options?.stack ?? error?.stack
|
|
594
595
|
}], error);
|
|
595
596
|
}
|
|
597
|
+
if (hasActiveTraceView) {
|
|
598
|
+
await recordBrowserTraceEntry(currentTest, {
|
|
599
|
+
name,
|
|
600
|
+
kind: "mark",
|
|
601
|
+
range: {
|
|
602
|
+
id: traceRangeId,
|
|
603
|
+
phase: "start"
|
|
604
|
+
},
|
|
605
|
+
stack: options?.stack ?? error?.stack
|
|
606
|
+
});
|
|
607
|
+
}
|
|
596
608
|
try {
|
|
597
609
|
return await bodyOrOptions();
|
|
598
610
|
} catch (err) {
|
|
@@ -600,13 +612,14 @@ const page = {
|
|
|
600
612
|
throw err;
|
|
601
613
|
} finally {
|
|
602
614
|
if (hasActiveTraceView) {
|
|
603
|
-
|
|
604
|
-
recordBrowserTraceEntry(currentTest, {
|
|
615
|
+
await recordBrowserTraceEntry(currentTest, {
|
|
605
616
|
name,
|
|
606
|
-
kind: "mark",
|
|
617
|
+
kind: options?.kind ?? "mark",
|
|
618
|
+
range: {
|
|
619
|
+
id: traceRangeId,
|
|
620
|
+
phase: "end"
|
|
621
|
+
},
|
|
607
622
|
status,
|
|
608
|
-
startTime,
|
|
609
|
-
duration: now() - startTime,
|
|
610
623
|
stack: options?.stack ?? error?.stack
|
|
611
624
|
});
|
|
612
625
|
}
|
|
@@ -619,11 +632,11 @@ const page = {
|
|
|
619
632
|
if (!hasActiveTrace && !hasActiveTraceView) {
|
|
620
633
|
return Promise.resolve();
|
|
621
634
|
}
|
|
622
|
-
return ensureAwaited((error) => {
|
|
635
|
+
return ensureAwaited(async (error) => {
|
|
623
636
|
if (hasActiveTraceView) {
|
|
624
|
-
recordBrowserTraceEntry(currentTest, {
|
|
637
|
+
await recordBrowserTraceEntry(currentTest, {
|
|
625
638
|
name,
|
|
626
|
-
kind: "mark",
|
|
639
|
+
kind: bodyOrOptions?.kind ?? "mark",
|
|
627
640
|
stack: bodyOrOptions?.stack ?? error?.stack
|
|
628
641
|
});
|
|
629
642
|
}
|
|
@@ -754,10 +767,26 @@ function prettyDOM(dom, maxLength = Number(defaultOptions?.maxLength ?? import.m
|
|
|
754
767
|
return dom.outerHTML.length > maxLength ? `${pretty.slice(0, maxLength)}...` : pretty;
|
|
755
768
|
}
|
|
756
769
|
function getElementError(selector, container) {
|
|
757
|
-
const
|
|
770
|
+
const locator = typeof selector === "string" ? __INTERNAL._asLocator("javascript", selector) : selector.asLocator();
|
|
771
|
+
const formatted = formatDOM(container);
|
|
772
|
+
const error = new Error(`Cannot find element with locator: ${locator}\n\n${formatted}`);
|
|
758
773
|
error.name = "VitestBrowserElementError";
|
|
759
774
|
return error;
|
|
760
775
|
}
|
|
776
|
+
function formatDOM(container) {
|
|
777
|
+
const format = getBrowserState().config.browser.locators.errorFormat;
|
|
778
|
+
if (format === "aria") {
|
|
779
|
+
return `ARIA tree:\n${formatAriaTree(container)}`;
|
|
780
|
+
}
|
|
781
|
+
if (format === "all") {
|
|
782
|
+
return `ARIA tree:\n${formatAriaTree(container)}\n\nHTML:\n${prettyDOM(container)}`;
|
|
783
|
+
}
|
|
784
|
+
return prettyDOM(container);
|
|
785
|
+
}
|
|
786
|
+
function formatAriaTree(container) {
|
|
787
|
+
const { generateAriaTree, renderAriaTree } = getBrowserState().aria;
|
|
788
|
+
return renderAriaTree(generateAriaTree(container));
|
|
789
|
+
}
|
|
761
790
|
function configurePrettyDOM(options) {
|
|
762
791
|
defaultOptions = options;
|
|
763
792
|
}
|