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.
Files changed (36) hide show
  1. package/README.md +403 -42
  2. package/esm2020/jasmine-matchers.mjs +2 -0
  3. package/esm2020/lib/component-tester.mjs +147 -0
  4. package/esm2020/lib/matchers.mjs +283 -0
  5. package/esm2020/lib/mock.mjs +25 -0
  6. package/esm2020/lib/route.mjs +319 -0
  7. package/{esm2015/lib/test-button.js → esm2020/lib/test-button.mjs} +0 -0
  8. package/esm2020/lib/test-element-querier.mjs +140 -0
  9. package/esm2020/lib/test-element.mjs +142 -0
  10. package/{esm2015/lib/test-html-element.js → esm2020/lib/test-html-element.mjs} +0 -0
  11. package/{esm2015/lib/test-input.js → esm2020/lib/test-input.mjs} +0 -0
  12. package/{esm2015/lib/test-select.js → esm2020/lib/test-select.mjs} +0 -0
  13. package/{esm2015/lib/test-textarea.js → esm2020/lib/test-textarea.mjs} +0 -0
  14. package/{esm2015/ngx-speculoos.js → esm2020/ngx-speculoos.mjs} +0 -0
  15. package/{esm2015/public_api.js → esm2020/public_api.mjs} +2 -1
  16. package/fesm2015/ngx-speculoos.mjs +1257 -0
  17. package/fesm2015/ngx-speculoos.mjs.map +1 -0
  18. package/{fesm2015/ngx-speculoos.js → fesm2020/ngx-speculoos.mjs} +436 -22
  19. package/fesm2020/ngx-speculoos.mjs.map +1 -0
  20. package/jasmine-matchers.d.ts +5 -0
  21. package/lib/component-tester.d.ts +87 -49
  22. package/lib/mock.d.ts +10 -0
  23. package/lib/route.d.ts +149 -0
  24. package/lib/test-element-querier.d.ts +15 -15
  25. package/lib/test-element.d.ts +87 -49
  26. package/package.json +24 -11
  27. package/public_api.d.ts +1 -0
  28. package/bundles/ngx-speculoos.umd.js +0 -1259
  29. package/bundles/ngx-speculoos.umd.js.map +0 -1
  30. package/esm2015/jasmine-matchers.js +0 -2
  31. package/esm2015/lib/component-tester.js +0 -96
  32. package/esm2015/lib/matchers.js +0 -244
  33. package/esm2015/lib/route.js +0 -81
  34. package/esm2015/lib/test-element-querier.js +0 -129
  35. package/esm2015/lib/test-element.js +0 -91
  36. package/fesm2015/ngx-speculoos.js.map +0 -1
