Files
one-pipe-system/scripts/tests/pollingScheduler.test.mjs
luo f2614d8efb
All checks were successful
构建并部署前端到测试环境 / deploy (push) Successful in 54s
fix: some
2026-10-28 15:18:44 +08:00

748 lines
26 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync, readdirSync, existsSync } from 'node:fs'
import ts from 'typescript'
import * as vue from 'vue'
import { compileScript, compileTemplate, parse } from '@vue/compiler-sfc'
const source = (path) => readFileSync(path, 'utf8')
function evaluate(text, imports = {}, globals = {}) {
const compiled = ts.transpileModule(text, {
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS }
}).outputText
const module = { exports: {} }
new Function('require', 'module', 'exports', ...Object.keys(globals), compiled)(
(name) => {
assert.ok(imports[name], `Unexpected dependency: ${name}`)
return imports[name]
},
module,
module.exports,
...Object.values(globals)
)
return module.exports
}
const gateway = evaluate(source('src/utils/business/gateway.ts'), {
'./format': evaluate(source('src/utils/business/format.ts'))
})
const helpers = evaluate(source('src/utils/business/pollingScheduler.ts'), { './gateway': gateway })
const constants = evaluate(source('src/config/constants/pollingScheduler.ts'))
const configHelpers = evaluate(source('src/utils/business/pollingConfig.ts'), {
'./gateway': gateway,
'@/config/constants/pollingConfig': evaluate(source('src/config/constants/pollingConfig.ts'))
})
const ok = (data) => ({ code: 0, data })
const flush = async () => {
await new Promise((done) => setImmediate(done))
await vue.nextTick()
}
const batchPath = 'src/components/business/polling/PollingBatchDialog.vue'
test('gateway metrics flatten into row arrays without old raw-data attribute overriding the table', () => {
const path = 'src/components/business/gateway/GatewayOperationTable.vue'
const { descriptor } = parse(source(path))
const definition = evaluate(compileScript(descriptor, { id: 'metric-table' }).content, {
vue,
'@/components/business/polling/SchedulerTable.vue': { default: {} },
'@/utils/business/gateway': gateway
}).default
const props = {
values: [
{
carrier_id: 8,
carrier_name: 'carrier',
operations: [
{ operation: 'stop', operation_name: 'stop', success_rate: 0.8, p95_duration_ms: -1 }
]
}
]
}
const state = definition.setup(props, { expose() {}, emit() {} })
assert.equal(state.rows.value.length, 1)
assert.equal(state.format(state.rows.value[0], 'success_rate'), '80.00%')
assert.equal(state.format(state.rows.value[0], 'p95_duration_ms'), '超过最大分桶')
assert.equal(definition.inheritAttrs, false)
assert.doesNotMatch(
source('src/views/polling-management/gateway-stats/index.vue'),
/<GatewayOperationTable[^>]*:data=/s
)
})
test('every one of the 46 documented added HTTP operations has a service method', async () => {
const calls = []
class BaseService {}
for (const [method, verb] of [
['get', 'GET'],
['post', 'POST'],
['put', 'PUT'],
['delete', 'DELETE']
])
BaseService[method] = (url) => {
calls.push(`${verb} ${url}`)
return ok({})
}
for (const [file, name] of [
['gateway', 'GatewayService'],
['pollingConfig', 'PollingConfigService'],
['pollingScheduler', 'PollingSchedulerService']
]) {
const service = evaluate(source(`src/api/modules/${file}.ts`), {
'../BaseService': { BaseService }
})[name]
for (const key of Object.getOwnPropertyNames(service))
if (typeof service[key] === 'function' && !['length', 'name', 'prototype'].includes(key))
await service[key](1, {}, '')
}
const normalize = (value) =>
value
.replace('/api/admin', '')
.replace(/\{[^}]+\}/g, '{}')
.replace(/\/1(?=\/|$)/g, '/{}')
// 原始后端文档在本仓库被忽略,固定路径快照保证全新检出也可运行;本地有原文时额外校对。
const snapshot = JSON.parse(source('scripts/tests/fixtures/pollingSchedulerEndpoints.json'))
const document = 'docs/2026-10-26-轮询按运营商账户限流-前端接口变更说明.md'
if (existsSync(document))
assert.deepEqual(
[...source(document).matchAll(/^#### `(GET|POST|PUT|DELETE) ([^`]+)`/gm)].map(
(match) => `${match[1]} ${match[2]}`
),
snapshot
)
const expected = snapshot.map(normalize)
assert.equal(expected.length, 46)
const actual = new Set(calls.map(normalize))
for (const endpoint of expected) assert.ok(actual.has(endpoint), endpoint)
})
test('scheduler request rejects stale responses after reload, permission change, scope switch and unmount', async () => {
const user = vue.reactive({ info: { id: 1, user_type: 2 }, isLogin: true }),
permission = vue.ref(true),
stops = [],
deactivations = [],
activations = []
const runtime = {
...vue,
onBeforeUnmount: (fn) => stops.push(fn),
onDeactivated: (fn) => deactivations.push(fn),
onActivated: (fn) => activations.push(fn)
}
const { useSchedulerRequest } = evaluate(source('src/composables/useSchedulerRequest.ts'), {
vue: runtime,
'@/store/modules/user': { useUserStore: () => user },
'@/utils/business/gateway': gateway,
'@/utils/business/pollingScheduler': helpers
})
const pending = []
const scope = vue.effectScope()
const state = scope.run(() =>
useSchedulerRequest(
() => new Promise((resolve) => pending.push(resolve)),
() => permission.value
)
)
const a = state.load(),
b = state.load()
pending[1]('new')
await b
pending[0]('old')
await a
assert.equal(state.data.value, 'new')
const c = state.load()
user.info.id = 2
pending[2]('old account')
await c
assert.equal(state.data.value, null)
permission.value = false
await state.load()
assert.equal(pending.length, 3)
permission.value = true
const d = state.load()
stops.forEach((fn) => fn())
pending[3]('unmounted')
await d
assert.equal(state.data.value, null)
scope.stop()
})
function mount(
path,
{
userType = 2,
permissions = ['test'],
props = {},
services = {},
storage = {},
confirm = async () => 'confirm'
} = {}
) {
const user = vue.reactive({ info: { id: 1, user_type: userType }, isLogin: true })
const events = [],
calls = [],
beforeUnmount = [],
deactivated = []
const api = new Proxy(services, {
get(target, key) {
return async (...args) => {
calls.push({ name: key, args })
return key in target
? target[key](...args)
: ok({ enable: ['Enable impact from server'], disable: ['Disable impact from server'] })
}
}
})
const runtime = {
...vue,
onMounted: () => {},
onBeforeUnmount: (fn) => beforeUnmount.push(fn),
onDeactivated: (fn) => deactivated.push(fn),
onActivated: () => {}
}
const { descriptor } = parse(source(path))
const script = compileScript(descriptor, { id: 'scheduler-test' }).content
const store = { useUserStore: () => user }
const schedulerRequest = evaluate(source('src/composables/useSchedulerRequest.ts'), {
vue: runtime,
'@/store/modules/user': store,
'@/utils/business/gateway': gateway,
'@/utils/business/pollingScheduler': helpers
})
const imports = {
vue: runtime,
'vue-router': {
useRoute: () => ({ params: { id: '1', carrier_id: '8' }, fullPath: '/local/detail/1' }),
useRouter: () => ({ back() {} })
},
'element-plus': { ElMessageBox: { confirm }, ElMessage: { success() {} } },
'@/api/modules/pollingScheduler': { PollingSchedulerService: api },
'@/api/modules/storage': { StorageService: storage },
'@/store/modules/user': store,
'@/composables/useAuth': {
useAuth: () => ({ hasAuth: (permission) => permissions.includes(permission) })
},
'@/utils/business/gateway': gateway,
'@/utils/business/pollingScheduler': helpers,
'@/config/constants/pollingScheduler': constants,
'@/config/constants/gateway': evaluate(source('src/config/constants/gateway.ts')),
'@/composables/useSchedulerRequest': schedulerRequest,
'@/composables/useGateway': {
useGatewayCarriers: () => ({ options: vue.ref([]), search() {}, loading: vue.ref(false) })
},
'@/components/business/polling/SchedulerTable.vue': { default: {} },
'@/components/business/gateway/StopResumeRecords.vue': { default: {} },
'./PollingStateDialog.vue': { default: {} },
'@vueuse/core': { useWindowSize: () => ({ width: vue.ref(1200) }) }
}
const scope = vue.effectScope()
const state = scope.run(() =>
evaluate(script, imports, runtime).default.setup(props, {
expose() {},
emit: (...args) => events.push(args)
})
)
return {
state,
calls,
user,
events,
dispose: () => {
beforeUnmount.forEach((fn) => fn())
scope.stop()
},
deactivate: () => deactivated.forEach((fn) => fn())
}
}
const dryRunPath = 'src/views/polling-management/stop-resume-dry-run/index.vue'
const schedulerDetailPath = 'src/components/business/polling/SchedulerDetail.vue'
const dryRunResult = {
items: [{ card_id: 42, iccid: '89860426012345678901' }],
total: 1,
page: 3,
size: 20,
summary: [{ carrier_id: 8, count: 1 }],
totals: { total: 1, will_stop: 1, will_resume: 0, no_action: 0 }
}
test('dry-run reset clears filters, old summary/details and errors without starting an unfiltered scan', async () => {
const h = mount(dryRunPath, {
permissions: [constants.SCHEDULER_PERMISSIONS.dryRun.page],
services: { dryRun: () => ok(dryRunResult) }
})
Object.assign(h.state.filters, {
carrier_id: 8,
stop_reason: 'manual',
result: 'will_stop',
assume_control_on: [8],
assume_control_off: [9]
})
h.state.page.value = 3
await h.state.load()
assert.equal(h.state.data.value.totals.total, 1)
assert.equal(h.state.data.value.summary.length, 1)
assert.equal(h.state.data.value.items.length, 1)
h.state.error.value = 'old export error'
h.state.reset()
assert.deepEqual(
{ ...h.state.filters },
{
carrier_id: undefined,
stop_reason: '',
result: '',
assume_control_on: [],
assume_control_off: []
}
)
assert.equal(h.state.page.value, 1)
assert.equal(h.state.data.value, null)
assert.equal(h.state.error.value, '')
assert.equal(h.state.loading.value, false)
assert.equal(h.calls.length, 1)
h.dispose()
})
test('dry-run reset invalidates pending success/error and allows a fresh explicit query', async () => {
for (const fail of [false, true]) {
let resolve, reject
const pendingResponse = new Promise((done, failed) => {
resolve = done
reject = failed
})
let requests = 0
const h = mount(dryRunPath, {
permissions: [constants.SCHEDULER_PERMISSIONS.dryRun.page],
services: { dryRun: () => (++requests === 1 ? pendingResponse : ok(dryRunResult)) }
})
h.state.filters.stop_reason = 'manual'
const pending = h.state.load()
assert.equal(h.state.loading.value, true)
h.state.reset()
if (fail) reject(new Error('stale request failed'))
else resolve(ok(dryRunResult))
await pending
assert.equal(h.state.data.value, null)
assert.equal(h.state.error.value, '')
assert.equal(h.state.loading.value, false)
assert.equal(requests, 1)
h.state.filters.carrier_id = 9
await h.state.load()
assert.equal(h.state.data.value.totals.total, 1)
assert.equal(h.calls.at(-1).args[0].carrier_id, 9)
assert.equal(h.calls.at(-1).args[0].stop_reason, undefined)
h.dispose()
}
})
for (const [kind, method] of [
['params', 'getCarrierParam'],
['alerts', 'getAlert'],
['imports', 'getImport']
])
test(`${kind} direct detail requires both page and detail permissions and retains the role boundary`, async () => {
const P = constants.SCHEDULER_PERMISSIONS[kind]
for (const permissions of [
[],
[P.page],
[P.detailPage],
[P.detail],
[P.detailPage, P.detail]
]) {
const allowed = permissions.includes(P.detailPage) && permissions.includes(P.detail)
const h = mount(schedulerDetailPath, {
props: { kind },
permissions,
services: { [method]: () => ok({ id: 1, carrier_id: 8, card_id: 0, status: 3 }) }
})
await h.state.load()
assert.equal(h.calls.length, allowed ? 1 : 0, permissions.join(','))
assert.equal(!!h.state.data.value, allowed)
h.dispose()
}
for (const userType of [1, 3, 4]) {
const allowed = kind !== 'imports' || userType === 1
const h = mount(schedulerDetailPath, {
props: { kind },
userType,
permissions: [P.detailPage, P.detail],
services: { [method]: () => ok({ id: 1, status: 3 }) }
})
await h.state.load()
assert.equal(h.calls.length, allowed ? 1 : 0)
assert.equal(!!h.state.data.value, allowed)
h.dispose()
}
})
test('revoking either detail permission clears displayed data and blocks subsequent refresh', async () => {
for (const kind of ['params', 'alerts', 'imports']) {
const P = constants.SCHEDULER_PERMISSIONS[kind]
for (const revoked of [P.detailPage, P.detail]) {
const permissions = vue.reactive([P.detailPage, P.detail])
const h = mount(schedulerDetailPath, { props: { kind }, permissions })
await h.state.load()
assert.ok(h.state.data.value)
permissions.splice(permissions.indexOf(revoked), 1)
assert.equal(h.state.data.value, null)
await h.state.load()
assert.equal(h.calls.length, 1)
h.dispose()
}
}
})
test('pending detail responses cannot appear after the detail permission is revoked', async () => {
for (const [kind, method] of [
['params', 'getCarrierParam'],
['alerts', 'getAlert'],
['imports', 'getImport']
]) {
const P = constants.SCHEDULER_PERMISSIONS[kind]
const permissions = vue.reactive([P.detailPage, P.detail])
let resolve
const response = new Promise((done) => {
resolve = done
})
const h = mount(schedulerDetailPath, {
props: { kind },
permissions,
services: { [method]: () => response }
})
const pending = h.state.load()
permissions.splice(permissions.indexOf(P.detail), 1)
resolve(ok({ id: 1, status: 3 }))
await pending
assert.equal(h.state.data.value, null)
assert.equal(h.state.loading.value, false)
assert.equal(h.calls.length, 1)
h.dispose()
}
})
test('22 scheduler methods preserve exact HTTP paths, bodies, null and false', async () => {
const calls = []
class BaseService {}
for (const method of ['get', 'put', 'post', 'delete'])
BaseService[method] = (url, data) => {
calls.push({ method, url, data })
return ok(data)
}
const { PollingSchedulerService: service } = evaluate(
source('src/api/modules/pollingScheduler.ts'),
{ '../BaseService': { BaseService } }
)
const toggle = { asset_ids: [1, 2], enable_polling: false, reason: 'test' },
input = { inflight_limit: null },
query = { page: 1, page_size: 20, status: 1 }
const specs = [
['getCarrierParams', [], 'get', '/polling-carrier-params'],
['getCarrierParam', [8], 'get', '/polling-carrier-params/8'],
['saveCarrierParam', [8, input], 'put', '/polling-carrier-params/8', input],
['resetCarrierParam', [8], 'delete', '/polling-carrier-params/8'],
['getScenes', [], 'get', '/polling-check-scenes'],
['saveScene', ['scene/a', input], 'put', '/polling-check-scenes/scene%2Fa', input],
['getAlerts', [query], 'get', '/polling-failure-alerts', query],
['getAlertSummary', [], 'get', '/polling-failure-alerts/summary'],
['getAlert', [1], 'get', '/polling-failure-alerts/1'],
[
'handleAlert',
[1, 'note'],
'put',
'/polling-failure-alerts/1/status',
{ status: 4, remark: 'note' }
],
[
'closeAlertPolling',
[[1, 2], 'note'],
'post',
'/polling-failure-alerts/close-polling',
{ alert_ids: [1, 2], reason: 'note' }
],
['getQueues', [], 'get', '/polling-stats/carrier-queues'],
['getCardState', [42], 'get', '/iot-cards/42/polling-state'],
['getImpact', [], 'get', '/polling-status/impact'],
['toggleCards', [toggle], 'post', '/iot-cards/batch-update-polling-status', toggle],
['toggleDevices', [toggle], 'post', '/devices/batch-update-polling-status', toggle],
['getPauses', [query], 'get', '/card-polling-pauses', query],
['getImports', [query], 'get', '/polling-status-imports', query],
['getImport', [3], 'get', '/polling-status-imports/3'],
['createImport', [toggle], 'post', '/polling-status-imports', toggle],
['dryRun', [query], 'get', '/stop-resume-rule/dry-run', query],
[
'exportDryRun',
[{ carrier_id: 8, format: 'csv' }],
'post',
'/stop-resume-rule/dry-run/export',
{ carrier_id: 8, format: 'csv' }
]
]
for (const [name, args, method, path, data] of specs) {
await service[name](...args)
assert.deepEqual(calls.at(-1), { method, url: `/api/admin${path}`, data })
}
assert.equal(specs.length, 22)
})
test('role boundaries and unique 1..500 ids are enforced', () => {
for (const type of [1, 2, 3, 4, 0, undefined]) {
assert.equal(helpers.isPollingPlatform(type), [1, 2].includes(type))
assert.equal(helpers.canPollingBatch(type), [1, 2, 3].includes(type))
}
for (const ids of [[], [1, 1], [0], [-1], [1.5], Array.from({ length: 501 }, (_, i) => i + 1)])
assert.throws(() => helpers.validatePollingIds(ids))
assert.doesNotThrow(() =>
helpers.validatePollingIds(Array.from({ length: 500 }, (_, i) => i + 1))
)
assert.throws(() => helpers.pollingReason(' '))
assert.equal(helpers.pollingReason(' reason '), 'reason')
})
test('special alert view only sends documented query filters; zero status is preserved', () => {
const query = {
page: 1,
page_size: 20,
carrier_id: 8,
status: 0,
view: 'stop_refused',
check_type: 'stop_resume',
error_category: 'terminal',
alert_level: 'card',
iccid: 'x'
}
assert.deepEqual(helpers.failureQuery(query), {
page: 1,
page_size: 20,
carrier_id: 8,
status: 0,
iccid: 'x',
view: 'stop_refused'
})
assert.equal(helpers.failureQuery({ ...query, view: undefined }).check_type, 'stop_resume')
assert.deepEqual(
helpers.schedulerTime(['2026-10-26T00:00:00+08:00', '2026-10-27T00:00:00+08:00']),
{ start_time: '2026-10-25T16:00:00Z', end_time: '2026-10-26T16:00:00Z' }
)
assert.throws(() => helpers.schedulerTime(['bad', 'date']))
})
test('parameter, capacity, nullable cooldown and strictly increasing delays validate', () => {
assert.doesNotThrow(() => helpers.validateCarrierParams({ inflight_limit: null }, 0))
assert.throws(() => helpers.validateCarrierParams({ inflight_limit: 11 }, 10))
assert.throws(() => helpers.validateCarrierParams({ priority_max_ratio: 0 }, 10))
for (const delays of [[30, 30], [60, 30], [0]])
assert.throws(() =>
helpers.validateScene({ check_types: ['carddata'], confirm_delays: delays, cooldown: null })
)
assert.doesNotThrow(() =>
helpers.validateScene({ check_types: ['realname'], confirm_delays: [30, 60], cooldown: 0 })
)
assert.throws(() =>
helpers.validateScene({ check_types: ['package'], confirm_delays: [], cooldown: null })
)
assert.throws(() => helpers.validateDryAssumptions([1, 2], [2]))
assert.doesNotThrow(() => helpers.validateDryAssumptions([1], [2]))
})
test('CSV header, quoted newlines, escaped quotes, empty lines, 50000 limit and string ids', () => {
const id = '89860426012345678901'
assert.equal(
helpers.validatePollingCsv(
'file.CSV',
`\uFEFFiccid,note\r\n${id},"line\nline"\r\n\r\n${id},"a""b"`
),
2
)
assert.equal(id, '89860426012345678901')
for (const [name, text] of [
['f.txt', 'h\nx'],
['f.csv', 'header'],
['f.csv', 'h\n"x'],
['f.csv', `h\n${'x\n'.repeat(50001)}`]
])
assert.throws(() => helpers.validatePollingCsv(name, text))
assert.equal(helpers.validatePollingCsv('f.csv', `h\n${'x\n'.repeat(50000)}`), 50000)
})
test('config save persists new fields, removes protection, uses clear_fields without losing zero', () => {
const draft = {
...configHelpers.newPollingDraft(),
config_name: 'x',
asset_status: 2,
package_state: 'active',
carddata_check_interval: 0,
protect_check_interval: 30
}
const body = configHelpers.pollingSaveBody(draft, true)
assert.equal(body.asset_status, 2)
assert.equal(body.package_state, 'active')
assert.equal(body.carddata_check_interval, 0)
assert.ok(body.clear_fields.includes('realname_check_interval'))
assert.equal('realname_check_interval' in body, false)
assert.equal('protect_check_interval' in body, false)
assert.equal('id' in body, false)
assert.equal('disabled' in body, false)
assert.deepEqual(
configHelpers.pollingDraftBody([{ id: 3, disabled: true, priority: -1, config_name: '' }]),
[{ id: 3, disabled: true }]
)
assert.throws(() =>
gateway.validateGatewayRule({
category: 'business',
auto_disable_polling: true,
keyword: 'a',
match_type: 'contains'
})
)
})
for (const [target, type, allowed] of [
['iot_card', 3, true],
['device', 4, false],
['alert', 3, false],
['alert', 2, true]
])
test(`batch ${target} type${type} validates identity and shows partial results`, async () => {
const result = {
success_count: 1,
skip_count: 1,
fail_count: 2,
impact: ['impact'],
items: [1, 2, 3, 4].map((status) => ({
asset_id: status,
identifier: String(status),
status,
status_name: String(status),
reason: 'reason'
}))
}
const h = mount(batchPath, {
userType: type,
props: { target, ids: [1, 2, 3, 4], permission: 'test' },
services: {
toggleCards: () => ok(result),
toggleDevices: () => ok(result),
closeAlertPolling: () => ok(result)
}
})
await h.state.loadImpact()
h.state.reason.value = ' reason '
await h.state.submit()
assert.equal(
h.calls.some((call) =>
['toggleCards', 'toggleDevices', 'closeAlertPolling'].includes(call.name)
),
allowed
)
if (allowed) {
assert.deepEqual(h.state.result.value, result)
assert.equal(h.state.resultRows.value.length, 4)
await h.state.submit()
assert.equal(h.calls.length, 2)
}
h.dispose()
})
test('batch missing impact, cancel, duplicate ids, unmount, and changed scope never write', async () => {
for (const variant of ['impact', 'cancel', 'duplicate', 'unmount', 'scope']) {
const props = {
target: 'iot_card',
ids: variant === 'duplicate' ? [1, 1] : [1],
permission: 'test'
}
let h
h = mount(batchPath, {
props,
services:
variant === 'impact'
? {
getImpact: () => {
throw Error('offline')
}
}
: {},
confirm: async () => {
if (variant === 'cancel') throw 'cancel'
if (variant === 'unmount') h.dispose()
if (variant === 'scope') h.user.info.id = 2
}
})
await h.state.loadImpact()
h.state.reason.value = 'reason'
await h.state.submit()
assert.equal(
h.calls.some((call) => call.name === 'toggleCards'),
false,
variant
)
h.dispose()
}
})
test('import validates purpose/prefix and never creates after upload failure', async () => {
for (const fail of [false, true]) {
const uploads = []
const h = mount('src/components/business/polling/PollingImportDialog.vue', {
permissions: [constants.SCHEDULER_PERMISSIONS.imports.create],
storage: {
getUploadUrl: async (data) => {
uploads.push(data)
return ok({ upload_url: 'local-upload', file_key: 'polling-status-imports/test.csv' })
},
uploadFile: async () => {
if (fail) throw Error('upload failed')
}
},
services: { createImport: () => ok({ id: 3 }) }
})
h.state.file.value = { name: 'file.csv', text: async () => 'iccid\n89860426012345678901' }
h.state.reason.value = 'reason'
await h.state.submit()
assert.equal(uploads[0].purpose, 'polling_status_import')
assert.equal(
h.calls.some((call) => call.name === 'createImport'),
!fail
)
h.dispose()
}
})
test('all new Vue templates compile, including multi-statement event handlers', () => {
const dirs = [
'src/components/business/polling',
'src/views/polling-management/carrier-params',
'src/views/polling-management/check-scenes',
'src/views/polling-management/carrier-queues',
'src/views/polling-management/failure-alerts',
'src/views/polling-management/polling-pauses',
'src/views/polling-management/polling-status-imports',
'src/views/polling-management/stop-resume-dry-run'
]
for (const dir of dirs)
for (const file of readdirSync(dir).filter((file) => file.endsWith('.vue'))) {
const path = `${dir}/${file}`,
{ descriptor, errors } = parse(source(path))
assert.deepEqual(errors, [], path)
const result = compileTemplate({
source: descriptor.template.content,
filename: path,
id: path,
compilerOptions: { expressionPlugins: ['typescript'] }
})
assert.deepEqual(result.errors, [], path)
}
})
test('protection is absent from admin forms and polling pause/alert/dry-run export scenes are registered', () => {
for (const path of [
'src/views/polling-management/config/index.vue',
'src/views/polling-management/config/components/PollingDraftFields.vue',
'src/views/asset-management/asset-information/components/BasicInfoCard.vue'
])
assert.equal(/protect_check_interval|device_protect_status/.test(source(path)), false, path)
for (const scene of ['polling_pause', 'polling_stop_refused_alert', 'stop_resume_rule_dry_run'])
assert.ok(source('src/config/constants/exportTask.ts').includes(`scene: '${scene}'`))
for (const kind of [
'carrier-params',
'check-scenes',
'failure-alerts',
'carrier-queues',
'stop-resume-dry-run',
'polling-pauses',
'polling-status-imports'
])
assert.ok(source('src/router/routes/asyncRoutes.ts').includes(`path: '${kind}'`))
assert.match(
source('src/views/polling-management/stop-resume-dry-run/index.vue'),
/assume_control_off/
)
})