Skip to content

Commit 8424ea2

Browse files
committed
e2e(ui): add browser helper and new smoke tests
Introduce launchBrowserOrSkip helper to centralize Chromium launch and skip logic when not installed, and refactor existing smoke test to use it. Add several end-to-end UI smoke tests: successful create submission (stubbing /create), client-side JSON validation that prevents network calls, query form results (stubbing /query), and overwrite conflict handling (stubbing /overwrite returning 409). Tests use Playwright routing to mock server responses and assert flash messages and object viewer output.
1 parent 091234b commit 8424ea2

1 file changed

Lines changed: 138 additions & 8 deletions

File tree

test/e2e/ui-smoke.test.js

Lines changed: 138 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ import app from "../../app.js"
88
let server
99
let baseUrl
1010

11+
async function launchBrowserOrSkip(t) {
12+
try {
13+
return await chromium.launch({ headless: true })
14+
}
15+
catch (error) {
16+
t.skip(`Chromium is not installed. Run 'npm run e2e:install'. ${error.message}`)
17+
return null
18+
}
19+
}
20+
1121
before(async () => {
1222
server = http.createServer(app)
1323
await new Promise(resolve => {
@@ -26,14 +36,8 @@ after(async () => {
2636

2737
describe("TinyNode browser smoke checks. __e2e", () => {
2838
it("Loads index page and toggles forms from the button panel. __e2e", async t => {
29-
let browser
30-
try {
31-
browser = await chromium.launch({ headless: true })
32-
}
33-
catch (error) {
34-
t.skip(`Chromium is not installed. Run 'npm run e2e:install'. ${error.message}`)
35-
return
36-
}
39+
const browser = await launchBrowserOrSkip(t)
40+
if (!browser) return
3741

3842
try {
3943
const page = await browser.newPage()
@@ -57,4 +61,130 @@ describe("TinyNode browser smoke checks. __e2e", () => {
5761
await browser.close()
5862
}
5963
})
64+
65+
it("Submits create form and renders success message and object payload. __e2e", async t => {
66+
const browser = await launchBrowserOrSkip(t)
67+
if (!browser) return
68+
69+
try {
70+
const page = await browser.newPage()
71+
await page.route("**/create", async route => {
72+
await route.fulfill({
73+
status: 201,
74+
contentType: "application/json",
75+
body: JSON.stringify({
76+
"@id": "https://devstore.rerum.io/v1/id/e2e-created",
77+
test: "created"
78+
})
79+
})
80+
})
81+
82+
await page.goto(`${baseUrl}/index.html`, { waitUntil: "domcontentloaded" })
83+
await page.fill("#createJSON", JSON.stringify({ test: "created" }))
84+
await page.click('form#create button[type="submit"]')
85+
86+
await page.waitForSelector('#flash-message:not([style*="display: none"])')
87+
const message = await page.locator("#flash-message").textContent()
88+
const objectView = await page.locator("#obj-viewer").textContent()
89+
90+
assert.match(message ?? "", /Created new object at/i)
91+
assert.match(objectView ?? "", /e2e-created/)
92+
assert.match(objectView ?? "", /"test":\s*"created"/)
93+
}
94+
finally {
95+
await browser.close()
96+
}
97+
})
98+
99+
it("Blocks invalid create JSON in the client before issuing network calls. __e2e", async t => {
100+
const browser = await launchBrowserOrSkip(t)
101+
if (!browser) return
102+
103+
try {
104+
const page = await browser.newPage()
105+
let createRequests = 0
106+
await page.route("**/create", async route => {
107+
createRequests += 1
108+
await route.fulfill({ status: 500, body: "should not be called" })
109+
})
110+
111+
await page.goto(`${baseUrl}/index.html`, { waitUntil: "domcontentloaded" })
112+
await page.fill("#createJSON", "{not valid json}")
113+
await page.click('form#create button[type="submit"]')
114+
115+
await page.waitForSelector('#flash-message:not([style*="display: none"])')
116+
const message = await page.locator("#flash-message").textContent()
117+
assert.match(message ?? "", /You did not provide valid JSON/i)
118+
assert.equal(createRequests, 0)
119+
}
120+
finally {
121+
await browser.close()
122+
}
123+
})
124+
125+
it("Submits query form and renders returned results. __e2e", async t => {
126+
const browser = await launchBrowserOrSkip(t)
127+
if (!browser) return
128+
129+
try {
130+
const page = await browser.newPage()
131+
await page.route("**/query", async route => {
132+
await route.fulfill({
133+
status: 200,
134+
contentType: "application/json",
135+
body: JSON.stringify([
136+
{ "@id": "https://devstore.rerum.io/v1/id/query-hit", label: "found" }
137+
])
138+
})
139+
})
140+
141+
await page.goto(`${baseUrl}/index.html`, { waitUntil: "domcontentloaded" })
142+
await page.click('.button-panel > button[data-lang-key="queryBtn"]')
143+
await page.fill("#queryKey", "label")
144+
await page.fill("#queryValue", "found")
145+
await page.click('form#query button[type="submit"]')
146+
147+
await page.waitForSelector('#flash-message:not([style*="display: none"])')
148+
const message = await page.locator("#flash-message").textContent()
149+
const objectView = await page.locator("#obj-viewer").textContent()
150+
151+
assert.match(message ?? "", /matching results/i)
152+
assert.match(objectView ?? "", /query-hit/)
153+
assert.match(objectView ?? "", /"label":\s*"found"/)
154+
}
155+
finally {
156+
await browser.close()
157+
}
158+
})
159+
160+
it("Shows a conflict error when overwrite returns 409. __e2e", async t => {
161+
const browser = await launchBrowserOrSkip(t)
162+
if (!browser) return
163+
164+
try {
165+
const page = await browser.newPage()
166+
await page.route("**/overwrite", async route => {
167+
await route.fulfill({
168+
status: 409,
169+
statusText: "Conflict",
170+
contentType: "application/json",
171+
body: JSON.stringify({ message: "Version conflict", currentVersion: { "@id": "v2" } })
172+
})
173+
})
174+
175+
await page.goto(`${baseUrl}/index.html`, { waitUntil: "domcontentloaded" })
176+
await page.click('.button-panel > button[data-lang-key="overwriteBtn"]')
177+
await page.fill("#overwriteId", "https://devstore.rerum.io/v1/id/overwrite-target")
178+
await page.fill("#overwriteJSON", JSON.stringify({ testing: "overwrite" }))
179+
await page.click('form#overwrite button[type="submit"]')
180+
181+
await page.waitForSelector('#flash-message:not([style*="display: none"])')
182+
const message = await page.locator("#flash-message").textContent()
183+
assert.match(message ?? "", /Conflict detected while trying to overwrite object/i)
184+
assert.match(message ?? "", /409/i)
185+
}
186+
finally {
187+
await browser.close()
188+
}
189+
})
60190
})

0 commit comments

Comments
 (0)