mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-20 09:09:35 +02:00
The toast every user sees once per release had no zh-CN entries, so a Chinese interface showed it, button included, in English. The button gets an entry and the frame a translateDynamic pattern that keeps the version, the "(+N more)" count and the highlights (those come from CHANGELOG.md and stay English). Tested through the real i18n.js in JSDOM, plus a pin on the strings settings-ui.js builds, which the pattern depends on. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
129 lines
5.2 KiB
TypeScript
129 lines
5.2 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
||
import { readFileSync } from 'node:fs';
|
||
import { resolve } from 'node:path';
|
||
import vm from 'node:vm';
|
||
import { JSDOM } from 'jsdom';
|
||
// @ts-expect-error plain .mjs helper shared with scripts/generate-whats-new.mjs
|
||
import { extractHighlights, versionSection, buildWhatsNew } from '../scripts/lib/whats-new.mjs';
|
||
|
||
const root = resolve(import.meta.dirname, '..');
|
||
|
||
const CHANGELOG = `# aicodeman
|
||
|
||
## 1.2.0
|
||
|
||
### Minor Changes
|
||
|
||
- ### Thanks
|
||
- @someone for a fix
|
||
|
||
**The tile grid: six sessions side by side.** Body text with **inline bold** later.
|
||
|
||
- **Split view (#560).** More text.
|
||
- A bullet with no bold lead.
|
||
|
||
**Behaviour change: \`Ctrl+W\` no longer closes a session.** Details.
|
||
|
||
## 1.1.0
|
||
|
||
- **Old feature.** Not for 1.2.0.
|
||
`;
|
||
|
||
describe('whats-new extraction', () => {
|
||
it('limits itself to the requested version section', () => {
|
||
const section = versionSection(CHANGELOG, '1.2.0');
|
||
expect(section).toContain('tile grid');
|
||
expect(section).not.toContain('Old feature');
|
||
expect(versionSection(CHANGELOG, '9.9.9')).toBe('');
|
||
});
|
||
|
||
it('takes bold lead-ins only, cleaned, and skips the Thanks block', () => {
|
||
expect(extractHighlights(versionSection(CHANGELOG, '1.2.0'))).toEqual([
|
||
'The tile grid: six sessions side by side',
|
||
'Split view (#560)',
|
||
'Behaviour change: `Ctrl+W` no longer closes a session',
|
||
]);
|
||
});
|
||
|
||
it('reads a lead-in behind the commit prefix changesets writes for a committed changeset', () => {
|
||
const section = '- d18deed: **Each release announces itself (#618).** Text.\n- 13e652e: A fix with no lead-in.';
|
||
expect(extractHighlights(section)).toEqual(['Each release announces itself (#618)']);
|
||
});
|
||
|
||
it('skips group labels the release notes put in bold (Fixes, Fixes applied while landing)', () => {
|
||
const section = [
|
||
' **Tab layouts (#538).** Text.',
|
||
' **Fixes.**',
|
||
' - **Shells reload cleanly (#601).** Text.',
|
||
' **Fixes applied while landing.** Remote tabs keep their scrollback.',
|
||
].join('\n');
|
||
expect(extractHighlights(section)).toEqual(['Tab layouts (#538)', 'Shells reload cleanly (#601)']);
|
||
});
|
||
|
||
it('yields no highlights for a release written without bold lead-ins', () => {
|
||
const changelog =
|
||
'## 1.32.1\n\n### Patch Changes\n\n- 13e652e: Terminal copy: no gutter on the clipboard (#469).\n';
|
||
expect(buildWhatsNew(changelog, '1.32.1')).toEqual({ version: '1.32.1', highlights: [] });
|
||
});
|
||
|
||
it('caps the list', () => {
|
||
const many = Array.from({ length: 20 }, (_, i) => `**Feature number ${i}.** text`).join('\n\n');
|
||
expect(extractHighlights(many)).toHaveLength(8);
|
||
});
|
||
|
||
it('builds the payload the client reads', () => {
|
||
expect(buildWhatsNew(CHANGELOG, '1.2.0').version).toBe('1.2.0');
|
||
});
|
||
});
|
||
|
||
describe('shipped whats-new.json', () => {
|
||
it("matches the package version, so a release cannot ship the previous release's notes", () => {
|
||
const pkg = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'));
|
||
const notes = JSON.parse(readFileSync(resolve(root, 'src/web/public/whats-new.json'), 'utf8'));
|
||
expect(notes.version).toBe(pkg.version);
|
||
// A release written without bold lead-ins legitimately has none (the client then stays
|
||
// quiet), so an empty list must not turn the release commit's CI red.
|
||
expect(Array.isArray(notes.highlights)).toBe(true);
|
||
});
|
||
|
||
it('is regenerated by the release script and consumed once per version by the client', () => {
|
||
const pkg = readFileSync(resolve(root, 'package.json'), 'utf8');
|
||
expect(pkg).toMatch(/"version-packages": "changeset version && node scripts\/generate-whats-new\.mjs/);
|
||
const ui = readFileSync(resolve(root, 'src/web/public/settings-ui.js'), 'utf8');
|
||
expect(ui).toContain("'codeman-whats-new-seen'");
|
||
expect(ui).toContain("fetch('/whats-new.json'");
|
||
expect(readFileSync(resolve(root, 'src/web/public/app.js'), 'utf8')).toContain(
|
||
'this.checkWhatsNew?.(data.version)'
|
||
);
|
||
});
|
||
});
|
||
|
||
describe('the announcement in zh-CN', () => {
|
||
const dom = new JSDOM('<!doctype html><html><body></body></html>', {
|
||
runScripts: 'outside-only',
|
||
url: 'http://localhost/',
|
||
});
|
||
vm.runInContext(readFileSync(resolve(root, 'src/web/public/i18n.js'), 'utf8'), dom.getInternalVMContext());
|
||
const api = (
|
||
dom.window as unknown as { CodemanI18n: { t(s: string): string; configure(o: Record<string, string>): void } }
|
||
).CodemanI18n;
|
||
api.configure({ language: 'zh-CN' });
|
||
|
||
it('translates the button and the toast frame, keeping the version and the English highlights', () => {
|
||
expect(api.t("See what's new")).toBe('查看更新内容');
|
||
expect(api.t('Codeman updated to v1.41.2: Tab layouts (#538) · Split view')).toBe(
|
||
'Codeman 已更新到 v1.41.2:Tab layouts (#538) · Split view'
|
||
);
|
||
expect(api.t('Codeman updated to v1.41.2: A · B · C (+5 more)')).toBe(
|
||
'Codeman 已更新到 v1.41.2:A · B · C(另有 5 项)'
|
||
);
|
||
});
|
||
|
||
it('builds the strings the pattern expects', () => {
|
||
const ui = readFileSync(resolve(root, 'src/web/public/settings-ui.js'), 'utf8');
|
||
expect(ui).toContain('`Codeman updated to v${runningVersion}: ${shown}${more}`');
|
||
expect(ui).toContain('` (+${notes.highlights.length - 3} more)`');
|
||
expect(ui).toContain(`label: "See what's new"`);
|
||
});
|
||
});
|