@lookit/record 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +199 -8
- package/dist/index.browser.js +257 -39
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +16 -16
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +256 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +185 -14
- package/dist/index.js +256 -38
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/consentVideo.spec.ts +9 -1
- package/src/consentVideo.ts +10 -2
- package/src/errors.ts +28 -0
- package/src/index.spec.ts +835 -17
- package/src/recorder.spec.ts +677 -73
- package/src/recorder.ts +191 -35
- package/src/start.ts +51 -5
- package/src/stop.ts +56 -5
- package/src/trial.ts +128 -16
- package/src/types.ts +21 -0
- package/src/utils.spec.ts +129 -0
- package/src/utils.ts +45 -0
package/src/index.spec.ts
CHANGED
|
@@ -1,20 +1,45 @@
|
|
|
1
1
|
import { LookitWindow } from "@lookit/data/dist/types";
|
|
2
|
+
import chsTemplates from "@lookit/templates";
|
|
2
3
|
import { initJsPsych, PluginInfo, TrialType } from "jspsych";
|
|
3
4
|
import { ExistingRecordingError, NoSessionRecordingError } from "./errors";
|
|
4
5
|
import Rec from "./index";
|
|
5
6
|
import Recorder from "./recorder";
|
|
7
|
+
import type { StopResult } from "./types";
|
|
6
8
|
|
|
7
9
|
declare const window: LookitWindow;
|
|
8
10
|
|
|
11
|
+
let global_display_el: HTMLDivElement;
|
|
12
|
+
|
|
13
|
+
let consoleLogSpy: jest.SpyInstance<
|
|
14
|
+
void,
|
|
15
|
+
[message?: unknown, ...optionalParams: unknown[]],
|
|
16
|
+
unknown
|
|
17
|
+
>;
|
|
18
|
+
let consoleWarnSpy: jest.SpyInstance<
|
|
19
|
+
void,
|
|
20
|
+
[message?: unknown, ...optionalParams: unknown[]],
|
|
21
|
+
unknown
|
|
22
|
+
>;
|
|
23
|
+
let consoleErrorSpy: jest.SpyInstance<
|
|
24
|
+
void,
|
|
25
|
+
[message?: unknown, ...optionalParams: unknown[]],
|
|
26
|
+
unknown
|
|
27
|
+
>;
|
|
28
|
+
|
|
9
29
|
jest.mock("./recorder");
|
|
10
30
|
jest.mock("@lookit/data");
|
|
11
31
|
jest.mock("jspsych", () => ({
|
|
12
32
|
...jest.requireActual("jspsych"),
|
|
13
|
-
initJsPsych: jest.fn().
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.
|
|
33
|
+
initJsPsych: jest.fn().mockImplementation(() => {
|
|
34
|
+
// create a new display element for each jsPsych instance
|
|
35
|
+
global_display_el = document.createElement("div");
|
|
36
|
+
return {
|
|
37
|
+
finishTrial: jest.fn().mockImplementation(),
|
|
38
|
+
getCurrentTrial: jest
|
|
39
|
+
.fn()
|
|
40
|
+
.mockReturnValue({ type: { info: { name: "test-type" } } }),
|
|
41
|
+
getDisplayElement: jest.fn(() => global_display_el),
|
|
42
|
+
};
|
|
18
43
|
}),
|
|
19
44
|
}));
|
|
20
45
|
|
|
@@ -24,31 +49,43 @@ jest.mock("jspsych", () => ({
|
|
|
24
49
|
* @param chs - Contents of chs storage.
|
|
25
50
|
*/
|
|
26
51
|
const setCHSValue = (chs = {}) => {
|
|
27
|
-
Object.defineProperty(
|
|
28
|
-
value:
|
|
29
|
-
|
|
30
|
-
|
|
52
|
+
Object.defineProperty(window, "chs", {
|
|
53
|
+
value: chs,
|
|
54
|
+
configurable: true,
|
|
55
|
+
writable: true,
|
|
31
56
|
});
|
|
32
57
|
};
|
|
33
58
|
|
|
34
59
|
beforeEach(() => {
|
|
35
60
|
setCHSValue();
|
|
61
|
+
// Hide the console output during tests. Tests can still assert on these spies to check console calls.
|
|
62
|
+
consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => {});
|
|
63
|
+
consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
64
|
+
consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {});
|
|
36
65
|
});
|
|
37
66
|
|
|
38
67
|
afterEach(() => {
|
|
68
|
+
jest.restoreAllMocks();
|
|
39
69
|
jest.clearAllMocks();
|
|
70
|
+
|
|
71
|
+
consoleLogSpy.mockRestore();
|
|
72
|
+
consoleWarnSpy.mockRestore();
|
|
73
|
+
consoleErrorSpy.mockRestore();
|
|
40
74
|
});
|
|
41
75
|
|
|
42
|
-
test("Trial recording", () => {
|
|
76
|
+
test("Trial recording", async () => {
|
|
43
77
|
const mockRecStart = jest.spyOn(Recorder.prototype, "start");
|
|
44
|
-
const mockRecStop = jest.spyOn(Recorder.prototype, "stop")
|
|
78
|
+
const mockRecStop = jest.spyOn(Recorder.prototype, "stop").mockReturnValue({
|
|
79
|
+
stopped: Promise.resolve("url"),
|
|
80
|
+
uploaded: Promise.resolve(),
|
|
81
|
+
});
|
|
45
82
|
const jsPsych = initJsPsych();
|
|
46
83
|
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
47
84
|
const getCurrentPluginNameSpy = jest.spyOn(trialRec, "getCurrentPluginName");
|
|
48
85
|
|
|
49
86
|
trialRec.on_start();
|
|
50
87
|
trialRec.on_load();
|
|
51
|
-
trialRec.on_finish();
|
|
88
|
+
await trialRec.on_finish();
|
|
52
89
|
|
|
53
90
|
expect(Recorder).toHaveBeenCalledTimes(1);
|
|
54
91
|
expect(mockRecStart).toHaveBeenCalledTimes(1);
|
|
@@ -57,23 +94,464 @@ test("Trial recording", () => {
|
|
|
57
94
|
expect(getCurrentPluginNameSpy).toHaveBeenCalledTimes(1);
|
|
58
95
|
});
|
|
59
96
|
|
|
60
|
-
test("Trial recording's initialize
|
|
97
|
+
test("Trial recording's initialize with no parameters", async () => {
|
|
61
98
|
const jsPsych = initJsPsych();
|
|
62
99
|
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
63
100
|
|
|
64
101
|
expect(await trialRec.initialize()).toBeUndefined();
|
|
102
|
+
expect(trialRec["uploadMsg"]).toBeNull;
|
|
103
|
+
expect(trialRec["locale"]).toBe("en-us");
|
|
104
|
+
expect(Recorder).toHaveBeenCalledTimes(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("Trial recording's initialize with locale parameter", async () => {
|
|
108
|
+
const jsPsych = initJsPsych();
|
|
109
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
110
|
+
|
|
111
|
+
expect(await trialRec.initialize({ locale: "fr" })).toBeUndefined();
|
|
112
|
+
expect(trialRec["locale"]).toBe("fr");
|
|
113
|
+
expect(trialRec["uploadMsg"]).toBeNull;
|
|
114
|
+
expect(Recorder).toHaveBeenCalledTimes(0);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("Trial recording's initialize with wait_for_upload_message parameter", async () => {
|
|
118
|
+
const jsPsych = initJsPsych();
|
|
119
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
120
|
+
|
|
121
|
+
expect(
|
|
122
|
+
await trialRec.initialize({ wait_for_upload_message: "Please wait..." }),
|
|
123
|
+
).toBeUndefined();
|
|
124
|
+
expect(trialRec["uploadMsg"]).toBe("Please wait...");
|
|
125
|
+
expect(trialRec["locale"]).toBe("en-us");
|
|
126
|
+
expect(Recorder).toHaveBeenCalledTimes(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("Trial recording start with locale parameter", async () => {
|
|
130
|
+
const jsPsych = initJsPsych();
|
|
131
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
132
|
+
|
|
133
|
+
await trialRec.initialize();
|
|
134
|
+
|
|
135
|
+
expect(trialRec["uploadMsg"]).toBeNull;
|
|
136
|
+
expect(trialRec["locale"]).toBe("en-us");
|
|
65
137
|
expect(Recorder).toHaveBeenCalledTimes(0);
|
|
138
|
+
|
|
139
|
+
trialRec.on_start({ locale: "fr" });
|
|
140
|
+
|
|
141
|
+
expect(trialRec["uploadMsg"]).toBeNull;
|
|
142
|
+
expect(trialRec["locale"]).toBe("fr");
|
|
143
|
+
expect(Recorder).toHaveBeenCalledTimes(1);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("Trial recording start with wait_for_upload_message parameter", async () => {
|
|
147
|
+
const jsPsych = initJsPsych();
|
|
148
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
149
|
+
|
|
150
|
+
await trialRec.initialize();
|
|
151
|
+
|
|
152
|
+
expect(trialRec["uploadMsg"]).toBeNull;
|
|
153
|
+
expect(trialRec["locale"]).toBe("en-us");
|
|
154
|
+
expect(Recorder).toHaveBeenCalledTimes(0);
|
|
155
|
+
|
|
156
|
+
trialRec.on_start({ wait_for_upload_message: "Please wait..." });
|
|
157
|
+
|
|
158
|
+
expect(trialRec["uploadMsg"]).toBe("Please wait...");
|
|
159
|
+
expect(trialRec["locale"]).toBe("en-us");
|
|
160
|
+
expect(Recorder).toHaveBeenCalledTimes(1);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("Trial recording stop/finish with default uploading msg in English", async () => {
|
|
164
|
+
// control the recorder stop promise so that we can inspect the display before it resolves
|
|
165
|
+
let resolveStop!: (value: string) => void;
|
|
166
|
+
let resolveUpload!: () => void;
|
|
167
|
+
const stopPromise = new Promise<string>((res) => {
|
|
168
|
+
resolveStop = res;
|
|
169
|
+
});
|
|
170
|
+
const uploadPromise = new Promise<void>((res) => {
|
|
171
|
+
resolveUpload = res;
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
jest
|
|
175
|
+
.spyOn(Recorder.prototype, "stop")
|
|
176
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
177
|
+
|
|
178
|
+
const jsPsych = initJsPsych();
|
|
179
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
180
|
+
|
|
181
|
+
const params = {
|
|
182
|
+
locale: "en-us",
|
|
183
|
+
wait_for_upload_message: null,
|
|
184
|
+
};
|
|
185
|
+
await trialRec.initialize(params);
|
|
186
|
+
trialRec.on_start();
|
|
187
|
+
trialRec.on_load();
|
|
188
|
+
|
|
189
|
+
// call on_finish but don't await so that we can inspect before it resolves
|
|
190
|
+
trialRec.on_finish();
|
|
191
|
+
|
|
192
|
+
expect(global_display_el.innerHTML).toBe(
|
|
193
|
+
chsTemplates.uploadingVideo({
|
|
194
|
+
type: jsPsych.getCurrentTrial().type,
|
|
195
|
+
locale: params.locale,
|
|
196
|
+
} as TrialType<PluginInfo>),
|
|
197
|
+
);
|
|
198
|
+
expect(global_display_el.innerHTML).toBe(
|
|
199
|
+
`<div id="lookit-uploading-video-msg-container">
|
|
200
|
+
<div>uploading video, please wait...</div>
|
|
201
|
+
<div id="lookit-loader-container"><div class="loader"></div></div></div>`,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// resolve the stop promise
|
|
205
|
+
resolveStop("url");
|
|
206
|
+
await stopPromise;
|
|
207
|
+
// resolve the upload promise
|
|
208
|
+
resolveUpload();
|
|
209
|
+
await uploadPromise;
|
|
210
|
+
|
|
211
|
+
// check the display cleanup
|
|
212
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("Trial recording stop/finish with different locale should display default uploading msg in specified language", async () => {
|
|
216
|
+
// control the recorder stop promise so that we can inspect the display before it resolves
|
|
217
|
+
let resolveStop!: (value: string) => void;
|
|
218
|
+
let resolveUpload!: () => void;
|
|
219
|
+
const stopPromise = new Promise<string>((res) => {
|
|
220
|
+
resolveStop = res;
|
|
221
|
+
});
|
|
222
|
+
const uploadPromise = new Promise<void>((res) => {
|
|
223
|
+
resolveUpload = res;
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
jest
|
|
227
|
+
.spyOn(Recorder.prototype, "stop")
|
|
228
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
229
|
+
|
|
230
|
+
const jsPsych = initJsPsych();
|
|
231
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
232
|
+
|
|
233
|
+
const params = {
|
|
234
|
+
locale: "fr",
|
|
235
|
+
wait_for_upload_message: null,
|
|
236
|
+
};
|
|
237
|
+
await trialRec.initialize(params);
|
|
238
|
+
trialRec.on_start();
|
|
239
|
+
trialRec.on_load();
|
|
240
|
+
|
|
241
|
+
// call on_finish but don't await so that we can inspect before it resolves
|
|
242
|
+
trialRec.on_finish();
|
|
243
|
+
|
|
244
|
+
expect(global_display_el.innerHTML).toBe(
|
|
245
|
+
chsTemplates.uploadingVideo({
|
|
246
|
+
type: jsPsych.getCurrentTrial().type,
|
|
247
|
+
locale: params.locale,
|
|
248
|
+
} as TrialType<PluginInfo>),
|
|
249
|
+
);
|
|
250
|
+
expect(global_display_el.innerHTML).toBe(
|
|
251
|
+
`<div id="lookit-uploading-video-msg-container">
|
|
252
|
+
<div>téléchargement video en cours, veuillez attendre...</div>
|
|
253
|
+
<div id="lookit-loader-container"><div class="loader"></div></div></div>`,
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
// resolve the stop promise
|
|
257
|
+
resolveStop("url");
|
|
258
|
+
await stopPromise;
|
|
259
|
+
// resolve the upload promise
|
|
260
|
+
resolveUpload();
|
|
261
|
+
await uploadPromise;
|
|
262
|
+
|
|
263
|
+
// check the display cleanup
|
|
264
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("Trial recording stop/finish with custom uploading message", async () => {
|
|
268
|
+
// control the recorder stop promise so that we can inspect the display before it resolves
|
|
269
|
+
let resolveStop!: (value: string) => void;
|
|
270
|
+
let resolveUpload!: () => void;
|
|
271
|
+
const stopPromise = new Promise<string>((res) => {
|
|
272
|
+
resolveStop = res;
|
|
273
|
+
});
|
|
274
|
+
const uploadPromise = new Promise<void>((res) => {
|
|
275
|
+
resolveUpload = res;
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
jest
|
|
279
|
+
.spyOn(Recorder.prototype, "stop")
|
|
280
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
281
|
+
|
|
282
|
+
const jsPsych = initJsPsych();
|
|
283
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
284
|
+
|
|
285
|
+
const params = {
|
|
286
|
+
wait_for_upload_message: "Wait!",
|
|
287
|
+
};
|
|
288
|
+
await trialRec.initialize(params);
|
|
289
|
+
trialRec.on_start();
|
|
290
|
+
trialRec.on_load();
|
|
291
|
+
|
|
292
|
+
// call on_finish but don't await so that we can inspect before it resolves
|
|
293
|
+
trialRec.on_finish();
|
|
294
|
+
|
|
295
|
+
expect(global_display_el.innerHTML).toBe("Wait!");
|
|
296
|
+
|
|
297
|
+
// resolve the stop promise
|
|
298
|
+
resolveStop("url");
|
|
299
|
+
await stopPromise;
|
|
300
|
+
resolveUpload();
|
|
301
|
+
await uploadPromise;
|
|
302
|
+
|
|
303
|
+
// check the display cleanup
|
|
304
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("Trial recording stop/finish timeout with default parameters", async () => {
|
|
308
|
+
// simulate a resolved stop promise and timeout upload promise
|
|
309
|
+
const stopPromise = new Promise<string>((res) => res("url"));
|
|
310
|
+
const uploadPromise = new Promise<string>((res) => res("timeout"));
|
|
311
|
+
|
|
312
|
+
const recStopSpy = jest
|
|
313
|
+
.spyOn(Recorder.prototype, "stop")
|
|
314
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
315
|
+
|
|
316
|
+
const jsPsych = initJsPsych();
|
|
317
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
318
|
+
|
|
319
|
+
await trialRec.initialize();
|
|
320
|
+
trialRec.on_start();
|
|
321
|
+
trialRec.on_load();
|
|
322
|
+
|
|
323
|
+
await trialRec.on_finish();
|
|
324
|
+
|
|
325
|
+
// recorder.stop should be called with the default max upload duration
|
|
326
|
+
expect(recStopSpy).toHaveBeenCalledWith({
|
|
327
|
+
upload_timeout_ms: 10000,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// check the display cleanup
|
|
331
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test("Trial recording stop/finish with max upload duration initialize parameter", async () => {
|
|
335
|
+
// simulate a resolved stop promise and timeout upload promise
|
|
336
|
+
const stopPromise = new Promise<string>((res) => res("url"));
|
|
337
|
+
const uploadPromise = new Promise<string>((res) => res("timeout"));
|
|
338
|
+
|
|
339
|
+
const recStopSpy = jest
|
|
340
|
+
.spyOn(Recorder.prototype, "stop")
|
|
341
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
342
|
+
|
|
343
|
+
const jsPsych = initJsPsych();
|
|
344
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
345
|
+
|
|
346
|
+
const params = {
|
|
347
|
+
max_upload_seconds: 20,
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
await trialRec.initialize(params);
|
|
351
|
+
trialRec.on_start();
|
|
352
|
+
trialRec.on_load();
|
|
353
|
+
|
|
354
|
+
await trialRec.on_finish();
|
|
355
|
+
|
|
356
|
+
// recorder.stop should be called with 20 seconds as the max upload duration
|
|
357
|
+
expect(recStopSpy).toHaveBeenCalledWith({
|
|
358
|
+
upload_timeout_ms: 20000,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// check the display cleanup
|
|
362
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test("Trial recording stop/finish with max upload duration start parameter", async () => {
|
|
366
|
+
// simulate a resolved stop promise and timeout upload promise
|
|
367
|
+
const stopPromise = new Promise<string>((res) => res("url"));
|
|
368
|
+
const uploadPromise = new Promise<string>((res) => res("timeout"));
|
|
369
|
+
|
|
370
|
+
const recStopSpy = jest
|
|
371
|
+
.spyOn(Recorder.prototype, "stop")
|
|
372
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
373
|
+
|
|
374
|
+
const jsPsych = initJsPsych();
|
|
375
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
376
|
+
|
|
377
|
+
const initParams = {
|
|
378
|
+
max_upload_seconds: null,
|
|
379
|
+
};
|
|
380
|
+
const startParams = {
|
|
381
|
+
max_upload_seconds: 20,
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
await trialRec.initialize(initParams);
|
|
385
|
+
trialRec.on_start(startParams);
|
|
386
|
+
trialRec.on_load();
|
|
387
|
+
|
|
388
|
+
await trialRec.on_finish();
|
|
389
|
+
|
|
390
|
+
// recorder.stop should be called with 20 seconds as the max upload duration
|
|
391
|
+
expect(recStopSpy).toHaveBeenCalledWith({
|
|
392
|
+
upload_timeout_ms: 20000,
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// check the display cleanup
|
|
396
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test("Trial recording stop/finish with null max upload duration", async () => {
|
|
400
|
+
// simulate a resolved stop promise and resolved upload promise
|
|
401
|
+
const stopPromise = new Promise<string>((res) => res("url"));
|
|
402
|
+
const uploadPromise = new Promise<void>((res) => res());
|
|
403
|
+
|
|
404
|
+
const recStopSpy = jest
|
|
405
|
+
.spyOn(Recorder.prototype, "stop")
|
|
406
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
407
|
+
|
|
408
|
+
const jsPsych = initJsPsych();
|
|
409
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
410
|
+
|
|
411
|
+
const params = {
|
|
412
|
+
max_upload_seconds: null,
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
await trialRec.initialize();
|
|
416
|
+
trialRec.on_start(params);
|
|
417
|
+
trialRec.on_load();
|
|
418
|
+
|
|
419
|
+
await trialRec.on_finish();
|
|
420
|
+
|
|
421
|
+
// recorder.stop should be called with null as the max upload duration
|
|
422
|
+
expect(recStopSpy).toHaveBeenCalledWith({
|
|
423
|
+
upload_timeout_ms: null,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// check the display cleanup
|
|
427
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test("Trial recording stop with failure during stop", async () => {
|
|
431
|
+
// Create a controlled promise and capture the reject function
|
|
432
|
+
let rejectStop!: (err: unknown) => void;
|
|
433
|
+
const stopPromise = new Promise<string>((_, reject) => {
|
|
434
|
+
rejectStop = reject;
|
|
435
|
+
});
|
|
436
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
437
|
+
let resolveUpload!: () => void;
|
|
438
|
+
const uploadPromise = new Promise<void>((res) => {
|
|
439
|
+
resolveUpload = res;
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
jest
|
|
443
|
+
.spyOn(Recorder.prototype, "stop")
|
|
444
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
445
|
+
|
|
446
|
+
const jsPsych = initJsPsych();
|
|
447
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
448
|
+
|
|
449
|
+
await trialRec.initialize();
|
|
450
|
+
trialRec.on_start();
|
|
451
|
+
trialRec.on_load();
|
|
452
|
+
|
|
453
|
+
// call on_finish but don't await so that we can inspect before it resolves
|
|
454
|
+
trialRec.on_finish();
|
|
455
|
+
|
|
456
|
+
// Should show initial wait for upload message
|
|
457
|
+
expect(global_display_el.innerHTML).toBe(
|
|
458
|
+
`<div id="lookit-uploading-video-msg-container">
|
|
459
|
+
<div>uploading video, please wait...</div>
|
|
460
|
+
<div id="lookit-loader-container"><div class="loader"></div></div></div>`,
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
// Reject stop
|
|
464
|
+
rejectStop(new Error("stop failed"));
|
|
465
|
+
|
|
466
|
+
// Wait for plugin's `.catch()` handler to run
|
|
467
|
+
await Promise.resolve();
|
|
468
|
+
|
|
469
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
470
|
+
"TrialRecordExtension: recorder stop/upload failed.",
|
|
471
|
+
Error("stop failed"),
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
// Wait for plugin's `.catch()` handler to run
|
|
475
|
+
await Promise.resolve();
|
|
476
|
+
|
|
477
|
+
// TO DO: modify the trial extension code to display translated error msg and/or researcher contact info
|
|
478
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
66
479
|
});
|
|
67
480
|
|
|
68
|
-
test("
|
|
481
|
+
test("Trial recording stop with failure during upload", async () => {
|
|
482
|
+
let resolveStop!: (value: string) => void;
|
|
483
|
+
const stopPromise = new Promise<string>((res) => {
|
|
484
|
+
resolveStop = res;
|
|
485
|
+
});
|
|
486
|
+
// Create a controlled promise and capture the reject function
|
|
487
|
+
let rejectUpload!: (err: unknown) => void;
|
|
488
|
+
const uploadPromise = new Promise<string>((_, reject) => {
|
|
489
|
+
rejectUpload = reject;
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
jest
|
|
493
|
+
.spyOn(Recorder.prototype, "stop")
|
|
494
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
495
|
+
|
|
496
|
+
const jsPsych = initJsPsych();
|
|
497
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
498
|
+
|
|
499
|
+
await trialRec.initialize();
|
|
500
|
+
trialRec.on_start();
|
|
501
|
+
trialRec.on_load();
|
|
502
|
+
|
|
503
|
+
// call on_finish but don't await so that we can inspect before it resolves
|
|
504
|
+
trialRec.on_finish();
|
|
505
|
+
|
|
506
|
+
// Should show initial wait for upload message
|
|
507
|
+
expect(global_display_el.innerHTML).toBe(
|
|
508
|
+
`<div id="lookit-uploading-video-msg-container">
|
|
509
|
+
<div>uploading video, please wait...</div>
|
|
510
|
+
<div id="lookit-loader-container"><div class="loader"></div></div></div>`,
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
// Resolve stop
|
|
514
|
+
resolveStop("url");
|
|
515
|
+
// Reject upload
|
|
516
|
+
rejectUpload(new Error("upload failed"));
|
|
517
|
+
|
|
518
|
+
// Wait for plugin's `.catch()` handler to run and flush microtasks
|
|
519
|
+
await Promise.resolve();
|
|
520
|
+
await Promise.resolve();
|
|
521
|
+
|
|
522
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
523
|
+
"TrialRecordExtension: recorder stop/upload failed.",
|
|
524
|
+
Error("upload failed"),
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
// TO DO: modify the trial extension code to display translated error msg and/or researcher contact info
|
|
528
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test("Trial recording stop with no recorder", async () => {
|
|
532
|
+
const jsPsych = initJsPsych();
|
|
533
|
+
const trialRec = new Rec.TrialRecordExtension(jsPsych);
|
|
534
|
+
|
|
535
|
+
// no recorder - extension should clean up display and immediately resolve on_finish
|
|
536
|
+
await trialRec.on_finish();
|
|
537
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
test("Start session recording", async () => {
|
|
69
541
|
const mockRecStart = jest.spyOn(Recorder.prototype, "start");
|
|
70
542
|
const jsPsych = initJsPsych();
|
|
71
543
|
const startRec = new Rec.StartRecordPlugin(jsPsych);
|
|
544
|
+
const display_element = jest
|
|
545
|
+
.fn()
|
|
546
|
+
.mockImplementation() as unknown as HTMLElement;
|
|
547
|
+
const trial = {
|
|
548
|
+
locale: "en-us",
|
|
549
|
+
} as unknown as TrialType<PluginInfo>;
|
|
72
550
|
|
|
73
551
|
// manual mock
|
|
74
552
|
mockRecStart.mockImplementation(jest.fn().mockReturnValue(Promise.resolve()));
|
|
75
553
|
|
|
76
|
-
await startRec.trial();
|
|
554
|
+
await startRec.trial(display_element, trial);
|
|
77
555
|
|
|
78
556
|
expect(jsPsych.finishTrial).toHaveBeenCalledTimes(1);
|
|
79
557
|
expect(() => {
|
|
@@ -81,7 +559,102 @@ test("Start Recording", async () => {
|
|
|
81
559
|
}).toThrow(ExistingRecordingError);
|
|
82
560
|
});
|
|
83
561
|
|
|
84
|
-
test("
|
|
562
|
+
test("Start session recording with default wait for connection message", async () => {
|
|
563
|
+
let resolveMockStart!: () => void;
|
|
564
|
+
const startRecPromise = new Promise<void>((res) => {
|
|
565
|
+
resolveMockStart = res;
|
|
566
|
+
});
|
|
567
|
+
const mockRecStart = jest.spyOn(Recorder.prototype, "start");
|
|
568
|
+
mockRecStart.mockImplementation(jest.fn().mockReturnValue(startRecPromise));
|
|
569
|
+
|
|
570
|
+
const jsPsych = initJsPsych();
|
|
571
|
+
const startRec = new Rec.StartRecordPlugin(jsPsych);
|
|
572
|
+
const display_element = jest
|
|
573
|
+
.fn()
|
|
574
|
+
.mockImplementation() as unknown as HTMLElement;
|
|
575
|
+
const trial = { locale: "en-us" } as unknown as TrialType<PluginInfo>;
|
|
576
|
+
|
|
577
|
+
// call trial but don't await so that we can inspect display element
|
|
578
|
+
startRec.trial(display_element, trial);
|
|
579
|
+
expect(display_element.innerHTML).toBe(
|
|
580
|
+
`<div id="lookit-establishing-connection-msg">
|
|
581
|
+
<div>establishing video connection, please wait...</div>
|
|
582
|
+
<div id="lookit-loader-container"><div class="loader"></div></div></div>`,
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
// now resolve start promise and await
|
|
586
|
+
resolveMockStart();
|
|
587
|
+
await startRecPromise;
|
|
588
|
+
|
|
589
|
+
// clean up tasks should run
|
|
590
|
+
expect(display_element.innerHTML).toBe("");
|
|
591
|
+
expect(jsPsych.finishTrial).toHaveBeenCalledTimes(1);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
test("Start session recording with translated connection message", async () => {
|
|
595
|
+
let resolveMockStart!: () => void;
|
|
596
|
+
const startRecPromise = new Promise<void>((res) => {
|
|
597
|
+
resolveMockStart = res;
|
|
598
|
+
});
|
|
599
|
+
const mockRecStart = jest.spyOn(Recorder.prototype, "start");
|
|
600
|
+
mockRecStart.mockImplementation(jest.fn().mockReturnValue(startRecPromise));
|
|
601
|
+
|
|
602
|
+
const jsPsych = initJsPsych();
|
|
603
|
+
const startRec = new Rec.StartRecordPlugin(jsPsych);
|
|
604
|
+
const display_element = jest
|
|
605
|
+
.fn()
|
|
606
|
+
.mockImplementation() as unknown as HTMLElement;
|
|
607
|
+
const trial = { locale: "fr" } as unknown as TrialType<PluginInfo>;
|
|
608
|
+
|
|
609
|
+
// call trial but don't await so that we can inspect display element
|
|
610
|
+
startRec.trial(display_element, trial);
|
|
611
|
+
expect(display_element.innerHTML).toBe(
|
|
612
|
+
`<div id="lookit-establishing-connection-msg">
|
|
613
|
+
<div>en attente de connection video, veuillez attendre...</div>
|
|
614
|
+
<div id="lookit-loader-container"><div class="loader"></div></div></div>`,
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
// now resolve start promise and await
|
|
618
|
+
resolveMockStart();
|
|
619
|
+
await startRecPromise;
|
|
620
|
+
|
|
621
|
+
// clean up tasks should run
|
|
622
|
+
expect(display_element.innerHTML).toBe("");
|
|
623
|
+
expect(jsPsych.finishTrial).toHaveBeenCalledTimes(1);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
test("Start session recording with custom wait for connection message", async () => {
|
|
627
|
+
let resolveMockStart!: () => void;
|
|
628
|
+
const startRecPromise = new Promise<void>((res) => {
|
|
629
|
+
resolveMockStart = res;
|
|
630
|
+
});
|
|
631
|
+
const mockRecStart = jest.spyOn(Recorder.prototype, "start");
|
|
632
|
+
mockRecStart.mockImplementation(jest.fn().mockReturnValue(startRecPromise));
|
|
633
|
+
|
|
634
|
+
const jsPsych = initJsPsych();
|
|
635
|
+
const startRec = new Rec.StartRecordPlugin(jsPsych);
|
|
636
|
+
const display_element = jest
|
|
637
|
+
.fn()
|
|
638
|
+
.mockImplementation() as unknown as HTMLElement;
|
|
639
|
+
const trial = {
|
|
640
|
+
wait_for_connection_message: "Hello!",
|
|
641
|
+
locale: "de", // should be ignored
|
|
642
|
+
} as unknown as TrialType<PluginInfo>;
|
|
643
|
+
|
|
644
|
+
// call trial but don't await so that we can inspect display element
|
|
645
|
+
startRec.trial(display_element, trial);
|
|
646
|
+
expect(display_element.innerHTML).toBe("Hello!");
|
|
647
|
+
|
|
648
|
+
// now resolve start promise and await
|
|
649
|
+
resolveMockStart();
|
|
650
|
+
await startRecPromise;
|
|
651
|
+
|
|
652
|
+
// clean up tasks should run
|
|
653
|
+
expect(display_element.innerHTML).toBe("");
|
|
654
|
+
expect(jsPsych.finishTrial).toHaveBeenCalledTimes(1);
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
test("Stop session recording", async () => {
|
|
85
658
|
const mockRecStop = jest.spyOn(Recorder.prototype, "stop");
|
|
86
659
|
const jsPsych = initJsPsych();
|
|
87
660
|
|
|
@@ -94,7 +667,12 @@ test("Stop Recording", async () => {
|
|
|
94
667
|
.fn()
|
|
95
668
|
.mockImplementation() as unknown as HTMLElement;
|
|
96
669
|
|
|
97
|
-
mockRecStop.mockImplementation(
|
|
670
|
+
mockRecStop.mockImplementation(
|
|
671
|
+
(): StopResult => ({
|
|
672
|
+
stopped: Promise.resolve("mock-url"),
|
|
673
|
+
uploaded: Promise.resolve(),
|
|
674
|
+
}),
|
|
675
|
+
);
|
|
98
676
|
|
|
99
677
|
const trial = {
|
|
100
678
|
locale: "en-us",
|
|
@@ -112,3 +690,243 @@ test("Stop Recording", async () => {
|
|
|
112
690
|
NoSessionRecordingError,
|
|
113
691
|
);
|
|
114
692
|
});
|
|
693
|
+
|
|
694
|
+
test("Stop session recording should display default uploading msg in English", async () => {
|
|
695
|
+
// control the recorder stop promise so that we can inspect the display before it resolves
|
|
696
|
+
let resolveStop!: (value: string) => void;
|
|
697
|
+
let resolveUpload!: () => void;
|
|
698
|
+
const stopPromise = new Promise<string>((res) => {
|
|
699
|
+
resolveStop = res;
|
|
700
|
+
});
|
|
701
|
+
const uploadPromise = new Promise<void>((res) => {
|
|
702
|
+
resolveUpload = res;
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
jest
|
|
706
|
+
.spyOn(Recorder.prototype, "stop")
|
|
707
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
708
|
+
|
|
709
|
+
const jsPsych = initJsPsych();
|
|
710
|
+
|
|
711
|
+
setCHSValue({
|
|
712
|
+
sessionRecorder: new Recorder(jsPsych),
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
const stop_rec_plugin = new Rec.StopRecordPlugin(jsPsych);
|
|
716
|
+
const display_element = document.createElement("div");
|
|
717
|
+
|
|
718
|
+
const trial = {
|
|
719
|
+
type: Rec.StopRecordPlugin.info.name,
|
|
720
|
+
locale: "en-us",
|
|
721
|
+
wait_for_upload_message: null,
|
|
722
|
+
} as unknown as TrialType<PluginInfo>; // need to cast here because the "type" param is a string and should be a class
|
|
723
|
+
|
|
724
|
+
// call trial but don't await so that we can inspect before it resolves
|
|
725
|
+
stop_rec_plugin.trial(display_element, trial);
|
|
726
|
+
|
|
727
|
+
const en_uploading_msg = chsTemplates.uploadingVideo(trial);
|
|
728
|
+
|
|
729
|
+
// check that en (default) is used
|
|
730
|
+
expect(en_uploading_msg).toBe(
|
|
731
|
+
`<div id="lookit-uploading-video-msg-container">
|
|
732
|
+
<div>uploading video, please wait...</div>
|
|
733
|
+
<div id="lookit-loader-container"><div class="loader"></div></div></div>`,
|
|
734
|
+
);
|
|
735
|
+
expect(display_element.innerHTML).toBe(en_uploading_msg);
|
|
736
|
+
expect(Recorder.prototype.stop).toHaveBeenCalledTimes(1);
|
|
737
|
+
|
|
738
|
+
// resolve the stop promise and upload promise
|
|
739
|
+
resolveStop("url");
|
|
740
|
+
await stopPromise;
|
|
741
|
+
await Promise.resolve();
|
|
742
|
+
resolveUpload();
|
|
743
|
+
await uploadPromise;
|
|
744
|
+
await Promise.resolve();
|
|
745
|
+
|
|
746
|
+
// check the cleanup tasks after the trial method has resolved
|
|
747
|
+
expect(display_element.innerHTML).toBe("");
|
|
748
|
+
expect(jsPsych.finishTrial).toHaveBeenCalledTimes(1);
|
|
749
|
+
expect(window.chs.sessionRecorder).toBeNull();
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
test("Stop session recording with different locale should display default uploading msg in specified language", async () => {
|
|
753
|
+
// control the recorder stop promise so that we can inspect the display before it resolves
|
|
754
|
+
let resolveStop!: (value: string) => void;
|
|
755
|
+
let resolveUpload!: () => void;
|
|
756
|
+
const stopPromise = new Promise<string>((res) => {
|
|
757
|
+
resolveStop = res;
|
|
758
|
+
});
|
|
759
|
+
const uploadPromise = new Promise<void>((res) => {
|
|
760
|
+
resolveUpload = res;
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
jest
|
|
764
|
+
.spyOn(Recorder.prototype, "stop")
|
|
765
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
766
|
+
|
|
767
|
+
const jsPsych = initJsPsych();
|
|
768
|
+
|
|
769
|
+
setCHSValue({
|
|
770
|
+
sessionRecorder: new Recorder(jsPsych),
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
const stop_rec_plugin = new Rec.StopRecordPlugin(jsPsych);
|
|
774
|
+
const display_element = document.createElement("div");
|
|
775
|
+
|
|
776
|
+
// set locale to fr
|
|
777
|
+
const trial = {
|
|
778
|
+
type: Rec.StopRecordPlugin.info.name,
|
|
779
|
+
locale: "fr",
|
|
780
|
+
wait_for_upload_message: null,
|
|
781
|
+
} as unknown as TrialType<PluginInfo>; // need to cast here because the "type" param is a string and should be a class
|
|
782
|
+
|
|
783
|
+
// call trial but don't await so that we can inspect before it resolves
|
|
784
|
+
stop_rec_plugin.trial(display_element, trial);
|
|
785
|
+
|
|
786
|
+
const fr_uploading_msg = chsTemplates.uploadingVideo(trial);
|
|
787
|
+
|
|
788
|
+
// check that fr translation is used
|
|
789
|
+
expect(fr_uploading_msg).toBe(
|
|
790
|
+
`<div id="lookit-uploading-video-msg-container">
|
|
791
|
+
<div>téléchargement video en cours, veuillez attendre...</div>
|
|
792
|
+
<div id="lookit-loader-container"><div class="loader"></div></div></div>`,
|
|
793
|
+
);
|
|
794
|
+
expect(display_element.innerHTML).toBe(fr_uploading_msg);
|
|
795
|
+
expect(Recorder.prototype.stop).toHaveBeenCalledTimes(1);
|
|
796
|
+
|
|
797
|
+
// resolve the stop promise and upload promise
|
|
798
|
+
resolveStop("url");
|
|
799
|
+
await stopPromise;
|
|
800
|
+
await Promise.resolve();
|
|
801
|
+
resolveUpload();
|
|
802
|
+
await uploadPromise;
|
|
803
|
+
await Promise.resolve();
|
|
804
|
+
|
|
805
|
+
// check the cleanup tasks after the trial method has resolved
|
|
806
|
+
expect(display_element.innerHTML).toBe("");
|
|
807
|
+
expect(jsPsych.finishTrial).toHaveBeenCalledTimes(1);
|
|
808
|
+
expect(window.chs.sessionRecorder).toBeNull();
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
test("Stop session recording with custom uploading message", async () => {
|
|
812
|
+
// control the recorder stop promise so that we can inspect the display before it resolves
|
|
813
|
+
let resolveStop!: (value: string) => void;
|
|
814
|
+
let resolveUpload!: () => void;
|
|
815
|
+
const stopPromise = new Promise<string>((res) => {
|
|
816
|
+
resolveStop = res;
|
|
817
|
+
});
|
|
818
|
+
const uploadPromise = new Promise<void>((res) => {
|
|
819
|
+
resolveUpload = res;
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
jest
|
|
823
|
+
.spyOn(Recorder.prototype, "stop")
|
|
824
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
825
|
+
|
|
826
|
+
const jsPsych = initJsPsych();
|
|
827
|
+
setCHSValue({ sessionRecorder: new Recorder(jsPsych) });
|
|
828
|
+
|
|
829
|
+
const stop_rec_plugin = new Rec.StopRecordPlugin(jsPsych);
|
|
830
|
+
const display_element = document.createElement("div");
|
|
831
|
+
|
|
832
|
+
const trial = {
|
|
833
|
+
type: Rec.StopRecordPlugin.info.name,
|
|
834
|
+
locale: "en-us",
|
|
835
|
+
wait_for_upload_message: "<p>Custom message…</p>",
|
|
836
|
+
} as unknown as TrialType<PluginInfo>; // need to cast here because the "type" param is a string and should be a class
|
|
837
|
+
|
|
838
|
+
stop_rec_plugin.trial(display_element, trial);
|
|
839
|
+
|
|
840
|
+
// check display before stop is resolved
|
|
841
|
+
expect(display_element.innerHTML).toBe("<p>Custom message…</p>");
|
|
842
|
+
|
|
843
|
+
resolveStop("url");
|
|
844
|
+
await stopPromise;
|
|
845
|
+
await Promise.resolve();
|
|
846
|
+
resolveUpload();
|
|
847
|
+
await uploadPromise;
|
|
848
|
+
await Promise.resolve();
|
|
849
|
+
|
|
850
|
+
// check the cleanup tasks after the trial method has resolved
|
|
851
|
+
expect(display_element.innerHTML).toBe("");
|
|
852
|
+
expect(jsPsych.finishTrial).toHaveBeenCalledTimes(1);
|
|
853
|
+
expect(window.chs.sessionRecorder).toBeNull();
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
test("Session recording stop with null as max upload seconds (no upload timeout)", () => {
|
|
857
|
+
// simulate a resolved stop promise and upload promise
|
|
858
|
+
const stopPromise = new Promise<string>((res) => res("url"));
|
|
859
|
+
const uploadPromise = new Promise<void>((res) => res());
|
|
860
|
+
|
|
861
|
+
const recStopSpy = jest
|
|
862
|
+
.spyOn(Recorder.prototype, "stop")
|
|
863
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
864
|
+
|
|
865
|
+
const jsPsych = initJsPsych();
|
|
866
|
+
setCHSValue({ sessionRecorder: new Recorder(jsPsych) });
|
|
867
|
+
|
|
868
|
+
const stop_rec_plugin = new Rec.StopRecordPlugin(jsPsych);
|
|
869
|
+
const display_element = document.createElement("div");
|
|
870
|
+
|
|
871
|
+
const trial = {
|
|
872
|
+
type: Rec.StopRecordPlugin.info.name,
|
|
873
|
+
locale: "en-us",
|
|
874
|
+
max_upload_seconds: null,
|
|
875
|
+
} as unknown as TrialType<PluginInfo>; // need to cast here because the "type" param is a string and should be a class
|
|
876
|
+
|
|
877
|
+
stop_rec_plugin.trial(display_element, trial);
|
|
878
|
+
|
|
879
|
+
// recorder.stop should be called with null as the max upload duration
|
|
880
|
+
expect(recStopSpy).toHaveBeenCalledWith({
|
|
881
|
+
upload_timeout_ms: null,
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
// check the display cleanup
|
|
885
|
+
expect(global_display_el.innerHTML).toBe("");
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
test("Stop recording stop with failure during upload", async () => {
|
|
889
|
+
// Create a controlled promise and capture the reject function
|
|
890
|
+
let rejectStop!: (err: unknown) => void;
|
|
891
|
+
const stopPromise = new Promise<string>((_, reject) => {
|
|
892
|
+
rejectStop = reject;
|
|
893
|
+
});
|
|
894
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
895
|
+
let resolveUpload!: () => void;
|
|
896
|
+
const uploadPromise = new Promise<void>((res) => {
|
|
897
|
+
resolveUpload = res;
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
jest
|
|
901
|
+
.spyOn(Recorder.prototype, "stop")
|
|
902
|
+
.mockReturnValue({ stopped: stopPromise, uploaded: uploadPromise });
|
|
903
|
+
|
|
904
|
+
const jsPsych = initJsPsych();
|
|
905
|
+
setCHSValue({ sessionRecorder: new Recorder(jsPsych) });
|
|
906
|
+
|
|
907
|
+
const stop_rec_plugin = new Rec.StopRecordPlugin(jsPsych);
|
|
908
|
+
const display_element = document.createElement("div");
|
|
909
|
+
|
|
910
|
+
const trial = {
|
|
911
|
+
type: Rec.StopRecordPlugin.info.name,
|
|
912
|
+
locale: "en-us",
|
|
913
|
+
wait_for_upload_message: "Wait…",
|
|
914
|
+
} as unknown as TrialType<PluginInfo>; // need to cast here because the "type" param is a string and should be a class
|
|
915
|
+
|
|
916
|
+
stop_rec_plugin.trial(display_element, trial);
|
|
917
|
+
|
|
918
|
+
// Should show initial wait for upload message
|
|
919
|
+
expect(display_element.innerHTML).toBe("Wait…");
|
|
920
|
+
|
|
921
|
+
// Reject stop
|
|
922
|
+
rejectStop(new Error("upload failed"));
|
|
923
|
+
|
|
924
|
+
// Wait for plugin's `.catch()` handler to run
|
|
925
|
+
await Promise.resolve();
|
|
926
|
+
|
|
927
|
+
// TO DO: modify the plugin code to display translated error msg and/or researcher contact info
|
|
928
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
929
|
+
"StopRecordPlugin: recorder stop/upload failed.",
|
|
930
|
+
Error("upload failed"),
|
|
931
|
+
);
|
|
932
|
+
});
|