Merge pull request #3211 from jspsych/add-response-ends-trial-vs-circle

Add `response_ends_trial` for visual search circle plugin
This commit is contained in:
Josh de Leeuw 2024-01-14 13:02:34 -05:00 committed by GitHub
commit c64d3fe9b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 23 deletions

View File

@ -0,0 +1,5 @@
---
"@jspsych/plugin-visual-search-circle": minor
---
Adds response_ends_trial parameter, with a default value of `true`

View File

@ -32,6 +32,7 @@ The `target_present` and `fixation_image` parameters must always be specified. O
| target_absent_key | string | 'f' | The key to press if the target is not present in the search array. | | target_absent_key | string | 'f' | The key to press if the target is not present in the search array. |
| trial_duration | numeric | null | The maximum amount of time the participant is allowed to search before the trial will continue. A value of null will allow the participant to search indefinitely. | | trial_duration | numeric | null | The maximum amount of time the participant is allowed to search before the trial will continue. A value of null will allow the participant to search indefinitely. |
| fixation_duration | numeric | 1000 | How long to show the fixation image for before the search array (in milliseconds). | | fixation_duration | numeric | 1000 | How long to show the fixation image for before the search array (in milliseconds). |
| response_ends_trial| boolean | true | If true, the trial will end when the participant makes a response. |
## Data Generated ## Data Generated

View File

@ -29,6 +29,36 @@ describe("visual-search-circle", () => {
expect(getData().values()[0].correct).toBe(true); expect(getData().values()[0].correct).toBe(true);
}); });
it("wait when response_ends_trial is false", async () => {
const { displayElement, expectFinished, expectRunning, getData } = await startTimeline([
{
type: visualSearchCircle,
target: "target.png",
foil: "foil.png",
fixation_image: "fixation.png",
set_size: 4,
target_present: true,
target_present_key: "a",
target_absent_key: "b",
response_ends_trial: false,
trial_duration: 1500,
},
]);
expect(displayElement.querySelectorAll("img").length).toBe(1);
jest.advanceTimersByTime(1000); // fixation duration
expect(displayElement.querySelectorAll("img").length).toBe(5);
pressKey("a");
await expectRunning();
jest.runAllTimers();
await expectFinished();
expect(getData().values()[0].correct).toBe(true);
});
}); });
describe("visual-search-circle simulation", () => { describe("visual-search-circle simulation", () => {

View File

@ -89,6 +89,12 @@ const info = <const>{
pretty_name: "Fixation duration", pretty_name: "Fixation duration",
default: 1000, default: 1000,
}, },
/** Whether a keyboard response ends the trial early */
response_ends_trial: {
type: ParameterType.BOOL,
pretty_name: "Response ends trial",
default: true,
},
}, },
}; };
@ -153,12 +159,21 @@ class VisualSearchCirclePlugin implements JsPsychPlugin<Info> {
}, trial.fixation_duration); }, trial.fixation_duration);
}; };
const end_trial = (rt: number, correct: boolean, key_press: string) => { const response = {
rt: null,
key: null,
correct: false,
};
const end_trial = () => {
this.jsPsych.pluginAPI.clearAllTimeouts();
this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
// data saving // data saving
var trial_data = { const trial_data = {
correct: correct, correct: response.correct,
rt: rt, rt: response.rt,
response: key_press, response: response.key,
locations: display_locs, locations: display_locs,
target_present: trial.target_present, target_present: trial.target_present,
set_size: trial.set_size, set_size: trial.set_size,
@ -186,11 +201,7 @@ class VisualSearchCirclePlugin implements JsPsychPlugin<Info> {
"px;'></img>"; "px;'></img>";
} }
var trial_over = false;
const after_response = (info: { key: string; rt: number }) => { const after_response = (info: { key: string; rt: number }) => {
trial_over = true;
var correct = false; var correct = false;
if ( if (
@ -202,12 +213,16 @@ class VisualSearchCirclePlugin implements JsPsychPlugin<Info> {
correct = true; correct = true;
} }
clear_display(); response.rt = info.rt;
response.key = info.key;
response.correct = correct;
end_trial(info.rt, correct, info.key); if (trial.response_ends_trial) {
end_trial();
}
}; };
var valid_keys = [trial.target_present_key, trial.target_absent_key]; const valid_keys = [trial.target_present_key, trial.target_absent_key];
const key_listener = this.jsPsych.pluginAPI.getKeyboardResponse({ const key_listener = this.jsPsych.pluginAPI.getKeyboardResponse({
callback_function: after_response, callback_function: after_response,
@ -219,19 +234,11 @@ class VisualSearchCirclePlugin implements JsPsychPlugin<Info> {
if (trial.trial_duration !== null) { if (trial.trial_duration !== null) {
this.jsPsych.pluginAPI.setTimeout(() => { this.jsPsych.pluginAPI.setTimeout(() => {
if (!trial_over) { if (!response.rt) {
this.jsPsych.pluginAPI.cancelKeyboardResponse(key_listener); this.jsPsych.pluginAPI.cancelKeyboardResponse(key_listener);
trial_over = true;
var rt = null;
var correct = false;
var key_press = null;
clear_display();
end_trial(rt, correct, key_press);
} }
end_trial();
}, trial.trial_duration); }, trial.trial_duration);
} }