Fix blank screen on the pronoun levels
Opening Singular Pronomen rendered nothing at all. The console said "Cannot read properties of undefined (reading 'w')". My regression, from the commit that added the "why Regens?" explainer. That button is rendered on the screen shared by the article, contraction, possessive AND pronoun questions, and its visibility test calls nounForm(q.noun, q.cas) — but pronoun questions have no noun. The read threw, React unmounted the tree, and the whole page went blank rather than losing just that one line. Guarded the access. Then swept all 32 levels across the six tabs in a real browser: every one renders with no page errors. Also wrote the contract down, since this is the second field a screen has dereferenced without checking. engine.test.js now asserts which question types carry a noun and which deliberately do not, that pronoun questions stay noun-less (so the guard keeps being necessary rather than quietly redundant), and that every question of every type has an answer and a blank to fill. A new question type that lands on a shared screen without the fields that screen reads is now a test failure instead of a blank page. 170 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Uza6mhTpAvSwMQuFc4Cz8
This commit is contained in:
+2
-1
@@ -606,7 +606,8 @@ export default function App() {
|
||||
{q.tw !== null && q.tw !== undefined && <> · {q.tw ? '→ motion' : '● location'}</>}
|
||||
</Hideable>
|
||||
|
||||
{nounForm(q.noun, q.cas) !== q.noun.w && (
|
||||
{/* Pronoun questions have no noun at all, so this must be guarded. */}
|
||||
{q.noun && nounForm(q.noun, q.cas) !== q.noun.w && (
|
||||
<div style={{...S.secondaryContext, marginTop:-10}}>
|
||||
<button style={{...S.ruleBtn, marginLeft:0}} onClick={()=>setNounInfo(q)}>
|
||||
ⓘ why {nounForm(q.noun, q.cas)}?
|
||||
|
||||
@@ -942,3 +942,58 @@ describe('no repeats within a round', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ── What the screens are allowed to assume ──────────────────
|
||||
// A question that is missing a field its screen dereferences does not fail
|
||||
// gracefully — React unmounts and the learner gets a blank page. That has now
|
||||
// happened twice, so the contract is written down here rather than left
|
||||
// implicit in the JSX.
|
||||
|
||||
describe('question field contract', () => {
|
||||
// The article / possessive / pronoun screen is shared by four types. Only
|
||||
// pronoun questions have no noun, which is why every noun access on that
|
||||
// screen has to be guarded.
|
||||
const NEEDS_NOUN = ['article','chunk','poss','wechselContext','adjective','adjClass','verbSentence'];
|
||||
const NO_NOUN = ['pronoun','prep','contraction','verbCase'];
|
||||
|
||||
it('every question carries the fields its screen reads', () => {
|
||||
for (const lv of LEVELS) {
|
||||
for (const q of runSession(lv.id, 120)) {
|
||||
expect([...NEEDS_NOUN, ...NO_NOUN], `unknown question type ${q.type}`).toContain(q.type);
|
||||
|
||||
if (NEEDS_NOUN.includes(q.type)) {
|
||||
expect(q.noun, `${q.type} (level ${lv.id}) has no noun`).toBeTruthy();
|
||||
expect(q.noun.g, `${q.type} noun has no gender`).toBeTruthy();
|
||||
expect(q.noun.w).toBeTruthy();
|
||||
expect(q.noun.en).toBeTruthy();
|
||||
}
|
||||
// every type needs something to show and something to grade
|
||||
expect(q.ans, `${q.type} (level ${lv.id}) has no answer`).toBeTruthy();
|
||||
if (q.sentence) expect(q.sentence).toContain('_____');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('pronoun questions have a pronoun and deliberately no noun', () => {
|
||||
for (const id of [14, 15, 16, 17]) {
|
||||
for (const q of runSession(id, 120)) {
|
||||
expect(q.pron, 'pronoun question has no pron').toBeTruthy();
|
||||
expect(q.pron.nom).toBeTruthy();
|
||||
expect(q.pron.en).toBeTruthy();
|
||||
expect(q.pron.tag).toBeTruthy();
|
||||
// this is the one the blank screen came from
|
||||
expect(q.noun, 'pronoun questions must stay noun-less so the guard is real').toBeUndefined();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('verbCase questions carry the verb, not a noun', () => {
|
||||
for (const id of [30, 31]) {
|
||||
for (const q of runSession(id, 120)) {
|
||||
expect(q.verb?.de).toBeTruthy();
|
||||
expect(q.verb?.en).toBeTruthy();
|
||||
expect(q.noun).toBeUndefined();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user