test/dashboard-free-badge-render.test.js
Rama master · réplica de lectura del repositorio original.
// The FREE badge must carry the promo end date, in both languages.
//
// The chip used to say just "FREE"/"免费" and hide the end date in a tooltip.
// "Free" with no horizon is how a promotion quietly turns into a surprise bill:
// the badge is what an operator scans, so the date belongs in the badge. These
// execute the SHIPPED filterModels method (brace-sliced out of index.html), with
// the real translation files — a mirror implementation would not prove the
// shipped copy says it.
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
const HTML = readFileSync(new URL('../src/dashboard/index.html', import.meta.url), 'utf8');
const LOCALES = {
en: JSON.parse(readFileSync(new URL('../src/dashboard/i18n/en.json', import.meta.url), 'utf8')),
zh: JSON.parse(readFileSync(new URL('../src/dashboard/i18n/zh-CN.json', import.meta.url), 'utf8')),
};
/** Slice one App method out of the shipped template. */
function methodParts(name) {
const at = HTML.indexOf(`\n ${name}(`);
assert.notEqual(at, -1, `${name} must exist as a method in the dashboard App object`);
const parenOpen = HTML.indexOf('(', at);
let depthParen = 0;
let parenClose = -1;
for (let i = parenOpen; i < HTML.length; i++) {
if (HTML[i] === '(') depthParen++;
else if (HTML[i] === ')' && --depthParen === 0) { parenClose = i; break; }
}
assert.notEqual(parenClose, -1, `could not delimit the parameters of ${name}`);
const params = HTML.slice(parenOpen, parenClose + 1);
const open = HTML.indexOf('{', parenClose);
let depth = 0;
let end = -1;
for (let i = open; i < HTML.length; i++) {
if (HTML[i] === '{') depth++;
else if (HTML[i] === '}' && --depth === 0) { end = i + 1; break; }
}
assert.notEqual(end, -1, `could not delimit the body of ${name}`);
return { params, body: HTML.slice(open, end) };
}
function lookup(locale, key) {
return key.split('.').reduce((node, part) => (node && typeof node === 'object' ? node[part] : undefined), locale);
}
function translator(locale) {
return (key, vars) => {
const raw = lookup(locale, key);
assert.equal(typeof raw, 'string', `missing i18n key ${key}`);
return vars ? raw.replace(/\{\{(\w+)\}\}/g, (_, name) => String(vars[name] ?? '')) : raw;
};
}
function esc(value) {
return String(value ?? '')
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
}
/** Run the shipped filterModels against one model row and return the chip HTML. */
function renderChips(localeName, models) {
const { params, body } = methodParts('filterModels');
const container = { innerHTML: '' };
const document = {
getElementById: (id) => (id === 'model-chips-container' ? container : { value: '' }),
};
const self = {
esc,
escJsAttr: (value) => String(value ?? '').replace(/"/g, '"'),
allModels: models,
modelAccessConfig: { list: [] },
};
// eslint-disable-next-line no-new-func -- executing the shipped source is the point
const fn = new Function('I18n', 'document', 'self',
`return (function filterModels${params} ${body}).apply(self, []);`);
fn({ t: translator(LOCALES[localeName]) }, document, self);
return container.innerHTML;
}
const FREE_PROMO = [{
id: 'swe-2-medium',
name: 'swe-2-medium',
provider: 'windsurf',
reachable: true,
freeNow: true,
freeReason: 'cost_tier_free',
promoEndsAt: '2026-11-01T06:59:59.999Z',
quotaState: 'free',
connectSelector: 'swe-2-medium',
pricingStale: false,
}];
describe('FREE badge carries the promo end date', () => {
it('renders 免费(促销至 …) in zh-CN', () => {
const html = renderChips('zh', FREE_PROMO);
assert.ok(html.includes('免费(促销至 2026-11-01)'), html);
});
it('renders FREE (promo until …) in en', () => {
const html = renderChips('en', FREE_PROMO);
assert.ok(html.includes('FREE (promo until 2026-11-01)'), html);
});
it('keeps the plain FREE badge when the upstream sent no promo end', () => {
const noPromo = [{ ...FREE_PROMO[0], promoEndsAt: null }];
assert.ok(renderChips('zh', noPromo).includes('>免费<'));
assert.ok(renderChips('en', noPromo).includes('>FREE<'));
});
it('still renders QUOTA for a billable row', () => {
const paid = [{ ...FREE_PROMO[0], freeNow: false, freeReason: null, promoEndsAt: null, quotaState: 'unknown' }];
assert.ok(renderChips('zh', paid).includes('>计费<'));
assert.ok(renderChips('en', paid).includes('>QUOTA<'));
});
it('renders no price badge when freeNow is unknown (no live data)', () => {
const unknown = [{ ...FREE_PROMO[0], freeNow: null, freeReason: null, promoEndsAt: null, quotaState: null }];
const html = renderChips('zh', unknown);
// No badge span at all: the chip's tooltip (costUnknownHint) legitimately
// mentions 免费 in prose, so the assertion is about the badge, not the word.
assert.ok(!html.includes('chip-badge'), html);
});
it('surfaces staleness in the tooltip when the sync stopped', () => {
const stale = [{ ...FREE_PROMO[0], pricingStale: true }];
const html = renderChips('zh', stale);
assert.ok(html.includes('定价数据已过期'), html);
});
});