import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' import { parse } from '@vue/compiler-sfc' import { computed, ref } from 'vue' import ts from 'typescript' function component(path: string) { return parse(readFileSync(new URL(`../src/views/m/${path}.vue`, import.meta.url), 'utf8')) .descriptor } function evaluate(path: string, name: string, bindings: Record) { const ast = ts.createSourceFile( 'page.ts', component(path).scriptSetup!.content, ts.ScriptTarget.Latest, true ) const statement = ast.statements.find((node) => ts.isVariableStatement(node) ? node.declarationList.declarations.some((d) => d.name.getText(ast) === name) : ts.isFunctionDeclaration(node) && node.name?.text === name )! assert.ok(statement) return new Function( ...Object.keys(bindings), ts.transpile(statement.getText(ast)) + `; return ${name}` )(...Object.values(bindings)) } test('main record list/detail templates have no delete buttons or delete event bindings', () => { for (const path of [ 'crm/customer/index', 'crm/customer/detail', 'crm/product/index', 'crm/product/detail', 'crm/quote/MpQuoteList', 'crm/quote/detail', 'crm/quote/index', 'crm/quote/approval', 'crm/contract/MpContractList', 'crm/contract/detail', 'crm/contract/index', 'crm/contract/approval', 'pl/plan/index', 'pl/plan/detail', 'pl/plan-cert/index', 'pl/plan-cert/PlanCard', 'pl/plan-cert/detail', 'pl/plan-cert/CertDetail' ]) { const template = component(path).template!.content assert.doesNotMatch(template, /删除|@click="(?:onDelete|deleteCard|remove)\b/, path) } }) test('quote and contract management/approval actions never offer main record deletion across statuses', () => { for (const domain of ['quote', 'contract']) { for (const approve of [false, true]) { for (const status of [ 'draft', 'submitted', 'approving', 'accepted', 'rejected', 'approved', 'executing', 'completed', 'invalid' ]) { const actions = evaluate(`crm/${domain}/detail`, 'fabActions', { computed, quote: ref({ status }), contract: ref({ status }), props: { embedded: false }, isApproveEntry: ref(approve), pendingApproval: ref({ id: 'approval' }), canApproveNow: ref(true), canInvalid: ref(true), canDelete: ref(true), CREATOR_DELETE_TIP: '', isQuoteEditableStatus: () => true, isContractEditableStatus: () => true }).value assert.ok(actions.length > 0) assert.ok( actions.every((action: any) => action.key !== 'delete' && action.label !== '删除') ) const cardActions = evaluate( `crm/${domain}/Mp${domain === 'quote' ? 'Quote' : 'Contract'}List`, 'cardActions', { props: { mode: approve ? 'approve' : 'manage' }, view: ref('submitted') } )({ status, canApprove: true }) assert.ok( cardActions.every((action: any) => action.key !== 'delete' && action.label !== '删除') ) } } } })