// Run with the Playwright browser_run_code tool's filename option against an isolated development API. async (page) => { const origin = new URL(page.url()).origin; const password = 'Architecture-test-password-2026'; const suffix = Date.now().toString(); const ownerName = `smoke_owner_${suffix}`; const memberName = `smoke_member_${suffix}`; const browser = page.context().browser(); const contexts = []; const results = []; async function login(target, username, create = false, path = '/') { await target.goto(origin + path); if (!create) await target.getByRole('button', { name: '已有账号', exact: true }).click(); await target.getByLabel('用户名', { exact: true }).fill(username); await target.getByLabel('密码', { exact: true }).fill(password); await target.getByRole('button', { name: create ? '创建账号' : '登录并同步', exact: true }).click(); await target.getByRole('button', { name: '新建空间', exact: true }).waitFor(); } async function newDevice() { const context = await browser.newContext({ viewport: { width: 1440, height: 900 } }); contexts.push(context); return context.newPage(); } try { const owner = await newDevice(); await login(owner, ownerName, true); await owner.getByLabel('个人名称', { exact: true }).fill('多设备测试用户'); const avatar = await owner.evaluate(() => { const canvas = document.createElement('canvas'); canvas.width = 32; canvas.height = 32; const context = canvas.getContext('2d'); context.fillRect(0, 0, 32, 32); return canvas.toDataURL('image/png').split(',')[1]; }); await owner.getByLabel('头像', { exact: true }).setInputFiles({ name: 'avatar.png', mimeType: 'image/png', buffer: Buffer.from(avatar, 'base64') }); await owner.getByRole('img', { name: '个人头像', exact: true }).waitFor(); await owner.getByRole('button', { name: '保存个人资料', exact: true }).click(); await owner.getByText('个人资料已保存并同步。', { exact: true }).waitFor(); await owner.reload(); await owner.getByRole('button', { name: '新建空间', exact: true }).waitFor(); if (await owner.getByLabel('个人名称', { exact: true }).inputValue() !== '多设备测试用户') throw new Error('Profile did not survive reload'); await owner.getByRole('img', { name: '个人头像', exact: true }).waitFor(); results.push('server-owned profile and avatar survive login and reload'); await owner.getByRole('button', { name: '新建空间', exact: true }).click(); await owner.getByLabel('空间名称(加入者和服务端可见)').fill('架构验收空间'); await owner.getByRole('button', { name: '创建', exact: true }).click(); await owner.getByRole('button', { name: '常规', exact: true }).waitFor(); await owner.getByRole('button', { name: '新建频道分组', exact: true }).click(); await owner.getByLabel('分组名称', { exact: true }).fill('工作'); await owner.getByRole('button', { name: '创建', exact: true }).click(); await owner.getByRole('button', { name: '在 工作 新建频道', exact: true }).click(); await owner.getByLabel('频道名称', { exact: true }).fill('讨论'); await owner.getByRole('button', { name: '创建', exact: true }).click(); await owner.getByRole('button', { name: '讨论', exact: true }).waitFor(); await owner.getByRole('button', { name: '常规', exact: true }).click(); await owner.getByRole('button', { name: '频道设置 常规', exact: true }).click(); await owner.getByLabel('所属分组').selectOption({ label: '工作' }); await owner.getByRole('button', { name: '保存', exact: true }).click(); await owner.getByRole('button', { name: '保存', exact: true }).waitFor({ state: 'hidden' }); await owner.getByRole('button', { name: '重命名分组 工作', exact: true }).click(); await owner.getByLabel('分组名称', { exact: true }).fill('项目'); await owner.getByRole('button', { name: '保存', exact: true }).click(); await owner.getByRole('button', { name: '保存', exact: true }).waitFor({ state: 'hidden' }); await owner.getByRole('button', { name: '分组 项目', exact: true }).click(); if (await owner.getByRole('button', { name: '讨论', exact: true }).isVisible()) throw new Error('Group did not collapse'); await owner.getByRole('button', { name: '分组 项目', exact: true }).click(); await owner.reload(); await owner.getByRole('region', { name: '频道分组 项目', exact: true }).getByRole('button', { name: '常规', exact: true }).waitFor(); await owner.getByRole('region', { name: '频道分组 项目', exact: true }).getByRole('button', { name: '讨论', exact: true }).waitFor(); await owner.getByRole('button', { name: '讨论', exact: true }).click(); await owner.getByRole('button', { name: '频道设置 讨论', exact: true }).click(); await owner.getByLabel('所属分组').selectOption({ label: '未分组' }); await owner.getByRole('button', { name: '保存', exact: true }).click(); await owner.getByRole('button', { name: '保存', exact: true }).waitFor({ state: 'hidden' }); if (await owner.getByRole('region', { name: '频道分组 项目', exact: true }).getByRole('button', { name: '讨论', exact: true }).count()) throw new Error('Ungrouping did not remove group membership'); await owner.getByRole('button', { name: '频道设置 讨论', exact: true }).click(); await owner.getByLabel('所属分组').selectOption({ label: '项目' }); await owner.getByRole('button', { name: '保存', exact: true }).click(); await owner.getByRole('button', { name: '保存', exact: true }).waitFor({ state: 'hidden' }); await owner.getByRole('button', { name: '常规', exact: true }).click(); results.push('backend channels and groups create, rename, collapse and survive reload'); const path = new URL(owner.url()).pathname; if (owner.url().includes('#')) throw new Error('Account channel navigation retained a secret fragment'); await owner.locator('textarea').last().fill('常规 thread 聊天验证'); await owner.getByRole('button', { name: '发送消息', exact: true }).click(); await owner.locator('article p').filter({ hasText: '常规 thread 聊天验证' }).waitFor(); const payload = '端到端文件验证:只有成员设备可解密。'.repeat(60000); await owner.getByRole('region', { name: '频道文件管理器' }).locator('input[type=file]').setInputFiles({ name: 'architecture-file.txt', mimeType: 'text/plain', buffer: Buffer.from(payload) }); await owner.getByRole('button', { name: '继续上传', exact: true }).click(); await owner.getByRole('button', { name: 'architecture-file.txt', exact: true }).waitFor(); results.push('default thread, center chat and right file explorer work'); const second = await newDevice(); await login(second, ownerName, false, path); await second.getByRole('button', { name: 'architecture-file.txt', exact: true }).waitFor(); await second.locator('article p').filter({ hasText: '常规 thread 聊天验证' }).waitFor(); await second.getByRole('region', { name: '频道分组 项目', exact: true }).getByRole('button', { name: '讨论', exact: true }).waitFor(); results.push('second independent device recovers owner channel without a secret link'); const member = await newDevice(); await login(member, memberName, true); await owner.getByRole('button', { name: '空间设置', exact: true }).click(); await owner.getByLabel('被邀请用户的用户名').fill(memberName); await owner.getByRole('button', { name: '发送邀请', exact: true }).click(); await owner.getByText(`${memberName} · 已邀请`, { exact: true }).waitFor(); await owner.getByRole('dialog').getByRole('button', { name: '关闭', exact: true }).click(); let downloads = 0; member.on('request', (request) => { if (/\/items\/[^/]+\/frames/.test(request.url())) downloads++; }); await member.goto(origin + path); await member.getByRole('button', { name: '接受并加入空间', exact: true }).click(); await member.getByRole('button', { name: 'architecture-file.txt', exact: true }).waitFor(); if (downloads !== 0) throw new Error('File catalog consumed a gated download'); await member.getByRole('button', { name: 'architecture-file.txt', exact: true }).dblclick(); await member.getByRole('button', { name: '解密并预览文件', exact: true }).click(); await member.locator('pre').filter({ hasText: '端到端文件验证' }).waitFor(); if (downloads !== 1) throw new Error(`Expected one explicit gated read, got ${downloads}`); await member.reload(); await member.getByRole('button', { name: 'architecture-file.txt', exact: true }).waitFor(); if (downloads !== 1) throw new Error('Refreshing consumed another download'); results.push('account invitation automatically unlocks ciphertext and catalog never auto-downloads'); await owner.getByRole('button', { name: '新建文件夹', exact: true }).click(); await owner.getByLabel('文件夹名称', { exact: true }).fill('资料目录'); await owner.getByRole('button', { name: '创建', exact: true }).click(); await owner.getByRole('button', { name: '资料目录', exact: true }).waitFor(); await owner.getByRole('checkbox', { name: '选择文件 architecture-file.txt', exact: true }).check(); await owner.getByLabel('移动所选文件到文件夹').selectOption({ label: '资料目录' }); await owner.getByRole('button', { name: '资料目录', exact: true }).dblclick(); await owner.getByRole('button', { name: 'architecture-file.txt', exact: true }).waitFor(); results.push('backend folders and file locations update without re-encrypting content'); await second.goto(origin); const selfRow = second.getByRole('region', { name: '账号设备' }).locator('div').filter({ has: second.getByText('本机', { exact: true }) }); await selfRow.locator('input').fill('验收副设备'); await selfRow.getByRole('button', { name: '重命名', exact: true }).click(); await second.getByLabel('验收副设备 的设备名称', { exact: true }).waitFor(); await owner.goto(origin); const otherRow = owner.getByRole('region', { name: '账号设备' }).locator('div').filter({ has: owner.getByLabel('验收副设备 的设备名称', { exact: true }) }); await owner.evaluate(() => { window.confirm = () => true; }); await otherRow.getByRole('button', { name: '移除设备', exact: true }).click(); await second.reload(); await second.getByRole('button', { name: '创建账号', exact: true }).waitFor(); await owner.reload(); await owner.getByRole('button', { name: '新建空间', exact: true }).waitFor(); results.push('revoked device loses remembered login while the other device stays logged in'); await member.goto(origin + path); for (const width of [1440, 390]) { await member.setViewportSize({ width, height: 900 }); for (const colorScheme of ['light', 'dark']) { await member.emulateMedia({ colorScheme }); if (await member.evaluate(() => document.documentElement.scrollWidth > innerWidth)) throw new Error(`Horizontal overflow at ${width}/${colorScheme}`); } } results.push('desktop/mobile and light/dark layouts fit viewport'); return { passed: results }; } finally { for (const context of contexts) await context.close(); } }