| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- import assert from 'node:assert/strict'
- import { readFile } from 'node:fs/promises'
- import { compileScript, compileTemplate, parse } from '@vue/compiler-sfc'
- import * as vue from 'vue'
- import ts from 'typescript'
- // Run the real component setup with controlled confirmation and API dependencies.
- const filename = 'WaterWorkspace.vue'
- const source = await readFile(
- new URL('../src/views/water/modules/WaterWorkspace.vue', import.meta.url),
- 'utf8'
- )
- const { descriptor, errors } = parse(source, { filename })
- assert.deepEqual(errors, [])
- const script = compileScript(descriptor, { id: 'package-delete-test' })
- const template = compileTemplate({
- source: descriptor.template.content,
- filename,
- id: 'package-delete-test',
- compilerOptions: { bindingMetadata: script.bindings }
- })
- assert.deepEqual(template.errors, [])
- const saves = []
- const reads = []
- const messages = []
- const confirmations = []
- let confirm = async () => {}
- let save = async () => ({})
- let listing = async () => ({ data: [{ id: 2, name: '保留套餐' }], total: 1 })
- const user = vue.reactive({ info: { id: 1, buttons: [] } })
- const dependencies = {
- vue: { ...vue, onMounted: () => {} },
- 'vue-router': { useRoute: () => ({ query: {} }), useRouter: () => ({ push: () => {} }) },
- 'element-plus': {
- ElMessage: { success: (message) => messages.push(message), warning: () => {} },
- ElMessageBox: {
- confirm: (...args) => {
- confirmations.push(args)
- return confirm()
- }
- }
- },
- xlsx: {},
- '@/store/modules/user': { useUserStore: () => user },
- '@/api/water': {
- saveWater: async (...args) => {
- saves.push(args)
- return save()
- },
- getWater: async (...args) => {
- reads.push(args)
- return listing()
- },
- waterOptions: async () => []
- }
- }
- function evaluate(code) {
- const { outputText } = ts.transpileModule(code, {
- compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS }
- })
- const exports = {}
- const require = (name) => {
- if (name in dependencies) return dependencies[name]
- if (name.endsWith('.vue') || name.endsWith('.scss')) return {}
- throw new Error(`Unexpected dependency: ${name}`)
- }
- new Function('require', 'exports', outputText)(require, exports)
- return exports
- }
- dependencies['./catalog'] = evaluate(
- await readFile(new URL('../src/views/water/modules/catalog.ts', import.meta.url), 'utf8')
- )
- const component = evaluate(script.content).default
- const state = component.setup({ module: 'packages' }, { expose: () => {} })
- const row = { id: '1', name: '待删除套餐' }
- confirm = async () => {
- throw new Error('cancel')
- }
- await state.removePackage(row)
- assert.equal(saves.length, 0)
- assert.equal(reads.length, 0)
- assert.equal(state.removingPackageId.value, undefined)
- assert.match(confirmations[0][0], /待删除套餐.*ID:1/)
- assert.equal(confirmations[0][2].cancelButtonText, '取消')
- let resolveConfirmation
- confirm = () => new Promise((resolve) => (resolveConfirmation = resolve))
- state.preview.value = row
- const deleting = state.removePackage(row)
- assert.equal(saves.length, 0, 'no deletion before confirmation')
- const confirmationCount = confirmations.length
- await state.removePackage(row)
- assert.equal(confirmations.length, confirmationCount, 'repeated click does not open another dialog')
- resolveConfirmation()
- await deleting
- assert.deepEqual(saves, [['packages', { id: 1, action: 'delete' }]])
- assert.equal(reads.length, 1)
- assert.equal(state.preview.value, undefined)
- assert.equal(messages.at(-1), '删除成功')
- assert.equal(state.removingPackageId.value, undefined)
- confirm = async () => {}
- state.page.value = 2
- listing = async () =>
- reads.at(-1)[1].page === 2 ? { data: [], total: 20 } : { data: [{ id: 2 }], total: 20 }
- await state.removePackage({ id: 21, name: '第二页最后一条' })
- assert.deepEqual(
- reads.slice(-2).map(([, query]) => query.page),
- [2, 1]
- )
- assert.equal(state.page.value, 1)
- assert.equal(state.rows.value[0].id, 2)
- const successCount = messages.length
- const readCount = reads.length
- save = async () => {
- throw new Error('API rejected')
- }
- await state.removePackage(row)
- assert.equal(reads.length, readCount, 'failed deletion does not refresh or remove a row')
- assert.equal(messages.length, successCount, 'failed deletion shows no success message')
- assert.equal(state.removingPackageId.value, undefined)
- user.info.id = 2
- const saveCount = saves.length
- const dialogs = confirmations.length
- await state.removePackage(row)
- assert.equal(confirmations.length, dialogs, 'read-only operator cannot initiate deletion')
- assert.equal(saves.length, saveCount)
- console.log(
- 'PASS: component compilation, confirmation, cancellation, duplicate click, ID deletion, pagination, API failure and write permission.'
- )
|