Skip to content

Commit e2b0df6

Browse files
committed
Add Playwright tests for web app (19 tests)
1 parent a9de3c6 commit e2b0df6

File tree

1 file changed

+296
-0
lines changed

1 file changed

+296
-0
lines changed

tests/web-app.spec.ts

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
import { test, expect } from '@playwright/test'
2+
import { readFileSync } from 'fs'
3+
import { join, dirname } from 'path'
4+
import { fileURLToPath } from 'url'
5+
6+
const __filename = fileURLToPath(import.meta.url)
7+
const __dirname = dirname(__filename)
8+
const WEB_APP_URL = 'file://' + join(__dirname, '../docs/app/index.html')
9+
10+
test.describe('Web App - Browser Version', () => {
11+
test.beforeEach(async ({ page }) => {
12+
await page.goto(WEB_APP_URL)
13+
// Wait for page to load
14+
await page.waitForSelector('.upload-area')
15+
})
16+
17+
test('loads the page correctly', async ({ page }) => {
18+
// Check header
19+
await expect(page.locator('header .logo span')).toHaveText('maskr')
20+
await expect(page.locator('.badge')).toHaveText('Web Version')
21+
22+
// Check privacy badge
23+
await expect(page.locator('.privacy-badge')).toContainText('100% Client-Side')
24+
25+
// Check steps are visible
26+
await expect(page.locator('.step').first()).toHaveClass(/active/)
27+
await expect(page.locator('.step-label').first()).toHaveText('Upload')
28+
})
29+
30+
test('can toggle text input area', async ({ page }) => {
31+
// Initially hidden
32+
await expect(page.locator('#textInputArea')).not.toHaveClass(/visible/)
33+
34+
// Click toggle button
35+
await page.click('#toggleTextInput')
36+
37+
// Now visible
38+
await expect(page.locator('#textInputArea')).toHaveClass(/visible/)
39+
})
40+
41+
test('analyze button is disabled until text is entered', async ({ page }) => {
42+
await page.click('#toggleTextInput')
43+
44+
// Initially disabled
45+
await expect(page.locator('#analyzeTextBtn')).toBeDisabled()
46+
47+
// Type some text
48+
await page.fill('#textInput', 'Hello world')
49+
50+
// Now enabled
51+
await expect(page.locator('#analyzeTextBtn')).toBeEnabled()
52+
})
53+
54+
test('detects email addresses from text input', async ({ page }) => {
55+
await page.click('#toggleTextInput')
56+
await page.fill('#textInput', 'Contact us at test@example.com for more info.')
57+
await page.click('#analyzeTextBtn')
58+
59+
// Wait for detection to complete
60+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
61+
62+
// Check detection table has email
63+
const table = page.locator('#detectionTable')
64+
await expect(table).toContainText('email')
65+
await expect(table).toContainText('test@example.com')
66+
})
67+
68+
test('detects phone numbers from text input', async ({ page }) => {
69+
await page.click('#toggleTextInput')
70+
await page.fill('#textInput', 'Call me at +1 555-123-4567 tomorrow.')
71+
await page.click('#analyzeTextBtn')
72+
73+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
74+
75+
const table = page.locator('#detectionTable')
76+
await expect(table).toContainText('phone')
77+
await expect(table).toContainText('+1 555-123-4567')
78+
})
79+
80+
test('detects credit card numbers from text input', async ({ page }) => {
81+
await page.click('#toggleTextInput')
82+
await page.fill('#textInput', 'My card is 4111 1111 1111 1111')
83+
await page.click('#analyzeTextBtn')
84+
85+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
86+
87+
const table = page.locator('#detectionTable')
88+
await expect(table).toContainText('credit_card')
89+
await expect(table).toContainText('4111 1111 1111 1111')
90+
})
91+
92+
test('detects SSN from text input', async ({ page }) => {
93+
await page.click('#toggleTextInput')
94+
await page.fill('#textInput', 'SSN: 123-45-6789')
95+
await page.click('#analyzeTextBtn')
96+
97+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
98+
99+
const table = page.locator('#detectionTable')
100+
await expect(table).toContainText('ssn')
101+
await expect(table).toContainText('123-45-6789')
102+
})
103+
104+
test('detects Saudi ID from text input', async ({ page }) => {
105+
await page.click('#toggleTextInput')
106+
await page.fill('#textInput', 'ID number: 1234567890')
107+
await page.click('#analyzeTextBtn')
108+
109+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
110+
111+
const table = page.locator('#detectionTable')
112+
await expect(table).toContainText('saudi_id')
113+
await expect(table).toContainText('1234567890')
114+
})
115+
116+
test('detects IBAN from text input', async ({ page }) => {
117+
await page.click('#toggleTextInput')
118+
await page.fill('#textInput', 'Bank account: SA0380000000608010167519')
119+
await page.click('#analyzeTextBtn')
120+
121+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
122+
123+
const table = page.locator('#detectionTable')
124+
await expect(table).toContainText('iban')
125+
await expect(table).toContainText('SA0380000000608010167519')
126+
})
127+
128+
test('detects URLs from text input', async ({ page }) => {
129+
await page.click('#toggleTextInput')
130+
await page.fill('#textInput', 'Visit https://example.com/page for details')
131+
await page.click('#analyzeTextBtn')
132+
133+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
134+
135+
const table = page.locator('#detectionTable')
136+
await expect(table).toContainText('url')
137+
await expect(table).toContainText('https://example.com/page')
138+
})
139+
140+
test('detects IP addresses from text input', async ({ page }) => {
141+
await page.click('#toggleTextInput')
142+
await page.fill('#textInput', 'Server IP: 192.168.1.100')
143+
await page.click('#analyzeTextBtn')
144+
145+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
146+
147+
const table = page.locator('#detectionTable')
148+
await expect(table).toContainText('ip')
149+
await expect(table).toContainText('192.168.1.100')
150+
})
151+
152+
test('detects financial amounts from text input', async ({ page }) => {
153+
await page.click('#toggleTextInput')
154+
await page.fill('#textInput', 'The total is $1,500.00')
155+
await page.click('#analyzeTextBtn')
156+
157+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
158+
159+
const table = page.locator('#detectionTable')
160+
await expect(table).toContainText('financial')
161+
await expect(table).toContainText('$1,500.00')
162+
})
163+
164+
test('custom names are detected with 100% confidence', async ({ page }) => {
165+
// Add custom name
166+
await page.fill('#customNamesInput', 'John Doe')
167+
168+
await page.click('#toggleTextInput')
169+
await page.fill('#textInput', 'Contact John Doe for more information.')
170+
await page.click('#analyzeTextBtn')
171+
172+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
173+
174+
const table = page.locator('#detectionTable')
175+
await expect(table).toContainText('person')
176+
await expect(table).toContainText('John Doe')
177+
await expect(table).toContainText('100%')
178+
})
179+
180+
test('can navigate through all steps', async ({ page }) => {
181+
// Step 1: Enter text
182+
await page.click('#toggleTextInput')
183+
await page.fill('#textInput', 'Email: test@example.com')
184+
await page.click('#analyzeTextBtn')
185+
186+
// Step 2: Review
187+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
188+
await expect(page.locator('.step').nth(1)).toHaveClass(/active/)
189+
190+
// Continue to Step 3
191+
await page.click('#continueToExport')
192+
await page.waitForSelector('#step3:not(.hidden)')
193+
await expect(page.locator('.step').nth(2)).toHaveClass(/active/)
194+
195+
// Check previews are populated
196+
await expect(page.locator('#originalPreview')).toContainText('test@example.com')
197+
await expect(page.locator('#sanitizedPreview')).toContainText('<EMAIL_1>')
198+
199+
// Go back to Step 2
200+
await page.click('#backToReview')
201+
await expect(page.locator('#step2')).not.toHaveClass(/hidden/)
202+
203+
// Go back to Step 1
204+
await page.click('#backToUpload')
205+
await expect(page.locator('#step1')).not.toHaveClass(/hidden/)
206+
})
207+
208+
test('can toggle detection items on/off', async ({ page }) => {
209+
await page.click('#toggleTextInput')
210+
await page.fill('#textInput', 'Email: test@example.com')
211+
await page.click('#analyzeTextBtn')
212+
213+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
214+
215+
// Toggle off the detection by unchecking the checkbox via JS
216+
await page.evaluate(() => {
217+
const checkbox = document.querySelector('#detectionTable input[type="checkbox"]') as HTMLInputElement
218+
if (checkbox) {
219+
checkbox.checked = false
220+
checkbox.dispatchEvent(new Event('change', { bubbles: true }))
221+
}
222+
})
223+
224+
// Continue to export
225+
await page.click('#continueToExport')
226+
await page.waitForSelector('#step3:not(.hidden)')
227+
228+
// Original email should still be in sanitized output since we disabled the detection
229+
await expect(page.locator('#sanitizedPreview')).toContainText('test@example.com')
230+
})
231+
232+
test('copy to clipboard button exists', async ({ page }) => {
233+
await page.click('#toggleTextInput')
234+
await page.fill('#textInput', 'Email: test@example.com')
235+
await page.click('#analyzeTextBtn')
236+
237+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
238+
await page.click('#continueToExport')
239+
await page.waitForSelector('#step3:not(.hidden)')
240+
241+
await expect(page.locator('#copyBtn')).toBeVisible()
242+
await expect(page.locator('#downloadBtn')).toBeVisible()
243+
})
244+
245+
test('stats are displayed correctly', async ({ page }) => {
246+
await page.click('#toggleTextInput')
247+
await page.fill('#textInput', 'Email: test@example.com, Phone: +1 555-123-4567')
248+
await page.click('#analyzeTextBtn')
249+
250+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
251+
252+
// Check stats section exists and has values
253+
const stats = page.locator('#stats .stat')
254+
await expect(stats).toHaveCount(2) // email and phone
255+
})
256+
257+
test('multiple detections of same type get unique placeholders', async ({ page }) => {
258+
await page.click('#toggleTextInput')
259+
await page.fill('#textInput', 'Contact alice@test.com or bob@test.com')
260+
await page.click('#analyzeTextBtn')
261+
262+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
263+
264+
// Continue to export to see placeholders in action
265+
await page.click('#continueToExport')
266+
await page.waitForSelector('#step3:not(.hidden)')
267+
268+
// Check that both emails are replaced with unique placeholders
269+
const sanitized = page.locator('#sanitizedPreview')
270+
await expect(sanitized).toContainText('EMAIL_1')
271+
await expect(sanitized).toContainText('EMAIL_2')
272+
})
273+
274+
test('detects multiple entity types in one document', async ({ page }) => {
275+
await page.click('#toggleTextInput')
276+
await page.fill('#textInput', `
277+
Contact: john@example.com
278+
Phone: +966 512345678
279+
Card: 4111 1111 1111 1111
280+
SSN: 123-45-6789
281+
IBAN: SA0380000000608010167519
282+
Amount: $5,000
283+
`)
284+
await page.click('#analyzeTextBtn')
285+
286+
await page.waitForSelector('#step2:not(.hidden)', { timeout: 10000 })
287+
288+
const table = page.locator('#detectionTable')
289+
await expect(table).toContainText('email')
290+
await expect(table).toContainText('phone')
291+
await expect(table).toContainText('credit_card')
292+
await expect(table).toContainText('ssn')
293+
await expect(table).toContainText('iban')
294+
await expect(table).toContainText('financial')
295+
})
296+
})

0 commit comments

Comments
 (0)