UNPKG

26.7 kBJavaScriptView Raw
1import { __awaiter } from 'tslib';
2import { HarnessPredicate, ComponentHarness, parallel, TestKey } from '@angular/cdk/testing';
3import { MatFormFieldControlHarness } from '@angular/material/form-field/testing/control';
4import { coerceBooleanProperty } from '@angular/cdk/coercion';
5
6/** Sets up the filter predicates for a datepicker input harness. */
7function getInputPredicate(type, options) {
8 return new HarnessPredicate(type, options)
9 .addOption('value', options.value, (harness, value) => {
10 return HarnessPredicate.stringMatches(harness.getValue(), value);
11 })
12 .addOption('placeholder', options.placeholder, (harness, placeholder) => {
13 return HarnessPredicate.stringMatches(harness.getPlaceholder(), placeholder);
14 });
15}
16/** Base class for datepicker input harnesses. */
17class MatDatepickerInputHarnessBase extends MatFormFieldControlHarness {
18 /** Whether the input is disabled. */
19 isDisabled() {
20 return __awaiter(this, void 0, void 0, function* () {
21 return (yield this.host()).getProperty('disabled');
22 });
23 }
24 /** Whether the input is required. */
25 isRequired() {
26 return __awaiter(this, void 0, void 0, function* () {
27 return (yield this.host()).getProperty('required');
28 });
29 }
30 /** Gets the value of the input. */
31 getValue() {
32 return __awaiter(this, void 0, void 0, function* () {
33 // The "value" property of the native input is always defined.
34 return yield (yield this.host()).getProperty('value');
35 });
36 }
37 /**
38 * Sets the value of the input. The value will be set by simulating
39 * keypresses that correspond to the given value.
40 */
41 setValue(newValue) {
42 return __awaiter(this, void 0, void 0, function* () {
43 const inputEl = yield this.host();
44 yield inputEl.clear();
45 // We don't want to send keys for the value if the value is an empty
46 // string in order to clear the value. Sending keys with an empty string
47 // still results in unnecessary focus events.
48 if (newValue) {
49 yield inputEl.sendKeys(newValue);
50 }
51 yield inputEl.dispatchEvent('change');
52 });
53 }
54 /** Gets the placeholder of the input. */
55 getPlaceholder() {
56 return __awaiter(this, void 0, void 0, function* () {
57 return yield (yield this.host()).getProperty('placeholder');
58 });
59 }
60 /**
61 * Focuses the input and returns a promise that indicates when the
62 * action is complete.
63 */
64 focus() {
65 return __awaiter(this, void 0, void 0, function* () {
66 return (yield this.host()).focus();
67 });
68 }
69 /**
70 * Blurs the input and returns a promise that indicates when the
71 * action is complete.
72 */
73 blur() {
74 return __awaiter(this, void 0, void 0, function* () {
75 return (yield this.host()).blur();
76 });
77 }
78 /** Whether the input is focused. */
79 isFocused() {
80 return __awaiter(this, void 0, void 0, function* () {
81 return (yield this.host()).isFocused();
82 });
83 }
84 /** Gets the formatted minimum date for the input's value. */
85 getMin() {
86 return __awaiter(this, void 0, void 0, function* () {
87 return (yield this.host()).getAttribute('min');
88 });
89 }
90 /** Gets the formatted maximum date for the input's value. */
91 getMax() {
92 return __awaiter(this, void 0, void 0, function* () {
93 return (yield this.host()).getAttribute('max');
94 });
95 }
96}
97
98/** Harness for interacting with a standard Material calendar cell in tests. */
99class MatCalendarCellHarness extends ComponentHarness {
100 constructor() {
101 super(...arguments);
102 /** Reference to the inner content element inside the cell. */
103 this._content = this.locatorFor('.mat-calendar-body-cell-content');
104 }
105 /**
106 * Gets a `HarnessPredicate` that can be used to search for a `MatCalendarCellHarness`
107 * that meets certain criteria.
108 * @param options Options for filtering which cell instances are considered a match.
109 * @return a `HarnessPredicate` configured with the given options.
110 */
111 static with(options = {}) {
112 return new HarnessPredicate(MatCalendarCellHarness, options)
113 .addOption('text', options.text, (harness, text) => {
114 return HarnessPredicate.stringMatches(harness.getText(), text);
115 })
116 .addOption('selected', options.selected, (harness, selected) => __awaiter(this, void 0, void 0, function* () {
117 return (yield harness.isSelected()) === selected;
118 }))
119 .addOption('active', options.active, (harness, active) => __awaiter(this, void 0, void 0, function* () {
120 return (yield harness.isActive()) === active;
121 }))
122 .addOption('disabled', options.disabled, (harness, disabled) => __awaiter(this, void 0, void 0, function* () {
123 return (yield harness.isDisabled()) === disabled;
124 }))
125 .addOption('today', options.today, (harness, today) => __awaiter(this, void 0, void 0, function* () {
126 return (yield harness.isToday()) === today;
127 }))
128 .addOption('inRange', options.inRange, (harness, inRange) => __awaiter(this, void 0, void 0, function* () {
129 return (yield harness.isInRange()) === inRange;
130 }))
131 .addOption('inComparisonRange', options.inComparisonRange, (harness, inComparisonRange) => __awaiter(this, void 0, void 0, function* () {
132 return (yield harness.isInComparisonRange()) === inComparisonRange;
133 }))
134 .addOption('inPreviewRange', options.inPreviewRange, (harness, inPreviewRange) => __awaiter(this, void 0, void 0, function* () {
135 return (yield harness.isInPreviewRange()) === inPreviewRange;
136 }));
137 }
138 /** Gets the text of the calendar cell. */
139 getText() {
140 return __awaiter(this, void 0, void 0, function* () {
141 return (yield this._content()).text();
142 });
143 }
144 /** Gets the aria-label of the calendar cell. */
145 getAriaLabel() {
146 return __awaiter(this, void 0, void 0, function* () {
147 // We're guaranteed for the `aria-label` to be defined
148 // since this is a private element that we control.
149 return (yield this.host()).getAttribute('aria-label');
150 });
151 }
152 /** Whether the cell is selected. */
153 isSelected() {
154 return __awaiter(this, void 0, void 0, function* () {
155 const host = yield this.host();
156 return (yield host.getAttribute('aria-pressed')) === 'true';
157 });
158 }
159 /** Whether the cell is disabled. */
160 isDisabled() {
161 return __awaiter(this, void 0, void 0, function* () {
162 return this._hasState('disabled');
163 });
164 }
165 /** Whether the cell is currently activated using keyboard navigation. */
166 isActive() {
167 return __awaiter(this, void 0, void 0, function* () {
168 return this._hasState('active');
169 });
170 }
171 /** Whether the cell represents today's date. */
172 isToday() {
173 return __awaiter(this, void 0, void 0, function* () {
174 return (yield this._content()).hasClass('mat-calendar-body-today');
175 });
176 }
177 /** Selects the calendar cell. Won't do anything if the cell is disabled. */
178 select() {
179 return __awaiter(this, void 0, void 0, function* () {
180 return (yield this.host()).click();
181 });
182 }
183 /** Hovers over the calendar cell. */
184 hover() {
185 return __awaiter(this, void 0, void 0, function* () {
186 return (yield this.host()).hover();
187 });
188 }
189 /** Moves the mouse away from the calendar cell. */
190 mouseAway() {
191 return __awaiter(this, void 0, void 0, function* () {
192 return (yield this.host()).mouseAway();
193 });
194 }
195 /** Focuses the calendar cell. */
196 focus() {
197 return __awaiter(this, void 0, void 0, function* () {
198 return (yield this.host()).focus();
199 });
200 }
201 /** Removes focus from the calendar cell. */
202 blur() {
203 return __awaiter(this, void 0, void 0, function* () {
204 return (yield this.host()).blur();
205 });
206 }
207 /** Whether the cell is the start of the main range. */
208 isRangeStart() {
209 return __awaiter(this, void 0, void 0, function* () {
210 return this._hasState('range-start');
211 });
212 }
213 /** Whether the cell is the end of the main range. */
214 isRangeEnd() {
215 return __awaiter(this, void 0, void 0, function* () {
216 return this._hasState('range-end');
217 });
218 }
219 /** Whether the cell is part of the main range. */
220 isInRange() {
221 return __awaiter(this, void 0, void 0, function* () {
222 return this._hasState('in-range');
223 });
224 }
225 /** Whether the cell is the start of the comparison range. */
226 isComparisonRangeStart() {
227 return __awaiter(this, void 0, void 0, function* () {
228 return this._hasState('comparison-start');
229 });
230 }
231 /** Whether the cell is the end of the comparison range. */
232 isComparisonRangeEnd() {
233 return __awaiter(this, void 0, void 0, function* () {
234 return this._hasState('comparison-end');
235 });
236 }
237 /** Whether the cell is inside of the comparison range. */
238 isInComparisonRange() {
239 return __awaiter(this, void 0, void 0, function* () {
240 return this._hasState('in-comparison-range');
241 });
242 }
243 /** Whether the cell is the start of the preview range. */
244 isPreviewRangeStart() {
245 return __awaiter(this, void 0, void 0, function* () {
246 return this._hasState('preview-start');
247 });
248 }
249 /** Whether the cell is the end of the preview range. */
250 isPreviewRangeEnd() {
251 return __awaiter(this, void 0, void 0, function* () {
252 return this._hasState('preview-end');
253 });
254 }
255 /** Whether the cell is inside of the preview range. */
256 isInPreviewRange() {
257 return __awaiter(this, void 0, void 0, function* () {
258 return this._hasState('in-preview');
259 });
260 }
261 /** Returns whether the cell has a particular CSS class-based state. */
262 _hasState(name) {
263 return __awaiter(this, void 0, void 0, function* () {
264 return (yield this.host()).hasClass(`mat-calendar-body-${name}`);
265 });
266 }
267}
268MatCalendarCellHarness.hostSelector = '.mat-calendar-body-cell';
269
270/** Harness for interacting with a standard Material calendar in tests. */
271class MatCalendarHarness extends ComponentHarness {
272 constructor() {
273 super(...arguments);
274 /** Queries for the calendar's period toggle button. */
275 this._periodButton = this.locatorFor('.mat-calendar-period-button');
276 }
277 /**
278 * Gets a `HarnessPredicate` that can be used to search for a `MatCalendarHarness`
279 * that meets certain criteria.
280 * @param options Options for filtering which calendar instances are considered a match.
281 * @return a `HarnessPredicate` configured with the given options.
282 */
283 static with(options = {}) {
284 return new HarnessPredicate(MatCalendarHarness, options);
285 }
286 /**
287 * Gets a list of cells inside the calendar.
288 * @param filter Optionally filters which cells are included.
289 */
290 getCells(filter = {}) {
291 return __awaiter(this, void 0, void 0, function* () {
292 return this.locatorForAll(MatCalendarCellHarness.with(filter))();
293 });
294 }
295 /** Gets the current view that is being shown inside the calendar. */
296 getCurrentView() {
297 return __awaiter(this, void 0, void 0, function* () {
298 if (yield this.locatorForOptional('mat-multi-year-view')()) {
299 return 2 /* CalendarView.MULTI_YEAR */;
300 }
301 if (yield this.locatorForOptional('mat-year-view')()) {
302 return 1 /* CalendarView.YEAR */;
303 }
304 return 0 /* CalendarView.MONTH */;
305 });
306 }
307 /** Gets the label of the current calendar view. */
308 getCurrentViewLabel() {
309 return __awaiter(this, void 0, void 0, function* () {
310 return (yield this._periodButton()).text();
311 });
312 }
313 /** Changes the calendar view by clicking on the view toggle button. */
314 changeView() {
315 return __awaiter(this, void 0, void 0, function* () {
316 return (yield this._periodButton()).click();
317 });
318 }
319 /** Goes to the next page of the current view (e.g. next month when inside the month view). */
320 next() {
321 return __awaiter(this, void 0, void 0, function* () {
322 return (yield this.locatorFor('.mat-calendar-next-button')()).click();
323 });
324 }
325 /**
326 * Goes to the previous page of the current view
327 * (e.g. previous month when inside the month view).
328 */
329 previous() {
330 return __awaiter(this, void 0, void 0, function* () {
331 return (yield this.locatorFor('.mat-calendar-previous-button')()).click();
332 });
333 }
334 /**
335 * Selects a cell in the current calendar view.
336 * @param filter An optional filter to apply to the cells. The first cell matching the filter
337 * will be selected.
338 */
339 selectCell(filter = {}) {
340 return __awaiter(this, void 0, void 0, function* () {
341 const cells = yield this.getCells(filter);
342 if (!cells.length) {
343 throw Error(`Cannot find calendar cell matching filter ${JSON.stringify(filter)}`);
344 }
345 yield cells[0].select();
346 });
347 }
348}
349MatCalendarHarness.hostSelector = '.mat-calendar';
350
351/** Base class for harnesses that can trigger a calendar. */
352class DatepickerTriggerHarnessBase extends ComponentHarness {
353 /** Opens the calendar if the trigger is enabled and it has a calendar. */
354 openCalendar() {
355 return __awaiter(this, void 0, void 0, function* () {
356 const [isDisabled, hasCalendar] = yield parallel(() => [this.isDisabled(), this.hasCalendar()]);
357 if (!isDisabled && hasCalendar) {
358 return this._openCalendar();
359 }
360 });
361 }
362 /** Closes the calendar if it is open. */
363 closeCalendar() {
364 return __awaiter(this, void 0, void 0, function* () {
365 if (yield this.isCalendarOpen()) {
366 yield closeCalendar(getCalendarId(this.host()), this.documentRootLocatorFactory());
367 // This is necessary so that we wait for the closing animation to finish in touch UI mode.
368 yield this.forceStabilize();
369 }
370 });
371 }
372 /** Gets whether there is a calendar associated with the trigger. */
373 hasCalendar() {
374 return __awaiter(this, void 0, void 0, function* () {
375 return (yield getCalendarId(this.host())) != null;
376 });
377 }
378 /**
379 * Gets the `MatCalendarHarness` that is associated with the trigger.
380 * @param filter Optionally filters which calendar is included.
381 */
382 getCalendar(filter = {}) {
383 return __awaiter(this, void 0, void 0, function* () {
384 return getCalendar(filter, this.host(), this.documentRootLocatorFactory());
385 });
386 }
387}
388/** Gets the ID of the calendar that a particular test element can trigger. */
389function getCalendarId(host) {
390 return __awaiter(this, void 0, void 0, function* () {
391 return (yield host).getAttribute('data-mat-calendar');
392 });
393}
394/** Closes the calendar with a specific ID. */
395function closeCalendar(calendarId, documentLocator) {
396 return __awaiter(this, void 0, void 0, function* () {
397 // We close the calendar by clicking on the backdrop, even though all datepicker variants
398 // have the ability to close by pressing escape. The backdrop is preferrable, because the
399 // escape key has multiple functions inside a range picker (either cancel the current range
400 // or close the calendar). Since we don't have access to set the ID on the backdrop in all
401 // cases, we set a unique class instead which is the same as the calendar's ID and suffixed
402 // with `-backdrop`.
403 const backdropSelector = `.${yield calendarId}-backdrop`;
404 return (yield documentLocator.locatorFor(backdropSelector)()).click();
405 });
406}
407/** Gets the test harness for a calendar associated with a particular host. */
408function getCalendar(filter, host, documentLocator) {
409 return __awaiter(this, void 0, void 0, function* () {
410 const calendarId = yield getCalendarId(host);
411 if (!calendarId) {
412 throw Error(`Element is not associated with a calendar`);
413 }
414 return documentLocator.locatorFor(MatCalendarHarness.with(Object.assign(Object.assign({}, filter), { selector: `#${calendarId}` })))();
415 });
416}
417
418/** Harness for interacting with a standard Material datepicker inputs in tests. */
419class MatDatepickerInputHarness extends MatDatepickerInputHarnessBase {
420 /**
421 * Gets a `HarnessPredicate` that can be used to search for a `MatDatepickerInputHarness`
422 * that meets certain criteria.
423 * @param options Options for filtering which input instances are considered a match.
424 * @return a `HarnessPredicate` configured with the given options.
425 */
426 static with(options = {}) {
427 return getInputPredicate(MatDatepickerInputHarness, options);
428 }
429 /** Gets whether the calendar associated with the input is open. */
430 isCalendarOpen() {
431 return __awaiter(this, void 0, void 0, function* () {
432 // `aria-owns` is set only if there's an open datepicker so we can use it as an indicator.
433 const host = yield this.host();
434 return (yield host.getAttribute('aria-owns')) != null;
435 });
436 }
437 /** Opens the calendar associated with the input. */
438 openCalendar() {
439 return __awaiter(this, void 0, void 0, function* () {
440 const [isDisabled, hasCalendar] = yield parallel(() => [this.isDisabled(), this.hasCalendar()]);
441 if (!isDisabled && hasCalendar) {
442 // Alt + down arrow is the combination for opening the calendar with the keyboard.
443 const host = yield this.host();
444 return host.sendKeys({ alt: true }, TestKey.DOWN_ARROW);
445 }
446 });
447 }
448 /** Closes the calendar associated with the input. */
449 closeCalendar() {
450 return __awaiter(this, void 0, void 0, function* () {
451 if (yield this.isCalendarOpen()) {
452 yield closeCalendar(getCalendarId(this.host()), this.documentRootLocatorFactory());
453 // This is necessary so that we wait for the closing animation to finish in touch UI mode.
454 yield this.forceStabilize();
455 }
456 });
457 }
458 /** Whether a calendar is associated with the input. */
459 hasCalendar() {
460 return __awaiter(this, void 0, void 0, function* () {
461 return (yield getCalendarId(this.host())) != null;
462 });
463 }
464 /**
465 * Gets the `MatCalendarHarness` that is associated with the trigger.
466 * @param filter Optionally filters which calendar is included.
467 */
468 getCalendar(filter = {}) {
469 return __awaiter(this, void 0, void 0, function* () {
470 return getCalendar(filter, this.host(), this.documentRootLocatorFactory());
471 });
472 }
473}
474MatDatepickerInputHarness.hostSelector = '.mat-datepicker-input';
475
476/** Harness for interacting with a standard Material datepicker toggle in tests. */
477class MatDatepickerToggleHarness extends DatepickerTriggerHarnessBase {
478 constructor() {
479 super(...arguments);
480 /** The clickable button inside the toggle. */
481 this._button = this.locatorFor('button');
482 }
483 /**
484 * Gets a `HarnessPredicate` that can be used to search for a `MatDatepickerToggleHarness` that
485 * meets certain criteria.
486 * @param options Options for filtering which datepicker toggle instances are considered a match.
487 * @return a `HarnessPredicate` configured with the given options.
488 */
489 static with(options = {}) {
490 return new HarnessPredicate(MatDatepickerToggleHarness, options);
491 }
492 /** Gets whether the calendar associated with the toggle is open. */
493 isCalendarOpen() {
494 return __awaiter(this, void 0, void 0, function* () {
495 return (yield this.host()).hasClass('mat-datepicker-toggle-active');
496 });
497 }
498 /** Whether the toggle is disabled. */
499 isDisabled() {
500 return __awaiter(this, void 0, void 0, function* () {
501 const button = yield this._button();
502 return coerceBooleanProperty(yield button.getAttribute('disabled'));
503 });
504 }
505 _openCalendar() {
506 return __awaiter(this, void 0, void 0, function* () {
507 return (yield this._button()).click();
508 });
509 }
510}
511MatDatepickerToggleHarness.hostSelector = '.mat-datepicker-toggle';
512
513/** Harness for interacting with a standard Material date range start input in tests. */
514class MatStartDateHarness extends MatDatepickerInputHarnessBase {
515 /**
516 * Gets a `HarnessPredicate` that can be used to search for a `MatStartDateHarness`
517 * that meets certain criteria.
518 * @param options Options for filtering which input instances are considered a match.
519 * @return a `HarnessPredicate` configured with the given options.
520 */
521 static with(options = {}) {
522 return getInputPredicate(MatStartDateHarness, options);
523 }
524}
525MatStartDateHarness.hostSelector = '.mat-start-date';
526/** Harness for interacting with a standard Material date range end input in tests. */
527class MatEndDateHarness extends MatDatepickerInputHarnessBase {
528 /**
529 * Gets a `HarnessPredicate` that can be used to search for a `MatEndDateHarness`
530 * that meets certain criteria.
531 * @param options Options for filtering which input instances are considered a match.
532 * @return a `HarnessPredicate` configured with the given options.
533 */
534 static with(options = {}) {
535 return getInputPredicate(MatEndDateHarness, options);
536 }
537}
538MatEndDateHarness.hostSelector = '.mat-end-date';
539/** Harness for interacting with a standard Material date range input in tests. */
540class MatDateRangeInputHarness extends DatepickerTriggerHarnessBase {
541 /**
542 * Gets a `HarnessPredicate` that can be used to search for a `MatDateRangeInputHarness`
543 * that meets certain criteria.
544 * @param options Options for filtering which input instances are considered a match.
545 * @return a `HarnessPredicate` configured with the given options.
546 */
547 static with(options = {}) {
548 return new HarnessPredicate(MatDateRangeInputHarness, options).addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value));
549 }
550 /** Gets the combined value of the start and end inputs, including the separator. */
551 getValue() {
552 return __awaiter(this, void 0, void 0, function* () {
553 const [start, end, separator] = yield parallel(() => [
554 this.getStartInput().then(input => input.getValue()),
555 this.getEndInput().then(input => input.getValue()),
556 this.getSeparator(),
557 ]);
558 return start + `${end ? ` ${separator} ${end}` : ''}`;
559 });
560 }
561 /** Gets the inner start date input inside the range input. */
562 getStartInput() {
563 return __awaiter(this, void 0, void 0, function* () {
564 // Don't pass in filters here since the start input is required and there can only be one.
565 return this.locatorFor(MatStartDateHarness)();
566 });
567 }
568 /** Gets the inner start date input inside the range input. */
569 getEndInput() {
570 return __awaiter(this, void 0, void 0, function* () {
571 // Don't pass in filters here since the end input is required and there can only be one.
572 return this.locatorFor(MatEndDateHarness)();
573 });
574 }
575 /** Gets the separator text between the values of the two inputs. */
576 getSeparator() {
577 return __awaiter(this, void 0, void 0, function* () {
578 return (yield this.locatorFor('.mat-date-range-input-separator')()).text();
579 });
580 }
581 /** Gets whether the range input is disabled. */
582 isDisabled() {
583 return __awaiter(this, void 0, void 0, function* () {
584 // We consider the input as disabled if both of the sub-inputs are disabled.
585 const [startDisabled, endDisabled] = yield parallel(() => [
586 this.getStartInput().then(input => input.isDisabled()),
587 this.getEndInput().then(input => input.isDisabled()),
588 ]);
589 return startDisabled && endDisabled;
590 });
591 }
592 /** Gets whether the range input is required. */
593 isRequired() {
594 return __awaiter(this, void 0, void 0, function* () {
595 return (yield this.host()).hasClass('mat-date-range-input-required');
596 });
597 }
598 /** Opens the calendar associated with the input. */
599 isCalendarOpen() {
600 return __awaiter(this, void 0, void 0, function* () {
601 // `aria-owns` is set on both inputs only if there's an
602 // open range picker so we can use it as an indicator.
603 const startHost = yield (yield this.getStartInput()).host();
604 return (yield startHost.getAttribute('aria-owns')) != null;
605 });
606 }
607 _openCalendar() {
608 return __awaiter(this, void 0, void 0, function* () {
609 // Alt + down arrow is the combination for opening the calendar with the keyboard.
610 const startHost = yield (yield this.getStartInput()).host();
611 return startHost.sendKeys({ alt: true }, TestKey.DOWN_ARROW);
612 });
613 }
614}
615MatDateRangeInputHarness.hostSelector = '.mat-date-range-input';
616
617/**
618 * @license
619 * Copyright Google LLC All Rights Reserved.
620 *
621 * Use of this source code is governed by an MIT-style license that can be
622 * found in the LICENSE file at https://angular.io/license
623 */
624
625/**
626 * @license
627 * Copyright Google LLC All Rights Reserved.
628 *
629 * Use of this source code is governed by an MIT-style license that can be
630 * found in the LICENSE file at https://angular.io/license
631 */
632
633export { MatCalendarCellHarness, MatCalendarHarness, MatDateRangeInputHarness, MatDatepickerInputHarness, MatDatepickerToggleHarness, MatEndDateHarness, MatStartDateHarness };
634//# sourceMappingURL=testing.mjs.map