import test from 'node:test'; import assert from 'node:assert/strict'; import type { MergedToken } from '../types'; import { computeWordClass } from './subtitle-render.js'; import { createToken } from './subtitle-render-test-helpers.js'; test('computeWordClass preserves known and n+1 classes while adding JLPT classes', () => { const knownJlpt = createToken({ isKnown: true, jlptLevel: 'N1', surface: '猫', }); const nPlusOneJlpt = createToken({ isNPlusOneTarget: true, jlptLevel: 'N2', surface: '犬', }); assert.equal(computeWordClass(knownJlpt), 'word word-known word-jlpt-n1'); assert.equal(computeWordClass(nPlusOneJlpt), 'word word-n-plus-one word-jlpt-n2'); }); test('computeWordClass applies name-match class ahead of known, n+1, frequency, and JLPT classes when enabled', () => { const token = createToken({ isKnown: true, isNPlusOneTarget: true, jlptLevel: 'N2', frequencyRank: 10, surface: 'アクア', }) as MergedToken & { isNameMatch?: boolean }; token.isNameMatch = true; assert.equal( computeWordClass(token, { nameMatchEnabled: true, enabled: true, topX: 100, mode: 'single', singleColor: '#000000', bandedColors: ['#000000', '#000000', '#000000', '#000000', '#000000'] as const, }), 'word word-name-match', ); }); test('computeWordClass skips name-match class by default', () => { const token = createToken({ surface: 'アクア', }) as MergedToken & { isNameMatch?: boolean }; token.isNameMatch = true; assert.equal(computeWordClass(token), 'word'); }); test('computeWordClass skips name-match class when disabled', () => { const token = createToken({ surface: 'アクア', }) as MergedToken & { isNameMatch?: boolean }; token.isNameMatch = true; assert.equal( computeWordClass(token, { nameMatchEnabled: false, enabled: true, topX: 100, mode: 'single', singleColor: '#000000', bandedColors: ['#000000', '#000000', '#000000', '#000000', '#000000'] as const, }), 'word', ); }); test('computeWordClass keeps known and N+1 color classes exclusive over frequency classes', () => { const known = createToken({ isKnown: true, frequencyRank: 10, surface: '既知', }); const nPlusOne = createToken({ isNPlusOneTarget: true, frequencyRank: 10, surface: '目標', }); const frequency = createToken({ frequencyRank: 10, surface: '頻度', }); assert.equal( computeWordClass(known, { enabled: true, topX: 100, mode: 'single', singleColor: '#000000', bandedColors: ['#000000', '#000000', '#000000', '#000000', '#000000'] as const, }), 'word word-known', ); assert.equal( computeWordClass(nPlusOne, { enabled: true, topX: 100, mode: 'single', singleColor: '#000000', bandedColors: ['#000000', '#000000', '#000000', '#000000', '#000000'] as const, }), 'word word-n-plus-one', ); assert.equal( computeWordClass(frequency, { enabled: true, topX: 100, mode: 'single', singleColor: '#000000', bandedColors: ['#000000', '#000000', '#000000', '#000000', '#000000'] as const, }), 'word word-frequency-single', ); }); test('computeWordClass adds frequency class for single mode when rank is within topX', () => { const token = createToken({ surface: '猫', frequencyRank: 50, }); const actual = computeWordClass(token, { enabled: true, topX: 100, mode: 'single', singleColor: '#000000', bandedColors: ['#000000', '#000000', '#000000', '#000000', '#000000'] as const, }); assert.equal(actual, 'word word-frequency-single'); }); test('computeWordClass adds frequency class when rank equals topX', () => { const token = createToken({ surface: '水', frequencyRank: 100, }); const actual = computeWordClass(token, { enabled: true, topX: 100, mode: 'single', singleColor: '#000000', bandedColors: ['#000000', '#000000', '#000000', '#000000', '#000000'] as const, }); assert.equal(actual, 'word word-frequency-single'); }); test('computeWordClass adds frequency class for banded mode', () => { const token = createToken({ surface: '犬', frequencyRank: 250, }); const actual = computeWordClass(token, { enabled: true, topX: 1000, mode: 'banded', singleColor: '#000000', bandedColors: ['#111111', '#222222', '#333333', '#444444', '#555555'] as const, }); assert.equal(actual, 'word word-frequency-band-2'); }); test('computeWordClass uses configured band count for banded mode', () => { const token = createToken({ surface: '犬', frequencyRank: 2, }); const actual = computeWordClass(token, { enabled: true, topX: 4, mode: 'banded', singleColor: '#000000', bandedColors: ['#111111', '#222222', '#333333', '#444444', '#555555'], } as any); assert.equal(actual, 'word word-frequency-band-3'); }); test('computeWordClass skips frequency class when rank is out of topX', () => { const token = createToken({ surface: '犬', frequencyRank: 1200, }); const actual = computeWordClass(token, { enabled: true, topX: 1000, mode: 'single', singleColor: '#000000', bandedColors: ['#000000', '#000000', '#000000', '#000000', '#000000'] as const, }); assert.equal(actual, 'word'); });