jsPsych/tests/jsPsych.pluginAPI/pluginapi.test.js

366 lines
17 KiB
JavaScript

const root = '../../';
beforeEach(function(){
require(root + 'jspsych.js');
require(root + 'plugins/jspsych-html-keyboard-response.js');
});
describe('#getKeyboardResponse', function(){
test('should execute a function after successful keypress', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t]
})
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback});
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
expect(callback.mock.calls.length).toBe(1);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'a'}));
expect(callback.mock.calls.length).toBe(1);
});
test('should execute only valid keys', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t]
})
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: ['a']});
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'b'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'b'}));
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'a'}));
expect(callback.mock.calls.length).toBe(1);
});
test('should not respond when jsPsych.NO_KEYS is used', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t]
})
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: jsPsych.NO_KEYS});
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'a'}));
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'a'}));
expect(callback.mock.calls.length).toBe(0);
});
test('should not respond to held keys when allow_held_key is false', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t]
})
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: jsPsych.ALL_KEYS, allow_held_key: false});
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'a'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'a'}));
expect(callback.mock.calls.length).toBe(1);
});
test('should respond to held keys when allow_held_key is true', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t]
})
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: jsPsych.ALL_KEYS, allow_held_key: true});
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
expect(callback.mock.calls.length).toBe(1);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'a'}));
});
test('should convert response key to lowercase before determining validity, when case_sensitive_responses is false', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t],
case_sensitive_responses: false
})
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: ['a']});
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'A'}));
expect(callback.mock.calls.length).toBe(1);
});
test('should not convert response key to lowercase before determining validity, when case_sensitive_responses is true', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t],
case_sensitive_responses: true
})
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: ['a']});
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'A'}));
expect(callback.mock.calls.length).toBe(0);
});
test('should not respond to held key when response/valid key case differs, case_sensitive_responses is false, and allow held key is false', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t],
case_sensitive_responses: false
})
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: ['a'], allow_held_key: false});
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'A'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'A'}));
expect(callback.mock.calls.length).toBe(1);
});
test('should respond to held keys when response/valid case differs, case_sensitive_responses is false, and allow_held_key is true', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t],
case_sensitive_responses: false
})
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: ['a'], allow_held_key: true});
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
expect(callback.mock.calls.length).toBe(1);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'A'}));
});
test('should not respond to a held key when response/valid case differs, case_sensitive_responses is true, and allow_held_key is true', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t],
case_sensitive_responses: true
})
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: ['a'], allow_held_key: true});
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'A'}));
});
test('should not respond to a held key when response/valid case differs, case_sensitive_responses is true, and allow_held_key is false', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t],
case_sensitive_responses: true
})
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: ['a'], allow_held_key: false});
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
expect(callback.mock.calls.length).toBe(0);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'A'}));
});
test('should default to case insensitive when used before jsPsych.init is called', function(){
expect(typeof jsPsych.initSettings().case_sensitive_responses).toBe("undefined");
var callback = jest.fn();
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: ['a']});
jsPsych.pluginAPI.createKeyboardEventListeners(document.body);
expect(callback.mock.calls.length).toBe(0);
document.body.dispatchEvent(new KeyboardEvent('keydown', {key: 'a'}));
document.body.dispatchEvent(new KeyboardEvent('keyup', {key: 'a'}));
expect(callback.mock.calls.length).toBe(1);
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback, valid_responses: ['a']});
jsPsych.pluginAPI.createKeyboardEventListeners(document.body);
document.body.dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}));
document.body.dispatchEvent(new KeyboardEvent('keyup', {key: 'A'}));
expect(callback.mock.calls.length).toBe(2);
jsPsych.pluginAPI.reset(document.body);
});
})
describe('#cancelKeyboardResponse', function(){
test('should cancel a keyboard response listener', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t]
})
var listener = jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback});
expect(callback.mock.calls.length).toBe(0);
jsPsych.pluginAPI.cancelKeyboardResponse(listener);
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'q'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'q'}));
expect(callback.mock.calls.length).toBe(0);
});
});
describe('#cancelAllKeyboardResponses', function(){
test('should cancel all keyboard response listeners', function(){
var callback = jest.fn();
var t = {
type: 'html-keyboard-response',
stimulus: 'foo',
choices: ['q']
};
jsPsych.init({
timeline: [t]
})
jsPsych.pluginAPI.getKeyboardResponse({callback_function: callback});
expect(callback.mock.calls.length).toBe(0);
jsPsych.pluginAPI.cancelAllKeyboardResponses();
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keydown', {key: 'q'}));
document.querySelector('.jspsych-display-element').dispatchEvent(new KeyboardEvent('keyup', {key: 'q'}));
expect(callback.mock.calls.length).toBe(0);
});
});
describe('#compareKeys', function(){
test('should compare keys regardless of type (old key-keyCode functionality)', function(){
expect(jsPsych.pluginAPI.compareKeys('q', 81)).toBe(true);
expect(jsPsych.pluginAPI.compareKeys(81, 81)).toBe(true);
expect(jsPsych.pluginAPI.compareKeys('q', 'Q')).toBe(true);
expect(jsPsych.pluginAPI.compareKeys(80, 81)).toBe(false);
expect(jsPsych.pluginAPI.compareKeys('q','1')).toBe(false);
expect(jsPsych.pluginAPI.compareKeys('q',80)).toBe(false);
});
test('should be case sensitive when case_sensitive_responses is true', function(){
var t = {
type: 'html-keyboard-response',
stimulus: 'foo'
};
jsPsych.init({
timeline: [t],
case_sensitive_responses: true
})
expect(jsPsych.pluginAPI.compareKeys('q', 'Q')).toBe(false);
expect(jsPsych.pluginAPI.compareKeys('q', 'q')).toBe(true);
});
test('should not be case sensitive when case_sensitive_responses is false', function(){
var t = {
type: 'html-keyboard-response',
stimulus: 'foo'
};
jsPsych.init({
timeline: [t],
case_sensitive_responses: false
})
expect(jsPsych.pluginAPI.compareKeys('q', 'Q')).toBe(true);
expect(jsPsych.pluginAPI.compareKeys('q', 'q')).toBe(true);
});
test('should default to case insensitive for strings when used before jsPsych.init is called', function(){
expect(typeof jsPsych.initSettings().case_sensitive_responses).toBe("undefined");
expect(jsPsych.pluginAPI.compareKeys('q', 'Q')).toBe(true);
expect(jsPsych.pluginAPI.compareKeys('q', 'q')).toBe(true);
});
test('should accept null as argument, and return true if both arguments are null, and return false if one argument is null and other is string or numeric', function() {
const spy = jest.spyOn(console, 'error').mockImplementation();
expect(jsPsych.pluginAPI.compareKeys(null, 'Q')).toBe(false);
expect(jsPsych.pluginAPI.compareKeys(80, null)).toBe(false);
expect(jsPsych.pluginAPI.compareKeys(null, null)).toBe(true);
expect(console.error).not.toHaveBeenCalled();
spy.mockRestore();
});
test('should return undefined and produce a console warning if either/both arguments are not a string, integer, or null', function() {
const spy = jest.spyOn(console, 'error').mockImplementation();
var t1 = jsPsych.pluginAPI.compareKeys({}, 'Q');
var t2 = jsPsych.pluginAPI.compareKeys(true, null);
var t3 = jsPsych.pluginAPI.compareKeys(null, ['Q']);
expect(typeof t1).toBe('undefined');
expect(typeof t2).toBe('undefined');
expect(typeof t3).toBe('undefined');
expect(console.error).toHaveBeenCalledTimes(3);
expect(console.error.mock.calls).toEqual([
['Error in jsPsych.pluginAPI.compareKeys: arguments must be numeric key codes, key strings, or null.'],
['Error in jsPsych.pluginAPI.compareKeys: arguments must be numeric key codes, key strings, or null.'],
['Error in jsPsych.pluginAPI.compareKeys: arguments must be numeric key codes, key strings, or null.'],
]);
spy.mockRestore();
});
});
describe('#convertKeyCharacterToKeyCode', function(){
test('should return the keyCode for a particular character', function(){
expect(jsPsych.pluginAPI.convertKeyCharacterToKeyCode('q')).toBe(81);
expect(jsPsych.pluginAPI.convertKeyCharacterToKeyCode('1')).toBe(49);
expect(jsPsych.pluginAPI.convertKeyCharacterToKeyCode('space')).toBe(32);
expect(jsPsych.pluginAPI.convertKeyCharacterToKeyCode('enter')).toBe(13);
});
});
describe('#convertKeyCodeToKeyCharacter', function(){
test('should return the keyCode for a particular character', function(){
expect(jsPsych.pluginAPI.convertKeyCodeToKeyCharacter(81)).toBe('q');
expect(jsPsych.pluginAPI.convertKeyCodeToKeyCharacter(49)).toBe('1');
expect(jsPsych.pluginAPI.convertKeyCodeToKeyCharacter(32)).toBe('space');
expect(jsPsych.pluginAPI.convertKeyCodeToKeyCharacter(13)).toBe('enter');
});
});
describe('#setTimeout', function(){
test('basic setTimeout control with centralized storage', function(){
jest.useFakeTimers();
var callback = jest.fn();
jsPsych.pluginAPI.setTimeout(callback, 1000);
expect(callback).not.toBeCalled();
jest.runAllTimers();
expect(callback).toBeCalled();
})
})
describe('#clearAllTimeouts', function(){
test('clear timeouts before they execute', function(){
jest.useFakeTimers();
var callback = jest.fn();
jsPsych.pluginAPI.setTimeout(callback, 5000);
expect(callback).not.toBeCalled();
jsPsych.pluginAPI.clearAllTimeouts();
jest.runAllTimers();
expect(callback).not.toBeCalled();
})
})