@player-ui/async-node-plugin 0.8.0-next.0 → 0.8.0-next.10

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/src/index.test.ts CHANGED
@@ -1,453 +1,569 @@
1
- import { expect, test } from "vitest";
1
+ import { expect, test, describe } from "vitest";
2
2
  import { Node, InProgressState, ViewInstance } from "@player-ui/player";
3
- import { Player } from "@player-ui/player";
3
+ import { Player, Parser } from "@player-ui/player";
4
4
  import { waitFor } from "@testing-library/react";
5
5
  import { AsyncNodePlugin, AsyncNodePluginPlugin } from "./index";
6
6
 
7
- const basicFRFWithActions = {
8
- id: "test-flow",
9
- views: [
10
- {
11
- id: "my-view",
12
- actions: [
13
- {
14
- asset: {
15
- id: "action-0",
16
- type: "action",
17
- value: "{{foo.bar}}",
7
+ describe("view", () => {
8
+ const basicFRFWithActions = {
9
+ id: "test-flow",
10
+ views: [
11
+ {
12
+ id: "my-view",
13
+ actions: [
14
+ {
15
+ asset: {
16
+ id: "action-0",
17
+ type: "action",
18
+ value: "{{foo.bar}}",
19
+ },
18
20
  },
21
+ {
22
+ id: "nodeId",
23
+ async: "true",
24
+ },
25
+ ],
26
+ },
27
+ ],
28
+ navigation: {
29
+ BEGIN: "FLOW_1",
30
+ FLOW_1: {
31
+ startState: "VIEW_1",
32
+ VIEW_1: {
33
+ state_type: "VIEW",
34
+ ref: "my-view",
35
+ transitions: {},
19
36
  },
20
- {
21
- id: "nodeId",
22
- async: "true",
23
- },
24
- ],
25
- },
26
- ],
27
- navigation: {
28
- BEGIN: "FLOW_1",
29
- FLOW_1: {
30
- startState: "VIEW_1",
31
- VIEW_1: {
32
- state_type: "VIEW",
33
- ref: "my-view",
34
- transitions: {},
35
37
  },
36
38
  },
37
- },
38
- };
39
+ };
39
40
 
40
- const asyncNodeTest = async (resolvedValue: any) => {
41
- const plugin = new AsyncNodePlugin({
42
- plugins: [new AsyncNodePluginPlugin()],
43
- });
41
+ const asyncNodeTest = async (resolvedValue: any) => {
42
+ const plugin = new AsyncNodePlugin({
43
+ plugins: [new AsyncNodePluginPlugin()],
44
+ });
44
45
 
45
- let deferredResolve: ((value: any) => void) | undefined;
46
+ let deferredResolve: ((value: any) => void) | undefined;
46
47
 
47
- plugin.hooks.onAsyncNode.tap("test", async (node: Node.Async) => {
48
- return new Promise((resolve) => {
49
- deferredResolve = resolve; // Promise would be resolved only once
50
- });
51
- });
48
+ let updateContent: any;
52
49
 
53
- let updateNumber = 0;
50
+ plugin.hooks.onAsyncNode.tap(
51
+ "test",
52
+ async (node: Node.Async, update: (content: any) => void) => {
53
+ const result = new Promise((resolve) => {
54
+ deferredResolve = resolve; // Promise would be resolved only once
55
+ });
54
56
 
55
- const player = new Player({ plugins: [plugin] });
57
+ updateContent = update;
58
+ // Return the result to follow the same mechanism as before
59
+ return result;
60
+ },
61
+ );
62
+
63
+ let updateNumber = 0;
64
+
65
+ const player = new Player({ plugins: [plugin] });
56
66
 
57
- let viewInstance: ViewInstance | undefined;
67
+ let viewInstance: ViewInstance | undefined;
58
68
 
59
- player.hooks.viewController.tap("async-node-test", (vc) => {
60
- vc.hooks.view.tap("async-node-test", (view) => {
61
- viewInstance = view;
62
- view.hooks.onUpdate.tap("async-node-test", () => {
63
- updateNumber++;
69
+ player.hooks.viewController.tap("async-node-test", (vc) => {
70
+ vc.hooks.view.tap("async-node-test", (view) => {
71
+ viewInstance = view;
72
+ view.hooks.onUpdate.tap("async-node-test", () => {
73
+ updateNumber++;
74
+ });
64
75
  });
65
76
  });
66
- });
67
77
 
68
- player.start(basicFRFWithActions as any);
78
+ player.start(basicFRFWithActions as any);
69
79
 
70
- let view = (player.getState() as InProgressState).controllers.view.currentView
71
- ?.lastUpdate;
80
+ let view = (player.getState() as InProgressState).controllers.view
81
+ .currentView?.lastUpdate;
72
82
 
73
- expect(view).toBeDefined();
74
- expect(view?.actions[0].asset.type).toBe("action");
75
- expect(view?.actions[1]).toBeUndefined();
83
+ expect(view).toBeDefined();
84
+ expect(view?.actions[0].asset.type).toBe("action");
85
+ expect(view?.actions[1]).toBeUndefined();
76
86
 
77
- await waitFor(() => {
78
- expect(deferredResolve).toBeDefined();
79
- });
87
+ await waitFor(() => {
88
+ expect(deferredResolve).toBeDefined();
89
+ });
80
90
 
81
- // Consumer responds with null/undefined
82
- if (deferredResolve) {
83
- deferredResolve(resolvedValue);
84
- }
91
+ // Consumer responds with null/undefined
92
+ if (deferredResolve) {
93
+ deferredResolve(resolvedValue);
94
+ }
85
95
 
86
- await waitFor(() => {
87
- expect(updateNumber).toBe(2);
88
- });
96
+ await waitFor(() => {
97
+ expect(updateNumber).toBe(1);
98
+ });
99
+
100
+ view = (player.getState() as InProgressState).controllers.view.currentView
101
+ ?.lastUpdate;
102
+
103
+ expect(view?.actions[0].asset.type).toBe("action");
104
+ expect(view?.actions.length).toBe(1);
89
105
 
90
- view = (player.getState() as InProgressState).controllers.view.currentView
91
- ?.lastUpdate;
106
+ // Consumer responds with null/undefined
107
+ if (deferredResolve) {
108
+ updateContent(resolvedValue);
109
+ }
110
+
111
+ //Even after an update, the view should not change as we are deleting the resolved node if there is no view update
112
+ await waitFor(() => {
113
+ expect(updateNumber).toBe(1);
114
+ });
92
115
 
93
- expect(view?.actions[0].asset.type).toBe("action");
94
- expect(view?.actions.length).toBe(1);
116
+ view = (player.getState() as InProgressState).controllers.view.currentView
117
+ ?.lastUpdate;
95
118
 
96
- viewInstance?.update();
119
+ expect(view?.actions[0].asset.type).toBe("action");
120
+ expect(view?.actions.length).toBe(1);
121
+ };
97
122
 
98
- await waitFor(() => {
99
- expect(updateNumber).toBe(3);
123
+ test("should return current node view when the resolved node is null", async () => {
124
+ await asyncNodeTest(null);
100
125
  });
101
126
 
102
- view = (player.getState() as InProgressState).controllers.view.currentView
103
- ?.lastUpdate;
127
+ test("should return current node view when the resolved node is undefined", async () => {
128
+ await asyncNodeTest(undefined);
129
+ });
104
130
 
105
- expect(view?.actions[0].asset.type).toBe("action");
106
- expect(view?.actions.length).toBe(1);
107
- };
131
+ test("can handle multiple updates through callback mechanism", async () => {
132
+ const plugin = new AsyncNodePlugin({
133
+ plugins: [new AsyncNodePluginPlugin()],
134
+ });
108
135
 
109
- test("should return current node view when the resolved node is null", async () => {
110
- await asyncNodeTest(null);
111
- });
136
+ let deferredResolve: ((value: any) => void) | undefined;
112
137
 
113
- test("should return current node view when the resolved node is undefined", async () => {
114
- await asyncNodeTest(undefined);
115
- });
138
+ let updateContent: any;
116
139
 
117
- test("replaces async nodes with provided node", async () => {
118
- const plugin = new AsyncNodePlugin({
119
- plugins: [new AsyncNodePluginPlugin()],
120
- });
140
+ plugin.hooks.onAsyncNode.tap(
141
+ "test",
142
+ async (node: Node.Async, update: (content: any) => void) => {
143
+ const result = new Promise((resolve) => {
144
+ deferredResolve = resolve; // Promise would be resolved only once
145
+ });
121
146
 
122
- let deferredResolve: ((value: any) => void) | undefined;
147
+ updateContent = update;
148
+ // Return the result to follow the same mechanism as before
149
+ return result;
150
+ },
151
+ );
123
152
 
124
- plugin.hooks.onAsyncNode.tap("test", async (node: Node.Async) => {
125
- return new Promise((resolve) => {
126
- deferredResolve = resolve;
127
- });
128
- });
129
- let updateNumber = 0;
153
+ let updateNumber = 0;
130
154
 
131
- const player = new Player({ plugins: [plugin] });
155
+ const player = new Player({ plugins: [plugin] });
132
156
 
133
- player.hooks.viewController.tap("async-node-test", (vc) => {
134
- vc.hooks.view.tap("async-node-test", (view) => {
135
- view.hooks.onUpdate.tap("async-node-test", (update) => {
136
- updateNumber++;
157
+ player.hooks.viewController.tap("async-node-test", (vc) => {
158
+ vc.hooks.view.tap("async-node-test", (view) => {
159
+ view.hooks.onUpdate.tap("async-node-test", (update) => {
160
+ updateNumber++;
161
+ });
137
162
  });
138
163
  });
139
- });
140
164
 
141
- player.start(basicFRFWithActions as any);
165
+ player.start(basicFRFWithActions as any);
142
166
 
143
- let view = (player.getState() as InProgressState).controllers.view.currentView
144
- ?.lastUpdate;
167
+ let view = (player.getState() as InProgressState).controllers.view
168
+ .currentView?.lastUpdate;
145
169
 
146
- expect(view).toBeDefined();
147
- expect(view?.actions[0].asset.type).toBe("action");
148
- expect(view?.actions[1]).toBeUndefined();
149
- expect(updateNumber).toBe(1);
170
+ expect(view).toBeDefined();
171
+ expect(view?.actions[1]).toBeUndefined();
150
172
 
151
- await waitFor(() => {
152
- expect(deferredResolve).toBeDefined();
153
- });
173
+ await waitFor(() => {
174
+ expect(updateNumber).toBe(1);
175
+ expect(deferredResolve).toBeDefined();
176
+ });
154
177
 
155
- if (deferredResolve) {
156
- deferredResolve({
157
- asset: {
158
- id: "next-label-action",
159
- type: "action",
160
- value: "dummy value",
161
- },
178
+ if (deferredResolve) {
179
+ deferredResolve({
180
+ asset: {
181
+ id: "next-label-action",
182
+ type: "action",
183
+ value: "dummy value",
184
+ },
185
+ });
186
+ }
187
+
188
+ await waitFor(() => {
189
+ expect(updateNumber).toBe(2);
162
190
  });
163
- }
164
191
 
165
- await waitFor(() => {
192
+ view = (player.getState() as InProgressState).controllers.view.currentView
193
+ ?.lastUpdate;
194
+
195
+ expect(view?.actions[0].asset.type).toBe("action");
196
+ expect(view?.actions[1].asset.type).toBe("action");
166
197
  expect(updateNumber).toBe(2);
167
- });
168
198
 
169
- view = (player.getState() as InProgressState).controllers.view.currentView
170
- ?.lastUpdate;
199
+ if (deferredResolve) {
200
+ updateContent(null);
201
+ }
171
202
 
172
- expect(view?.actions[0].asset.type).toBe("action");
173
- expect(view?.actions[1].asset.type).toBe("action");
174
- });
203
+ await waitFor(() => {
204
+ expect(updateNumber).toBe(3);
205
+ });
175
206
 
176
- test("replaces async nodes with multi node", async () => {
177
- const plugin = new AsyncNodePlugin({
178
- plugins: [new AsyncNodePluginPlugin()],
179
- });
207
+ view = (player.getState() as InProgressState).controllers.view.currentView
208
+ ?.lastUpdate;
180
209
 
181
- let deferredResolve: ((value: any) => void) | undefined;
210
+ expect(view?.actions[0].asset.type).toBe("action");
211
+ expect(view?.actions[1]).toBeUndefined();
212
+ });
182
213
 
183
- plugin.hooks.onAsyncNode.tap("test", async (node) => {
184
- return new Promise((resolve) => {
185
- deferredResolve = resolve;
214
+ test("replaces async nodes with provided node", async () => {
215
+ const plugin = new AsyncNodePlugin({
216
+ plugins: [new AsyncNodePluginPlugin()],
186
217
  });
187
- });
188
218
 
189
- let updateNumber = 0;
219
+ let deferredResolve: ((value: any) => void) | undefined;
220
+
221
+ plugin.hooks.onAsyncNode.tap("test", async (node: Node.Async) => {
222
+ return new Promise((resolve) => {
223
+ deferredResolve = resolve;
224
+ });
225
+ });
226
+ let updateNumber = 0;
190
227
 
191
- const player = new Player({ plugins: [plugin] });
228
+ const player = new Player({ plugins: [plugin] });
192
229
 
193
- player.hooks.viewController.tap("async-node-test", (vc) => {
194
- vc.hooks.view.tap("async-node-test", (view) => {
195
- view.hooks.onUpdate.tap("async-node-test", (update) => {
196
- updateNumber++;
230
+ player.hooks.viewController.tap("async-node-test", (vc) => {
231
+ vc.hooks.view.tap("async-node-test", (view) => {
232
+ view.hooks.onUpdate.tap("async-node-test", (update) => {
233
+ updateNumber++;
234
+ });
197
235
  });
198
236
  });
199
- });
200
237
 
201
- player.start(basicFRFWithActions as any);
238
+ player.start(basicFRFWithActions as any);
202
239
 
203
- let view = (player.getState() as InProgressState).controllers.view.currentView
204
- ?.lastUpdate;
240
+ let view = (player.getState() as InProgressState).controllers.view
241
+ .currentView?.lastUpdate;
205
242
 
206
- expect(view).toBeDefined();
207
- expect(view?.actions[1]).toBeUndefined();
208
- expect(updateNumber).toBe(1);
243
+ expect(view).toBeDefined();
244
+ expect(view?.actions[0].asset.type).toBe("action");
245
+ expect(view?.actions[1]).toBeUndefined();
246
+ expect(updateNumber).toBe(1);
209
247
 
210
- await waitFor(() => {
211
- expect(deferredResolve).toBeDefined();
212
- });
248
+ await waitFor(() => {
249
+ expect(deferredResolve).toBeDefined();
250
+ });
213
251
 
214
- if (deferredResolve) {
215
- deferredResolve([
216
- {
252
+ if (deferredResolve) {
253
+ deferredResolve({
217
254
  asset: {
218
- id: "value-1",
219
- type: "text",
220
- value: "1st value in the multinode",
221
- },
222
- },
223
- {
224
- asset: {
225
- id: "value-2",
226
- type: "text",
227
- value: "2nd value in the multinode",
255
+ id: "next-label-action",
256
+ type: "action",
257
+ value: "dummy value",
228
258
  },
229
- },
230
- ]);
231
- }
232
-
233
- await waitFor(() => {
234
- expect(updateNumber).toBe(2);
235
- });
259
+ });
260
+ }
236
261
 
237
- view = (player.getState() as InProgressState).controllers.view.currentView
238
- ?.lastUpdate;
262
+ await waitFor(() => {
263
+ expect(updateNumber).toBe(2);
264
+ });
239
265
 
240
- expect(view?.actions[0].asset.type).toBe("action");
241
- expect(view?.actions[1].asset.type).toBe("text");
242
- expect(view?.actions[2].asset.type).toBe("text");
243
- });
266
+ view = (player.getState() as InProgressState).controllers.view.currentView
267
+ ?.lastUpdate;
244
268
 
245
- test("replaces async nodes with chained multiNodes", async () => {
246
- const plugin = new AsyncNodePlugin({
247
- plugins: [new AsyncNodePluginPlugin()],
269
+ expect(view?.actions[0].asset.type).toBe("action");
270
+ expect(view?.actions[1].asset.type).toBe("action");
248
271
  });
249
272
 
250
- let deferredResolve: ((value: any) => void) | undefined;
251
-
252
- plugin.hooks.onAsyncNode.tap("test", async (node: Node.Async) => {
253
- return new Promise((resolve) => {
254
- deferredResolve = resolve;
273
+ test("replaces async nodes with multi node", async () => {
274
+ const plugin = new AsyncNodePlugin({
275
+ plugins: [new AsyncNodePluginPlugin()],
255
276
  });
256
- });
257
- let updateNumber = 0;
258
277
 
259
- const player = new Player({ plugins: [plugin] });
278
+ let deferredResolve: ((value: any) => void) | undefined;
260
279
 
261
- player.hooks.viewController.tap("async-node-test", (vc) => {
262
- vc.hooks.view.tap("async-node-test", (view) => {
263
- view.hooks.onUpdate.tap("async-node-test", (update) => {
264
- updateNumber++;
280
+ plugin.hooks.onAsyncNode.tap("test", async (node) => {
281
+ return new Promise((resolve) => {
282
+ deferredResolve = resolve;
265
283
  });
266
284
  });
267
- });
268
285
 
269
- player.start(basicFRFWithActions as any);
286
+ let updateNumber = 0;
287
+
288
+ const player = new Player({ plugins: [plugin] });
289
+
290
+ player.hooks.viewController.tap("async-node-test", (vc) => {
291
+ vc.hooks.view.tap("async-node-test", (view) => {
292
+ view.hooks.onUpdate.tap("async-node-test", (update) => {
293
+ updateNumber++;
294
+ });
295
+ });
296
+ });
270
297
 
271
- let view = (player.getState() as InProgressState).controllers.view.currentView
272
- ?.lastUpdate;
298
+ player.start(basicFRFWithActions as any);
273
299
 
274
- expect(view).toBeDefined();
275
- expect(view?.actions[1]).toBeUndefined();
300
+ let view = (player.getState() as InProgressState).controllers.view
301
+ .currentView?.lastUpdate;
276
302
 
277
- await waitFor(() => {
303
+ expect(view).toBeDefined();
304
+ expect(view?.actions[1]).toBeUndefined();
278
305
  expect(updateNumber).toBe(1);
279
- expect(deferredResolve).toBeDefined();
280
- });
281
306
 
282
- if (deferredResolve) {
283
- deferredResolve([
284
- {
285
- asset: {
286
- id: "value-1",
287
- type: "text",
288
- value: "1st value in the multinode",
307
+ await waitFor(() => {
308
+ expect(deferredResolve).toBeDefined();
309
+ });
310
+
311
+ if (deferredResolve) {
312
+ deferredResolve([
313
+ {
314
+ asset: {
315
+ id: "value-1",
316
+ type: "text",
317
+ value: "1st value in the multinode",
318
+ },
289
319
  },
290
- },
291
- {
292
- id: "another-async",
293
- async: true,
294
- },
295
- ]);
296
- }
320
+ {
321
+ asset: {
322
+ id: "value-2",
323
+ type: "text",
324
+ value: "2nd value in the multinode",
325
+ },
326
+ },
327
+ ]);
328
+ }
297
329
 
298
- await waitFor(() => {
299
- expect(updateNumber).toBe(2);
330
+ await waitFor(() => {
331
+ expect(updateNumber).toBe(2);
332
+ });
333
+
334
+ view = (player.getState() as InProgressState).controllers.view.currentView
335
+ ?.lastUpdate;
336
+
337
+ expect(view?.actions[0].asset.type).toBe("action");
338
+ expect(view?.actions[1].asset.type).toBe("text");
339
+ expect(view?.actions[2].asset.type).toBe("text");
300
340
  });
301
341
 
302
- view = (player.getState() as InProgressState).controllers.view.currentView
303
- ?.lastUpdate;
342
+ test("replaces async nodes with chained multiNodes", async () => {
343
+ const plugin = new AsyncNodePlugin({
344
+ plugins: [new AsyncNodePluginPlugin()],
345
+ });
304
346
 
305
- expect(view?.actions[0].asset.type).toBe("action");
306
- expect(view?.actions[1].asset.type).toBe("text");
307
- expect(view?.actions[2]).toBeUndefined();
308
- expect(updateNumber).toBe(2);
347
+ let deferredResolve: ((value: any) => void) | undefined;
309
348
 
310
- if (deferredResolve) {
311
- deferredResolve([
312
- {
313
- asset: {
314
- id: "value-2",
315
- type: "text",
316
- value: "2nd value in the multinode",
349
+ plugin.hooks.onAsyncNode.tap("test", async (node: Node.Async) => {
350
+ return new Promise((resolve) => {
351
+ deferredResolve = resolve;
352
+ });
353
+ });
354
+ let updateNumber = 0;
355
+
356
+ const player = new Player({ plugins: [plugin] });
357
+
358
+ player.hooks.viewController.tap("async-node-test", (vc) => {
359
+ vc.hooks.view.tap("async-node-test", (view) => {
360
+ view.hooks.onUpdate.tap("async-node-test", (update) => {
361
+ updateNumber++;
362
+ });
363
+ });
364
+ });
365
+
366
+ player.start(basicFRFWithActions as any);
367
+
368
+ let view = (player.getState() as InProgressState).controllers.view
369
+ .currentView?.lastUpdate;
370
+
371
+ expect(view).toBeDefined();
372
+ expect(view?.actions[1]).toBeUndefined();
373
+
374
+ await waitFor(() => {
375
+ expect(updateNumber).toBe(1);
376
+ expect(deferredResolve).toBeDefined();
377
+ });
378
+
379
+ if (deferredResolve) {
380
+ deferredResolve([
381
+ {
382
+ asset: {
383
+ id: "value-1",
384
+ type: "text",
385
+ value: "1st value in the multinode",
386
+ },
317
387
  },
318
- },
319
- {
320
- asset: {
321
- id: "value-3",
322
- type: "text",
323
- value: "3rd value in the multinode",
388
+ {
389
+ id: "another-async",
390
+ async: true,
324
391
  },
325
- },
326
- ]);
327
- }
392
+ ]);
393
+ }
328
394
 
329
- await waitFor(() => {
330
- expect(updateNumber).toBe(3);
331
- });
395
+ await waitFor(() => {
396
+ expect(updateNumber).toBe(2);
397
+ });
332
398
 
333
- view = (player.getState() as InProgressState).controllers.view.currentView
334
- ?.lastUpdate;
399
+ view = (player.getState() as InProgressState).controllers.view.currentView
400
+ ?.lastUpdate;
335
401
 
336
- expect(view?.actions[0].asset.type).toBe("action");
337
- expect(view?.actions[1].asset.type).toBe("text");
338
- expect(view?.actions[2].asset.type).toBe("text");
339
- expect(view?.actions[3].asset.type).toBe("text");
340
- });
402
+ expect(view?.actions[0].asset.type).toBe("action");
403
+ expect(view?.actions[1].asset.type).toBe("text");
404
+ expect(view?.actions[2]).toBeUndefined();
405
+ expect(updateNumber).toBe(2);
406
+
407
+ if (deferredResolve) {
408
+ deferredResolve([
409
+ {
410
+ asset: {
411
+ id: "value-2",
412
+ type: "text",
413
+ value: "2nd value in the multinode",
414
+ },
415
+ },
416
+ {
417
+ asset: {
418
+ id: "value-3",
419
+ type: "text",
420
+ value: "3rd value in the multinode",
421
+ },
422
+ },
423
+ ]);
424
+ }
425
+
426
+ await waitFor(() => {
427
+ expect(updateNumber).toBe(3);
428
+ });
429
+
430
+ view = (player.getState() as InProgressState).controllers.view.currentView
431
+ ?.lastUpdate;
341
432
 
342
- test("replaces async nodes with chained multiNodes singular", async () => {
343
- const plugin = new AsyncNodePlugin({
344
- plugins: [new AsyncNodePluginPlugin()],
433
+ expect(view?.actions[0].asset.type).toBe("action");
434
+ expect(view?.actions[1].asset.type).toBe("text");
435
+ expect(view?.actions[2].asset.type).toBe("text");
436
+ expect(view?.actions[3].asset.type).toBe("text");
345
437
  });
346
438
 
347
- let deferredResolve: ((value: any) => void) | undefined;
439
+ test("replaces async nodes with chained multiNodes singular", async () => {
440
+ const plugin = new AsyncNodePlugin({
441
+ plugins: [new AsyncNodePluginPlugin()],
442
+ });
443
+
444
+ let deferredResolve: ((value: any) => void) | undefined;
348
445
 
349
- plugin.hooks.onAsyncNode.tap("test", async (node: Node.Async) => {
350
- return new Promise((resolve) => {
351
- deferredResolve = resolve;
446
+ plugin.hooks.onAsyncNode.tap("test", async (node: Node.Async) => {
447
+ return new Promise((resolve) => {
448
+ deferredResolve = resolve;
449
+ });
352
450
  });
353
- });
354
- let updateNumber = 0;
451
+ let updateNumber = 0;
355
452
 
356
- const player = new Player({ plugins: [plugin] });
453
+ const player = new Player({ plugins: [plugin] });
357
454
 
358
- player.hooks.viewController.tap("async-node-test", (vc) => {
359
- vc.hooks.view.tap("async-node-test", (view) => {
360
- view.hooks.onUpdate.tap("async-node-test", (update) => {
361
- updateNumber++;
455
+ player.hooks.viewController.tap("async-node-test", (vc) => {
456
+ vc.hooks.view.tap("async-node-test", (view) => {
457
+ view.hooks.onUpdate.tap("async-node-test", (update) => {
458
+ updateNumber++;
459
+ });
362
460
  });
363
461
  });
364
- });
365
462
 
366
- player.start(basicFRFWithActions as any);
463
+ player.start(basicFRFWithActions as any);
367
464
 
368
- let view = (player.getState() as InProgressState).controllers.view.currentView
369
- ?.lastUpdate;
465
+ let view = (player.getState() as InProgressState).controllers.view
466
+ .currentView?.lastUpdate;
370
467
 
371
- expect(view).toBeDefined();
372
- expect(view?.actions[1]).toBeUndefined();
468
+ expect(view).toBeDefined();
469
+ expect(view?.actions[1]).toBeUndefined();
373
470
 
374
- await waitFor(() => {
375
- expect(updateNumber).toBe(1);
376
- expect(deferredResolve).toBeDefined();
377
- });
471
+ await waitFor(() => {
472
+ expect(updateNumber).toBe(1);
473
+ expect(deferredResolve).toBeDefined();
474
+ });
378
475
 
379
- if (deferredResolve) {
380
- deferredResolve([
381
- {
382
- asset: {
383
- id: "value-1",
384
- type: "text",
385
- value: "1st value in the multinode",
476
+ if (deferredResolve) {
477
+ deferredResolve([
478
+ {
479
+ asset: {
480
+ id: "value-1",
481
+ type: "text",
482
+ value: "1st value in the multinode",
483
+ },
386
484
  },
387
- },
388
- {
389
- id: "another-async",
390
- async: true,
391
- },
392
- ]);
393
- }
485
+ {
486
+ id: "another-async",
487
+ async: true,
488
+ },
489
+ ]);
490
+ }
394
491
 
395
- await waitFor(() => {
396
- expect(updateNumber).toBe(2);
397
- });
492
+ await waitFor(() => {
493
+ expect(updateNumber).toBe(2);
494
+ });
398
495
 
399
- view = (player.getState() as InProgressState).controllers.view.currentView
400
- ?.lastUpdate;
496
+ view = (player.getState() as InProgressState).controllers.view.currentView
497
+ ?.lastUpdate;
401
498
 
402
- expect(view?.actions[0].asset.type).toBe("action");
403
- expect(view?.actions[1].asset.type).toBe("text");
404
- expect(view?.actions[2]).toBeUndefined();
499
+ expect(view?.actions[0].asset.type).toBe("action");
500
+ expect(view?.actions[1].asset.type).toBe("text");
501
+ expect(view?.actions[2]).toBeUndefined();
405
502
 
406
- if (deferredResolve) {
407
- deferredResolve({
408
- asset: {
409
- id: "value-2",
410
- type: "text",
411
- value: "2nd value in the multinode",
412
- },
503
+ if (deferredResolve) {
504
+ deferredResolve({
505
+ asset: {
506
+ id: "value-2",
507
+ type: "text",
508
+ value: "2nd value in the multinode",
509
+ },
510
+ });
511
+ }
512
+
513
+ await waitFor(() => {
514
+ expect(updateNumber).toBe(3);
413
515
  });
414
- }
415
516
 
416
- await waitFor(() => {
417
- expect(updateNumber).toBe(3);
517
+ view = (player.getState() as InProgressState).controllers.view.currentView
518
+ ?.lastUpdate;
519
+
520
+ expect(view?.actions[0].asset.type).toBe("action");
521
+ expect(view?.actions[1].asset.type).toBe("text");
522
+ expect(view?.actions[2].asset.type).toBe("text");
418
523
  });
419
524
 
420
- view = (player.getState() as InProgressState).controllers.view.currentView
421
- ?.lastUpdate;
525
+ test("should call onAsyncNode hook when async node is encountered", async () => {
526
+ const plugin = new AsyncNodePlugin({
527
+ plugins: [new AsyncNodePluginPlugin()],
528
+ });
422
529
 
423
- expect(view?.actions[0].asset.type).toBe("action");
424
- expect(view?.actions[1].asset.type).toBe("text");
425
- expect(view?.actions[2].asset.type).toBe("text");
426
- });
530
+ let localNode: Node.Async;
531
+ plugin.hooks.onAsyncNode.tap("test", async (node: Node.Async) => {
532
+ if (node !== null) {
533
+ // assigns node value to a local variable
534
+ localNode = node;
535
+ }
427
536
 
428
- test("should call onAsyncNode hook when async node is encountered", async () => {
429
- const plugin = new AsyncNodePlugin({
430
- plugins: [new AsyncNodePluginPlugin()],
431
- });
537
+ return new Promise((resolve) => {
538
+ resolve("Promise resolved");
539
+ });
540
+ });
432
541
 
433
- let localNode: Node.Async;
434
- plugin.hooks.onAsyncNode.tap("test", async (node: Node.Async) => {
435
- if (node !== null) {
436
- // assigns node value to a local variable
437
- localNode = node;
438
- }
542
+ const player = new Player({ plugins: [plugin] });
543
+
544
+ player.start(basicFRFWithActions as any);
439
545
 
440
- return new Promise((resolve) => {
441
- resolve("Promise resolved");
546
+ await waitFor(() => {
547
+ expect(localNode.id).toStrictEqual("nodeId");
548
+ expect(localNode.type).toStrictEqual("async");
442
549
  });
443
550
  });
551
+ });
552
+
553
+ describe("parser", () => {
554
+ test("missing node-id parent async node", async () => {
555
+ const parser = new Parser();
556
+ new AsyncNodePluginPlugin().applyParser(parser);
557
+ const parsedAST = parser.parseObject({ async: "true" });
444
558
 
445
- const player = new Player({ plugins: [plugin] });
559
+ expect(parsedAST).toStrictEqual(null);
560
+ });
446
561
 
447
- player.start(basicFRFWithActions as any);
562
+ test("missing node-id child async node", async () => {
563
+ const parser = new Parser();
564
+ new AsyncNodePluginPlugin().applyParser(parser);
565
+ const parsedAST = parser.parseObject({ fields: { async: "true" } });
448
566
 
449
- await waitFor(() => {
450
- expect(localNode.id).toStrictEqual("nodeId");
451
- expect(localNode.type).toStrictEqual("async");
567
+ expect(parsedAST).toStrictEqual(null);
452
568
  });
453
569
  });