| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- import assert from 'node:assert/strict'
- import { readFileSync } from 'node:fs'
- import { createRequire } from 'node:module'
- import test from 'node:test'
- // Reuse the monorepo's Vue runtime to exercise the page's actual watchers.
- const require = createRequire(new URL('../../web-admin/package.json', import.meta.url))
- const { ref, reactive, computed, watch, nextTick, effectScope } = require('vue')
- const { parse, compileTemplate } = require('@vue/compiler-sfc')
- const source = readFileSync(new URL('../pages/pl/vehicle-recharge-apply/form.vue', import.meta.url), 'utf8')
- const { descriptor } = parse(source)
- const script = descriptor.scriptSetup.content.replace(/^import .+ from .+$/gm, '')
- const cardNo = '0012345678901234567'
- const flush = async () => { await nextTick(); await Promise.resolve(); await nextTick() }
- function createPage(t, { lookup = async () => ({ cardNo }), detail } = {}) {
- const hooks = {}
- const calls = []
- const scope = effectScope()
- const context = {
- ref, reactive, computed, watch,
- onLoad: (fn) => { hooks.load = fn },
- onUnload: (fn) => { hooks.unload = fn },
- PAGE_PATHS: {}, resolveMinioUrl: (url) => url,
- vehicleRechargeApplyApi: {
- lookupFuelCard: (plate) => { calls.push(plate); return lookup(plate) },
- get: async () => detail
- },
- fetchVehicleOptions: async () => [],
- chooseAndUploadImages: async () => [], previewImages: () => {},
- useUserStore: () => ({ userInfo: { id: 'driver-1' } }),
- getSafeAreaStyle: () => ({}), uni: { showToast: () => {} },
- setTimeout, clearTimeout
- }
- const page = scope.run(() => new Function(...Object.keys(context), `${script}\nreturn {
- form, readonly, imageUrls, buildPayload, onTypeChange, confirmVehiclePick,
- display: typeof fuelCardDisplay === 'undefined' ? undefined : fuelCardDisplay
- }`)(...Object.values(context)))
- t.after(() => { hooks.unload?.(); scope.stop() })
- return { ...page, hooks, calls }
- }
- test('fuel selection distinguishes empty plate, loading and exact string card number', async (t) => {
- let resolve
- const page = createPage(t, { lookup: () => new Promise((done) => { resolve = done }) })
- page.onTypeChange({ detail: { value: 1 } })
- await flush()
- assert.equal(page.display?.value, '请先选择车牌')
- assert.equal(page.calls.length, 0)
- page.confirmVehiclePick({ licensePlate: '鄂A12345' })
- await flush()
- assert.equal(page.display.value, '查询中…')
- resolve({ cardNo })
- await flush()
- assert.equal(page.display.value, cardNo)
- assert.deepEqual(page.calls, ['鄂A12345'])
- })
- test('unbound and failed queries have different visible results', async (t) => {
- const page = createPage(t, { lookup: async (plate) => {
- if (plate === '鄂A失败') throw new Error('Network unavailable')
- return { cardNo: null }
- } })
- page.form.rechargeType = 'fuel'
- page.form.licensePlate = '鄂A未绑'
- await flush()
- assert.equal(page.display?.value, '未绑定油卡')
- page.form.licensePlate = '鄂A失败'
- await flush()
- assert.equal(page.display.value, '油卡查询失败')
- })
- test('later plate wins over stale successful and failed responses', async (t) => {
- const pending = []
- const page = createPage(t, { lookup: () => new Promise((resolve, reject) => pending.push({ resolve, reject })) })
- page.form.rechargeType = 'fuel'
- for (const plate of ['鄂A旧卡', '鄂A失败', '鄂A新卡']) {
- page.form.licensePlate = plate
- await flush()
- }
- assert.equal(pending.length, 3)
- pending[2].resolve({ cardNo })
- await flush()
- pending[0].resolve({ cardNo: '9999999999999999999' })
- pending[1].reject(new Error('stale failure'))
- await flush()
- assert.equal(page.display?.value, cardNo)
- })
- test('malformed responses cannot be mistaken for an unbound card or a rounded number', async (t) => {
- const page = createPage(t, { lookup: async (plate) =>
- plate === '鄂A缺字段' ? {} : { cardNo: 1234567890123456789 }
- })
- page.form.rechargeType = 'fuel'
- for (const plate of ['鄂A缺字段', '鄂A数字']) {
- page.form.licensePlate = plate
- await flush()
- assert.equal(page.display.value, '油卡查询失败')
- }
- })
- test('clearing the plate resets the card and invalidates its pending response', async (t) => {
- let resolve
- const page = createPage(t, { lookup: () => new Promise((done) => { resolve = done }) })
- page.form.rechargeType = 'fuel'
- page.form.licensePlate = '鄂A12345'
- await flush()
- page.form.licensePlate = ''
- await flush()
- resolve({ cardNo })
- await flush()
- assert.equal(page.display.value, '请先选择车牌')
- })
- test('ETC and shanxia do not query; switching away invalidates an in-flight query', async (t) => {
- let resolve
- const page = createPage(t, { lookup: () => new Promise((done) => { resolve = done }) })
- page.form.licensePlate = '鄂A12345'
- await flush()
- page.onTypeChange({ detail: { value: 2 } })
- await flush()
- assert.equal(page.form.rechargeType, 'shanxia')
- assert.equal(page.calls.length, 0)
- page.onTypeChange({ detail: { value: 1 } })
- await flush()
- assert.equal(page.calls.length, 1)
- page.onTypeChange({ detail: { value: 0 } })
- await flush()
- resolve({ cardNo })
- await flush()
- assert.equal(page.display?.value, '')
- })
- for (const status of ['draft', 'approved']) {
- test(`${status} detail load queries the current bound card without changing submission fields`, async (t) => {
- const page = createPage(t, { detail: {
- id: 'apply-1', applicantId: 'driver-1', licensePlate: '鄂A12345', rechargeType: 'fuel',
- status, applyDate: '2026-10-25', odometerKm: 100, remark: '保留备注', attachmentUrls: 'proof.jpg'
- } })
- await page.hooks.load({ id: 'apply-1' })
- await flush()
- assert.equal(page.display?.value, cardNo)
- assert.equal(page.readonly.value, status !== 'draft')
- assert.deepEqual(page.buildPayload(), {
- licensePlate: '鄂A12345', rechargeType: 'fuel', applyDate: '2026-10-25', odometerKm: 100,
- attachmentUrls: 'proof.jpg', remark: '保留备注', applicantId: 'driver-1'
- })
- })
- }
- test('page unload stops lookup updates and new requests', async (t) => {
- let resolve
- const page = createPage(t, { lookup: () => new Promise((done) => { resolve = done }) })
- page.form.rechargeType = 'fuel'
- page.form.licensePlate = '鄂A12345'
- await flush()
- assert.equal(typeof page.hooks.unload, 'function')
- page.hooks.unload()
- const before = page.display.value
- resolve({ cardNo })
- await flush()
- assert.equal(page.display.value, before)
- page.form.licensePlate = '鄂A54321'
- await flush()
- assert.equal(page.calls.length, 1)
- })
- test('template provides a readonly fuel-only field and compiles', () => {
- assert.match(descriptor.template.content, /v-if="form.rechargeType === 'fuel'"[\s\S]*?绑定油卡卡号[\s\S]*?:value="fuelCardDisplay"[\s\S]*?disabled/)
- const compiled = compileTemplate({ source: descriptor.template.content, filename: 'form.vue', id: 'fuel-card-test' })
- assert.deepEqual(compiled.errors, [])
- })
- test('lookup uses the authenticated db client and limits the request to own vehicles', async () => {
- let request
- const dbSource = readFileSync(new URL('../utils/dbRequest.js', import.meta.url), 'utf8')
- .replace(/^import .+ from .+$/gm, '').replace(/^export default db$/m, '')
- .replace(/^export /gm, '')
- const db = new Function('uni', 'TOKEN_KEY', 'REFRESH_TOKEN_KEY', 'USER_INFO_KEY', 'resolveApiBase', `${dbSource}\nreturn db`)(
- { getStorageSync: () => 'driver-token', request: (options) => {
- request = options
- options.success({ statusCode: 200, data: { code: 200, data: { licensePlate: '鄂A12345', cardNo } } })
- } }, 'token', 'refresh', 'user', () => 'https://example.test/api'
- )
- const apiSource = readFileSync(new URL('../api/pl.js', import.meta.url), 'utf8')
- .replace(/^import .+ from .+$/gm, '').replace(/^export (?:default .*|\{.*\})$/gm, '')
- .replace(/^export /gm, '')
- const api = new Function('db', 'fetchVehicleOptions', `${apiSource}\nreturn vehicleRechargeApplyApi`)(db, () => {})
- assert.equal(typeof api.lookupFuelCard, 'function')
- const data = await api.lookupFuelCard('鄂A12345')
- const url = new URL(request.url)
- assert.equal(url.pathname, '/api/transport/vehicle-recharge-apply/fuel-card')
- assert.equal(url.searchParams.get('licensePlate'), '鄂A12345')
- assert.equal(url.searchParams.get('mineVehicles'), 'true')
- assert.equal(request.method, 'GET')
- assert.equal(request.header.Authorization, 'Bearer driver-token')
- assert.deepEqual(data, { licensePlate: '鄂A12345', cardNo })
- })
|