mirror of
https://github.com/jspsych/jsPsych.git
synced 2025-05-10 11:10:54 +00:00
Merge branch 'main' into fix-build-citations-test
This commit is contained in:
commit
ad6c988449
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -1,11 +1,13 @@
|
||||
name: build
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
types: [opened, reopened, ready_for_review, review_requested]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, lint, and test on Node.js ${{ matrix.node }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
@ -25,7 +25,7 @@ You can learn more about setting up a project by following the [hello world tuto
|
||||
|
||||
Once you've got a project set up, the [reaction time task tutorial](https://www.jspsych.org/latest/tutorials/rt-task/) is a great next step, since it covers many core topics and features.
|
||||
|
||||
There are also a number of [video tutorials](https://www.jspsych.org/latest/tutorials/video-tutorials) available on the website.
|
||||
There are also a number of [community tutorials](https://www.jspsych.org/latest/tutorials/community-tutorials) available on the website.
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -3,11 +3,15 @@ The following people have contributed to the development of jsPsych by writing c
|
||||
* Antonia - https://github.com/Ahoidal
|
||||
* aucuparia - https://github.com/aucuparia
|
||||
* Xiaolu Bai - https://github.com/lbai001
|
||||
* Niranjan Baskaran - https://github.com/Bankminer78
|
||||
* brchristian - https://github.com/brchristian
|
||||
* bjoluc - https://github.com/bjoluc
|
||||
* Christian Brickhouse - https://github.com/chrisbrickhouse
|
||||
* Teon L Brooks - https://github.com/teonbrooks
|
||||
* Susan Buck - https://github.com/susanBuck
|
||||
* Eamon Caddigan - https://github.com/eamoncaddigan
|
||||
* Jason Carpenter
|
||||
* Cherrie Chang - https://github.com/cherriechang
|
||||
* Steve Chao - https://github.com/stchao
|
||||
* Zhanwen "Phil" Chen - https://github.com/zhanwenchen
|
||||
* cthorey - https://github.com/cthorey
|
||||
@ -20,15 +24,17 @@ The following people have contributed to the development of jsPsych by writing c
|
||||
* Becky Gilbert - https://github.com/becky-gilbert
|
||||
* Mark Gorenstein - https://github.com/mgorenstein
|
||||
* Rui Han - https://github.com/hrcn
|
||||
* Eitan Hemed - https://github.com/EitanHemed
|
||||
* Andy Heusser - https://github.com/andrewheusser
|
||||
* Angus Hughes - https://github.com/awhug
|
||||
* jadeddelta - https://github.com/jadeddelta
|
||||
* jade - https://github.com/jadeddelta
|
||||
* Gustavo Juantorena - https://github.com/GEJ1
|
||||
* Chris Jungerius - https://github.com/cjungerius
|
||||
* George Kachergis - https://github.com/kachergis
|
||||
* Yul Kang - https://github.com/yulkang
|
||||
* Spencer King - https://github.com/spencerking
|
||||
* Jana Klaus - https://github.com/janakl4us
|
||||
* Ethan Knights - https://github.com/ethanknights
|
||||
* Arnold Kochari - https://github.com/akochari
|
||||
* Peter Jes Kohler - https://github.com/pjkohler
|
||||
* kupiqu - https://github.com/kupiqu
|
||||
@ -38,6 +44,7 @@ The following people have contributed to the development of jsPsych by writing c
|
||||
* madebyafox - https://github.com/madebyafox
|
||||
* Shane Martin - https://github.com/shamrt
|
||||
* Vijay Marupudi - https://github.com/vijaymarupudi
|
||||
* Cian Monnin - https://github.com/CMonnin
|
||||
* Adrian Oesch - https://github.com/adrianoesch
|
||||
* Benjamin Ooghe-Tabanou - https://github.com/boogheta
|
||||
* Nikolay B Petrov - https://github.com/nikbpetrov
|
||||
@ -65,3 +72,4 @@ The following people have contributed to the development of jsPsych by writing c
|
||||
* Shaobin Jiang - https://github.com/Shaobin-Jiang
|
||||
* Haotian Tu - https://github.com/thtTNT
|
||||
* Joshua Unrau - https://github.com/joshunrau
|
||||
* Victor Zhang - https://github.com/vzhang03
|
@ -66,7 +66,7 @@ let trial = {
|
||||
extensions: [
|
||||
{type: myAwesomeExtension, params: {demo: 'value'}}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
//... extension code ...//
|
||||
class MyAwesomeExtension {
|
||||
@ -91,7 +91,7 @@ let trial = {
|
||||
extensions: [
|
||||
{type: myAwesomeExtension, params: {demo: 'value'}}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
//... extension code ...//
|
||||
class MyAwesomeExtension {
|
||||
@ -122,7 +122,7 @@ let trial = {
|
||||
on_finish: (data) => {
|
||||
console.log(data.awesome); // will output 'value'.
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//... extension code ...//
|
||||
class MyAwesomeExtension {
|
||||
@ -168,9 +168,11 @@ The `version` field describes the version of the extension used and then durin t
|
||||
|
||||
The `data` field is an object containing all of the `data` generated for the plugin. Each 'data' object has a `type` and `default` property. Additionally, this should be only used for data you choose to generate. Any jsdoc (comments included in the /** */ tags) you include will be scraped as metadata if you are choosing to generate metadata. This scraped metadata will also be used to create the JsPsych documentation.
|
||||
|
||||
For more information on the various types of parameters one can include in their data field, see our [documentation on `ParameterType`s](./plugin-development.md#parameter-types).
|
||||
|
||||
### Optional methods
|
||||
|
||||
The extension can also include any additional methods that are necessary for interacting with it. See the [webgazer extension](../extensions/webgazer.md) for an example.
|
||||
The extension can also include any additional methods that are necessary for interacting with it. See the [WebGazer extension](../extensions/webgazer.md) for an example.
|
||||
|
||||
## Advice for writing extensions
|
||||
|
||||
|
@ -63,6 +63,10 @@ const info = {
|
||||
}
|
||||
```
|
||||
|
||||
??? info "Automatic versioning with custom build environments"
|
||||
|
||||
If you are using a custom build environment that imports its own `tsconfig.json` file that does not extend jsPsych's, and you want to use this automatic versioning syntax, you must add `"resolveJsonModule": true` to the config's `compilerOptions` object.
|
||||
|
||||
If you are not using a build environment that supports `import` and `package.json` (such as writing a plain JS file), you can manually enter the `version` as a string.
|
||||
|
||||
```javascript
|
||||
@ -165,6 +169,89 @@ class MyAwesomePlugin {
|
||||
MyAwesomePlugin.info = info;
|
||||
```
|
||||
|
||||
#### Parameter Types
|
||||
|
||||
jsPsych currently has support for the following parameter types:
|
||||
|
||||
| Type Name | Description | Example |
|
||||
| --------- | ----------- | ------- |
|
||||
| BOOL | A simple truth value. | `true` or `false` |
|
||||
| STRING | A set of characters. | "Continue" |
|
||||
| INT | A value that supports whole numbers. | 12 |
|
||||
| FLOAT | A value that supports decimal numbers. | 5.55 |
|
||||
| FUNCTION | A Javascript function, tends to process multiple objects in an array from other parameters. | `function(tries) { return "<p>You have " + tries + " tries left." }` |
|
||||
| KEY | A single key, with support for function keys like arrows and spacebars. | `"j"`, `"n"`, `"ArrowLeft"` |
|
||||
| KEYS | Either an array of keys, or the string `"ALL_KEYS"` or `"NO_KEYS"`, indicating their respective inclusion/exclusion criterea. | `["f", "j"]` |
|
||||
| SELECT | A list of strings that a developer can choose between as a parameter. | `["cm", "px", "em"]` |
|
||||
| HTML_STRING | A string with HTML markup. | `"<p>This is the prompt.</p>"` |
|
||||
| IMAGE | A string that contains the path to an image file. | `"my_image.jpg"` |
|
||||
| AUDIO | A string that contains the path to an audio file. | `"my_sound.mp3"` |
|
||||
| VIDEO | A string that contains the path to a video file. | `"my_video.mp4"` |
|
||||
| OBJECT | A general JSON object (key-value pairs). | `{ rt: 350, response: "hello!", correct: true }` |
|
||||
| COMPLEX | A JSON object that one can specify nested parameters for. | `{ rt: 350, response: "hello!", correct: true }` |
|
||||
| TIMELINE | A jsPsych timeline object with trials. | `[{ type: jsPsychKeyboardResponse, stimulus: 'my_image.jpg }]` |
|
||||
|
||||
Within each parameter, you may also specify if it is an array of the specific type. For example, a parameter that requires a list of button labels would be described as:
|
||||
|
||||
```js
|
||||
const info = {
|
||||
// ...
|
||||
parameters: {
|
||||
/** The labels to be displayed on each button. */
|
||||
labels: {
|
||||
type: ParameterType.STRING,
|
||||
array: true,
|
||||
default: ["Pause", "Play", "Continue"]
|
||||
}
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Specific parameter types also have their own special markup. For `ParameterType.SELECT`, you specify the options one can choose with an `options` field, and then the `default` field must be within that field.
|
||||
|
||||
```js
|
||||
const info = {
|
||||
// ...
|
||||
parameters: {
|
||||
/** The units of measure used to display the length and width of the stimulus. */
|
||||
units: {
|
||||
type: ParameterType.SELECT,
|
||||
options: ["em", "px", "vh", "vw"],
|
||||
default: "px"
|
||||
}
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
For `ParameterType.COMPLEX`, we may specify the underlying fields in the object with the `nested` field. This acts in the same way as us defining parameters regularly, only we are now just delineating the fields within the object itself.
|
||||
|
||||
```js
|
||||
const info = {
|
||||
// ...
|
||||
parameters: {
|
||||
/** Where to display the location of the stimuli. */
|
||||
locations: {
|
||||
type: ParameterType.COMPLEX,
|
||||
array: true,
|
||||
default: undefined,
|
||||
nested: {
|
||||
/** The x-coordinate of the stimulus, in the units from the `units` field. */
|
||||
x: {
|
||||
type: ParameterType.INT
|
||||
},
|
||||
/** The y-coordinate of the stimulus. */
|
||||
y: {
|
||||
type: ParameterType.INT
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin functionality
|
||||
|
||||
Inside the `.trial()` method you can do pretty much anything that you want, including modifying the DOM, setting up event listeners, and making asynchronous requests. In this section we'll highlight a few common things that you might want to do as examples.
|
||||
|
@ -22,11 +22,12 @@ There are many other web browsers that are available, but that are not commonly
|
||||
In general, jsPsych experiments can be run on mobile devices (smartphones and tablets). However, certain plugins will not work on mobile. For instance, any plugin that requires a keyboard response without a text input box, such as the *-keyboard-response plugins, will not work. Even plugins that do work on mobile might work differently than they do on desktop and laptop computers. For instance, on mobile devices, a text input box will cause an on-screen keyboard to pop up, which affects the visible content on the screen.
|
||||
|
||||
If you plan to run an experiment that allows people to use mobile devices, we recommend doing some extra testing to make sure that everything works as expected. In particular, you may want to check that:
|
||||
* Font sizes are readable on smaller screens
|
||||
* Stimuli sizes are large enough and appropriate for the task
|
||||
* Page is laid out as intended (e.g. elements are centered and do not overlap)
|
||||
* Response options are touchscreen-friendly (e.g. buttons rather than key presses)
|
||||
* Response options (e.g. buttons, text boxes, radio buttons) are large enough and far enough apart to be easily selected with a finger tap
|
||||
|
||||
* Font sizes are readable on smaller screens
|
||||
* Stimuli sizes are large enough and appropriate for the task
|
||||
* Page is laid out as intended (e.g. elements are centered and do not overlap)
|
||||
* Response options are touchscreen-friendly (e.g. buttons rather than key presses)
|
||||
* Response options (e.g. buttons, text boxes, radio buttons) are large enough and far enough apart to be easily selected with a finger tap
|
||||
|
||||
It's possible to use your browser's developer tools to emulate mobile devices ([this page shows how to do it in Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode)), which is useful for getting a sense of how your experiment will look on mobile devices. Just be aware that there are limitations to emulator tools, and there are some aspects of mobile devices/browsers that a desktop browser will not be able to simulate.
|
||||
|
||||
|
@ -387,7 +387,7 @@ Rather than repeating a question format within the same trial, perhaps you want
|
||||
elements: [
|
||||
{
|
||||
type: "text",
|
||||
title: `Enter a word related to ${jsPsych.timelineVariable('word').toUpperCase()}:`,
|
||||
title: `Enter a word related to ${jsPsych.evaluteTimelineVariable('word').toUpperCase()}:`,
|
||||
autocomplete: "off"
|
||||
}
|
||||
],
|
||||
@ -416,7 +416,7 @@ Rather than repeating a question format within the same trial, perhaps you want
|
||||
// Create question using timeline variables
|
||||
const page = survey.addNewPage('page1');
|
||||
const question = page.addNewQuestion('text');
|
||||
question.title = `Enter a word related to ${jsPsych.timelineVariable('word').toUpperCase()}`;
|
||||
question.title = `Enter a word related to ${jsPsych.evaluateTimelineVariable('word').toUpperCase()}`;
|
||||
question.autocomplete = "off";
|
||||
// Set survey-level parameters
|
||||
survey.showQuestionNumbers = false;
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Extensions
|
||||
|
||||
Extensions are jsPsych modules that can interface with any plugin to extend the functionality of the plugin. A canonical example of an extension is eye tracking. An eye tracking extension allows a plugin to gather gaze data and add it to the plugin's data object.
|
||||
In jsPsych, extensions allow one to extend the functionality of various plugins, giving individual plugins the ability to collect more data, display additional stimuli, and more. A canonical example of an extension is [eye tracking](../extensions/webgazer.md), which allow plugins to gather gaze data and add it to the their respective data objects. For a full list of extensions directly included in the jsPsych release, see [here](../extensions/list-of-extensions.md).
|
||||
|
||||
## Using an Extension
|
||||
|
||||
@ -16,7 +16,7 @@ To use an extension in an experiment, you'll load the extension file via a `<scr
|
||||
```js
|
||||
initJsPsych({
|
||||
extensions: [
|
||||
{type: jsPsychExtensionExample, params: {...} }
|
||||
{ type: jsPsychExtensionExample, params: {...} }
|
||||
]
|
||||
})
|
||||
```
|
||||
@ -26,17 +26,11 @@ To enable an extension during a trial, add the extension to the `extensions` lis
|
||||
```js
|
||||
var trial = {
|
||||
extensions: [
|
||||
{type: jsPsychExtensionExample, params: {...} }
|
||||
{ type: jsPsychExtensionExample, params: {...} }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## List of Extensions
|
||||
|
||||
Extension | Description
|
||||
------ | -----------
|
||||
[jspsych‑ext‑webgazer.js](../extensions/webgazer.md) | Enables eye tracking using the [WebGazer](https://webgazer.cs.brown.edu/) library.
|
||||
|
||||
## Writing an Extension
|
||||
|
||||
See our [developer's guide for extensions](../developers/extension-development.md) for information about how to create a new extension.
|
@ -3,7 +3,7 @@
|
||||
If an experiment uses image, audio, or video files as stimuli, it is a good idea to preload the files before running the experiment. You can preload files at any point in your experiment using the [jsPsych `preload` plugin](../plugins/preload.md). Preloading files means that the participant's browser will download the files and store them in local memory on the participant's computer. This is important because displaying or playing a media file is much faster if it is already in memory on the participant's computer. Without preloading, there will be noticeable delays in the display of media, which will affect any timing measurements (such as how long an image is displayed, or a participant's response time since first viewing an image). For particularly large files, like video, preloading content avoids lengthy pauses in the middle of the experiment that can be disruptive to the flow of the experiment.
|
||||
|
||||
!!! warning
|
||||
Note that video preloading will not work when you run your experiment offline (e.g., by double-clicking on the HTML file), but it will work once your experiment is running online (hosted on a server). The [Cross-origin requests (CORS) and safe mode](running-experiments.md#cross-origin-requests-cors-and-safe-mode) section on the Running Experiments page contains more information about this.
|
||||
Note that video preloading will not work when you run your experiment offline (e.g., by double-clicking on the HTML file), but it will work once your experiment is running online (hosted on a server). The [Cross-origin requests (CORS) and safe mode](running-experiments.md#cross-origin-requests-cors-and-safe-mode) section on the Running Experiments page contains more information about this.
|
||||
|
||||
## Automatic Preloading
|
||||
|
||||
|
@ -28,6 +28,7 @@ In addition to the [parameters available in all plugins](../overview/plugins.md#
|
||||
| prompt | string | null | This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press). |
|
||||
| trial_duration | numeric | null | How long to wait for the participant to make a response before ending the trial in milliseconds. If the participant fails to make a response before this timer is reached, the participant's response will be recorded as null for the trial and the trial will end. If the value of this parameter is null, then the trial will wait for a response indefinitely. |
|
||||
| response_ends_trial | boolean | true | If true, then the trial will end whenever the participant makes a response (assuming they make their response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will continue until the value for `trial_duration` is reached. You can set this parameter to `false` to force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete. |
|
||||
| trial_ends_after_audio | boolean | false | If true, then the trial will end as soon as the audio file finishes playing. |
|
||||
| response_allowed_while_playing | boolean | true | If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before the slider is enabled and the trial can end via the next button click. Once the audio has played all the way through, the slider is enabled and a response is allowed (including while the audio is being re-played via on-screen playback controls). |
|
||||
|
||||
## Data Generated
|
||||
|
@ -18,7 +18,7 @@ In addition to the [parameters available in all plugins](../overview/plugins.md#
|
||||
Parameter | Type | Default Value | Description
|
||||
----------|------|---------------|------------
|
||||
device_select_message | html string | `<p>Please select the camera you would like to use.</p>` | The message to display when the user is presented with a dropdown list of available devices.
|
||||
button_label | sting | 'Use this camera.' | The label for the select button.
|
||||
button_label | string | 'Use this camera.' | The label for the select button.
|
||||
include_audio | bool | false | Set to `true` to include an audio track in the recordings.
|
||||
width | int | null | Request a specific width for the recording. This is not a guarantee that this width will be used, as it depends on the capabilities of the participant's device. Learn more about `MediaRecorder` constraints [here](https://developer.mozilla.org/en-US/docs/Web/API/Media_Streams_API/Constraints#requesting_a_specific_value_for_a_setting).
|
||||
height | int | null | Request a specific height for the recording. This is not a guarantee that this height will be used, as it depends on the capabilities of the participant's device. Learn more about `MediaRecorder` constraints [here](https://developer.mozilla.org/en-US/docs/Web/API/Media_Streams_API/Constraints#requesting_a_specific_value_for_a_setting).
|
||||
|
@ -18,7 +18,7 @@ In addition to the [parameters available in all plugins](../overview/plugins.md#
|
||||
Parameter | Type | Default Value | Description
|
||||
----------|------|---------------|------------
|
||||
device_select_message | html string | `<p>Please select the microphone you would like to use.</p>` | The message to display when the user is presented with a dropdown list of available devices.
|
||||
button_label | sting | 'Use this microphone.' | The label for the select button.
|
||||
button_label | string | 'Use this microphone.' | The label for the select button.
|
||||
|
||||
|
||||
## Data Generated
|
||||
|
@ -234,7 +234,7 @@ Returns a Promise that resolves to an instance of an AudioPlayer class that hold
|
||||
|
||||
#### Description
|
||||
|
||||
Gets an AudioPlayer class instance which has methods that can be used to play or stop audio that can be played with the WebAudio API or an audio object that can be played as HTML5 Audio.
|
||||
Gets an AudioPlayer class instance which has methods that can be used to play or stop audio that can be played with the WebAudio API or an audio object that can be played as HTML5 Audio. By default, jsPsych uses the WebAudio API to play audio in the browser, so if you want to use HTML5 Audio, be sure to put `use_webaudio: false` in the `initJsPsych` object.
|
||||
|
||||
It is strongly recommended that you preload audio files before calling this method. This method will load the files if they are not preloaded, but this may result in delays during the experiment as audio is downloaded.
|
||||
|
||||
@ -289,7 +289,7 @@ See the `audio-keyboard-response` plugin for an example in a fuller context.
|
||||
```javascript
|
||||
const audio = jsPsych.pluginAPI.getAudioPlayer(filepath);
|
||||
|
||||
audio.play();
|
||||
audio.stop();
|
||||
```
|
||||
|
||||
#### Return value
|
||||
@ -298,7 +298,7 @@ Returns nothing.
|
||||
|
||||
#### Description
|
||||
|
||||
Method that belongs to the AudioPlayer class. Stops the audio loaded into the audio buffer of the AudioPlayer instance for a particular file. If the audio is an HTML5 audio object it pauses it. If the audio is a Webaudio API object it stops it.
|
||||
Method that belongs to the AudioPlayer class. Stops the audio loaded into the audio buffer of the AudioPlayer instance for a particular file. If the audio is an HTML5 audio object it pauses it. If the audio is a WebAudio API object it stops it. This will regenerate the audio player, allowing you to call the `play()` method upon it again.
|
||||
|
||||
#### Example
|
||||
|
||||
@ -310,7 +310,6 @@ const audio = await jsPsych.pluginAPI.getAudioPlayer('my-sound.mp3');
|
||||
audio.play();
|
||||
|
||||
audio.stop();
|
||||
|
||||
```
|
||||
|
||||
See the `audio-keyboard-response` plugin for an example in a fuller context.
|
||||
@ -385,63 +384,6 @@ See the `audio-keyboard-response` plugin for an example in a fuller context.
|
||||
|
||||
## Other Media
|
||||
|
||||
### getAudioBuffer
|
||||
|
||||
```javascript
|
||||
jsPsych.pluginAPI.getAudioBuffer(filepath)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
Parameter | Type | Description
|
||||
----------|------|------------
|
||||
filepath | string | The path to the audio file that was preloaded.
|
||||
|
||||
#### Return value
|
||||
|
||||
Returns a Promise that resolves when the audio file loads. Success handler's parameter will be the audio buffer. If the experiment is running using the WebAudio API it will be an AudioBuffer object. Otherwise, it will be an HTML5 Audio object. The failure handler's parameter is the error generated by `preloadAudio`.
|
||||
|
||||
#### Description
|
||||
|
||||
Gets an AudioBuffer that can be played with the WebAudio API or an Audio object that can be played with HTML5 Audio.
|
||||
|
||||
It is strongly recommended that you preload audio files before calling this method. This method will load the files if they are not preloaded, but this may result in delays during the experiment as audio is downloaded.
|
||||
|
||||
#### Examples
|
||||
|
||||
##### HTML 5 Audio
|
||||
|
||||
```javascript
|
||||
jsPsych.pluginAPI.getAudioBuffer('my-sound.mp3')
|
||||
.then(function(audio){
|
||||
audio.play();
|
||||
})
|
||||
.catch(function(err){
|
||||
console.error('Audio file failed to load')
|
||||
})
|
||||
```
|
||||
|
||||
##### WebAudio API
|
||||
|
||||
```javascript
|
||||
var context = jsPsych.pluginAPI.audioContext();
|
||||
|
||||
jsPsych.pluginAPI.getAudioBuffer('my-sound.mp3')
|
||||
.then(function(buffer){
|
||||
audio = context.createBufferSource();
|
||||
audio.buffer = buffer;
|
||||
audio.connect(context.destination);
|
||||
audio.start(context.currentTime);
|
||||
})
|
||||
.catch(function(err){
|
||||
console.error('Audio file failed to load')
|
||||
})
|
||||
```
|
||||
|
||||
See the `audio-keyboard-response` plugin for an example in a fuller context.
|
||||
|
||||
---
|
||||
|
||||
### getAutoPreloadList
|
||||
|
||||
```javascript
|
||||
@ -565,7 +507,7 @@ None.
|
||||
|
||||
#### Description
|
||||
|
||||
Generates a `MediaRecorder` object from provided `MediaStream` and stores this for access via [getCameraRecorder()](#getcamerarecorder).
|
||||
Generates a `MediaRecorder` object from provided `MediaStream` and stores this for access via [`getCameraRecorder()`](#getcamerarecorder).
|
||||
|
||||
#### Example
|
||||
|
||||
@ -598,7 +540,7 @@ None.
|
||||
|
||||
#### Description
|
||||
|
||||
Generates a `MediaRecorder` object from provided `MediaStream` and stores this for access via [getMicrophoneRecorder()](#getmicrophonerecorder).
|
||||
Generates a `MediaRecorder` object from provided `MediaStream` and stores this for access via [`getMicrophoneRecorder()`](#getmicrophonerecorder).
|
||||
|
||||
#### Example
|
||||
|
||||
|
@ -228,42 +228,6 @@ const memoryTestProcedure = {
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
## jsPsych.addNodeToEndOfTimeline
|
||||
|
||||
```javascript
|
||||
jsPsych.addNodeToEndOfTimeline(node_parameters)
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------------- | -------- | ---------------------------------------- |
|
||||
| node_parameters | object | An object defining a timeline. It must have, at a minimum, a `timeline` parameter with a valid timeline array as the value for that parameter. |
|
||||
|
||||
### Return value
|
||||
|
||||
None.
|
||||
|
||||
### Description
|
||||
|
||||
Adds the timeline to the end of the experiment.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
var trial = {
|
||||
type: jsPsychHtmlKeyboardResponse,
|
||||
stimulus: 'This is a new trial.'
|
||||
}
|
||||
|
||||
var new_timeline = {
|
||||
timeline: [trial]
|
||||
}
|
||||
|
||||
jsPsych.addNodeToEndOfTimeline(new_timeline)
|
||||
```
|
||||
|
||||
---
|
||||
## jsPsych.evaluateTimelineVariable
|
||||
|
||||
@ -330,13 +294,14 @@ Returns nothing.
|
||||
|
||||
### Description
|
||||
|
||||
This method tells jsPsych that the current trial is over. It is used in all of the plugins to end the current trial. When the trial ends a few things happen:
|
||||
This method tells jsPsych that the current trial is over. It is used in all of the plugins to end the current trial. When the trial ends, a few things happen:
|
||||
|
||||
* The data is stored using `jsPsych.data.write()`
|
||||
* The on_finish callback function is executed for the trial
|
||||
* The on_trial_finish callback function is executed
|
||||
* The `on_finish` callback function is executed for the trial
|
||||
* The `on_trial_finish` callback function is executed
|
||||
* The display element is cleared, and any timeouts that are pending are cleared.
|
||||
* The progress bar is updated if it is being displayed
|
||||
* The experiment ends if the trial is the last one (and the on_finish callback function is executed).
|
||||
* The experiment ends if the trial is the last one (and the `on_finish` callback function is executed).
|
||||
* The next trial, if one exists, is started.
|
||||
|
||||
### Example
|
||||
@ -391,7 +356,7 @@ Returns the object describing the current trial. The object will contain all of
|
||||
|
||||
### Description
|
||||
|
||||
Get a description of the current trial
|
||||
Get a description of the current trial.
|
||||
|
||||
### Example
|
||||
|
||||
@ -428,6 +393,8 @@ var el = jsPsych.getDisplayElement();
|
||||
// hide the jsPsych display
|
||||
el.style.visibility = 'hidden';
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## jsPsych.getInitSettings
|
||||
@ -493,31 +460,6 @@ alert('You have completed approximately '+progress.percent_complete+'% of the ex
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
## jsPsych.getProgressBarCompleted
|
||||
|
||||
```javascript
|
||||
jsPsych.getProgressBarCompleted()
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
None.
|
||||
|
||||
### Return value
|
||||
|
||||
Returns a value between 0 and 1 representing how full the progress bar currently is.
|
||||
|
||||
### Description
|
||||
|
||||
Used to get the current value of the progress bar. Works for automated and manual control.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
var progress_bar_amount = jsPsych.getProgressBarCompleted();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## jsPsych.getStartTime
|
||||
@ -596,9 +538,9 @@ Pauses the experiment. The experiment will finish the current trial, but will no
|
||||
var trial = {
|
||||
type: jsPsychHtmlKeyboardResponse,
|
||||
stimulus: 'Press p to take a 30 second break. Otherwise, press c to continue immediately.',
|
||||
choices: ['p','c'],
|
||||
choices: ['p', 'c'],
|
||||
on_finish: function(data){
|
||||
if(jsPsych.pluginAPI.compareKeys(data.response, "p")) {
|
||||
if (jsPsych.pluginAPI.compareKeys(data.response, "p")) {
|
||||
jsPsych.pauseExperiment();
|
||||
setTimeout(jsPsych.resumeExperiment, 30000);
|
||||
}
|
||||
@ -674,35 +616,6 @@ jsPsych.run(timeline);
|
||||
|
||||
---
|
||||
|
||||
## jsPsych.setProgressBar
|
||||
|
||||
```javascript
|
||||
jsPsych.setProgressBar(value)
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------- | ---------------------------------------- |
|
||||
| value | numeric | Proprotion (between 0 and 1) to fill the progress bar. |
|
||||
|
||||
|
||||
### Return value
|
||||
|
||||
None.
|
||||
|
||||
### Description
|
||||
|
||||
Set the progress bar to a custom amount. Proportion must be between 0 and 1. Values larger than 1 are treated as 1.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
jsPsych.setProgressBar(0.85);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## jsPsych.timelineVariable
|
||||
|
||||
```javascript
|
||||
|
@ -3,6 +3,7 @@
|
||||
Version 8.x of jsPsych focused on a complete rewrite of the core library to enable new features and make it easier to maintain.
|
||||
Most of the changes in version 8.x are behind the scenes.
|
||||
However, there are some breaking changes that you will need to address in your experiment code in order to upgrade to v8.x.
|
||||
If you are a plugin developer, there are also some special considerations below to factor in when developing your plugins or modifying existing ones.
|
||||
|
||||
This guide is aimed at upgrades from version 7.x to 8.x.
|
||||
If you are using version 6.x or earlier, please follow the [migration guide for v7.x](./migration-v7.md) before trying to upgrade to v8.x.
|
||||
@ -83,7 +84,7 @@ const trial = {
|
||||
```
|
||||
|
||||
The `button_html` parameter can also support different HTML for each button.
|
||||
See the [plugin documentation](https://www.jspsych.org/plugins/jspsych-html-button-response/) for more details.
|
||||
See the [plugin documentation](https://www.jspsych.org/latest/plugins/html-button-response/index.html) for more details.
|
||||
|
||||
## Plugin parameter handling
|
||||
|
||||
@ -104,6 +105,49 @@ Including these properties is not *required* for a plugin to work, but it is rec
|
||||
In version 8.x, jsPsych will throw a warning if a plugin is used that does not have a `version` or `data` property in the `info` object.
|
||||
In version 9.x, we plan to make this a requirement.
|
||||
|
||||
## Changes to the `AudioPlayer` class
|
||||
|
||||
In version 7.x, jsPsych's `pluginAPI` class exposed WebAudio and HTML5 audio APIs through `getAudioBuffer()`. However, this required different implementations done by the developer to account for each API.
|
||||
In version 8.x, we've removed this in favor of `getAudioPlayer()`, which handles both API choices under the hood.
|
||||
|
||||
This change only affects plugin developers. If you want to update to use the new `getAudioPlayer()`, it is recommend that you call this new method using the `await` syntax, which requires an asynchronous `trial` function:
|
||||
```js
|
||||
const audio = await jsPsych.pluginAPI.getAudioPlayer('my-sound.mp3');
|
||||
```
|
||||
|
||||
If you'd like to still use the `.then()` syntax to resolve the Promise generated, you may update it as such:
|
||||
|
||||
Version 7.x:
|
||||
```js
|
||||
this.jsPsych.pluginAPI
|
||||
.getAudioBuffer('my-audio.mp3')
|
||||
.then((audio) => {
|
||||
// call play on audio if HTML5 audio API, create and connect buffer if WebAudio API
|
||||
})
|
||||
.catch((err) =>{
|
||||
// handle error
|
||||
});
|
||||
```
|
||||
|
||||
Version 8.x:
|
||||
```js
|
||||
this.jsPsych.pluginAPI
|
||||
.getAudioPlayer('my-audio.mp3')
|
||||
.then((player) => {
|
||||
// no need to create and connect buffer, can just directly call functions on player
|
||||
})
|
||||
.catch((err) => {
|
||||
// handle error
|
||||
})
|
||||
```
|
||||
|
||||
Along with this, the `start()` and `pause()` functions were removed from the `AudioPlayer` class.
|
||||
You can still call `stop()` upon an audio ending in order to regenerate the `AudioPlayer`, and be able
|
||||
to call `play()` on it again.
|
||||
|
||||
For a general guide on implementation, the [`audio-button-response`](https://github.com/jspsych/jsPsych/blob/main/packages/plugin-audio-button-response/src/index.ts) plugin uses the `await` syntax
|
||||
to handle playing audio.
|
||||
|
||||
## Changes to `finishTrial()`
|
||||
|
||||
When a plugin calls `finishTrial()` or ends via a `return` statement, jsPsych will now automatically clear the display and clear any timeouts that are still pending. This change should only affect plugin developers. If you are using built-in plugins you should not notice any difference.
|
||||
|
45
docs/tutorials/community-tutorials.md
Normal file
45
docs/tutorials/community-tutorials.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Community Tutorials
|
||||
|
||||
If you're interested in learning more about jsPsych, prefer video tutorials, or would like to just see other perspectives on how to use the codebase, we have some community resources that can assist you. If you have an idea for a tutorial to add, if you want to make your own, or if you have found a particularly helpful resource while online, be sure to tell us through our [issues board](https://github.com/jspsych/jsPsych/issues) so we may potentially feature it here!
|
||||
|
||||
## Version 8.x
|
||||
|
||||
Currently there many aren't any tutorials specific to jsPsych version `8.x` due to the relatively recent release date. However, concerning most experiments, the `7.x` tutorials should provide plenty of information. Along with this, our [migration guide](../support/migration-v8.md) can also assist in bridging the gap, though most breaking changes have to do with plugin development, rather than experiment building.
|
||||
|
||||
### University of Edinburgh
|
||||
|
||||
This [tutorial](https://softdev.ppls.ed.ac.uk/online_experiments/index.html) provided by the folks at the University of Edinburgh is primarily used for students learning how to construct online experiments with jsPsych, but there is plenty of helpful information on here for anyone. A [basic tutorial](https://softdev.ppls.ed.ac.uk/online_experiments/02_first.html) is initially provided, along with framework-specific topics like timeline variables and factorial design [covered here](https://softdev.ppls.ed.ac.uk/online_experiments/04_jspsych.html). There are some more advanced tutorials covering [participant and condition numbers](https://softdev.ppls.ed.ac.uk/online_experiments/07_ppt.html), among other tutorials that should give you a deeper understanding of JavaScript and our framework.
|
||||
|
||||
## Version 7.x
|
||||
|
||||
These tutorials use version `7.x` of jsPsych.
|
||||
|
||||
### jamesbrandscience
|
||||
|
||||
This very handy [website](https://jamesbrandscience.github.io/tutorials/) focuses primarily on teaching how to use [Cognition.run](https://www.cognition.run/), a hosting service for jsPsych experiments, in tandem with teaching jsPsych. It is split into an [introduction](https://jamesbrandscience.github.io/jspsych-tutorial/jspsych_1.html) with jsPsych and Cognition.run, the basics of [customizing timelines, CSS, and data processing](https://jamesbrandscience.github.io/jspsych-tutorial/jspsych_2.html), an overview of a [proper experiment](https://jamesbrandscience.github.io/jspsych-tutorial/jspsych_2.html) with the `fullscreen`, `instructions`, and `survey-html-form` plugins, and finally, a tutorial on using [advanced stimuli](https://jamesbrandscience.github.io/jspsych-tutorial/jspsych_4.html), such as audio and images. For those interested in contributing to jsPsych, but have limited knowledge of Git and GitHub, there is also a very helpful [tutorial](https://jamesbrandscience.github.io/tutorials/GitHub_stats_chat/GitHub.html) for that as well.
|
||||
|
||||
### Cognition and Learning Lab @ Purdue University
|
||||
|
||||
Purdue University's Cognition and Learning Lab has a [simple tutorial](https://learninglab.psych.purdue.edu/handbook/programming-guide/getting-started-jspsych-7/) on utilizing jsPsych `7.x`. The rest of the tutorials pertaining to jsPsych on this website are written in jsPsych `6.x`, but some of the information may still be applicable assuming you have a good grasp on how `7.x` works.
|
||||
|
||||
### Crump Lab @ Brooklyn College
|
||||
|
||||
The Psyc 2001 course at Brooklyn College used a combination of [Quarto](https://quarto.org/), an academic blogging workflow in RStudio, along with jsPsych to create tutorials on setting up a [Quarto blog](https://www.crumplab.com/blog/post_887_8_25_22_quartoblog/). While most of the advanced posts on the [course blog](https://www.crumplab.com/psyc2001/blog.html) concern data processing and R, the first few tutorials have videos accompanied for a basic overview on [web development](https://www.crumplab.com/psyc2001/blog/2_Basic_web/), our simple [reaction time tutorial](https://www.crumplab.com/psyc2001/blog/3_jspsych_tutorial/), and programming a [Stroop experiment](https://www.crumplab.com/psyc2001/blog/3_jspsych_tutorial/) in jsPsych.
|
||||
|
||||
### (中文) Resources in Chinese
|
||||
|
||||
Contributor [Shaobin-Jiang](https://github.com/Shaobin-Jiang) has created resources specifically tailored to those who require a Chinese language tutorial, including a full [video tutorial](https://www.bilibili.com/video/BV1Qs4y1y7c9/) and a version of this [documentation](https://shaobin-jiang.github.io/jsPsych-Chinese-Documentation/), in Chinese.
|
||||
|
||||
## Version 6.x
|
||||
|
||||
These tutorials use version `6.x` of jsPsych, and are not very applicable to the modern patterns of jsPsych. If you are to use this for a starting point, make sure to read the tutorials on this website to understand how `8.x` works, watching these videos for any additional information. Some of the content still applies to `8.x` and their corresponding plugins, but a solid understanding of the current framework is required in order to make use of the examples in the videos.
|
||||
|
||||
### YouTube Channel
|
||||
|
||||
A variety of video tutorials are available on [Josh de Leeuw's YouTube channel](https://www.youtube.com/playlist?list=PLnfo1lBY1P2Mf_o6rV5wiqqn92Mw3UTGh). Some tutorials walk through creating a basic version of an entire experiment, like the tutorial on creating a [dichotic listening experiment](https://www.youtube.com/playlist?list=PLnfo1lBY1P2Mf_o6rV5wiqqn92Mw3UTGh) aimed at new users. Others focus on specific features of jsPsych, like how to use [functions as parameters](https://www.youtube.com/watch?v=8-j2aAZ_iOk&list=PLnfo1lBY1P2Mf_o6rV5wiqqn92Mw3UTGh&index=5) to create experiments that change in response to participant input or how to [create a new plugin](https://www.youtube.com/watch?v=XQcsFwAmbiw&list=PLnfo1lBY1P2Mf_o6rV5wiqqn92Mw3UTGh&index=4).
|
||||
|
||||
### Workshops
|
||||
|
||||
**Moving Research Online (2020)**. Recordings from a [Summer 2020 workshop](https://www.movingresearchonline.info) on conducting online research are available on the [workshop's YouTube channel](https://www.youtube.com/channel/UCBZ5F1UysHWlplUNDRwbsWA). [Session 1](https://www.youtube.com/watch?v=BuhfsIFRFe8) provides an overview of jsPsych suitable for brand new users. [Session 3](https://www.youtube.com/watch?v=LP7o0iAALik) covers some more advanced features of jsPsych. This workshop was funded by the National Science Foundation.
|
||||
|
||||
**babySTEP (2021)**. The Centre for Comparative Psycholinguistics (CCP, University of Alberta Department of Linguistics) hosted a two-part jsPsych workshop in 2021 as part of their annual [STEP program](https://ccp.artsrn.ualberta.ca/portfolio/step/). [Day 1](https://drive.google.com/file/d/1_bd_Tz1IoyGaZzuPoR_Qb6Rtd5wg4t4D/view?usp=drive_web) covered the basics of creating a jsPsych experiment, with an emphasis on audio stimuli. [Day 2](https://drive.google.com/file/d/1dIw1xIVY1lCHwFKGRaUnWMguwHfdkbGK/view?usp=drive_web) was organized around pre-submitted questions. The video demonstrates how to create a more complex experiment involving reading a sentence and hearing different audio options for completing the sentences, and answers several questions about timing accuracy, recording participant generated audio, embedding jsPsych into course (or other) websites, and some (non-empirical) advice about attention checks.
|
@ -1,14 +0,0 @@
|
||||
# Video tutorials
|
||||
|
||||
!!! warning
|
||||
Most of these videos are using version `6.x` of jsPsych. Using version `7.x` requires a few changes that are not covered in these videos. We recommend starting with the tutorials on this website to understand how to work with `7.x` and then watching these videos for additional information. Much of the content covered in the videos still applies to `7.x`, but you'll need a solid understanding of how to use `7.x` in order to make use of the examples in the videos.
|
||||
|
||||
## YouTube Channel
|
||||
|
||||
A variety of video tutorials are available on [Josh de Leeuw's YouTube channel](https://www.youtube.com/playlist?list=PLnfo1lBY1P2Mf_o6rV5wiqqn92Mw3UTGh). Some tutorials walk through creating a basic version of an entire experiment, like the tutorial on creating a [dichotic listening experiment](https://www.youtube.com/playlist?list=PLnfo1lBY1P2Mf_o6rV5wiqqn92Mw3UTGh) aimed at new users. Others focus on specific features of jsPsych, like how to use [functions as parameters](https://www.youtube.com/watch?v=8-j2aAZ_iOk&list=PLnfo1lBY1P2Mf_o6rV5wiqqn92Mw3UTGh&index=5) to create experiments that change in response to participant input or how to [create a new plugin](https://www.youtube.com/watch?v=XQcsFwAmbiw&list=PLnfo1lBY1P2Mf_o6rV5wiqqn92Mw3UTGh&index=4).
|
||||
|
||||
## Workshops
|
||||
|
||||
**Moving Research Online (2020)**. Recordings from a [Summer 2020 workshop](https://www.movingresearchonline.info) on conducting online research are available on the [workshop's YouTube channel](https://www.youtube.com/channel/UCBZ5F1UysHWlplUNDRwbsWA). [Session 1](https://www.youtube.com/watch?v=BuhfsIFRFe8) provides an overview of jsPsych suitable for brand new users. [Session 3](https://www.youtube.com/watch?v=LP7o0iAALik) covers some more advanced features of jsPsych. This workshop was funded by the National Science Foundation.
|
||||
|
||||
**babySTEP (2021)**. The Centre for Comparative Psycholinguistics (CCP, University of Alberta Department of Linguistics) hosted a two-part jsPsych workshop in 2021 as part of their annual [STEP program](https://ccp.artsrn.ualberta.ca/portfolio/step/). [Day 1](https://drive.google.com/file/d/1_bd_Tz1IoyGaZzuPoR_Qb6Rtd5wg4t4D/view?usp=drive_web) covered the basics of creating a jsPsych experiment, with an emphasis on audio stimuli. [Day 2](https://drive.google.com/file/d/1dIw1xIVY1lCHwFKGRaUnWMguwHfdkbGK/view?usp=drive_web) was organized around pre-submitted questions. The video demonstrates how to create a more complex experiment involving reading a sentence and hearing different audio options for completing the sentences, and answers several questions about timing accuracy, recording participant generated audio, embedding jsPsych into course (or other) websites, and some (non-empirical) advice about attention checks.
|
@ -32,7 +32,7 @@
|
||||
type: jsPsychHtmlKeyboardResponse,
|
||||
stimulus: ()=>{
|
||||
let html = `<div style="width:100vw; height:100vh; position: relative;">
|
||||
<div style="width:15px; height:15px; border-radius:15px; background-color: #000; position:absolute; top: ${jsPsych.timelineVariable('y')}%; left: ${jsPsych.timelineVariable('x')}%;"></div>
|
||||
<div style="width:15px; height:15px; border-radius:15px; background-color: #000; position:absolute; top: ${jsPsych.evaluateTimelineVariable('y')}%; left: ${jsPsych.evaluateTimelineVariable('x')}%;"></div>
|
||||
</div>`;
|
||||
return html;
|
||||
},
|
||||
|
@ -109,8 +109,8 @@
|
||||
stimulus: function () {
|
||||
return(
|
||||
`<div style="position: relative; width: 400px; height: 400px;">
|
||||
<div style="position: absolute; top:${jsPsych.timelineVariable('top', true)}%; left: ${jsPsych.timelineVariable('left', true)}%">
|
||||
<span id="arrow-target" style="font-size: 40px; transform: translate(-50%, -50%);">${jsPsych.timelineVariable('direction', true) == 'left' ? '⬅' : '➡'}</span>
|
||||
<div style="position: absolute; top:${jsPsych.evaluateTimelineVariable('top', true)}%; left: ${jsPsych.evaluateTimelineVariable('left', true)}%">
|
||||
<span id="arrow-target" style="font-size: 40px; transform: translate(-50%, -50%);">${jsPsych.evaluateTimelineVariable('direction', true) == 'left' ? '⬅' : '➡'}</span>
|
||||
</div>
|
||||
</div>`
|
||||
)
|
||||
|
@ -43,7 +43,7 @@ nav:
|
||||
- Tutorials:
|
||||
- 'The Basics: Hello World': 'tutorials/hello-world.md'
|
||||
- 'Demo Experiment: Simple Reaction Time Task': 'tutorials/rt-task.md'
|
||||
- 'Video Tutorials': 'tutorials/video-tutorials.md'
|
||||
- 'Community Tutorials': 'tutorials/community-tutorials.md'
|
||||
- Overview:
|
||||
- 'Creating an Experiment: The Timeline': 'overview/timeline.md'
|
||||
- 'Plugins': 'overview/plugins.md'
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -16061,7 +16061,7 @@
|
||||
},
|
||||
"packages/config": {
|
||||
"name": "@jspsych/config",
|
||||
"version": "3.2.0",
|
||||
"version": "3.2.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@citation-js/core": "^0.7.14",
|
||||
|
@ -1,5 +1,11 @@
|
||||
# @jspsych/config
|
||||
|
||||
## 3.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#3486](https://github.com/jspsych/jsPsych/pull/3486) [`ad1d854f43c1e25ba988a3aa2a23a8ab22be3535`](https://github.com/jspsych/jsPsych/commit/ad1d854f43c1e25ba988a3aa2a23a8ab22be3535) Thanks [@jadeddelta](https://github.com/jadeddelta)! - remove DOM clearing after each individual test, fixes issues with testing in other repositories
|
||||
|
||||
## 3.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
@ -18,7 +18,6 @@ module.exports.makePackageConfig = (dirname) => {
|
||||
displayName: {
|
||||
name: packageBaseName,
|
||||
color: packageBaseName === "jspsych" ? "white" : "cyanBright",
|
||||
},
|
||||
setupFilesAfterEnv: ["../config/jest.setup.js"],
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -1,3 +0,0 @@
|
||||
global.afterEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jspsych/config",
|
||||
"version": "3.2.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Shared (build) configuration for jsPsych packages",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
@ -586,10 +586,10 @@ describe("Timeline", () => {
|
||||
const variable = new TimelineVariable("x");
|
||||
|
||||
await timeline.run();
|
||||
expect(() => timeline.evaluateTimelineVariable(variable)).toThrowError("");
|
||||
expect(() => timeline.evaluateTimelineVariable(variable)).toThrow();
|
||||
expect(() =>
|
||||
(timeline.children[0] as Timeline).evaluateTimelineVariable(variable)
|
||||
).toThrowError("");
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -524,7 +524,7 @@ describe("Trial", () => {
|
||||
);
|
||||
await expect(
|
||||
createTrial({ type: TestPlugin, requiredComplexNested: {} }).run()
|
||||
).rejects.toThrowError('"requiredComplexNested.requiredChild" parameter');
|
||||
).rejects.toThrow('"requiredComplexNested.requiredChild" parameter');
|
||||
});
|
||||
|
||||
it("errors on missing parameters nested in `COMPLEX` array parameters", async () => {
|
||||
|
@ -21,7 +21,7 @@ describe("standard use of function as parameter", () => {
|
||||
test("parameters can be protected from early evaluation using ParameterType.FUNCTION", async () => {
|
||||
const mock = jest.fn();
|
||||
|
||||
await startTimeline([
|
||||
const { displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "%foo%",
|
||||
@ -31,7 +31,7 @@ describe("standard use of function as parameter", () => {
|
||||
]);
|
||||
|
||||
expect(mock).not.toHaveBeenCalled();
|
||||
await clickTarget(document.querySelector("#finish_cloze_button"));
|
||||
await clickTarget(displayElement.querySelector("#finish_cloze_button"));
|
||||
expect(mock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@ -76,12 +76,12 @@ describe("nested parameters as functions", () => {
|
||||
]);
|
||||
|
||||
expect(displayElement.querySelectorAll("p.jspsych-survey-text").length).toBe(2);
|
||||
await clickTarget(document.querySelector("#jspsych-survey-text-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-text-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("nested parameter can be a function", async () => {
|
||||
const { expectFinished } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyText,
|
||||
questions: [
|
||||
@ -95,18 +95,18 @@ describe("nested parameters as functions", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(document.querySelector("#jspsych-survey-text-0 p.jspsych-survey-text").innerHTML).toBe(
|
||||
expect(displayElement.querySelector("#jspsych-survey-text-0 p.jspsych-survey-text").innerHTML).toBe(
|
||||
"foo"
|
||||
);
|
||||
expect(document.querySelector("#jspsych-survey-text-1 p.jspsych-survey-text").innerHTML).toBe(
|
||||
expect(displayElement.querySelector("#jspsych-survey-text-1 p.jspsych-survey-text").innerHTML).toBe(
|
||||
"bar"
|
||||
);
|
||||
await clickTarget(document.querySelector("#jspsych-survey-text-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-text-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("multiple nested parameters can be functions", async () => {
|
||||
const { expectFinished } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyMultiChoice,
|
||||
questions: [
|
||||
@ -128,11 +128,11 @@ describe("nested parameters as functions", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(document.querySelector("#jspsych-survey-multi-choice-0").innerHTML).toMatch("foo");
|
||||
expect(document.querySelector("#jspsych-survey-multi-choice-0").innerHTML).toMatch("buzz");
|
||||
expect(document.querySelector("#jspsych-survey-multi-choice-1").innerHTML).toMatch("bar");
|
||||
expect(document.querySelector("#jspsych-survey-multi-choice-1").innerHTML).toMatch("one");
|
||||
await clickTarget(document.querySelector("#jspsych-survey-multi-choice-next"));
|
||||
expect(displayElement.querySelector("#jspsych-survey-multi-choice-0").innerHTML).toMatch("foo");
|
||||
expect(displayElement.querySelector("#jspsych-survey-multi-choice-0").innerHTML).toMatch("buzz");
|
||||
expect(displayElement.querySelector("#jspsych-survey-multi-choice-1").innerHTML).toMatch("bar");
|
||||
expect(displayElement.querySelector("#jspsych-survey-multi-choice-1").innerHTML).toMatch("one");
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-multi-choice-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
|
@ -1,23 +1,24 @@
|
||||
import htmlKeyboardResponse from "@jspsych/plugin-html-keyboard-response";
|
||||
import { pressKey, startTimeline } from "@jspsych/test-utils";
|
||||
|
||||
import { initJsPsych } from "../../src";
|
||||
import { initJsPsych, JsPsych } from "../../src";
|
||||
|
||||
describe("automatic progress bar", () => {
|
||||
test("progress bar does not display by default", async () => {
|
||||
await startTimeline([
|
||||
const { jsPsych } = await startTimeline([
|
||||
{
|
||||
type: htmlKeyboardResponse,
|
||||
stimulus: "foo",
|
||||
},
|
||||
]);
|
||||
|
||||
expect(document.querySelector("#jspsych-progressbar-container")).toBeNull();
|
||||
expect(jsPsych.getDisplayContainerElement().querySelector("#jspsych-progressbar-container"))
|
||||
.toBeNull();
|
||||
await pressKey("a");
|
||||
});
|
||||
|
||||
test("progress bar displays when show_progress_bar is true", async () => {
|
||||
await startTimeline(
|
||||
const { jsPsych } = await startTimeline(
|
||||
[
|
||||
{
|
||||
type: htmlKeyboardResponse,
|
||||
@ -27,9 +28,10 @@ describe("automatic progress bar", () => {
|
||||
{ show_progress_bar: true }
|
||||
);
|
||||
|
||||
expect(document.querySelector("#jspsych-progressbar-container").innerHTML).toMatch(
|
||||
'<span>Completion Progress</span><div id="jspsych-progressbar-outer"><div id="jspsych-progressbar-inner" style="width: 0%;"></div></div>'
|
||||
);
|
||||
expect(jsPsych.getDisplayContainerElement().querySelector("#jspsych-progressbar-container").innerHTML)
|
||||
.toMatch(
|
||||
'<span>Completion Progress</span><div id="jspsych-progressbar-outer"><div id="jspsych-progressbar-inner" style="width: 0%;"></div></div>'
|
||||
);
|
||||
});
|
||||
|
||||
test("progress bar automatically updates by default", async () => {
|
||||
@ -38,9 +40,13 @@ describe("automatic progress bar", () => {
|
||||
stimulus: "foo",
|
||||
};
|
||||
|
||||
await startTimeline([trial, trial, trial, trial], { show_progress_bar: true });
|
||||
const { jsPsych } = await startTimeline(
|
||||
[trial, trial, trial, trial],
|
||||
{ show_progress_bar: true }
|
||||
);
|
||||
|
||||
const progressbarElement = document.querySelector<HTMLElement>("#jspsych-progressbar-inner");
|
||||
const progressbarElement =
|
||||
jsPsych.getDisplayContainerElement().querySelector<HTMLElement>("#jspsych-progressbar-inner");
|
||||
|
||||
expect(progressbarElement.style.width).toEqual("0%");
|
||||
await pressKey("a");
|
||||
@ -59,12 +65,13 @@ describe("automatic progress bar", () => {
|
||||
stimulus: "foo",
|
||||
};
|
||||
|
||||
await startTimeline([trial, trial, trial, trial], {
|
||||
const { jsPsych } = await startTimeline([trial, trial, trial, trial], {
|
||||
show_progress_bar: true,
|
||||
auto_update_progress_bar: false,
|
||||
});
|
||||
|
||||
const progressbarElement = document.querySelector<HTMLElement>("#jspsych-progressbar-inner");
|
||||
const progressbarElement =
|
||||
jsPsych.getDisplayContainerElement().querySelector<HTMLElement>("#jspsych-progressbar-inner");
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
expect(progressbarElement.style.width).toEqual("0%");
|
||||
@ -74,7 +81,7 @@ describe("automatic progress bar", () => {
|
||||
});
|
||||
|
||||
test("set `progressBar.progress` manually", async () => {
|
||||
const jsPsych = initJsPsych({
|
||||
const jsPsychObject = initJsPsych({
|
||||
show_progress_bar: true,
|
||||
auto_update_progress_bar: false,
|
||||
});
|
||||
@ -96,9 +103,10 @@ describe("automatic progress bar", () => {
|
||||
},
|
||||
];
|
||||
|
||||
await startTimeline(timeline, jsPsych);
|
||||
const { jsPsych } = await startTimeline(timeline, jsPsychObject);
|
||||
|
||||
const progressbarElement = document.querySelector<HTMLElement>("#jspsych-progressbar-inner");
|
||||
const progressbarElement =
|
||||
jsPsych.getDisplayContainerElement().querySelector<HTMLElement>("#jspsych-progressbar-inner");
|
||||
|
||||
expect(progressbarElement.style.width).toEqual("0%");
|
||||
await pressKey("a");
|
||||
|
@ -400,8 +400,7 @@ describe("timeline variables are correctly evaluated", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// using console.warn instead of error for now. plan is to enable this test with version 8.
|
||||
test.skip("timelineVariable() throws an error when variable doesn't exist", async () => {
|
||||
test("throws an error when variable doesn't exist", async () => {
|
||||
const jsPsych = initJsPsych();
|
||||
const { expectFinished } = await startTimeline(
|
||||
[
|
||||
@ -411,7 +410,7 @@ test.skip("timelineVariable() throws an error when variable doesn't exist", asyn
|
||||
type: htmlKeyboardResponse,
|
||||
stimulus: "foo",
|
||||
on_start: () => {
|
||||
expect(() => jsPsych.evaluateTimelineVariable("c")).toThrowError();
|
||||
expect(() => jsPsych.evaluateTimelineVariable("c")).toThrow();
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -430,7 +429,7 @@ test.skip("timelineVariable() throws an error when variable doesn't exist", asyn
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("timelineVariable() can fetch a variable called 'data'", async () => {
|
||||
test("can fetch a variable called 'data'", async () => {
|
||||
const jsPsych = initJsPsych();
|
||||
const { expectFinished } = await startTimeline(
|
||||
[
|
||||
@ -440,7 +439,7 @@ test("timelineVariable() can fetch a variable called 'data'", async () => {
|
||||
type: htmlKeyboardResponse,
|
||||
stimulus: "foo",
|
||||
on_start: () => {
|
||||
expect(() => jsPsych.evaluateTimelineVariable("data")).not.toThrowError();
|
||||
expect(() => jsPsych.evaluateTimelineVariable("data")).not.toThrow();
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -7,17 +7,17 @@ jest.useFakeTimers();
|
||||
|
||||
describe("data conversion to csv", () => {
|
||||
test("survey-text data response object is correctly converted", async () => {
|
||||
const { getData } = await startTimeline([
|
||||
const { getData, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyText,
|
||||
questions: [{ prompt: "Q1" }, { prompt: "Q2" }],
|
||||
},
|
||||
]);
|
||||
|
||||
document.querySelector<HTMLInputElement>("#input-0").value = "Response 1";
|
||||
document.querySelector<HTMLInputElement>("#input-1").value = "Response 2";
|
||||
displayElement.querySelector<HTMLInputElement>("#input-0").value = "Response 1";
|
||||
displayElement.querySelector<HTMLInputElement>("#input-1").value = "Response 2";
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-survey-text-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-text-next"));
|
||||
|
||||
expect(
|
||||
getData()
|
||||
@ -62,7 +62,7 @@ describe("data conversion to csv", () => {
|
||||
});
|
||||
|
||||
test("survey-multi-select response array is correctly converted", async () => {
|
||||
const { getHTML, getData } = await startTimeline([
|
||||
const { getHTML, getData, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyMultiSelect,
|
||||
questions: [{ prompt: "foo", options: ["fuzz", "bizz", "bar"], name: "q" }],
|
||||
@ -70,9 +70,9 @@ describe("data conversion to csv", () => {
|
||||
]);
|
||||
|
||||
expect(getHTML()).toMatch("foo");
|
||||
await clickTarget(document.querySelector("#jspsych-survey-multi-select-response-0-0"));
|
||||
await clickTarget(document.querySelector("#jspsych-survey-multi-select-response-0-1"));
|
||||
await clickTarget(document.querySelector("#jspsych-survey-multi-select-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-multi-select-response-0-0"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-multi-select-response-0-1"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-multi-select-next"));
|
||||
expect(getHTML()).toBe("");
|
||||
|
||||
expect(
|
||||
|
@ -8,17 +8,17 @@ jest.useFakeTimers();
|
||||
|
||||
describe("data conversion to json", () => {
|
||||
test("survey-text data response object is correctly converted", async () => {
|
||||
const { getData } = await startTimeline([
|
||||
const { getData, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyText,
|
||||
questions: [{ prompt: "Q1" }, { prompt: "Q2" }],
|
||||
},
|
||||
]);
|
||||
|
||||
document.querySelector<HTMLInputElement>("#input-0").value = "Response 1";
|
||||
document.querySelector<HTMLInputElement>("#input-1").value = "Response 2";
|
||||
displayElement.querySelector<HTMLInputElement>("#input-0").value = "Response 1";
|
||||
displayElement.querySelector<HTMLInputElement>("#input-1").value = "Response 2";
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-survey-text-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-text-next"));
|
||||
|
||||
expect(
|
||||
getData()
|
||||
@ -71,7 +71,7 @@ describe("data conversion to json", () => {
|
||||
});
|
||||
|
||||
test("survey-multi-select response array is correctly converted", async () => {
|
||||
const { getHTML, getData } = await startTimeline([
|
||||
const { getHTML, getData, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyMultiSelect,
|
||||
questions: [{ prompt: "foo", options: ["fuzz", "bizz", "bar"], name: "q" }],
|
||||
@ -79,9 +79,9 @@ describe("data conversion to json", () => {
|
||||
]);
|
||||
|
||||
expect(getHTML()).toMatch("foo");
|
||||
await clickTarget(document.querySelector("#jspsych-survey-multi-select-response-0-0"));
|
||||
await clickTarget(document.querySelector("#jspsych-survey-multi-select-response-0-1"));
|
||||
await clickTarget(document.querySelector("#jspsych-survey-multi-select-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-multi-select-response-0-0"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-multi-select-response-0-1"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-multi-select-next"));
|
||||
expect(getHTML()).toBe("");
|
||||
|
||||
expect(
|
||||
|
@ -65,7 +65,7 @@ describe("Trial parameters in the data", () => {
|
||||
test("Arrayed objects work with save_trial_parameters ", async () => {
|
||||
const questions = [{ prompt: "foo" }, { prompt: "bar" }];
|
||||
|
||||
const { getData } = await startTimeline([
|
||||
const { getData, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyText,
|
||||
questions,
|
||||
@ -75,7 +75,7 @@ describe("Trial parameters in the data", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-survey-text-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-text-next"));
|
||||
|
||||
const data = getData().values()[0];
|
||||
expect(data.questions[0].prompt).toBe(questions[0].prompt);
|
||||
@ -96,7 +96,7 @@ describe("Trial parameters in the data", () => {
|
||||
return html;
|
||||
};
|
||||
|
||||
const { getData } = await startTimeline([
|
||||
const { getData, displayElement } = await startTimeline([
|
||||
{
|
||||
type: reconstruction,
|
||||
stim_function: sample_function,
|
||||
@ -107,7 +107,7 @@ describe("Trial parameters in the data", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
await clickTarget(document.querySelector("button"));
|
||||
await clickTarget(displayElement.querySelector("button"));
|
||||
|
||||
expect(getData().values()[0].stim_function).toBe(sample_function.toString());
|
||||
});
|
||||
|
@ -198,7 +198,7 @@ describe("randomInt", () => {
|
||||
test("setting upper < lower throws an error", () => {
|
||||
expect(() => {
|
||||
randomInt(1, 0);
|
||||
}).toThrowError();
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -273,9 +273,9 @@ describe("audio-button-response", () => {
|
||||
use_webaudio: false,
|
||||
});
|
||||
|
||||
await startTimeline(timeline, jsPsych);
|
||||
const { displayElement } = await startTimeline(timeline, jsPsych);
|
||||
|
||||
const btns = document.querySelectorAll(".jspsych-html-button-response-button button");
|
||||
const btns = displayElement.querySelectorAll(".jspsych-html-button-response-button button");
|
||||
|
||||
for (let i = 0; i < btns.length; i++) {
|
||||
expect(btns[i].getAttribute("disabled")).toBe(true);
|
||||
|
@ -4,13 +4,18 @@ import cloze from ".";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
const getInputElementById = (id: string) => document.getElementById(id) as HTMLInputElement;
|
||||
const getInputElementById = (
|
||||
id: string,
|
||||
displayElement: HTMLElement
|
||||
) => displayElement.querySelector("#" + id) as HTMLInputElement;
|
||||
|
||||
const clickFinishButton = () => clickTarget(document.querySelector("#finish_cloze_button"));
|
||||
const clickFinishButton = (
|
||||
displayElement: HTMLElement
|
||||
) => clickTarget(displayElement.querySelector("#finish_cloze_button"));
|
||||
|
||||
describe("cloze", () => {
|
||||
test("displays cloze", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
@ -21,12 +26,12 @@ describe("cloze", () => {
|
||||
'<div class="cloze">This is a <input type="text" id="input0" value=""> text.</div>'
|
||||
);
|
||||
|
||||
await clickFinishButton();
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("displays default button text", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
@ -37,12 +42,12 @@ describe("cloze", () => {
|
||||
'<button class="jspsych-html-button-response-button" type="button" id="finish_cloze_button">OK</button>'
|
||||
);
|
||||
|
||||
await clickFinishButton();
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("displays custom button text", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
@ -54,24 +59,24 @@ describe("cloze", () => {
|
||||
'<button class="jspsych-html-button-response-button" type="button" id="finish_cloze_button">Next</button>'
|
||||
);
|
||||
|
||||
await clickFinishButton();
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("ends trial on button click when using default settings, i.e. answers are not checked", async () => {
|
||||
const { expectFinished } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
},
|
||||
]);
|
||||
|
||||
await clickFinishButton();
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("ends trial on button click when answers are checked and correct", async () => {
|
||||
const { expectFinished } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
@ -79,13 +84,13 @@ describe("cloze", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElementById("input0").value = "cloze";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "cloze";
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("ends trial on button click when answers are checked and correct without case sensitivity", async () => {
|
||||
const { expectFinished } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
@ -94,13 +99,13 @@ describe("cloze", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElementById("input0").value = "CLOZE";
|
||||
clickTarget(document.querySelector("#finish_cloze_button"));
|
||||
getInputElementById("input0", displayElement).value = "CLOZE";
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("ends trial on button click when all answers are checked for completion and are complete", async () => {
|
||||
const { expectFinished } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
@ -108,13 +113,13 @@ describe("cloze", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElementById("input0").value = "filler";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "filler";
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("does not end trial on button click when answers are checked and not correct or missing", async () => {
|
||||
const { expectRunning, expectFinished } = await startTimeline([
|
||||
const { expectRunning, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
@ -122,23 +127,23 @@ describe("cloze", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElementById("input0").value = "some wrong answer";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "some wrong answer";
|
||||
await clickFinishButton(displayElement);
|
||||
await expectRunning();
|
||||
|
||||
getInputElementById("input0").value = "";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "";
|
||||
await clickFinishButton(displayElement);
|
||||
await expectRunning();
|
||||
|
||||
getInputElementById("input0").value = "cloze";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "cloze";
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("does not call mistake function on button click when answers are checked and correct", async () => {
|
||||
const mistakeFn = jest.fn();
|
||||
|
||||
const { expectFinished } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
@ -147,8 +152,8 @@ describe("cloze", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElementById("input0").value = "cloze";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "cloze";
|
||||
await clickFinishButton(displayElement);
|
||||
expect(mistakeFn).not.toHaveBeenCalled();
|
||||
|
||||
await expectFinished();
|
||||
@ -157,7 +162,7 @@ describe("cloze", () => {
|
||||
test("does not call mistake function on button click when answers are checked for completion and are complete", async () => {
|
||||
const mistakeFn = jest.fn();
|
||||
|
||||
const { expectFinished } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
@ -166,8 +171,8 @@ describe("cloze", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElementById("input0").value = "cloze";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "cloze";
|
||||
await clickFinishButton(displayElement);
|
||||
expect(mistakeFn).not.toHaveBeenCalled();
|
||||
|
||||
await expectFinished();
|
||||
@ -176,7 +181,7 @@ describe("cloze", () => {
|
||||
test("calls mistake function on button click when answers are checked and not correct or missing", async () => {
|
||||
const mistakeFn = jest.fn();
|
||||
|
||||
const { expectFinished } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text.",
|
||||
@ -185,25 +190,25 @@ describe("cloze", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElementById("input0").value = "some wrong answer";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "some wrong answer";
|
||||
await clickFinishButton(displayElement);
|
||||
expect(mistakeFn).toHaveBeenCalled();
|
||||
|
||||
mistakeFn.mockReset();
|
||||
|
||||
getInputElementById("input0").value = "";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "";
|
||||
await clickFinishButton(displayElement);
|
||||
expect(mistakeFn).toHaveBeenCalled();
|
||||
|
||||
getInputElementById("input0").value = "cloze";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "cloze";
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("calls mistake function on button click when answers are checked and do not belong to a multiple answer blank", async () => {
|
||||
const mistakeFn = jest.fn();
|
||||
|
||||
const { expectFinished } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze/jspsych% text.",
|
||||
@ -212,26 +217,26 @@ describe("cloze", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElementById("input0").value = "not fitting in answer";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "not fitting in answer";
|
||||
await clickFinishButton(displayElement);
|
||||
expect(mistakeFn).toHaveBeenCalled();
|
||||
|
||||
getInputElementById("input0").value = "cloze";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "cloze";
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("response data is stored as an array", async () => {
|
||||
const { getData, expectFinished } = await startTimeline([
|
||||
const { getData, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: cloze,
|
||||
text: "This is a %cloze% text. Here is another cloze response box %%.",
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElementById("input0").value = "cloze1";
|
||||
getInputElementById("input1").value = "cloze2";
|
||||
await clickFinishButton();
|
||||
getInputElementById("input0", displayElement).value = "cloze1";
|
||||
getInputElementById("input1", displayElement).value = "cloze2";
|
||||
await clickFinishButton(displayElement);
|
||||
await expectFinished();
|
||||
|
||||
const data = getData().values()[0].response;
|
||||
|
@ -12,7 +12,7 @@ describe("fullscreen plugin", () => {
|
||||
});
|
||||
|
||||
test("launches fullscreen mode by default", async () => {
|
||||
await startTimeline([
|
||||
const { displayElement } = await startTimeline([
|
||||
{
|
||||
type: fullscreen,
|
||||
delay_after: 0,
|
||||
@ -20,12 +20,12 @@ describe("fullscreen plugin", () => {
|
||||
]);
|
||||
|
||||
expect(document.documentElement.requestFullscreen).not.toHaveBeenCalled();
|
||||
await clickTarget(document.querySelector("#jspsych-fullscreen-btn"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-fullscreen-btn"));
|
||||
expect(document.documentElement.requestFullscreen).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("records RT of click", async () => {
|
||||
const { getData, expectFinished } = await startTimeline([
|
||||
const { getData, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: fullscreen,
|
||||
delay_after: 0,
|
||||
@ -34,7 +34,7 @@ describe("fullscreen plugin", () => {
|
||||
|
||||
expect(document.documentElement.requestFullscreen).not.toHaveBeenCalled();
|
||||
jest.advanceTimersByTime(1000);
|
||||
clickTarget(document.querySelector("#jspsych-fullscreen-btn"));
|
||||
clickTarget(displayElement.querySelector("#jspsych-fullscreen-btn"));
|
||||
expect(document.documentElement.requestFullscreen).toHaveBeenCalled();
|
||||
jest.runAllTimers();
|
||||
await expectFinished();
|
||||
|
@ -142,7 +142,7 @@ describe("html-button-response", () => {
|
||||
});
|
||||
|
||||
test("buttons should be disabled first and then enabled after enable_button_after is set", async () => {
|
||||
const { getHTML } = await startTimeline([
|
||||
const { displayElement } = await startTimeline([
|
||||
{
|
||||
type: htmlButtonResponse,
|
||||
stimulus: "this is html",
|
||||
@ -151,7 +151,7 @@ describe("html-button-response", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const btns = document.querySelectorAll("div#jspsych-html-button-response-btngroup button");
|
||||
const btns = displayElement.querySelectorAll("div#jspsych-html-button-response-btngroup button");
|
||||
expect(btns.length).toBeGreaterThan(0);
|
||||
|
||||
for (let i = 0; i < btns.length; i++) {
|
||||
|
@ -113,7 +113,7 @@ describe("html-keyboard-response", () => {
|
||||
});
|
||||
|
||||
test("class should say responded when key is pressed", async () => {
|
||||
const { getHTML, expectRunning } = await startTimeline([
|
||||
const { getHTML, expectRunning, displayElement } = await startTimeline([
|
||||
{
|
||||
type: htmlKeyboardResponse,
|
||||
stimulus: "this is html",
|
||||
@ -128,7 +128,7 @@ describe("html-keyboard-response", () => {
|
||||
|
||||
await pressKey("f");
|
||||
|
||||
expect(document.querySelector("#jspsych-html-keyboard-response-stimulus").className).toBe(
|
||||
expect(displayElement.querySelector("#jspsych-html-keyboard-response-stimulus").className).toBe(
|
||||
" responded"
|
||||
);
|
||||
|
||||
|
@ -130,7 +130,7 @@ describe("html-slider-response", () => {
|
||||
});
|
||||
|
||||
test("should end trial when button is clicked", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: htmlSliderResponse,
|
||||
stimulus: "this is html",
|
||||
@ -144,7 +144,7 @@ describe("html-slider-response", () => {
|
||||
'<div id="jspsych-html-slider-response-stimulus">this is html</div>'
|
||||
);
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-html-slider-response-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-html-slider-response-next"));
|
||||
|
||||
await expectFinished();
|
||||
});
|
||||
|
@ -15,7 +15,6 @@ describe("image-button-response", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
// expect(getHTML()).toContain('<img ');
|
||||
expect(getHTML()).toMatchInlineSnapshot(
|
||||
'"<img src="../media/blue.png" id="jspsych-image-button-response-stimulus"><div id="jspsych-image-button-response-btngroup" class="jspsych-btn-group-grid" style="grid-template-columns: repeat(1, 1fr); grid-template-rows: repeat(1, 1fr);"><button class="jspsych-btn" data-choice="0">button-choice</button></div>"'
|
||||
);
|
||||
@ -68,7 +67,7 @@ describe("image-button-response", () => {
|
||||
});
|
||||
|
||||
test("should hide stimulus if stimulus-duration is set", async () => {
|
||||
const { getHTML, displayElement } = await startTimeline([
|
||||
const { displayElement } = await startTimeline([
|
||||
{
|
||||
type: imageButtonResponse,
|
||||
stimulus: "../media/blue.png",
|
||||
@ -105,7 +104,7 @@ describe("image-button-response", () => {
|
||||
});
|
||||
|
||||
test("should end trial when button is clicked", async () => {
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
const { expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: imageButtonResponse,
|
||||
stimulus: "../media/blue.png",
|
||||
@ -138,7 +137,7 @@ describe("image-button-response", () => {
|
||||
});
|
||||
|
||||
test("delay enable button", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { displayElement } = await startTimeline([
|
||||
{
|
||||
type: imageButtonResponse,
|
||||
stimulus: "../media/blue.png",
|
||||
@ -148,7 +147,7 @@ describe("image-button-response", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const btns = document.querySelectorAll(".jspsych-image-button-response-button button");
|
||||
const btns = displayElement.querySelectorAll(".jspsych-image-button-response-button button");
|
||||
|
||||
for (let i = 0; i < btns.length; i++) {
|
||||
expect(btns[i].getAttribute("disabled")).toBe("disabled");
|
||||
@ -200,7 +199,7 @@ describe("image-button-response simulation", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const { expectFinished, expectRunning, getHTML, getData } = await simulateTimeline(
|
||||
const { expectFinished, expectRunning, getData } = await simulateTimeline(
|
||||
timeline,
|
||||
"visual"
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ jest.useFakeTimers();
|
||||
|
||||
describe("image-slider-response", () => {
|
||||
test("displays image stimulus", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: imageSliderResponse,
|
||||
stimulus: "../media/blue.png",
|
||||
@ -19,12 +19,12 @@ describe("image-slider-response", () => {
|
||||
expect(getHTML()).toContain(
|
||||
'<div id="jspsych-image-slider-response-stimulus"><img src="../media/blue.png"'
|
||||
);
|
||||
await clickTarget(document.querySelector("#jspsych-image-slider-response-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-image-slider-response-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("displays labels", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: imageSliderResponse,
|
||||
stimulus: "../media/blue.png",
|
||||
@ -37,12 +37,12 @@ describe("image-slider-response", () => {
|
||||
expect(getHTML()).toContain('<span style="text-align: center; font-size: 80%;">left</span>');
|
||||
expect(getHTML()).toContain('<span style="text-align: center; font-size: 80%;">right</span>');
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-image-slider-response-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-image-slider-response-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("displays button label", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: imageSliderResponse,
|
||||
stimulus: "../media/blue.png",
|
||||
@ -56,7 +56,7 @@ describe("image-slider-response", () => {
|
||||
'<button id="jspsych-image-slider-response-next" class="jspsych-btn">button</button>'
|
||||
);
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-image-slider-response-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-image-slider-response-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
@ -82,12 +82,12 @@ describe("image-slider-response", () => {
|
||||
expect(responseElement.max).toBe("10");
|
||||
expect(responseElement.step).toBe("2");
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-image-slider-response-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-image-slider-response-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("prompt should append to bottom of stimulus", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: imageSliderResponse,
|
||||
stimulus: "../media/blue.png",
|
||||
@ -100,7 +100,7 @@ describe("image-slider-response", () => {
|
||||
|
||||
expect(getHTML()).toContain("<p>This is a prompt</p>");
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-image-slider-response-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-image-slider-response-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
@ -123,7 +123,7 @@ describe("image-slider-response", () => {
|
||||
jest.advanceTimersByTime(500);
|
||||
expect(stimulusElement.style.visibility).toContain("hidden");
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-image-slider-response-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-image-slider-response-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
@ -148,7 +148,7 @@ describe("image-slider-response", () => {
|
||||
});
|
||||
|
||||
test("should end trial when button is clicked", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: imageSliderResponse,
|
||||
stimulus: "../media/blue.png",
|
||||
@ -163,7 +163,7 @@ describe("image-slider-response", () => {
|
||||
'<div id="jspsych-image-slider-response-stimulus"><img src="../media/blue.png"'
|
||||
);
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-image-slider-response-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-image-slider-response-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
});
|
||||
|
@ -6,7 +6,7 @@ jest.useFakeTimers();
|
||||
|
||||
describe("maxdiff plugin", () => {
|
||||
test("returns appropriate response with randomization", async () => {
|
||||
const { getData, expectFinished } = await startTimeline([
|
||||
const { getData, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: maxdiff,
|
||||
alternatives: ["a", "b", "c", "d"],
|
||||
@ -15,10 +15,10 @@ describe("maxdiff plugin", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
document.querySelector<HTMLInputElement>('input[data-name="0"][name="left"]').checked = true;
|
||||
document.querySelector<HTMLInputElement>('input[data-name="1"][name="right"]').checked = true;
|
||||
displayElement.querySelector<HTMLInputElement>('input[data-name="0"][name="left"]').checked = true;
|
||||
displayElement.querySelector<HTMLInputElement>('input[data-name="1"][name="right"]').checked = true;
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-maxdiff-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-maxdiff-next"));
|
||||
await expectFinished();
|
||||
|
||||
expect(getData().values()[0].response).toEqual({ left: "a", right: "b" });
|
||||
|
@ -4,28 +4,31 @@ import serialReactionTimeMouse from ".";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
const getCellElement = (cellId: string) =>
|
||||
document.querySelector(`#jspsych-serial-reaction-time-stimulus-cell-${cellId}`) as HTMLElement;
|
||||
const getCellElement = (
|
||||
cellId: string,
|
||||
displayElement: HTMLElement
|
||||
) =>
|
||||
displayElement.querySelector(`#jspsych-serial-reaction-time-stimulus-cell-${cellId}`) as HTMLElement;
|
||||
|
||||
describe("serial-reaction-time-mouse plugin", () => {
|
||||
test("default behavior", async () => {
|
||||
const { getHTML, expectFinished } = await startTimeline([
|
||||
const { getHTML, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: serialReactionTimeMouse,
|
||||
target: [0, 0],
|
||||
},
|
||||
]);
|
||||
|
||||
expect(getCellElement("0-0").style.backgroundColor).toBe("rgb(153, 153, 153)");
|
||||
expect(getCellElement("0-1").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-2").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-3").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-0", displayElement).style.backgroundColor).toBe("rgb(153, 153, 153)");
|
||||
expect(getCellElement("0-1", displayElement).style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-2", displayElement).style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-3", displayElement).style.backgroundColor).toBe("");
|
||||
|
||||
mouseDownMouseUpTarget(getCellElement("0-1"));
|
||||
mouseDownMouseUpTarget(getCellElement("0-1", displayElement));
|
||||
|
||||
expect(getHTML()).not.toBe("");
|
||||
|
||||
mouseDownMouseUpTarget(getCellElement("0-0"));
|
||||
mouseDownMouseUpTarget(getCellElement("0-0", displayElement));
|
||||
|
||||
await expectFinished();
|
||||
});
|
||||
|
@ -4,22 +4,24 @@ import serialReactionTime from ".";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
const getCellElement = (cellId: string) =>
|
||||
document.querySelector(`#jspsych-serial-reaction-time-stimulus-cell-${cellId}`) as HTMLElement;
|
||||
const getCellElement = (
|
||||
cellId: string,
|
||||
displayElement: HTMLElement
|
||||
) => displayElement.querySelector(`#jspsych-serial-reaction-time-stimulus-cell-${cellId}`) as HTMLElement;
|
||||
|
||||
describe("serial-reaction-time plugin", () => {
|
||||
test("default behavior", async () => {
|
||||
const { expectFinished, getData } = await startTimeline([
|
||||
const { expectFinished, getData, displayElement } = await startTimeline([
|
||||
{
|
||||
type: serialReactionTime,
|
||||
target: [0, 0],
|
||||
},
|
||||
]);
|
||||
|
||||
expect(getCellElement("0-0").style.backgroundColor).toBe("rgb(153, 153, 153)");
|
||||
expect(getCellElement("0-1").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-2").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-3").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-0", displayElement).style.backgroundColor).toBe("rgb(153, 153, 153)");
|
||||
expect(getCellElement("0-1", displayElement).style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-2", displayElement).style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-3", displayElement).style.backgroundColor).toBe("");
|
||||
|
||||
await pressKey("3");
|
||||
|
||||
@ -28,7 +30,7 @@ describe("serial-reaction-time plugin", () => {
|
||||
});
|
||||
|
||||
test("response ends trial is false", async () => {
|
||||
const { getHTML, expectFinished, getData } = await startTimeline([
|
||||
const { getHTML, expectFinished, getData, displayElement } = await startTimeline([
|
||||
{
|
||||
type: serialReactionTime,
|
||||
target: [0, 0],
|
||||
@ -37,10 +39,10 @@ describe("serial-reaction-time plugin", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(getCellElement("0-0").style.backgroundColor).toBe("rgb(153, 153, 153)");
|
||||
expect(getCellElement("0-1").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-2").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-3").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-0", displayElement).style.backgroundColor).toBe("rgb(153, 153, 153)");
|
||||
expect(getCellElement("0-1", displayElement).style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-2", displayElement).style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-3", displayElement).style.backgroundColor).toBe("");
|
||||
|
||||
await pressKey("3");
|
||||
|
||||
@ -53,7 +55,7 @@ describe("serial-reaction-time plugin", () => {
|
||||
});
|
||||
|
||||
test("responses are scored correctly", async () => {
|
||||
const { getHTML, expectFinished, getData } = await startTimeline([
|
||||
const { displayElement, expectFinished, getData } = await startTimeline([
|
||||
{
|
||||
type: serialReactionTime,
|
||||
target: [0, 0],
|
||||
@ -64,19 +66,19 @@ describe("serial-reaction-time plugin", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(getCellElement("0-0").style.backgroundColor).toBe("rgb(153, 153, 153)");
|
||||
expect(getCellElement("0-1").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-2").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-3").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-0", displayElement).style.backgroundColor).toBe("rgb(153, 153, 153)");
|
||||
expect(getCellElement("0-1", displayElement).style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-2", displayElement).style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-3", displayElement).style.backgroundColor).toBe("");
|
||||
|
||||
await pressKey("3");
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(getCellElement("0-0").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-1").style.backgroundColor).toBe("rgb(153, 153, 153)");
|
||||
expect(getCellElement("0-2").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-3").style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-0", displayElement).style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-1", displayElement).style.backgroundColor).toBe("rgb(153, 153, 153)");
|
||||
expect(getCellElement("0-2", displayElement).style.backgroundColor).toBe("");
|
||||
expect(getCellElement("0-3", displayElement).style.backgroundColor).toBe("");
|
||||
|
||||
await pressKey("3");
|
||||
|
||||
|
@ -22,7 +22,7 @@ describe("survey-html-form plugin", () => {
|
||||
'#jspsych-survey-html-form input[name="second"]'
|
||||
)[0].value = TEST_VALUE;
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-survey-html-form-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-html-form-next"));
|
||||
|
||||
await expectFinished();
|
||||
|
||||
|
@ -4,13 +4,16 @@ import surveyLikert from ".";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
const selectInput = (name: string, value: string) =>
|
||||
document.querySelector(`input[name="${name}"][value="${value}"]`) as HTMLInputElement;
|
||||
const selectInput = (
|
||||
name: string,
|
||||
value: string,
|
||||
displayElement: HTMLElement
|
||||
) => displayElement.querySelector(`input[name="${name}"][value="${value}"]`) as HTMLInputElement;
|
||||
|
||||
describe("survey-likert plugin", () => {
|
||||
test("data are logged with the right question when randomize order is true", async () => {
|
||||
const scale = ["a", "b", "c", "d", "e"];
|
||||
const { getData, expectFinished } = await startTimeline([
|
||||
const { getData, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyLikert,
|
||||
questions: [
|
||||
@ -24,13 +27,13 @@ describe("survey-likert plugin", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
selectInput("Q0", "0").checked = true;
|
||||
selectInput("Q1", "1").checked = true;
|
||||
selectInput("Q2", "2").checked = true;
|
||||
selectInput("Q3", "3").checked = true;
|
||||
selectInput("Q4", "4").checked = true;
|
||||
selectInput("Q0", "0", displayElement).checked = true;
|
||||
selectInput("Q1", "1", displayElement).checked = true;
|
||||
selectInput("Q2", "2", displayElement).checked = true;
|
||||
selectInput("Q3", "3", displayElement).checked = true;
|
||||
selectInput("Q4", "4", displayElement).checked = true;
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-survey-likert-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-likert-next"));
|
||||
|
||||
await expectFinished();
|
||||
|
||||
|
@ -5,8 +5,12 @@ import surveyMultiChoice from ".";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
const getInputElement = (choiceId: number, value: string) =>
|
||||
document.querySelector(
|
||||
const getInputElement = (
|
||||
choiceId: number,
|
||||
value: string,
|
||||
displayElement: HTMLElement
|
||||
) =>
|
||||
displayElement.querySelector(
|
||||
`#jspsych-survey-multi-choice-${choiceId} input[value="${value}"]`
|
||||
) as HTMLInputElement;
|
||||
|
||||
@ -24,7 +28,7 @@ describe("survey-multi-choice plugin", () => {
|
||||
const jsPsychInst = initJsPsych({ display_element: innerDiv })
|
||||
const options = ["a", "b", "c"];
|
||||
|
||||
const { getData, expectFinished } = await startTimeline([
|
||||
const { displayElement, expectFinished } = await startTimeline([
|
||||
{
|
||||
type: surveyMultiChoice,
|
||||
questions: [
|
||||
@ -34,16 +38,14 @@ describe("survey-multi-choice plugin", () => {
|
||||
},
|
||||
], jsPsychInst);
|
||||
|
||||
getInputElement(0, "a").checked = true;
|
||||
await clickTarget(document.querySelector("#jspsych-survey-multi-choice-next"));
|
||||
getInputElement(0, "a", displayElement).checked = true;
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-multi-choice-next"));
|
||||
await expectFinished();
|
||||
|
||||
})
|
||||
|
||||
|
||||
test("data are logged with the right question when randomize order is true", async () => {
|
||||
var scale = ["a", "b", "c", "d", "e"];
|
||||
const { getData, expectFinished } = await startTimeline([
|
||||
const { getData, expectFinished, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyMultiChoice,
|
||||
questions: [
|
||||
@ -57,13 +59,13 @@ describe("survey-multi-choice plugin", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElement(0, "a").checked = true;
|
||||
getInputElement(1, "b").checked = true;
|
||||
getInputElement(2, "c").checked = true;
|
||||
getInputElement(3, "d").checked = true;
|
||||
getInputElement(4, "e").checked = true;
|
||||
getInputElement(0, "a", displayElement).checked = true;
|
||||
getInputElement(1, "b", displayElement).checked = true;
|
||||
getInputElement(2, "c", displayElement).checked = true;
|
||||
getInputElement(3, "d", displayElement).checked = true;
|
||||
getInputElement(4, "e", displayElement).checked = true;
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-survey-multi-choice-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-multi-choice-next"));
|
||||
|
||||
await expectFinished();
|
||||
|
||||
|
@ -4,8 +4,12 @@ import surveyMultiSelect from ".";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
const getInputElement = (selectId: number, value: string) =>
|
||||
document.querySelector(
|
||||
const getInputElement = (
|
||||
selectId: number,
|
||||
value: string,
|
||||
displayElement: HTMLElement
|
||||
) =>
|
||||
displayElement.querySelector(
|
||||
`#jspsych-survey-multi-select-${selectId} input[value="${value}"]`
|
||||
) as HTMLInputElement;
|
||||
|
||||
@ -43,7 +47,7 @@ describe("survey-multi-select plugin", () => {
|
||||
|
||||
test("data are logged with the right question when randomize order is true", async () => {
|
||||
const scale = ["a", "b", "c", "d", "e"];
|
||||
const { expectFinished, getData } = await startTimeline([
|
||||
const { expectFinished, getData, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyMultiSelect,
|
||||
questions: [
|
||||
@ -57,13 +61,13 @@ describe("survey-multi-select plugin", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
getInputElement(0, "a").checked = true;
|
||||
getInputElement(1, "b").checked = true;
|
||||
getInputElement(2, "c").checked = true;
|
||||
getInputElement(3, "d").checked = true;
|
||||
getInputElement(4, "e").checked = true;
|
||||
getInputElement(0, "a", displayElement).checked = true;
|
||||
getInputElement(1, "b", displayElement).checked = true;
|
||||
getInputElement(2, "c", displayElement).checked = true;
|
||||
getInputElement(3, "d", displayElement).checked = true;
|
||||
getInputElement(4, "e", displayElement).checked = true;
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-survey-multi-select-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-multi-select-next"));
|
||||
|
||||
await expectFinished();
|
||||
|
||||
|
@ -2,8 +2,10 @@ import { clickTarget, simulateTimeline, startTimeline } from "@jspsych/test-util
|
||||
|
||||
import surveyText from ".";
|
||||
|
||||
const selectInput = (inputId: number) =>
|
||||
document.querySelector<HTMLInputElement>(`#input-${inputId}`);
|
||||
const selectInput = (
|
||||
inputId: number,
|
||||
displayElement: HTMLElement
|
||||
) => displayElement.querySelector<HTMLInputElement>(`#input-${inputId}`);
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
@ -17,10 +19,10 @@ describe("survey-text plugin", () => {
|
||||
]);
|
||||
|
||||
expect(displayElement.querySelectorAll("p.jspsych-survey-text").length).toBe(2);
|
||||
expect(selectInput(0).size).toBe(40);
|
||||
expect(selectInput(1).size).toBe(40);
|
||||
expect(selectInput(0, displayElement).size).toBe(40);
|
||||
expect(selectInput(1, displayElement).size).toBe(40);
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-survey-text-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-text-next"));
|
||||
|
||||
await expectFinished();
|
||||
});
|
||||
@ -37,10 +39,10 @@ describe("survey-text plugin", () => {
|
||||
]);
|
||||
|
||||
expect(displayElement.querySelectorAll("p.jspsych-survey-text").length).toBe(2);
|
||||
expect(selectInput(0).size).toBe(50);
|
||||
expect(selectInput(1).size).toBe(20);
|
||||
expect(selectInput(0, displayElement).size).toBe(50);
|
||||
expect(selectInput(1, displayElement).size).toBe(20);
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-survey-text-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-text-next"));
|
||||
|
||||
await expectFinished();
|
||||
});
|
||||
@ -57,16 +59,16 @@ describe("survey-text plugin", () => {
|
||||
]);
|
||||
|
||||
expect(displayElement.querySelectorAll("p.jspsych-survey-text").length).toBe(2);
|
||||
expect(selectInput(0).required).toBe(true);
|
||||
expect(selectInput(1).required).toBe(false);
|
||||
expect(selectInput(0, displayElement).required).toBe(true);
|
||||
expect(selectInput(1, displayElement).required).toBe(false);
|
||||
|
||||
selectInput(0).value = "42";
|
||||
await clickTarget(document.querySelector("#jspsych-survey-text-next"));
|
||||
selectInput(0, displayElement).value = "42";
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-text-next"));
|
||||
await expectFinished();
|
||||
});
|
||||
|
||||
test("data are logged with the right question when randomize order is true", async () => {
|
||||
const { expectFinished, getData } = await startTimeline([
|
||||
const { expectFinished, getData, displayElement } = await startTimeline([
|
||||
{
|
||||
type: surveyText,
|
||||
questions: [
|
||||
@ -80,13 +82,13 @@ describe("survey-text plugin", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
selectInput(0).value = "a0";
|
||||
selectInput(1).value = "a1";
|
||||
selectInput(2).value = "a2";
|
||||
selectInput(3).value = "a3";
|
||||
selectInput(4).value = "a4";
|
||||
selectInput(0, displayElement).value = "a0";
|
||||
selectInput(1, displayElement).value = "a1";
|
||||
selectInput(2, displayElement).value = "a2";
|
||||
selectInput(3, displayElement).value = "a3";
|
||||
selectInput(4, displayElement).value = "a4";
|
||||
|
||||
await clickTarget(document.querySelector("#jspsych-survey-text-next"));
|
||||
await clickTarget(displayElement.querySelector("#jspsych-survey-text-next"));
|
||||
|
||||
await expectFinished();
|
||||
|
||||
|
@ -100,7 +100,7 @@
|
||||
type: jsPsychHtmlKeyboardResponse,
|
||||
stimulus: function () {
|
||||
var html = `
|
||||
<div style="display:flex;width:70%;margin:auto;justify-content:space-around;"><span>${jsPsych.timelineVariable('word1')}</span><span>${jsPsych.timelineVariable('word2')}</span></div>`;
|
||||
<div style="display:flex;width:70%;margin:auto;justify-content:space-around;"><span>${jsPsych.evaluateTimelineVariable('word1')}</span><span>${jsPsych.evaluateTimelineVariable('word2')}</span></div>`;
|
||||
return html;
|
||||
},
|
||||
choices: ['f', 'j'],
|
||||
@ -112,7 +112,7 @@
|
||||
survey_json: function () {
|
||||
const last_response = jsPsych.data.getLastTrialData().values()[0].response;
|
||||
const response_type = (last_response === 'j') ? "RELATED" : "NOT RELATED";
|
||||
const question_text = `You said that the words "${jsPsych.timelineVariable('word1')}" and "${jsPsych.timelineVariable('word2')}" are ${response_type}. Please explain your answer.`;
|
||||
const question_text = `You said that the words "${jsPsych.evaluateTimelineVariable('word1')}" and "${jsPsych.evaluateTimelineVariable('word2')}" are ${response_type}. Please explain your answer.`;
|
||||
const survey_json = {
|
||||
showQuestionNumbers: false,
|
||||
completeText: "Next",
|
||||
|
@ -23,7 +23,7 @@ describe("video-button-response", () => {
|
||||
|
||||
await expect(async () => {
|
||||
await jsPsych.run(timeline);
|
||||
}).rejects.toThrowError();
|
||||
}).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("enable buttons during video playing", async () => {
|
||||
@ -40,9 +40,9 @@ describe("video-button-response", () => {
|
||||
|
||||
const jsPsych = initJsPsych();
|
||||
|
||||
const { getHTML, finished } = await startTimeline(timeline, jsPsych);
|
||||
const { displayElement } = await startTimeline(timeline, jsPsych);
|
||||
|
||||
const btns = document.querySelectorAll(".jspsych-html-button-response-button button");
|
||||
const btns = displayElement.querySelectorAll(".jspsych-html-button-response-button button");
|
||||
|
||||
for (let i = 0; i < btns.length; i++) {
|
||||
expect(btns[i].getAttribute("disabled")).toBe(true);
|
||||
|
@ -23,7 +23,7 @@ describe("video-keyboard-response", () => {
|
||||
|
||||
await expect(async () => {
|
||||
await jsPsych.run(timeline);
|
||||
}).rejects.toThrowError();
|
||||
}).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -22,7 +22,7 @@ describe("video-slider-response", () => {
|
||||
|
||||
await expect(async () => {
|
||||
await jsPsych.run(timeline);
|
||||
}).rejects.toThrowError();
|
||||
}).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user