Repository navigation
Stroop Task never calls done, so it can never be completed #1634
Description
The built-in Stroop Task can never be completed. StroopTask takes a done prop but never reads it, because the component is declared as React.FC<StroopTaskProps> = () => { ... }. When the 60-second countdown reaches zero, it shows "Time is up! Your final score is N" and stops there. Nothing is ever submitted, the instrument never moves on to its summary, and no record is created. The instrument is not seeded into demo instances (apps/api/src/demo/demo.service.ts has no import for it), so this only affects someone who uploads it from the library. Anyone who does gets an instrument that cannot be completed.
There is a related gap in DNP_STROOP_TASK/index.tsx: render creates a React root and never unmounts it, so the 1s setInterval keeps running. Today that has no effect, since done is never called and the host tears down the iframe after a submit. It is worth handling in the same change.
Where
packages/instrument-library/src/interactive/DNP_STROOP_TASK/StroopTask.tsx:14:
export const StroopTask: React.FC<StroopTaskProps> = () => {packages/instrument-library/src/interactive/DNP_STROOP_TASK/StroopTask.tsx:20-31: the countdown stops at 0 and calls nothing.
packages/instrument-library/src/interactive/DNP_STROOP_TASK/index.tsx:15-20: render passes done into a component that ignores it.
Reproduce
- Upload the built
DNP_STROOP_TASKbundle to an instance and start it for a subject. - Answer a few prompts and wait 60 seconds.
Actual: "Time is up! Your final score is N" stays on screen indefinitely. No record is saved and the summary never appears.
Expected: when time runs out, the task calls done({ score }) and the renderer moves on to the summary with the record saved.
Tests
packages/instrument-library/src/__tests__/StroopTask.test.tsxrenders<StroopTask done={vi.fn()} />but never checks the mock. When fixing, addshould report the final score through done once time runs out, assertingdoneis called once with{ score }afteradvanceSeconds(60), and not called again on later ticks.packages/instrument-library/src/__tests__/DNP_STROOP_TASK.test.tsx:should forward the score the task reports to the done callbackcalls the captured prop directly, so it passes even though the component never calls it. ItsafterEachwrapscreateRootto unmount roots the source leaves mounted. Ifrenderstarts unmounting its own root on completion, that workaround can go.
Suggested fix
Destructure done in StroopTask, and call done({ score }) once from an effect when timeLeft reaches 0, guarded so it fires only once.
Found while raising unit test coverage in #1632.
Activity