import assert from 'node:assert/strict'; import {chromium} from 'playwright'; const base = new URL(process.argv[2]); assert.equal(base.protocol, 'http:'); assert.ok(['localhost', '127.0.0.1'].includes(base.hostname)); const browser = await chromium.launch({headless: true}); try { const context = await browser.newContext(); const unexpected = []; await context.route('**/*', route => { if (new URL(route.request().url()).origin === base.origin) return route.continue(); unexpected.push(route.request().url()); return route.abort(); }); const page = await context.newPage(); page.setDefaultTimeout(10000); async function login(subject) { await page.goto(`${base.origin}/auth/dev-login/`); await page.getByLabel('Subject').fill(subject); await page.getByLabel('Tenant').fill('tenant:attachment-test'); await page.getByRole('button', {name: 'Establish session'}).click(); await page.waitForURL('**/app/'); } const shared = `${base.origin}/app/spaces/attachments-shared/`; const destination = `${base.origin}/app/spaces/attachments-private/`; const source = `${shared}?page=notes`; async function menu(url, author) { await page.goto(url); await page.locator('summary', {hasText: 'Attachments'}).click(); await page.getByRole('link', {name: author ? 'Open, download or manage attachments' : 'Open or download attachments', exact: true}).click(); await page.waitForURL('**/attachments/'); } async function downloadedBytes(tab = page) { const pending = tab.waitForEvent('download'); await tab.getByRole('link', {name: 'Download', exact: true}).click(); const stream = await (await pending).createReadStream(); const chunks = []; for await (const chunk of stream) chunks.push(chunk); return Buffer.concat(chunks).toString(); } await login('file-author'); await menu(source, true); await page.getByLabel('File (up to 16 MiB)').setInputFiles({ name: 'meeting-notes.txt', mimeType: 'text/plain', buffer: Buffer.from('Synthetic attachment bytes'), }); await page.getByRole('button', {name: 'Add attachment', exact: true}).click(); await page.waitForURL('**/attachments/'); assert.match(await page.locator('main').innerText(), /Attachment added/); const originalDownload = await page.getByRole('link', {name: 'Download', exact: true}).getAttribute('href'); assert.equal(await downloadedBytes(), 'Synthetic attachment bytes'); const editUrl = `${shared}pages/notes/edit/`; await page.goto(editUrl); const files = page.getByRole('region', {name: 'Attachments', exact: true}); assert.match(await files.innerText(), /meeting-notes.txt/); assert.equal(await downloadedBytes(), 'Synthetic attachment bytes'); await page.getByLabel('Body (markdown)').fill('Unsaved author notes'); const opened = context.waitForEvent('page'); await page.getByRole('link', {name: 'Manage attachments (new tab)', exact: true}).click(); const management = await opened; await management.waitForURL('**/attachments/'); assert.equal(await page.getByLabel('Body (markdown)').inputValue(), 'Unsaved author notes'); assert.match(await management.locator('main').innerText(), /meeting-notes.txt/); await management.close(); const stale = await context.newPage(); await stale.goto(editUrl); assert.match(await stale.getByRole('region', {name: 'Attachments', exact: true}).innerText(), /meeting-notes.txt/); await stale.getByLabel('Body (markdown)').fill('Keep my stale edit'); await page.getByRole('button', {name: 'Save page', exact: true}).click(); await page.waitForURL('**?page=notes'); await stale.getByRole('button', {name: 'Save page', exact: true}).click(); await stale.waitForURL('**/edit/'); assert.match(await stale.locator('main').innerText(), /This page changed/); assert.equal(await stale.getByLabel('Body (markdown)').inputValue(), 'Keep my stale edit'); assert.match(await stale.getByRole('region', {name: 'Attachments', exact: true}).innerText(), /meeting-notes.txt/); assert.equal(await downloadedBytes(stale), 'Synthetic attachment bytes'); await stale.close(); await page.goto(editUrl); assert.equal(await page.getByLabel('Body (markdown)').inputValue(), 'Unsaved author notes\n'); assert.match(await files.innerText(), /meeting-notes.txt/); await login('file-reader'); await menu(source, false); assert.equal(await page.getByRole('button', {name: 'Add attachment', exact: true}).count(), 0); assert.equal(await page.getByRole('button', {name: 'Remove from page', exact: true}).count(), 0); assert.equal(await downloadedBytes(), 'Synthetic attachment bytes'); await login('file-outsider'); assert.equal((await page.goto(new URL(originalDownload, base).href)).status(), 404); assert.equal((await page.goto(`${shared}pages/notes/attachments/`)).status(), 404); await login('file-author'); await page.goto(source); await page.getByRole('link', {name: 'Duplicate page', exact: true}).click(); await page.getByLabel('Destination space').selectOption('attachments-private'); await page.getByLabel('Title', {exact: false}).fill('Independent files'); await page.getByLabel('New page slug', {exact: false}).fill('copied'); await page.getByRole('button', {name: 'Create duplicate'}).click(); await page.waitForURL('**?page=copied'); await menu(`${destination}?page=copied`, true); assert.equal(await downloadedBytes(), 'Synthetic attachment bytes'); const copiedDownload = await page.getByRole('link', {name: 'Download', exact: true}).getAttribute('href'); assert.notEqual(copiedDownload, originalDownload); await page.getByRole('button', {name: 'Remove from page', exact: true}).click(); await page.waitForURL('**/attachments/'); assert.match(await page.locator('main').innerText(), /No attachments on this page/); await menu(source, true); assert.equal(await downloadedBytes(), 'Synthetic attachment bytes'); await page.goto(source); await page.getByRole('link', {name: 'Transfer', exact: true}).click(); await page.getByLabel('Destination space').selectOption('attachments-private'); await page.getByLabel('Slug in destination', {exact: false}).fill('moved'); await page.getByRole('button', {name: 'Transfer page', exact: true}).click(); await page.waitForURL('**?page=moved'); await menu(`${destination}?page=moved`, true); assert.equal(await downloadedBytes(), 'Synthetic attachment bytes'); const movedDownload = await page.getByRole('link', {name: 'Download', exact: true}).getAttribute('href'); await login('file-reader'); assert.equal((await page.goto(source)).status(), 404); assert.equal((await page.goto(new URL(movedDownload, base).href)).status(), 404); assert.deepEqual(unexpected, []); console.log('Attachment menu upload, download, independent copy, move and removal passed'); } finally { await browser.close(); }