From ee4e13680f522253f94e8ebdea5df80332afc495 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 16 May 2026 04:51:57 +0530 Subject: Phase 2 Playwright tests: 10/10 passing (portal, captive detection, API) - Updated from Phase 1 tests to Phase 2 (302 redirects, Cashu token input) - Test captive detection URIs return 302 (using request API) - Test invalid token via request API (no CORS issues) - Tests: portal branding, price, token input, pay button, detection redirects - Tests: whoami, usage, API advertisement, invalid token --- tests/captive-portal.spec.mjs | 73 ++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 32 deletions(-) (limited to 'tests') diff --git a/tests/captive-portal.spec.mjs b/tests/captive-portal.spec.mjs index b6ad96b..acd2a40 100644 --- a/tests/captive-portal.spec.mjs +++ b/tests/captive-portal.spec.mjs @@ -2,8 +2,9 @@ import { test, expect } from '@playwright/test'; const PORTAL_IP = process.env.TOLLGATE_IP || '192.168.4.1'; const PORTAL_URL = `http://${PORTAL_IP}`; +const API_URL = `http://${PORTAL_IP}:2121`; -test.describe('Captive Portal - Phase 1', () => { +test.describe('Captive Portal - Phase 2', () => { test('portal page loads with TollGate branding', async ({ page }) => { await page.goto(PORTAL_URL); @@ -11,65 +12,73 @@ test.describe('Captive Portal - Phase 1', () => { await expect(page.locator('.subtitle')).toContainText('internet access'); }); - test('portal shows price', async ({ page }) => { + test('portal shows price from API', async ({ page }) => { await page.goto(PORTAL_URL); const priceEl = page.locator('.price-amount'); await expect(priceEl).not.toBeEmpty({ timeout: 5000 }); }); - test('grant access button exists', async ({ page }) => { + test('portal has Cashu token input', async ({ page }) => { await page.goto(PORTAL_URL); - const btn = page.locator('#grantBtn'); - await expect(btn).toBeVisible(); - await expect(btn).toHaveText(/Grant Free Access/i); + const textarea = page.locator('#tokenInput'); + await expect(textarea).toBeVisible(); + await expect(textarea).toHaveAttribute('placeholder', /cashuA/); }); - test('click grant access shows connected', async ({ page }) => { + test('portal has Pay & Connect button', async ({ page }) => { await page.goto(PORTAL_URL); - const btn = page.locator('#grantBtn'); - await btn.click(); - const status = page.locator('#status.success'); - await expect(status).toBeVisible({ timeout: 10000 }); - await expect(status).toContainText(/Connected/i); + const btn = page.locator('#payBtn'); + await expect(btn).toBeVisible(); + await expect(btn).toHaveText(/Pay/); }); - test('captive detection URIs return portal', async ({ page }) => { + test('captive detection URIs return 302 redirect', async ({ request }) => { const uris = ['/generate_204', '/hotspot-detect.html', '/canonical.html', '/success.txt']; for (const uri of uris) { - const resp = await page.goto(`${PORTAL_URL}${uri}`); - expect(resp.status()).toBe(200); - const body = await resp.text(); - expect(body).toContain('TollGate'); + const resp = await request.fetch(`${PORTAL_URL}${uri}`, { maxRedirects: 0, ignoreHTTPSErrors: true }); + expect(resp.status()).toBe(302); + const location = resp.headers()['location']; + expect(location).toBe('http://192.168.4.1/'); } }); - test('/api/status returns JSON with price', async ({ page }) => { - const resp = await page.goto(`${PORTAL_URL}/api/status`); - expect(resp.status()).toBe(200); - const data = await resp.json(); - expect(data).toHaveProperty('connected'); - expect(data).toHaveProperty('price'); - expect(typeof data.price).toBe('number'); + test('captive detection redirects to portal page', async ({ page }) => { + await page.goto(`${PORTAL_URL}/generate_204`); + await expect(page.locator('h1')).toHaveText('TollGate'); }); - test('/whoami returns mac address', async ({ page }) => { - const resp = await page.goto(`${PORTAL_URL}/whoami`); + test('/whoami returns ip and mac', async ({ page }) => { + const resp = await page.goto(`${API_URL}/whoami`); expect(resp.status()).toBe(200); const text = await resp.text(); - expect(text).toMatch(/^mac=/); + expect(text).toMatch(/ip=\d+\.\d+\.\d+\.\d+/); + expect(text).toMatch(/mac=[0-9a-f]{2}:/); }); - test('/usage returns -1/-1 before auth', async ({ page }) => { - const resp = await page.goto(`${PORTAL_URL}/usage`); + test('/usage returns -1/-1 before payment', async ({ page }) => { + const resp = await page.goto(`${API_URL}/usage`); expect(resp.status()).toBe(200); const text = await resp.text(); expect(text).toBe('-1/-1'); }); - test('/reset_authentication works', async ({ page }) => { - const resp = await page.goto(`${PORTAL_URL}/reset_authentication`); + test('API advertisement has correct structure', async ({ page }) => { + const resp = await page.goto(API_URL); expect(resp.status()).toBe(200); const data = await resp.json(); - expect(data.status).toBe('reset'); + expect(data.kind).toBe(10021); + expect(data.tags).toBeDefined(); + expect(data.tags.some(t => t[0] === 'price_per_step')).toBe(true); + expect(data.tags.some(t => t[0] === 'step_size')).toBe(true); + }); + + test('invalid token returns error', async ({ request }) => { + const resp = await request.fetch(API_URL, { + method: 'POST', + data: 'garbage_not_a_token' + }); + expect(resp.status()).toBe(400); + const data = await resp.json(); + expect(data.kind).toBe(21023); }); }); -- cgit v1.2.3