Skip to content

Commit 1d6c9db

Browse files
committed
test: 11 edge case E2E tests for demo transliteration
Tests boundary conditions beyond the happy path: - Empty input, single character - Very long input (1200+ chars) - Rapid typing (no debounce issues) - Mixed scripts (Latin + Cyrillic) - Special characters (newlines, tabs, emoji surrogate pairs) - Numbers and punctuation passthrough - System switching preserves input - Input that produces no transformation - Character counter updates Total E2E: 38 tests (27 happy path + 11 edge cases), all passing.
1 parent 8a5c182 commit 1d6c9db

1 file changed

Lines changed: 173 additions & 0 deletions

File tree

e2e/demo-edge-cases.spec.ts

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/**
2+
* Edge case E2E tests for the /demo transliteration page.
3+
*
4+
* Tests boundary conditions, unicode edge cases, performance,
5+
* and error recovery that the happy-path tests don't cover.
6+
*/
7+
import { test, expect } from "@playwright/test"
8+
9+
test.describe("demo edge cases", () => {
10+
test("handles empty input", async ({ page }) => {
11+
await page.goto("/demo")
12+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
13+
14+
const textarea = page.locator("textarea.pane-body")
15+
await textarea.fill("")
16+
await page.waitForTimeout(300)
17+
18+
const output = page.locator(".pane-result")
19+
const text = await output.textContent()
20+
expect(text).toBeTruthy()
21+
})
22+
23+
test("handles single character", async ({ page }) => {
24+
await page.goto("/demo")
25+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
26+
27+
const textarea = page.locator("textarea.pane-body")
28+
await textarea.fill("А")
29+
await page.waitForTimeout(300)
30+
31+
const output = page.locator(".pane-result")
32+
const text = (await output.textContent())?.trim()
33+
expect(text?.length).toBeGreaterThan(0)
34+
})
35+
36+
test("handles very long input (1000+ chars)", async ({ page }) => {
37+
await page.goto("/demo")
38+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
39+
40+
const longText = "Антон ".repeat(200) // ~1200 chars
41+
const textarea = page.locator("textarea.pane-body")
42+
await textarea.fill(longText)
43+
await page.waitForTimeout(500)
44+
45+
const output = page.locator(".pane-result")
46+
const text = (await output.textContent())?.trim()
47+
expect(text?.length).toBeGreaterThan(100)
48+
expect(text).toContain("Anton")
49+
})
50+
51+
test("handles rapid typing without errors", async ({ page }) => {
52+
await page.goto("/demo")
53+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
54+
55+
const textarea = page.locator("textarea.pane-body")
56+
// Type rapidly character by character
57+
for (const char of "АнтонМир") {
58+
await textarea.type(char, { delay: 10 })
59+
}
60+
await page.waitForTimeout(500)
61+
62+
const errorBanner = page.locator(".error-banner")
63+
await expect(errorBanner).not.toBeVisible()
64+
})
65+
66+
test("handles mixed scripts", async ({ page }) => {
67+
await page.goto("/demo")
68+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
69+
70+
const textarea = page.locator("textarea.pane-body")
71+
await textarea.fill("Hello Антон мир 123")
72+
await page.waitForTimeout(300)
73+
74+
const output = page.locator(".pane-result")
75+
const text = (await output.textContent())?.trim()
76+
expect(text?.length).toBeGreaterThan(0)
77+
// Latin chars should pass through
78+
expect(text).toContain("Hello")
79+
})
80+
81+
test("handles special characters (newlines, tabs)", async ({ page }) => {
82+
await page.goto("/demo")
83+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
84+
85+
const textarea = page.locator("textarea.pane-body")
86+
await textarea.fill("Антон\nМир\tПривет")
87+
await page.waitForTimeout(300)
88+
89+
const output = page.locator(".pane-result")
90+
const text = (await output.textContent())?.trim()
91+
expect(text?.length).toBeGreaterThan(0)
92+
})
93+
94+
test("handles numbers and punctuation", async ({ page }) => {
95+
await page.goto("/demo")
96+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
97+
98+
const textarea = page.locator("textarea.pane-body")
99+
await textarea.fill("Антон123!@#")
100+
await page.waitForTimeout(300)
101+
102+
const output = page.locator(".pane-result")
103+
const text = (await output.textContent())?.trim()
104+
expect(text).toContain("123")
105+
})
106+
107+
test("handles unicode surrogate pairs (emoji)", async ({ page }) => {
108+
await page.goto("/demo")
109+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
110+
111+
const textarea = page.locator("textarea.pane-body")
112+
await textarea.fill("Антон 🎉 мир")
113+
await page.waitForTimeout(300)
114+
115+
const output = page.locator(".pane-result")
116+
const text = (await output.textContent())?.trim()
117+
// Emoji should pass through (not crash)
118+
expect(text).toBeTruthy()
119+
})
120+
121+
test("system switching preserves input", async ({ page }) => {
122+
await page.goto("/demo")
123+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
124+
125+
const textarea = page.locator("textarea.pane-body")
126+
const select = page.locator("select.field-input")
127+
128+
// Type in Ukrainian
129+
await textarea.fill("Антон")
130+
await page.waitForTimeout(300)
131+
132+
// Switch to Russian
133+
await select.selectOption("odni-rus-Cyrl-Latn-2015")
134+
await page.waitForTimeout(300)
135+
136+
// Input should be preserved
137+
const inputValue = await textarea.inputValue()
138+
expect(inputValue).toBe("Антон")
139+
140+
// Output should change (different system)
141+
const output = page.locator(".pane-result")
142+
const text = (await output.textContent())?.trim()
143+
expect(text?.length).toBeGreaterThan(0)
144+
})
145+
146+
test("handles input that produces no transformation", async ({ page }) => {
147+
await page.goto("/demo")
148+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
149+
150+
const textarea = page.locator("textarea.pane-body")
151+
// Pure Latin text — should pass through unchanged for Cyrillic→Latin map
152+
await textarea.fill("Hello World")
153+
await page.waitForTimeout(300)
154+
155+
const output = page.locator(".pane-result")
156+
const text = (await output.textContent())?.trim()
157+
expect(text).toBe("Hello World")
158+
})
159+
160+
test("character counter updates correctly", async ({ page }) => {
161+
await page.goto("/demo")
162+
await expect(page.locator(".rail-status")).toContainText("ready", { timeout: 20000 })
163+
164+
const textarea = page.locator("textarea.pane-body")
165+
await textarea.fill("Hi")
166+
await page.waitForTimeout(300)
167+
168+
// Look for character count indicator
169+
const meta = page.locator(".pane-meta").first()
170+
const metaText = await meta.textContent()
171+
expect(metaText).toMatch(/\d+/)
172+
})
173+
})

0 commit comments

Comments
 (0)