ngx-speculoos 6.0.0 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +403 -42
- package/esm2020/jasmine-matchers.mjs +2 -0
- package/esm2020/lib/component-tester.mjs +147 -0
- package/esm2020/lib/matchers.mjs +283 -0
- package/esm2020/lib/mock.mjs +25 -0
- package/esm2020/lib/route.mjs +319 -0
- package/{esm2015/lib/test-button.js → esm2020/lib/test-button.mjs} +0 -0
- package/esm2020/lib/test-element-querier.mjs +140 -0
- package/esm2020/lib/test-element.mjs +142 -0
- package/{esm2015/lib/test-html-element.js → esm2020/lib/test-html-element.mjs} +0 -0
- package/{esm2015/lib/test-input.js → esm2020/lib/test-input.mjs} +0 -0
- package/{esm2015/lib/test-select.js → esm2020/lib/test-select.mjs} +0 -0
- package/{esm2015/lib/test-textarea.js → esm2020/lib/test-textarea.mjs} +0 -0
- package/{esm2015/ngx-speculoos.js → esm2020/ngx-speculoos.mjs} +0 -0
- package/{esm2015/public_api.js → esm2020/public_api.mjs} +2 -1
- package/fesm2015/ngx-speculoos.mjs +1257 -0
- package/fesm2015/ngx-speculoos.mjs.map +1 -0
- package/{fesm2015/ngx-speculoos.js → fesm2020/ngx-speculoos.mjs} +436 -22
- package/fesm2020/ngx-speculoos.mjs.map +1 -0
- package/jasmine-matchers.d.ts +5 -0
- package/lib/component-tester.d.ts +87 -49
- package/lib/mock.d.ts +10 -0
- package/lib/route.d.ts +149 -0
- package/lib/test-element-querier.d.ts +15 -15
- package/lib/test-element.d.ts +87 -49
- package/package.json +24 -11
- package/public_api.d.ts +1 -0
- package/bundles/ngx-speculoos.umd.js +0 -1259
- package/bundles/ngx-speculoos.umd.js.map +0 -1
- package/esm2015/jasmine-matchers.js +0 -2
- package/esm2015/lib/component-tester.js +0 -96
- package/esm2015/lib/matchers.js +0 -244
- package/esm2015/lib/route.js +0 -81
- package/esm2015/lib/test-element-querier.js +0 -129
- package/esm2015/lib/test-element.js +0 -91
- package/fesm2015/ngx-speculoos.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
2
|
import { By } from '@angular/platform-browser';
|
|
3
|
-
import { convertToParamMap } from '@angular/router';
|
|
4
|
-
import { map } from 'rxjs
|
|
3
|
+
import { convertToParamMap, ActivatedRouteSnapshot, ActivatedRoute } from '@angular/router';
|
|
4
|
+
import { map, BehaviorSubject } from 'rxjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A wrapped DOM element, providing additional methods and attributes helping with writing tests
|
|
@@ -60,7 +60,7 @@ class TestElement {
|
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
62
|
* Gets the first input matched by the given selector. Throws an Error if the matched element isn't actually an input.
|
|
63
|
-
* @param selector a CSS selector
|
|
63
|
+
* @param selector a CSS or directive selector
|
|
64
64
|
* @returns the wrapped input, or null if no element was matched
|
|
65
65
|
*/
|
|
66
66
|
input(selector) {
|
|
@@ -68,7 +68,7 @@ class TestElement {
|
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* Gets the first select matched by the given selector. Throws an Error if the matched element isn't actually a select.
|
|
71
|
-
* @param selector a CSS selector
|
|
71
|
+
* @param selector a CSS or directive selector
|
|
72
72
|
* @returns the wrapped select, or null if no element was matched
|
|
73
73
|
*/
|
|
74
74
|
select(selector) {
|
|
@@ -76,7 +76,7 @@ class TestElement {
|
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
78
|
* Gets the first textarea matched by the given selector
|
|
79
|
-
* @param selector a CSS selector
|
|
79
|
+
* @param selector a CSS or directive selector
|
|
80
80
|
* @returns the wrapped textarea, or null if no element was matched. Throws an Error if the matched element isn't actually a textarea.
|
|
81
81
|
* @throws {Error} if the matched element isn't actually a textarea
|
|
82
82
|
*/
|
|
@@ -85,12 +85,63 @@ class TestElement {
|
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
87
|
* Gets the first button matched by the given selector. Throws an Error if the matched element isn't actually a button.
|
|
88
|
-
* @param selector a CSS selector
|
|
88
|
+
* @param selector a CSS or directive selector
|
|
89
89
|
* @returns the wrapped button, or null if no element was matched
|
|
90
90
|
*/
|
|
91
91
|
button(selector) {
|
|
92
92
|
return this.querier.button(selector);
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Gets the first directive matching the given component directive selector and returns its component instance
|
|
96
|
+
* @param selector the selector of a component directive
|
|
97
|
+
*/
|
|
98
|
+
component(selector) {
|
|
99
|
+
return this.querier.element(selector)?.debugElement?.componentInstance ?? null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Gets the directives matching the given component directive selector and returns their component instance
|
|
103
|
+
* @param selector the selector of a component directive
|
|
104
|
+
*/
|
|
105
|
+
components(selector) {
|
|
106
|
+
return this.querier.elements(selector).map(e => e.debugElement.componentInstance);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Gets the first element matching the given selector, then gets the given token from its injector, or null if there is no such token
|
|
110
|
+
* @param selector a CSS or directive selector
|
|
111
|
+
* @param token the token to get from the matched element injector
|
|
112
|
+
*/
|
|
113
|
+
token(selector, token) {
|
|
114
|
+
return this.querier.element(selector)?.debugElement?.injector?.get(token, null) ?? null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Gets the elements matching the given selector, then gets their given token from their injector, or null if there is no such token
|
|
118
|
+
* @param selector a CSS or directive selector
|
|
119
|
+
* @param token the token to get from the matched element injector
|
|
120
|
+
*/
|
|
121
|
+
tokens(selector, token) {
|
|
122
|
+
return this.querier.elements(selector).map(e => e.debugElement.injector.get(token, null) ?? null);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Gets the element matching the given selector, and if found, creates and returns a custom TestElement of the provided
|
|
126
|
+
* type. This is useful to create custom higher-level abstractions similar to TestInput, TestSelect, etc. for
|
|
127
|
+
* custom elements or components.
|
|
128
|
+
* @param selector a CSS or directive selector
|
|
129
|
+
* @param customTestElementType the type of the TestElement subclass that will wrap the found element
|
|
130
|
+
*/
|
|
131
|
+
custom(selector, customTestElementType) {
|
|
132
|
+
const element = this.querier.element(selector);
|
|
133
|
+
return element && new customTestElementType(this.tester, element.debugElement);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Gets the elements matching the given selector, and creates and returns custom TestElements of the provided
|
|
137
|
+
* type. This is useful to create custom higher-level abstractions similar to TestInput, TestSelect, etc. for
|
|
138
|
+
* custom elements or components.
|
|
139
|
+
* @param selector a CSS or directive selector
|
|
140
|
+
* @param customTestElementType the type of the TestElement subclass that will wrap the found elements
|
|
141
|
+
*/
|
|
142
|
+
customs(selector, customTestElementType) {
|
|
143
|
+
return this.querier.elements(selector).map(element => new customTestElementType(this.tester, element.debugElement));
|
|
144
|
+
}
|
|
94
145
|
}
|
|
95
146
|
|
|
96
147
|
/**
|
|
@@ -296,6 +347,7 @@ class TestInput extends TestHtmlElement {
|
|
|
296
347
|
}
|
|
297
348
|
}
|
|
298
349
|
|
|
350
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
299
351
|
/**
|
|
300
352
|
* @internal
|
|
301
353
|
*/
|
|
@@ -326,11 +378,11 @@ class TestElementQuerier {
|
|
|
326
378
|
}
|
|
327
379
|
}
|
|
328
380
|
/**
|
|
329
|
-
* Gets the first element matching the given
|
|
381
|
+
* Gets the first element matching the given selector and wraps it into a TestElement. The actual type
|
|
330
382
|
* of the returned value is the TestElement subclass matching the type of the found element. So, if the
|
|
331
383
|
* matched element is an input for example, the method will return a TestInput. You can thus use
|
|
332
384
|
* `tester.element('#some-input') as TestInput`.
|
|
333
|
-
* @param selector a CSS selector
|
|
385
|
+
* @param selector a CSS or directive selector
|
|
334
386
|
* @returns the wrapped element, or null if no element matches the selector.
|
|
335
387
|
*/
|
|
336
388
|
element(selector) {
|
|
@@ -338,11 +390,11 @@ class TestElementQuerier {
|
|
|
338
390
|
return childElement && TestElementQuerier.wrap(childElement, this.tester);
|
|
339
391
|
}
|
|
340
392
|
/**
|
|
341
|
-
* Gets all the elements matching the given
|
|
393
|
+
* Gets all the elements matching the given selector and wraps them into a TestElement. The actual type
|
|
342
394
|
* of the returned elements is the TestElement subclass matching the type of the found element. So, if the
|
|
343
395
|
* matched elements are inputs for example, the method will return an array of TestInput. You can thus use
|
|
344
396
|
* `tester.elements('input') as Array<TestInput>`.
|
|
345
|
-
* @param selector a CSS selector
|
|
397
|
+
* @param selector a CSS or directive selector
|
|
346
398
|
* @returns the array of matched elements, empty if no element was matched
|
|
347
399
|
*/
|
|
348
400
|
elements(selector) {
|
|
@@ -351,7 +403,7 @@ class TestElementQuerier {
|
|
|
351
403
|
}
|
|
352
404
|
/**
|
|
353
405
|
* Gets the first input matched by the given selector. Throws an Error if the matched element isn't actually an input.
|
|
354
|
-
* @param selector a CSS selector
|
|
406
|
+
* @param selector a CSS or directive selector
|
|
355
407
|
* @returns the wrapped input, or null if no element was matched
|
|
356
408
|
*/
|
|
357
409
|
input(selector) {
|
|
@@ -366,7 +418,7 @@ class TestElementQuerier {
|
|
|
366
418
|
}
|
|
367
419
|
/**
|
|
368
420
|
* Gets the first select matched by the given selector. Throws an Error if the matched element isn't actually a select.
|
|
369
|
-
* @param selector a CSS selector
|
|
421
|
+
* @param selector a CSS or directive selector
|
|
370
422
|
* @returns the wrapped select, or null if no element was matched
|
|
371
423
|
*/
|
|
372
424
|
select(selector) {
|
|
@@ -381,7 +433,7 @@ class TestElementQuerier {
|
|
|
381
433
|
}
|
|
382
434
|
/**
|
|
383
435
|
* Gets the first textarea matched by the given selector
|
|
384
|
-
* @param selector a CSS selector
|
|
436
|
+
* @param selector a CSS or directive selector
|
|
385
437
|
* @returns the wrapped textarea, or null if no element was matched. Throws an Error if the matched element isn't actually a textarea.
|
|
386
438
|
* @throws {Error} if the matched element isn't actually a textarea
|
|
387
439
|
*/
|
|
@@ -397,7 +449,7 @@ class TestElementQuerier {
|
|
|
397
449
|
}
|
|
398
450
|
/**
|
|
399
451
|
* Gets the first button matched by the given selector. Throws an Error if the matched element isn't actually a button.
|
|
400
|
-
* @param selector a CSS selector
|
|
452
|
+
* @param selector a CSS or directive selector
|
|
401
453
|
* @returns the wrapped button, or null if no element was matched
|
|
402
454
|
*/
|
|
403
455
|
button(selector) {
|
|
@@ -411,10 +463,20 @@ class TestElementQuerier {
|
|
|
411
463
|
return new TestButton(this.tester, childElement);
|
|
412
464
|
}
|
|
413
465
|
query(selector) {
|
|
414
|
-
|
|
466
|
+
if (typeof selector === 'string') {
|
|
467
|
+
return this.root.query(By.css(selector));
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
return this.root.query(By.directive(selector));
|
|
471
|
+
}
|
|
415
472
|
}
|
|
416
473
|
queryAll(selector) {
|
|
417
|
-
|
|
474
|
+
if (typeof selector === 'string') {
|
|
475
|
+
return this.root.queryAll(By.css(selector));
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
return this.root.queryAll(By.directive(selector));
|
|
479
|
+
}
|
|
418
480
|
}
|
|
419
481
|
}
|
|
420
482
|
|
|
@@ -473,7 +535,7 @@ class ComponentTester {
|
|
|
473
535
|
}
|
|
474
536
|
/**
|
|
475
537
|
* Gets the first input matched by the given selector. Throws an Error if the matched element isn't actually an input.
|
|
476
|
-
* @param selector a CSS selector
|
|
538
|
+
* @param selector a CSS or directive selector
|
|
477
539
|
* @returns the wrapped input, or null if no element was matched
|
|
478
540
|
*/
|
|
479
541
|
input(selector) {
|
|
@@ -481,7 +543,7 @@ class ComponentTester {
|
|
|
481
543
|
}
|
|
482
544
|
/**
|
|
483
545
|
* Gets the first select matched by the given selector. Throws an Error if the matched element isn't actually a select.
|
|
484
|
-
* @param selector a CSS selector
|
|
546
|
+
* @param selector a CSS or directive selector
|
|
485
547
|
* @returns the wrapped select, or null if no element was matched
|
|
486
548
|
*/
|
|
487
549
|
select(selector) {
|
|
@@ -489,7 +551,7 @@ class ComponentTester {
|
|
|
489
551
|
}
|
|
490
552
|
/**
|
|
491
553
|
* Gets the first textarea matched by the given selector
|
|
492
|
-
* @param selector a CSS selector
|
|
554
|
+
* @param selector a CSS or directive selector
|
|
493
555
|
* @returns the wrapped textarea, or null if no element was matched. Throws an Error if the matched element isn't actually a textarea.
|
|
494
556
|
* @throws {Error} if the matched element isn't actually a textarea
|
|
495
557
|
*/
|
|
@@ -498,12 +560,62 @@ class ComponentTester {
|
|
|
498
560
|
}
|
|
499
561
|
/**
|
|
500
562
|
* Gets the first button matched by the given selector. Throws an Error if the matched element isn't actually a button.
|
|
501
|
-
* @param selector a CSS selector
|
|
563
|
+
* @param selector a CSS or directive selector
|
|
502
564
|
* @returns the wrapped button, or null if no element was matched
|
|
503
565
|
*/
|
|
504
566
|
button(selector) {
|
|
505
567
|
return this.testElement.button(selector);
|
|
506
568
|
}
|
|
569
|
+
/**
|
|
570
|
+
* Gets the first directive matching the given component directive selector and returns its component instance
|
|
571
|
+
* @param selector the selector of a component directive
|
|
572
|
+
*/
|
|
573
|
+
component(selector) {
|
|
574
|
+
return this.testElement.component(selector);
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Gets the directives matching the given component directive selector and returns their component instance
|
|
578
|
+
* @param selector the selector of a component directive
|
|
579
|
+
*/
|
|
580
|
+
components(selector) {
|
|
581
|
+
return this.testElement.components(selector);
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Gets the first element matching the given selector, then gets the given token from its injector, or null if there is no such token
|
|
585
|
+
* @param selector a CSS or directive selector
|
|
586
|
+
* @param token the token to get from the matched element injector
|
|
587
|
+
*/
|
|
588
|
+
token(selector, token) {
|
|
589
|
+
return this.testElement.token(selector, token);
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Gets the elements matching the given selector, then gets their given token from their injector, or null if there is no such token
|
|
593
|
+
* @param selector a CSS or directive selector
|
|
594
|
+
* @param token the token to get from the matched element injector
|
|
595
|
+
*/
|
|
596
|
+
tokens(selector, token) {
|
|
597
|
+
return this.testElement.tokens(selector, token);
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Gets the element matching the given selector, and if found, creates and returns a custom TestElement of the provided
|
|
601
|
+
* type. This is useful to create custom higher-level abstractions similar to TestInput, TestSelect, etc. for
|
|
602
|
+
* custom elements or components.
|
|
603
|
+
* @param selector a CSS or directive selector
|
|
604
|
+
* @param customTestElementType the type of the TestElement subclass that will wrap the found element
|
|
605
|
+
*/
|
|
606
|
+
custom(selector, customTestElementType) {
|
|
607
|
+
return this.testElement.custom(selector, customTestElementType);
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Gets the elements matching the given selector, and creates and returns custom TestElements of the provided
|
|
611
|
+
* type. This is useful to create custom higher-level abstractions similar to TestInput, TestSelect, etc. for
|
|
612
|
+
* custom elements or components.
|
|
613
|
+
* @param selector a CSS or directive selector
|
|
614
|
+
* @param customTestElementType the type of the TestElement subclass that will wrap the found elements
|
|
615
|
+
*/
|
|
616
|
+
customs(selector, customTestElementType) {
|
|
617
|
+
return this.testElement.customs(selector, customTestElementType);
|
|
618
|
+
}
|
|
507
619
|
/**
|
|
508
620
|
* Triggers a change detection using the wrapped fixture
|
|
509
621
|
*/
|
|
@@ -527,6 +639,7 @@ class ComponentTester {
|
|
|
527
639
|
* `route.parent.snapshot` or `route.snapshot.parent`.
|
|
528
640
|
*
|
|
529
641
|
* @returns a partially populated, fake ActivatedRoute, depending on what you passed in
|
|
642
|
+
* @deprecated favor stubRoute, which creates an easier to use and more logical stub
|
|
530
643
|
*/
|
|
531
644
|
function fakeRoute(options) {
|
|
532
645
|
const result = {
|
|
@@ -549,12 +662,14 @@ function fakeRoute(options) {
|
|
|
549
662
|
};
|
|
550
663
|
for (let route = result; route; route = route.parent) {
|
|
551
664
|
if (route.parent && route.parent.snapshot && !route.snapshot) {
|
|
665
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
552
666
|
route.snapshot = fakeSnapshot({});
|
|
553
667
|
}
|
|
554
668
|
if (route.parent && route.parent.snapshot && !route.snapshot.parent) {
|
|
555
669
|
route.snapshot.parent = route.parent.snapshot;
|
|
556
670
|
}
|
|
557
671
|
if (route.snapshot && route.snapshot.parent && !route.parent) {
|
|
672
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
558
673
|
route.parent = fakeRoute({});
|
|
559
674
|
}
|
|
560
675
|
if (route.snapshot && route.snapshot.parent && route.parent && !route.parent.snapshot) {
|
|
@@ -570,6 +685,7 @@ function fakeRoute(options) {
|
|
|
570
685
|
* The same goes for queryParams and queryParamMap.
|
|
571
686
|
*
|
|
572
687
|
* @returns a partially populated, fake ActivatedRoute, depending on what you passed in
|
|
688
|
+
* @deprecated favor stubRoute, which creates an easier to use and more logical stub for both the route and its snapshot
|
|
573
689
|
*/
|
|
574
690
|
function fakeSnapshot(options) {
|
|
575
691
|
return {
|
|
@@ -590,6 +706,240 @@ function fakeSnapshot(options) {
|
|
|
590
706
|
pathFromRoot: options.pathFromRoot
|
|
591
707
|
};
|
|
592
708
|
}
|
|
709
|
+
class ActivatedRouteSnapshotStub extends ActivatedRouteSnapshot {
|
|
710
|
+
constructor() {
|
|
711
|
+
super();
|
|
712
|
+
this._parent = null;
|
|
713
|
+
this._firstChild = null;
|
|
714
|
+
this._children = [];
|
|
715
|
+
this._pathFromRoot = [];
|
|
716
|
+
this._routeConfig = null;
|
|
717
|
+
this._root = this;
|
|
718
|
+
}
|
|
719
|
+
get parent() {
|
|
720
|
+
return this._parent;
|
|
721
|
+
}
|
|
722
|
+
set parent(value) {
|
|
723
|
+
this._parent = value;
|
|
724
|
+
}
|
|
725
|
+
get root() {
|
|
726
|
+
return this._root;
|
|
727
|
+
}
|
|
728
|
+
set root(value) {
|
|
729
|
+
this._root = value;
|
|
730
|
+
}
|
|
731
|
+
get firstChild() {
|
|
732
|
+
return this._firstChild;
|
|
733
|
+
}
|
|
734
|
+
set firstChild(value) {
|
|
735
|
+
this._firstChild = value;
|
|
736
|
+
}
|
|
737
|
+
get children() {
|
|
738
|
+
return this._children;
|
|
739
|
+
}
|
|
740
|
+
set children(value) {
|
|
741
|
+
this._children = value;
|
|
742
|
+
}
|
|
743
|
+
get pathFromRoot() {
|
|
744
|
+
return this._pathFromRoot;
|
|
745
|
+
}
|
|
746
|
+
set pathFromRoot(value) {
|
|
747
|
+
this._pathFromRoot = value;
|
|
748
|
+
}
|
|
749
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
750
|
+
// @ts-ignore
|
|
751
|
+
get routeConfig() {
|
|
752
|
+
return this._routeConfig;
|
|
753
|
+
}
|
|
754
|
+
set routeConfig(route) {
|
|
755
|
+
this._routeConfig = route;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* A stub for ActivatedRoute. It behaves almost the same way as the actual ActivatedRoute, exposing a snapshot
|
|
760
|
+
* and observables for the params, query params etc., which are kept in sync.
|
|
761
|
+
*
|
|
762
|
+
* In addition, this stub allows simulating a navigation by changing the params, the query params, the fragment, etc.
|
|
763
|
+
* When that happens, the snapshot is modified, then the relevant observables emit the new values.
|
|
764
|
+
*
|
|
765
|
+
* There are some things that don't really work the same way as the real ActivatedRoute though:
|
|
766
|
+
* - the handling of the firstChild and of the children is entirely under the tester's responsibility. Setting the parent
|
|
767
|
+
* of a route stub does not add this route to the children of its parent, for example.
|
|
768
|
+
* - when changing the params, query params, fragment, etc., their associated observable emits unconditionally, instead of
|
|
769
|
+
* first checking if the value is actually different from before. It's thus the responsibility of the tester to not
|
|
770
|
+
* change the values if they're the same as before.
|
|
771
|
+
*/
|
|
772
|
+
class ActivatedRouteStub extends ActivatedRoute {
|
|
773
|
+
/**
|
|
774
|
+
* Constructs a new instance, based on the given options.
|
|
775
|
+
* If an option is not provided (or if no option is provided at all), then the route has a default value for this option
|
|
776
|
+
* (empty parameters for example, null fragment, etc.)
|
|
777
|
+
* If no parent is passed, then this route has no parent and is thus set as the root. Otherwise, the root and the path
|
|
778
|
+
* from root are created based on the root and path from root of the given parent route.
|
|
779
|
+
*/
|
|
780
|
+
constructor(options) {
|
|
781
|
+
super();
|
|
782
|
+
const snapshot = new ActivatedRouteSnapshotStub();
|
|
783
|
+
this.snapshot = snapshot;
|
|
784
|
+
this._firstChild = options?.firstChild ?? null;
|
|
785
|
+
this._children = options?.children ?? [];
|
|
786
|
+
this._parent = options?.parent ?? null;
|
|
787
|
+
this._root = this.parent?.root ?? this;
|
|
788
|
+
this._pathFromRoot = this.parent ? [...this.parent.pathFromRoot, this] : [this];
|
|
789
|
+
snapshot.params = options?.params ?? {};
|
|
790
|
+
snapshot.queryParams = options?.queryParams ?? {};
|
|
791
|
+
snapshot.data = options?.data ?? {};
|
|
792
|
+
snapshot.fragment = options?.fragment ?? null;
|
|
793
|
+
snapshot.url = options?.url ?? [];
|
|
794
|
+
snapshot.routeConfig = options?.routeConfig ?? null;
|
|
795
|
+
snapshot.firstChild = this.firstChild?.snapshot ?? null;
|
|
796
|
+
snapshot.children = this.children?.map(route => route.snapshot) ?? [];
|
|
797
|
+
snapshot.parent = this.parent?.snapshot ?? null;
|
|
798
|
+
snapshot.root = this.root.snapshot;
|
|
799
|
+
snapshot.pathFromRoot = this.pathFromRoot.map(route => route.snapshot);
|
|
800
|
+
this.paramsSubject = new BehaviorSubject(this.snapshot.params);
|
|
801
|
+
this.queryParamsSubject = new BehaviorSubject(this.snapshot.queryParams);
|
|
802
|
+
this.dataSubject = new BehaviorSubject(this.snapshot.data);
|
|
803
|
+
this.fragmentSubject = new BehaviorSubject(this.snapshot.fragment);
|
|
804
|
+
this.urlSubject = new BehaviorSubject(this.snapshot.url);
|
|
805
|
+
this.params = this.paramsSubject.asObservable();
|
|
806
|
+
this.queryParams = this.queryParamsSubject.asObservable();
|
|
807
|
+
this.data = this.dataSubject.asObservable();
|
|
808
|
+
this.fragment = this.fragmentSubject.asObservable();
|
|
809
|
+
this.url = this.urlSubject.asObservable();
|
|
810
|
+
}
|
|
811
|
+
get root() {
|
|
812
|
+
return this._root;
|
|
813
|
+
}
|
|
814
|
+
get parent() {
|
|
815
|
+
return this._parent;
|
|
816
|
+
}
|
|
817
|
+
get pathFromRoot() {
|
|
818
|
+
return this._pathFromRoot;
|
|
819
|
+
}
|
|
820
|
+
get firstChild() {
|
|
821
|
+
return this._firstChild;
|
|
822
|
+
}
|
|
823
|
+
get children() {
|
|
824
|
+
return this._children;
|
|
825
|
+
}
|
|
826
|
+
get routeConfig() {
|
|
827
|
+
return this.snapshot.routeConfig;
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Triggers a navigation with the given new parameters. All the other parts (query params etc.) stay as the are.
|
|
831
|
+
* This is a shortcut to `triggerNavigation` that can be used to only change the parameters.
|
|
832
|
+
*/
|
|
833
|
+
setParams(params) {
|
|
834
|
+
this.triggerNavigation({ params });
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Triggers a navigation with the given new parameter. The other parameters, as well as all the other parts (query params etc.)
|
|
838
|
+
* stay as the are.
|
|
839
|
+
* This is a shortcut to `triggerNavigation` that can be used to only change one parameter.
|
|
840
|
+
*/
|
|
841
|
+
setParam(name, value) {
|
|
842
|
+
this.setParams({ ...this.snapshot.params, [name]: value });
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Triggers a navigation with the given new query parameters. All the other parts (params etc.) stay as the are.
|
|
846
|
+
* This is a shortcut to `triggerNavigation` that can be used to only change the query parameters.
|
|
847
|
+
*/
|
|
848
|
+
setQueryParams(queryParams) {
|
|
849
|
+
this.triggerNavigation({ queryParams });
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Triggers a navigation with the given new parameter. The other query parameters, as well as all the other parts (params etc.)
|
|
853
|
+
* stay as the are.
|
|
854
|
+
* This is a shortcut to `triggerNavigation` that can be used to only change one query parameter.
|
|
855
|
+
*/
|
|
856
|
+
setQueryParam(name, value) {
|
|
857
|
+
this.setQueryParams({ ...this.snapshot.queryParams, [name]: value });
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Triggers a navigation with the given new data. The other parameters, as well as all the other parts (params etc.)
|
|
861
|
+
* stay as the are.
|
|
862
|
+
* This is a shortcut to `triggerNavigation` that can be used to only change the data.
|
|
863
|
+
*/
|
|
864
|
+
setData(data) {
|
|
865
|
+
this.triggerNavigation({ data });
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Triggers a navigation with the given new data item. The other data, as well as all the other parts (params etc.)
|
|
869
|
+
* stay as the are.
|
|
870
|
+
* This is a shortcut to `triggerNavigation` that can be used to only change one data item.
|
|
871
|
+
*/
|
|
872
|
+
setDataItem(name, value) {
|
|
873
|
+
this.setData({ ...this.snapshot.data, [name]: value });
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Triggers a navigation with the given new fragment. The other parts (params etc.) stay as the are.
|
|
877
|
+
* This is a shortcut to `triggerNavigation` that can be used to only change the fragment.
|
|
878
|
+
*/
|
|
879
|
+
setFragment(fragment) {
|
|
880
|
+
this.triggerNavigation({ fragment });
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Triggers a navigation with the given new url. The other parts (params etc.) stay as the are.
|
|
884
|
+
* This is a shortcut to `triggerNavigation` that can be used to only change the url.
|
|
885
|
+
*/
|
|
886
|
+
setUrl(url) {
|
|
887
|
+
this.triggerNavigation({ url });
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Triggers a navigation based on the given options. If an option is undefined or null, it's ignored. Except for fragment, which is only
|
|
891
|
+
* ignored if it's undefined, because null is a valid value for a fragment.
|
|
892
|
+
*
|
|
893
|
+
* The non-ignored values are used to change the snapshot of the route. Once the snapshot has been modified,
|
|
894
|
+
* the observables corresponding to the updated parts emit the new value.
|
|
895
|
+
*
|
|
896
|
+
* So, setting params and query params will make the params and queryParams observables emit, but not the fragment, data and
|
|
897
|
+
* url observables for example. This is consistent to how the router behaves.
|
|
898
|
+
*/
|
|
899
|
+
triggerNavigation(options) {
|
|
900
|
+
// set the snapshot first
|
|
901
|
+
if (options.params) {
|
|
902
|
+
this.snapshot.params = options.params;
|
|
903
|
+
}
|
|
904
|
+
if (options.queryParams) {
|
|
905
|
+
this.snapshot.queryParams = options.queryParams;
|
|
906
|
+
}
|
|
907
|
+
if (options.fragment !== undefined) {
|
|
908
|
+
this.snapshot.fragment = options.fragment;
|
|
909
|
+
}
|
|
910
|
+
if (options.data) {
|
|
911
|
+
this.snapshot.data = options.data;
|
|
912
|
+
}
|
|
913
|
+
if (options.url) {
|
|
914
|
+
this.snapshot.url = options.url;
|
|
915
|
+
}
|
|
916
|
+
// then emit everything that has changed
|
|
917
|
+
if (options.params) {
|
|
918
|
+
this.paramsSubject.next(this.snapshot.params);
|
|
919
|
+
}
|
|
920
|
+
if (options.queryParams) {
|
|
921
|
+
this.queryParamsSubject.next(this.snapshot.queryParams);
|
|
922
|
+
}
|
|
923
|
+
if (options.fragment !== undefined) {
|
|
924
|
+
this.fragmentSubject.next(this.snapshot.fragment);
|
|
925
|
+
}
|
|
926
|
+
if (options.data) {
|
|
927
|
+
this.dataSubject.next(this.snapshot.data);
|
|
928
|
+
}
|
|
929
|
+
if (options.url) {
|
|
930
|
+
this.urlSubject.next(this.snapshot.url);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
toString() {
|
|
934
|
+
return 'ActivatedRouteStub';
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Creates a new ActivatedRouteStub, by calling its constructor.
|
|
939
|
+
*/
|
|
940
|
+
function stubRoute(options) {
|
|
941
|
+
return new ActivatedRouteStub(options);
|
|
942
|
+
}
|
|
593
943
|
|
|
594
944
|
const speculoosMatchers = {
|
|
595
945
|
/**
|
|
@@ -671,6 +1021,45 @@ const speculoosMatchers = {
|
|
|
671
1021
|
}
|
|
672
1022
|
};
|
|
673
1023
|
},
|
|
1024
|
+
/**
|
|
1025
|
+
* Checks that the receiver is a TestElement wrapping a DOM element and has the given textContent, after both have been trimmed.
|
|
1026
|
+
* So, An element such as
|
|
1027
|
+
* ```
|
|
1028
|
+
* <h1>
|
|
1029
|
+
* Some title
|
|
1030
|
+
* </h1>
|
|
1031
|
+
* ```
|
|
1032
|
+
* will pass the test
|
|
1033
|
+
* ```
|
|
1034
|
+
* expect(tester.title).toHaveTrimmedText('Some title')
|
|
1035
|
+
* ```
|
|
1036
|
+
*/
|
|
1037
|
+
toHaveTrimmedText: () => {
|
|
1038
|
+
const assert = (isNegative, el, expected) => {
|
|
1039
|
+
const trimmedExpected = expected.trim();
|
|
1040
|
+
if (!el) {
|
|
1041
|
+
return { pass: false, message: `Expected to check trimmed text '${trimmedExpected}' on element, but element was falsy` };
|
|
1042
|
+
}
|
|
1043
|
+
if (!(el instanceof TestElement)) {
|
|
1044
|
+
return {
|
|
1045
|
+
pass: false,
|
|
1046
|
+
message: `Expected to check trimmed text '${trimmedExpected}' on element, but element was not a TestElement`
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
const actual = el.textContent?.trim();
|
|
1050
|
+
const pass = actual === trimmedExpected;
|
|
1051
|
+
const message = `Expected element to ${isNegative ? 'not ' : ''}have trimmed text '${trimmedExpected}', but had '${actual}'`;
|
|
1052
|
+
return { pass: isNegative ? !pass : pass, message };
|
|
1053
|
+
};
|
|
1054
|
+
return {
|
|
1055
|
+
compare: (el, expected) => {
|
|
1056
|
+
return assert(false, el, expected);
|
|
1057
|
+
},
|
|
1058
|
+
negativeCompare: (el, expected) => {
|
|
1059
|
+
return assert(true, el, expected);
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
},
|
|
674
1063
|
/**
|
|
675
1064
|
* Checks that the receiver is a TestElement wrapping a DOM element and contains the given textContent
|
|
676
1065
|
*/
|
|
@@ -829,11 +1218,36 @@ const speculoosMatchers = {
|
|
|
829
1218
|
}
|
|
830
1219
|
};
|
|
831
1220
|
|
|
1221
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1222
|
+
function collectMethodNames(proto) {
|
|
1223
|
+
if (!proto || proto === Object.prototype) {
|
|
1224
|
+
return [];
|
|
1225
|
+
}
|
|
1226
|
+
const methodNames = [];
|
|
1227
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
1228
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, key);
|
|
1229
|
+
if (descriptor && typeof descriptor.value === 'function' && key !== 'constructor') {
|
|
1230
|
+
methodNames.push(key);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return [...methodNames, ...collectMethodNames(Object.getPrototypeOf(proto))];
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Creates a spy object for a class where all the methods of the class (and of its superclasses) are spies.
|
|
1237
|
+
* I.e., for a class `UserService` with methods `get()`, `create()`, `update()` and `delete()`, calling
|
|
1238
|
+
* `createMock(UserService)` is equivalent to calling
|
|
1239
|
+
* `jasmine.createSpyObj<UserService>('UserService', ['get', 'create', 'update', 'delete'])`.
|
|
1240
|
+
* @param type the type to mock (usually a service class)
|
|
1241
|
+
*/
|
|
1242
|
+
function createMock(type) {
|
|
1243
|
+
return jasmine.createSpyObj(type.name, collectMethodNames(type.prototype));
|
|
1244
|
+
}
|
|
1245
|
+
|
|
832
1246
|
/* eslint-disable */
|
|
833
1247
|
|
|
834
1248
|
/**
|
|
835
1249
|
* Generated bundle index. Do not edit.
|
|
836
1250
|
*/
|
|
837
1251
|
|
|
838
|
-
export { ComponentTester, TestButton, TestElement, TestHtmlElement, TestInput, TestSelect, TestTextArea, fakeRoute, fakeSnapshot, speculoosMatchers };
|
|
839
|
-
//# sourceMappingURL=ngx-speculoos.
|
|
1252
|
+
export { ActivatedRouteStub, ComponentTester, TestButton, TestElement, TestHtmlElement, TestInput, TestSelect, TestTextArea, createMock, fakeRoute, fakeSnapshot, speculoosMatchers, stubRoute };
|
|
1253
|
+
//# sourceMappingURL=ngx-speculoos.mjs.map
|