test_Actions.js (22880B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { actions, CLICK_INTERVAL, ClickTracker } = ChromeUtils.importESModule( 8 "chrome://remote/content/shared/webdriver/Actions.sys.mjs" 9 ); 10 11 const { setTimeout } = ChromeUtils.importESModule( 12 "resource://gre/modules/Timer.sys.mjs" 13 ); 14 15 const XHTMLNS = "http://www.w3.org/1999/xhtml"; 16 17 const domEl = { 18 nodeType: 1, 19 ELEMENT_NODE: 1, 20 namespaceURI: XHTMLNS, 21 }; 22 23 add_task(function test_createInputState() { 24 for (let type of ["none", "key", "pointer" /*"wheel"*/]) { 25 const state = new actions.State(); 26 const id = "device"; 27 const actionSequence = { 28 type, 29 id, 30 actions: [], 31 }; 32 actions.Chain.fromJSON(state, [actionSequence]); 33 equal(state.inputStateMap.size, 1); 34 equal(state.inputStateMap.get(id).constructor.type, type); 35 } 36 }); 37 38 add_task(async function test_defaultPointerParameters() { 39 let state = new actions.State(); 40 const inputTickActions = [ 41 { type: "pointer", subtype: "pointerDown", button: 0 }, 42 ]; 43 const chain = await actions.Chain.fromJSON( 44 state, 45 chainForTick(inputTickActions) 46 ); 47 const pointerAction = chain[0][0]; 48 equal( 49 state.getInputSource(pointerAction.id).pointer.constructor.type, 50 "mouse" 51 ); 52 }); 53 54 add_task(async function test_processPointerParameters() { 55 for (let subtype of ["pointerDown", "pointerUp"]) { 56 for (let pointerType of [2, true, {}, []]) { 57 const inputTickActions = [ 58 { 59 type: "pointer", 60 parameters: { pointerType }, 61 subtype, 62 button: 0, 63 }, 64 ]; 65 let message = `Action sequence with parameters: {pointerType: ${pointerType} subtype: ${subtype}}`; 66 await checkFromJSONErrors( 67 inputTickActions, 68 /Expected "pointerType" to be a string/, 69 message 70 ); 71 } 72 73 for (let pointerType of ["", "foo"]) { 74 const inputTickActions = [ 75 { 76 type: "pointer", 77 parameters: { pointerType }, 78 subtype, 79 button: 0, 80 }, 81 ]; 82 let message = `Action sequence with parameters: {pointerType: ${pointerType} subtype: ${subtype}}`; 83 await checkFromJSONErrors( 84 inputTickActions, 85 /Expected "pointerType" to be one of/, 86 message 87 ); 88 } 89 } 90 91 for (let pointerType of ["mouse" /*"touch"*/]) { 92 let state = new actions.State(); 93 const inputTickActions = [ 94 { 95 type: "pointer", 96 parameters: { pointerType }, 97 subtype: "pointerDown", 98 button: 0, 99 }, 100 ]; 101 const chain = await actions.Chain.fromJSON( 102 state, 103 chainForTick(inputTickActions) 104 ); 105 const pointerAction = chain[0][0]; 106 equal( 107 state.getInputSource(pointerAction.id).pointer.constructor.type, 108 pointerType 109 ); 110 } 111 }); 112 113 add_task(async function test_processPointerDownAction() { 114 for (let button of [-1, "a"]) { 115 const inputTickActions = [ 116 { type: "pointer", subtype: "pointerDown", button }, 117 ]; 118 await checkFromJSONErrors( 119 inputTickActions, 120 /Expected "button" to be a positive integer/, 121 `pointerDown with {button: ${button}}` 122 ); 123 } 124 let state = new actions.State(); 125 const inputTickActions = [ 126 { type: "pointer", subtype: "pointerDown", button: 5 }, 127 ]; 128 const chain = await actions.Chain.fromJSON( 129 state, 130 chainForTick(inputTickActions) 131 ); 132 equal(chain[0][0].button, 5); 133 }); 134 135 add_task(async function test_validateActionDurationAndCoordinates() { 136 for (let [type, subtype] of [ 137 ["none", "pause"], 138 ["pointer", "pointerMove"], 139 ]) { 140 for (let duration of [-1, "a"]) { 141 const inputTickActions = [{ type, subtype, duration }]; 142 await checkFromJSONErrors( 143 inputTickActions, 144 /Expected "duration" to be a positive integer/, 145 `{subtype} with {duration: ${duration}}` 146 ); 147 } 148 } 149 for (let name of ["x", "y"]) { 150 const actionItem = { 151 type: "pointer", 152 subtype: "pointerMove", 153 duration: 5000, 154 }; 155 actionItem[name] = "a"; 156 await checkFromJSONErrors( 157 [actionItem], 158 /Expected "." to be a finite number/, 159 `${name}: "a", subtype: pointerMove` 160 ); 161 } 162 }); 163 164 add_task(async function test_processPointerMoveActionOriginStringValidation() { 165 for (let origin of ["", "viewports", "pointers"]) { 166 const inputTickActions = [ 167 { 168 type: "pointer", 169 x: 0, 170 y: 0, 171 duration: 5000, 172 subtype: "pointerMove", 173 origin, 174 }, 175 ]; 176 await checkFromJSONErrors( 177 inputTickActions, 178 /Expected "origin" to be undefined, "viewport", "pointer", or an element/, 179 `actionItem.origin: ${origin}`, 180 { isElementOrigin: () => false } 181 ); 182 } 183 }); 184 185 add_task(async function test_processPointerMoveActionOriginElementValidation() { 186 const element = { foo: "bar" }; 187 const inputTickActions = [ 188 { 189 type: "pointer", 190 x: 0, 191 y: 0, 192 duration: 5000, 193 subtype: "pointerMove", 194 origin: element, 195 }, 196 ]; 197 198 // invalid element origin 199 await checkFromJSONErrors( 200 inputTickActions, 201 /Expected "origin" to be undefined, "viewport", "pointer", or an element/, 202 `actionItem.origin: (${getTypeString(element)})`, 203 { isElementOrigin: elem => "foo1" in elem } 204 ); 205 206 let state = new actions.State(); 207 const actionsOptions = { 208 isElementOrigin: elem => "foo" in elem, 209 getElementOrigin: elem => elem, 210 }; 211 212 // valid element origin 213 const chain = await actions.Chain.fromJSON( 214 state, 215 chainForTick(inputTickActions), 216 actionsOptions 217 ); 218 deepEqual(chain[0][0].origin, { element }); 219 }); 220 221 add_task(async function test_processPointerMoveActionDefaultOrigin() { 222 let state = new actions.State(); 223 const inputTickActions = [ 224 { type: "pointer", x: 0, y: 0, duration: 5000, subtype: "pointerMove" }, 225 ]; 226 const chain = await actions.Chain.fromJSON( 227 state, 228 chainForTick(inputTickActions), 229 {} 230 ); 231 // The default is viewport coordinates which have an origin at [0,0] and don't depend on inputSource 232 deepEqual(chain[0][0].origin.getOriginCoordinates(null, null), { 233 x: 0, 234 y: 0, 235 }); 236 }); 237 238 add_task(async function test_processPointerMoveAction() { 239 let state = new actions.State(); 240 const actionItems = [ 241 { 242 duration: 5000, 243 type: "pointerMove", 244 origin: undefined, 245 x: 0, 246 y: 0, 247 }, 248 { 249 duration: undefined, 250 type: "pointerMove", 251 origin: domEl, 252 x: 0, 253 y: 0, 254 }, 255 { 256 duration: 5000, 257 type: "pointerMove", 258 x: 1, 259 y: 2, 260 origin: undefined, 261 }, 262 ]; 263 const actionSequence = { 264 id: "some_id", 265 type: "pointer", 266 actions: actionItems, 267 }; 268 let actionsOptions = { 269 isElementOrigin: elem => elem == domEl, 270 getElementOrigin: elem => elem, 271 }; 272 273 let chain = await actions.Chain.fromJSON( 274 state, 275 [actionSequence], 276 actionsOptions 277 ); 278 equal(chain.length, actionItems.length); 279 for (let i = 0; i < actionItems.length; i++) { 280 let actual = chain[i][0]; 281 let expected = actionItems[i]; 282 equal(actual.duration, expected.duration); 283 equal(actual.x, expected.x); 284 equal(actual.y, expected.y); 285 286 let originClass; 287 if (expected.origin === undefined || expected.origin == "viewport") { 288 originClass = "ViewportOrigin"; 289 } else if (expected.origin === "pointer") { 290 originClass = "PointerOrigin"; 291 } else { 292 originClass = "ElementOrigin"; 293 } 294 deepEqual(actual.origin.constructor.name, originClass); 295 } 296 }); 297 298 add_task(async function test_computePointerDestinationViewport() { 299 const state = new actions.State(); 300 const inputTickActions = [ 301 { 302 type: "pointer", 303 subtype: "pointerMove", 304 x: 100, 305 y: 200, 306 origin: "viewport", 307 }, 308 ]; 309 const chain = await actions.Chain.fromJSON( 310 state, 311 chainForTick(inputTickActions), 312 {} 313 ); 314 const actionItem = chain[0][0]; 315 const inputSource = state.getInputSource(actionItem.id); 316 // these values should not affect the outcome 317 inputSource.moveTo(99, 10); 318 const target = await actionItem.origin.getTargetCoordinates( 319 inputSource, 320 [actionItem.x, actionItem.y], 321 null 322 ); 323 equal(actionItem.x, target[0]); 324 equal(actionItem.y, target[1]); 325 }); 326 327 add_task(async function test_computePointerDestinationPointer() { 328 const state = new actions.State(); 329 const inputTickActions = [ 330 { 331 type: "pointer", 332 subtype: "pointerMove", 333 x: 100, 334 y: 200, 335 origin: "pointer", 336 }, 337 ]; 338 const chain = await actions.Chain.fromJSON( 339 state, 340 chainForTick(inputTickActions), 341 {} 342 ); 343 const actionItem = chain[0][0]; 344 const inputSource = state.getInputSource(actionItem.id); 345 inputSource.moveTo(10, 99); 346 const target = await actionItem.origin.getTargetCoordinates( 347 inputSource, 348 [actionItem.x, actionItem.y], 349 null 350 ); 351 equal(actionItem.x + inputSource.x, target[0]); 352 equal(actionItem.y + inputSource.y, target[1]); 353 }); 354 355 add_task(async function test_processPointerAction() { 356 for (let pointerType of ["mouse", "touch"]) { 357 const actionItems = [ 358 { 359 duration: 2000, 360 type: "pause", 361 }, 362 { 363 type: "pointerMove", 364 duration: 2000, 365 x: 0, 366 y: 0, 367 }, 368 { 369 type: "pointerUp", 370 button: 1, 371 }, 372 ]; 373 let actionSequence = { 374 type: "pointer", 375 id: "some_id", 376 parameters: { 377 pointerType, 378 }, 379 actions: actionItems, 380 }; 381 const state = new actions.State(); 382 const chain = await actions.Chain.fromJSON(state, [actionSequence], {}); 383 equal(chain.length, actionItems.length); 384 for (let i = 0; i < actionItems.length; i++) { 385 const actual = chain[i][0]; 386 const expected = actionItems[i]; 387 equal(actual.type, expected.type === "pause" ? "none" : "pointer"); 388 equal(actual.subtype, expected.type); 389 equal(actual.id, actionSequence.id); 390 if (expected.type === "pointerUp") { 391 equal(actual.button, expected.button); 392 } else { 393 equal(actual.duration, expected.duration); 394 } 395 if (expected.type !== "pause") { 396 equal( 397 state.getInputSource(actual.id).pointer.constructor.type, 398 pointerType 399 ); 400 } 401 } 402 } 403 }); 404 405 add_task(async function test_processPauseAction() { 406 for (let type of ["none", "key", "pointer"]) { 407 const state = new actions.State(); 408 const actionSequence = { 409 type, 410 id: "some_id", 411 actions: [{ type: "pause", duration: 5000 }], 412 }; 413 const chain = await actions.Chain.fromJSON(state, [actionSequence], {}); 414 const actionItem = chain[0][0]; 415 equal(actionItem.type, "none"); 416 equal(actionItem.subtype, "pause"); 417 equal(actionItem.id, "some_id"); 418 equal(actionItem.duration, 5000); 419 } 420 const state = new actions.State(); 421 const actionSequence = { 422 type: "none", 423 id: "some_id", 424 actions: [{ type: "pause" }], 425 }; 426 const chain = await actions.Chain.fromJSON(state, [actionSequence], {}); 427 const actionItem = chain[0][0]; 428 equal(actionItem.duration, undefined); 429 }); 430 431 add_task(async function test_processActionSubtypeValidation() { 432 for (let type of ["none", "key", "pointer"]) { 433 const message = `type: ${type}, subtype: dancing`; 434 const inputTickActions = [{ type, subtype: "dancing" }]; 435 await checkFromJSONErrors( 436 inputTickActions, 437 new RegExp(`Expected known subtype for type`), 438 message 439 ); 440 } 441 }); 442 443 add_task(async function test_processKeyActionDown() { 444 for (let value of [-1, undefined, [], ["a"], { length: 1 }, null]) { 445 const inputTickActions = [{ type: "key", subtype: "keyDown", value }]; 446 const message = `actionItem.value: (${getTypeString(value)})`; 447 await checkFromJSONErrors( 448 inputTickActions, 449 /Expected "value" to be a string that represents single code point/, 450 message 451 ); 452 } 453 454 const state = new actions.State(); 455 const actionSequence = { 456 type: "key", 457 id: "keyboard", 458 actions: [{ type: "keyDown", value: "\uE004" }], 459 }; 460 const chain = await actions.Chain.fromJSON(state, [actionSequence], {}); 461 const actionItem = chain[0][0]; 462 463 equal(actionItem.type, "key"); 464 equal(actionItem.id, "keyboard"); 465 equal(actionItem.subtype, "keyDown"); 466 equal(actionItem.value, "\ue004"); 467 }); 468 469 add_task(async function test_processInputSourceActionSequenceValidation() { 470 await checkFromJSONErrors( 471 [{ type: "swim", subtype: "pause", id: "some id" }], 472 /Expected known action type/, 473 "actionSequence type: swim" 474 ); 475 476 await checkFromJSONErrors( 477 [{ type: "none", subtype: "pause", id: -1 }], 478 /Expected "id" to be a string/, 479 "actionSequence id: -1" 480 ); 481 482 await checkFromJSONErrors( 483 [{ type: "none", subtype: "pause", id: undefined }], 484 /Expected "id" to be a string/, 485 "actionSequence id: undefined" 486 ); 487 488 const state = new actions.State(); 489 const actionSequence = [ 490 { type: "none", subtype: "pause", id: "some_id", actions: -1 }, 491 ]; 492 const errorRegex = /Expected "actionSequence.actions" to be an array/; 493 const message = "actionSequence actions: -1"; 494 495 await Assert.rejects( 496 actions.Chain.fromJSON(state, actionSequence, {}), 497 /InvalidArgumentError/, 498 message 499 ); 500 await Assert.rejects( 501 actions.Chain.fromJSON(state, actionSequence, {}), 502 errorRegex, 503 message 504 ); 505 }); 506 507 add_task(async function test_processInputSourceActionSequence() { 508 const state = new actions.State(); 509 const actionItem = { type: "pause", duration: 5 }; 510 const actionSequence = { 511 type: "none", 512 id: "some id", 513 actions: [actionItem], 514 }; 515 const chain = await actions.Chain.fromJSON(state, [actionSequence], {}); 516 equal(chain.length, 1); 517 const tickActions = chain[0]; 518 equal(tickActions.length, 1); 519 equal(tickActions[0].type, "none"); 520 equal(tickActions[0].subtype, "pause"); 521 equal(tickActions[0].duration, 5); 522 equal(tickActions[0].id, "some id"); 523 }); 524 525 add_task(async function test_processInputSourceActionSequencePointer() { 526 const state = new actions.State(); 527 const actionItem = { type: "pointerDown", button: 1 }; 528 const actionSequence = { 529 type: "pointer", 530 id: "9", 531 actions: [actionItem], 532 parameters: { 533 pointerType: "mouse", // TODO "pen" 534 }, 535 }; 536 const chain = await actions.Chain.fromJSON(state, [actionSequence], {}); 537 equal(chain.length, 1); 538 const tickActions = chain[0]; 539 equal(tickActions.length, 1); 540 equal(tickActions[0].type, "pointer"); 541 equal(tickActions[0].subtype, "pointerDown"); 542 equal(tickActions[0].button, 1); 543 equal(tickActions[0].id, "9"); 544 const inputSource = state.getInputSource(tickActions[0].id); 545 equal(inputSource.constructor.type, "pointer"); 546 equal(inputSource.pointer.constructor.type, "mouse"); 547 }); 548 549 add_task(async function test_processInputSourceActionSequenceKey() { 550 const state = new actions.State(); 551 const actionItem = { type: "keyUp", value: "a" }; 552 const actionSequence = { 553 type: "key", 554 id: "9", 555 actions: [actionItem], 556 }; 557 const chain = await actions.Chain.fromJSON(state, [actionSequence], {}); 558 equal(chain.length, 1); 559 const tickActions = chain[0]; 560 equal(tickActions.length, 1); 561 equal(tickActions[0].type, "key"); 562 equal(tickActions[0].subtype, "keyUp"); 563 equal(tickActions[0].value, "a"); 564 equal(tickActions[0].id, "9"); 565 }); 566 567 add_task(async function test_processInputSourceActionSequenceInputStateMap() { 568 const state = new actions.State(); 569 const id = "1"; 570 const actionItem = { type: "pause", duration: 5000 }; 571 const actionSequence = { 572 type: "key", 573 id, 574 actions: [actionItem], 575 }; 576 await actions.Chain.fromJSON(state, [actionSequence], {}); 577 equal(state.inputStateMap.size, 1); 578 equal(state.inputStateMap.get(id).constructor.type, "key"); 579 580 // Construct a different state with the same input id 581 const state1 = new actions.State(); 582 const actionItem1 = { type: "pointerDown", button: 0 }; 583 const actionSequence1 = { 584 type: "pointer", 585 id, 586 actions: [actionItem1], 587 }; 588 await actions.Chain.fromJSON(state1, [actionSequence1], {}); 589 equal(state1.inputStateMap.size, 1); 590 591 // Overwrite the state in the initial map with one of a different type 592 state.inputStateMap.set(id, state1.inputStateMap.get(id)); 593 equal(state.inputStateMap.get(id).constructor.type, "pointer"); 594 595 const message = "Wrong state for input id type"; 596 await Assert.rejects( 597 actions.Chain.fromJSON(state, [actionSequence]), 598 /InvalidArgumentError/, 599 message 600 ); 601 await Assert.rejects( 602 actions.Chain.fromJSON(state, [actionSequence]), 603 /Expected input source \[object String\] "1" to be type pointer/, 604 message 605 ); 606 }); 607 608 add_task(async function test_extractActionChainValidation() { 609 for (let action of [-1, "a", undefined, null]) { 610 const state = new actions.State(); 611 let message = `actions: ${getTypeString(action)}`; 612 await Assert.rejects( 613 actions.Chain.fromJSON(state, action), 614 /InvalidArgumentError/, 615 message 616 ); 617 await Assert.rejects( 618 actions.Chain.fromJSON(state, action), 619 /Expected "actions" to be an array/, 620 message 621 ); 622 } 623 }); 624 625 add_task(async function test_extractActionChainEmpty() { 626 const state = new actions.State(); 627 deepEqual(await actions.Chain.fromJSON(state, [], {}), []); 628 }); 629 630 add_task(async function test_extractActionChain_oneTickOneInput() { 631 const state = new actions.State(); 632 const actionItem = { type: "pause", duration: 5000 }; 633 const actionSequence = { 634 type: "none", 635 id: "some id", 636 actions: [actionItem], 637 }; 638 const actionsByTick = await actions.Chain.fromJSON( 639 state, 640 [actionSequence], 641 {} 642 ); 643 equal(1, actionsByTick.length); 644 equal(1, actionsByTick[0].length); 645 equal(actionsByTick[0][0].id, actionSequence.id); 646 equal(actionsByTick[0][0].type, "none"); 647 equal(actionsByTick[0][0].subtype, "pause"); 648 equal(actionsByTick[0][0].duration, actionItem.duration); 649 }); 650 651 add_task(async function test_extractActionChain_twoAndThreeTicks() { 652 const state = new actions.State(); 653 const mouseActionItems = [ 654 { 655 type: "pointerDown", 656 button: 2, 657 }, 658 { 659 type: "pointerUp", 660 button: 2, 661 }, 662 ]; 663 const mouseActionSequence = { 664 type: "pointer", 665 id: "7", 666 actions: mouseActionItems, 667 parameters: { 668 pointerType: "mouse", 669 }, 670 }; 671 const keyActionItems = [ 672 { 673 type: "keyDown", 674 value: "a", 675 }, 676 { 677 type: "pause", 678 duration: 4, 679 }, 680 { 681 type: "keyUp", 682 value: "a", 683 }, 684 ]; 685 let keyActionSequence = { 686 type: "key", 687 id: "1", 688 actions: keyActionItems, 689 }; 690 let actionsByTick = await actions.Chain.fromJSON( 691 state, 692 [keyActionSequence, mouseActionSequence], 693 {} 694 ); 695 // number of ticks is same as longest action sequence 696 equal(keyActionItems.length, actionsByTick.length); 697 equal(2, actionsByTick[0].length); 698 equal(2, actionsByTick[1].length); 699 equal(1, actionsByTick[2].length); 700 701 equal(actionsByTick[2][0].id, keyActionSequence.id); 702 equal(actionsByTick[2][0].type, "key"); 703 equal(actionsByTick[2][0].subtype, "keyUp"); 704 }); 705 706 add_task(async function test_computeTickDuration() { 707 const state = new actions.State(); 708 const expected = 8000; 709 const inputTickActions = [ 710 { type: "none", subtype: "pause", duration: 5000 }, 711 { type: "key", subtype: "pause", duration: 1000 }, 712 { type: "pointer", subtype: "pointerMove", duration: 6000, x: 0, y: 0 }, 713 // invalid because keyDown should not have duration, so duration should be ignored. 714 { type: "key", subtype: "keyDown", duration: 100000, value: "a" }, 715 { type: "pointer", subtype: "pause", duration: expected }, 716 { type: "pointer", subtype: "pointerUp", button: 0 }, 717 ]; 718 const chain = await actions.Chain.fromJSON( 719 state, 720 chainForTick(inputTickActions), 721 {} 722 ); 723 equal(1, chain.length); 724 const tickActions = chain[0]; 725 equal(expected, tickActions.getDuration()); 726 }); 727 728 add_task(async function test_computeTickDuration_noDurations() { 729 const state = new actions.State(); 730 const inputTickActions = [ 731 // invalid because keyDown should not have duration, so duration should be ignored. 732 { type: "key", subtype: "keyDown", duration: 100000, value: "a" }, 733 // undefined duration permitted 734 { type: "none", subtype: "pause" }, 735 { type: "pointer", subtype: "pointerMove", button: 0, x: 0, y: 0 }, 736 { type: "pointer", subtype: "pointerDown", button: 0 }, 737 { type: "key", subtype: "keyUp", value: "a" }, 738 ]; 739 const chain = await actions.Chain.fromJSON( 740 state, 741 chainForTick(inputTickActions), 742 {} 743 ); 744 equal(0, chain[0].getDuration()); 745 }); 746 747 add_task(function test_ClickTracker_setClick() { 748 const clickTracker = new ClickTracker(); 749 const button1 = 1; 750 const button2 = 2; 751 752 clickTracker.setClick(button1); 753 equal(1, clickTracker.count); 754 755 // Make sure that clicking different mouse buttons doesn't increase the count. 756 clickTracker.setClick(button2); 757 equal(1, clickTracker.count); 758 759 clickTracker.setClick(button2); 760 equal(2, clickTracker.count); 761 762 clickTracker.reset(); 763 equal(0, clickTracker.count); 764 }); 765 766 add_task(function test_ClickTracker_reset_after_timeout() { 767 const clickTracker = new ClickTracker(); 768 769 clickTracker.setClick(1); 770 equal(1, clickTracker.count); 771 772 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 773 setTimeout(() => equal(0, clickTracker.count), CLICK_INTERVAL + 10); 774 }); 775 776 // helpers 777 function getTypeString(obj) { 778 return Object.prototype.toString.call(obj); 779 } 780 781 async function checkFromJSONErrors( 782 inputTickActions, 783 regex, 784 message, 785 options = {} 786 ) { 787 const { isElementOrigin = () => true, getElementOrigin = elem => elem } = 788 options; 789 790 const state = new actions.State(); 791 const actionsOptions = { isElementOrigin, getElementOrigin }; 792 793 if (typeof message == "undefined") { 794 message = `fromJSON`; 795 } 796 797 await Assert.rejects( 798 actions.Chain.fromJSON( 799 state, 800 chainForTick(inputTickActions), 801 actionsOptions 802 ), 803 /InvalidArgumentError/, 804 message 805 ); 806 await Assert.rejects( 807 actions.Chain.fromJSON( 808 state, 809 chainForTick(inputTickActions), 810 actionsOptions 811 ), 812 regex, 813 message 814 ); 815 } 816 817 function chainForTick(tickActions) { 818 const actions = []; 819 let lastId = 0; 820 for (let { type, subtype, parameters, ...props } of tickActions) { 821 let id; 822 if (!props.hasOwnProperty("id")) { 823 id = `${type}_${lastId++}`; 824 } else { 825 id = props.id; 826 delete props.id; 827 } 828 const inputAction = { type, id, actions: [{ type: subtype, ...props }] }; 829 if (parameters !== undefined) { 830 inputAction.parameters = parameters; 831 } 832 actions.push(inputAction); 833 } 834 return actions; 835 }