@lullabot/playwright-testing 1.0.0 → 1.1.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/bin/github-a11y-summary +2 -2
- package/bin/github-failure-summary +4 -4
- package/lib/accessibility-baseline-file.d.ts +6 -6
- package/lib/accessibility-baseline-file.js +29 -23
- package/lib/accessibility-baseline.js +5 -3
- package/lib/accessible-screenshot.d.ts +4 -4
- package/lib/accessible-screenshot.js +110 -88
- package/lib/focus.js +4 -1
- package/lib/frames.js +4 -4
- package/lib/github/a11y-summary.js +74 -79
- package/lib/github/attachments.d.ts +1 -1
- package/lib/github/attachments.js +36 -34
- package/lib/github/failure-summary.d.ts +4 -4
- package/lib/github/failure-summary.js +140 -108
- package/lib/github/index.d.ts +6 -6
- package/lib/github/report-paths.js +17 -9
- package/lib/hover.js +17 -17
- package/lib/images.js +15 -12
- package/lib/index.d.ts +13 -13
- package/lib/interaction-states.d.ts +2 -2
- package/lib/interaction-states.js +7 -7
- package/lib/mock/index.d.ts +1 -1
- package/lib/mock/youtube.d.ts +1 -1
- package/lib/mock/youtube.js +1 -1
- package/lib/pseudo-state.d.ts +2 -2
- package/lib/pseudo-state.js +15 -7
- package/lib/videos.js +21 -10
- package/lib/visualdiff.d.ts +4 -4
- package/lib/visualdiff.js +35 -27
- package/package.json +3 -3
- package/src/accessibility-baseline-file.test.ts +190 -154
- package/src/accessibility-baseline-file.ts +107 -83
- package/src/accessibility-baseline.test.ts +869 -433
- package/src/accessibility-baseline.ts +41 -21
- package/src/accessible-screenshot.test.ts +551 -423
- package/src/accessible-screenshot.ts +358 -251
- package/src/focus.test.ts +23 -23
- package/src/focus.ts +7 -2
- package/src/fonts.test.ts +13 -13
- package/src/fonts.ts +1 -1
- package/src/frames.test.ts +10 -8
- package/src/frames.ts +5 -5
- package/src/github/a11y-summary.test.ts +436 -332
- package/src/github/a11y-summary.ts +189 -179
- package/src/github/attachments.test.ts +228 -167
- package/src/github/attachments.ts +144 -121
- package/src/github/failure-summary.test.ts +660 -445
- package/src/github/failure-summary.ts +376 -274
- package/src/github/index.test.ts +14 -14
- package/src/github/index.ts +6 -6
- package/src/github/report-paths.test.ts +211 -156
- package/src/github/report-paths.ts +75 -61
- package/src/hover.test.ts +50 -46
- package/src/hover.ts +18 -18
- package/src/images.test.ts +251 -206
- package/src/images.ts +52 -43
- package/src/index.ts +13 -13
- package/src/interaction-states.test.ts +72 -42
- package/src/interaction-states.ts +43 -29
- package/src/mock/index.ts +1 -1
- package/src/mock/youtube.test.ts +26 -26
- package/src/mock/youtube.ts +4 -6
- package/src/pseudo-state.test.ts +58 -51
- package/src/pseudo-state.ts +32 -27
- package/src/videos.test.ts +559 -399
- package/src/videos.ts +68 -44
- package/src/visualdiff.test.ts +416 -315
- package/src/visualdiff.ts +140 -99
package/src/pseudo-state.test.ts
CHANGED
|
@@ -1,83 +1,90 @@
|
|
|
1
|
-
import {describe, expect, it, vi} from
|
|
2
|
-
import {forcePseudoState} from
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { forcePseudoState } from "./pseudo-state";
|
|
3
3
|
|
|
4
4
|
function fakePage(nodeId = 2) {
|
|
5
5
|
const session = {
|
|
6
6
|
send: vi.fn(async (method: string) => {
|
|
7
|
-
if (method ===
|
|
8
|
-
return {root: {nodeId: 1}}
|
|
7
|
+
if (method === "DOM.getDocument") {
|
|
8
|
+
return { root: { nodeId: 1 } };
|
|
9
9
|
}
|
|
10
|
-
if (method ===
|
|
11
|
-
return {nodeId}
|
|
10
|
+
if (method === "DOM.querySelector") {
|
|
11
|
+
return { nodeId };
|
|
12
12
|
}
|
|
13
|
-
return {}
|
|
13
|
+
return {};
|
|
14
14
|
}),
|
|
15
15
|
detach: vi.fn().mockResolvedValue(undefined),
|
|
16
|
-
}
|
|
16
|
+
};
|
|
17
17
|
const context = {
|
|
18
18
|
newCDPSession: vi.fn().mockResolvedValue(session),
|
|
19
|
-
}
|
|
19
|
+
};
|
|
20
20
|
const page = {
|
|
21
21
|
context: vi.fn(() => context),
|
|
22
|
-
}
|
|
22
|
+
};
|
|
23
23
|
|
|
24
|
-
return {context, page, session}
|
|
24
|
+
return { context, page, session };
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
describe(
|
|
28
|
-
it(
|
|
29
|
-
const {context, page, session} = fakePage()
|
|
27
|
+
describe("forcePseudoState", () => {
|
|
28
|
+
it("forces pseudo-classes on the first selector match and clears them", async () => {
|
|
29
|
+
const { context, page, session } = fakePage();
|
|
30
30
|
|
|
31
|
-
const cleanup = await forcePseudoState(page as any,
|
|
31
|
+
const cleanup = await forcePseudoState(page as any, ".menu-trigger", [
|
|
32
|
+
"hover",
|
|
33
|
+
"focus-visible",
|
|
34
|
+
]);
|
|
32
35
|
|
|
33
|
-
expect(context.newCDPSession).toHaveBeenCalledWith(page)
|
|
34
|
-
expect(session.send).toHaveBeenNthCalledWith(1,
|
|
35
|
-
expect(session.send).toHaveBeenNthCalledWith(2,
|
|
36
|
-
expect(session.send).toHaveBeenNthCalledWith(3,
|
|
37
|
-
expect(session.send).toHaveBeenNthCalledWith(4,
|
|
36
|
+
expect(context.newCDPSession).toHaveBeenCalledWith(page);
|
|
37
|
+
expect(session.send).toHaveBeenNthCalledWith(1, "DOM.enable");
|
|
38
|
+
expect(session.send).toHaveBeenNthCalledWith(2, "CSS.enable");
|
|
39
|
+
expect(session.send).toHaveBeenNthCalledWith(3, "DOM.getDocument");
|
|
40
|
+
expect(session.send).toHaveBeenNthCalledWith(4, "DOM.querySelector", {
|
|
38
41
|
nodeId: 1,
|
|
39
|
-
selector:
|
|
40
|
-
})
|
|
41
|
-
expect(session.send).toHaveBeenNthCalledWith(5,
|
|
42
|
+
selector: ".menu-trigger",
|
|
43
|
+
});
|
|
44
|
+
expect(session.send).toHaveBeenNthCalledWith(5, "CSS.forcePseudoState", {
|
|
42
45
|
nodeId: 2,
|
|
43
|
-
forcedPseudoClasses: [
|
|
44
|
-
})
|
|
46
|
+
forcedPseudoClasses: ["hover", "focus-visible"],
|
|
47
|
+
});
|
|
45
48
|
|
|
46
|
-
await cleanup()
|
|
47
|
-
await cleanup()
|
|
49
|
+
await cleanup();
|
|
50
|
+
await cleanup();
|
|
48
51
|
|
|
49
|
-
expect(session.send).toHaveBeenNthCalledWith(6,
|
|
52
|
+
expect(session.send).toHaveBeenNthCalledWith(6, "CSS.forcePseudoState", {
|
|
50
53
|
nodeId: 2,
|
|
51
54
|
forcedPseudoClasses: [],
|
|
52
|
-
})
|
|
53
|
-
expect(session.detach).toHaveBeenCalledOnce()
|
|
54
|
-
})
|
|
55
|
+
});
|
|
56
|
+
expect(session.detach).toHaveBeenCalledOnce();
|
|
57
|
+
});
|
|
55
58
|
|
|
56
|
-
it(
|
|
57
|
-
const {page, session} = fakePage(0)
|
|
59
|
+
it("fails clearly and detaches when the selector does not match", async () => {
|
|
60
|
+
const { page, session } = fakePage(0);
|
|
58
61
|
|
|
59
|
-
await expect(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
await expect(
|
|
63
|
+
forcePseudoState(page as any, ".missing", ["hover"]),
|
|
64
|
+
).rejects.toThrow('could not find an element matching ".missing"');
|
|
65
|
+
expect(session.detach).toHaveBeenCalledOnce();
|
|
66
|
+
});
|
|
63
67
|
|
|
64
|
-
it(
|
|
68
|
+
it("reports that CDP requires Chromium", async () => {
|
|
65
69
|
const page = {
|
|
66
70
|
context: () => ({
|
|
67
|
-
newCDPSession: vi
|
|
71
|
+
newCDPSession: vi
|
|
72
|
+
.fn()
|
|
73
|
+
.mockRejectedValue(new Error("CDP only supported in Chromium")),
|
|
68
74
|
}),
|
|
69
|
-
}
|
|
75
|
+
};
|
|
70
76
|
|
|
71
|
-
await expect(
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
await expect(
|
|
78
|
+
forcePseudoState(page as any, "button", ["focus"]),
|
|
79
|
+
).rejects.toThrow("requires a Chromium browser project");
|
|
80
|
+
});
|
|
74
81
|
|
|
75
|
-
it(
|
|
76
|
-
const {page, session} = fakePage()
|
|
77
|
-
const cleanup = await forcePseudoState(page as any,
|
|
78
|
-
session.send.mockRejectedValueOnce(new Error(
|
|
82
|
+
it("detaches even when clearing the state fails", async () => {
|
|
83
|
+
const { page, session } = fakePage();
|
|
84
|
+
const cleanup = await forcePseudoState(page as any, "button", ["focus"]);
|
|
85
|
+
session.send.mockRejectedValueOnce(new Error("node disappeared"));
|
|
79
86
|
|
|
80
|
-
await expect(cleanup()).rejects.toThrow(
|
|
81
|
-
expect(session.detach).toHaveBeenCalledOnce()
|
|
82
|
-
})
|
|
83
|
-
})
|
|
87
|
+
await expect(cleanup()).rejects.toThrow("node disappeared");
|
|
88
|
+
expect(session.detach).toHaveBeenCalledOnce();
|
|
89
|
+
});
|
|
90
|
+
});
|
package/src/pseudo-state.ts
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import {Page} from
|
|
1
|
+
import { Page } from "@playwright/test";
|
|
2
2
|
|
|
3
3
|
/** Pseudo-classes supported by Chromium's CSS.forcePseudoState command. */
|
|
4
4
|
export type ForcedPseudoClass =
|
|
5
|
-
|
|
|
6
|
-
| 'focus'
|
|
7
|
-
| 'focus-visible'
|
|
8
|
-
| 'focus-within'
|
|
9
|
-
| 'hover'
|
|
10
|
-
| 'target'
|
|
5
|
+
"active" | "focus" | "focus-visible" | "focus-within" | "hover" | "target";
|
|
11
6
|
|
|
12
7
|
/** A CSS selector and the pseudo-classes to force on its first match. */
|
|
13
8
|
export interface ForcedPseudoState {
|
|
14
|
-
selector: string
|
|
15
|
-
pseudoClasses: ForcedPseudoClass[]
|
|
9
|
+
selector: string;
|
|
10
|
+
pseudoClasses: ForcedPseudoClass[];
|
|
16
11
|
}
|
|
17
12
|
|
|
18
13
|
/**
|
|
@@ -28,42 +23,52 @@ export async function forcePseudoState(
|
|
|
28
23
|
selector: string,
|
|
29
24
|
pseudoClasses: ForcedPseudoClass[],
|
|
30
25
|
): Promise<() => Promise<void>> {
|
|
31
|
-
let session
|
|
26
|
+
let session;
|
|
32
27
|
try {
|
|
33
|
-
session = await page.context().newCDPSession(page)
|
|
28
|
+
session = await page.context().newCDPSession(page);
|
|
34
29
|
} catch (error) {
|
|
35
|
-
throw new Error(
|
|
30
|
+
throw new Error("forcePseudoState() requires a Chromium browser project.", {
|
|
31
|
+
cause: error,
|
|
32
|
+
});
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
try {
|
|
39
|
-
await session.send(
|
|
40
|
-
await session.send(
|
|
41
|
-
const {root} = await session.send(
|
|
42
|
-
const {nodeId} = await session.send(
|
|
36
|
+
await session.send("DOM.enable");
|
|
37
|
+
await session.send("CSS.enable");
|
|
38
|
+
const { root } = await session.send("DOM.getDocument");
|
|
39
|
+
const { nodeId } = await session.send("DOM.querySelector", {
|
|
43
40
|
nodeId: root.nodeId,
|
|
44
41
|
selector,
|
|
45
|
-
})
|
|
42
|
+
});
|
|
46
43
|
|
|
47
44
|
if (!nodeId) {
|
|
48
|
-
throw new Error(
|
|
45
|
+
throw new Error(
|
|
46
|
+
`forcePseudoState() could not find an element matching "${selector}".`,
|
|
47
|
+
);
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
await session.send(
|
|
50
|
+
await session.send("CSS.forcePseudoState", {
|
|
51
|
+
nodeId,
|
|
52
|
+
forcedPseudoClasses: pseudoClasses,
|
|
53
|
+
});
|
|
52
54
|
|
|
53
|
-
let cleared = false
|
|
55
|
+
let cleared = false;
|
|
54
56
|
return async () => {
|
|
55
57
|
if (cleared) {
|
|
56
|
-
return
|
|
58
|
+
return;
|
|
57
59
|
}
|
|
58
|
-
cleared = true
|
|
60
|
+
cleared = true;
|
|
59
61
|
try {
|
|
60
|
-
await session.send(
|
|
62
|
+
await session.send("CSS.forcePseudoState", {
|
|
63
|
+
nodeId,
|
|
64
|
+
forcedPseudoClasses: [],
|
|
65
|
+
});
|
|
61
66
|
} finally {
|
|
62
|
-
await session.detach()
|
|
67
|
+
await session.detach();
|
|
63
68
|
}
|
|
64
|
-
}
|
|
69
|
+
};
|
|
65
70
|
} catch (error) {
|
|
66
|
-
await session.detach()
|
|
67
|
-
throw error
|
|
71
|
+
await session.detach();
|
|
72
|
+
throw error;
|
|
68
73
|
}
|
|
69
74
|
}
|