| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702 |
- import assert from 'node:assert/strict'
- import test from 'node:test'
- import { readFileSync, existsSync } from 'node:fs'
- import { parse } from '@vue/compiler-sfc'
- import ts from 'typescript'
- import * as vue from 'vue'
- import { useClientColumnFilters } from '../src/composables/useClientColumnFilters'
- import * as customerFilterDefinitions from '../src/views/crm/customer/dialogColumnFilterDefs'
- import { contactFields } from '../src/views/m/crm/customer/model'
- import { contactDetailsError, requiredFieldsError } from '../src/views/m/crm/customer/validation'
- function deferred() {
- let resolve!: (value?: any) => void
- let reject!: (error: any) => void
- const promise = new Promise<any>((a, b) => {
- resolve = a
- reject = b
- })
- return { promise, resolve, reject }
- }
- const flush = async () => {
- for (let i = 0; i < 20; i++) await Promise.resolve()
- }
- // Execute actual script-setup functions inside Vue's renderer; API responses alone are deferred.
- const helperPath = new URL('../src/utils/mp-customer-panel-identity.ts', import.meta.url)
- function evaluate(page: string, props: any, overrides: Record<string, any>, events: any[]) {
- const source = parse(
- readFileSync(new URL(`../src/views/m/crm/customer/${page}.vue`, import.meta.url), 'utf8')
- ).descriptor.scriptSetup!.content
- const ast = ts.createSourceFile('panel.ts', source, ts.ScriptTarget.Latest, true)
- const vueEntries = Object.entries(vue).filter(
- ([k]) => /^[a-zA-Z_$][\w$]*$/.test(k) && k !== 'default'
- )
- const bindings: Record<string, any> = Object.fromEntries(vueEntries)
- delete bindings.default
- for (const statement of ast.statements) {
- if (!ts.isImportDeclaration(statement)) continue
- const clause = statement.importClause
- if (clause?.name) bindings[clause.name.text] = () => undefined
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings))
- for (const item of clause.namedBindings.elements) bindings[item.name.text] = () => undefined
- }
- if (existsSync(helperPath)) {
- const helper = ts.transpile(
- readFileSync(helperPath, 'utf8').replace('export function', 'function'),
- { target: ts.ScriptTarget.ESNext }
- )
- bindings.useCustomerPanelIdentity = new Function(
- ...vueEntries.map(([k]) => k),
- `${helper};return useCustomerPanelIdentity`
- )(...vueEntries.map(([, v]) => v))
- }
- Object.assign(bindings, {
- useClientColumnFilters,
- ...customerFilterDefinitions,
- defineProps: () => props,
- defineEmits:
- () =>
- (...args: any[]) =>
- events.push(args),
- defineExpose() {},
- useRouter: () => ({ push: async () => {} }),
- useDictStore: () => ({ getOptions: () => [], getLabel: () => '', getTagType: () => '' }),
- unwrapRows: (v: any) => (Array.isArray(v) ? v : v?.data?.rows || v?.data || v?.rows || []),
- unwrapData: (v: any) => v?.data || v,
- canDeleteByCreator: () => true,
- contactFields: [{ key: 'contactName', required: true }],
- invoiceFields: [{ key: 'companyName' }],
- requiredFieldsError: () => '',
- contactDetailsError: () => '',
- percentageError: () => '',
- apiTime: (v: any) => v,
- opportunityProgress: () => ({}),
- showCrmConfirmDialog: async () => {},
- fetchContactByCustomerId: async () => [],
- fetchFollowByCustomerId: async () => [],
- getInvoiceInfoByCustomerId: async () => [],
- fetchContactDetail: async (v: any) => v,
- fetchUserOptions: async () => [],
- fetchCustomerOptions: async () => [],
- listOpportunity: async () => [],
- listContract: async () => [],
- listOperLog: async () => [],
- getOpportunity: async (id: string) => ({ id }),
- listFollowByOpportunity: async () => [],
- getStageLogList: async () => [],
- ...overrides
- })
- 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((s) => s.getText(ast)).join('\n'), {
- target: ts.ScriptTarget.ESNext
- })
- return new Function(...Object.keys(bindings), `${code};return {${names.join(',')}}`)(
- ...Object.values(bindings)
- )
- }
- function mount(page: string, overrides: Record<string, any> = {}) {
- const routeState = vue.reactive({ params: { id: 'A' }, query: { tab: 'basic' }, path: '/A' })
- const route = {
- get id() {
- return routeState.params.id
- },
- set id(id: string) {
- routeState.params.id = id
- },
- get tab() {
- return routeState.query.tab
- },
- set tab(tab: string) {
- routeState.query.tab = tab
- }
- }
- const events: any[] = []
- let api: any
- let actions: any
- const renderer = vue.createRenderer<any, any>({
- patchProp() {},
- insert() {},
- remove() {},
- createElement: () => ({}),
- createText: () => ({}),
- createComment: () => ({}),
- setText() {},
- setElementText() {},
- parentNode: () => null,
- nextSibling: () => null
- })
- const NestedActions = vue.defineComponent({
- props: ['opportunity', 'getIdentity'],
- setup(props) {
- actions = evaluate('OpportunityActions', props, overrides, events)
- return () => vue.h('div')
- }
- })
- const Child = vue.defineComponent({
- props: ['customerId', 'opportunity', 'getIdentity', 'kind'],
- setup(props) {
- api = evaluate(
- page === 'RelatedPanelActions' ? 'RelatedPanel' : page,
- props,
- overrides,
- events
- )
- return () =>
- page === 'RelatedPanelActions' && api.detail.value && api.detailOpen.value
- ? vue.h(NestedActions, { opportunity: api.detail.value, getIdentity: api.identity.key })
- : vue.h('div')
- }
- })
- const Parent = vue.defineComponent({
- setup() {
- const parent = evaluate(
- 'detail',
- {},
- { useRoute: () => routeState, useRouter: () => ({ replace() {}, push() {} }) },
- []
- )
- const getIdentity = parent.getPanelIdentity || (() => route.id)
- return () =>
- vue.h(Child, {
- customerId: route.id,
- opportunity: { id: route.id, status: 'following' },
- kind: 'opportunity',
- getIdentity
- })
- }
- })
- const app = renderer.createApp(Parent)
- app.mount({})
- return {
- get api() {
- return api
- },
- get actions() {
- return actions
- },
- route,
- events,
- unmount: () => app.unmount()
- }
- }
- const listApis: Record<string, string> = {
- ContactPanel: 'fetchContactByCustomerId',
- FollowPanel: 'fetchFollowByCustomerId',
- InvoicePanel: 'getInvoiceInfoByCustomerId',
- RelatedPanel: 'listOpportunity'
- }
- test('ContactPanel: editing visible fields preserves hidden contact values in the update payload', async () => {
- const writes: any[] = []
- const original = {
- id: 'contact-A',
- contactName: '验收联系人',
- mobile: '19900000000',
- qq: 'synthetic-qq',
- birthday: '2000-02-29',
- address: '验收合成住址',
- department: '原部门'
- }
- const h = mount('ContactPanel', {
- contactFields,
- contactDetailsError,
- requiredFieldsError,
- fetchContactDetail: async () => ({ data: original }),
- fetchUpdateContact: async (payload: any) => writes.push(payload)
- })
- try {
- await h.api.open({ id: original.id })
- assert.equal(h.api.visibleContactFields.value.some((field: any) => field.hidden), false)
- h.api.form.value.department = '已改部门'
- await h.api.save()
- assert.equal(writes.length, 1)
- assert.equal(writes[0].department, '已改部门')
- for (const key of ['qq', 'birthday', 'address']) assert.equal(writes[0][key], original[key])
- assert.equal(writes[0].id, original.id)
- assert.equal(writes[0].customerId, 'A')
- assert.equal(original.department, '原部门')
- assert.equal(h.api.editing.value, false)
- } finally {
- h.unmount()
- }
- })
- test('ContactPanel: opening another contact does not carry hidden fields from the previous draft', async () => {
- const writes: any[] = []
- const h = mount('ContactPanel', {
- contactFields,
- contactDetailsError,
- requiredFieldsError,
- fetchContactDetail: async ({ id }: { id: string }) => ({
- data: {
- id,
- contactName: id,
- mobile: '19900000000',
- ...(id === 'contact-A'
- ? { qq: 'synthetic-qq', birthday: '2000-02-29', address: '验收合成住址' }
- : { qq: null })
- }
- }),
- fetchUpdateContact: async (payload: any) => writes.push(payload)
- })
- try {
- await h.api.open({ id: 'contact-A' })
- h.api.closeEditing()
- await h.api.open({ id: 'contact-B' })
- h.api.form.value.department = '第二位联系人部门'
- await h.api.save()
- assert.equal(writes.length, 1)
- assert.equal(writes[0].id, 'contact-B')
- for (const key of ['qq', 'birthday', 'address']) assert.equal(writes[0][key], '')
- } finally {
- h.unmount()
- }
- })
- test('ContactPanel: failed update retains hidden values and retries the same edited payload', async () => {
- const writes: any[] = []
- const h = mount('ContactPanel', {
- contactFields,
- contactDetailsError,
- requiredFieldsError,
- fetchContactDetail: async () => ({
- data: {
- id: 'contact-A',
- contactName: '验收联系人',
- mobile: '19900000000',
- qq: 'synthetic-qq',
- birthday: '2000-02-29',
- address: '验收合成住址'
- }
- }),
- fetchUpdateContact: async (payload: any) => {
- writes.push(payload)
- if (writes.length === 1) throw new Error('验收模拟保存失败')
- }
- })
- try {
- await h.api.open({ id: 'contact-A' })
- h.api.form.value.department = '重试仍保留的新部门'
- await h.api.save()
- assert.equal(h.api.editing.value, true)
- assert.equal(h.api.acting.value, false)
- assert.equal(h.api.saveError.value, '验收模拟保存失败')
- assert.equal(h.api.form.value.birthday, '2000-02-29')
- assert.equal(h.api.form.value.department, '重试仍保留的新部门')
- await h.api.save()
- assert.equal(writes.length, 2)
- assert.deepEqual(writes[1], writes[0])
- assert.equal(writes[1].qq, 'synthetic-qq')
- assert.equal(writes[1].address, '验收合成住址')
- assert.equal(h.api.editing.value, false)
- assert.equal(h.api.saveError.value, '')
- } finally {
- h.unmount()
- }
- })
- test('InvoicePanel: setting default announces success only after the write succeeds', async () => {
- const write = deferred()
- const messages: string[] = []
- const h = mount('InvoicePanel', {
- request: { post: () => write.promise },
- showSuccessToast: (message: string) => messages.push(message)
- })
- try {
- await flush()
- const pending = h.api.setDefault({ id: 'invoice-A' })
- assert.deepEqual(messages, [])
- write.resolve()
- await pending
- assert.deepEqual(messages, ['设置成功'])
- assert.equal(h.api.acting.value, false)
- } finally {
- h.unmount()
- }
- })
- test('InvoicePanel: rejected default write retains its error without announcing success', async () => {
- const messages: string[] = []
- const h = mount('InvoicePanel', {
- request: { post: async () => { throw new Error('验收请求失败') } },
- showSuccessToast: (message: string) => messages.push(message)
- })
- try {
- await flush()
- await h.api.setDefault({ id: 'invoice-A' })
- assert.deepEqual(messages, [])
- assert.equal(h.api.error.value, '验收请求失败')
- assert.equal(h.api.acting.value, false)
- } finally {
- h.unmount()
- }
- })
- for (const outcome of ['switch', 'ABA', 'unmount', 'same-customer-tab']) {
- test(`InvoicePanel: default success feedback respects ${outcome}`, async () => {
- const write = deferred()
- const messages: string[] = []
- const h = mount('InvoicePanel', {
- request: { post: () => write.promise },
- showSuccessToast: (message: string) => messages.push(message)
- })
- await flush()
- const pending = h.api.setDefault({ id: 'invoice-A' })
- if (outcome === 'unmount') h.unmount()
- else if (outcome === 'same-customer-tab') h.route.tab = 'invoice'
- else {
- h.route.id = 'B'
- if (outcome === 'ABA') h.route.id = 'A'
- }
- write.resolve()
- try {
- await pending
- assert.deepEqual(messages, outcome === 'same-customer-tab' ? ['设置成功'] : [])
- } finally {
- if (outcome !== 'unmount') h.unmount()
- }
- })
- }
- for (const [page, read] of Object.entries(listApis)) {
- test(`${page}: late initial load cannot overwrite B or ABA`, async () => {
- const old = deferred()
- let calls = 0
- const h = mount(page, {
- [read]: () => (++calls === 1 ? old.promise : Promise.resolve([{ id: 'fresh' }]))
- })
- h.route.id = 'B'
- await flush()
- h.route.id = 'A'
- await flush()
- old.resolve([{ id: 'old' }])
- await flush()
- assert.deepEqual(h.api.rows.value, [{ id: 'fresh' }])
- h.unmount()
- })
- }
- const deletes: Record<string, string> = {
- ContactPanel: 'fetchDeleteContact',
- FollowPanel: 'fetchDeleteCustomerFollow',
- InvoicePanel: 'delInvoiceInfo',
- OpportunityActions: 'delOpportunity'
- }
- for (const [page, write] of Object.entries(deletes)) {
- for (const outcome of ['switch', 'ABA', 'unmount'])
- test(`${page}: confirmation resolves before same-tick ${outcome}`, async () => {
- const confirm = deferred(),
- writes: any[] = []
- const h = mount(page, {
- showCrmConfirmDialog: () => confirm.promise,
- [write]: async (v: any) => writes.push(v)
- })
- await flush()
- const pending = h.api.remove(page === 'OpportunityActions' ? undefined : { id: 'row-A' })
- confirm.resolve()
- if (outcome === 'unmount') h.unmount()
- else {
- h.route.id = 'B'
- if (outcome === 'ABA') h.route.id = 'A'
- }
- await pending
- assert.deepEqual(writes, [])
- assert.deepEqual(h.events, [])
- if (outcome !== 'unmount') h.unmount()
- })
- test(`${page}: same-ID tab query retains legitimate confirmation`, async () => {
- const confirm = deferred(),
- writes: any[] = []
- const h = mount(page, {
- showCrmConfirmDialog: () => confirm.promise,
- [write]: async (v: any) => writes.push(v)
- })
- await flush()
- const pending = h.api.remove(page === 'OpportunityActions' ? undefined : { id: 'row-A' })
- h.route.tab = 'contact'
- confirm.resolve()
- await pending
- assert.equal(writes.length, 1)
- h.unmount()
- })
- }
- test('ContactPanel: reversed detail opens retain latest row', async () => {
- const first = deferred(),
- second = deferred()
- const h = mount('ContactPanel', {
- fetchContactDetail: ({ id }: any) => (id === 'one' ? first.promise : second.promise)
- })
- const a = h.api.open({ id: 'one' }),
- b = h.api.open({ id: 'two' })
- second.resolve({ id: 'two' })
- await b
- first.resolve({ id: 'one' })
- await a
- assert.equal(h.api.form.value.id, 'two')
- h.unmount()
- })
- test('OpportunityActions: reversed customer searches retain latest query', async () => {
- const first = deferred(),
- second = deferred()
- const h = mount('OpportunityActions', {
- fetchCustomerOptions: ({ keyword }: any) => (keyword === 'one' ? first.promise : second.promise)
- })
- await h.api.open('edit')
- h.api.customerKeyword.value = 'one'
- const a = h.api.searchCustomers()
- h.api.customerKeyword.value = 'two'
- const b = h.api.searchCustomers()
- second.resolve([{ id: 'two', customerName: 'two' }])
- await b
- first.resolve([{ id: 'one', customerName: 'one' }])
- await a
- assert.equal(h.api.customers.value[0].value, 'two')
- h.unmount()
- })
- const saves: Record<string, string> = {
- ContactPanel: 'fetchCreateContact',
- FollowPanel: 'fetchCreateCustomerFollow',
- InvoicePanel: 'addInvoiceInfo',
- OpportunityActions: 'addOpportunityFollow'
- }
- for (const [page, write] of Object.entries(saves)) {
- test(`${page}: old save completion does not close, emit or clear new busy`, async () => {
- const old = deferred(),
- fresh = deferred()
- let count = 0
- const h = mount(page, { [write]: () => (++count === 1 ? old.promise : fresh.promise) })
- await flush()
- await (page === 'OpportunityActions' ? h.api.open('follow') : h.api.open())
- Object.assign(h.api.form.value, {
- contactName: 'A',
- companyName: 'A',
- followContent: 'A',
- followType: 'phone'
- })
- const a = h.api.save()
- h.route.id = 'B'
- await flush()
- await (page === 'OpportunityActions' ? h.api.open('follow') : h.api.open())
- Object.assign(h.api.form.value, {
- contactName: 'B',
- companyName: 'B',
- followContent: 'B',
- followType: 'phone'
- })
- const b = h.api.save()
- old.resolve()
- await a
- assert.equal((h.api.acting || h.api.busy).value, true)
- assert.equal(
- (h.api.editing || h.api.mode).value,
- page === 'OpportunityActions' ? 'follow' : true
- )
- assert.deepEqual(h.events, [])
- fresh.resolve()
- await b
- assert.equal((h.api.acting || h.api.busy).value, false)
- h.unmount()
- })
- }
- test('FollowPanel: close/reopen ignores old contacts error', async () => {
- const first = deferred(),
- second = deferred()
- let calls = 0
- const h = mount('FollowPanel', {
- fetchContactByCustomerId: () => (++calls === 1 ? first.promise : second.promise)
- })
- const a = h.api.open()
- h.api.closeEditing()
- const b = h.api.open()
- second.resolve([{ id: 'new' }])
- await b
- first.reject(new Error('old error'))
- await a
- assert.equal(h.api.saveError.value, '')
- assert.deepEqual(h.api.contacts.value, [{ id: 'new' }])
- h.unmount()
- })
- test('RelatedPanel: reversed opportunity opens keep fresh detail and actions', async () => {
- const first = deferred(),
- second = deferred()
- const h = mount('RelatedPanel', {
- getOpportunity: (id: string) => (id === 'one' ? first.promise : second.promise)
- })
- const a = h.api.open({ id: 'one' }),
- b = h.api.open({ id: 'two' })
- second.resolve({ id: 'two' })
- await b
- first.resolve({ id: 'one' })
- await a
- assert.equal(h.api.detail.value.id, 'two')
- h.unmount()
- })
- for (const transition of ['openB', 'close', 'closeReopenA', 'routeABA']) {
- test(`nested RelatedPanel/OpportunityActions: confirmation cannot survive ${transition}`, async () => {
- const confirm = deferred(),
- writes: any[] = []
- const h = mount('RelatedPanelActions', {
- showCrmConfirmDialog: () => confirm.promise,
- delOpportunity: async (id: string) => writes.push(id)
- })
- await flush()
- await h.api.open({ id: 'opportunity-A' })
- await flush()
- const pending = h.actions.remove()
- confirm.resolve()
- if (transition === 'openB') void h.api.open({ id: 'opportunity-B' })
- if (transition === 'close') h.api.closeDetail()
- if (transition === 'closeReopenA') {
- h.api.closeDetail()
- void h.api.open({ id: 'opportunity-A' })
- }
- if (transition === 'routeABA') {
- h.route.id = 'B'
- h.route.id = 'A'
- }
- await pending
- assert.deepEqual(writes, [])
- assert.deepEqual(h.events, [])
- h.unmount()
- })
- }
- for (const [page, read] of Object.entries(listApis)) {
- test(`${page}: old list error and finally cannot overwrite B loading`, async () => {
- const first = deferred(),
- second = deferred()
- let calls = 0
- const h = mount(page, { [read]: () => (++calls === 1 ? first.promise : second.promise) })
- h.route.id = 'B'
- await flush()
- first.reject(new Error('old error'))
- await flush()
- assert.equal(h.api.error.value, '')
- assert.equal(h.api.loading.value, true)
- second.resolve([{ id: 'B' }])
- await flush()
- assert.deepEqual(h.api.rows.value, [{ id: 'B' }])
- h.unmount()
- })
- }
- test('ContactPanel: primary confirmation identifies the selected contact before writing', async () => {
- const confirmation = deferred()
- const prompts: any[] = []
- const writes: any[] = []
- const h = mount('ContactPanel', {
- showCrmConfirmDialog: (prompt: any) => {
- prompts.push(prompt)
- return confirmation.promise
- },
- fetchSetPrimaryContact: async (value: any) => writes.push(value)
- })
- try {
- await flush()
- const pending = h.api.primary({ id: 'contact-A', contactName: '验收联系人甲' })
- assert.equal(prompts[0].message, '确认将「验收联系人甲」设置为主要联系人吗?')
- assert.deepEqual(writes, [])
- confirmation.reject('cancel')
- await pending
- assert.deepEqual(writes, [])
- assert.equal(h.api.error.value, '')
- } finally {
- confirmation.reject('cancel')
- h.unmount()
- }
- })
- for (const outcome of ['success', 'failure', 'switch', 'ABA', 'unmount']) {
- test(`ContactPanel: primary feedback respects ${outcome}`, async () => {
- const write = deferred()
- const messages: string[] = []
- const h = mount('ContactPanel', {
- fetchSetPrimaryContact: () => write.promise,
- showSuccessToast: (message: string) => messages.push(message)
- })
- await flush()
- const pending = h.api.primary({ id: 'contact-A', contactName: '验收联系人甲' })
- await flush()
- assert.deepEqual(messages, [])
- if (outcome === 'unmount') h.unmount()
- if (outcome === 'switch' || outcome === 'ABA') {
- h.route.id = 'B'
- if (outcome === 'ABA') h.route.id = 'A'
- }
- if (outcome === 'failure') write.reject(new Error('验收请求失败'))
- else write.resolve()
- try {
- await pending
- assert.deepEqual(messages, outcome === 'success' ? ['设置成功'] : [])
- if (outcome === 'failure') assert.equal(h.api.error.value, '验收请求失败')
- } finally {
- if (outcome !== 'unmount') h.unmount()
- }
- })
- }
- test('ContactPanel: stale primary confirmation cannot set a row on the next customer', async () => {
- const confirm = deferred(),
- writes: any[] = []
- const h = mount('ContactPanel', {
- showCrmConfirmDialog: () => confirm.promise,
- fetchSetPrimaryContact: async (v: any) => writes.push(v)
- })
- await flush()
- const pending = h.api.primary({ id: 'contact-A' })
- confirm.resolve()
- h.route.id = 'B'
- await pending
- assert.deepEqual(writes, [])
- h.unmount()
- })
- test('InvoicePanel: default write failure cannot replace the next customer error or busy', async () => {
- const first = deferred(),
- second = deferred()
- let calls = 0
- const h = mount('InvoicePanel', {
- request: { post: () => (++calls === 1 ? first.promise : second.promise) }
- })
- await flush()
- const a = h.api.setDefault({ id: 'invoice-A' })
- h.route.id = 'B'
- await flush()
- const b = h.api.setDefault({ id: 'invoice-B' })
- first.reject(new Error('old error'))
- await a
- assert.equal(h.api.error.value, '')
- assert.equal(h.api.acting.value, true)
- second.resolve()
- await b
- assert.equal(h.api.acting.value, false)
- h.unmount()
- })
- test('OpportunityActions: old owner options cannot replace a reopened edit session', async () => {
- const first = deferred(),
- second = deferred()
- let calls = 0
- const h = mount('OpportunityActions', {
- fetchUserOptions: () => (++calls === 1 ? first.promise : second.promise)
- })
- const a = h.api.open('edit')
- const b = h.api.open('edit')
- second.resolve([{ value: 'B', label: 'B' }])
- await b
- first.resolve([{ value: 'A', label: 'A' }])
- await a
- assert.deepEqual(h.api.users.value, [{ value: 'B', label: 'B' }])
- h.unmount()
- })
- test('OpportunityActions: delete uses the captured follow ID', async () => {
- const confirm = deferred(),
- writes: any[] = []
- const h = mount('OpportunityActions', {
- showCrmConfirmDialog: () => confirm.promise,
- delOpportunityFollow: async (v: any) => writes.push(v)
- })
- const row = { id: 'follow-A' }
- const pending = h.api.remove(row)
- row.id = 'follow-B'
- confirm.resolve()
- await pending
- assert.deepEqual(writes, ['follow-A'])
- assert.deepEqual(h.events, [['changed']])
- h.unmount()
- })
|