| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- import assert from 'node:assert/strict'
- import { chromium } from 'playwright'
- const browser = await chromium.launch({ headless: true, channel: 'chrome' })
- try {
- for (const mobile of [false, true]) {
- const context = await browser.newContext({
- permissions: ['clipboard-read', 'clipboard-write'],
- viewport: mobile ? { width: 375, height: 812 } : { width: 1440, height: 1000 }
- })
- const page = await context.newPage()
- await page.clock.setFixedTime(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))
- let rows = []
- let writes = 0
- let failSave = false
- let allowManage = true
- let failLoad = false
- 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: false,
- menuCodes: [
- 'pl:dispatch-calendar:list',
- 'mini:pl:dispatch-calendar:list',
- ...(allowManage ? ['pl:dispatch-calendar:manage'] : [])
- ]
- }
- }
- })
- if (path === '/transport/dispatch-calendar/vehicles')
- return route.fulfill({
- json: {
- list: [
- { id: 'v1', licensePlate: '皖H12345' },
- { id: 'v2', licensePlate: '皖H54321' }
- ],
- total: 2
- }
- })
- if (path === '/transport/dispatch-calendar/products')
- return route.fulfill({ json: { list: [{ id: 'p1', productName: '液碱' }], total: 1 } })
- if (path.startsWith('/transport/dispatch-calendar')) {
- if (req.method() === 'POST' || req.method() === 'PUT') {
- writes++
- if (failSave) {
- failSave = false
- return route.fulfill({ status: 500, json: { detail: 'fixture' } })
- }
- const body = req.postDataJSON(),
- id = req.method() === 'PUT' ? path.split('/').pop() : String(writes)
- const row = { ...body, id, licensePlate: '皖H12345', productName: '液碱' }
- rows = rows.filter((r) => r.id !== id).concat(row)
- return route.fulfill({ json: row })
- }
- if (req.method() === 'DELETE') {
- rows = rows.filter((r) => r.id !== path.split('/').pop())
- return route.fulfill({ json: { deleted: 1 } })
- }
- if (failLoad) {
- failLoad = false
- return route.fulfill({ status: 500, json: { detail: 'expected load failure' } })
- }
- const kind = url.searchParams.get('calendarKind') || 'acid'
- const list = rows.filter(
- (r) =>
- r.dispatchDate.startsWith(url.searchParams.get('month')) &&
- (r.calendarKind || 'acid') === kind
- )
- return route.fulfill({ json: { list, total: list.length } })
- }
- if (path === '/transport/plan/') return route.fulfill({ json: { list: [], total: 0 } })
- return route.fulfill({
- json: {
- code: 200,
- data: path.includes('favorites') || path.includes('upgrade-logs') ? [] : {}
- }
- })
- })
- await page.goto(
- `http://localhost:3006/#/${mobile ? 'm/pl/dispatch-calendar?mpTicket=manual-test' : 'pl/calendar-module/dispatch-calendar'}`
- )
- await page
- .getByRole('tab', { name: '酸派车日历', exact: true })
- .waitFor()
- .catch(async (e) => {
- console.log('PAGE', await page.locator('body').innerText(), errors)
- throw e
- })
- if (mobile) {
- 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()
- } else {
- await page.getByRole('button', { name: '上月', exact: true }).click()
- }
- await page.getByRole('button', { name: '今天', exact: true }).click()
- await page.locator('[data-date="2026-10-26"].selected').waitFor()
- if (mobile) {
- const size = await page.getByRole('button', { name: '今天', exact: true }).boundingBox()
- assert.ok(size.height >= 44)
- assert.equal(
- await page.locator('.mp-calendar-month-row').evaluate((el) => getComputedStyle(el).display),
- 'grid'
- )
- }
- await page
- .getByRole('button', {
- name: '新增2026-10-26派车计划',
- exact: true
- })
- .click({ timeout: 10000 })
- const editor = page.locator('[data-testid="manual-editor"]:visible')
- if (mobile) {
- await editor.getByPlaceholder('选择车辆').click()
- await page
- .getByRole('button', { name: '皖H12345', exact: true })
- .click({ timeout: 8000 })
- .catch(async (e) => {
- console.log('PICKER', await page.locator('.mp-manual-sheet:visible').allTextContents())
- await page.screenshot({ path: '/tmp/manual-calendar-picker-debug.png' })
- throw e
- })
- await editor.getByPlaceholder('选择产品').click()
- await page.getByRole('button', { name: '液碱', exact: true }).click()
- } else {
- await editor.getByRole('combobox', { name: '车牌号', exact: true }).click()
- await page.getByRole('option', { name: '皖H12345', exact: true }).click()
- await editor.getByRole('combobox', { name: '产品名称', exact: true }).click()
- await page.getByRole('option', { name: '液碱', exact: true }).click()
- }
- await editor.getByPlaceholder('请输入始发地').fill('山峡')
- await editor.getByPlaceholder('请输入目的地').fill('云栋')
- failSave = true
- await editor.getByRole('button', { name: '保存', exact: true }).click()
- await editor.getByText('保存失败,请检查填写内容或权限后重试').waitFor()
- assert.equal(await editor.getByPlaceholder('请输入始发地').inputValue(), '山峡')
- await editor.getByRole('button', { name: '保存', exact: true }).click()
- await editor.waitFor({ state: 'hidden' })
- assert.equal(rows.length, 1)
- const card = page.locator('[data-testid="manual-record"]').first()
- await card.waitFor()
- await page.screenshot({
- path: `/tmp/manual-calendar-${mobile ? 'mobile' : 'web'}.png`,
- fullPage: false
- })
- assert.match(await card.innerText(), /山峡 → 云栋/)
- await page
- .locator('[data-date="2026-10-26"]')
- .getByRole('button', { name: mobile ? '复制' : '复制2026-10-26派车计划', exact: true })
- .click()
- assert.match(
- await page.evaluate(() => navigator.clipboard.readText()),
- /皖H12345.*液碱.*山峡 → 云栋/
- )
- await card.click()
- await page.getByRole('button', { name: '编辑', exact: true }).click()
- await editor.getByPlaceholder('请输入目的地').fill('安庆')
- await editor.getByRole('button', { name: '保存', exact: true }).click()
- await editor.waitFor({ state: 'hidden' })
- await page.getByText('山峡 → 安庆', { exact: true }).first().waitFor()
- await page.locator('[data-testid="manual-record"]').first().click()
- await page.getByRole('button', { name: '删除', exact: true }).click()
- await page.getByRole('button', { name: mobile ? '确认删除' : '确定', exact: true }).click()
- await page.locator('[data-testid="manual-record"]').waitFor({ state: 'hidden' })
- assert.equal(rows.length, 0)
- failLoad = true
- await page.getByRole('tab', { name: '实际派车信息', exact: true }).click()
- await page.getByRole('tab', { name: '酸派车日历', exact: true }).click()
- await page.getByText('派车日历加载失败,请重试', { exact: false }).first().waitFor()
- await page.getByRole('button', { name: '重试', exact: true }).click()
- await page
- .getByText('派车日历加载失败,请重试', { exact: false })
- .first()
- .waitFor({ state: 'hidden' })
- if (mobile)
- 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.getByRole('tab', { name: '实际派车信息', exact: true }).click()
- await page.getByText('本月暂无派车计划', { exact: true }).first().waitFor()
- allowManage = false
- rows = [
- {
- id: 'readonly',
- dispatchDate: '2026-10-26',
- vehicleId: 'v1',
- licensePlate: '皖H12345',
- productId: 'p1',
- productName: '液碱',
- origin: '山峡',
- destination: '云栋',
- remark: '',
- calendarKind: 'acid'
- }
- ]
- await page.getByRole('tab', { name: '酸派车日历', exact: true }).click()
- await page.locator('[data-testid="manual-record"]').first().waitFor()
- assert.equal(await page.getByRole('button', { name: '新增派车计划', exact: true }).count(), 0)
- await page.locator('[data-testid="manual-record"]').first().click()
- assert.equal(await page.getByRole('button', { name: '编辑', exact: true }).count(), 0)
- assert.equal(await page.getByRole('button', { name: '删除', exact: true }).count(), 0)
- assert.deepEqual(errors, [])
- await context.close()
- console.log(mobile ? 'mobile CRUD passed' : 'web CRUD passed')
- }
- } finally {
- await browser.close()
- }
|