diff --git a/frontend/vue-app/auth-setup-after-pin.png b/frontend/vue-app/auth-setup-after-pin.png new file mode 100644 index 0000000..b63f7e6 Binary files /dev/null and b/frontend/vue-app/auth-setup-after-pin.png differ diff --git a/frontend/vue-app/auth-setup-before-pin.png b/frontend/vue-app/auth-setup-before-pin.png new file mode 100644 index 0000000..e2fd3e4 Binary files /dev/null and b/frontend/vue-app/auth-setup-before-pin.png differ diff --git a/frontend/vue-app/auth-setup-before-signin.png b/frontend/vue-app/auth-setup-before-signin.png deleted file mode 100644 index c85213e..0000000 Binary files a/frontend/vue-app/auth-setup-before-signin.png and /dev/null differ diff --git a/frontend/vue-app/auth-setup-parent-fail.png b/frontend/vue-app/auth-setup-parent-fail.png deleted file mode 100644 index cfd0e04..0000000 Binary files a/frontend/vue-app/auth-setup-parent-fail.png and /dev/null differ diff --git a/frontend/vue-app/e2e/.auth/user-no-pin.json b/frontend/vue-app/e2e/.auth/user-no-pin.json index 768de9e..48ff9e5 100644 --- a/frontend/vue-app/e2e/.auth/user-no-pin.json +++ b/frontend/vue-app/e2e/.auth/user-no-pin.json @@ -2,17 +2,17 @@ "cookies": [ { "name": "refresh_token", - "value": "exz9voXnacTUkQGnKkc2QHLZA1DB3-7neit29Gtan5w", + "value": "C3wwythvEFsezN93gTCH0C7TP4UEMJT1CszA66dP9Es", "domain": "localhost", "path": "/api/auth", - "expires": 1780801137.642288, + "expires": 1780853177.47085, "httpOnly": true, "secure": true, "sameSite": "Strict" }, { "name": "access_token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiI2NmQ5Yzk0NC05MzFmLTQyODktOWYxZS1kNzZhODQyZTM0MzIiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzMwMjYwMzd9.gjcizOIYTbdX6B-AobROaoJtMczY-7EnoyUco-b-xE8", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiI2OGFiNGNkNi04Y2NmLTQxNDItOWRmZC1kYjVmZmNmNDQ4OGQiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzMwNzgwNzd9.zZErQX-waP_VILAEaZbNnZmFlGAc6wvNiSQEop0IjsQ", "domain": "localhost", "path": "/", "expires": -1, @@ -27,7 +27,7 @@ "localStorage": [ { "name": "authSyncEvent", - "value": "{\"type\":\"logout\",\"at\":1773025137442}" + "value": "{\"type\":\"logout\",\"at\":1773077177348}" } ] } diff --git a/frontend/vue-app/e2e/.auth/user.json b/frontend/vue-app/e2e/.auth/user.json index f764683..0505dc0 100644 --- a/frontend/vue-app/e2e/.auth/user.json +++ b/frontend/vue-app/e2e/.auth/user.json @@ -2,17 +2,17 @@ "cookies": [ { "name": "refresh_token", - "value": "aQ7Hdjmxefq4F6nLro-Sz0d2qO_3XN3v_tO4ioHOH6w", + "value": "AkJCQm0cJAkwg6CEzwBZMGks62XDowJwEaapsYWLc-o", "domain": "localhost", "path": "/api/auth", - "expires": 1780799347.476442, + "expires": 1780853177.819182, "httpOnly": true, "secure": true, "sameSite": "Strict" }, { "name": "access_token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiIzMzg1YWRjNC0yYmI4LTQyMTktOGFiNi05ZjkxNzAyNzA0MjEiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzMwMjQyNDd9.g3audbmZ_S-Bc5ZfgwvpfoQuJEjCS2vd3dF8baExFEA", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiI5MTBjZmZmNS01NzhjLTRmZDgtYTM1NS1hN2JkYTUyZmE2OGUiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzMwNzgwNzd9.BkKApnds25Nw7wMJ8wQcwPJ-tahduQCC_le_6PT180I", "domain": "localhost", "path": "/", "expires": -1, @@ -27,7 +27,11 @@ "localStorage": [ { "name": "authSyncEvent", - "value": "{\"type\":\"parent_logout\",\"at\":1773023350687}" + "value": "{\"type\":\"logout\",\"at\":1773077177706}" + }, + { + "name": "parentAuth", + "value": "{\"expiresAt\":1773249977951}" } ] } diff --git a/frontend/vue-app/e2e/auth.setup.ts b/frontend/vue-app/e2e/auth.setup.ts index ff90af8..a33a0fa 100644 --- a/frontend/vue-app/e2e/auth.setup.ts +++ b/frontend/vue-app/e2e/auth.setup.ts @@ -21,12 +21,11 @@ setup('authenticate', async ({ page }) => { await page.getByRole('button', { name: 'Parent login' }).click() // Fill in the PIN and submit - const pinInput = page.getByLabel('PIN').or(page.getByPlaceholder('Enter PIN')) + const pinInput = page.getByPlaceholder('4–6 digits') await pinInput.waitFor({ timeout: 5000 }) - await page.screenshot({ path: 'auth-setup-before-pin.png' }) await pinInput.fill(E2E_PIN) - await page.screenshot({ path: 'auth-setup-after-pin.png' }) - await page.getByRole('button', { name: 'Verify' }).click() + await page.getByLabel('Stay in parent mode on this device').check() + await page.getByRole('button', { name: 'OK' }).click() // LoginButton does router.push('/parent') after PIN - wait for it await page.waitForURL(/\/parent(\/|$)/) @@ -36,7 +35,9 @@ setup('authenticate', async ({ page }) => { await page.getByRole('button', { name: 'Add Child' }).waitFor({ timeout: 5000 }) } catch (e) { await page.screenshot({ path: 'auth-setup-parent-fail.png' }) - throw new Error('Parent mode not reached after PIN entry. See auth-setup-parent-fail.png for details.') + throw new Error( + 'Parent mode not reached after PIN entry. See auth-setup-parent-fail.png for details.', + ) } await page.context().storageState({ path: STORAGE_STATE }) diff --git a/frontend/vue-app/e2e/create-child/validation.spec.ts b/frontend/vue-app/e2e/create-child/validation.spec.ts index 5d90bea..3edff9d 100644 --- a/frontend/vue-app/e2e/create-child/validation.spec.ts +++ b/frontend/vue-app/e2e/create-child/validation.spec.ts @@ -3,7 +3,9 @@ import { test, expect } from '@playwright/test' test.describe('Create Child', () => { - test.beforeEach(async ({ page }) => { + test.beforeEach(async ({ page }, testInfo) => { + test.skip(testInfo.project.name === 'chromium-no-pin', 'Requires parent-authenticated mode') + // Navigate to app root - router redirects to /parent (children list) when parent-authenticated await page.goto('/') await page.getByRole('button', { name: 'Add Child' }).click() diff --git a/frontend/vue-app/package-lock.json b/frontend/vue-app/package-lock.json index b589a09..5f16105 100644 --- a/frontend/vue-app/package-lock.json +++ b/frontend/vue-app/package-lock.json @@ -112,7 +112,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -665,7 +664,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -709,7 +707,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -1975,7 +1972,6 @@ "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.4", "@typescript-eslint/types": "8.46.4", @@ -2731,7 +2727,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2926,7 +2921,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -3432,7 +3426,6 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3493,7 +3486,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -3541,7 +3533,6 @@ "integrity": "sha512-SbR9ZBUFKgvWAbq3RrdCtWaW0IKm6wwUiApxf3BVTNfqUIo4IQQmreMg2iHFJJ6C/0wss3LXURBJ1OwS/MhFcQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", @@ -4221,7 +4212,6 @@ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -5046,7 +5036,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -5617,7 +5606,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5756,7 +5744,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5880,7 +5867,6 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -6101,7 +6087,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -6115,7 +6100,6 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -6208,7 +6192,6 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz", "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.24", "@vue/compiler-sfc": "3.5.24", diff --git a/frontend/vue-app/playwright.config.ts b/frontend/vue-app/playwright.config.ts index 502de96..1b3aae6 100644 --- a/frontend/vue-app/playwright.config.ts +++ b/frontend/vue-app/playwright.config.ts @@ -93,7 +93,9 @@ export default defineConfig({ }, { command: - '.venv\\Scripts\\python.exe -m flask run --host=0.0.0.0 --port=5000 --no-debugger --no-reload', + process.platform === 'win32' + ? 'echo Platform: %PROCESS_PLATFORM% && .venv\\Scripts\\python.exe -m flask run --host=0.0.0.0 --port=5000 --no-debugger --no-reload' + : 'echo Platform: $PROCESS_PLATFORM && .venv/bin/python -m flask run --host=0.0.0.0 --port=5000 --no-debugger --no-reload', url: 'http://localhost:5000/version', reuseExistingServer: !process.env.CI, stdout: 'pipe', @@ -106,6 +108,7 @@ export default defineConfig({ DATA_ENV: 'e2e', SECRET_KEY: 'dev-secret-key-change-in-production', REFRESH_TOKEN_EXPIRY_DAYS: '90', + PROCESS_PLATFORM: process.platform, }, }, ], diff --git a/frontend/vue-app/vite.config.ts b/frontend/vue-app/vite.config.ts index c884384..bedcafd 100644 --- a/frontend/vue-app/vite.config.ts +++ b/frontend/vue-app/vite.config.ts @@ -5,7 +5,7 @@ import fs from 'fs' export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), '') - const backendHost = env.VITE_BACKEND_HOST ?? '192.168.1.219' + const backendHost = env.VITE_BACKEND_HOST ?? '127.0.0.1' return { plugins: [vue()],