596 lines
24 KiB
JavaScript
596 lines
24 KiB
JavaScript
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) >= 44, `${label} 高度应至少 44px`)
|
|
}
|
|
|
|
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) >= 44)
|
|
}
|
|
|
|
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()
|
|
}
|