Skip to content

Commit 992e116

Browse files
committed
Add a test for the code page
1 parent 0814420 commit 992e116

File tree

2 files changed

+217
-113
lines changed

2 files changed

+217
-113
lines changed

test/e2e/code-page.spec.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { test, expect } from "@playwright/test"
2+
3+
test("icon buttons on the code page render visible non-zero SVGs for each link type", async ({
4+
page,
5+
}) => {
6+
await page.goto("/community/tools-and-libraries/")
7+
8+
await page.waitForSelector("article header", { timeout: 10000 })
9+
10+
const headers = page.locator("article header")
11+
const headerCount = await headers.count()
12+
expect(headerCount).toBeGreaterThan(0)
13+
14+
type IconType = "site" | "github" | "npm" | "gem"
15+
const collected = new Map<
16+
IconType,
17+
{ href: string; width: number; height: number }
18+
>()
19+
let firstHeaderWithAnyIconIndex: number | null = null
20+
21+
const haveAll = () =>
22+
collected.has("site") &&
23+
collected.has("github") &&
24+
collected.has("npm") &&
25+
collected.has("gem")
26+
27+
for (let i = 0; i < headerCount; i++) {
28+
if (haveAll()) break
29+
30+
const header = headers.nth(i)
31+
32+
const githubLink = header.locator(
33+
'a[data-variant=tertiary][href*="github.com"]:has(svg)',
34+
)
35+
const npmLink = header.locator(
36+
'a[data-variant=tertiary][href*="npmjs.com/package/"]:has(svg)',
37+
)
38+
const gemLink = header.locator(
39+
'a[data-variant=tertiary][href*="rubygems.org/gems/"]:has(svg)',
40+
)
41+
const siteLink = header.locator(
42+
'a[data-variant=tertiary][href^="http"]:not([href*="github.com"]):not([href*="npmjs.com/package/"]):not([href*="rubygems.org/gems/"]):has(svg)',
43+
)
44+
45+
const process = async (
46+
loc: ReturnType<typeof header.locator>,
47+
type: IconType,
48+
) => {
49+
if (collected.has(type)) return
50+
if (await loc.count()) {
51+
const btn = loc.first()
52+
await btn.scrollIntoViewIfNeeded()
53+
const svg = btn.locator("svg").first()
54+
const box = await svg.boundingBox()
55+
const href = await btn.getAttribute("href")
56+
expect(
57+
box,
58+
`${type} icon SVG should have a bounding box`,
59+
).not.toBeNull()
60+
expect(
61+
box!.width,
62+
`${type} icon SVG width should be > 0 (${href})`,
63+
).toBeGreaterThan(0)
64+
expect(
65+
box!.height,
66+
`${type} icon SVG height should be > 0 (${href})`,
67+
).toBeGreaterThan(0)
68+
collected.set(type, {
69+
href: href || "",
70+
width: box!.width,
71+
height: box!.height,
72+
})
73+
if (firstHeaderWithAnyIconIndex === null) {
74+
firstHeaderWithAnyIconIndex = i
75+
}
76+
}
77+
}
78+
79+
await process(siteLink, "site")
80+
await process(githubLink, "github")
81+
await process(npmLink, "npm")
82+
await process(gemLink, "gem")
83+
}
84+
85+
const missing: IconType[] = []
86+
for (const t of ["site", "github", "npm", "gem"] as IconType[]) {
87+
if (!collected.has(t)) missing.push(t)
88+
}
89+
90+
expect(
91+
missing,
92+
missing.length
93+
? `Missing icon types: ${missing.join(", ")}.\nCollected: ${JSON.stringify(
94+
Object.fromEntries(collected),
95+
null,
96+
2,
97+
)}`
98+
: "All icon types collected.",
99+
).toHaveLength(0)
100+
101+
expect(firstHeaderWithAnyIconIndex).not.toBeNull()
102+
const headerForScreenshot = headers.nth(firstHeaderWithAnyIconIndex!)
103+
const headerScreenshot = await headerForScreenshot.screenshot()
104+
expect(
105+
headerScreenshot.length,
106+
// we had a bug where SVGs were rendered at 0x0 px.
107+
"Screenshot should be visually visible",
108+
).toBeGreaterThan(100)
109+
})
Lines changed: 108 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,119 @@
11
import { test, expect, type Locator } from "@playwright/test"
22

