Pop-up dictionary browser extension for language learning. Successor to Yomichan. (PERSONAL FORK)
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});