mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-04 00:41:33 -07:00
94 lines
3.1 KiB
TypeScript
94 lines
3.1 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
import {
|
|
SessionDetail,
|
|
buildKnownWordsRatioChartData,
|
|
getKnownPctAxisMax,
|
|
} from '../components/sessions/SessionDetail';
|
|
import { buildSessionChartEvents } from './session-events';
|
|
import { EventType } from '../types/stats';
|
|
|
|
test('SessionDetail omits the misleading new words metric', () => {
|
|
const markup = renderToStaticMarkup(
|
|
<SessionDetail
|
|
session={{
|
|
sessionId: 7,
|
|
canonicalTitle: 'Episode 7',
|
|
videoId: 7,
|
|
animeId: null,
|
|
animeTitle: null,
|
|
startedAtMs: 0,
|
|
endedAtMs: null,
|
|
totalWatchedMs: 0,
|
|
activeWatchedMs: 0,
|
|
linesSeen: 12,
|
|
tokensSeen: 24,
|
|
cardsMined: 0,
|
|
lookupCount: 0,
|
|
lookupHits: 0,
|
|
yomitanLookupCount: 0,
|
|
knownWordsSeen: 0,
|
|
knownWordRate: 0,
|
|
}}
|
|
/>,
|
|
);
|
|
|
|
assert.match(markup, /No word data/);
|
|
assert.doesNotMatch(markup, /New words/);
|
|
});
|
|
|
|
test('buildSessionChartEvents keeps only chart-relevant events and pairs pause ranges', () => {
|
|
const chartEvents = buildSessionChartEvents([
|
|
{ eventType: EventType.SUBTITLE_LINE, tsMs: 1_000, payload: '{"line":"ignored"}' },
|
|
{ eventType: EventType.PAUSE_START, tsMs: 2_000, payload: null },
|
|
{ eventType: EventType.SEEK_FORWARD, tsMs: 3_000, payload: null },
|
|
{ eventType: EventType.PAUSE_END, tsMs: 4_000, payload: null },
|
|
{ eventType: EventType.CARD_MINED, tsMs: 5_000, payload: '{"cardsMined":1}' },
|
|
{ eventType: EventType.YOMITAN_LOOKUP, tsMs: 6_000, payload: null },
|
|
{ eventType: EventType.SEEK_BACKWARD, tsMs: 7_000, payload: null },
|
|
{ eventType: EventType.LOOKUP, tsMs: 8_000, payload: '{"hit":true}' },
|
|
]);
|
|
|
|
// Seek events are intentionally dropped from the chart — they were too noisy.
|
|
assert.deepEqual(
|
|
chartEvents.markers.filter((marker) => marker.kind !== 'pause' && marker.kind !== 'card'),
|
|
[],
|
|
);
|
|
assert.deepEqual(
|
|
chartEvents.cardEvents.map((event) => event.tsMs),
|
|
[5_000],
|
|
);
|
|
assert.deepEqual(
|
|
chartEvents.yomitanLookupEvents.map((event) => event.tsMs),
|
|
[6_000],
|
|
);
|
|
assert.deepEqual(chartEvents.pauseRegions, [{ startMs: 2_000, endMs: 4_000 }]);
|
|
});
|
|
|
|
test('getKnownPctAxisMax adds headroom above the highest known percentage', () => {
|
|
assert.equal(getKnownPctAxisMax([22.4, 31.2, 29.8]), 40);
|
|
});
|
|
|
|
test('getKnownPctAxisMax caps the chart top at 100%', () => {
|
|
assert.equal(getKnownPctAxisMax([97.1, 98.6]), 100);
|
|
});
|
|
|
|
test('buildKnownWordsRatioChartData uses filtered known-word timeline totals', () => {
|
|
const chartData = buildKnownWordsRatioChartData(
|
|
[
|
|
{ sampleMs: 1_000, linesSeen: 1, tokensSeen: 10 },
|
|
{ sampleMs: 2_000, linesSeen: 2, tokensSeen: 20 },
|
|
],
|
|
new Map([
|
|
[1, { knownWordsSeen: 2, totalWordsSeen: 3 }],
|
|
[2, { knownWordsSeen: 3, totalWordsSeen: 4 }],
|
|
]),
|
|
);
|
|
|
|
assert.deepEqual(chartData, [
|
|
{ tsMs: 1_000, knownWords: 2, unknownWords: 1, totalWords: 3 },
|
|
{ tsMs: 2_000, knownWords: 3, unknownWords: 1, totalWords: 4 },
|
|
]);
|
|
});
|