import test from 'node:test' import assert from 'node:assert/strict' import { readSource } from './helpers.ts' import { formatSkipPriceText, skipPriceDisplayRows, skipPriceRowEntries, type SkipPriceItem, } from '../src/pages/asin/skip-price-model.ts' /** 对齐 admin.js:4839-4890 buildSkipPriceAsinEntries/renderSkipPriceAsinRows:纵向 rowspan + 最低价两位小数。 */ function item(id: number, overrides: Partial = {}): SkipPriceItem { return { id, groupId: 1, groupName: 'A组', shopName: `店铺${id}`, asinDe: '', minimumPriceDe: null, asinUk: '', minimumPriceUk: null, asinFr: '', minimumPriceFr: null, asinIt: '', minimumPriceIt: null, asinEs: '', minimumPriceEs: null, ...overrides, } } test('align_skip_price_row_entries_collects_asin_or_price_rows', () => { const entries = skipPriceRowEntries(item(1, { asinDe: 'B0DE', minimumPriceFr: 12.5 })) assert.deepEqual(entries, [ { country: 'DE', asin: 'B0DE', minimumPrice: '' }, { country: 'FR', asin: '', minimumPrice: '12.50' }, ]) }) test('align_skip_price_row_entries_empty_shop_yields_single_dash_row', () => { assert.deepEqual(skipPriceRowEntries(item(2)), [{ country: null, asin: '', minimumPrice: '' }]) }) test('align_skip_price_price_text_two_decimals', () => { // 对齐 formatSkipPriceMinimumPrice:数字→toFixed(2),空→'',非法→原串。 assert.equal(formatSkipPriceText(3), '3.00') assert.equal(formatSkipPriceText(12.5), '12.50') assert.equal(formatSkipPriceText(null), '') assert.equal(formatSkipPriceText(undefined), '') assert.equal(formatSkipPriceText('x' as unknown as number), 'x') }) test('align_skip_price_display_rows_merge_and_numbering', () => { const rows = skipPriceDisplayRows([item(1, { asinDe: 'B0DE', asinUk: 'B0UK' }), item(2)], 1) assert.equal(rows.length, 3) const [r0, r1, r2] = rows assert.equal(r0.rowNo, 1) assert.equal(r0.rowspan, 2) assert.equal(r0.isFirst, true) assert.equal(r0.asin, 'B0DE') assert.equal(r1.isFirst, false) assert.equal(r1.country, 'UK') assert.equal(r2.rowNo, 2) assert.equal(r2.country, null) assert.equal(new Set(rows.map((r) => r.key)).size, rows.length) }) test('align_skip_price_display_rows_empty_input', () => { assert.deepEqual(skipPriceDisplayRows([], 1), []) }) test('align_skip_price_create_api_wiring', () => { const api = readSource('src/pages/asin/skip-price-api.ts') assert.match(api, /createSkipPriceAsin/, '含新增适配函数') assert.match(api, /skip-price-asins/, '端点正确') assert.match(api, /minimumPriceMappings/, '载荷含逐国最低价映射') }) test('align_skip_price_page_layout_wiring', () => { const page = readSource('src/pages/asin/SkipPricePage.vue') assert.match(page, /新增 ASIN/, '顶栏补新增 ASIN 按钮') assert.match(page, /:rowspan="row\.rowspan"/, '表格用原生 rowspan 合并(像素复刻旧版)') assert.match(page, /CopyText/, 'ASIN 单元格点击复制') assert.doesNotMatch(page, /更新时间/, '去掉更新时间列(参考无此列)') assert.match(page, /skipPriceDisplayRows/, '行展开走纯模型') assert.match(page, /请完整填写分组、店铺名、国家和 ASIN/, '新增弹窗校验文案对齐') assert.match(page, /最低价格式不正确/, '最低价格式校验对齐') assert.match(page, /请先选择分组/, '店铺下拉未选分组时占位对齐') }) test('align_skip_price_filter_price_range_same_row', () => { // 验收反馈:筛选条件「最低价」的 ≥/≤ 输入并排同一排(price-range-row),不换行拆开。 const page = readSource('src/pages/asin/SkipPricePage.vue') assert.match(page, /price-range-row/, '最低价范围筛选并排容器存在') assert.match(page, /placeholder="最低价 ≥"/, '下限占位文案(旧版)') assert.match(page, /placeholder="最高价 ≤"/, '上限占位文案(旧版)') assert.ok(!page.includes('label="最低价 ≥"'), '不再有独立的最低价 ≥ 筛选项') }) test('align_skip_price_action_delete_button_wiring', () => { // 操作列新增整条删除:确认弹窗 + DELETE /skip-price-asins/{id} + 删空页回退。 const page = readSource('src/pages/asin/SkipPricePage.vue') assert.match(page, /btn-danger/, '删除按钮使用危险样式') assert.match(page, /@click="removeRow/, '按钮绑定整条删除') assert.match(page, /ElMessageBox\.confirm/, '删除前二次确认') assert.match(page, /确定删除「/, '确认文案含店铺名') assert.match(page, /deleteSkipPriceAsin\(item\.id\)/, '调用整条删除 API') assert.match(page, /page\.value -= 1/, '删空当前页后回退一页') const api = readSource('src/pages/asin/skip-price-detail-api.ts') assert.match(api, /deleteSkipPriceAsin/, '导出整条删除函数') assert.match(api, /SKIP_PRICE_DETAIL_ENDPOINT}\/\$\{id\}/, '删除端点指向整条记录') })