From a7d0a672d59bf8985a6fc0e61b49015fabd96513 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 15 May 2026 17:03:40 +0530 Subject: Phase 1 working: captive portal, DNS hijack, NAT-based access control - Fix WiFi init order: netif creation before esp_wifi_init, set mode before set_config - Replace broken netif input filter with NAPT on/off per authentication state - NAPT disabled by default, enabled when client granted, disabled on revoke - Fix test helpers: use -I wlp59s0 for ping, handle nslookup exit code 1 - All 20 API tests pass, all 6 smoke tests pass --- tests/captive-portal.spec.mjs | 75 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/captive-portal.spec.mjs (limited to 'tests/captive-portal.spec.mjs') diff --git a/tests/captive-portal.spec.mjs b/tests/captive-portal.spec.mjs new file mode 100644 index 0000000..b6ad96b --- /dev/null +++ b/tests/captive-portal.spec.mjs @@ -0,0 +1,75 @@ +import { test, expect } from '@playwright/test'; + +const PORTAL_IP = process.env.TOLLGATE_IP || '192.168.4.1'; +const PORTAL_URL = `http://${PORTAL_IP}`; + +test.describe('Captive Portal - Phase 1', () => { + + test('portal page loads with TollGate branding', async ({ page }) => { + await page.goto(PORTAL_URL); + await expect(page.locator('h1')).toHaveText('TollGate'); + await expect(page.locator('.subtitle')).toContainText('internet access'); + }); + + test('portal shows price', 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 }) => { + await page.goto(PORTAL_URL); + const btn = page.locator('#grantBtn'); + await expect(btn).toBeVisible(); + await expect(btn).toHaveText(/Grant Free Access/i); + }); + + test('click grant access shows connected', 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); + }); + + test('captive detection URIs return portal', async ({ page }) => { + 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'); + } + }); + + 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('/whoami returns mac address', async ({ page }) => { + const resp = await page.goto(`${PORTAL_URL}/whoami`); + expect(resp.status()).toBe(200); + const text = await resp.text(); + expect(text).toMatch(/^mac=/); + }); + + test('/usage returns -1/-1 before auth', async ({ page }) => { + const resp = await page.goto(`${PORTAL_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`); + expect(resp.status()).toBe(200); + const data = await resp.json(); + expect(data.status).toBe('reset'); + }); +}); -- cgit v1.2.3