// All responses are fixtures. No real emails or issue updates. const assert = require('node:assert/strict') const { chromium } = require(process.env.REVIEW_PLAYWRIGHT || 'playwright') const base = process.env.REVIEW_BASE || 'http://127.0.0.1:3101' ;(async () => { const browser = await chromium.launch({ headless: true }) try { const context = await browser.newContext() await context.addCookies([{ name: 'magent_logged_in', value: '1', url: base }]) let status = 'awaiting_confirmation', canConfirm = true, unauthorized = false const answers = [], errors = [] await context.route('**/api/**', (route) => { const path = new URL(route.request().url()).pathname const reply = (json) => route.fulfill({ json }) if (path === '/api/auth/me') return reply({ username: 'Member', role: 'user' }) if (path === '/api/auth/logout') return reply({ status: 'ok' }) if (path === '/api/auth/login' || path === '/api/auth/jellyfin/login') { unauthorized = false; status = 'awaiting_confirmation'; return reply({ authenticated: true }) } if (path === '/api/portal/items/12') return unauthorized ? route.fulfill({ status: 401, json: { detail: 'Sign in' } }) : reply({ item: { id: 12, kind: 'issue', title: 'Picture broken: Example movie', status, permissions: { can_confirm_resolution: canConfirm } } }) if (path.endsWith('/resolution-response')) { answers.push(route.request().postDataJSON()); return reply({}) } if (route.request().method() !== 'GET') throw Error('Unexpected mutation: ' + path) return reply({ navigation: { showRequests: true }, services: [], login: { showJellyfinLogin: true } }) }) const page = await context.newPage() page.on('pageerror', (error) => errors.push(error.message)) for (const width of [1440, 390]) { await page.setViewportSize({ width, height: 1000 }) for (const yes of [true, false]) { const count = answers.length await page.goto(base + '/issues/confirm/12#' + (yes ? 'yes' : 'no')) await page.reload() const button = page.getByRole('button', { name: yes ? 'YES It works — close this issue' : 'NO Still broken — keep it open', exact: true }) await button.waitFor() assert.equal(await button.evaluate((el) => getComputedStyle(el).backgroundColor), yes ? 'rgb(180, 244, 210)' : 'rgb(255, 193, 197)') assert.equal(answers.length, count, 'Email GET must never submit an answer') const box = await button.boundingBox() assert.ok(box.height >= 100 && box.y + box.height <= 1000, 'Both choices should be large and immediately visible') assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false) if (process.env.REVIEW_DIR) await page.screenshot({ path: `${process.env.REVIEW_DIR}/acceptance-${width}.png` }) await button.click() await page.getByRole('heading', { name: yes ? 'Thanks! Your issue is now closed.' : 'Thanks for letting us know. Your issue stays open for another look.', exact: true }).waitFor() assert.deepEqual(answers.at(-1), { resolved: yes }) } } canConfirm = false await page.goto(base + '/issues/confirm/12') await page.getByRole('heading', { name: 'This question is for the person who reported the issue.' }).waitFor() assert.equal(await page.getByRole('button', { name: /^YES/ }).count(), 0) canConfirm = true; status = 'closed' await page.goto(base + '/issues/confirm/12') await page.getByRole('heading', { name: 'No answer is needed right now.' }).waitFor() unauthorized = true await page.goto(base + '/issues/confirm/12') await page.waitForURL('**/login?next=*') assert.equal(new URL(page.url()).searchParams.get('next'), '/issues/confirm/12') await page.getByLabel('Username', { exact: true }).fill('fixture-user') await page.getByLabel('Password', { exact: true }).fill('fixture-password') await page.getByRole('button', { name: 'Sign in', exact: true }).click() await page.waitForURL('**/issues/confirm/12') await page.getByRole('button', { name: /^YES/ }).waitFor() assert.deepEqual(errors, []) console.log('PASS: prominent desktop/mobile YES/NO, exact answer POSTs, safe email GET, reporter permission, closed issue and sign-in return link; no live writes.') } finally { await browser.close() } })().catch((error) => { console.error(error); process.exitCode = 1 })