| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- import assert from 'node:assert/strict'
- import { mkdir } from 'node:fs/promises'
- import { chromium } from 'playwright'
- // 本地 H5 交互回归:所有 API 均由 fixture 拦截,不写真实业务库。
- const origin = process.env.QUOTE_TEST_ORIGIN || 'http://localhost:3006'
- const artifacts = '/tmp/shanxia-quote-e2e'
- await mkdir(artifacts, { recursive: true })
- const browser = await chromium.launch({ headless: true, channel: 'chrome' })
- const context = await browser.newContext({
- viewport: { width: 375, height: 812 },
- isMobile: true,
- deviceScaleFactor: 1
- })
- const page = await context.newPage()
- const pageErrors = []
- const calls = []
- page.on('pageerror', (error) => pageErrors.push(error.message))
- const quote = {
- id: 'quote-1',
- quoteNo: 'BJ-20261017-001',
- quoteTitle: '山峡化工年度报价',
- quoteType: 'purchase',
- customerId: 'customer-1',
- customerName: '宜昌测试客户',
- contactName: '张经理',
- contactMobile: '13800138000',
- version: 'V1',
- parentId: '0',
- isLatest: 1,
- totalAmount: 128600,
- discountRate: 95,
- discountAmount: 6430,
- finalAmount: 122170,
- validityDays: 30,
- quoteDate: '2026-10-17',
- expireDate: '2026-11-16',
- status: 'draft',
- ownerUserId: 'user-1',
- ownerUserName: '测试销售',
- paymentTerms: '月结 30 天',
- deliveryTerms: '送货上门',
- freight: 500,
- barrelDeposit: 1000,
- attachments: JSON.stringify([{ ossId: '/fixtures/报价说明.pdf', category: 'other' }]),
- remark: 'fixture 报价单',
- createBy: 'user-1',
- createTime: '2026-10-17 09:00:00',
- productList: [
- {
- id: 'product-line-1',
- standardProductName: '工业甲醇',
- model: '优级品',
- quantity: 10,
- unit: 'ton',
- price: 12000,
- subtotal: 120000
- }
- ]
- }
- let approvals = [
- {
- id: 'approval-1',
- quoteId: quote.id,
- quoteNo: quote.quoteNo,
- step: 1,
- approverId: 'user-1',
- approverName: '测试审批人',
- approvalResult: 'pending',
- approvalOpinion: '',
- approvalTime: '',
- createTime: '2026-10-17 10:00:00'
- }
- ]
- let slowNextPage2 = false
- let failNextPage2 = false
- let forbidNextList = false
- const dict = {
- crm_quote_status: [
- { value: 'draft', label: '草稿', tagType: 'info' },
- { value: 'submitted', label: '待审批', tagType: 'warning' },
- { value: 'accepted', label: '已审批', tagType: 'success' },
- { value: 'rejected', label: '已驳回', tagType: 'danger' }
- ],
- crm_product_unit: [{ value: 'ton', label: '吨', tagType: 'info' }]
- }
- await context.addInitScript(() => {
- window.wx = {
- miniProgram: {
- navigateBack() {},
- reLaunch() {},
- getEnv(callback) {
- callback({ miniprogram: true })
- }
- }
- }
- })
- await context.route('**/api/**', async (route) => {
- const request = route.request()
- const url = new URL(request.url())
- if (!url.pathname.startsWith('/api/')) return route.continue()
- const path = url.pathname.replace(/^\/api/, '')
- let body = {}
- try {
- body = request.postDataJSON() || {}
- } catch {
- // 非 JSON 请求不参与本用例的 body 断言。
- }
- calls.push({ path, body })
- let data = {}
- let code = 200
- let msg = 'ok'
- if (path === '/web/auth/mp-webview-exchange') {
- data = {
- token: 'fixture-token',
- refreshToken: 'fixture-refresh',
- userInfo: {
- id: 'user-1',
- userId: 'user-1',
- userName: '测试用户',
- role: 'admin',
- roles: ['admin']
- }
- }
- } else if (path === '/web/admin/dict-data/all') {
- data = dict
- } else if (path === '/web/admin/users/options') {
- data = [{ value: 'user-1', label: '测试审批人', userNo: 'approver' }]
- } else if (path === '/web/crm/quote/list') {
- if (forbidNextList) {
- forbidNextList = false
- code = 403
- msg = '无权限'
- } else if (Number(body.pageNum ?? body.page) === 2 && failNextPage2) {
- failNextPage2 = false
- code = 500
- msg = '分页测试失败'
- } else {
- if (Number(body.pageNum ?? body.page) === 2 && slowNextPage2) {
- slowNextPage2 = false
- await new Promise((resolve) => setTimeout(resolve, 350))
- }
- data = {
- list:
- Number(body.pageNum ?? body.page) === 2
- ? [{ ...quote, id: 'quote-2', quoteNo: 'BJ-002' }]
- : [quote],
- total: 21
- }
- }
- } else if (path === '/web/crm/quote/detail') {
- data = quote
- } else if (path === '/web/crm/quote/product/list') {
- data = { list: quote.productList, total: quote.productList.length }
- } else if (path === '/web/crm/quote/approval/list') {
- data = { list: approvals }
- } else if (path === '/web/crm/quote-approver-setting/get') {
- data = { approverIds: ['user-1'], status: '0' }
- } else if (path === '/web/crm/quote/submit') {
- quote.status = 'submitted'
- data = true
- } else if (path === '/web/crm/quote/approval/approve') {
- approvals = approvals.map((item) => ({
- ...item,
- approvalResult: body.approvalResult,
- approvalOpinion: body.approvalOpinion,
- approvalTime: '2026-10-17 11:00:00'
- }))
- quote.status = body.approvalResult === 'approved' ? 'accepted' : 'rejected'
- data = true
- } else if (path === '/web/crm/quote/invalid' || path === '/web/crm/quote/delete') {
- data = true
- } else if (path === '/web/crm/quote/versions') {
- data = [quote]
- } else if (path === '/web/admin/attachment/preview-url') {
- data = { previewUrl: 'about:blank#quote-preview' }
- }
- await route.fulfill({
- contentType: 'application/json',
- body: JSON.stringify({ code, msg, data })
- })
- })
- const click = (name) =>
- page.getByRole('button', { name, exact: true }).filter({ visible: true }).click({ force: true })
- const lastCall = (path) => calls.findLast((item) => item.path === path)
- async function openMoreSheet() {
- await page.locator('.mp-wv-fab-bar .mp-wv-btn-more:visible').click({ force: true })
- const sheet = page.getByRole('dialog').filter({ hasText: '更多操作' })
- await sheet.waitFor()
- return sheet
- }
- async function assertNoOverflow(width) {
- await page.setViewportSize({ width, height: 812 })
- assert.equal(
- await page.evaluate(() => document.documentElement.scrollWidth > innerWidth),
- false,
- `页面在 ${width}px 不应横向溢出`
- )
- }
- async function assertTouchTarget(locator, label) {
- const box = await locator.boundingBox()
- assert.ok((box?.width || 0) >= 44, `${label} 宽度应至少 44px`)
- assert.ok((box?.height || 0) >= 28 && (box?.height || 0) <= 32, `${label} 上下内间距减半后高约 30px`)
- }
- try {
- await page.goto(`${origin}/#/m/crm/quote?mpTicket=quote-manage-fixture`)
- await page.getByRole('heading', { name: '报价单管理', exact: true }).waitFor()
- await page.getByRole('button', { name: `查看报价单:${quote.quoteTitle}` }).waitFor()
- const listOrder = await Promise.all(
- ['.mp-wv-header', '.mp-quote-view-tabs', '.mp-quote-dd', '.mp-quote-list-card'].map(
- async (selector) => (await page.locator(selector).first().boundingBox())?.y ?? -1
- )
- )
- assert.deepEqual(
- [...listOrder].sort((a, b) => a - b),
- listOrder
- )
- assert.ok(await page.locator('.mp-quote-dd .van-dropdown-menu__item').count() >= 3)
- assert.equal(await page.locator('.mp-wv-speed-dial').count(), 1)
- const firstManageCard = page.locator('.mp-quote-list-card').first()
- assert.ok(await firstManageCard.getByText('更多字段', { exact: true }).isVisible())
- assert.ok(await firstManageCard.getByRole('button', { name: '复制', exact: true }).isVisible())
- assert.ok(
- await firstManageCard.getByRole('button', { name: '提交审批', exact: true }).isVisible()
- )
- assert.ok(await firstManageCard.getByRole('button', { name: '查看', exact: true }).isVisible())
- await assertTouchTarget(
- page.getByRole('button', { name: '加载更多', exact: true }),
- '加载更多'
- )
- await click('快捷操作')
- await assertTouchTarget(
- page.getByRole('button', { name: '新增报价单', exact: true }),
- 'Speed Dial 新增报价单'
- )
- await click('快捷操作')
- const search = page.getByRole('searchbox', { name: '搜索报价单' })
- await search.fill('临时检索')
- const searchCallsBeforeEnter = calls.filter((item) => item.path === '/web/crm/quote/list').length
- await Promise.all([
- page.waitForResponse((response) => response.url().endsWith('/web/crm/quote/list')),
- search.press('Enter')
- ])
- assert.equal(
- calls.filter((item) => item.path === '/web/crm/quote/list').length,
- searchCallsBeforeEnter + 1
- )
- assert.equal(lastCall('/web/crm/quote/list').body.keyword, '临时检索')
- await Promise.all([
- page.waitForResponse((response) => response.url().endsWith('/web/crm/quote/list')),
- search.fill('')
- ])
- assert.equal(lastCall('/web/crm/quote/list').body.keyword, undefined)
- await click('全部类型')
- await Promise.all([
- page.waitForResponse((response) => response.url().endsWith('/web/crm/quote/list')),
- click('采购')
- ])
- assert.equal(lastCall('/web/crm/quote/list').body.quoteType, 'purchase')
- await click('更多')
- const filterSheet = page.getByRole('dialog').filter({ hasText: '更多筛选' })
- assert.ok(await filterSheet.getByText('报价日期', { exact: true }).isVisible())
- assert.ok(await filterSheet.getByText('金额(元)', { exact: true }).isVisible())
- await filterSheet.locator('input[type="search"]').fill('年度')
- await filterSheet.locator('input[type="date"]').nth(0).fill('2026-10-01')
- await filterSheet.locator('input[type="date"]').nth(1).fill('2026-10-31')
- await filterSheet.locator('input[type="number"]').nth(0).fill('100')
- await filterSheet.locator('input[type="number"]').nth(1).fill('200000')
- await assertTouchTarget(
- filterSheet.getByRole('button', { name: '确定', exact: true }),
- '筛选确定'
- )
- await Promise.all([
- page.waitForResponse((response) => response.url().endsWith('/web/crm/quote/list')),
- filterSheet.getByRole('button', { name: '确定', exact: true }).click()
- ])
- assert.deepEqual(
- {
- keyword: lastCall('/web/crm/quote/list').body.keyword,
- quoteDateBegin: lastCall('/web/crm/quote/list').body.quoteDateBegin,
- quoteDateEnd: lastCall('/web/crm/quote/list').body.quoteDateEnd,
- minAmount: lastCall('/web/crm/quote/list').body.minAmount,
- maxAmount: lastCall('/web/crm/quote/list').body.maxAmount
- },
- {
- keyword: '年度',
- quoteDateBegin: '2026-10-01',
- quoteDateEnd: '2026-10-31',
- minAmount: 100,
- maxAmount: 200000
- }
- )
- slowNextPage2 = true
- const stalePageRequest = page.waitForRequest(
- (request) =>
- request.url().endsWith('/web/crm/quote/list') && request.postDataJSON()?.pageNum === 2
- )
- void click('加载更多')
- await stalePageRequest
- await click('采购')
- await Promise.all([
- page.waitForResponse((response) => response.url().endsWith('/web/crm/quote/list')),
- click('销售')
- ])
- await page.waitForTimeout(450)
- assert.equal(await page.getByText('BJ-002', { exact: true }).count(), 0)
- failNextPage2 = true
- await Promise.all([
- page.waitForResponse((response) => response.url().endsWith('/web/crm/quote/list')),
- click('加载更多')
- ])
- await page.getByText('分页测试失败', { exact: true }).waitFor()
- assert.ok(await page.getByRole('button', { name: '加载更多', exact: true }).isVisible())
- await click('加载更多')
- await page.getByText('BJ-002', { exact: true }).waitFor()
- assert.equal(lastCall('/web/crm/quote/list').body.pageNum, 2)
- await click('快捷操作')
- assert.ok(await page.getByRole('button', { name: '新增报价单', exact: true }).isVisible())
- await click('快捷操作')
- await page.waitForFunction(() => document.querySelectorAll('.el-message').length === 0)
- await page.screenshot({ path: `${artifacts}/manage-list-375.png`, fullPage: true })
- await assertNoOverflow(375)
- await firstManageCard.getByRole('button', { name: '复制', exact: true }).click()
- await page.waitForURL(/\/m\/crm\/quote\/new\?copyFrom=quote-1/)
- await page.goto(`${origin}/#/m/crm/quote?mpTicket=quote-manage-actions`)
- await page.getByRole('heading', { name: '报价单管理', exact: true }).waitFor()
- await firstManageCard.getByRole('button', { name: '提交审批', exact: true }).click()
- await page.getByRole('heading', { name: '报价单详情', exact: true }).waitFor()
- const submitSheet = page.getByRole('dialog').filter({ hasText: '提交审批' })
- await submitSheet.waitFor()
- await assertTouchTarget(
- submitSheet.getByRole('button', { name: '确认提交', exact: true }),
- '提交审批确认'
- )
- assert.doesNotMatch(page.url(), /action=/)
- await submitSheet.getByRole('button', { name: '关闭', exact: true }).click()
- assert.equal(await page.locator('.mp-wv-header').getByText(quote.quoteTitle).count(), 0)
- assert.equal(await page.locator('.mp-wv-header__sub').count(), 0)
- for (const button of await page.locator('.mp-wv-fab-bar .mp-wv-btn').all()) {
- const box = await button.boundingBox()
- assert.ok((box?.width || 0) >= 44 && (box?.height || 0) >= 28 && (box?.height || 0) <= 32)
- }
- await click('编辑')
- await page.waitForURL(/\/m\/crm\/quote\/quote-1\/edit/)
- await page.goBack()
- await page.getByRole('heading', { name: '报价单详情', exact: true }).waitFor()
- let actionSheet = await openMoreSheet()
- const deleteAction = actionSheet.getByRole('button', { name: '删除', exact: true })
- assert.equal(await deleteAction.isDisabled(), false)
- await deleteAction.click()
- const deleteDialog = page.getByRole('dialog', { name: '提示', exact: true })
- await deleteDialog.getByRole('button', { name: '取消', exact: true }).click()
- await deleteDialog.waitFor({ state: 'hidden' })
- await actionSheet.waitFor({ state: 'hidden' })
- actionSheet = await openMoreSheet()
- await actionSheet.getByRole('button', { name: '作废', exact: true }).click()
- const voidDialog = page.getByRole('dialog', { name: '作废报价单', exact: true })
- await voidDialog.getByRole('button', { name: '取消', exact: true }).click()
- await voidDialog.waitFor({ state: 'hidden' })
- await actionSheet.waitFor({ state: 'hidden' })
- actionSheet = await openMoreSheet()
- await actionSheet.getByRole('button', { name: '复制新增', exact: true }).click()
- await page.waitForURL(/\/m\/crm\/quote\/new\?copyFrom=quote-1/)
- await page.goBack()
- await page.getByRole('heading', { name: '报价单详情', exact: true }).waitFor()
- actionSheet = await openMoreSheet()
- await actionSheet.getByRole('button', { name: '版本历史', exact: true }).click()
- await page.waitForURL(/\/m\/crm\/quote\/quote-1\/versions/)
- await page.getByRole('heading', { name: '版本历史', exact: true }).waitFor()
- await page.goBack()
- await page.getByRole('heading', { name: '报价单详情', exact: true }).waitFor()
- await click('产品明细')
- await page.getByText('工业甲醇', { exact: true }).waitFor()
- await click('审批信息')
- await page.locator('.mp-crm-approval-timeline').waitFor()
- await click('附件')
- const attachment = page.locator('.mp-crm-att-grid .att-item').first()
- const attachmentBox = await attachment.boundingBox()
- assert.equal(Math.round(attachmentBox?.width || 0), 72)
- assert.equal(Math.round(attachmentBox?.height || 0), 72)
- assert.ok(await page.getByText('报价说明.pdf', { exact: true }).isVisible())
- await Promise.all([
- page.waitForResponse((response) =>
- response.url().endsWith('/web/admin/attachment/preview-url')
- ),
- attachment.locator('.att-preview').click()
- ])
- assert.equal(lastCall('/web/admin/attachment/preview-url').body.fileName, '报价说明.pdf')
- const previewDialog = page.getByRole('dialog', { name: '报价说明.pdf', exact: true })
- await previewDialog.waitFor()
- await previewDialog.locator('.el-dialog__headerbtn').click()
- await previewDialog.waitFor({ state: 'hidden' })
- await page
- .locator('.mp-quote-detail-tab')
- .filter({ hasText: '基础信息' })
- .evaluate((button) => button.click())
- await page.getByText(quote.quoteNo, { exact: true }).waitFor()
- await page.waitForFunction(() => document.querySelectorAll('.el-message').length === 0)
- await page.screenshot({ path: `${artifacts}/manage-detail-375.png`, fullPage: true })
- await click('提交审批')
- await page.getByRole('dialog').filter({ hasText: '提交审批' }).waitFor()
- await click('确认提交')
- await page.getByText('待审批', { exact: true }).filter({ visible: true }).first().waitFor()
- assert.deepEqual(lastCall('/web/crm/quote/submit').body.approverIds, ['user-1'])
- approvals = [
- {
- ...approvals[0],
- id: 'approval-prior',
- step: '1',
- approverId: 'user-2',
- approverName: '前置审批人',
- approvalResult: 'pending'
- },
- {
- ...approvals[0],
- id: 'approval-current',
- step: 2,
- approverId: 'user-1',
- approverName: '测试审批人',
- approvalResult: 'pending'
- }
- ]
- await page.goto(`${origin}/#/m/crm/quote/quote-1?from=approve&mpTicket=quote-direct-fixture`)
- await page.getByRole('heading', { name: '报价单详情', exact: true }).waitFor()
- await page.reload()
- await page.getByRole('heading', { name: '报价单详情', 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)
- approvals = [
- {
- ...approvals[0],
- id: 'approval-rejected-prior',
- step: 1,
- approverId: 'user-2',
- approvalResult: 'rejected'
- },
- {
- ...approvals[1],
- id: 'approval-current-after-rejected',
- step: 2,
- approverId: 'user-1',
- approvalResult: 'pending'
- }
- ]
- await page.reload()
- await page.getByRole('heading', { name: '报价单详情', 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)
- approvals = [
- {
- ...approvals[1],
- id: 'approval-1',
- step: 1,
- approvalResult: 'pending'
- }
- ]
- quote.status = 'draft'
- await page.reload()
- await page.getByRole('heading', { name: '报价单详情', 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)
- quote.status = 'submitted'
- await page.goto(`${origin}/#/m/crm/quote/approval?mpTicket=quote-approve-fixture`)
- await page.getByRole('heading', { name: '报价单审批', exact: true }).waitFor()
- await page.getByRole('button', { name: `查看报价单:${quote.quoteTitle}` }).waitFor()
- assert.equal(await page.locator('.mp-wv-speed-dial').count(), 0)
- assert.equal(lastCall('/web/crm/quote/list').body.pendingApprovalUserId, 'user-1')
- let approvalCard = page.locator('.mp-quote-list-card').first()
- for (const label of ['驳回', '通过']) {
- const button = approvalCard.getByRole('button', { name: label, exact: true })
- assert.ok(await button.isVisible())
- }
- await approvalCard.getByRole('button', { name: '驳回', exact: true }).click()
- await page.getByRole('heading', { name: '报价单详情', exact: true }).waitFor()
- assert.match(page.url(), /from=approve/)
- const rejectSheet = page.getByRole('dialog').filter({ hasText: '驳回报价单' })
- await rejectSheet.waitFor()
- const approveCallsBeforeRequired = calls.filter(
- (item) => item.path === '/web/crm/quote/approval/approve'
- ).length
- const rejectConfirm = rejectSheet.getByRole('button', { name: '确认驳回', exact: true })
- await assertTouchTarget(rejectConfirm, '驳回确认')
- await rejectConfirm.evaluate((button) => button.click())
- await page.getByText('驳回时请填写拒绝理由', { exact: true }).waitFor()
- assert.equal(
- calls.filter((item) => item.path === '/web/crm/quote/approval/approve').length,
- approveCallsBeforeRequired
- )
- await rejectSheet.locator('textarea').fill('资料不完整')
- await rejectConfirm.evaluate((button) => button.click())
- const rejectDialog = page.locator('.van-dialog.mp-crm-dialog').filter({ hasText: '确认驳回' })
- await rejectDialog.waitFor()
- await rejectDialog.getByRole('button', { name: '确认驳回', exact: true }).click()
- await rejectDialog.waitFor({ state: 'hidden' })
- await rejectSheet.waitFor({ state: 'hidden' })
- assert.equal(lastCall('/web/crm/quote/approval/approve').body.approvalResult, 'rejected')
- assert.equal(lastCall('/web/crm/quote/approval/approve').body.approvalOpinion, '资料不完整')
- quote.status = 'submitted'
- approvals = [
- {
- ...approvals[0],
- id: 'approval-1',
- step: 1,
- approvalResult: 'pending',
- approvalOpinion: '',
- approvalTime: ''
- }
- ]
- await page.goto(`${origin}/#/m/crm/quote/approval?mpTicket=quote-approve-again`)
- await page.getByRole('heading', { name: '报价单审批', exact: true }).waitFor()
- approvalCard = page.locator('.mp-quote-list-card').first()
- await approvalCard.getByRole('button', { name: '通过', exact: true }).click()
- const approveSheet = page.getByRole('dialog').filter({ hasText: '通过报价单' })
- await approveSheet.waitFor()
- const approveConfirm = approveSheet.getByRole('button', { name: '确认通过', exact: true })
- await assertTouchTarget(approveConfirm, '通过确认')
- await approveConfirm.evaluate((button) => button.click())
- const confirmDialog = page.locator('.van-dialog.mp-crm-dialog').filter({ hasText: '确认通过' })
- await confirmDialog.waitFor()
- await confirmDialog.getByRole('button', { name: '确认', exact: true }).click()
- await confirmDialog.waitFor({ state: 'hidden' })
- await approveSheet.waitFor({ state: 'hidden' })
- await page.getByText('已审批', { exact: true }).filter({ visible: true }).first().waitFor()
- assert.equal(lastCall('/web/crm/quote/approval/approve').body.id, 'approval-1')
- assert.equal(lastCall('/web/crm/quote/approval/approve').body.approvalResult, 'approved')
- await page.waitForFunction(() => document.querySelectorAll('.el-message').length === 0)
- await assertNoOverflow(414)
- await page.screenshot({ path: `${artifacts}/approve-detail-414.png`, fullPage: true })
- await page.getByRole('button', { name: '返回报价单审批', exact: true }).click()
- await page.getByRole('heading', { name: '报价单审批', exact: true }).waitFor()
- assert.match(page.url(), /\/m\/crm\/quote\/approval/)
- await Promise.all([
- page.waitForResponse((response) => response.url().endsWith('/web/crm/quote/list')),
- click('已审批')
- ])
- assert.equal(lastCall('/web/crm/quote/list').body.approvedByUserId, 'user-1')
- assert.equal(await page.getByRole('button', { name: '驳回', exact: true }).count(), 0)
- assert.equal(await page.getByRole('button', { name: '通过', exact: true }).count(), 0)
- await page.screenshot({ path: `${artifacts}/approve-list-414.png`, fullPage: true })
- forbidNextList = true
- await page.goto(`${origin}/#/m/crm/quote?mpTicket=quote-forbidden-fixture`)
- await page.getByText('无权限', { exact: true }).waitFor()
- assert.equal(await page.locator('.mp-quote-list-card').count(), 0)
- assert.equal(await page.locator('.mp-wv-speed-dial').count(), 0)
- assert.deepEqual(pageErrors, [])
- console.log(
- JSON.stringify({
- result: 'PASS',
- assertions:
- '管理/审批列表与卡片动作、搜索清空、更多筛选、分页乱序与失败回滚、独立详情直达刷新、列表/Speed Dial/Sheet/FAB 44px触达、空操作底栏、最早审批步骤与前置驳回/报价状态门禁、驳回必填与通过、附件解析预览、403、375/414布局',
- apiCalls: calls.length,
- screenshots: artifacts
- })
- )
- } catch (error) {
- await page.screenshot({ path: `${artifacts}/failure.png`, fullPage: true })
- console.error(await page.locator('body').innerText())
- console.error('Page errors:', pageErrors)
- console.error('API calls:', calls)
- throw error
- } finally {
- await browser.close()
- }
|