Pop-up dictionary browser extension for language learning. Successor to Yomichan. (PERSONAL FORK)
at lambda-fork/main 1112 lines 47 kB view raw
1/* 2 * Copyright (C) 2023-2025 Yomitan Authors 3 * Copyright (C) 2020-2022 Yomichan Authors 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19import {describe, expect, test} from 'vitest'; 20import {createSchema, normalizeContext} from '../ext/js/background/profile-conditions-util.js'; 21 22describe('Profile conditions utilities', () => { 23 describe('NormalizeContext', () => { 24 /** @type {{context: import('settings').OptionsContext, expected: import('profile-conditions-util').NormalizedOptionsContext}[]} */ 25 const data = [ 26 // Empty 27 { 28 context: {index: 0}, 29 expected: {index: 0, flags: []}, 30 }, 31 32 // Domain normalization 33 { 34 context: {depth: 0, url: ''}, 35 expected: {depth: 0, url: '', flags: []}, 36 }, 37 { 38 context: {depth: 0, url: 'http://example.com/'}, 39 expected: {depth: 0, url: 'http://example.com/', domain: 'example.com', flags: []}, 40 }, 41 { 42 context: {depth: 0, url: 'http://example.com:1234/'}, 43 expected: {depth: 0, url: 'http://example.com:1234/', domain: 'example.com', flags: []}, 44 }, 45 { 46 context: {depth: 0, url: 'http://user@example.com:1234/'}, 47 expected: {depth: 0, url: 'http://user@example.com:1234/', domain: 'example.com', flags: []}, 48 }, 49 ]; 50 51 test.each(data)('normalize-context-test-%#', ({context, expected}) => { 52 const actual = normalizeContext(context); 53 expect(actual).toStrictEqual(expected); 54 }); 55 }); 56 57 describe('Schemas', () => { 58 /* eslint-disable @stylistic/no-multi-spaces */ 59 /** @type {{conditionGroups: import('settings').ProfileConditionGroup[], expectedSchema?: import('ext/json-schema').Schema, inputs?: {expected: boolean, context: import('settings').OptionsContext}[]}[]} */ 60 const data = [ 61 // Empty 62 { 63 conditionGroups: [], 64 expectedSchema: {}, 65 inputs: [ 66 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 67 ], 68 }, 69 { 70 conditionGroups: [ 71 {conditions: []}, 72 ], 73 expectedSchema: {}, 74 inputs: [ 75 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 76 ], 77 }, 78 { 79 conditionGroups: [ 80 {conditions: []}, 81 {conditions: []}, 82 ], 83 expectedSchema: {}, 84 inputs: [ 85 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 86 ], 87 }, 88 89 // popupLevel tests 90 { 91 conditionGroups: [ 92 { 93 conditions: [ 94 { 95 type: 'popupLevel', 96 operator: 'equal', 97 value: '0', 98 }, 99 ], 100 }, 101 ], 102 expectedSchema: { 103 properties: { 104 depth: {const: 0}, 105 }, 106 required: ['depth'], 107 }, 108 inputs: [ 109 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 110 {expected: false, context: {depth: 1, url: 'http://example.com/'}}, 111 {expected: false, context: {depth: -1, url: 'http://example.com/'}}, 112 ], 113 }, 114 { 115 conditionGroups: [ 116 { 117 conditions: [ 118 { 119 type: 'popupLevel', 120 operator: 'notEqual', 121 value: '0', 122 }, 123 ], 124 }, 125 ], 126 expectedSchema: { 127 not: { 128 anyOf: [ 129 { 130 properties: { 131 depth: {const: 0}, 132 }, 133 required: ['depth'], 134 }, 135 ], 136 }, 137 }, 138 inputs: [ 139 {expected: false, context: {depth: 0, url: 'http://example.com/'}}, 140 {expected: true, context: {depth: 1, url: 'http://example.com/'}}, 141 {expected: true, context: {depth: -1, url: 'http://example.com/'}}, 142 ], 143 }, 144 { 145 conditionGroups: [ 146 { 147 conditions: [ 148 { 149 type: 'popupLevel', 150 operator: 'lessThan', 151 value: '0', 152 }, 153 ], 154 }, 155 ], 156 expectedSchema: { 157 properties: { 158 depth: { 159 type: 'number', 160 exclusiveMaximum: 0, 161 }, 162 }, 163 required: ['depth'], 164 }, 165 inputs: [ 166 {expected: false, context: {depth: 0, url: 'http://example.com/'}}, 167 {expected: false, context: {depth: 1, url: 'http://example.com/'}}, 168 {expected: true, context: {depth: -1, url: 'http://example.com/'}}, 169 ], 170 }, 171 { 172 conditionGroups: [ 173 { 174 conditions: [ 175 { 176 type: 'popupLevel', 177 operator: 'greaterThan', 178 value: '0', 179 }, 180 ], 181 }, 182 ], 183 expectedSchema: { 184 properties: { 185 depth: { 186 type: 'number', 187 exclusiveMinimum: 0, 188 }, 189 }, 190 required: ['depth'], 191 }, 192 inputs: [ 193 {expected: false, context: {depth: 0, url: 'http://example.com/'}}, 194 {expected: true, context: {depth: 1, url: 'http://example.com/'}}, 195 {expected: false, context: {depth: -1, url: 'http://example.com/'}}, 196 ], 197 }, 198 { 199 conditionGroups: [ 200 { 201 conditions: [ 202 { 203 type: 'popupLevel', 204 operator: 'lessThanOrEqual', 205 value: '0', 206 }, 207 ], 208 }, 209 ], 210 expectedSchema: { 211 properties: { 212 depth: { 213 type: 'number', 214 maximum: 0, 215 }, 216 }, 217 required: ['depth'], 218 }, 219 inputs: [ 220 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 221 {expected: false, context: {depth: 1, url: 'http://example.com/'}}, 222 {expected: true, context: {depth: -1, url: 'http://example.com/'}}, 223 ], 224 }, 225 { 226 conditionGroups: [ 227 { 228 conditions: [ 229 { 230 type: 'popupLevel', 231 operator: 'greaterThanOrEqual', 232 value: '0', 233 }, 234 ], 235 }, 236 ], 237 expectedSchema: { 238 properties: { 239 depth: { 240 type: 'number', 241 minimum: 0, 242 }, 243 }, 244 required: ['depth'], 245 }, 246 inputs: [ 247 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 248 {expected: true, context: {depth: 1, url: 'http://example.com/'}}, 249 {expected: false, context: {depth: -1, url: 'http://example.com/'}}, 250 ], 251 }, 252 253 // Url tests 254 { 255 conditionGroups: [ 256 { 257 conditions: [ 258 { 259 type: 'url', 260 operator: 'matchDomain', 261 value: 'example.com', 262 }, 263 ], 264 }, 265 ], 266 expectedSchema: { 267 properties: { 268 domain: { 269 oneOf: [ 270 {const: 'example.com'}, 271 ], 272 }, 273 }, 274 required: ['domain'], 275 }, 276 inputs: [ 277 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 278 {expected: false, context: {depth: 0, url: 'http://example1.com/'}}, 279 {expected: false, context: {depth: 0, url: 'http://example2.com/'}}, 280 {expected: true, context: {depth: 0, url: 'http://example.com:1234/'}}, 281 {expected: true, context: {depth: 0, url: 'http://user@example.com:1234/'}}, 282 ], 283 }, 284 { 285 conditionGroups: [ 286 { 287 conditions: [ 288 { 289 type: 'url', 290 operator: 'matchDomain', 291 value: 'example.com, example1.com, example2.com', 292 }, 293 ], 294 }, 295 ], 296 expectedSchema: { 297 properties: { 298 domain: { 299 oneOf: [ 300 {const: 'example.com'}, 301 {const: 'example1.com'}, 302 {const: 'example2.com'}, 303 ], 304 }, 305 }, 306 required: ['domain'], 307 }, 308 inputs: [ 309 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 310 {expected: true, context: {depth: 0, url: 'http://example1.com/'}}, 311 {expected: true, context: {depth: 0, url: 'http://example2.com/'}}, 312 {expected: false, context: {depth: 0, url: 'http://example3.com/'}}, 313 {expected: true, context: {depth: 0, url: 'http://example.com:1234/'}}, 314 {expected: true, context: {depth: 0, url: 'http://user@example.com:1234/'}}, 315 ], 316 }, 317 { 318 conditionGroups: [ 319 { 320 conditions: [ 321 { 322 type: 'url', 323 operator: 'matchRegExp', 324 value: '^http://example\\d?\\.com/[\\w\\W]*$', 325 }, 326 ], 327 }, 328 ], 329 expectedSchema: { 330 properties: { 331 url: { 332 type: 'string', 333 pattern: '^http://example\\d?\\.com/[\\w\\W]*$', 334 patternFlags: 'i', 335 }, 336 }, 337 required: ['url'], 338 }, 339 inputs: [ 340 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 341 {expected: true, context: {depth: 0, url: 'http://example1.com/'}}, 342 {expected: true, context: {depth: 0, url: 'http://example2.com/'}}, 343 {expected: true, context: {depth: 0, url: 'http://example3.com/'}}, 344 {expected: true, context: {depth: 0, url: 'http://example.com/example'}}, 345 {expected: false, context: {depth: 0, url: 'http://example.com:1234/'}}, 346 {expected: false, context: {depth: 0, url: 'http://user@example.com:1234/'}}, 347 {expected: false, context: {depth: 0, url: 'http://example-1.com/'}}, 348 ], 349 }, 350 351 // modifierKeys tests 352 { 353 conditionGroups: [ 354 { 355 conditions: [ 356 { 357 type: 'modifierKeys', 358 operator: 'are', 359 value: '', 360 }, 361 ], 362 }, 363 ], 364 expectedSchema: { 365 properties: { 366 modifierKeys: { 367 type: 'array', 368 maxItems: 0, 369 minItems: 0, 370 }, 371 }, 372 required: ['modifierKeys'], 373 }, 374 inputs: [ 375 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, 376 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}}, 377 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}}, 378 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}, 379 ], 380 }, 381 { 382 conditionGroups: [ 383 { 384 conditions: [ 385 { 386 type: 'modifierKeys', 387 operator: 'are', 388 value: 'alt, shift', 389 }, 390 ], 391 }, 392 ], 393 expectedSchema: { 394 properties: { 395 modifierKeys: { 396 type: 'array', 397 maxItems: 2, 398 minItems: 2, 399 allOf: [ 400 {contains: {const: 'alt'}}, 401 {contains: {const: 'shift'}}, 402 ], 403 }, 404 }, 405 required: ['modifierKeys'], 406 }, 407 inputs: [ 408 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, 409 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}}, 410 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}}, 411 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}, 412 ], 413 }, 414 { 415 conditionGroups: [ 416 { 417 conditions: [ 418 { 419 type: 'modifierKeys', 420 operator: 'areNot', 421 value: '', 422 }, 423 ], 424 }, 425 ], 426 expectedSchema: { 427 not: { 428 anyOf: [ 429 { 430 properties: { 431 modifierKeys: { 432 type: 'array', 433 maxItems: 0, 434 minItems: 0, 435 }, 436 }, 437 required: ['modifierKeys'], 438 }, 439 ], 440 }, 441 }, 442 inputs: [ 443 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, 444 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}}, 445 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}}, 446 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}, 447 ], 448 }, 449 { 450 conditionGroups: [ 451 { 452 conditions: [ 453 { 454 type: 'modifierKeys', 455 operator: 'areNot', 456 value: 'alt, shift', 457 }, 458 ], 459 }, 460 ], 461 expectedSchema: { 462 not: { 463 anyOf: [ 464 { 465 properties: { 466 modifierKeys: { 467 type: 'array', 468 maxItems: 2, 469 minItems: 2, 470 allOf: [ 471 {contains: {const: 'alt'}}, 472 {contains: {const: 'shift'}}, 473 ], 474 }, 475 }, 476 required: ['modifierKeys'], 477 }, 478 ], 479 }, 480 }, 481 inputs: [ 482 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, 483 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}}, 484 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}}, 485 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}, 486 ], 487 }, 488 { 489 conditionGroups: [ 490 { 491 conditions: [ 492 { 493 type: 'modifierKeys', 494 operator: 'include', 495 value: '', 496 }, 497 ], 498 }, 499 ], 500 expectedSchema: { 501 properties: { 502 modifierKeys: { 503 type: 'array', 504 minItems: 0, 505 }, 506 }, 507 required: ['modifierKeys'], 508 }, 509 inputs: [ 510 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, 511 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}}, 512 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}}, 513 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}, 514 ], 515 }, 516 { 517 conditionGroups: [ 518 { 519 conditions: [ 520 { 521 type: 'modifierKeys', 522 operator: 'include', 523 value: 'alt, shift', 524 }, 525 ], 526 }, 527 ], 528 expectedSchema: { 529 properties: { 530 modifierKeys: { 531 type: 'array', 532 minItems: 2, 533 allOf: [ 534 {contains: {const: 'alt'}}, 535 {contains: {const: 'shift'}}, 536 ], 537 }, 538 }, 539 required: ['modifierKeys'], 540 }, 541 inputs: [ 542 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, 543 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}}, 544 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}}, 545 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}, 546 ], 547 }, 548 { 549 conditionGroups: [ 550 { 551 conditions: [ 552 { 553 type: 'modifierKeys', 554 operator: 'notInclude', 555 value: '', 556 }, 557 ], 558 }, 559 ], 560 expectedSchema: { 561 properties: { 562 modifierKeys: { 563 type: 'array', 564 }, 565 }, 566 required: ['modifierKeys'], 567 }, 568 inputs: [ 569 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, 570 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}}, 571 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}}, 572 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}, 573 ], 574 }, 575 { 576 conditionGroups: [ 577 { 578 conditions: [ 579 { 580 type: 'modifierKeys', 581 operator: 'notInclude', 582 value: 'alt, shift', 583 }, 584 ], 585 }, 586 ], 587 expectedSchema: { 588 properties: { 589 modifierKeys: { 590 type: 'array', 591 not: { 592 anyOf: [ 593 {contains: {const: 'alt'}}, 594 {contains: {const: 'shift'}}, 595 ], 596 }, 597 }, 598 }, 599 required: ['modifierKeys'], 600 }, 601 inputs: [ 602 {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, 603 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}}, 604 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}}, 605 {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}, 606 ], 607 }, 608 609 // Flags tests 610 { 611 conditionGroups: [ 612 { 613 conditions: [ 614 { 615 type: 'flags', 616 operator: 'are', 617 value: '', 618 }, 619 ], 620 }, 621 ], 622 expectedSchema: { 623 required: ['flags'], 624 properties: { 625 flags: { 626 type: 'array', 627 maxItems: 0, 628 minItems: 0, 629 }, 630 }, 631 }, 632 inputs: [ 633 {expected: true, context: {depth: 0, url: ''}}, 634 {expected: true, context: {depth: 0, url: '', flags: []}}, 635 {expected: false, context: {depth: 0, url: '', flags: ['clipboard']}}, 636 // @ts-expect-error - Ignore type for string flag for testing purposes 637 {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}}, 638 // @ts-expect-error - Ignore type for string flag for testing purposes 639 {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}, 640 ], 641 }, 642 { 643 conditionGroups: [ 644 { 645 conditions: [ 646 { 647 type: 'flags', 648 operator: 'are', 649 value: 'clipboard, test2', 650 }, 651 ], 652 }, 653 ], 654 expectedSchema: { 655 required: ['flags'], 656 properties: { 657 flags: { 658 type: 'array', 659 maxItems: 2, 660 minItems: 2, 661 allOf: [ 662 {contains: {const: 'clipboard'}}, 663 {contains: {const: 'test2'}}, 664 ], 665 }, 666 }, 667 }, 668 inputs: [ 669 {expected: false, context: {depth: 0, url: ''}}, 670 {expected: false, context: {depth: 0, url: '', flags: []}}, 671 {expected: false, context: {depth: 0, url: '', flags: ['clipboard']}}, 672 // @ts-expect-error - Ignore type for string flag for testing purposes 673 {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}}, 674 // @ts-expect-error - Ignore type for string flag for testing purposes 675 {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}, 676 ], 677 }, 678 { 679 conditionGroups: [ 680 { 681 conditions: [ 682 { 683 type: 'flags', 684 operator: 'areNot', 685 value: '', 686 }, 687 ], 688 }, 689 ], 690 expectedSchema: { 691 not: { 692 anyOf: [ 693 { 694 required: ['flags'], 695 properties: { 696 flags: { 697 type: 'array', 698 maxItems: 0, 699 minItems: 0, 700 }, 701 }, 702 }, 703 ], 704 }, 705 }, 706 inputs: [ 707 {expected: false, context: {depth: 0, url: ''}}, 708 {expected: false, context: {depth: 0, url: '', flags: []}}, 709 {expected: true, context: {depth: 0, url: '', flags: ['clipboard']}}, 710 // @ts-expect-error - Ignore type for string flag for testing purposes 711 {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}}, 712 // @ts-expect-error - Ignore type for string flag for testing purposes 713 {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}, 714 ], 715 }, 716 { 717 conditionGroups: [ 718 { 719 conditions: [ 720 { 721 type: 'flags', 722 operator: 'areNot', 723 value: 'clipboard, test2', 724 }, 725 ], 726 }, 727 ], 728 expectedSchema: { 729 not: { 730 anyOf: [ 731 { 732 required: ['flags'], 733 properties: { 734 flags: { 735 type: 'array', 736 maxItems: 2, 737 minItems: 2, 738 allOf: [ 739 {contains: {const: 'clipboard'}}, 740 {contains: {const: 'test2'}}, 741 ], 742 }, 743 }, 744 }, 745 ], 746 }, 747 }, 748 inputs: [ 749 {expected: true, context: {depth: 0, url: ''}}, 750 {expected: true, context: {depth: 0, url: '', flags: []}}, 751 {expected: true, context: {depth: 0, url: '', flags: ['clipboard']}}, 752 // @ts-expect-error - Ignore type for string flag for testing purposes 753 {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}}, 754 // @ts-expect-error - Ignore type for string flag for testing purposes 755 {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}, 756 ], 757 }, 758 { 759 conditionGroups: [ 760 { 761 conditions: [ 762 { 763 type: 'flags', 764 operator: 'include', 765 value: '', 766 }, 767 ], 768 }, 769 ], 770 expectedSchema: { 771 required: ['flags'], 772 properties: { 773 flags: { 774 type: 'array', 775 minItems: 0, 776 }, 777 }, 778 }, 779 inputs: [ 780 {expected: true, context: {depth: 0, url: ''}}, 781 {expected: true, context: {depth: 0, url: '', flags: []}}, 782 {expected: true, context: {depth: 0, url: '', flags: ['clipboard']}}, 783 // @ts-expect-error - Ignore type for string flag for testing purposes 784 {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}}, 785 // @ts-expect-error - Ignore type for string flag for testing purposes 786 {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}, 787 ], 788 }, 789 { 790 conditionGroups: [ 791 { 792 conditions: [ 793 { 794 type: 'flags', 795 operator: 'include', 796 value: 'clipboard, test2', 797 }, 798 ], 799 }, 800 ], 801 expectedSchema: { 802 required: ['flags'], 803 properties: { 804 flags: { 805 type: 'array', 806 minItems: 2, 807 allOf: [ 808 {contains: {const: 'clipboard'}}, 809 {contains: {const: 'test2'}}, 810 ], 811 }, 812 }, 813 }, 814 inputs: [ 815 {expected: false, context: {depth: 0, url: ''}}, 816 {expected: false, context: {depth: 0, url: '', flags: []}}, 817 {expected: false, context: {depth: 0, url: '', flags: ['clipboard']}}, 818 // @ts-expect-error - Ignore type for string flag for testing purposes 819 {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}}, 820 // @ts-expect-error - Ignore type for string flag for testing purposes 821 {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}, 822 ], 823 }, 824 { 825 conditionGroups: [ 826 { 827 conditions: [ 828 { 829 type: 'flags', 830 operator: 'notInclude', 831 value: '', 832 }, 833 ], 834 }, 835 ], 836 expectedSchema: { 837 required: ['flags'], 838 properties: { 839 flags: { 840 type: 'array', 841 }, 842 }, 843 }, 844 inputs: [ 845 {expected: true, context: {depth: 0, url: ''}}, 846 {expected: true, context: {depth: 0, url: '', flags: []}}, 847 {expected: true, context: {depth: 0, url: '', flags: ['clipboard']}}, 848 // @ts-expect-error - Ignore type for string flag for testing purposes 849 {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}}, 850 // @ts-expect-error - Ignore type for string flag for testing purposes 851 {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}, 852 ], 853 }, 854 { 855 conditionGroups: [ 856 { 857 conditions: [ 858 { 859 type: 'flags', 860 operator: 'notInclude', 861 value: 'clipboard, test2', 862 }, 863 ], 864 }, 865 ], 866 expectedSchema: { 867 required: ['flags'], 868 properties: { 869 flags: { 870 type: 'array', 871 not: { 872 anyOf: [ 873 {contains: {const: 'clipboard'}}, 874 {contains: {const: 'test2'}}, 875 ], 876 }, 877 }, 878 }, 879 }, 880 inputs: [ 881 {expected: true, context: {depth: 0, url: ''}}, 882 {expected: true, context: {depth: 0, url: '', flags: []}}, 883 {expected: false, context: {depth: 0, url: '', flags: ['clipboard']}}, 884 // @ts-expect-error - Ignore type for string flag for testing purposes 885 {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}}, 886 // @ts-expect-error - Ignore type for string flag for testing purposes 887 {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}, 888 ], 889 }, 890 891 // Multiple conditions tests 892 { 893 conditionGroups: [ 894 { 895 conditions: [ 896 { 897 type: 'popupLevel', 898 operator: 'greaterThan', 899 value: '0', 900 }, 901 { 902 type: 'popupLevel', 903 operator: 'lessThan', 904 value: '3', 905 }, 906 ], 907 }, 908 ], 909 expectedSchema: { 910 allOf: [ 911 { 912 properties: { 913 depth: { 914 type: 'number', 915 exclusiveMinimum: 0, 916 }, 917 }, 918 required: ['depth'], 919 }, 920 { 921 properties: { 922 depth: { 923 type: 'number', 924 exclusiveMaximum: 3, 925 }, 926 }, 927 required: ['depth'], 928 }, 929 ], 930 }, 931 inputs: [ 932 {expected: false, context: {depth: -2, url: 'http://example.com/'}}, 933 {expected: false, context: {depth: -1, url: 'http://example.com/'}}, 934 {expected: false, context: {depth: 0, url: 'http://example.com/'}}, 935 {expected: true, context: {depth: 1, url: 'http://example.com/'}}, 936 {expected: true, context: {depth: 2, url: 'http://example.com/'}}, 937 {expected: false, context: {depth: 3, url: 'http://example.com/'}}, 938 ], 939 }, 940 { 941 conditionGroups: [ 942 { 943 conditions: [ 944 { 945 type: 'popupLevel', 946 operator: 'greaterThan', 947 value: '0', 948 }, 949 { 950 type: 'popupLevel', 951 operator: 'lessThan', 952 value: '3', 953 }, 954 ], 955 }, 956 { 957 conditions: [ 958 { 959 type: 'popupLevel', 960 operator: 'equal', 961 value: '0', 962 }, 963 ], 964 }, 965 ], 966 expectedSchema: { 967 anyOf: [ 968 { 969 allOf: [ 970 { 971 properties: { 972 depth: { 973 type: 'number', 974 exclusiveMinimum: 0, 975 }, 976 }, 977 required: ['depth'], 978 }, 979 { 980 properties: { 981 depth: { 982 type: 'number', 983 exclusiveMaximum: 3, 984 }, 985 }, 986 required: ['depth'], 987 }, 988 ], 989 }, 990 { 991 properties: { 992 depth: {const: 0}, 993 }, 994 required: ['depth'], 995 }, 996 ], 997 }, 998 inputs: [ 999 {expected: false, context: {depth: -2, url: 'http://example.com/'}}, 1000 {expected: false, context: {depth: -1, url: 'http://example.com/'}}, 1001 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 1002 {expected: true, context: {depth: 1, url: 'http://example.com/'}}, 1003 {expected: true, context: {depth: 2, url: 'http://example.com/'}}, 1004 {expected: false, context: {depth: 3, url: 'http://example.com/'}}, 1005 ], 1006 }, 1007 { 1008 conditionGroups: [ 1009 { 1010 conditions: [ 1011 { 1012 type: 'popupLevel', 1013 operator: 'greaterThan', 1014 value: '0', 1015 }, 1016 { 1017 type: 'popupLevel', 1018 operator: 'lessThan', 1019 value: '3', 1020 }, 1021 ], 1022 }, 1023 { 1024 conditions: [ 1025 { 1026 type: 'popupLevel', 1027 operator: 'lessThanOrEqual', 1028 value: '0', 1029 }, 1030 { 1031 type: 'popupLevel', 1032 operator: 'greaterThanOrEqual', 1033 value: '-1', 1034 }, 1035 ], 1036 }, 1037 ], 1038 expectedSchema: { 1039 anyOf: [ 1040 { 1041 allOf: [ 1042 { 1043 properties: { 1044 depth: { 1045 type: 'number', 1046 exclusiveMinimum: 0, 1047 }, 1048 }, 1049 required: ['depth'], 1050 }, 1051 { 1052 properties: { 1053 depth: { 1054 type: 'number', 1055 exclusiveMaximum: 3, 1056 }, 1057 }, 1058 required: ['depth'], 1059 }, 1060 ], 1061 }, 1062 { 1063 allOf: [ 1064 { 1065 properties: { 1066 depth: { 1067 type: 'number', 1068 maximum: 0, 1069 }, 1070 }, 1071 required: ['depth'], 1072 }, 1073 { 1074 properties: { 1075 depth: { 1076 type: 'number', 1077 minimum: -1, 1078 }, 1079 }, 1080 required: ['depth'], 1081 }, 1082 ], 1083 }, 1084 ], 1085 }, 1086 inputs: [ 1087 {expected: false, context: {depth: -2, url: 'http://example.com/'}}, 1088 {expected: true, context: {depth: -1, url: 'http://example.com/'}}, 1089 {expected: true, context: {depth: 0, url: 'http://example.com/'}}, 1090 {expected: true, context: {depth: 1, url: 'http://example.com/'}}, 1091 {expected: true, context: {depth: 2, url: 'http://example.com/'}}, 1092 {expected: false, context: {depth: 3, url: 'http://example.com/'}}, 1093 ], 1094 }, 1095 ]; 1096 /* eslint-enable @stylistic/no-multi-spaces */ 1097 1098 test.each(data)('schemas-test-%#', ({conditionGroups, expectedSchema, inputs}) => { 1099 const schema = createSchema(conditionGroups); 1100 if (typeof expectedSchema !== 'undefined') { 1101 expect(schema.schema).toStrictEqual(expectedSchema); 1102 } 1103 if (Array.isArray(inputs)) { 1104 for (const {expected, context} of inputs) { 1105 const normalizedContext = normalizeContext(context); 1106 const actual = schema.isValid(normalizedContext); 1107 expect(actual).toStrictEqual(expected); 1108 } 1109 } 1110 }); 1111 }); 1112});