Remove nominative testing, add hideable hints and week streak dots
- Remove the Nominativ level and drop nominative-case testing from mixed/ possessive/pronoun levels (nominative kept only in reference charts) - Add persistent hide/show toggles on the two giveaway hint lines in practice (noun gender, case/preposition info); state persists in localStorage - De-spoil "Wechsel im Kontext": remove motion/location hint from answer buttons; put sentence translation + preposition meaning behind a toggle - Add a 7-day practice-history dot strip beside the streak counter - Extend engine + tests (61 vitest tests) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
d30e213104
commit
cd0b6c800d
+40
-4
@@ -4,7 +4,7 @@ import {
|
||||
NOUNS, nounForm, LEVELS, CONTRACTIONS, CONTRACTABLE, CHUNK_PREPS,
|
||||
POSS_ENDINGS, combinePoss, PRONOUNS, VERBS, PREP_FRAMES,
|
||||
createSession, gen, genContractionContextQ, newState, buildChunkChoices,
|
||||
bumpStreak, currentStreak,
|
||||
bumpStreak, currentStreak, weekDots,
|
||||
} from './engine.js';
|
||||
|
||||
const runSession = (levelId, n) => {
|
||||
@@ -238,9 +238,22 @@ describe('pronoun questions', () => {
|
||||
it.each([14, 15, 16, 17])('level %s: answer matches the pronoun table', (id) => {
|
||||
for (const q of runSession(id, 200)) {
|
||||
const p = PRONOUNS.find(x => x.key === q.pronKey);
|
||||
const expected = p[{nominative:'nom',accusative:'acc',dative:'dat'}[q.cas]];
|
||||
const want = q.cas === 'nominative' ? expected.charAt(0).toUpperCase() + expected.slice(1) : expected;
|
||||
expect(q.ans).toBe(want);
|
||||
expect(q.ans).toBe(p[{accusative:'acc',dative:'dat'}[q.cas]]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ── Nominative is never tested ──────────────────────────────
|
||||
|
||||
describe('no nominative anywhere', () => {
|
||||
it('no level tests the nominative — you already know der/die/das', () => {
|
||||
expect(LEVELS.some(l => l.name === 'Nominativ')).toBe(false);
|
||||
for (const lv of LEVELS) {
|
||||
if (lv.cases) expect(lv.cases, `level ${lv.id} still lists nominative`).not.toContain('nominative');
|
||||
for (const q of runSession(lv.id, 150)) {
|
||||
if (q.cas) expect(q.cas, `level ${lv.id} generated a nominative question`).not.toBe('nominative');
|
||||
expect(q.ans).not.toBe('nominative');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -289,6 +302,29 @@ describe('streaks', () => {
|
||||
expect(st.streak).toBe(10);
|
||||
});
|
||||
|
||||
it('records practice days and prunes old ones', () => {
|
||||
let st = bumpStreak({ streak: 0, ld: null }, '2026-07-01');
|
||||
expect(st.days['2026-07-01']).toBe(true);
|
||||
st = bumpStreak(st, '2026-07-01'); // same day, already recorded
|
||||
expect(Object.keys(st.days).length).toBe(1);
|
||||
st = bumpStreak(st, '2026-07-02');
|
||||
expect(st.days['2026-07-01']).toBe(true);
|
||||
expect(st.days['2026-07-02']).toBe(true);
|
||||
st = bumpStreak(st, '2026-07-20'); // far in the future → old days pruned
|
||||
expect(st.days['2026-07-01']).toBeUndefined();
|
||||
expect(st.days['2026-07-20']).toBe(true);
|
||||
});
|
||||
|
||||
it('weekDots reports the last 7 days, today last', () => {
|
||||
const st = { days: { '2026-07-10': true, '2026-07-12': true, '2026-07-14': true } };
|
||||
const dots = weekDots(st, '2026-07-14');
|
||||
expect(dots.length).toBe(7);
|
||||
expect(dots[0].date).toBe('2026-07-08');
|
||||
expect(dots[6].date).toBe('2026-07-14');
|
||||
expect(dots[6].isToday).toBe(true);
|
||||
expect(dots.map(d => d.practiced)).toEqual([false, false, true, false, true, false, true]);
|
||||
});
|
||||
|
||||
it('displays 0 once the streak has lapsed, without needing a new answer', () => {
|
||||
const st = { streak: 7, ld: '2026-07-01' };
|
||||
expect(currentStreak(st, '2026-07-01')).toBe(7);
|
||||
|
||||
Reference in New Issue
Block a user