-
-
Notifications
You must be signed in to change notification settings - Fork 157
/
_utils.ts
116 lines (93 loc) · 3.47 KB
/
_utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import AxeBuilder from "@axe-core/playwright"
import { expect, type Locator, type Page } from "@playwright/test"
export async function a11y(page: Page, selector = "[data-part=root]") {
await page.waitForSelector(selector)
const results = await new AxeBuilder({ page: page as any })
.disableRules(["color-contrast"])
.include(selector)
.analyze()
expect(results.violations).toEqual([])
}
export const testid = (part: string) => `[data-testid=${esc(part)}]`
export const controls = (page: Page) => {
return {
num: async (id: string, value: string) => {
const el = page.locator(testid(id))
await el.selectText()
await page.keyboard.press("Backspace")
await el.fill(value)
await page.keyboard.press("Enter")
},
bool: async (id: string, value = true) => {
const el = page.locator(testid(id))
if (value) await el.check()
else await el.uncheck()
},
select: async (id: string, value: string) => {
const el = page.locator(testid(id))
await el.selectOption(value)
},
}
}
export const part = (part: string) => `[data-part=${esc(part)}]`
const esc = (str: string) => str.replace(/[-[\]{}()*+?:.,\\^$|#\s]/g, "\\$&")
export const clickViz = (page: Page) => page.locator("text=Visualizer").first().click()
export const clickControls = (page: Page) => page.locator("text=Controls").first().click()
export const paste = (node: HTMLElement, value: string) => {
const clipboardData = new DataTransfer()
clipboardData.setData("text/plain", value)
const event = new ClipboardEvent("paste", {
clipboardData,
bubbles: true,
cancelable: true,
})
node.dispatchEvent(event)
}
export const nativeInput = (node: HTMLInputElement | HTMLTextAreaElement, value: string) => {
const event = new InputEvent("input", {
bubbles: true,
inputType: "insertFromPaste",
})
const __input__setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set
const __textarea__setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value")?.set
const textValue = `${node.value}${value}`
const set = node.localName === "input" ? __input__setter : __textarea__setter
set?.call(node, textValue)
node.dispatchEvent(event)
}
export const clickOutside = (page: Page) => page.click("body", { force: true })
export const rect = async (el: Locator) => {
const bbox = await el.boundingBox()
if (!bbox) {
throw new Error("Element not found")
}
return { ...bbox, midX: bbox.x + bbox.width / 2, midY: bbox.y + bbox.height / 2 }
}
export async function isInViewport(viewport: Locator, el: Locator) {
const bbox = await rect(el)
const viewportBbox = await rect(viewport)
return (
bbox.x >= viewportBbox.x &&
bbox.y >= viewportBbox.y &&
bbox.x + bbox.width <= viewportBbox.x + viewportBbox.width &&
bbox.y + bbox.height <= viewportBbox.y + viewportBbox.height
)
}
export const repeat = async (count: number, fn: () => unknown) => {
await [...new Array(count)].reduce((p) => p.then(fn), Promise.resolve())
}
export const pointer = {
down(el: Locator) {
return el.dispatchEvent("pointerdown", { pointerType: "mouse", button: 0 })
},
up(el: Locator) {
return el.dispatchEvent("pointerup", { pointerType: "mouse", button: 0 })
},
async move(el: Locator) {
await el.hover()
return el.dispatchEvent("pointermove", { button: 0 })
},
}
export const textSelection = (page: Page) => {
return page.evaluate(() => window.getSelection()?.toString())
}