92 lines
4.8 KiB
TypeScript
92 lines
4.8 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
import { readFileSync } from 'node:fs'
|
|
import { parse } from '@vue/compiler-sfc'
|
|
import ts from 'typescript'
|
|
import * as vue from 'vue'
|
|
import { createRouter, createMemoryHistory, useRoute, useRouter } from 'vue-router'
|
|
import * as navigation from '../src/utils/mp-webview'
|
|
|
|
const flush = async () => { for (let i = 0; i < 20; i++) await Promise.resolve() }
|
|
async function mount(kind: 'contract' | 'quote', embedded = false) {
|
|
const filename = `../src/views/m/crm/${kind}/detail.vue`
|
|
const script = parse(readFileSync(new URL(filename, import.meta.url), 'utf8')).descriptor.scriptSetup!.content
|
|
const ast = ts.createSourceFile('detail.ts', script, ts.ScriptTarget.Latest, true)
|
|
const bindings: Record<string, any> = {}
|
|
for (const n of ast.statements.filter(ts.isImportDeclaration)) {
|
|
if (n.importClause?.name) bindings[n.importClause.name.text] = () => undefined
|
|
const named = n.importClause?.namedBindings
|
|
if (named && ts.isNamedImports(named)) for (const e of named.elements) bindings[e.name.text] = () => undefined
|
|
}
|
|
const router = createRouter({ history: createMemoryHistory(), routes: [
|
|
{ path: '/m/crm/contract/:id', name: 'MpCrmContractDetail', component: {} },
|
|
{ path: '/m/crm/quote/:id', name: 'MpCrmQuoteDetail', component: {} },
|
|
{ path: '/m/crm/customer/:id', name: 'MpCrmCustomerDetail', component: {} },
|
|
{ path: '/m/pl/plan/:id', name: 'MpPlanDetail', component: {} }
|
|
] })
|
|
await router.push(embedded ? '/m/pl/plan/P1' : `/m/crm/${kind}/A`)
|
|
const reads: Array<{ id: string; resolve: (value: any) => void }> = []
|
|
const props = vue.reactive({ embedded, recordId: 'E1', hideProductPrice: false })
|
|
const transport = (id: string) => new Promise((resolve) => reads.push({ id, resolve }))
|
|
Object.assign(bindings, Object.fromEntries(Object.entries(vue).filter(([k]) => /^[a-zA-Z_$][\w$]*$/.test(k) && k !== 'default')), navigation, {
|
|
useRoute, useRouter,
|
|
defineProps: () => props, withDefaults: (value: any) => value,
|
|
useUserStore: () => ({ info: {} }),
|
|
useDictStore: () => ({ dictMap: {}, getOptions: () => [], getLabel: () => '', getTagType: () => 'info' }),
|
|
getContract: transport, getQuote: transport,
|
|
fetchCategoryTree: async () => [], buildPathLabelMapFromTreeRoots: () => new Map(),
|
|
listContractApproval: async () => ({ data: [] }), listApproval: async () => ({ data: [] }),
|
|
listQuoteProduct: async () => ({ rows: [] }), enrichQuoteDetailProductMeta: async () => {},
|
|
document: { querySelector: () => null }, window: { addEventListener() {}, removeEventListener() {}, clearTimeout() {} }
|
|
})
|
|
const statements = ast.statements.filter((s) => !ts.isImportDeclaration(s))
|
|
const names = statements.flatMap((s) => ts.isFunctionDeclaration(s) ? [s.name!.text] : ts.isVariableStatement(s) ? s.declarationList.declarations.map((d) => d.name.getText(ast)) : [])
|
|
const code = ts.transpile(statements.map((n) => n.getText(ast)).join('\n'), {target:ts.ScriptTarget.ESNext})
|
|
const renderer = vue.createRenderer<any, any>({patchProp(){},insert(){},remove(){},createElement:()=>({}),createText:()=>({}),createComment:()=>({}),setText(){},setElementText(){},parentNode:()=>null,nextSibling:()=>null})
|
|
let api: any
|
|
const app = renderer.createApp({setup() {
|
|
api = new Function(...Object.keys(bindings), `${code};return {${names.join(',')}}`)(...Object.values(bindings))
|
|
return () => vue.h('div')
|
|
}})
|
|
app.use(router)
|
|
app.mount({})
|
|
api.onShellReady()
|
|
await flush()
|
|
return { api, reads, router, props, stop: () => app.unmount() }
|
|
}
|
|
|
|
for (const kind of ['contract', 'quote'] as const) for (const nextId of ['A', 'B']) {
|
|
test(`${kind}离开到客户${nextId},不请求客户ID且迟到详情无权回写`, async () => {
|
|
const h = await mount(kind)
|
|
assert.deepEqual(h.reads.map((r) => r.id), ['A'])
|
|
await h.router.push(`/m/crm/customer/${nextId}`)
|
|
await flush()
|
|
assert.deepEqual(h.reads.map((r) => r.id), ['A'], '离开watch不能发起外模块请求')
|
|
void h.api.loadDetail()
|
|
await flush()
|
|
assert.deepEqual(h.reads.map((r) => r.id), ['A'])
|
|
h.reads[0].resolve({ id: 'A', products: [], productList: [] })
|
|
await flush()
|
|
assert.equal(h.api[kind].value, null)
|
|
assert.equal(h.api.currentRecordId(), '')
|
|
await h.router.push(`/m/crm/${kind}/A`)
|
|
await flush()
|
|
assert.deepEqual(h.reads.map((r) => r.id), ['A', 'A'])
|
|
h.stop()
|
|
})
|
|
}
|
|
|
|
test('embedded合同只认recordId,宿主路径和ID变化不触发详情读取', async () => {
|
|
const h = await mount('contract', true)
|
|
await h.router.push('/m/crm/customer/B')
|
|
await flush()
|
|
assert.deepEqual(h.reads.map((r) => r.id), ['E1'])
|
|
h.props.recordId = 'E2'
|
|
await flush()
|
|
assert.deepEqual(h.reads.map((r) => r.id), ['E1', 'E2'])
|
|
h.reads[0].resolve({id:'E1'})
|
|
await flush()
|
|
assert.equal(h.api.contract.value, null)
|
|
h.stop()
|
|
})
|