import assert from 'node:assert/strict' import { chromium } from 'playwright' const browser = await chromium.launch({ headless: true, channel: 'chrome' }) try { for (const allPermissions of [false, true]) { const context = await browser.newContext({ permissions: ['clipboard-read', 'clipboard-write'], viewport: { width: 375, height: 812 } }) const page = await context.newPage() // 固定起始日期但保持时钟前进;冻结 Date.now 会触发 Vue 嵌套点击事件的时间戳保护。 await page.clock.install({ time: new Date('2026-10-26T04:00:00Z') }) page.setDefaultTimeout(20000) page.on('console', (msg) => { if (msg.type() === 'error') console.log('CONSOLE', msg.text()) }) page.on('requestfailed', (r) => console.log('FAILED', r.url(), r.failure())) const errors = [] page.on('pageerror', (e) => errors.push(e.message)) const rows = [ { id: 'site1', dispatchDate: '2026-10-26', calendarKind: 'site', vehicleId: 'v1', licensePlate: '皖H12345', productId: 'p1', productName: '液碱', origin: '山峡', destination: '武汉', quantity: '32', documentType: '出库计划', remark: '' }, { id: 'site2', dispatchDate: '2026-10-26', calendarKind: 'site', sourceRecordId: 'source2', vehicleId: 'v2', licensePlate: '皖H54321', productId: 'p1', productName: '液碱', origin: '武汉', destination: '云栋', quantity: '28', documentType: '入库计划', remark: '' } ] const forbiddenRequests = [] let lastMonth = '' await context.addInitScript(() => { localStorage.setItem( 'sys-v3.0.2-user', JSON.stringify({ isLogin: true, accessToken: 'fixture', info: { id: 'fixture', userId: 'fixture', userName: '测试', role: 'admin', roles: ['admin'] } }) ) localStorage.setItem('sys-version', '3.0.2') }) await context.route('**/api/**', async (route) => { const req = route.request(), url = new URL(req.url()), path = url.pathname.slice(4) if (!url.pathname.startsWith('/api/')) return route.continue() const user = { id: 'fixture', userId: 'fixture', userName: '测试', role: 'admin', roles: ['admin'] } if (path === '/web/auth/mp-webview-exchange') return route.fulfill({ json: { code: 200, data: { token: 'fixture', refreshToken: 'refresh', expiresIn: 3600, userInfo: user } } }) if (path === '/web/auth/current-user') return route.fulfill({ json: { code: 200, data: user } }) if (path === '/web/auth/menu-codes') return route.fulfill({ json: { code: 200, data: { all: allPermissions, menuCodes: ['mini:pl:site-dispatch-calendar:list'] } } }) if (path === '/transport/site-dispatch-calendar' && req.method() === 'GET') { lastMonth = url.searchParams.get('month') assert.equal(url.searchParams.has('calendarKind'), false) const list = rows.filter((row) => row.dispatchDate.startsWith(lastMonth)) return route.fulfill({ json: { list, total: list.length } }) } if (path.startsWith('/transport/')) { forbiddenRequests.push(req.method() + ' ' + path) return route.fulfill({ status: 403, json: { detail: 'forbidden' } }) } return route.fulfill({ json: { code: 200, data: path.includes('favorites') || path.includes('upgrade-logs') ? [] : {} } }) }) await page.goto('http://localhost:3006/#/m/pl/site-dispatch-calendar?mpTicket=readonly-test') await page.getByText('现场派车日历', { exact: true }).waitFor() await page.locator('[data-testid="manual-record"]').nth(1).waitFor() assert.equal(await page.getByRole('tab').count(), 0) assert.equal(await page.getByRole('button', { name: /新增/ }).count(), 0) assert.equal(await page.locator('[data-testid="manual-editor"]').count(), 0) for (let index = 0; index < 2; index++) { await page.locator('[data-testid="manual-record"]').nth(index).click() await page.getByText('派车计划详情', { exact: true }).waitFor() assert.equal(await page.getByRole('button', { name: '编辑', exact: true }).count(), 0) assert.equal(await page.getByRole('button', { name: '删除', exact: true }).count(), 0) await page.locator('.van-popup:visible .van-popup__close-icon').click() } await page .locator('[data-date="2026-10-26"]') .getByRole('button', { name: '复制', exact: true }) .click() assert.equal( await page.evaluate(() => navigator.clipboard.readText()), '2026-10-26 派车计划:\n\n液碱2车\n ·皖H12345 液碱 32吨(出库计划)\n ·皖H54321 液碱 28吨(入库计划)' ) await page.getByLabel('车辆筛选', { exact: true }).click() let vehiclePicker = page.locator('.mp-calendar-vehicle-picker:visible') await vehiclePicker.getByText('皖H54321', { exact: true }).click() await vehiclePicker.getByRole('button', { name: '确认', exact: true }).click() await page .locator('[data-testid="manual-record"]') .filter({ hasText: '皖H12345' }) .waitFor({ state: 'hidden' }) assert.equal(await page.locator('[data-testid="manual-record"]').count(), 1) await page .locator('[data-date="2026-10-26"]') .getByRole('button', { name: '复制', exact: true }) .click() assert.equal( await page.evaluate(() => navigator.clipboard.readText()), '2026-10-26 派车计划:\n\n液碱1车\n ·皖H54321 液碱 28吨(入库计划)' ) await page.getByLabel('车辆筛选', { exact: true }).click() vehiclePicker = page.locator('.mp-calendar-vehicle-picker:visible') await vehiclePicker.getByText('全部车辆', { exact: true }).click() await vehiclePicker.getByRole('button', { name: '确认', exact: true }).click() await page.getByLabel('查看月份', { exact: true }).click() const popup = page.locator('.mp-calendar-date-picker:visible') await popup.locator('.van-picker-column').nth(1).getByText('09', { exact: true }).click() await popup.getByRole('button', { name: '确认', exact: true }).click() await page.getByText('本月暂无现场派车计划', { exact: true }).waitFor() assert.equal(lastMonth, '2026-09') await page.getByRole('button', { name: '今天', exact: true }).click() await page.locator('[data-testid="manual-record"]').nth(1).waitFor() for (const width of [320, 375, 414]) { await page.setViewportSize({ width, height: 812 }) assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1)) } await page.screenshot({ path: `/tmp/site-readonly-${allPermissions ? 'admin' : 'staff'}.png` }) assert.deepEqual(forbiddenRequests, []) assert.deepEqual(errors, []) console.log( `readonly site ${allPermissions ? 'all permissions' : 'site permission only'} passed` ) await context.close() } } finally { await browser.close() }