3-
test.describe("interactive examples", () => {
4-
test("adds appearsIn field to hero query and gets correct response", async ({
5-
page,
6-
}) => {
7-
await page.goto("/learn")
8-
await page.waitForSelector(".cm-editor", { timeout: 10000 })
9-
10-
const editors = page.locator(".cm-editor")
11-
let heroEditor: Locator | null = null
12-
13-
for (let i = 0; i < (await editors.count()); i++) {
14-
const editor = editors.nth(i)
15-
const content = await editor.textContent()
16-
if (content && content.includes("hero")) {
17-
heroEditor = editor
18-
break
19-
}
3+
test("adds appearsIn field to hero query and gets correct response", async ({
4+
page,
5+
}) => {
6+
await page.goto("/learn")
7+
await page.waitForSelector(".cm-editor", { timeout: 10000 })
8+
9+
const editors = page.locator(".cm-editor")
10+
let heroEditor: Locator | null = null
11+
12+
for (let i = 0; i < (await editors.count()); i++) {
13+
const editor = editors.nth(i)
14+
const content = await editor.textContent()
15+
if (content && content.includes("hero")) {
16+
heroEditor = editor
17+
break
2018
}
21-
22-
if (!heroEditor) {
23-
throw new Error("Could not find hero GraphQL editor")
19+
}
20+
21+
if (!heroEditor) {
22+
throw new Error("Could not find hero GraphQL editor")
23+
}
24+
25+
const codeLines = heroEditor.locator(".cm-line")
26+
27+
// Find the line containing "name" and click after it
28+
for (let i = 0; i < (await codeLines.count()); i++) {
29+
const line = codeLines.nth(i)
30+
const lineText = await line.textContent()
31+
if (lineText && lineText.includes("name")) {
32+
await line.click()
33+
// Move to end of line
34+
await page.keyboard.press("End")
35+
// Add new line
36+
await page.keyboard.press("Enter")
37+
break
2438
}
39+
}
2540

26-
const codeLines = heroEditor.locator(".cm-line")
27-
28-
// Find the line containing "name" and click after it
29-
for (let i = 0; i < (await codeLines.count()); i++) {
30-
const line = codeLines.nth(i)
31-
const lineText = await line.textContent()
32-
if (lineText && lineText.includes("name")) {
33-
await line.click()
34-
// Move to end of line
35-
await page.keyboard.press("End")
36-
// Add new line
37-
await page.keyboard.press("Enter")
38-
break
39-
}
40-
}
41+
await page.keyboard.type("ap")
42+
await page.keyboard.press("Control+Space")
4143

42-
await page.keyboard.type("ap")
43-
await page.keyboard.press("Control+Space")
44+
const autoCompleteMenu = page.locator(".cm-tooltip-autocomplete")
45+
await expect(autoCompleteMenu).toBeVisible({ timeout: 5000 })
4446

45-
const autoCompleteMenu = page.locator(".cm-tooltip-autocomplete")
46-
await expect(autoCompleteMenu).toBeVisible({ timeout: 5000 })
47+
const appearsInSuggestion = page
48+
.locator(".cm-completionLabel")
49+
.filter({ hasText: "appearsIn" })
4750

48-
const appearsInSuggestion = page
49-
.locator(".cm-completionLabel")
50-
.filter({ hasText: "appearsIn" })
51+
expect(page.locator(".cm-completionDetail").first()).toHaveText("[Episode]!")
5152

52-
expect(page.locator(".cm-completionDetail").first()).toHaveText(
53-
"[Episode]!",
54-
)
53+
if (await appearsInSuggestion.isVisible()) {
54+
await appearsInSuggestion.click()
55+
} else {
56+
await page.keyboard.press("Enter")
57+
}
5558

56-
if (await appearsInSuggestion.isVisible()) {
57-
await appearsInSuggestion.click()
58-
} else {
59-
await page.keyboard.press("Enter")
59+
const resultViewer = page.locator(".result-window").first()
60+
await expect(resultViewer).toBeVisible()
61+
62+
await expect
63+
.poll(async () => {
64+
const resultContent = await resultViewer.textContent()
65+
const jsonMatch = resultContent?.match(/\{[\s\S]*\}/)
66+
if (jsonMatch) {
67+
const responseJson = JSON.parse(jsonMatch[0])
68+
return responseJson
69+
}
70+
71+
return {}
72+
})
73+
.toStrictEqual({
74+
data: {
75+
hero: {
76+
name: "R2-D2",
77+
appearsIn: ["NEWHOPE", "EMPIRE", "JEDI"],
78+
},
79+
},
80+
})
81+
})
82+
83+
test("edits variables and receives an expected mutation result", async ({
84+
page,
85+
}) => {
86+
await page.goto("/learn/mutations")
87+
await page.waitForLoadState("networkidle")
88+
89+
// Find the mutation example that has GraphiQL enabled
90+
const editors = page.locator(".cm-editor")
91+
let mutationEditor: Locator | null = null
92+
93+
for (let i = 0; i < (await editors.count()); i++) {
94+
const editor = editors.nth(i)
95+
const content = await editor.textContent()
96+
if (content && content.includes("CreateReviewForEpisode")) {
97+
mutationEditor = editor
98+
break
6099
}
100+
}
61101

62-
const resultViewer = page.locator(".result-window").first()
102+
if (!mutationEditor) {
103+
throw new Error("Could not find mutation GraphQL editor")
104+
}
105+
106+
const variableEditor = mutationEditor.locator(".variable-editor").first()
107+
108+
if (await variableEditor.isVisible()) {
109+
await variableEditor.click()
110+
111+
await page.getByText('"This is a great movie!"').first().click()
112+
await page.keyboard.press("ControlOrMeta+ArrowRight")
113+
for (let i = 0; i < 4; i++) await page.keyboard.press("Alt+Shift+ArrowLeft")
114+
await page.keyboard.type('almost as good as Andor"')
115+
116+
const resultViewer = mutationEditor.locator(".result-window")
63117
await expect(resultViewer).toBeVisible()
64118

65119
await expect
@@ -70,74 +124,15 @@ test.describe("interactive examples", () => {
70124
const responseJson = JSON.parse(jsonMatch[0])
71125
return responseJson
72126
}
73-
74127
return {}
75128
})
76129
.toStrictEqual({
77130
data: {
78-
hero: {
79-
name: "R2-D2",
80-
appearsIn: ["NEWHOPE", "EMPIRE", "JEDI"],
131+
createReview: {
132+
stars: 5,
133+
commentary: "This is almost as good as Andor",
81134
},
82135
},
83136
})
84-
})
85-
86-
test("edits variables and receives an expected mutation result", async ({
87-
page,
88-
}) => {
89-
await page.goto("/learn/mutations")
90-
await page.waitForLoadState("networkidle")
91-
92-
// Find the mutation example that has GraphiQL enabled
93-
const editors = page.locator(".cm-editor")
94-
let mutationEditor: Locator | null = null
95-
96-
for (let i = 0; i < (await editors.count()); i++) {
97-
const editor = editors.nth(i)
98-
const content = await editor.textContent()
99-
if (content && content.includes("CreateReviewForEpisode")) {
100-
mutationEditor = editor
101-
break
102-
}
103-
}
104-
105-
if (!mutationEditor) {
106-
throw new Error("Could not find mutation GraphQL editor")
107-
}
108-
109-
const variableEditor = mutationEditor.locator(".variable-editor").first()
110-
111-
if (await variableEditor.isVisible()) {
112-
await variableEditor.click()
113-
114-
await page.getByText('"This is a great movie!"').first().click()
115-
await page.keyboard.press("ControlOrMeta+ArrowRight")
116-
for (let i = 0; i < 4; i++)
117-
await page.keyboard.press("Alt+Shift+ArrowLeft")
118-
await page.keyboard.type('almost as good as Andor"')
119-
120-
const resultViewer = mutationEditor.locator(".result-window")
121-
await expect(resultViewer).toBeVisible()
122-
123-
await expect
124-
.poll(async () => {
125-
const resultContent = await resultViewer.textContent()
126-
const jsonMatch = resultContent?.match(/\{[\s\S]*\}/)
127-
if (jsonMatch) {
128-
const responseJson = JSON.parse(jsonMatch[0])
129-
return responseJson
130-
}
131-
return {}
132-
})
133-
.toStrictEqual({
134-
data: {
135-
createReview: {
136-
stars: 5,
137-
commentary: "This is almost as good as Andor",
138-
},
139-
},
140-
})
141-
}
142-
})
137+
}
143138
})

0 commit comments

Comments
 (0)