2026-10-10 02:19:39 +08:00
import assert from 'node:assert/strict'
import test from 'node:test'
import { readFileSync } from 'node:fs'
2026-10-29 14:14:56 +08:00
import { createRequire } from 'node:module'
2026-10-10 02:19:39 +08:00
import { runInNewContext } from 'node:vm'
import ts from 'typescript'
import * as vue from 'vue'
import { parse , compileScript } from 'vue/compiler-sfc'
2026-10-29 14:14:56 +08:00
const { default : Schema } = createRequire ( import . meta . resolve ( 'element-plus' ))( 'async-validator' )
2026-10-10 02:19:39 +08:00
let nextKey = 0
const crypto = { randomUUID : () => `test-key- ${ ++ nextKey } ` }
function moduleFrom ( source , require = () => { throw new Error ( 'unexpected import' ) }) {
const context = { exports : {}, require , URL , Error , crypto }
const code = ts . transpileModule ( source , { compilerOptions : { target : ts . ScriptTarget . ES2022 , module : ts . ModuleKind . CommonJS } }). outputText
runInNewContext ( code , context )
return context . exports
}
const presentation = moduleFrom ( readFileSync ( new URL ( './procurementPresentation.ts' , import . meta . url ), 'utf8' ))
const submission = moduleFrom ( readFileSync ( new URL ( './procurementSubmission.ts' , import . meta . url ), 'utf8' ), name => {
if ( name === './procurementPresentation' ) return presentation
throw new Error ( `unexpected import ${ name } ` )
})
const normalized = value => JSON . parse ( JSON . stringify ( value ))
const draft = () => ({ ... presentation . emptyProcurementDraft (), purchaseCategory : 'Sample' , productName : 'Test product' , productSpecification : 'A' ,
purchaseQuantity : '3' , budgetUnitPrice : '0.29' , receivingAddress : '测试省测试市测试街 1 号' , contactName : '测试收件人' , contactPhone : '13800000000' })
const fee = ( purpose , walletStatus , amount ) => ({ purpose , walletStatus , amount , currency : 'CNY' })
test ( 'budget distinguishes unknown from zero, preserves cents, and requires a real telephone/address' , () => {
assert . equal ( presentation . draftBudgetTotal ( draft ()), '0.87' )
const valid = presentation . buildProcurementCreate ( draft (), 'Authenticated Company' , 'same-key' )
assert . equal ( valid . customerName , 'Authenticated Company' )
assert . equal ( valid . budgetUnitPrice , 0.29 )
for ( const value of [ '' , '-1' , '1.001' , '90071992547409.91' ]) assert . throws (() => presentation . buildProcurementCreate ({ ... draft (), budgetUnitPrice : value }, 'Company' , 'key' ), /预算/ )
assert . equal ( presentation . buildProcurementCreate ({ ... draft (), budgetUnitPrice : '0' }, 'Company' , 'key' ). budgetUnitPrice , 0 )
for ( const patch of [{ contactPhone : 'wechat-name' }, { contactPhone : 'a@test.invalid' }, { receivingAddress : '' }, { purchaseQuantity : '1.5' }]) {
assert . throws (() => presentation . buildProcurementCreate ({ ... draft (), ... patch }, 'Company' , 'key' ))
}
assert . equal ( presentation . measurement ( 20 , 2 ), '20.00' )
assert . equal ( presentation . measurement ( '5.125' , 3 ), '5.125' )
assert . equal ( presentation . measurement ( '1.234' , 2 ), '待核对' )
assert . equal ( presentation . money ( null ), '—' )
assert . equal ( presentation . money ( 0 ), 'CNY 0.00' )
assert . equal ( presentation . productHref ( 'javascript:alert(1)' ), undefined )
assert . equal ( presentation . productHref ( 'https://example.com/item' ), 'https://example.com/item' )
})
test ( 'successful net debit excludes the original estimate and counts only successful refunds and differences' , () => {
const result = presentation . feeTotals ([ fee ( 'INTERNATIONAL_ESTIMATE' , 'SETTLED' , '100' ), fee ( 'INTERNATIONAL_FREIGHT' , 'SETTLED' , '100' ),
fee ( 'ADJUSTMENT' , 'SETTLED' , '20' ), fee ( 'ADJUSTMENT' , 'SETTLED' , '10' ), fee ( 'REFUND' , 'REFUNDED' , '20' ), fee ( 'REFUND' , 'REFUND_PENDING' , '5' )])
assert . deepEqual ( normalized ( result ), { paid : '130.00' , refunded : '20.00' , net : '110.00' , estimatedHold : '0.00' })
assert . equal ( presentation . feeTotals ([ fee ( 'INTERNATIONAL_ESTIMATE' , 'RELEASE_PENDING' , '100' )]). estimatedHold , '100.00' )
assert . equal ( presentation . feeTotals ([ fee ( 'DUTY' , 'SETTLED' , 'unknown' )]), null )
assert . equal ( presentation . feeStatus ( fee ( 'INTERNATIONAL_ESTIMATE' , 'FROZEN' , 100 )), '已冻结,未扣款' )
assert . equal ( presentation . feeStatus ( fee ( 'INTERNATIONAL_ESTIMATE' , 'SETTLED' , 100 )), '原预估已结清' )
assert . equal ( presentation . feeStatus ( fee ( 'DUTY' , null , 100 )), '待核定' )
})
2026-10-22 23:06:02 +08:00
test ( 'new requests get unique keys without restoring stored submissions' , () => {
assert . notEqual ( submission . procurementRequestKey ( crypto ), submission . procurementRequestKey ( crypto ))
2026-10-10 02:19:39 +08:00
})
class ApiError extends Error { constructor ( message , code , status ) { super ( message ); this . code = code ; this . status = status } }
const flush = async () => { await vue . nextTick (); await new Promise ( resolve => setImmediate ( resolve )); await vue . nextTick () }
function viewHarness ({ createPage = false , detailPage = false , api = {}, storage = new Map () } = {}) {
const props = vue . reactive ({ createPage , detailPage })
const route = vue . reactive ({ params : { orderNo : detailPage ? 'PO-1' : '' } })
const auth = vue . reactive ({ token : 'identity-A' , user : { tenantName : 'Company' }, profile : { tenantName : 'Company' }, hasPermission : () => true })
const pushes = [], dispose = []
2026-10-27 17:39:28 +08:00
const defaults = { getProcurementWorkflow : async orderNo => ({ orderNo , version : 1 , status : 'WAIT_CUSTOMER_CONFIRM' , receipts : [], outbounds : [] }), confirmProcurement : async () => ({ orderNo : 'PO-1' , version : 2 , status : 'CONFIRMED' }), listProcurements : async () => ({ records : [], total : 0 }), getProcurement : async orderNo => ({ order : { orderNo } }), createProcurement : async () => ({ order : { orderNo : 'PO-CREATED' } }) }
2026-10-10 02:19:39 +08:00
const { descriptor } = parse ( readFileSync ( new URL ( './views/ProcurementView.vue' , import . meta . url ), 'utf8' ))
const source = compileScript ( descriptor , { id : 'procurement-test' }). content
const context = { exports : {}, Error , crypto , sessionStorage : { getItem : key => storage . get ( key ) || null , setItem : ( key , value ) => storage . set ( key , value ), removeItem : key => storage . delete ( key ) },
require ( name ) {
if ( name === 'vue' ) return { ... vue , onBeforeUnmount : callback => dispose . push ( callback ), watch : (... args ) => { const stop = vue . watch (... args ); dispose . push ( stop ); return stop } }
if ( name === 'vue-router' ) return { useRoute : () => route , useRouter : () => ({ push : async target => pushes . push ( target ) }) }
2026-10-29 13:37:28 +08:00
if ( name === 'element-plus' ) return { ElMessage : { success : () => {} }, ElMessageBox : { confirm : async () => {}, prompt : async (... args ) => { const value = api . rejectionReason ?? '照片不清晰' ; const valid = args [ 2 ]. inputValidator ( value ); if ( valid !== true ) throw new Error ( valid ); return { value } } } }
2026-10-10 02:19:39 +08:00
if ( name === '@/api/types' ) return { ApiError }
if ( name === '@/api/session' ) return { accessTokenIdentity : token => token }
if ( name === '@/modules/auth/public' ) return { useAuthStore : () => auth }
2026-10-29 13:37:28 +08:00
if ( name === '../api/orderBudgets' ) return { requireOrderBudget : api . requireOrderBudget || ( async () => {}) }
2026-10-10 02:19:39 +08:00
if ( name === '../api/procurement' ) return { ... defaults , ... api }
if ( name === '../procurementPresentation' ) return presentation
if ( name === '../procurementSubmission' ) return submission
if ( name . endsWith ( '.vue' )) return { default : {} }
throw new Error ( `unexpected view import ${ name } ` )
},
}
runInNewContext ( ts . transpileModule ( source , { compilerOptions : { target : ts . ScriptTarget . ES2022 , module : ts . ModuleKind . CommonJS } }). outputText , context )
const state = context . exports . default . setup ( props , { expose : () => {} })
2026-10-29 14:14:56 +08:00
state . formRef . value = {
validate : async () => { await new Schema ( state . formRules ). validate ( state . form ); return true },
clearValidate : () => {},
}
2026-10-10 02:19:39 +08:00
return { state , props , route , auth , pushes , storage , dispose : () => dispose . forEach ( callback => callback ()) }
}
2026-10-29 14:14:56 +08:00
test ( 'procurement required fields validate individually and blank submissions never call the API' , async () => {
const calls = []
const harness = viewHarness ({ createPage : true , api : { createProcurement : async payload => { calls . push ( payload ); return { order : { orderNo : 'PO-VALID' } } } } })
try {
const fields = [ 'procurementDirection' , 'productName' , 'purchaseQuantity' , 'budgetUnitPrice' , 'currency' , 'contactName' , 'contactPhone' , 'receivingAddress' ]
const source = readFileSync ( new URL ( './views/ProcurementView.vue' , import . meta . url ), 'utf8' )
for ( const field of fields ) {
assert . match ( source , new RegExp ( `<el-form-item[^>]*prop=" ${ field } "` ))
const schema = new Schema ({ [ field ] : harness . state . formRules [ field ] })
await assert . rejects ( schema . validate ({ [ field ] : '' }), error => Boolean ( error . fields [ field ]))
}
await assert . rejects ( new Schema ( harness . state . formRules ). validate ({ ... draft (), productName : ' ' }))
await harness . state . submit ()
assert . equal ( calls . length , 0 )
assert . equal ( harness . state . submitting . value , false )
assert . equal ( harness . state . submitError . value , '' )
Object . assign ( harness . state . form , draft (), { budgetUnitPrice : '0' })
await harness . state . submit ()
assert . equal ( calls . length , 1 )
assert . equal ( calls [ 0 ]. budgetUnitPrice , 0 )
} finally { harness . dispose () }
})
2026-10-29 11:39:01 +08:00
test ( 'create summary follows the current form, preserves cents and clears with the draft' , () => {
const harness = viewHarness ({ createPage : true })
try {
assert . equal ( harness . state . summaryQuantity . value , '1' )
assert . equal ( harness . state . summaryUnitPrice . value , '—' )
assert . equal ( harness . state . budgetTotal . value , null )
assert . equal ( harness . state . summaryCurrencyName . value , '美元' )
Object . assign ( harness . state . form , draft ())
assert . equal ( harness . state . summaryQuantity . value , '3' )
assert . equal ( harness . state . summaryUnitPrice . value , '0.29' )
assert . equal ( harness . state . budgetTotal . value , '0.87' )
harness . state . form . currency = 'CNY'
assert . equal ( harness . state . summaryCurrencyName . value , '人民币' )
assert . equal ( presentation . money ( harness . state . budgetTotal . value , harness . state . form . currency ), 'CNY 0.87' )
harness . state . form . budgetUnitPrice = '0'
assert . equal ( harness . state . summaryUnitPrice . value , '0.00' )
assert . equal ( harness . state . budgetTotal . value , '0.00' )
harness . state . resetForm ()
assert . equal ( harness . state . summaryQuantity . value , '1' )
assert . equal ( harness . state . summaryUnitPrice . value , '—' )
assert . equal ( harness . state . budgetTotal . value , null )
assert . equal ( harness . state . summaryCurrencyName . value , '美元' )
} finally { harness . dispose () }
})
test ( 'create summary never retains a stale budget when quantity or price becomes invalid' , () => {
const harness = viewHarness ({ createPage : true })
try {
Object . assign ( harness . state . form , draft ())
for ( const quantity of [ '' , '0' , '-1' , '1.5' , '9007199254740992' ]) {
harness . state . form . purchaseQuantity = quantity
assert . equal ( harness . state . summaryQuantity . value , '—' )
assert . equal ( harness . state . budgetTotal . value , null )
assert . equal ( harness . state . form . purchaseQuantity , quantity )
}
harness . state . form . purchaseQuantity = '3'
assert . equal ( harness . state . budgetTotal . value , '0.87' )
for ( const price of [ '' , '-1' , '1.001' , 'invalid' ]) {
harness . state . form . budgetUnitPrice = price
assert . equal ( harness . state . summaryUnitPrice . value , '—' )
assert . equal ( harness . state . budgetTotal . value , null )
assert . equal ( harness . state . form . budgetUnitPrice , price )
}
harness . state . form . budgetUnitPrice = '0.29'
assert . equal ( harness . state . budgetTotal . value , '0.87' )
} finally { harness . dispose () }
})
2026-10-10 02:19:39 +08:00
test ( 'detail has one in-flight request and an older response cannot overwrite another order' , async () => {
const calls = [], pending = new Map ()
const harness = viewHarness ({ detailPage : true , api : { getProcurement : orderNo => { calls . push ( orderNo ); return new Promise ( resolve => pending . set ( orderNo , resolve )) } } })
try {
assert . deepEqual ( calls , [ 'PO-1' ])
await harness . state . loadDetail (); assert . equal ( calls . length , 1 )
harness . route . params . orderNo = 'PO-2' ; await flush (); assert . deepEqual ( calls , [ 'PO-1' , 'PO-2' ])
pending . get ( 'PO-1' )({ order : { orderNo : 'PO-1' } }); await flush (); assert . equal ( harness . state . detail . value , null )
pending . get ( 'PO-2' )({ order : { orderNo : 'PO-2' } }); await flush (); assert . equal ( harness . state . detail . value . order . orderNo , 'PO-2' )
assert . equal ( harness . state . loading . value , false )
} finally { harness . dispose () }
})
2026-10-22 23:06:02 +08:00
test ( 'timeout releases the form and another click submits the edited current values' , async () => {
const calls = []
const harness = viewHarness ({ createPage : true , api : { createProcurement : async payload => { calls . push ( normalized ( payload )); throw new ApiError ( 'timeout' , 'TIMEOUT' ) } } })
2026-10-10 02:19:39 +08:00
try {
2026-10-22 23:06:02 +08:00
Object . assign ( harness . state . form , draft ()); await harness . state . submit ()
assert . equal ( harness . state . submitting . value , false ); assert . equal ( harness . storage . size , 0 )
harness . state . form . productName = 'changed' ; await harness . state . submit ()
assert . equal ( calls [ 1 ]. productName , 'changed' ); assert . notEqual ( calls [ 0 ]. idempotencyKey , calls [ 1 ]. idempotencyKey )
assert . equal ( harness . state . submitting . value , false )
} finally { harness . dispose () }
2026-10-10 02:19:39 +08:00
})
test ( 'submission double-click sends once and definitive validation rejection permits correction' , async () => {
let reject , calls = 0
const harness = viewHarness ({ createPage : true , api : { createProcurement : () => { calls ++ ; return new Promise (( _ , failure ) => { reject = failure }) } } })
try {
2026-10-29 14:14:56 +08:00
Object . assign ( harness . state . form , draft ()); const first = harness . state . submit (); await harness . state . submit (); await flush (); assert . equal ( calls , 1 )
2026-10-10 02:19:39 +08:00
reject ( new ApiError ( 'invalid' , 'A0422' )); await first
2026-10-22 23:06:02 +08:00
assert . equal ( harness . state . submitting . value , false ); assert . equal ( harness . storage . size , 0 )
2026-10-10 02:19:39 +08:00
} finally { harness . dispose () }
})
test ( 'pagination batches one request and switching account clears old rows before the new response' , async () => {
const calls = []; let resolveSecond
const harness = viewHarness ({ api : { listProcurements : async params => {
calls . push ( normalized ( params )); if ( calls . length < 3 ) return { records : [{ orderNo : 'PO-A' }], total : 100 }
return new Promise ( resolve => { resolveSecond = resolve })
} } })
try {
await flush (); assert . equal ( calls . length , 1 )
harness . state . query . page = 2 ; harness . state . query . size = 50 ; await flush (); assert . equal ( calls . length , 2 )
harness . auth . token = 'identity-B' ; await vue . nextTick (); assert . equal ( harness . state . rows . value . length , 0 ); assert . equal ( harness . state . total . value , 0 )
resolveSecond ({ records : [{ orderNo : 'PO-B' }], total : 1 }); await flush (); assert . equal ( harness . state . rows . value [ 0 ]. orderNo , 'PO-B' )
assert . ok ( calls . every ( params => ! ( 'customerId' in params ) && ! ( 'tenantId' in params )))
} finally { harness . dispose () }
})
2026-10-22 23:06:02 +08:00
test ( 'consecutive timeout and permission failures never persist or freeze a submission' , async () => {
let attempt = 0
const harness = viewHarness ({ createPage : true , api : { createProcurement : async () => { throw new ApiError ( 'failed' , ++ attempt === 1 ? 'TIMEOUT' : 'A0403' ) } } })
2026-10-10 02:19:39 +08:00
try {
2026-10-22 23:06:02 +08:00
Object . assign ( harness . state . form , draft ()); await harness . state . submit (); await harness . state . submit ()
assert . equal ( attempt , 2 ); assert . equal ( harness . state . submitting . value , false ); assert . equal ( harness . storage . size , 0 )
harness . state . form . productName = 'editable' ; assert . equal ( harness . state . form . productName , 'editable' )
2026-10-10 02:19:39 +08:00
} finally { harness . dispose () }
})
2026-10-20 11:54:44 +08:00
test ( 'purchase category is optional while its length remains bounded' , () => {
const payload = presentation . buildProcurementCreate ({ ... draft (), purchaseCategory : ' ' }, 'Company' , 'key' )
assert . equal ( payload . purchaseCategory , '' )
assert . throws (() => presentation . buildProcurementCreate ({ ... draft (), purchaseCategory : 'x' . repeat ( 129 ) }, 'Company' , 'key' ), /品类/ )
})
2026-10-26 13:43:20 +08:00
test ( 'domestic purchase creates an export order and displays the actual origin and destination regions' , () => {
const payload = presentation . buildProcurementCreate ({ ... draft (), procurementDirection : 'DOMESTIC_TO_OVERSEAS' , receivingAddress : 'US, CA, Los Angeles, test destination' }, 'Authenticated Company' , 'export-key' )
assert . equal ( payload . procurementDirection , 'DOMESTIC_TO_OVERSEAS' )
assert . equal ( payload . receivingAddress , 'US, CA, Los Angeles, test destination' )
2026-10-27 17:39:28 +08:00
assert . equal ( presentation . statusLabel ( 'RECEIVING' , payload . procurementDirection ), '收货中' )
assert . equal ( presentation . statusLabel ( 'DELIVERING' , payload . procurementDirection ), '派送中' )
assert . equal ( presentation . statusLabel ( 'WAIT_CUSTOMER_CONFIRM' , payload . procurementDirection ), '待客户确认' )
assert . equal ( presentation . statusLabel ( 'COMPLETED' ), '签收完成' )
2026-10-26 13:43:20 +08:00
assert . throws (() => presentation . buildProcurementCreate ({ ... draft (), procurementDirection : 'INVALID' }, 'Company' , 'key' ), /采购方向/ )
const source = readFileSync ( new URL ( './views/ProcurementView.vue' , import . meta . url ), 'utf8' )
assert . match ( source , /v-model="form.procurementDirection"/ )
assert . match ( source , /regionText\('最终国内收件资料', form.procurementDirection\)/ )
})
2026-10-27 17:39:28 +08:00
test ( 'customer confirms with current workflow version and double-click only sends once' , async () => {
let resolve , calls = []
const harness = viewHarness ({ detailPage : true , api : { confirmProcurement : ( orderNo , payload ) => {
calls . push ({ orderNo , ... payload }); return new Promise ( done => { resolve = done })
} } })
try {
await flush ()
const first = harness . state . confirmReceived (); await flush (); await harness . state . confirmReceived ()
assert . equal ( calls . length , 1 ); assert . equal ( calls [ 0 ]. version , 1 ); assert . equal ( calls [ 0 ]. orderNo , 'PO-1' )
assert . ok ( calls [ 0 ]. idempotencyKey )
resolve ({ orderNo : 'PO-1' , version : 2 , status : 'CONFIRMED' }); await first
assert . equal ( harness . state . workflow . value . status , 'CONFIRMED' ); assert . equal ( harness . state . canConfirm . value , false )
} finally { harness . dispose () }
})
test ( 'failed customer confirmation preserves receipt context for recovery' , async () => {
const harness = viewHarness ({ detailPage : true , api : { confirmProcurement : async () => { throw new ApiError ( '版本已更新,请刷新' , 'A0409' ) } } })
try {
await flush (); await harness . state . confirmReceived ()
assert . equal ( harness . state . workflow . value . status , 'WAIT_CUSTOMER_CONFIRM' )
assert . equal ( harness . state . confirming . value , false ); assert . equal ( harness . state . confirmError . value , '版本已更新,请刷新' )
} finally { harness . dispose () }
})
test ( 'unknown create and confirmation results reuse idempotency keys for unchanged retries' , async () => {
const creates = [], confirms = []
const create = viewHarness ({ createPage : true , api : { createProcurement : async payload => { creates . push ( payload ); throw new ApiError ( 'timeout' , 'TIMEOUT' ) } } })
const confirm = viewHarness ({ detailPage : true , api : { confirmProcurement : async ( _ , payload ) => { confirms . push ( payload ); throw new ApiError ( 'timeout' , 'TIMEOUT' ) } } })
try {
Object . assign ( create . state . form , draft ()); await create . state . submit (); await create . state . submit ()
assert . equal ( creates [ 0 ]. idempotencyKey , creates [ 1 ]. idempotencyKey )
await flush (); await confirm . state . confirmReceived (); await confirm . state . confirmReceived ()
assert . equal ( confirms [ 0 ]. idempotencyKey , confirms [ 1 ]. idempotencyKey )
} finally { create . dispose (); confirm . dispose () }
})
test ( 'bill-settled manual fees show review results without counting a balance debit' , () => {
const bill = determination => ({ ... fee ( 'OTHER' , 'NOT_REQUIRED' , '100' ), determination })
assert . equal ( presentation . feeStatus ( bill ( 'PENDING' )), '账单结算 · 待审核' )
assert . equal ( presentation . feeStatus ( bill ( 'APPROVED' )), '账单结算 · 已确认' )
assert . equal ( presentation . feeStatus ( bill ( 'REJECTED' )), '账单结算 · 已拒绝' )
assert . equal ( presentation . feeTone ( bill ( 'REJECTED' )), 'danger' )
assert . equal ( presentation . feeTotals ([ bill ( 'PENDING' ), bill ( 'APPROVED' ), bill ( 'REJECTED' )]). paid , '0.00' )
})
2026-10-29 13:37:28 +08:00
test ( 'insufficient balance and credit blocks procurement creation and preserves the draft' , async () => {
let creates = 0
const harness = viewHarness ({ createPage : true , api : { requireOrderBudget : async () => { throw new Error ( '余额及授信不足' ) }, createProcurement : async () => { creates ++ } } })
try {
Object . assign ( harness . state . form , draft ()); await harness . state . submit ()
assert . equal ( creates , 0 ); assert . equal ( harness . state . submitError . value , '余额及授信不足' )
assert . equal ( harness . state . form . productName , draft (). productName )
} finally { harness . dispose () }
})
test ( 'customer rejection requires a reason and carries current order version' , async () => {
const calls = []
const harness = viewHarness ({ detailPage : true , api : { rejectProcurement : async ( orderNo , command ) => { calls . push ({ orderNo , ... command }); return { orderNo , status : 'RECEIVING' , version : 2 , rejectionReason : command . reason } } } })
try {
await flush (); await harness . state . rejectReceived ()
assert . equal ( calls . length , 1 ); assert . equal ( calls [ 0 ]. orderNo , 'PO-1' ); assert . equal ( calls [ 0 ]. version , 1 )
assert . equal ( calls [ 0 ]. reason , '照片不清晰' ); assert . equal ( harness . state . workflow . value . status , 'RECEIVING' )
} finally { harness . dispose () }
const blank = viewHarness ({ detailPage : true , api : { rejectionReason : ' ' , rejectProcurement : async () => { throw new Error ( '不得发送空原因' ) } } })
try { await flush (); await blank . state . rejectReceived (); assert . match ( blank . state . confirmError . value , /请填写驳回原因/ ) }
finally { blank . dispose () }
})