57 lines
3.8 KiB
TypeScript
57 lines
3.8 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import { readFileSync } from 'node:fs'
|
|
import test from 'node:test'
|
|
import { parse } from '@vue/compiler-sfc'
|
|
import ts from 'typescript'
|
|
import * as stopForms from '../src/utils/pl-plan-stop-form'
|
|
import { collectStopContractIds } from '../src/utils/pl-plan-contract-product'
|
|
|
|
const source = parse(readFileSync(new URL('../src/components/business/pl-plan-form-dialog/index.vue', import.meta.url), 'utf8')).descriptor.scriptSetup!.content
|
|
const ast = ts.createSourceFile('plan.ts', source, ts.ScriptTarget.Latest, true)
|
|
|
|
function contractWatcher(side: 'load' | 'delivery', bindings: Record<string, unknown>) {
|
|
const statement = ast.statements.find((node) => {
|
|
if (!ts.isExpressionStatement(node) || !ts.isCallExpression(node.expression)) return false
|
|
const call = node.expression
|
|
return call.expression.getText(ast) === 'watch' && (side === 'load'
|
|
? call.arguments[0].getText(ast) === '() => loadForm.value.contracts'
|
|
: call.arguments[0].getText(ast).includes('contractKey: collectStopContractIds'))
|
|
}) as ts.ExpressionStatement
|
|
assert.ok(statement, 'actual contract watcher exists')
|
|
const callback = (statement.expression as ts.CallExpression).arguments[1].getText(ast)
|
|
return new Function(...Object.keys(bindings), ts.transpile(`let loadContractFetchSeq = 0; const run = ${callback};`) + ';return run')(...Object.values(bindings))
|
|
}
|
|
|
|
for (const mobile of [false, true]) {
|
|
for (const side of ['load', 'delivery'] as const) {
|
|
for (const change of ['add', 'remove', 'clear'] as const) {
|
|
test(`${mobile ? 'H5' : 'desktop'} ${side} ${change} contracts keeps unaffected row identities`, async () => {
|
|
const kept = { id: 'row-a', planProductId: 'pp-a', contractId: 'a', contractProductId: 'line-a', productId: 'sku', inboundQty: 3, archiveRemark: 'preserve', planQty: 8 }
|
|
const removed = { id: 'row-b', planProductId: 'pp-b', contractId: 'b', productId: 'sku', planQty: 6 }
|
|
const legacy = { id: 'row-old', planProductId: 'pp-old', productId: 'legacy', planQty: 2 }
|
|
const before = ['a', 'b']
|
|
const after = change === 'add' ? ['a', 'b', 'c'] : change === 'remove' ? ['a'] : []
|
|
const stop = { id: 'stop', contracts: after.map(contractId => ({ contractId })), products: [kept, removed, legacy] }
|
|
const loadForm = { value: stop }
|
|
const deliveryForms = { value: [stop] }
|
|
const run = contractWatcher(side, {
|
|
isPageVariant: { value: mobile }, pageReady: { value: true }, pageLoadGeneration: 1,
|
|
detailLoading: { value: false }, suppressStopProductSync: { value: false },
|
|
loadForm, deliveryForms, collectStopContractIds, mobileStopIdentity: () => 1,
|
|
deliveryContractFetchSeqByIndex: {}, loadContractProductIds: { value: [] },
|
|
contractProductIdsCache: { value: {} }, fetchContractProductIds: async () => {},
|
|
isPageLoadCurrent: () => true, unionContractProductIds: () => ['sku', 'legacy'],
|
|
filterProductsByAllowedProductIds: (rows: unknown[]) => rows, syncFromLoadProducts: () => {},
|
|
retainStopProductsForContracts: (stopForms as any).retainStopProductsForContracts
|
|
})
|
|
if (side === 'load') await run(stop.contracts, before.map(contractId => ({ contractId })))
|
|
else await run([{ identity: mobile ? 1 : undefined, contractKey: after.join(',') }], [{ identity: mobile ? 1 : undefined, contractKey: before.join(',') }])
|
|
const products = side === 'load' ? loadForm.value.products : deliveryForms.value[0].products
|
|
const expected = change === 'add' ? [kept, removed, legacy] : change === 'remove' ? [kept, legacy] : [legacy]
|
|
assert.deepEqual(products, expected)
|
|
expected.forEach((row, index) => assert.equal(products[index], row, 'keep original row object and all saved fields'))
|
|
})
|
|
}
|
|
}
|
|
}
|