| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- import assert from 'node:assert/strict'
- import test from 'node:test'
- import { readFileSync } from 'node:fs'
- import { ref, nextTick } from 'vue'
- import ts from 'typescript'
- import { parse } from '@vue/compiler-sfc'
- import * as routeUtils from '../src/utils/mp-webview'
- import * as listState from '../src/views/m/crm/quote/listState'
- function harness(mode: 'manage' | 'approve') {
- const base = mode === 'approve' ? '/m/crm/quote/approval' : '/m/crm/quote'
- let route: any = { path: base, query: {} }
- let leave: any, unmount: any
- const hooks = new Set<any>()
- const scrolls: number[] = []
- const bindings = {
- ref,
- nextTick,
- useRoute: () => route,
- useRouter: () => ({
- afterEach(fn: any) {
- hooks.add(fn)
- return () => hooks.delete(fn)
- }
- }),
- useUserStore: () => ({ accessToken: 'token', getUserInfo: { id: 'u', tenantNo: 'T' } }),
- onBeforeRouteLeave: (fn: any) => {
- leave = fn
- },
- onBeforeUnmount: (fn: any) => {
- unmount = fn
- },
- window: { scrollY: 720, scrollTo: ({ top }: any) => scrolls.push(top) }
- }
- const source = readFileSync(
- new URL('../src/views/m/crm/useListReturnState.ts', import.meta.url),
- 'utf8'
- )
- .replace(/^import .*$/gm, '')
- .replace('export function', 'function')
- const useState = new Function(
- ...Object.keys(bindings),
- ts.transpile(source, { target: ts.ScriptTarget.ESNext }) + ';return useListReturnState'
- )(...Object.values(bindings))
- // Evaluate the real quote call-site so this catches missing predicate wiring, not only the helper.
- const ast = ts.createSourceFile(
- 'list.ts',
- parse(
- readFileSync(new URL('../src/views/m/crm/quote/MpQuoteList.vue', import.meta.url), 'utf8')
- ).descriptor.scriptSetup!.content,
- ts.ScriptTarget.Latest,
- true
- )
- const statement = ast.statements.find(
- (n) =>
- ts.isVariableStatement(n) &&
- n.declarationList.declarations.some((d) => d.name.getText(ast) === 'listReturnState')
- )!
- function create() {
- const keyword = ref(''),
- query = ref({}),
- view = ref(mode === 'approve' ? 'submitted' : 'all'),
- tabQueries = ref({})
- const pageNum = ref(3),
- expandedIds = ref(new Set(['Q1'])),
- hasMore = ref(true)
- const requests: any[] = []
- const local = {
- ...listState,
- ...routeUtils,
- props: { mode },
- keyword,
- query,
- view,
- tabQueries,
- pageNum,
- expandedIds,
- hasMore,
- useListReturnState: useState,
- requestSnapshot: () => JSON.stringify([keyword.value, query.value, view.value]),
- fetchPage: async (reset: boolean) => {
- pageNum.value = reset ? 1 : pageNum.value + 1
- requests.push({ page: pageNum.value, keyword: keyword.value, query: query.value })
- expandedIds.value = new Set()
- return true
- }
- }
- const state = new Function(
- ...Object.keys(local),
- ts.transpile(statement.getText(ast), { target: ts.ScriptTarget.ESNext }) +
- ';return listReturnState'
- )(...Object.values(local))
- return { state, keyword, query, pageNum, expandedIds, requests }
- }
- function navigate(to: any) {
- const from = route
- route = to
- for (const hook of hooks) hook(to, from)
- }
- function detail() {
- const to = { path: '/m/crm/quote/Q1', query: { from: mode } }
- leave(to)
- navigate(to)
- unmount()
- }
- return { base, create, navigate, detail, scrolls }
- }
- for (const mode of ['manage', 'approve'] as const) {
- test(`${mode}: quote/customer/quote round trip restores search, filters, pages and scroll`, async () => {
- const h = harness(mode),
- first = h.create()
- first.keyword.value = '移动验收'
- first.query.value = { finalAmountMin: '10' }
- h.detail()
- h.navigate({
- path: '/m/crm/customer/C1',
- query: { from: 'quote', returnQuoteId: 'Q1', quoteFrom: mode }
- })
- h.navigate({ path: '/m/crm/quote/Q1', query: { from: mode } })
- h.navigate({ path: h.base, query: {} })
- const back = h.create()
- await back.state.restore()
- assert.equal(back.keyword.value, '移动验收')
- assert.deepEqual(back.query.value, { finalAmountMin: '10' })
- assert.deepEqual(
- back.requests.map((r: any) => r.page),
- [1, 2, 3]
- )
- assert.deepEqual([...back.expandedIds.value], ['Q1'])
- assert.deepEqual(h.scrolls, [720])
- })
- }
- test('unrelated customer paths and forged quote return IDs discard the quote snapshot', async () => {
- for (const query of [
- {},
- { from: 'customer', returnQuoteId: 'Q1' },
- { from: 'quote', returnQuoteId: '../Q1' }
- ]) {
- const h = harness('manage'),
- first = h.create()
- first.keyword.value = 'private'
- h.detail()
- h.navigate({ path: '/m/crm/customer/C1', query })
- h.navigate({ path: '/m/crm/quote/Q1', query: {} })
- h.navigate({ path: h.base, query: {} })
- const back = h.create()
- await back.state.restore()
- assert.equal(back.keyword.value, '')
- }
- })
|