import htmlKeyboardResponse from "@jspsych/plugin-html-keyboard-response"; import { initJsPsych } from "../../src"; import { pressKey, startTimeline } from "../utils"; // import htmlSliderResponse from "@jspsych/plugin-html-slider-response"; describe("on_finish (trial)", () => { test("should get an object of data generated by the trial", async () => { let key_data: string; await startTimeline([ { type: htmlKeyboardResponse, stimulus: "hello", on_finish: (data) => { key_data = data.response; }, }, ]); pressKey("a"); expect(key_data).toBe("a"); }); test("should be able to write to the data", async () => { const { getData } = await startTimeline([ { type: htmlKeyboardResponse, stimulus: "hello", on_finish: (data) => { data.response = 1; }, }, ]); pressKey("a"); expect(getData().values()[0].response).toBe(1); }); }); describe("on_start (trial)", () => { test("should get trial data with function parameters evaluated", async () => { let stimulus: string; await startTimeline([ { type: htmlKeyboardResponse, stimulus: () => "hello", on_start: (trial) => { stimulus = trial.stimulus; }, }, ]); pressKey("a"); expect(stimulus).toBe("hello"); }); test("should get trial data with timeline variables evaluated", async () => { let d: string; const jsPsych = initJsPsych(); await startTimeline( [ { timeline: [ { type: htmlKeyboardResponse, stimulus: jsPsych.timelineVariable("stimulus"), on_start: (trial) => { d = trial.stimulus; }, }, ], timeline_variables: [{ stimulus: "hello" }], }, ], jsPsych ); pressKey("a"); expect(d).toBe("hello"); }); }); describe("on_trial_finish (experiment level)", () => { test("should get an object containing the trial data", async () => { let key: string; const jsPsych = initJsPsych({ on_trial_finish: (data) => { key = data.response; }, }); await startTimeline( [ { type: htmlKeyboardResponse, stimulus: "hello", }, ], jsPsych ); pressKey("a"); expect(key).toBe("a"); }); test("should allow writing to the data object", async () => { const jsPsych = initJsPsych({ on_trial_finish: (data) => { data.write = true; }, }); const { getData } = await startTimeline( [ { type: htmlKeyboardResponse, stimulus: "hello", }, ], jsPsych ); pressKey("a"); expect(getData().values()[0].write).toBe(true); }); }); describe("on_data_update", () => { test("should get an object containing the trial data", async () => { let key: string; const jsPsych = initJsPsych({ on_data_update: (data) => { key = data.response; }, }); await startTimeline( [ { type: htmlKeyboardResponse, stimulus: "hello", }, ], jsPsych ); pressKey("a"); expect(key).toBe("a"); }); test.skip("should contain data with null values", async () => { const data = []; const jsPsych = initJsPsych({ on_data_update: (data) => { data.push(data); }, }); await startTimeline( [ { type: htmlKeyboardResponse, stimulus: "hello", trial_duration: 10, }, { // @ts-ignore TODO enable this test once the plugin is a class type: htmlSliderResponse, stimulus: "hello", trial_duration: 10, }, ], jsPsych ); expect(data[0].response).not.toBeUndefined(); expect(data[0].response).toBeNull(); expect(data[1].response).toBeNull(); expect(data[1].rt).toBeNull(); }); test("should contain data added with on_finish (trial level)", async () => { let trialLevel: boolean; const jsPsych = initJsPsych({ on_data_update: (data) => { trialLevel = data.trialLevel; }, }); await startTimeline( [ { type: htmlKeyboardResponse, stimulus: "hello", on_finish: (data) => { data.trialLevel = true; }, }, ], jsPsych ); pressKey("a"); expect(trialLevel).toBe(true); }); test("should contain data added with on_trial_finish (experiment level)", async () => { let experimentLevel: boolean; const jsPsych = initJsPsych({ on_trial_finish: (data) => { data.experimentLevel = true; }, on_data_update: (data) => { experimentLevel = data.experimentLevel; }, }); await startTimeline( [ { type: htmlKeyboardResponse, stimulus: "hello", }, ], jsPsych ); pressKey("a"); expect(experimentLevel).toBe(true); }); }); describe("on_trial_start", () => { test("should get an object containing the trial properties", async () => { let text: string; const jsPsych = initJsPsych({ on_trial_start: (trial) => { text = trial.stimulus; }, }); await startTimeline( [ { type: htmlKeyboardResponse, stimulus: "hello", }, ], jsPsych ); pressKey("a"); expect(text).toBe("hello"); }); test("should allow modification of the trial properties", async () => { const jsPsych = initJsPsych({ on_trial_start: (trial) => { trial.stimulus = "goodbye"; }, }); const { getHTML } = await startTimeline( [ { type: htmlKeyboardResponse, stimulus: "hello", }, ], jsPsych ); expect(getHTML()).toMatch("goodbye"); pressKey("a"); }); }); describe("on_timeline_finish", () => { test("should fire once when timeline is complete", async () => { var onFinishFunction = jest.fn(); await startTimeline([ { timeline: [ { type: htmlKeyboardResponse, stimulus: "foo", }, { type: htmlKeyboardResponse, stimulus: "foo", }, { type: htmlKeyboardResponse, stimulus: "foo", }, ], on_timeline_finish: onFinishFunction, }, ]); pressKey("a"); expect(onFinishFunction).not.toHaveBeenCalled(); pressKey("a"); expect(onFinishFunction).not.toHaveBeenCalled(); pressKey("a"); expect(onFinishFunction).toHaveBeenCalledTimes(1); }); test("should fire once even with timeline variables", async () => { var onFinishFunction = jest.fn(); await startTimeline([ { timeline: [ { type: htmlKeyboardResponse, stimulus: "foo", }, ], on_timeline_finish: onFinishFunction, timeline_variables: [{ x: 1 }, { x: 2 }], }, ]); pressKey("a"); pressKey("a"); expect(onFinishFunction).toHaveBeenCalledTimes(1); }); test("should fire on every repetition", async () => { var onFinishFunction = jest.fn(); await startTimeline([ { timeline: [ { type: htmlKeyboardResponse, stimulus: "foo", }, ], on_timeline_finish: onFinishFunction, repetitions: 2, }, ]); pressKey("a"); pressKey("a"); expect(onFinishFunction).toHaveBeenCalledTimes(2); }); test("should fire before a loop function", async () => { const callback = jest.fn().mockImplementation((str) => str); let count = 0; await startTimeline([ { timeline: [ { type: htmlKeyboardResponse, stimulus: "foo", }, ], on_timeline_finish: () => { callback("finish"); }, loop_function: () => { callback("loop"); count++; if (count == 2) { return false; } else { return true; } }, }, ]); pressKey("a"); pressKey("a"); expect(callback).toHaveBeenCalledTimes(4); expect(callback.mock.calls[0][0]).toBe("finish"); expect(callback.mock.calls[1][0]).toBe("loop"); expect(callback.mock.calls[2][0]).toBe("finish"); expect(callback.mock.calls[3][0]).toBe("loop"); }); }); describe("on_timeline_start", () => { test("should fire once when timeline starts", async () => { var onStartFunction = jest.fn(); await startTimeline([ { timeline: [ { type: htmlKeyboardResponse, stimulus: "foo", }, { type: htmlKeyboardResponse, stimulus: "foo", }, { type: htmlKeyboardResponse, stimulus: "foo", }, ], on_timeline_start: onStartFunction, }, ]); expect(onStartFunction).toHaveBeenCalledTimes(1); pressKey("a"); pressKey("a"); pressKey("a"); expect(onStartFunction).toHaveBeenCalledTimes(1); }); test("should fire once even with timeline variables", async () => { var onStartFunction = jest.fn(); await startTimeline([ { timeline: [ { type: htmlKeyboardResponse, stimulus: "foo", }, ], on_timeline_start: onStartFunction, timeline_variables: [{ x: 1 }, { x: 2 }], }, ]); expect(onStartFunction).toHaveBeenCalledTimes(1); pressKey("a"); pressKey("a"); expect(onStartFunction).toHaveBeenCalledTimes(1); }); test("should fire on every repetition", async () => { var onStartFunction = jest.fn(); await startTimeline([ { timeline: [ { type: htmlKeyboardResponse, stimulus: "foo", }, ], on_timeline_start: onStartFunction, repetitions: 2, }, ]); expect(onStartFunction).toHaveBeenCalledTimes(1); pressKey("a"); pressKey("a"); expect(onStartFunction).toHaveBeenCalledTimes(2); }); test("should fire after a conditional function", async () => { var callback = jest.fn().mockImplementation((str) => str); await startTimeline([ { timeline: [ { type: htmlKeyboardResponse, stimulus: "foo", }, ], on_timeline_start: () => { callback("start"); }, conditional_function: () => { callback("conditional"); return true; }, }, ]); expect(callback).toHaveBeenCalledTimes(2); expect(callback.mock.calls[0][0]).toBe("conditional"); expect(callback.mock.calls[1][0]).toBe("start"); pressKey("a"); }); });