// Fixture-only review: every API request is intercepted, including all send actions. const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const { chromium } = require(process.env.REVIEW_PLAYWRIGHT || 'playwright') const base = process.env.REVIEW_BASE || 'http://localhost:3114' const output = process.env.REVIEW_DIR const preview = JSON.parse(fs.readFileSync(process.env.REVIEW_EMAIL_FIXTURE, 'utf8')) const movie = '1'.repeat(32), series = '2'.repeat(32), editionId = 'e'.repeat(32) const clone = (value) => JSON.parse(JSON.stringify(value)) const titles = [{ id: movie, title: 'Arrival', type: 'movie', year: 2016, has_artwork: true, items: [{ id: movie }], selected: true, featured: false }, { id: series, title: 'Severance', type: 'series', year: null, has_artwork: true, items: [{ id: '3'.repeat(32), season: 2 }, { id: '4'.repeat(32), season: 2 }], selected: true, featured: false }] const draft = () => ({ id: editionId, subject: 'Weekend discoveries', intro: '', revision: 1, state: 'draft', origin: 'manual', send_at: null, created_at: Date.now()/1000, content: { titles: clone(titles), total_titles: 2, period_start: '2026-09-04T09:00:00+00:00', period_end: '2026-09-11T09:00:00+00:00' } }) ;(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 }]) const calls = [] let role = 'admin', failSave = false, failPreview = false, email = 'viewer@example.test', tokenState = 'ready' let heldConfirmation = null let settings = { enabled: false, weekday: 4, hour: 9, limit_titles: 12, public_url: 'https://beta.example.test', intro: '', revision: 1, next_send_at: null } let preference = { state: 'off', email, can_subscribe: true, detail: 'Ready', schedule_enabled: false, weekday: 4, hour: 9, next_send_at: null, resend_after: null } let edition = null, deliveries = [] const overview = () => ({ settings, ready: true, detail: 'Ready', subscribers: 12, editions: edition ? [{ ...edition, titles: edition.content.titles.filter((title) => title.selected).length, content: undefined, period_start: edition.content.period_start, period_end: edition.content.period_end }] : [], deliveries, total: deliveries.length }) await context.route('**/api/**', async (route) => { const request = route.request(), url = new URL(request.url()), payload = request.postDataJSON() calls.push({ method: request.method(), path: url.pathname, payload }) if (url.pathname === '/api/auth/me') return route.fulfill({ json: { username: 'Fixture viewer', role, email } }) if (url.pathname === '/api/auth/profile') return route.fulfill({ json: { user: { username: 'Fixture viewer', role, email, auth_provider: 'jellyfin', password_change_supported: true, password_provider: 'jellyfin' }, activity: { recent: [] } } }) if (url.pathname === '/api/auth/profile/email') { email = payload.email; preference = { ...preference, email, state: 'off' }; return route.fulfill({ json: { email } }) } if (url.pathname === '/api/profile/email-recaps') return route.fulfill({ json: { ...preference, state: 'enabled', day: 2, resend_after: null } }) if (url.pathname === '/api/profile/newsletters') { if (request.method() === 'PUT') preference = { ...preference, state: payload.enabled ? 'pending' : 'off', resend_after: payload.enabled ? Date.now()/1000+300 : null } return route.fulfill({ json: preference }) } if (url.pathname.startsWith('/api/newsletter-subscription/')) { if (url.pathname.endsWith('/confirm')) { if (heldConfirmation) { heldConfirmation.started(); await heldConfirmation.released } tokenState = payload.action === 'confirm' ? 'enabled' : 'off' } return route.fulfill({ json: { action: payload.action, state: tokenState } }) } if (url.pathname.startsWith('/api/admin/newsletters')) { if (role !== 'admin') return route.fulfill({ status: role === 'unauthorized' ? 401 : 403, json: { detail: 'Administrator access is required.' } }) if (url.pathname.includes('/artwork/')) return route.fulfill({ contentType: 'image/svg+xml', body: '' }) if (url.pathname.endsWith('/drafts')) { assert([7, 14, 30].includes(payload.days)); edition = draft(); return route.fulfill({ status: 201, json: edition }) } if (url.pathname.endsWith('/preview')) { assert.equal(payload.revision, edition.revision) if (failPreview) return route.fulfill({ status: 502, json: { detail: 'Jellyfin is temporarily unavailable. Please try again.' } }) return route.fulfill({ json: { ...preview, id: edition.id, revision: edition.revision, subject: edition.subject } }) } if (url.pathname.endsWith('/test')) { assert.deepEqual(Object.keys(payload).sort(), ['request_id', 'revision']) deliveries = [{ id: payload.request_id, subject: edition.subject, username: 'Fixture viewer', email, kind: 'test', state: 'queued', attempts: 0, updated_at: Date.now()/1000, detail: '' }] return route.fulfill({ status: 202, json: { id: payload.request_id, message: 'Test queued for your confirmed newsletter email.' } }) } if (url.pathname.endsWith('/publish')) { assert.equal(payload.revision, edition.revision); edition = { ...edition, state: 'scheduled', send_at: payload.send_at ? Date.parse(payload.send_at)/1000 : Date.now()/1000 }; return route.fulfill({ status: 202, json: edition }) } if (url.pathname.endsWith('/cancel')) { edition = { ...edition, state: 'cancelled' }; return route.fulfill({ json: edition }) } if (url.pathname.includes('/editions/')) { if (request.method() === 'PUT') { if (failSave) return route.fulfill({ status: 409, json: { detail: 'This edition changed. Reload it before continuing.' } }) assert.equal(payload.revision, edition.revision) assert.deepEqual(Object.keys(payload.titles[0]).sort(), ['featured', 'id', 'selected']) edition = { ...edition, subject: payload.subject, intro: payload.intro, revision: edition.revision+1, content: { ...edition.content, titles: edition.content.titles.map((title) => ({ ...title, ...payload.titles.find((selection) => selection.id === title.id) })) } } } return route.fulfill({ json: edition }) } if (request.method() === 'PUT') { settings = { ...payload, revision: payload.revision+1, next_send_at: payload.enabled ? Date.now()/1000+86400 : null }; return route.fulfill({ json: settings }) } return route.fulfill({ json: overview() }) } if (url.pathname.includes('/events/stream')) return route.fulfill({ contentType: 'text/event-stream', body: ': fixture\n\n' }) return route.fulfill({ json: {} }) }) const page = await context.newPage(), errors = [] page.on('pageerror', (error) => errors.push(error.message)) if (output) fs.mkdirSync(output, { recursive: true }) for (const width of [1440, 980, 390, 320]) { await page.setViewportSize({ width, height: 1000 }) await page.goto(`${base}/admin/newsletters`) await page.getByRole('button', { name: 'Create draft', exact: true }).click() await page.getByLabel('Email subject', { exact: true }).waitFor() assert.equal(await page.getByRole('button', { name: 'Send newsletter test to me' }).isDisabled(), true) await page.getByLabel('Feature Arrival', { exact: true }).check() await page.getByLabel('Announcement', { exact: false }).fill('A weekend of good stories.') assert.equal(await page.getByRole('button', { name: 'Preview edition', exact: true }).isDisabled(), true) await page.getByRole('button', { name: 'Save draft', exact: true }).click() await page.getByRole('button', { name: 'Preview edition', exact: true }).click() const frame = page.frameLocator('iframe[title="Newsletter email preview"]') await frame.getByRole('heading', { name: 'What’s new on Grizzlyflix', exact: true }).waitFor() assert.equal(await page.locator('iframe').getAttribute('sandbox'), '') assert.equal(await frame.locator('img').count(), 2) assert(await frame.locator('img').evaluateAll((images) => images.every((image) => image.complete && image.naturalWidth > 0)), 'Email posters did not load') assert.equal(await frame.getByRole('link', { name: 'Watch on Grizzlyflix ↗', exact: true }).first().getAttribute('href'), `https://watch.example.test/web/index.html#!/details?id=${movie}&serverId=${'b'.repeat(32)}`) assert(await page.locator('.newsletter-poster img').evaluateAll((images) => images.every((image) => image.complete && image.naturalWidth > 0)), 'Editor posters did not load') assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), `Editor overflow at ${width}`) assert(await frame.locator('body').evaluate(() => document.documentElement.scrollWidth <= innerWidth), `Email overflow at ${width}`) if (output) { await page.locator('iframe').scrollIntoViewIfNeeded() await page.locator('iframe').screenshot({ path: path.join(output, `newsletter-email-${width}.png`) }) await page.evaluate(() => window.scrollTo(0, 0)) await page.screenshot({ path: path.join(output, `newsletter-editor-${width}.png`), fullPage: true }) } await page.getByRole('button', { name: 'Plain text', exact: true }).click() assert.match(await page.locator('.recap-plain-preview').innerText(), /2 new episodes/) await page.getByRole('button', { name: 'Weekly schedule', exact: true }).click() await page.getByRole('heading', { name: 'A weekly discovery', exact: true }).waitFor() assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), `Schedule overflow at ${width}`) if (output) await page.screenshot({ path: path.join(output, `newsletter-schedule-${width}.png`), fullPage: true }) await page.goto(`${base}/profile#newsletters`) await page.getByRole('heading', { name: 'New on Grizzlyflix.', exact: true }).waitFor() assert.equal(await page.locator('#monthly-recaps').getByText('Subscribed', { exact: true }).count(), 1) assert.equal(await page.locator('#newsletters').getByText('Off', { exact: true }).count(), 1) assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), `Profile overflow at ${width}`) if (output) await page.locator('#newsletters').screenshot({ path: path.join(output, `newsletter-profile-${width}.png`) }) } await page.getByRole('button', { name: 'Email me new arrivals', exact: true }).click() await page.locator('#newsletters').getByText('Check your inbox', { exact: true }).waitFor() assert.equal(await page.getByRole('button', { name: 'Resend newsletter confirmation' }).isDisabled(), true) await page.getByRole('button', { name: 'Cancel newsletter subscription' }).click() await page.locator('#newsletters').getByText('Off', { exact: true }).waitFor() preference = { ...preference, state: 'enabled' } await page.getByRole('button', { name: 'Refresh newsletter preference' }).click() await page.locator('#newsletters').getByText('Subscribed', { exact: true }).waitFor() await page.getByRole('button', { name: 'Turn off newsletters' }).click() await page.locator('#newsletters').getByText('Off', { exact: true }).waitFor() assert.equal(await page.locator('#monthly-recaps').getByText('Subscribed', { exact: true }).count(), 1) assert.equal(calls.filter((call) => call.path === '/api/profile/email-recaps' && call.method === 'PUT').length, 0) await page.goto(`${base}/admin/newsletters`) await page.locator('.newsletter-edition').first().click() await page.getByLabel('Email subject').fill('Unsaved subject') failSave = true await page.getByRole('button', { name: 'Save draft', exact: true }).click() await page.getByRole('alert').filter({ hasText: 'This edition changed' }).waitFor() assert.equal(await page.getByRole('button', { name: 'Preview edition', exact: true }).isDisabled(), true) failSave = false await page.getByRole('button', { name: 'Discard changes', exact: true }).click() await page.getByRole('button', { name: 'Reload edition', exact: true }).waitFor() failPreview = true await page.getByRole('button', { name: 'Preview edition', exact: true }).click() await page.getByRole('alert').filter({ hasText: 'Jellyfin is temporarily unavailable' }).waitFor() failPreview = false await page.getByRole('button', { name: 'Preview edition', exact: true }).click() await page.locator('iframe').waitFor() await page.getByRole('button', { name: 'Send newsletter test to me', exact: true }).click() await page.getByRole('status').filter({ hasText: 'Test queued' }).waitFor() await page.getByLabel('Schedule for (UTC)', { exact: true }).fill('2026-09-18T09:00') await page.getByRole('button', { name: 'Schedule edition', exact: true }).click() await page.getByRole('heading', { name: 'Delivery controls', exact: true }).waitFor() assert.equal(await page.getByLabel('Email subject').isDisabled(), true) assert.equal(calls.filter((call) => call.path.endsWith('/publish')).at(-1).payload.send_at, '2026-09-18T09:00:00Z') await page.getByRole('button', { name: 'Cancel edition', exact: true }).click() await page.getByRole('status').filter({ hasText: 'Edition cancelled' }).waitFor() await page.getByRole('button', { name: 'Weekly schedule', exact: true }).click() await page.getByLabel('Send day').selectOption('5') await page.getByLabel('Enable automatic weekly newsletters').check() await page.getByRole('button', { name: 'Save weekly settings', exact: true }).click() await page.getByText('Weekly sending is on', { exact: true }).waitFor() assert.equal(settings.weekday, 5) assert.equal(settings.enabled, true) deliveries = ['sent', 'retry', 'unknown', 'skipped', 'cancelled'].map((state, index) => ({ ...deliveries[0], id: `fixture-${index}`, state, attempts: 1, detail: 'Fixture delivery result.', next_attempt_at: Date.now()/1000+300 })) await page.getByRole('button', { name: 'Delivery history', exact: true }).click() await page.getByRole('button', { name: 'Refresh history', exact: true }).click() await page.getByText('Needs review', { exact: true }).waitFor() assert.match(await page.locator('.recap-history').innerText(), /Automatic retries are stopped/) assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), 'History overflows mobile viewport') for (const width of [1440, 320]) { await page.setViewportSize({ width, height: 1000 }) tokenState = 'ready' const before = calls.filter((call) => call.path === '/api/newsletter-subscription/confirm').length await page.goto(`${base}/newsletter-subscription#action=confirm&token=${'a'.repeat(43)}`) await page.getByRole('button', { name: 'Confirm newsletter subscription', exact: true }).waitFor() assert.equal(calls.filter((call) => call.path === '/api/newsletter-subscription/confirm').length, before) assert.equal(await page.locator('.sidebar').count(), 0) assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), `Public page overflow at ${width}`) if (output) await page.screenshot({ path: path.join(output, `newsletter-confirm-${width}.png`), fullPage: true }) await page.getByRole('button', { name: 'Confirm newsletter subscription', exact: true }).click() await page.getByRole('heading', { name: 'You’re on the list.', exact: true }).waitFor() assert.equal(new URL(page.url()).hash, '') tokenState = 'ready' await page.goto(`${base}/newsletter-subscription#action=unsubscribe&token=${'b'.repeat(43)}`) await page.getByRole('button', { name: 'Unsubscribe from newsletters', exact: true }).click() await page.getByRole('heading', { name: 'Newsletters are turned off.', exact: true }).waitFor() } tokenState = 'ready' let started, release const waiting = new Promise((resolve) => { started = resolve }) heldConfirmation = { started, released: new Promise((resolve) => { release = resolve }) } await page.goto(`${base}/newsletter-subscription#action=confirm&token=${'c'.repeat(43)}`) await page.getByRole('button', { name: 'Confirm newsletter subscription', exact: true }).click() await waiting await page.goto(`${base}/newsletter-subscription#action=unsubscribe&token=${'d'.repeat(43)}`) await page.getByRole('button', { name: 'Unsubscribe from newsletters', exact: true }).waitFor() const applied = page.waitForResponse((response) => response.url().endsWith('/api/newsletter-subscription/confirm')) heldConfirmation = null release() await applied await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))) assert.match(new URL(page.url()).hash, /action=unsubscribe/) assert.equal(await page.getByRole('button', { name: 'Unsubscribe from newsletters', exact: true }).count(), 1) await page.goto(`${base}/newsletter-subscription#action=confirm&token=bad`) await page.getByRole('alert').filter({ hasText: 'This email link is incomplete' }).waitFor() assert.equal(await page.getByRole('button', { name: 'Confirm newsletter subscription', exact: true }).count(), 0) role = 'user' await page.goto(`${base}/admin/newsletters`) await page.waitForURL(`${base}/`) role = 'unauthorized' await page.goto(`${base}/admin/newsletters`) await page.waitForURL(/\/login\?next=%2Fadmin%2Fnewsletters/) assert.deepEqual(errors, []) console.log('Newsletter editor, posters, email layouts, consent, stale edits, scheduling, cancellation, history and access redirects passed at 1440/980/390/320px; all API calls used fixtures.') await context.close() } finally { await browser.close() } })().catch((error) => { console.error(error); process.exitCode = 1 })