Files
Magent/scripts/review_email_recaps_ui.cjs
Assclaw 1979e02cde
Magent CI/CD / verify (push) Canceled after 3m55s
Magent CI/CD / deploy-prod (push) Canceled after 0s
Magent CI/CD / deploy-beta (push) Canceled after 0s
Add opt-in monthly email recaps with scheduling and delivery history
2026-09-09 22:39:22 +12:00

147 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Fixture-only browser review. All API calls are intercepted; no real messages are sent.
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'))
;(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'
let failPreview = false
let email = 'viewer@example.test'
let settings = { enabled: false, day: 2, hour: 9, public_url: 'https://beta.example.test', next_send_at: null }
let preference = { state: 'off', email, can_subscribe: true, detail: 'Ready', schedule_enabled: false, day: 2, hour: 9, next_send_at: null, resend_after: null }
let deliveries = []
let tokenState = 'ready'
const months = Array.from({ length: 23 }, (_, index) => new Date(Date.UTC(2026, 7 - index, 1)).toISOString().slice(0, 7))
await context.route('**/api/**', async (route) => {
const request = route.request()
const url = new URL(request.url())
const 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') {
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/admin/email-recaps')) {
if (role !== 'admin') return route.fulfill({ status: role === 'unauthorized' ? 401 : 403, json: { detail: 'Administrator access is required.' } })
if (url.pathname.endsWith('/preview')) {
if (failPreview) return route.fulfill({ status: 502, json: { detail: 'Your report is temporarily unavailable. Please try again shortly.' } })
return route.fulfill({ json: { ...preview, month: url.searchParams.get('month') } })
}
if (url.pathname.endsWith('/test')) {
assert.deepEqual(Object.keys(payload).sort(), ['month', 'request_id'])
deliveries = [{ id: payload.request_id, month: payload.month, kind: 'test', email, username: 'Fixture viewer', state: 'queued', attempts: 0, created_at: Date.now() / 1000, updated_at: Date.now() / 1000, next_attempt_at: Date.now() / 1000, detail: '' }]
return route.fulfill({ status: 202, json: { id: payload.request_id, message: 'Test queued for your confirmed email. Check delivery history for the result.' } })
}
if (request.method() === 'PUT') { settings = { ...payload, next_send_at: payload.enabled ? Date.UTC(2026, 9, payload.day, payload.hour) / 1000 : null }; return route.fulfill({ json: settings }) }
return route.fulfill({ json: { settings, months, ready: true, detail: 'Ready', deliveries, total: deliveries.length, subscribers: 12, worker_enabled: true } })
}
if (url.pathname.startsWith('/api/email-recaps/')) {
if (url.pathname.endsWith('/confirm')) tokenState = payload.action === 'confirm' ? 'enabled' : 'off'
return route.fulfill({ json: { action: payload.action, state: tokenState } })
}
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()
const 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/recaps`)
await page.getByRole('heading', { name: 'Monthly schedule', exact: true }).waitFor()
assert.equal(await page.getByLabel('Enable scheduled monthly recaps').isChecked(), false)
assert.equal(await page.getByRole('button', { name: 'Send test to me', exact: true }).isDisabled(), true)
assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), `Admin overflow at ${width}`)
if (output) await page.screenshot({ path: path.join(output, `recaps-admin-${width}.png`), fullPage: true })
await page.getByRole('button', { name: 'Preview my recap', exact: true }).click()
const frame = page.frameLocator('iframe[title="Monthly recap email preview"]')
await frame.getByRole('heading', { name: 'August 2026', exact: true }).waitFor()
assert.equal(await page.locator('iframe').getAttribute('sandbox'), '')
assert.equal(await frame.getByRole('link', { name: /Explore your full report/ }).getAttribute('href'), 'https://beta.example.test/insights/reports?month=2026-08')
assert(await frame.locator('body').evaluate(() => document.documentElement.scrollWidth <= innerWidth), `Email overflow at ${width}`)
if (output) await page.locator('.recap-preview').screenshot({ path: path.join(output, `recap-preview-${width}.png`) })
await page.getByRole('button', { name: 'Plain text', exact: true }).click()
assert.match(await page.locator('.recap-plain-preview').innerText(), /Minutes watched: 1,500/)
await page.goto(`${base}/profile`)
await page.getByRole('heading', { name: 'Your month, delivered.', exact: true }).waitFor()
await page.getByRole('button', { name: 'Email me my monthly recap', exact: true }).waitFor()
assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), `Profile overflow at ${width}`)
if (output) await page.screenshot({ path: path.join(output, `recap-profile-${width}.png`), fullPage: true })
}
await page.getByRole('button', { name: 'Email me my monthly recap', exact: true }).click()
await page.getByText('Check your inbox', { exact: true }).waitFor()
assert.equal(await page.getByRole('button', { name: 'Send a new confirmation', exact: true }).isDisabled(), true)
await page.getByRole('button', { name: 'Cancel subscription', exact: true }).click()
await page.getByText('Off', { exact: true }).waitFor()
preference = { ...preference, state: 'enabled' }
await page.getByRole('button', { name: 'Refresh preference', exact: true }).click()
await page.getByText('Subscribed', { exact: true }).waitFor()
await page.getByRole('textbox', { name: 'Email address', exact: true }).fill('changed@example.test')
await page.getByRole('button', { name: 'Save email', exact: true }).click()
await page.getByText('Off', { exact: true }).waitFor()
await page.goto(`${base}/admin/recaps`)
await page.getByLabel('Day of the month').selectOption('3')
await page.getByLabel('Enable scheduled monthly recaps').check()
assert.equal(await page.getByRole('button', { name: 'Preview my recap', exact: true }).isDisabled(), true)
await page.getByRole('button', { name: 'Save schedule', exact: true }).click()
await page.getByText('Schedule running', { exact: true }).waitFor()
assert.equal(settings.day, 3)
assert.equal(settings.enabled, true)
await page.getByRole('button', { name: 'Preview my recap', exact: true }).click()
await page.locator('iframe').waitFor()
await page.getByRole('button', { name: 'Send test to me', exact: true }).click()
await page.getByText('Queued', { exact: true }).waitFor()
deliveries = ['sent', 'retry', 'unknown', 'failed', 'cancelled'].map((state, index) => ({ ...deliveries[0], id: `fixture-${index}`, state, attempts: index + 1, detail: state === 'unknown' ? 'Check the mail server.' : 'Fixture delivery result.' }))
await page.getByRole('button', { name: 'Refresh history', exact: true }).click()
await page.getByText('Needs review', { exact: true }).waitFor()
assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), 'History overflows mobile viewport')
assert.match(await page.locator('.recap-history').innerText(), /Automatic retries are stopped/)
await page.setViewportSize({ width: 1440, height: 1000 })
if (output) await page.screenshot({ path: path.join(output, 'recap-delivery-history.png'), fullPage: true })
failPreview = true
await page.getByRole('button', { name: 'Preview my recap', exact: true }).click()
await page.getByRole('alert').filter({ hasText: 'temporarily unavailable' }).waitFor()
await context.clearCookies()
const token = 'fixture'.repeat(7)
tokenState = 'ready'
await page.goto(`${base}/email-recaps#action=confirm&token=${token}`)
await page.getByRole('button', { name: 'Confirm email recaps', exact: true }).waitFor()
assert.equal(calls.filter((call) => call.path === '/api/email-recaps/confirm').length, 0)
assert.equal(await page.locator('.header').count(), 0)
await page.getByRole('button', { name: 'Confirm email recaps', exact: true }).click()
await page.getByRole('heading', { name: 'Youre on the list.', exact: true }).waitFor()
assert.equal(new URL(page.url()).hash, '')
tokenState = 'ready'
await page.setViewportSize({ width: 320, height: 800 })
await page.goto(`${base}/email-recaps#action=unsubscribe&token=${token}`)
await page.getByRole('button', { name: 'Unsubscribe from recaps', exact: true }).waitFor()
if (output) await page.screenshot({ path: path.join(output, 'recap-unsubscribe-mobile.png'), fullPage: true })
assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), 'Unsubscribe overflow')
await page.getByRole('button', { name: 'Unsubscribe from recaps', exact: true }).click()
await page.getByRole('heading', { name: 'Recaps are turned off.', exact: true }).waitFor()
await page.goto(`${base}/email-recaps`)
await page.getByRole('alert').filter({ hasText: 'incomplete' }).waitFor()
role = 'user'
await page.goto(`${base}/admin/recaps`)
await page.waitForURL(`${base}/`)
role = 'unauthorized'
await page.goto(`${base}/admin/recaps`)
await page.waitForURL(/\/login\?next=/)
assert.deepEqual(errors, [])
console.log(`Email recap UI passed: desktop/mobile layout, preview isolation, consent, schedule, test queue, history, public links and access control; ${calls.length} intercepted API calls.`)
} finally { await browser.close() }
})().catch((error) => { console.error(error); process.exitCode = 1 })