REQ-012 coverage: the new grouping/segment/bucket keys are present in de/en/es and the pre-existing timeline_grouping_date / timeline_tag_chip_label are reused, not re-declared. Refs #827 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
166 lines
6.7 KiB
TypeScript
166 lines
6.7 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import de from '../../messages/de.json';
|
|
import en from '../../messages/en.json';
|
|
import es from '../../messages/es.json';
|
|
|
|
describe('message key parity', () => {
|
|
it('de, en, and es have identical key sets', () => {
|
|
const deKeys = Object.keys(de).sort();
|
|
const enKeys = Object.keys(en).sort();
|
|
const esKeys = Object.keys(es).sort();
|
|
expect(enKeys).toEqual(deKeys);
|
|
expect(esKeys).toEqual(deKeys);
|
|
});
|
|
|
|
it('viewer navigation keys are present in all locales', () => {
|
|
const requiredViewerKeys = [
|
|
'viewer_previous_page',
|
|
'viewer_next_page',
|
|
'viewer_zoom_out',
|
|
'viewer_zoom_in'
|
|
];
|
|
for (const key of requiredViewerKeys) {
|
|
expect(de, `missing key in de: ${key}`).toHaveProperty(key);
|
|
expect(en, `missing key in en: ${key}`).toHaveProperty(key);
|
|
expect(es, `missing key in es: ${key}`).toHaveProperty(key);
|
|
}
|
|
});
|
|
|
|
it('transcribe mark-for-training key is present in all locales', () => {
|
|
expect(de).toHaveProperty('transcribe_mark_for_training');
|
|
expect(en).toHaveProperty('transcribe_mark_for_training');
|
|
expect(es).toHaveProperty('transcribe_mark_for_training');
|
|
});
|
|
|
|
it('layout menu open/close keys are present in all locales', () => {
|
|
expect(de).toHaveProperty('layout_menu_open');
|
|
expect(de).toHaveProperty('layout_menu_close');
|
|
expect(en).toHaveProperty('layout_menu_open');
|
|
expect(en).toHaveProperty('layout_menu_close');
|
|
expect(es).toHaveProperty('layout_menu_open');
|
|
expect(es).toHaveProperty('layout_menu_close');
|
|
});
|
|
|
|
// REQ-024: the timeline layer/life-event labels feed sr-only / aria text, so
|
|
// they are localized per locale (the original German-only MVP decision was
|
|
// reversed for accessibility). Pin the values so en/es can never silently
|
|
// drift back to the German source strings.
|
|
it('timeline layer/derived labels are localized per locale (REQ-024)', () => {
|
|
expect(de).toMatchObject({
|
|
timeline_layer_world: 'Weltgeschehen',
|
|
timeline_layer_family: 'Familie',
|
|
timeline_derived_birth: 'Geburt',
|
|
timeline_derived_death: 'Tod',
|
|
timeline_derived_marriage: 'Heirat'
|
|
});
|
|
expect(en).toMatchObject({
|
|
timeline_layer_world: 'World events',
|
|
timeline_layer_family: 'Family',
|
|
timeline_derived_birth: 'Birth',
|
|
timeline_derived_death: 'Death',
|
|
timeline_derived_marriage: 'Marriage'
|
|
});
|
|
expect(es).toMatchObject({
|
|
timeline_layer_world: 'Acontecimientos mundiales',
|
|
timeline_layer_family: 'Familia',
|
|
timeline_derived_birth: 'Nacimiento',
|
|
timeline_derived_death: 'Fallecimiento',
|
|
timeline_derived_marriage: 'Matrimonio'
|
|
});
|
|
});
|
|
|
|
// #833 REQ-015: the new visual-fidelity strings (meta line, provenance token,
|
|
// ✉ label, world-band suffix, density caption) are Paraglide keys present in
|
|
// every locale so no surface ever falls back to a missing translation.
|
|
it('zeitstrahl visual-fidelity keys are present in all locales (#833 REQ-015)', () => {
|
|
const requiredKeys = [
|
|
'timeline_grouping_date',
|
|
'timeline_provenance_derived',
|
|
'timeline_provenance_curated',
|
|
'timeline_letter_glyph_label',
|
|
'timeline_layer_historical_suffix',
|
|
'timeline_strip_density_caption',
|
|
'timeline_events_count',
|
|
'timeline_letters_count_singular',
|
|
'timeline_events_count_singular'
|
|
];
|
|
for (const key of requiredKeys) {
|
|
expect(de, `missing key in de: ${key}`).toHaveProperty(key);
|
|
expect(en, `missing key in en: ${key}`).toHaveProperty(key);
|
|
expect(es, `missing key in es: ${key}`).toHaveProperty(key);
|
|
}
|
|
});
|
|
|
|
// #835 REQ-013: the letter chip's sr-only theme label is a Paraglide key in every
|
|
// locale so color is never the only cue; the tag NAME is rendered as data, not translated.
|
|
it('zeitstrahl tag-chip label key is present in all locales (#835 REQ-013)', () => {
|
|
expect(de).toMatchObject({ timeline_tag_chip_label: 'Thema' });
|
|
expect(en).toMatchObject({ timeline_tag_chip_label: 'Topic' });
|
|
expect(es).toMatchObject({ timeline_tag_chip_label: 'Tema' });
|
|
});
|
|
|
|
// #780 REQ-010: the layer-filter strings are Paraglide keys in every locale.
|
|
// timeline_filter_trigger (0 active) and timeline_filter_trigger_active ({count},
|
|
// ≥1 active) are distinct keys so the trigger never reads "Filter (0 aktiv)".
|
|
it('zeitstrahl layer-filter keys are present in all locales (#780 REQ-010)', () => {
|
|
const requiredKeys = [
|
|
'timeline_filter_label_layers',
|
|
'timeline_filter_layer_personal',
|
|
'timeline_filter_layer_historical',
|
|
'timeline_filter_layer_letters',
|
|
'timeline_filter_trigger',
|
|
'timeline_filter_trigger_active',
|
|
'timeline_filter_reset',
|
|
'timeline_filter_empty_state'
|
|
];
|
|
for (const key of requiredKeys) {
|
|
expect(de, `missing key in de: ${key}`).toHaveProperty(key);
|
|
expect(en, `missing key in en: ${key}`).toHaveProperty(key);
|
|
expect(es, `missing key in es: ${key}`).toHaveProperty(key);
|
|
}
|
|
// the active-count key carries the established {count} placeholder
|
|
expect(de.timeline_filter_trigger_active).toContain('{count}');
|
|
expect(en.timeline_filter_trigger_active).toContain('{count}');
|
|
expect(es.timeline_filter_trigger_active).toContain('{count}');
|
|
});
|
|
|
|
// #842: the two curator-affordance CTA labels (Zeitstrahl header + person page)
|
|
// are Paraglide keys present in every locale; the edit pencils reuse btn_edit.
|
|
it('curator-affordance CTA keys are present in all locales (#842)', () => {
|
|
for (const key of ['timeline_add_event', 'person_add_event']) {
|
|
expect(de, `missing key in de: ${key}`).toHaveProperty(key);
|
|
expect(en, `missing key in en: ${key}`).toHaveProperty(key);
|
|
expect(es, `missing key in es: ${key}`).toHaveProperty(key);
|
|
}
|
|
});
|
|
|
|
// #827 REQ-012: the grouping toggle + bucket strings are new Paraglide keys in
|
|
// every locale; the pre-existing timeline_grouping_date / timeline_tag_chip_label /
|
|
// timeline_filter_* set is reused, never re-added.
|
|
it('zeitstrahl grouping + bucket keys are present in all locales (#827 REQ-012)', () => {
|
|
const requiredKeys = [
|
|
'timeline_grouping_event',
|
|
'timeline_grouping_thema',
|
|
'timeline_grouping_aria_label',
|
|
'timeline_grouping_segment_date',
|
|
'timeline_grouping_segment_event',
|
|
'timeline_grouping_segment_thema',
|
|
'timeline_grouping_segment_date_short',
|
|
'timeline_grouping_segment_event_short',
|
|
'timeline_grouping_segment_thema_short',
|
|
'timeline_grouping_disabled_reason',
|
|
'timeline_grouping_multitag_hint',
|
|
'timeline_bucket_other_letters',
|
|
'timeline_bucket_no_topic'
|
|
];
|
|
for (const key of requiredKeys) {
|
|
expect(de, `missing key in de: ${key}`).toHaveProperty(key);
|
|
expect(en, `missing key in en: ${key}`).toHaveProperty(key);
|
|
expect(es, `missing key in es: ${key}`).toHaveProperty(key);
|
|
}
|
|
// the pre-existing meta-line + chip keys are reused by #827, not re-declared
|
|
expect(de).toHaveProperty('timeline_grouping_date');
|
|
expect(de).toHaveProperty('timeline_tag_chip_label');
|
|
});
|
|
});
|