Files
Magent/scripts/review_activity_ui.cjs

75 lines
5.2 KiB
JavaScript

// Fixture-only action feedback and accessible dialog checks; no live API writes.
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 downloading = true, finished = false
const events = [
{ id: '1', service: 'Magent', state: 'complete', message: 'Your action has been received.' },
{ id: '2', service: 'Radarr', state: 'active', message: 'Checking whether the replacement has been downloaded…' },
]
let releaseAction
await context.route('**/api/**', async (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.endsWith('/snapshot')) return reply({ request_id: '12', title: 'Activity review', request_type: 'movie', state: 'DOWNLOADING', timeline: [], actions: [], presentation: {
status: { label: 'Download in progress', meaning: 'Your movie is downloading.' },
download: { visible: downloading, summary: 'Downloading (1 active).' },
nextStep: { title: 'Let the download finish', description: 'No action needed.', actionIds: [] }, pipeline: [],
} })
if (path.endsWith('/actions/recheck')) { await new Promise((resolve) => { releaseAction = resolve }); return reply({ message: 'Request checked.' }) }
if (path.includes('/operations/')) return reply({ id: 'fixture', label: 'Recheck request status', status: finished ? 'complete' : 'running', events: finished ? [...events, { id: '3', service: 'Magent', state: 'complete', message: 'This check has finished. Your movie is still downloading.' }] : events })
if (path.includes('/events/stream')) return route.fulfill({ contentType: 'text/event-stream', body: ': fixture\n\n' })
return reply({ navigation: { showRequests: true }, services: [] })
})
const page = await context.newPage()
const errors = []
page.on('pageerror', (error) => errors.push(error.message))
for (const width of [1440, 710, 390]) {
await page.setViewportSize({ width, height: 960 })
for (downloading of [true, false]) {
finished = false
await page.goto(base + '/requests/12')
await page.getByRole('button', { name: 'Recheck request', exact: true }).click({ timeout: 5000 }).catch(async (error) => {
console.error(errors, (await page.locator('body').innerText()).slice(0, 1600)); throw error
})
const card = page.locator('.latest-activity')
await card.locator('.latest-activity-message').filter({ hasText: events[1].message }).waitFor()
assert.equal(await card.getByText(events[0].message, { exact: true }).isVisible(), false)
if (width === 1440 && downloading) {
const activityBox = await card.boundingBox()
const downloadBox = await page.locator('.request-overview-block').filter({ hasText: 'Current download state' }).boundingBox()
assert.ok(activityBox.x > downloadBox.x && Math.abs(activityBox.y - downloadBox.y) < 2, 'Activity must use the spare right-hand quadrant')
}
const before = await page.locator('.request-next-step').boundingBox()
await card.getByRole('button').click()
const dialog = page.getByRole('dialog', { name: 'Recheck request status' })
await dialog.waitFor()
await dialog.getByText(events[0].message, { exact: true }).waitFor()
assert.equal(await page.evaluate(() => document.body.style.overflow), 'hidden')
const after = await page.locator('.request-next-step').boundingBox()
assert.equal(before.y, after.y, 'Opening the dialog must not expand the page')
await page.keyboard.press('Escape')
await dialog.waitFor({ state: 'hidden' })
assert.equal(await card.getByRole('button').evaluate((el) => el === document.activeElement), true)
finished = true; releaseAction()
await card.getByText('Finished', { exact: true }).waitFor()
await card.getByRole('button').click()
await dialog.getByText('This check has finished. Your movie is still downloading.', { exact: true }).waitFor()
if (process.env.REVIEW_DIR) await page.screenshot({ path: `${process.env.REVIEW_DIR}/activity-${width}-${downloading}.png` })
await dialog.getByRole('button', { name: 'Dismiss activity' }).click()
await card.waitFor({ state: 'hidden' })
assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false)
assert.notEqual(await page.evaluate(() => document.body.style.overflow), 'hidden')
}
}
assert.deepEqual(errors, [])
console.log('PASS: right-hand activity card, latest event only, live updates, modal history, Escape/focus return, dismissal, desktop/tablet/mobile; all API calls mocked.')
} finally { await browser.close() }
})().catch((error) => { console.error(error); process.exitCode = 1 })