test-pl-plan-cert-upload-dialog.mjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import assert from 'node:assert/strict'
  2. import { chromium } from 'playwright'
  3. import { installCertFixture } from './mp-pl-plan-cert-fixture.mjs'
  4. // 挂载真实 Web 弹窗与附件组件,仅替换 API 边界,不写入业务数据库。
  5. const origin = process.env.PLAN_CERT_TEST_ORIGIN || 'http://localhost:3006'
  6. const browser = await chromium.launch({ headless: true, channel: 'chrome' })
  7. const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } })
  8. const fixture = await installCertFixture(context)
  9. const page = await context.newPage()
  10. const errors = []
  11. page.on('pageerror', (error) => errors.push(error.message))
  12. page.setDefaultTimeout(15000)
  13. let releaseUpload
  14. const saves = () =>
  15. fixture.calls.filter((c) => c.method === 'PATCH' && c.path.endsWith('/product-certs'))
  16. try {
  17. await page.goto(`${origin}/#/m/pl/plan-cert?mpTicket=cert-fixture`)
  18. await page.getByText('YS001', { exact: true }).first().waitFor()
  19. await page.evaluate(async () => {
  20. const { createVNode, render, defineComponent, ref, h } = await import(
  21. '/node_modules/.vite/deps/vue.js'
  22. )
  23. const { default: Dialog } = await import(
  24. '/src/components/business/pl-plan-cert-upload-dialog/index.vue'
  25. )
  26. const host = document.createElement('div')
  27. document.body.appendChild(host)
  28. const Wrapper = defineComponent({
  29. setup() {
  30. const visible = ref(true)
  31. return () => [
  32. h(
  33. 'button',
  34. {
  35. id: 'reopen-web-cert',
  36. onClick: () => {
  37. visible.value = true
  38. }
  39. },
  40. '重新打开 Web 证书'
  41. ),
  42. h(Dialog, {
  43. visible: visible.value,
  44. planId: 'plan-1',
  45. 'onUpdate:visible': (v) => {
  46. visible.value = v
  47. }
  48. })
  49. ]
  50. }
  51. })
  52. const vnode = createVNode(Wrapper)
  53. vnode.appContext = document.querySelector('#app').__vue_app__._context
  54. render(vnode, host)
  55. })
  56. const dialog = page.getByRole('dialog').filter({ hasText: '上传危化品证书' })
  57. await dialog.getByText('测试易制毒产品', { exact: true }).waitFor()
  58. assert.equal(await dialog.locator('input[type="file"]').count(), 3)
  59. // 使用最后一个产品的附件,确保保护覆盖所有嵌套循环中的上传组件。
  60. fixture.uploadGate = new Promise((resolve) => {
  61. releaseUpload = resolve
  62. })
  63. await dialog
  64. .locator('input[type="file"]')
  65. .last()
  66. .setInputFiles({
  67. name: 'legal-cert.pdf',
  68. mimeType: 'application/pdf',
  69. buffer: Buffer.from('%PDF-1.4\nfixture')
  70. })
  71. await dialog.locator('.is-uploading').waitFor()
  72. await dialog.getByRole('button', { name: '保存', exact: true }).click()
  73. // toast 表明真实处理函数已完成,不以固定等待推断没有请求。
  74. await page
  75. .getByText(/^(附件正在上传,请稍候再保存|证书保存成功)$/)
  76. .first()
  77. .waitFor()
  78. assert.equal(saves().length, 0, '附件未上传完成时不能提交旧 URL')
  79. assert.equal(
  80. await page.getByText('附件正在上传,请稍候再保存', { exact: true }).isVisible(),
  81. true
  82. )
  83. assert.equal(await dialog.isVisible(), true)
  84. releaseUpload()
  85. await dialog.locator('.is-uploading').waitFor({ state: 'hidden' })
  86. await dialog.getByRole('button', { name: '保存', exact: true }).click()
  87. await dialog.waitFor({ state: 'hidden' })
  88. assert.equal(saves().length, 1)
  89. assert.ok(JSON.stringify(saves()[0].body).includes('/fixtures/cert.pdf'))
  90. // 关闭销毁后重新打开,不遗留上传引用;正常保存仍可用。
  91. await page.locator('#reopen-web-cert').click()
  92. await dialog.getByText('测试易制毒产品', { exact: true }).waitFor()
  93. await dialog.getByRole('button', { name: '保存', exact: true }).click()
  94. await dialog.waitFor({ state: 'hidden' })
  95. assert.equal(saves().length, 2)
  96. assert.deepEqual(errors, [])
  97. assert.deepEqual(fixture.unknown, [])
  98. console.log('PASS: Web 附件上传中阻止保存、上传后提交新 URL、销毁重开正常保存')
  99. } finally {
  100. releaseUpload?.()
  101. await browser.close()
  102. }