@@ -0,0 +1,1257 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { By } from '@angular/platform-browser';
3
+ import { convertToParamMap, ActivatedRouteSnapshot, ActivatedRoute } from '@angular/router';
4
+ import { map, BehaviorSubject } from 'rxjs';
5
+
6
+ /**
7
+ * A wrapped DOM element, providing additional methods and attributes helping with writing tests
8
+ */
9
+ class TestElement {
10
+ constructor(tester,
11
+ /**
12
+ * the wrapped debug element
13
+ */
14
+ debugElement) {
15
+ this.tester = tester;
16
+ this.debugElement = debugElement;
17
+ this.querier = new TestElementQuerier(tester, debugElement);
18
+ }
19
+ get nativeElement() {
20
+ return this.debugElement.nativeElement;
21
+ }
22
+ /**
23
+ * the text content of this element
24
+ */
25
+ get textContent() {
26
+ return this.nativeElement.textContent;
27
+ }
28
+ /**
29
+ * dispatches an event of the given type from the wrapped element, then triggers a change detection
30
+ */
31
+ dispatchEventOfType(type) {
32
+ this.nativeElement.dispatchEvent(new Event(type));
33
+ this.tester.detectChanges();
34
+ }
35
+ /**
36
+ * dispatches the given event from the wrapped element, then triggers a change detection
37
+ */
38
+ dispatchEvent(event) {
39
+ this.nativeElement.dispatchEvent(event);
40
+ this.tester.detectChanges();
41
+ }
42
+ /**
43
+ * Gets the CSS classes of the wrapped element, as an array
44
+ */
45
+ get classes() {
46
+ return Array.prototype.slice.call(this.nativeElement.classList);
47
+ }
48
+ /**
49
+ * Gets the attribute of the wrapped element with the given name
50
+ * @param name the name of the attribute to get
51
+ */
52
+ attr(name) {
53
+ return this.nativeElement.getAttribute(name);
54
+ }
55
+ element(selector) {
56
+ return this.querier.element(selector);
57
+ }
58
+ elements(selector) {
59
+ return this.querier.elements(selector);
60
+ }
61
+ /**
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 or directive selector
64
+ * @returns the wrapped input, or null if no element was matched
65
+ */
66
+ input(selector) {
67
+ return this.querier.input(selector);
68
+ }
69
+ /**
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 or directive selector
72
+ * @returns the wrapped select, or null if no element was matched
73
+ */
74
+ select(selector) {
75
+ return this.querier.select(selector);
76
+ }
77
+ /**
78
+ * Gets the first textarea matched by the given selector
79
+ * @param selector a CSS or directive selector
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
+ * @throws {Error} if the matched element isn't actually a textarea
82
+ */
83
+ textarea(selector) {
84
+ return this.querier.textarea(selector);
85
+ }
86
+ /**
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 or directive selector
89
+ * @returns the wrapped button, or null if no element was matched
90
+ */
91
+ button(selector) {
92
+ return this.querier.button(selector);
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
+ var _a, _b, _c;
100
+ return (_c = (_b = (_a = this.querier.element(selector)) === null || _a === void 0 ? void 0 : _a.debugElement) === null || _b === void 0 ? void 0 : _b.componentInstance) !== null && _c !== void 0 ? _c : null;
101
+ }
102
+ /**
103
+ * Gets the directives matching the given component directive selector and returns their component instance
104
+ * @param selector the selector of a component directive
105
+ */
106
+ components(selector) {
107
+ return this.querier.elements(selector).map(e => e.debugElement.componentInstance);
108
+ }
109
+ /**
110
+ * Gets the first element matching the given selector, then gets the given token from its injector, or null if there is no such token
111
+ * @param selector a CSS or directive selector
112
+ * @param token the token to get from the matched element injector
113
+ */
114
+ token(selector, token) {
115
+ var _a, _b, _c, _d;
116
+ return (_d = (_c = (_b = (_a = this.querier.element(selector)) === null || _a === void 0 ? void 0 : _a.debugElement) === null || _b === void 0 ? void 0 : _b.injector) === null || _c === void 0 ? void 0 : _c.get(token, null)) !== null && _d !== void 0 ? _d : null;
117
+ }
118
+ /**
119
+ * Gets the elements matching the given selector, then gets their given token from their injector, or null if there is no such token
120
+ * @param selector a CSS or directive selector
121
+ * @param token the token to get from the matched element injector
122
+ */
123
+ tokens(selector, token) {
124
+ return this.querier.elements(selector).map(e => { var _a; return (_a = e.debugElement.injector.get(token, null)) !== null && _a !== void 0 ? _a : null; });
125
+ }
126
+ /**
127
+ * Gets the element matching the given selector, and if found, creates and returns a custom TestElement of the provided
128
+ * type. This is useful to create custom higher-level abstractions similar to TestInput, TestSelect, etc. for
129
+ * custom elements or components.
130
+ * @param selector a CSS or directive selector
131
+ * @param customTestElementType the type of the TestElement subclass that will wrap the found element
132
+ */
133
+ custom(selector, customTestElementType) {
134
+ const element = this.querier.element(selector);
135
+ return element && new customTestElementType(this.tester, element.debugElement);
136
+ }
137
+ /**
138
+ * Gets the elements matching the given selector, and creates and returns custom TestElements of the provided
139
+ * type. This is useful to create custom higher-level abstractions similar to TestInput, TestSelect, etc. for
140
+ * custom elements or components.
141
+ * @param selector a CSS or directive selector
142
+ * @param customTestElementType the type of the TestElement subclass that will wrap the found elements
143
+ */
144
+ customs(selector, customTestElementType) {
145
+ return this.querier.elements(selector).map(element => new customTestElementType(this.tester, element.debugElement));
146
+ }
147
+ }
148
+
149
+ /**
150
+ * A wrapped DOM HTML element, providing additional methods and attributes helping with writing tests
151
+ */
152
+ class TestHtmlElement extends TestElement {
153
+ constructor(tester, debugElement) {
154
+ super(tester, debugElement);
155
+ }
156
+ /**
157
+ * Clicks on the wrapped element, then triggers a change detection
158
+ */
159
+ click() {
160
+ this.nativeElement.click();
161
+ this.tester.detectChanges();
162
+ }
163
+ /**
164
+ * Tests if the element is visible, in the same meaning (and implementation) as in jQuery, i.e.
165
+ * present anywhere in the DOM, and visible.
166
+ * An element is not visible typically, if its display style or any of its ancestors display style is none.
167
+ */
168
+ get visible() {
169
+ return !!(this.nativeElement.offsetWidth || this.nativeElement.offsetHeight || this.nativeElement.getClientRects().length);
170
+ }
171
+ }
172
+
173
+ /**
174
+ * A wrapped button element, providing additional methods and attributes helping with writing tests
175
+ */
176
+ class TestButton extends TestHtmlElement {
177
+ constructor(tester, debugElement) {
178
+ super(tester, debugElement);
179
+ }
180
+ /**
181
+ * the disabled flag of the button
182
+ */
183
+ get disabled() {
184
+ return this.nativeElement.disabled;
185
+ }
186
+ }
187
+
188
+ /**
189
+ * A wrapped DOM HTML select element, providing additional methods and attributes helping with writing tests
190
+ */
191
+ class TestSelect extends TestHtmlElement {
192
+ constructor(tester, debugElement) {
193
+ super(tester, debugElement);
194
+ }
195
+ /**
196
+ * Selects the option at the given index, then dispatches an event of type change and triggers a change detection
197
+ */
198
+ selectIndex(index) {
199
+ this.nativeElement.selectedIndex = index;
200
+ this.dispatchEventOfType('change');
201
+ }
202
+ /**
203
+ * Selects the first option with the given value, then dispatches an event of type change and triggers a change detection.
204
+ * If there is no option with the given value, then does nothing
205
+ * TODO should it throw instead?
206
+ */
207
+ selectValue(value) {
208
+ const index = this.optionValues.indexOf(value);
209
+ if (index >= 0) {
210
+ this.selectIndex(index);
211
+ }
212
+ }
213
+ /**
214
+ * Selects the first option with the given label (or text), then dispatches an event of type change and triggers a change detection.
215
+ * If there is no option with the given label, then does nothing
216
+ * TODO should it throw instead?
217
+ */
218
+ selectLabel(label) {
219
+ const index = this.optionLabels.indexOf(label);
220
+ if (index >= 0) {
221
+ this.selectIndex(index);
222
+ }
223
+ }
224
+ /**
225
+ * the selected index of the wrapped select
226
+ */
227
+ get selectedIndex() {
228
+ return this.nativeElement.selectedIndex;
229
+ }
230
+ /**
231
+ * the value of the selected option of the wrapped select, or null if there is no selected option
232
+ */
233
+ get selectedValue() {
234
+ if (this.selectedIndex < 0) {
235
+ return null;
236
+ }
237
+ return this.nativeElement.options[this.selectedIndex].value;
238
+ }
239
+ /**
240
+ * the label (or text if no label) of the selected option of the wrapped select, or null if there is no selected option
241
+ */
242
+ get selectedLabel() {
243
+ if (this.selectedIndex < 0) {
244
+ return null;
245
+ }
246
+ return this.nativeElement.options[this.selectedIndex].label;
247
+ }
248
+ /**
249
+ * the values of the options, as an array
250
+ */
251
+ get optionValues() {
252
+ return Array.prototype.slice.call(this.nativeElement.options).map(option => option.value);
253
+ }
254
+ /**
255
+ * the labels (or texts if no label) of the options, as an array
256
+ */
257
+ get optionLabels() {
258
+ return Array.prototype.slice.call(this.nativeElement.options).map(option => option.label);
259
+ }
260
+ /**
261
+ * the number of options in the select
262
+ */
263
+ get size() {
264
+ return this.nativeElement.options.length;
265
+ }
266
+ /**
267
+ * the disabled property of the wrapped select
268
+ */
269
+ get disabled() {
270
+ return this.nativeElement.disabled;
271
+ }
272
+ }
273
+
274
+ /**
275
+ * A wrapped DOM HTML textarea element, providing additional methods and attributes helping with writing tests
276
+ */
277
+ class TestTextArea extends TestHtmlElement {
278
+ constructor(tester, debugElement) {
279
+ super(tester, debugElement);
280
+ }
281
+ /**
282
+ * Sets the value of the wrapped textarea, then dispatches an event of type input and triggers a change detection
283
+ * @param value the new value of the textarea
284
+ */
285
+ fillWith(value) {
286
+ this.nativeElement.value = value;
287
+ this.dispatchEventOfType('input');
288
+ }
289
+ /**
290
+ * the value of the wrapped textarea
291
+ */
292
+ get value() {
293
+ return this.nativeElement.value;
294
+ }
295
+ /**
296
+ * the disabled property of the wrapped textarea
297
+ */
298
+ get disabled() {
299
+ return this.nativeElement.disabled;
300
+ }
301
+ }
302
+
303
+ /**
304
+ * A wrapped DOM HTML input element, providing additional methods and attributes helping with writing tests
305
+ */
306
+ class TestInput extends TestHtmlElement {
307
+ constructor(tester, debugElement) {
308
+ super(tester, debugElement);
309
+ }
310
+ /**
311
+ * Sets the value of the wrapped input, then dispatches an event of type input and triggers a change detection
312
+ * @param value the new value of the input
313
+ */
314
+ fillWith(value) {
315
+ this.nativeElement.value = value;
316
+ this.dispatchEventOfType('input');
317
+ }
318
+ /**
319
+ * the value of the wrapped input
320
+ */
321
+ get value() {
322
+ return this.nativeElement.value;
323
+ }
324
+ /**
325
+ * the checked property of the wrapped input
326
+ */
327
+ get checked() {
328
+ return this.nativeElement.checked;
329
+ }
330
+ /**
331
+ * the disabled property of the wrapped input
332
+ */
333
+ get disabled() {
334
+ return this.nativeElement.disabled;
335
+ }
336
+ /**
337
+ * Checks the wrapped input, then dispatches an event of type change and triggers a change detection
338
+ */
339
+ check() {
340
+ this.nativeElement.checked = true;
341
+ this.dispatchEventOfType('change');
342
+ }
343
+ /**
344
+ * Unchecks the wrapped input, then dispatches an event of type change and triggers a change detection
345
+ */
346
+ uncheck() {
347
+ this.nativeElement.checked = false;
348
+ this.dispatchEventOfType('change');
349
+ }
350
+ }
351
+
352
+ /* eslint-disable @typescript-eslint/no-explicit-any */
353
+ /**
354
+ * @internal
355
+ */
356
+ class TestElementQuerier {
357
+ constructor(tester, root) {
358
+ this.tester = tester;
359
+ this.root = root;
360
+ }
361
+ static wrap(childDebugElement, tester) {
362
+ const childElement = childDebugElement.nativeElement;
363
+ if (childElement instanceof HTMLButtonElement) {
364
+ return new TestButton(tester, childDebugElement);
365
+ }
366
+ else if (childElement instanceof HTMLInputElement) {
367
+ return new TestInput(tester, childDebugElement);
368
+ }
369
+ else if (childElement instanceof HTMLSelectElement) {
370
+ return new TestSelect(tester, childDebugElement);
371
+ }
372
+ else if (childElement instanceof HTMLTextAreaElement) {
373
+ return new TestTextArea(tester, childDebugElement);
374
+ }
375
+ else if (childElement instanceof HTMLElement) {
376
+ return new TestHtmlElement(tester, childDebugElement);
377
+ }
378
+ else {
379
+ return new TestElement(tester, childDebugElement);
380
+ }
381
+ }
382
+ /**
383
+ * Gets the first element matching the given selector and wraps it into a TestElement. The actual type
384
+ * of the returned value is the TestElement subclass matching the type of the found element. So, if the
385
+ * matched element is an input for example, the method will return a TestInput. You can thus use
386
+ * `tester.element('#some-input') as TestInput`.
387
+ * @param selector a CSS or directive selector
388
+ * @returns the wrapped element, or null if no element matches the selector.
389
+ */
390
+ element(selector) {
391
+ const childElement = this.query(selector);
392
+ return childElement && TestElementQuerier.wrap(childElement, this.tester);
393
+ }
394
+ /**
395
+ * Gets all the elements matching the given selector and wraps them into a TestElement. The actual type
396
+ * of the returned elements is the TestElement subclass matching the type of the found element. So, if the
397
+ * matched elements are inputs for example, the method will return an array of TestInput. You can thus use
398
+ * `tester.elements('input') as Array<TestInput>`.
399
+ * @param selector a CSS or directive selector
400
+ * @returns the array of matched elements, empty if no element was matched
401
+ */
402
+ elements(selector) {
403
+ const childElements = this.queryAll(selector);
404
+ return childElements.map(debugElement => TestElementQuerier.wrap(debugElement, this.tester));
405
+ }
406
+ /**
407
+ * Gets the first input matched by the given selector. Throws an Error if the matched element isn't actually an input.
408
+ * @param selector a CSS or directive selector
409
+ * @returns the wrapped input, or null if no element was matched
410
+ */
411
+ input(selector) {
412
+ const childElement = this.query(selector);
413
+ if (!childElement) {
414
+ return null;
415
+ }
416
+ else if (!(childElement.nativeElement instanceof HTMLInputElement)) {
417
+ throw new Error(`Element with selector ${selector} is not an HTMLInputElement`);
418
+ }
419
+ return new TestInput(this.tester, childElement);
420
+ }
421
+ /**
422
+ * Gets the first select matched by the given selector. Throws an Error if the matched element isn't actually a select.
423
+ * @param selector a CSS or directive selector
424
+ * @returns the wrapped select, or null if no element was matched
425
+ */
426
+ select(selector) {
427
+ const childElement = this.query(selector);
428
+ if (!childElement) {
429
+ return null;
430
+ }
431
+ else if (!(childElement.nativeElement instanceof HTMLSelectElement)) {
432
+ throw new Error(`Element with selector ${selector} is not an HTMLSelectElement`);
433
+ }
434
+ return new TestSelect(this.tester, childElement);
435
+ }
436
+ /**
437
+ * Gets the first textarea matched by the given selector
438
+ * @param selector a CSS or directive selector
439
+ * @returns the wrapped textarea, or null if no element was matched. Throws an Error if the matched element isn't actually a textarea.
440
+ * @throws {Error} if the matched element isn't actually a textarea
441
+ */
442
+ textarea(selector) {
443
+ const childElement = this.query(selector);
444
+ if (!childElement) {
445
+ return null;
446
+ }
447
+ else if (!(childElement.nativeElement instanceof HTMLTextAreaElement)) {
448
+ throw new Error(`Element with selector ${selector} is not an HTMLTextAreaElement`);
449
+ }
450
+ return new TestTextArea(this.tester, childElement);
451
+ }
452
+ /**
453
+ * Gets the first button matched by the given selector. Throws an Error if the matched element isn't actually a button.
454
+ * @param selector a CSS or directive selector
455
+ * @returns the wrapped button, or null if no element was matched
456
+ */
457
+ button(selector) {
458
+ const childElement = this.query(selector);
459
+ if (!childElement) {
460
+ return null;
461
+ }
462
+ else if (!(childElement.nativeElement instanceof HTMLButtonElement)) {
463
+ throw new Error(`Element with selector ${selector} is not an HTMLButtonElement`);
464
+ }
465
+ return new TestButton(this.tester, childElement);
466
+ }
467
+ query(selector) {
468
+ if (typeof selector === 'string') {
469
+ return this.root.query(By.css(selector));
470
+ }
471
+ else {
472
+ return this.root.query(By.directive(selector));
473
+ }
474
+ }
475
+ queryAll(selector) {
476
+ if (typeof selector === 'string') {
477
+ return this.root.queryAll(By.css(selector));
478
+ }
479
+ else {
480
+ return this.root.queryAll(By.directive(selector));
481
+ }
482
+ }
483
+ }
484
+
485
+ /* eslint-disable @typescript-eslint/no-unused-vars */
486
+ /**
487
+ * The main entry point of the API. It wraps an Angular ComponentFixture<T>, and gives access to its
488
+ * most used properties and methods. It also allows getting elements wrapped in TestElement (and its subclasses)
489
+ * @param <C> the type of the component to test
490
+ */
491
+ class ComponentTester {
492
+ /**
493
+ * Creates a ComponentFixture for the given component type using the TestBed, and creates a ComponentTester
494
+ * wrapping (and delegating) to this fixture. If a fixture is passed, then delegates to this fixture directly.
495
+ *
496
+ * Note that no `detectChanges()` call is made by this constructor. It's up to the subclass constructor,
497
+ * or to the user of the created ComponentTester, to call `detectChanges()` at least once to trigger change
498
+ * detection. This is necessary because some component templates can only be evaluated once inputs
499
+ * have been set on the component instance.
500
+ *
501
+ * @param arg the type of the component to wrap, or a component fixture to wrap
502
+ */
503
+ constructor(arg) {
504
+ this.fixture = arg instanceof ComponentFixture ? arg : TestBed.createComponent(arg);
505
+ this.testElement = TestElementQuerier.wrap(this.debugElement, this);
506
+ }
507
+ /**
508
+ * Creates a component fixture of the given type with the TestBed and wraps it into a ComponentTester
509
+ */
510
+ static create(componentType) {
511
+ const fixture = TestBed.createComponent(componentType);
512
+ return new ComponentTester(fixture);
513
+ }
514
+ /**
515
+ * The native DOM host element of the component
516
+ */
517
+ get nativeElement() {
518
+ return this.fixture.nativeElement;
519
+ }
520
+ /**
521
+ * Gets the instance of the tested component from the wrapped fixture
522
+ */
523
+ get componentInstance() {
524
+ return this.fixture.componentInstance;
525
+ }
526
+ /**
527
+ * Gets the debug element from the wrapped fixture
528
+ */
529
+ get debugElement() {
530
+ return this.fixture.debugElement;
531
+ }
532
+ element(selector) {
533
+ return this.testElement.element(selector);
534
+ }
535
+ elements(selector) {
536
+ return this.testElement.elements(selector);
537
+ }
538
+ /**
539
+ * Gets the first input matched by the given selector. Throws an Error if the matched element isn't actually an input.
540
+ * @param selector a CSS or directive selector
541
+ * @returns the wrapped input, or null if no element was matched
542
+ */
543
+ input(selector) {
544
+ return this.testElement.input(selector);
545
+ }
546
+ /**
547
+ * Gets the first select matched by the given selector. Throws an Error if the matched element isn't actually a select.
548
+ * @param selector a CSS or directive selector
549
+ * @returns the wrapped select, or null if no element was matched
550
+ */
551
+ select(selector) {
552
+ return this.testElement.select(selector);
553
+ }
554
+ /**
555
+ * Gets the first textarea matched by the given selector
556
+ * @param selector a CSS or directive selector
557
+ * @returns the wrapped textarea, or null if no element was matched. Throws an Error if the matched element isn't actually a textarea.
558
+ * @throws {Error} if the matched element isn't actually a textarea
559
+ */
560
+ textarea(selector) {
561
+ return this.testElement.textarea(selector);
562
+ }
563
+ /**
564
+ * Gets the first button matched by the given selector. Throws an Error if the matched element isn't actually a button.
565
+ * @param selector a CSS or directive selector
566
+ * @returns the wrapped button, or null if no element was matched
567
+ */
568
+ button(selector) {
569
+ return this.testElement.button(selector);
570
+ }
571
+ /**
572
+ * Gets the first directive matching the given component directive selector and returns its component instance
573
+ * @param selector the selector of a component directive
574
+ */
575
+ component(selector) {
576
+ return this.testElement.component(selector);
577
+ }
578
+ /**
579
+ * Gets the directives matching the given component directive selector and returns their component instance
580
+ * @param selector the selector of a component directive
581
+ */
582
+ components(selector) {
583
+ return this.testElement.components(selector);
584
+ }
585
+ /**
586
+ * Gets the first element matching the given selector, then gets the given token from its injector, or null if there is no such token
587
+ * @param selector a CSS or directive selector
588
+ * @param token the token to get from the matched element injector
589
+ */
590
+ token(selector, token) {
591
+ return this.testElement.token(selector, token);
592
+ }
593
+ /**
594
+ * Gets the elements matching the given selector, then gets their given token from their injector, or null if there is no such token
595
+ * @param selector a CSS or directive selector
596
+ * @param token the token to get from the matched element injector
597
+ */
598
+ tokens(selector, token) {
599
+ return this.testElement.tokens(selector, token);
600
+ }
601
+ /**
602
+ * Gets the element matching the given selector, and if found, creates and returns a custom TestElement of the provided
603
+ * type. This is useful to create custom higher-level abstractions similar to TestInput, TestSelect, etc. for
604
+ * custom elements or components.
605
+ * @param selector a CSS or directive selector
606
+ * @param customTestElementType the type of the TestElement subclass that will wrap the found element
607
+ */
608
+ custom(selector, customTestElementType) {
609
+ return this.testElement.custom(selector, customTestElementType);
610
+ }
611
+ /**
612
+ * Gets the elements matching the given selector, and creates and returns custom TestElements of the provided
613
+ * type. This is useful to create custom higher-level abstractions similar to TestInput, TestSelect, etc. for
614
+ * custom elements or components.
615
+ * @param selector a CSS or directive selector
616
+ * @param customTestElementType the type of the TestElement subclass that will wrap the found elements
617
+ */
618
+ customs(selector, customTestElementType) {
619
+ return this.testElement.customs(selector, customTestElementType);
620
+ }
621
+ /**
622
+ * Triggers a change detection using the wrapped fixture
623
+ */
624
+ detectChanges(checkNoChanges) {
625
+ this.fixture.detectChanges(checkNoChanges);
626
+ }
627
+ }
628
+
629
+ /**
630
+ * Creates a fake partial ActivatedRoute for tests.
631
+ *
632
+ * If you pass params, then the created route's paramMap will contain the same values.
633
+ * The same goes for queryParams and queryParamMap.
634
+ *
635
+ * If you pass a parent route and a snapshot, and the passed snapshot doesn't have a parent, then the snapshot's
636
+ * parent will be set to the parent route snapshot. This allows the code under test to use
637
+ * `route.parent.snapshot` or `route.snapshot.parent`.
638
+ *
639
+ * If you pass a snapshot with a parent, but don't pass a parent or pass a parent without snapshot, then the route's
640
+ * parent snapshot will be set to the given snapshot's parent. This allows the code under test to use
641
+ * `route.parent.snapshot` or `route.snapshot.parent`.
642
+ *
643
+ * @returns a partially populated, fake ActivatedRoute, depending on what you passed in
644
+ * @deprecated favor stubRoute, which creates an easier to use and more logical stub
645
+ */
646
+ function fakeRoute(options) {
647
+ const result = {
648
+ url: options.url,
649
+ params: options.params,
650
+ paramMap: options.params && options.params.pipe(map(params => convertToParamMap(params))),
651
+ queryParams: options.queryParams,
652
+ queryParamMap: options.queryParams && options.queryParams.pipe(map(params => convertToParamMap(params))),
653
+ fragment: options.fragment,
654
+ data: options.data,
655
+ outlet: options.outlet,
656
+ component: options.component,
657
+ snapshot: options.snapshot,
658
+ routeConfig: options.routeConfig,
659
+ root: options.root,
660
+ parent: options.parent,
661
+ firstChild: options.firstChild,
662
+ children: options.children,
663
+ pathFromRoot: options.pathFromRoot
664
+ };
665
+ for (let route = result; route; route = route.parent) {
666
+ if (route.parent && route.parent.snapshot && !route.snapshot) {
667
+ // eslint-disable-next-line deprecation/deprecation
668
+ route.snapshot = fakeSnapshot({});
669
+ }
670
+ if (route.parent && route.parent.snapshot && !route.snapshot.parent) {
671
+ route.snapshot.parent = route.parent.snapshot;
672
+ }
673
+ if (route.snapshot && route.snapshot.parent && !route.parent) {
674
+ // eslint-disable-next-line deprecation/deprecation
675
+ route.parent = fakeRoute({});
676
+ }
677
+ if (route.snapshot && route.snapshot.parent && route.parent && !route.parent.snapshot) {
678
+ route.parent.snapshot = route.snapshot.parent;
679
+ }
680
+ }
681
+ return result;
682
+ }
683
+ /**
684
+ * Creates a fake partial ActivatedRouteSnapshot for tests.
685
+ *
686
+ * If you pass params, then the created snapshot's paramMap will contain the same values.
687
+ * The same goes for queryParams and queryParamMap.
688
+ *
689
+ * @returns a partially populated, fake ActivatedRoute, depending on what you passed in
690
+ * @deprecated favor stubRoute, which creates an easier to use and more logical stub for both the route and its snapshot
691
+ */
692
+ function fakeSnapshot(options) {
693
+ return {
694
+ url: options.url,
695
+ params: options.params,
696
+ paramMap: options.params && convertToParamMap(options.params),
697
+ queryParams: options.queryParams,
698
+ queryParamMap: options.queryParams && convertToParamMap(options.queryParams),
699
+ fragment: options.fragment,
700
+ data: options.data,
701
+ outlet: options.outlet,
702
+ component: options.component,
703
+ routeConfig: options.routeConfig,
704
+ root: options.root,
705
+ parent: options.parent,
706
+ firstChild: options.firstChild,
707
+ children: options.children,
708
+ pathFromRoot: options.pathFromRoot
709
+ };
710
+ }
711
+ class ActivatedRouteSnapshotStub extends ActivatedRouteSnapshot {
712
+ constructor() {
713
+ super();
714
+ this._parent = null;
715
+ this._firstChild = null;
716
+ this._children = [];
717
+ this._pathFromRoot = [];
718
+ this._routeConfig = null;
719
+ this._root = this;
720
+ }
721
+ get parent() {
722
+ return this._parent;
723
+ }
724
+ set parent(value) {
725
+ this._parent = value;
726
+ }
727
+ get root() {
728
+ return this._root;
729
+ }
730
+ set root(value) {
731
+ this._root = value;
732
+ }
733
+ get firstChild() {
734
+ return this._firstChild;
735
+ }
736
+ set firstChild(value) {
737
+ this._firstChild = value;
738
+ }
739
+ get children() {
740
+ return this._children;
741
+ }
742
+ set children(value) {
743
+ this._children = value;
744
+ }
745
+ get pathFromRoot() {
746
+ return this._pathFromRoot;
747
+ }
748
+ set pathFromRoot(value) {
749
+ this._pathFromRoot = value;
750
+ }
751
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
752
+ // @ts-ignore
753
+ get routeConfig() {
754
+ return this._routeConfig;
755
+ }
756
+ set routeConfig(route) {
757
+ this._routeConfig = route;
758
+ }
759
+ }
760
+ /**
761
+ * A stub for ActivatedRoute. It behaves almost the same way as the actual ActivatedRoute, exposing a snapshot
762
+ * and observables for the params, query params etc., which are kept in sync.
763
+ *
764
+ * In addition, this stub allows simulating a navigation by changing the params, the query params, the fragment, etc.
765
+ * When that happens, the snapshot is modified, then the relevant observables emit the new values.
766
+ *
767
+ * There are some things that don't really work the same way as the real ActivatedRoute though:
768
+ * - the handling of the firstChild and of the children is entirely under the tester's responsibility. Setting the parent
769
+ * of a route stub does not add this route to the children of its parent, for example.
770
+ * - when changing the params, query params, fragment, etc., their associated observable emits unconditionally, instead of
771
+ * first checking if the value is actually different from before. It's thus the responsibility of the tester to not
772
+ * change the values if they're the same as before.
773
+ */
774
+ class ActivatedRouteStub extends ActivatedRoute {
775
+ /**
776
+ * Constructs a new instance, based on the given options.
777
+ * If an option is not provided (or if no option is provided at all), then the route has a default value for this option
778
+ * (empty parameters for example, null fragment, etc.)
779
+ * If no parent is passed, then this route has no parent and is thus set as the root. Otherwise, the root and the path
780
+ * from root are created based on the root and path from root of the given parent route.
781
+ */
782
+ constructor(options) {
783
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
784
+ super();
785
+ const snapshot = new ActivatedRouteSnapshotStub();
786
+ this.snapshot = snapshot;
787
+ this._firstChild = (_a = options === null || options === void 0 ? void 0 : options.firstChild) !== null && _a !== void 0 ? _a : null;
788
+ this._children = (_b = options === null || options === void 0 ? void 0 : options.children) !== null && _b !== void 0 ? _b : [];
789
+ this._parent = (_c = options === null || options === void 0 ? void 0 : options.parent) !== null && _c !== void 0 ? _c : null;
790
+ this._root = (_e = (_d = this.parent) === null || _d === void 0 ? void 0 : _d.root) !== null && _e !== void 0 ? _e : this;
791
+ this._pathFromRoot = this.parent ? [...this.parent.pathFromRoot, this] : [this];
792
+ snapshot.params = (_f = options === null || options === void 0 ? void 0 : options.params) !== null && _f !== void 0 ? _f : {};
793
+ snapshot.queryParams = (_g = options === null || options === void 0 ? void 0 : options.queryParams) !== null && _g !== void 0 ? _g : {};
794
+ snapshot.data = (_h = options === null || options === void 0 ? void 0 : options.data) !== null && _h !== void 0 ? _h : {};
795
+ snapshot.fragment = (_j = options === null || options === void 0 ? void 0 : options.fragment) !== null && _j !== void 0 ? _j : null;
796
+ snapshot.url = (_k = options === null || options === void 0 ? void 0 : options.url) !== null && _k !== void 0 ? _k : [];
797
+ snapshot.routeConfig = (_l = options === null || options === void 0 ? void 0 : options.routeConfig) !== null && _l !== void 0 ? _l : null;
798
+ snapshot.firstChild = (_o = (_m = this.firstChild) === null || _m === void 0 ? void 0 : _m.snapshot) !== null && _o !== void 0 ? _o : null;
799
+ snapshot.children = (_q = (_p = this.children) === null || _p === void 0 ? void 0 : _p.map(route => route.snapshot)) !== null && _q !== void 0 ? _q : [];
800
+ snapshot.parent = (_s = (_r = this.parent) === null || _r === void 0 ? void 0 : _r.snapshot) !== null && _s !== void 0 ? _s : null;
801
+ snapshot.root = this.root.snapshot;
802
+ snapshot.pathFromRoot = this.pathFromRoot.map(route => route.snapshot);
803
+ this.paramsSubject = new BehaviorSubject(this.snapshot.params);
804
+ this.queryParamsSubject = new BehaviorSubject(this.snapshot.queryParams);
805
+ this.dataSubject = new BehaviorSubject(this.snapshot.data);
806
+ this.fragmentSubject = new BehaviorSubject(this.snapshot.fragment);
807
+ this.urlSubject = new BehaviorSubject(this.snapshot.url);
808
+ this.params = this.paramsSubject.asObservable();
809
+ this.queryParams = this.queryParamsSubject.asObservable();
810
+ this.data = this.dataSubject.asObservable();
811
+ this.fragment = this.fragmentSubject.asObservable();
812
+ this.url = this.urlSubject.asObservable();
813
+ }
814
+ get root() {
815
+ return this._root;
816
+ }
817
+ get parent() {
818
+ return this._parent;
819
+ }
820
+ get pathFromRoot() {
821
+ return this._pathFromRoot;
822
+ }
823
+ get firstChild() {
824
+ return this._firstChild;
825
+ }
826
+ get children() {
827
+ return this._children;
828
+ }
829
+ get routeConfig() {
830
+ return this.snapshot.routeConfig;
831
+ }
832
+ /**
833
+ * Triggers a navigation with the given new parameters. All the other parts (query params etc.) stay as the are.
834
+ * This is a shortcut to `triggerNavigation` that can be used to only change the parameters.
835
+ */
836
+ setParams(params) {
837
+ this.triggerNavigation({ params });
838
+ }
839
+ /**
840
+ * Triggers a navigation with the given new parameter. The other parameters, as well as all the other parts (query params etc.)
841
+ * stay as the are.
842
+ * This is a shortcut to `triggerNavigation` that can be used to only change one parameter.
843
+ */
844
+ setParam(name, value) {
845
+ this.setParams(Object.assign(Object.assign({}, this.snapshot.params), { [name]: value }));
846
+ }
847
+ /**
848
+ * Triggers a navigation with the given new query parameters. All the other parts (params etc.) stay as the are.
849
+ * This is a shortcut to `triggerNavigation` that can be used to only change the query parameters.
850
+ */
851
+ setQueryParams(queryParams) {
852
+ this.triggerNavigation({ queryParams });
853
+ }
854
+ /**
855
+ * Triggers a navigation with the given new parameter. The other query parameters, as well as all the other parts (params etc.)
856
+ * stay as the are.
857
+ * This is a shortcut to `triggerNavigation` that can be used to only change one query parameter.
858
+ */
859
+ setQueryParam(name, value) {
860
+ this.setQueryParams(Object.assign(Object.assign({}, this.snapshot.queryParams), { [name]: value }));
861
+ }
862
+ /**
863
+ * Triggers a navigation with the given new data. The other parameters, as well as all the other parts (params etc.)
864
+ * stay as the are.
865
+ * This is a shortcut to `triggerNavigation` that can be used to only change the data.
866
+ */
867
+ setData(data) {
868
+ this.triggerNavigation({ data });
869
+ }
870
+ /**
871
+ * Triggers a navigation with the given new data item. The other data, as well as all the other parts (params etc.)
872
+ * stay as the are.
873
+ * This is a shortcut to `triggerNavigation` that can be used to only change one data item.
874
+ */
875
+ setDataItem(name, value) {
876
+ this.setData(Object.assign(Object.assign({}, this.snapshot.data), { [name]: value }));
877
+ }
878
+ /**
879
+ * Triggers a navigation with the given new fragment. The other parts (params etc.) stay as the are.
880
+ * This is a shortcut to `triggerNavigation` that can be used to only change the fragment.
881
+ */
882
+ setFragment(fragment) {
883
+ this.triggerNavigation({ fragment });
884
+ }
885
+ /**
886
+ * Triggers a navigation with the given new url. The other parts (params etc.) stay as the are.
887
+ * This is a shortcut to `triggerNavigation` that can be used to only change the url.
888
+ */
889
+ setUrl(url) {
890
+ this.triggerNavigation({ url });
891
+ }
892
+ /**
893
+ * Triggers a navigation based on the given options. If an option is undefined or null, it's ignored. Except for fragment, which is only
894
+ * ignored if it's undefined, because null is a valid value for a fragment.
895
+ *
896
+ * The non-ignored values are used to change the snapshot of the route. Once the snapshot has been modified,
897
+ * the observables corresponding to the updated parts emit the new value.
898
+ *
899
+ * So, setting params and query params will make the params and queryParams observables emit, but not the fragment, data and
900
+ * url observables for example. This is consistent to how the router behaves.
901
+ */
902
+ triggerNavigation(options) {
903
+ // set the snapshot first
904
+ if (options.params) {
905
+ this.snapshot.params = options.params;
906
+ }
907
+ if (options.queryParams) {
908
+ this.snapshot.queryParams = options.queryParams;
909
+ }
910
+ if (options.fragment !== undefined) {
911
+ this.snapshot.fragment = options.fragment;
912
+ }
913
+ if (options.data) {
914
+ this.snapshot.data = options.data;
915
+ }
916
+ if (options.url) {
917
+ this.snapshot.url = options.url;
918
+ }
919
+ // then emit everything that has changed
920
+ if (options.params) {
921
+ this.paramsSubject.next(this.snapshot.params);
922
+ }
923
+ if (options.queryParams) {
924
+ this.queryParamsSubject.next(this.snapshot.queryParams);
925
+ }
926
+ if (options.fragment !== undefined) {
927
+ this.fragmentSubject.next(this.snapshot.fragment);
928
+ }
929
+ if (options.data) {
930
+ this.dataSubject.next(this.snapshot.data);
931
+ }
932
+ if (options.url) {
933
+ this.urlSubject.next(this.snapshot.url);
934
+ }
935
+ }
936
+ toString() {
937
+ return 'ActivatedRouteStub';
938
+ }
939
+ }
940
+ /**
941
+ * Creates a new ActivatedRouteStub, by calling its constructor.
942
+ */
943
+ function stubRoute(options) {
944
+ return new ActivatedRouteStub(options);
945
+ }
946
+
947
+ const speculoosMatchers = {
948
+ /**
949
+ * Checks that the receiver is a TestElement wrapping a DOM element and as the given CSS class
950
+ */
951
+ toHaveClass: () => {
952
+ const assert = (isNegative, el, expected) => {
953
+ if (!el) {
954
+ return { pass: false, message: `Expected to check class '${expected}' on element, but element was falsy` };
955
+ }
956
+ if (!(el instanceof TestElement)) {
957
+ return { pass: false, message: `Expected to check class '${expected}' on element, but element was not a TestElement` };
958
+ }
959
+ const actual = el.classes;
960
+ const pass = actual.indexOf(expected) !== -1;
961
+ const message = `Expected element to ${isNegative ? 'not ' : ''}have class '${expected}', ` +
962
+ `but had ${actual.length ? "'" + actual.join(', ') + "'" : 'none'}`;
963
+ return { pass: isNegative ? !pass : pass, message };
964
+ };
965
+ return {
966
+ compare: (el, expected) => {
967
+ return assert(false, el, expected);
968
+ },
969
+ negativeCompare: (el, expected) => {
970
+ return assert(true, el, expected);
971
+ }
972
+ };
973
+ },
974
+ /**
975
+ * Checks that the receiver is a TestInput or a TestTextArea and has the given value
976
+ */
977
+ toHaveValue: () => {
978
+ const assert = (isNegative, el, expected) => {
979
+ if (!el) {
980
+ return { pass: false, message: `Expected to check value '${expected}' on element, but element was falsy` };
981
+ }
982
+ if (!(el instanceof TestInput) && !(el instanceof TestTextArea)) {
983
+ return {
984
+ pass: false,
985
+ message: `Expected to check value '${expected}' on element, but element was neither a TestInput nor a TestTextArea`
986
+ };
987
+ }
988
+ const actual = el.value;
989
+ const pass = actual === expected;
990
+ const message = `Expected element to ${isNegative ? 'not ' : ''}have value '${expected}', but had value '${actual}'`;
991
+ return { pass: isNegative ? !pass : pass, message };
992
+ };
993
+ return {
994
+ compare: (el, expected) => {
995
+ return assert(false, el, expected);
996
+ },
997
+ negativeCompare: (el, expected) => {
998
+ return assert(true, el, expected);
999
+ }
1000
+ };
1001
+ },
1002
+ /**
1003
+ * Checks that the receiver is a TestElement wrapping a DOM element and has the exact given textContent
1004
+ */
1005
+ toHaveText: () => {
1006
+ const assert = (isNegative, el, expected) => {
1007
+ if (!el) {
1008
+ return { pass: false, message: `Expected to check text '${expected}' on element, but element was falsy` };
1009
+ }
1010
+ if (!(el instanceof TestElement)) {
1011
+ return { pass: false, message: `Expected to check text '${expected}' on element, but element was not a TestElement` };
1012
+ }
1013
+ const actual = el.textContent;
1014
+ const pass = actual === expected;
1015
+ const message = `Expected element to ${isNegative ? 'not ' : ''}have text '${expected}', but had '${actual}'`;
1016
+ return { pass: isNegative ? !pass : pass, message };
1017
+ };
1018
+ return {
1019
+ compare: (el, expected) => {
1020
+ return assert(false, el, expected);
1021
+ },
1022
+ negativeCompare: (el, expected) => {
1023
+ return assert(true, el, expected);
1024
+ }
1025
+ };
1026
+ },
1027
+ /**
1028
+ * Checks that the receiver is a TestElement wrapping a DOM element and has the given textContent, after both have been trimmed.
1029
+ * So, An element such as
1030
+ * ```
1031
+ * <h1>
1032
+ * Some title
1033
+ * </h1>
1034
+ * ```
1035
+ * will pass the test
1036
+ * ```
1037
+ * expect(tester.title).toHaveTrimmedText('Some title')
1038
+ * ```
1039
+ */
1040
+ toHaveTrimmedText: () => {
1041
+ const assert = (isNegative, el, expected) => {
1042
+ var _a;
1043
+ const trimmedExpected = expected.trim();
1044
+ if (!el) {
1045
+ return { pass: false, message: `Expected to check trimmed text '${trimmedExpected}' on element, but element was falsy` };
1046
+ }
1047
+ if (!(el instanceof TestElement)) {
1048
+ return {
1049
+ pass: false,
1050
+ message: `Expected to check trimmed text '${trimmedExpected}' on element, but element was not a TestElement`
1051
+ };
1052
+ }
1053
+ const actual = (_a = el.textContent) === null || _a === void 0 ? void 0 : _a.trim();
1054
+ const pass = actual === trimmedExpected;
1055
+ const message = `Expected element to ${isNegative ? 'not ' : ''}have trimmed text '${trimmedExpected}', but had '${actual}'`;
1056
+ return { pass: isNegative ? !pass : pass, message };
1057
+ };
1058
+ return {
1059
+ compare: (el, expected) => {
1060
+ return assert(false, el, expected);
1061
+ },
1062
+ negativeCompare: (el, expected) => {
1063
+ return assert(true, el, expected);
1064
+ }
1065
+ };
1066
+ },
1067
+ /**
1068
+ * Checks that the receiver is a TestElement wrapping a DOM element and contains the given textContent
1069
+ */
1070
+ toContainText: () => {
1071
+ const assert = (isNegative, el, expected) => {
1072
+ if (!el) {
1073
+ return { pass: false, message: `Expected to check text '${expected}' on element, but element was falsy` };
1074
+ }
1075
+ if (!(el instanceof TestElement)) {
1076
+ return { pass: false, message: `Expected to check text '${expected}' on element, but element was not a TestElement` };
1077
+ }
1078
+ const actual = el.textContent;
1079
+ if (!actual) {
1080
+ return {
1081
+ pass: isNegative,
1082
+ message: `Expected element to ${isNegative ? 'not ' : ''}contain text '${expected}', but had no text`
1083
+ };
1084
+ }
1085
+ const pass = actual.indexOf(expected) !== -1;
1086
+ const message = `Expected element to ${isNegative ? 'not ' : ''}contain text '${expected}', but had text '${actual}'`;
1087
+ return { pass: isNegative ? !pass : pass, message };
1088
+ };
1089
+ return {
1090
+ compare: (el, expected) => {
1091
+ return assert(false, el, expected);
1092
+ },
1093
+ negativeCompare: (el, expected) => {
1094
+ return assert(true, el, expected);
1095
+ }
1096
+ };
1097
+ },
1098
+ /**
1099
+ * Checks that the receiver is a TestElement wrapping a DOM element and contains the given textContent
1100
+ */
1101
+ toBeChecked: () => {
1102
+ const assert = (isNegative, el) => {
1103
+ if (!el) {
1104
+ return { pass: false, message: `Expected to check if element was checked, but element was falsy` };
1105
+ }
1106
+ if (!(el instanceof TestInput)) {
1107
+ return { pass: false, message: `Expected to check if element was checked, but element was not a TestInput` };
1108
+ }
1109
+ const pass = el.checked;
1110
+ const message = `Expected element to be ${isNegative ? 'not ' : ''}checked, but was${!isNegative ? ' not' : ''}`;
1111
+ return { pass: isNegative ? !pass : pass, message };
1112
+ };
1113
+ return {
1114
+ compare: (el) => {
1115
+ return assert(false, el);
1116
+ },
1117
+ negativeCompare: (el) => {
1118
+ return assert(true, el);
1119
+ }
1120
+ };
1121
+ },
1122
+ /**
1123
+ * Checks that the receiver is a TestSelect wrapping a DOM element and has the given selected index
1124
+ */
1125
+ toHaveSelectedIndex: () => {
1126
+ const assert = (isNegative, el, expected) => {
1127
+ if (!el) {
1128
+ return { pass: false, message: `Expected to check selected index ${expected} on element, but element was falsy` };
1129
+ }
1130
+ if (!(el instanceof TestSelect)) {
1131
+ return { pass: false, message: `Expected to check selected index ${expected} on element, but element was not a TestSelect` };
1132
+ }
1133
+ const actual = el.selectedIndex;
1134
+ const pass = actual === expected;
1135
+ const message = `Expected element to ${isNegative ? 'not ' : ''}have selected index ${expected}, but had ${actual}`;
1136
+ return { pass: isNegative ? !pass : pass, message };
1137
+ };
1138
+ return {
1139
+ compare: (el, expected) => {
1140
+ return assert(false, el, expected);
1141
+ },
1142
+ negativeCompare: (el, expected) => {
1143
+ return assert(true, el, expected);
1144
+ }
1145
+ };
1146
+ },
1147
+ /**
1148
+ * Checks that the receiver is a TestSelect wrapping a DOM element with the selected option's value equal to the given value
1149
+ */
1150
+ toHaveSelectedValue: () => {
1151
+ const assert = (isNegative, el, expected) => {
1152
+ if (!el) {
1153
+ return { pass: false, message: `Expected to check selected value '${expected}' on element, but element was falsy` };
1154
+ }
1155
+ if (!(el instanceof TestSelect)) {
1156
+ return { pass: false, message: `Expected to check selected value '${expected}' on element, but element was not a TestSelect` };
1157
+ }
1158
+ const actual = el.selectedValue;
1159
+ const pass = actual === expected;
1160
+ const message = `Expected element to ${isNegative ? 'not ' : ''}have selected value '${expected}', but had '${actual}'`;
1161
+ return { pass: isNegative ? !pass : pass, message };
1162
+ };
1163
+ return {
1164
+ compare: (el, expected) => {
1165
+ return assert(false, el, expected);
1166
+ },
1167
+ negativeCompare: (el, expected) => {
1168
+ return assert(true, el, expected);
1169
+ }
1170
+ };
1171
+ },
1172
+ /**
1173
+ * Checks that the receiver is a TestSelect wrapping a DOM element with the selected option's label equal to the given label
1174
+ */
1175
+ toHaveSelectedLabel: () => {
1176
+ const assert = (isNegative, el, expected) => {
1177
+ if (!el) {
1178
+ return { pass: false, message: `Expected to check selected label '${expected}' on element, but element was falsy` };
1179
+ }
1180
+ if (!(el instanceof TestSelect)) {
1181
+ return { pass: false, message: `Expected to check selected label '${expected}' on element, but element was not a TestSelect` };
1182
+ }
1183
+ const actual = el.selectedLabel;
1184
+ const pass = actual === expected;
1185
+ const message = `Expected element to ${isNegative ? 'not ' : ''}have selected label '${expected}', but had '${actual}'`;
1186
+ return { pass: isNegative ? !pass : pass, message };
1187
+ };
1188
+ return {
1189
+ compare: (el, expected) => {
1190
+ return assert(false, el, expected);
1191
+ },
1192
+ negativeCompare: (el, expected) => {
1193
+ return assert(true, el, expected);
1194
+ }
1195
+ };
1196
+ },
1197
+ /**
1198
+ * Checks that the receiver is a TestHtmlElement which is visible
1199
+ */
1200
+ toBeVisible: () => {
1201
+ const assert = (isNegative, el) => {
1202
+ const expectedState = `${isNegative ? 'in' : ''}visible`;
1203
+ const inverseState = `${isNegative ? '' : 'in'}visible`;
1204
+ if (!el) {
1205
+ return { pass: false, message: `Expected to check if element was ${expectedState}, but element was falsy` };
1206
+ }
1207
+ if (!(el instanceof TestHtmlElement)) {
1208
+ return { pass: false, message: `Expected to check if element was ${expectedState}, but element was not a TestHtmlElement` };
1209
+ }
1210
+ const pass = el.visible;
1211
+ const message = `Expected element to be ${expectedState}, but was ${inverseState}`;
1212
+ return { pass: isNegative ? !pass : pass, message };
1213
+ };
1214
+ return {
1215
+ compare: (el) => {
1216
+ return assert(false, el);
1217
+ },
1218
+ negativeCompare: (el) => {
1219
+ return assert(true, el);
1220
+ }
1221
+ };
1222
+ }
1223
+ };
1224
+
1225
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1226
+ function collectMethodNames(proto) {
1227
+ if (!proto || proto === Object.prototype) {
1228
+ return [];
1229
+ }
1230
+ const methodNames = [];
1231
+ for (const key of Object.getOwnPropertyNames(proto)) {
1232
+ const descriptor = Object.getOwnPropertyDescriptor(proto, key);
1233
+ if (descriptor && typeof descriptor.value === 'function' && key !== 'constructor') {
1234
+ methodNames.push(key);
1235
+ }
1236
+ }
1237
+ return [...methodNames, ...collectMethodNames(Object.getPrototypeOf(proto))];
1238
+ }
1239
+ /**
1240
+ * Creates a spy object for a class where all the methods of the class (and of its superclasses) are spies.
1241
+ * I.e., for a class `UserService` with methods `get()`, `create()`, `update()` and `delete()`, calling
1242
+ * `createMock(UserService)` is equivalent to calling
1243
+ * `jasmine.createSpyObj<UserService>('UserService', ['get', 'create', 'update', 'delete'])`.
1244
+ * @param type the type to mock (usually a service class)
1245
+ */
1246
+ function createMock(type) {
1247
+ return jasmine.createSpyObj(type.name, collectMethodNames(type.prototype));
1248
+ }
1249
+
1250
+ /* eslint-disable */
1251
+
1252
+ /**
1253
+ * Generated bundle index. Do not edit.
1254
+ */
1255
+
1256
+ export { ActivatedRouteStub, ComponentTester, TestButton, TestElement, TestHtmlElement, TestInput, TestSelect, TestTextArea, createMock, fakeRoute, fakeSnapshot, speculoosMatchers, stubRoute };
1257
+ //# sourceMappingURL=ngx-speculoos.mjs.map