This commit is contained in:
279
scripts/tests/octoberBatchOne.test.mjs
Normal file
279
scripts/tests/octoberBatchOne.test.mjs
Normal file
@@ -0,0 +1,279 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { resolve } from 'node:path'
|
||||
import { createRequire } from 'node:module'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const esbuild = createRequire(require.resolve('vite'))('esbuild')
|
||||
|
||||
async function loadModule(entry, stubService = false) {
|
||||
const result = await esbuild.build({
|
||||
entryPoints: [entry],
|
||||
bundle: true,
|
||||
write: false,
|
||||
format: 'cjs',
|
||||
platform: 'node',
|
||||
alias: { '@': resolve('src') },
|
||||
plugins: stubService
|
||||
? [
|
||||
{
|
||||
name: 'stub-base-service',
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /BaseService$/ }, () => ({
|
||||
path: 'base-service',
|
||||
namespace: 'stub'
|
||||
}))
|
||||
build.onLoad({ filter: /.*/, namespace: 'stub' }, () => ({
|
||||
contents: `
|
||||
export class BaseService {
|
||||
static calls = [];
|
||||
static put(url, data, config) { this.calls.push({url, data, config}); return Promise.resolve({code:0, data:{}}); }
|
||||
static update(url, data) { return this.put(url, data); }
|
||||
static post(url, data, config) { this.calls.push({url, data, config}); return Promise.resolve({code:0, data:{}}); }
|
||||
}`
|
||||
}))
|
||||
}
|
||||
}
|
||||
]
|
||||
: []
|
||||
})
|
||||
const module = { exports: {} }
|
||||
new Function('require', 'module', 'exports', result.outputFiles[0].text)(
|
||||
require,
|
||||
module,
|
||||
module.exports
|
||||
)
|
||||
return module.exports
|
||||
}
|
||||
|
||||
test('new login password accepts only 8-12 ASCII letters/digits with all three classes', async () => {
|
||||
const { getNewLoginPasswordError, createNewLoginPasswordRule } = await loadModule(
|
||||
'src/utils/validation/loginPassword.ts'
|
||||
)
|
||||
for (const password of ['Abc12345', 'Abc123456789'])
|
||||
assert.equal(getNewLoginPasswordError(password), undefined)
|
||||
for (const password of [
|
||||
'Abc1234',
|
||||
'Abc123456789X',
|
||||
'abcd1234',
|
||||
'ABCD1234',
|
||||
'AbcDefGh',
|
||||
'Abc1234!',
|
||||
'Abc1234_',
|
||||
'Abc1234 ',
|
||||
' Abc12345',
|
||||
'Abc1234\n',
|
||||
'Abc1234中',
|
||||
'Abc123😀',
|
||||
'Abc12345'
|
||||
]) {
|
||||
assert.ok(getNewLoginPasswordError(password), `must reject ${JSON.stringify(password)}`)
|
||||
}
|
||||
const rule = createNewLoginPasswordRule()
|
||||
for (const value of ['', undefined, null])
|
||||
rule.validator({}, value, (error) => assert.equal(error, undefined))
|
||||
rule.validator({}, 'Abc1234!', (error) => assert.ok(error instanceof Error))
|
||||
})
|
||||
|
||||
test('account password requests use the right field and omit unchanged optional password', async () => {
|
||||
const { AccountService } = await loadModule('src/api/modules/account.ts', true)
|
||||
AccountService.calls = []
|
||||
await AccountService.updateAccountPassword(12, 'Abc12345')
|
||||
assert.deepEqual(AccountService.calls[0].data, { new_password: 'Abc12345' })
|
||||
await AccountService.updateAccount(12, { username: 'operator', password: '' })
|
||||
assert.deepEqual(AccountService.calls[1].data, { username: 'operator' })
|
||||
await assert.rejects(AccountService.updateAccount(12, { password: 'Abc1234!' }))
|
||||
await assert.rejects(AccountService.updateAccountPassword(12, 'abcd1234'))
|
||||
assert.equal(AccountService.calls.length, 2)
|
||||
})
|
||||
|
||||
test('writeoff terminal failure blocks recovery but allows resubmission; unknown result stays recoverable', async () => {
|
||||
const module = await loadModule(
|
||||
'src/views/finance/employee-collection/employeeCollectionDisplay.ts'
|
||||
)
|
||||
assert.equal(module.canRecoverApplicationApproval({ status: 4, recoverable: true }), false)
|
||||
assert.equal(module.canResubmitApplication({ status: 4 }), true)
|
||||
assert.equal(
|
||||
module.canRecoverApplicationApproval({
|
||||
status: 0,
|
||||
approval_submission_status: 4,
|
||||
recoverable: true
|
||||
}),
|
||||
true
|
||||
)
|
||||
assert.equal(module.canRecoverApplicationApproval({ status: 0, recoverable: false }), false)
|
||||
assert.equal(module.getApplicationStatusTagType(4), 'danger')
|
||||
for (const [value, label] of [
|
||||
[0, '待提交'],
|
||||
[1, '提交中'],
|
||||
[2, '已提交'],
|
||||
[3, '提交失败'],
|
||||
[4, '结果未知']
|
||||
]) {
|
||||
assert.equal(module.getApplicationSubmissionLabel({ approval_submission_status: value }), label)
|
||||
}
|
||||
})
|
||||
|
||||
test('refund submission failure uses resubmit, not recovery, without changing status 6 anomaly handling', async () => {
|
||||
const module = await loadModule('src/config/constants/refund.ts')
|
||||
assert.equal(module.canRecoverRefundApproval({ status: 7, approval_recoverable: true }), false)
|
||||
assert.equal(module.canResubmitRefund({ status: 7 }), true)
|
||||
assert.equal(module.canResubmitRefund({ status: 3 }), true)
|
||||
assert.equal(
|
||||
module.canRecoverRefundApproval({
|
||||
status: 1,
|
||||
submission_status: 4,
|
||||
approval_recoverable: true
|
||||
}),
|
||||
true
|
||||
)
|
||||
assert.equal(module.canResubmitRefund({ status: 6, anomaly_flag: 1 }), false)
|
||||
assert.equal(module.canResubmitRefund({ status: 6, anomaly_flag: 0 }), true)
|
||||
assert.equal(module.RefundStatusNameMap[7], '审批提交失败')
|
||||
})
|
||||
|
||||
test('admin agent registration does not recover terminal failures and provides status fallback', async () => {
|
||||
const module = await loadModule('src/types/api/agentDistributionRegistration.ts')
|
||||
assert.equal(module.canRecoverAgentRegistration({ status: 3, recoverable: true }), false)
|
||||
assert.equal(module.canRecoverAgentRegistration({ status: 0, recoverable: true }), true)
|
||||
assert.equal(module.getAgentRegistrationStatusName({ status: 3 }), '审批提交失败')
|
||||
})
|
||||
|
||||
test('offline recharge failure keeps read actions and starts a new order only with existing role/permission', async () => {
|
||||
const { buildAgentRechargeActions } = await loadModule(
|
||||
'src/views/finance/agent-recharge/agentRechargeActions.ts'
|
||||
)
|
||||
const row = { id: 8, status: 7, payment_method: 'offline', payment_voucher_key: ['voucher'] }
|
||||
let restarted = false
|
||||
const options = {
|
||||
hasAuth: () => true,
|
||||
canCreateOffline: true,
|
||||
onRestart: () => {
|
||||
restarted = true
|
||||
},
|
||||
onViewPaymentVoucher() {},
|
||||
onConfirmPayment() {},
|
||||
onReject() {},
|
||||
onTriggerApproval() {}
|
||||
}
|
||||
const actions = buildAgentRechargeActions(row, options)
|
||||
assert.deepEqual(
|
||||
actions.map((action) => action.label),
|
||||
['支付凭证', '重新发起']
|
||||
)
|
||||
actions[1].handler()
|
||||
assert.equal(restarted, true)
|
||||
assert.deepEqual(
|
||||
buildAgentRechargeActions(row, { ...options, canCreateOffline: false }).map(
|
||||
(action) => action.label
|
||||
),
|
||||
['支付凭证']
|
||||
)
|
||||
assert.deepEqual(buildAgentRechargeActions(row, { ...options, hasAuth: () => false }), [])
|
||||
})
|
||||
|
||||
test('withdrawal failure is independently typed from commission clawback and supports resubmit', async () => {
|
||||
const module = await loadModule('src/config/constants/commission.ts')
|
||||
assert.equal(module.WithdrawalStatusMap[5].label, '审批提交失败')
|
||||
assert.equal(module.canResubmitWithdrawal(5), true)
|
||||
assert.equal(module.canResubmitWithdrawal(3), true)
|
||||
assert.equal(module.canResubmitWithdrawal(1), false)
|
||||
assert.equal(module.getCommissionStatusLabel(5), '回溯')
|
||||
assert.equal(
|
||||
module.WITHDRAWAL_QUALIFICATION_STATUS_OPTIONS.find((item) => item.value === 4).label,
|
||||
'审批提交失败'
|
||||
)
|
||||
})
|
||||
|
||||
test('order payment record status is independent from order status and channels preserve unknown codes', async () => {
|
||||
const module = await loadModule('src/utils/business/orderPayment.ts')
|
||||
assert.equal(module.getOrderPaymentRecordStatusName(0), '待支付')
|
||||
assert.equal(module.getOrderPaymentRecordStatusName(1), '已支付')
|
||||
assert.equal(module.getOrderPaymentRecordStatusName(2), '已失败')
|
||||
assert.equal(module.getOrderPaymentRecordStatusName(3), '已退款')
|
||||
assert.equal(module.getOrderPaymentRecordTagType(2), 'danger')
|
||||
assert.equal(module.getOrderPaymentChannelName('wechat'), '微信直连 v3')
|
||||
assert.equal(module.getOrderPaymentChannelName('wechat_v2'), '微信直连 v2')
|
||||
assert.equal(module.getOrderPaymentChannelName('fuiou'), '富友')
|
||||
assert.equal(module.getOrderPaymentChannelName(''), '-')
|
||||
assert.equal(module.getOrderPaymentChannelName('future-channel'), 'future-channel')
|
||||
})
|
||||
|
||||
test('exchange phone conflicts support HTTP error and business response without inventing a missing number', async () => {
|
||||
const module = await loadModule('src/utils/business/exchangeError.ts')
|
||||
const failure = { code: 1202, data: { phone: '13800000000' } }
|
||||
assert.match(module.getExchangePhoneConflictMessage(failure), /13800000000/)
|
||||
assert.equal(
|
||||
module.getExchangePhoneConflictMessage({ response: { data: failure } }),
|
||||
module.getExchangePhoneConflictMessage(failure)
|
||||
)
|
||||
assert.match(module.getExchangePhoneConflictMessage({ code: 1202 }), /请先到/)
|
||||
assert.doesNotMatch(module.getExchangePhoneConflictMessage({ code: 1202 }), /undefined|null/)
|
||||
assert.equal(module.getExchangePhoneConflictMessage({ code: 1206 }), undefined)
|
||||
assert.equal(
|
||||
module.getExchangeBusinessErrorMessage({ response: { data: { message: '迁移失败' } } }),
|
||||
'迁移失败'
|
||||
)
|
||||
const { ExchangeService } = await loadModule('src/api/modules/exchange.ts', true)
|
||||
ExchangeService.calls = []
|
||||
const config = { requestOptions: { errorMessageMode: 'none' } }
|
||||
await ExchangeService.completeExchange(9, config)
|
||||
assert.deepEqual(ExchangeService.calls[0], {
|
||||
url: '/api/admin/exchanges/9/complete',
|
||||
data: {},
|
||||
config
|
||||
})
|
||||
})
|
||||
|
||||
test('password validation errors accept backend message as well as msg', async () => {
|
||||
const { normalizeApiError } = await loadModule('src/utils/business/apiError.ts')
|
||||
for (const field of ['msg', 'message']) {
|
||||
assert.equal(
|
||||
normalizeApiError({
|
||||
isAxiosError: true,
|
||||
response: { status: 400, data: { [field]: '新密码不合法' } }
|
||||
}).message,
|
||||
'新密码不合法'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
test('traffic snapshots preserve zero, missing values and MB precision', async () => {
|
||||
const { formatTrafficAlertMb } = await loadModule('src/utils/business/trafficAlert.ts')
|
||||
assert.equal(formatTrafficAlertMb(0), '0.00')
|
||||
assert.equal(formatTrafficAlertMb(1234.567), '1234.57')
|
||||
for (const value of [undefined, null, NaN]) assert.equal(formatTrafficAlertMb(value), '-')
|
||||
})
|
||||
|
||||
test('CSV download has the new exact four-column header and preserves phone identifiers as text', async () => {
|
||||
const { SHOP_BUSINESS_OWNER_CSV_TEMPLATE } = await loadModule(
|
||||
'src/utils/business/shopBusinessOwnerImport.ts'
|
||||
)
|
||||
assert.equal(SHOP_BUSINESS_OWNER_CSV_TEMPLATE.charCodeAt(0), 0xfeff)
|
||||
const [header, instruction] = SHOP_BUSINESS_OWNER_CSV_TEMPLATE.slice(1).trimEnd().split('\r\n')
|
||||
assert.deepEqual(header.split(','), ['店铺编码', '操作类型', '平台登录账号', '备注'])
|
||||
assert.equal(instruction.split(',').length, 4)
|
||||
assert.match(instruction, /手机号/)
|
||||
assert.match(instruction, /清空留空/)
|
||||
assert.doesNotMatch(SHOP_BUSINESS_OWNER_CSV_TEMPLATE, /业务员登录账号/)
|
||||
})
|
||||
|
||||
test('affected view contracts preserve raw payment history, package quota and device template', async () => {
|
||||
const order = await readFile('src/views/order-management/order-list/detail.vue', 'utf8')
|
||||
assert.match(order, /detailData\.payments \|\| \[\]/)
|
||||
assert.doesNotMatch(order, /payments\.(filter|sort)/)
|
||||
const rules = await readFile('src/views/package-management/traffic-alert-rules/index.vue', 'utf8')
|
||||
assert.match(rules, /item\.real_data_mb/)
|
||||
assert.doesNotMatch(rules, /row\.real_data_mb|prop: 'real_data_mb'/)
|
||||
const devices = await readFile(
|
||||
'src/views/asset-management/task-management/device-task/index.vue',
|
||||
'utf8'
|
||||
)
|
||||
assert.ok(
|
||||
/独立的 IMEI\s+列/.test(devices),
|
||||
'import instructions must require the standalone IMEI column'
|
||||
)
|
||||
assert.match(devices, /@\/template\/设备导入模板\.xlsx/)
|
||||
assert.match(devices, /failed_items/)
|
||||
})
|
||||
173
scripts/tests/octoberBrowserFixtures.mjs
Normal file
173
scripts/tests/octoberBrowserFixtures.mjs
Normal file
@@ -0,0 +1,173 @@
|
||||
const time = '2026-10-13T10:00:00+08:00'
|
||||
const order = {
|
||||
id: 1,
|
||||
order_no: 'MOCK-ORDER-001',
|
||||
order_type: 'single_card',
|
||||
buyer_type: 'personal',
|
||||
payment_status: 2,
|
||||
payment_status_text: '已支付',
|
||||
payment_method: 'wechat',
|
||||
total_amount: 1000,
|
||||
actual_paid_amount: 1000,
|
||||
third_party_trade_no: 'MOCK-TRADE-001',
|
||||
payment_channel: 'wechat',
|
||||
payment_merchant_name: '模拟收款商户',
|
||||
commission_status: 2,
|
||||
commission_result: 2,
|
||||
created_at: time,
|
||||
updated_at: time,
|
||||
paid_at: time,
|
||||
items: [],
|
||||
payments: [
|
||||
{
|
||||
payment_no: 'MOCK-PAY-002',
|
||||
payment_method: 'wechat',
|
||||
payment_channel: 'wechat',
|
||||
payment_merchant_name: '模拟收款商户',
|
||||
third_party_trade_no: 'MOCK-TRADE-001',
|
||||
amount: 1000,
|
||||
status: 1,
|
||||
created_at: time,
|
||||
paid_at: time,
|
||||
expire_at: null
|
||||
},
|
||||
{
|
||||
payment_no: 'MOCK-PAY-001',
|
||||
payment_method: 'alipay',
|
||||
payment_channel: 'fuiou',
|
||||
payment_merchant_name: '模拟收款商户',
|
||||
third_party_trade_no: '',
|
||||
amount: 1000,
|
||||
status: 2,
|
||||
created_at: time,
|
||||
paid_at: null,
|
||||
expire_at: null
|
||||
}
|
||||
]
|
||||
}
|
||||
const application = {
|
||||
id: 1,
|
||||
status: 4,
|
||||
status_name: '审批提交失败',
|
||||
recoverable: true,
|
||||
approval_submission_status: 3,
|
||||
paid_amount: 1000,
|
||||
created_at: time
|
||||
}
|
||||
const registration = {
|
||||
id: 1,
|
||||
status: 3,
|
||||
recoverable: true,
|
||||
phone: '13800000000',
|
||||
username: 'mock-user',
|
||||
shop_name: '模拟店铺',
|
||||
created_at: time
|
||||
}
|
||||
const rule = {
|
||||
id: 1,
|
||||
package_id: 1,
|
||||
package_name: '模拟套餐',
|
||||
threshold_percent: 80,
|
||||
enabled: true,
|
||||
enabled_name: '启用',
|
||||
remark: '',
|
||||
updated_at: time
|
||||
}
|
||||
const traffic = {
|
||||
id: 1,
|
||||
package_name: '模拟套餐',
|
||||
threshold_percent: 80,
|
||||
threshold_mb: 819.2,
|
||||
used_mb: 0,
|
||||
limit_mb: 1024,
|
||||
triggered_at: time,
|
||||
notification_status: 1,
|
||||
asset_type: 'iot_card',
|
||||
asset_identifier: 'MOCK-ASSET'
|
||||
}
|
||||
const exchange = {
|
||||
id: 1,
|
||||
exchange_no: 'MOCK-EXCHANGE-001',
|
||||
status: 3,
|
||||
status_name: '已发货待确认',
|
||||
status_text: '已发货待确认',
|
||||
old_asset_type: 'iot_card',
|
||||
old_asset_identifier: 'MOCK-OLD-ASSET',
|
||||
new_asset_identifier: 'MOCK-NEW-ASSET',
|
||||
flow_type: 'shipping',
|
||||
flow_type_name: '物流换货',
|
||||
migration_status: 'pending',
|
||||
created_at: time,
|
||||
updated_at: time
|
||||
}
|
||||
|
||||
export const mockApiPlugin = {
|
||||
name: 'october-local-mock-api',
|
||||
configureServer(server) {
|
||||
server.middlewares.use((req, res, next) => {
|
||||
const path = new URL(req.url, 'http://127.0.0.1').pathname
|
||||
if (!path.startsWith('/api/')) return next()
|
||||
if (path === '/api/auth/login' || path === '/api/auth/me') {
|
||||
const user = {
|
||||
id: 1,
|
||||
user_type: 1,
|
||||
username: '模拟管理员',
|
||||
phone: '13800000000',
|
||||
roles: ['R_SUPER']
|
||||
}
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8')
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
code: 0,
|
||||
msg: 'mock',
|
||||
data: {
|
||||
access_token: 'local-mock-only',
|
||||
refresh_token: '',
|
||||
expires_in: 3600,
|
||||
user,
|
||||
permissions: ['*:*:*'],
|
||||
buttons: [],
|
||||
menus: [{ url: '/asset-management', name: '资产管理' }]
|
||||
}
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
if (req.method !== 'GET') {
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8')
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
code: 1202,
|
||||
msg: '模拟请求:未调用真实后台',
|
||||
data: { phone: '13800000000' }
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
const page = (items) => ({
|
||||
items,
|
||||
list: items,
|
||||
total: items.length,
|
||||
page: 1,
|
||||
size: 20,
|
||||
page_size: 20
|
||||
})
|
||||
let data = page([])
|
||||
if (path === '/api/admin/orders/1') data = order
|
||||
if (path === '/api/admin/exchanges') data = page([exchange])
|
||||
if (path === '/api/admin/exchanges/1') data = exchange
|
||||
if (path === '/api/admin/orders') data = page([order])
|
||||
if (path === '/api/admin/employee-collection-applications/1')
|
||||
data = { application, allocations: [], attempts: [] }
|
||||
if (path === '/api/admin/employee-collection-applications') data = page([application])
|
||||
if (path === '/api/admin/agent-distribution-registrations/1') data = registration
|
||||
if (path === '/api/admin/agent-distribution-registrations') data = page([registration])
|
||||
if (path === '/api/admin/package-traffic-alert-rules') data = page([rule])
|
||||
if (path === '/api/admin/package-traffic-alert-rules/1') data = rule
|
||||
if (path === '/api/admin/package-traffic-alerts') data = page([traffic])
|
||||
if (path === '/api/admin/package-traffic-alerts/1') data = traffic
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8')
|
||||
res.end(JSON.stringify({ code: 0, msg: 'mock', data }))
|
||||
})
|
||||
}
|
||||
}
|
||||
14
scripts/tests/register-esbuild.mjs
Normal file
14
scripts/tests/register-esbuild.mjs
Normal file
@@ -0,0 +1,14 @@
|
||||
import { createRequire, register } from 'node:module'
|
||||
import { isMainThread } from 'node:worker_threads'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
|
||||
// pnpm 的严格依赖布局下,旧测试从根目录无法直接导入 Vite 的 esbuild 依赖。
|
||||
const require = createRequire(import.meta.url)
|
||||
const esbuildUrl = pathToFileURL(createRequire(require.resolve('vite')).resolve('esbuild')).href
|
||||
if (isMainThread) register(import.meta.url, { parentURL: import.meta.url })
|
||||
|
||||
export function resolve(specifier, context, nextResolve) {
|
||||
return specifier === 'esbuild'
|
||||
? { url: esbuildUrl, shortCircuit: true }
|
||||
: nextResolve(specifier, context)
|
||||
}
|
||||
71
scripts/tests/verifyBuild.mjs
Normal file
71
scripts/tests/verifyBuild.mjs
Normal file
@@ -0,0 +1,71 @@
|
||||
import { mkdtemp, readFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { build, createServer, loadConfigFromFile } from 'vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
|
||||
// 与生产配置相同的构建,关闭声明文件生成,避免覆盖工作区已有的用户修改。
|
||||
const protectedPaths = [
|
||||
'src/types/components.d.ts',
|
||||
'src/types/auto-imports.d.ts',
|
||||
'.auto-import.json'
|
||||
]
|
||||
const snapshots = await Promise.all(protectedPaths.map((path) => readFile(path)))
|
||||
const serve = process.argv.includes('--serve')
|
||||
const loaded = await loadConfigFromFile({
|
||||
command: serve ? 'serve' : 'build',
|
||||
mode: serve ? 'development' : 'production'
|
||||
})
|
||||
const outputDir = await mkdtemp(join(tmpdir(), 'one-pipe-october-build-'))
|
||||
const plugins = loaded.config.plugins
|
||||
.flat(Infinity)
|
||||
.filter(
|
||||
(plugin) => plugin && !['unplugin-vue-components', 'unplugin-auto-import'].includes(plugin.name)
|
||||
)
|
||||
plugins.push(
|
||||
Components({
|
||||
deep: true,
|
||||
extensions: ['vue'],
|
||||
dirs: ['src/components'],
|
||||
resolvers: [ElementPlusResolver()],
|
||||
dts: false
|
||||
}),
|
||||
AutoImport({
|
||||
imports: ['vue', 'vue-router', '@vueuse/core', 'pinia'],
|
||||
resolvers: [ElementPlusResolver()],
|
||||
dts: false,
|
||||
eslintrc: { enabled: false }
|
||||
})
|
||||
)
|
||||
if (serve) {
|
||||
const { mockApiPlugin } = await import('./octoberBrowserFixtures.mjs')
|
||||
const server = await createServer({
|
||||
...loaded.config,
|
||||
configFile: false,
|
||||
mode: 'development',
|
||||
plugins: [mockApiPlugin, ...plugins],
|
||||
server: { host: '127.0.0.1', port: 5190, strictPort: true, proxy: {} }
|
||||
})
|
||||
await server.listen()
|
||||
console.log(
|
||||
'Local mock preview: http://127.0.0.1:5190 (all /api requests intercepted; no real backend writes)'
|
||||
)
|
||||
} else {
|
||||
await build({
|
||||
...loaded.config,
|
||||
configFile: false,
|
||||
plugins,
|
||||
logLevel: 'warn',
|
||||
build: { ...loaded.config.build, outDir: outputDir, emptyOutDir: false }
|
||||
})
|
||||
}
|
||||
for (let i = 0; i < protectedPaths.length; i++) {
|
||||
if (!snapshots[i].equals(await readFile(protectedPaths[i])))
|
||||
throw new Error(`Build modified protected file: ${protectedPaths[i]}`)
|
||||
}
|
||||
if (!serve)
|
||||
console.log(
|
||||
`Production build passed. Generated declaration files preserved. Output: ${outputDir}`
|
||||
)
|
||||
Reference in New Issue
Block a user