123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- import { HarnessPredicate, ComponentHarness, parallel, TestKey } from '@angular/cdk/testing';
- import { MatFormFieldControlHarness } from './form-field/testing/control.mjs';
- /** Sets up the filter predicates for a datepicker input harness. */
- function getInputPredicate(type, options) {
- return new HarnessPredicate(type, options)
- .addOption('value', options.value, (harness, value) => {
- return HarnessPredicate.stringMatches(harness.getValue(), value);
- })
- .addOption('placeholder', options.placeholder, (harness, placeholder) => {
- return HarnessPredicate.stringMatches(harness.getPlaceholder(), placeholder);
- });
- }
- /** Base class for datepicker input harnesses. */
- class MatDatepickerInputHarnessBase extends MatFormFieldControlHarness {
- /** Whether the input is disabled. */
- async isDisabled() {
- return (await this.host()).getProperty('disabled');
- }
- /** Whether the input is required. */
- async isRequired() {
- return (await this.host()).getProperty('required');
- }
- /** Gets the value of the input. */
- async getValue() {
- // The "value" property of the native input is always defined.
- return await (await this.host()).getProperty('value');
- }
- /**
- * Sets the value of the input. The value will be set by simulating
- * keypresses that correspond to the given value.
- */
- async setValue(newValue) {
- const inputEl = await this.host();
- await inputEl.clear();
- // We don't want to send keys for the value if the value is an empty
- // string in order to clear the value. Sending keys with an empty string
- // still results in unnecessary focus events.
- if (newValue) {
- await inputEl.sendKeys(newValue);
- }
- await inputEl.dispatchEvent('change');
- }
- /** Gets the placeholder of the input. */
- async getPlaceholder() {
- return await (await this.host()).getProperty('placeholder');
- }
- /**
- * Focuses the input and returns a promise that indicates when the
- * action is complete.
- */
- async focus() {
- return (await this.host()).focus();
- }
- /**
- * Blurs the input and returns a promise that indicates when the
- * action is complete.
- */
- async blur() {
- return (await this.host()).blur();
- }
- /** Whether the input is focused. */
- async isFocused() {
- return (await this.host()).isFocused();
- }
- /** Gets the formatted minimum date for the input's value. */
- async getMin() {
- return (await this.host()).getAttribute('min');
- }
- /** Gets the formatted maximum date for the input's value. */
- async getMax() {
- return (await this.host()).getAttribute('max');
- }
- }
- /** Harness for interacting with a standard Material calendar cell in tests. */
- class MatCalendarCellHarness extends ComponentHarness {
- static hostSelector = '.mat-calendar-body-cell';
- /** Reference to the inner content element inside the cell. */
- _content = this.locatorFor('.mat-calendar-body-cell-content');
- /**
- * Gets a `HarnessPredicate` that can be used to search for a `MatCalendarCellHarness`
- * that meets certain criteria.
- * @param options Options for filtering which cell instances are considered a match.
- * @return a `HarnessPredicate` configured with the given options.
- */
- static with(options = {}) {
- return new HarnessPredicate(MatCalendarCellHarness, options)
- .addOption('text', options.text, (harness, text) => {
- return HarnessPredicate.stringMatches(harness.getText(), text);
- })
- .addOption('selected', options.selected, async (harness, selected) => {
- return (await harness.isSelected()) === selected;
- })
- .addOption('active', options.active, async (harness, active) => {
- return (await harness.isActive()) === active;
- })
- .addOption('disabled', options.disabled, async (harness, disabled) => {
- return (await harness.isDisabled()) === disabled;
- })
- .addOption('today', options.today, async (harness, today) => {
- return (await harness.isToday()) === today;
- })
- .addOption('inRange', options.inRange, async (harness, inRange) => {
- return (await harness.isInRange()) === inRange;
- })
- .addOption('inComparisonRange', options.inComparisonRange, async (harness, inComparisonRange) => {
- return (await harness.isInComparisonRange()) === inComparisonRange;
- })
- .addOption('inPreviewRange', options.inPreviewRange, async (harness, inPreviewRange) => {
- return (await harness.isInPreviewRange()) === inPreviewRange;
- });
- }
- /** Gets the text of the calendar cell. */
- async getText() {
- return (await this._content()).text();
- }
- /** Gets the aria-label of the calendar cell. */
- async getAriaLabel() {
- // We're guaranteed for the `aria-label` to be defined
- // since this is a private element that we control.
- return (await this.host()).getAttribute('aria-label');
- }
- /** Whether the cell is selected. */
- async isSelected() {
- const host = await this.host();
- return (await host.getAttribute('aria-pressed')) === 'true';
- }
- /** Whether the cell is disabled. */
- async isDisabled() {
- return this._hasState('disabled');
- }
- /** Whether the cell is currently activated using keyboard navigation. */
- async isActive() {
- return this._hasState('active');
- }
- /** Whether the cell represents today's date. */
- async isToday() {
- return (await this._content()).hasClass('mat-calendar-body-today');
- }
- /** Selects the calendar cell. Won't do anything if the cell is disabled. */
- async select() {
- return (await this.host()).click();
- }
- /** Hovers over the calendar cell. */
- async hover() {
- return (await this.host()).hover();
- }
- /** Moves the mouse away from the calendar cell. */
- async mouseAway() {
- return (await this.host()).mouseAway();
- }
- /** Focuses the calendar cell. */
- async focus() {
- return (await this.host()).focus();
- }
- /** Removes focus from the calendar cell. */
- async blur() {
- return (await this.host()).blur();
- }
- /** Whether the cell is the start of the main range. */
- async isRangeStart() {
- return this._hasState('range-start');
- }
- /** Whether the cell is the end of the main range. */
- async isRangeEnd() {
- return this._hasState('range-end');
- }
- /** Whether the cell is part of the main range. */
- async isInRange() {
- return this._hasState('in-range');
- }
- /** Whether the cell is the start of the comparison range. */
- async isComparisonRangeStart() {
- return this._hasState('comparison-start');
- }
- /** Whether the cell is the end of the comparison range. */
- async isComparisonRangeEnd() {
- return this._hasState('comparison-end');
- }
- /** Whether the cell is inside of the comparison range. */
- async isInComparisonRange() {
- return this._hasState('in-comparison-range');
- }
- /** Whether the cell is the start of the preview range. */
- async isPreviewRangeStart() {
- return this._hasState('preview-start');
- }
- /** Whether the cell is the end of the preview range. */
- async isPreviewRangeEnd() {
- return this._hasState('preview-end');
- }
- /** Whether the cell is inside of the preview range. */
- async isInPreviewRange() {
- return this._hasState('in-preview');
- }
- /** Returns whether the cell has a particular CSS class-based state. */
- async _hasState(name) {
- return (await this.host()).hasClass(`mat-calendar-body-${name}`);
- }
- }
- /** Possible views of a `MatCalendarHarness`. */
- var CalendarView;
- (function (CalendarView) {
- CalendarView[CalendarView["MONTH"] = 0] = "MONTH";
- CalendarView[CalendarView["YEAR"] = 1] = "YEAR";
- CalendarView[CalendarView["MULTI_YEAR"] = 2] = "MULTI_YEAR";
- })(CalendarView || (CalendarView = {}));
- /** Harness for interacting with a standard Material calendar in tests. */
- class MatCalendarHarness extends ComponentHarness {
- static hostSelector = '.mat-calendar';
- /** Queries for the calendar's period toggle button. */
- _periodButton = this.locatorFor('.mat-calendar-period-button');
- /**
- * Gets a `HarnessPredicate` that can be used to search for a `MatCalendarHarness`
- * that meets certain criteria.
- * @param options Options for filtering which calendar instances are considered a match.
- * @return a `HarnessPredicate` configured with the given options.
- */
- static with(options = {}) {
- return new HarnessPredicate(MatCalendarHarness, options);
- }
- /**
- * Gets a list of cells inside the calendar.
- * @param filter Optionally filters which cells are included.
- */
- async getCells(filter = {}) {
- return this.locatorForAll(MatCalendarCellHarness.with(filter))();
- }
- /** Gets the current view that is being shown inside the calendar. */
- async getCurrentView() {
- if (await this.locatorForOptional('mat-multi-year-view')()) {
- return CalendarView.MULTI_YEAR;
- }
- if (await this.locatorForOptional('mat-year-view')()) {
- return CalendarView.YEAR;
- }
- return CalendarView.MONTH;
- }
- /** Gets the label of the current calendar view. */
- async getCurrentViewLabel() {
- return (await this._periodButton()).text();
- }
- /** Changes the calendar view by clicking on the view toggle button. */
- async changeView() {
- return (await this._periodButton()).click();
- }
- /** Goes to the next page of the current view (e.g. next month when inside the month view). */
- async next() {
- return (await this.locatorFor('.mat-calendar-next-button')()).click();
- }
- /**
- * Goes to the previous page of the current view
- * (e.g. previous month when inside the month view).
- */
- async previous() {
- return (await this.locatorFor('.mat-calendar-previous-button')()).click();
- }
- /**
- * Selects a cell in the current calendar view.
- * @param filter An optional filter to apply to the cells. The first cell matching the filter
- * will be selected.
- */
- async selectCell(filter = {}) {
- const cells = await this.getCells(filter);
- if (!cells.length) {
- throw Error(`Cannot find calendar cell matching filter ${JSON.stringify(filter)}`);
- }
- await cells[0].select();
- }
- }
- /** Base class for harnesses that can trigger a calendar. */
- class DatepickerTriggerHarnessBase extends ComponentHarness {
- /** Opens the calendar if the trigger is enabled and it has a calendar. */
- async openCalendar() {
- const [isDisabled, hasCalendar] = await parallel(() => [this.isDisabled(), this.hasCalendar()]);
- if (!isDisabled && hasCalendar) {
- return this._openCalendar();
- }
- }
- /** Closes the calendar if it is open. */
- async closeCalendar() {
- if (await this.isCalendarOpen()) {
- await closeCalendar(getCalendarId(this.host()), this.documentRootLocatorFactory());
- // This is necessary so that we wait for the closing animation to finish in touch UI mode.
- await this.forceStabilize();
- }
- }
- /** Gets whether there is a calendar associated with the trigger. */
- async hasCalendar() {
- return (await getCalendarId(this.host())) != null;
- }
- /**
- * Gets the `MatCalendarHarness` that is associated with the trigger.
- * @param filter Optionally filters which calendar is included.
- */
- async getCalendar(filter = {}) {
- return getCalendar(filter, this.host(), this.documentRootLocatorFactory());
- }
- }
- /** Gets the ID of the calendar that a particular test element can trigger. */
- async function getCalendarId(host) {
- return (await host).getAttribute('data-mat-calendar');
- }
- /** Closes the calendar with a specific ID. */
- async function closeCalendar(calendarId, documentLocator) {
- // We close the calendar by clicking on the backdrop, even though all datepicker variants
- // have the ability to close by pressing escape. The backdrop is preferrable, because the
- // escape key has multiple functions inside a range picker (either cancel the current range
- // or close the calendar). Since we don't have access to set the ID on the backdrop in all
- // cases, we set a unique class instead which is the same as the calendar's ID and suffixed
- // with `-backdrop`.
- const backdropSelector = `.${await calendarId}-backdrop`;
- return (await documentLocator.locatorFor(backdropSelector)()).click();
- }
- /** Gets the test harness for a calendar associated with a particular host. */
- async function getCalendar(filter, host, documentLocator) {
- const calendarId = await getCalendarId(host);
- if (!calendarId) {
- throw Error(`Element is not associated with a calendar`);
- }
- return documentLocator.locatorFor(MatCalendarHarness.with({
- ...filter,
- selector: `#${calendarId}`,
- }))();
- }
- /** Harness for interacting with a standard Material datepicker inputs in tests. */
- class MatDatepickerInputHarness extends MatDatepickerInputHarnessBase {
- static hostSelector = '.mat-datepicker-input';
- /**
- * Gets a `HarnessPredicate` that can be used to search for a `MatDatepickerInputHarness`
- * that meets certain criteria.
- * @param options Options for filtering which input instances are considered a match.
- * @return a `HarnessPredicate` configured with the given options.
- */
- static with(options = {}) {
- return getInputPredicate(MatDatepickerInputHarness, options);
- }
- /** Gets whether the calendar associated with the input is open. */
- async isCalendarOpen() {
- // `aria-owns` is set only if there's an open datepicker so we can use it as an indicator.
- const host = await this.host();
- return (await host.getAttribute('aria-owns')) != null;
- }
- /** Opens the calendar associated with the input. */
- async openCalendar() {
- const [isDisabled, hasCalendar] = await parallel(() => [this.isDisabled(), this.hasCalendar()]);
- if (!isDisabled && hasCalendar) {
- // Alt + down arrow is the combination for opening the calendar with the keyboard.
- const host = await this.host();
- return host.sendKeys({ alt: true }, TestKey.DOWN_ARROW);
- }
- }
- /** Closes the calendar associated with the input. */
- async closeCalendar() {
- if (await this.isCalendarOpen()) {
- await closeCalendar(getCalendarId(this.host()), this.documentRootLocatorFactory());
- // This is necessary so that we wait for the closing animation to finish in touch UI mode.
- await this.forceStabilize();
- }
- }
- /** Whether a calendar is associated with the input. */
- async hasCalendar() {
- return (await getCalendarId(this.host())) != null;
- }
- /**
- * Gets the `MatCalendarHarness` that is associated with the trigger.
- * @param filter Optionally filters which calendar is included.
- */
- async getCalendar(filter = {}) {
- return getCalendar(filter, this.host(), this.documentRootLocatorFactory());
- }
- }
- /** Harness for interacting with a standard Material date range start input in tests. */
- class MatStartDateHarness extends MatDatepickerInputHarnessBase {
- static hostSelector = '.mat-start-date';
- /**
- * Gets a `HarnessPredicate` that can be used to search for a `MatStartDateHarness`
- * that meets certain criteria.
- * @param options Options for filtering which input instances are considered a match.
- * @return a `HarnessPredicate` configured with the given options.
- */
- static with(options = {}) {
- return getInputPredicate(MatStartDateHarness, options);
- }
- }
- /** Harness for interacting with a standard Material date range end input in tests. */
- class MatEndDateHarness extends MatDatepickerInputHarnessBase {
- static hostSelector = '.mat-end-date';
- /**
- * Gets a `HarnessPredicate` that can be used to search for a `MatEndDateHarness`
- * that meets certain criteria.
- * @param options Options for filtering which input instances are considered a match.
- * @return a `HarnessPredicate` configured with the given options.
- */
- static with(options = {}) {
- return getInputPredicate(MatEndDateHarness, options);
- }
- }
- /** Harness for interacting with a standard Material date range input in tests. */
- class MatDateRangeInputHarness extends DatepickerTriggerHarnessBase {
- static hostSelector = '.mat-date-range-input';
- /**
- * Gets a `HarnessPredicate` that can be used to search for a `MatDateRangeInputHarness`
- * that meets certain criteria.
- * @param options Options for filtering which input instances are considered a match.
- * @return a `HarnessPredicate` configured with the given options.
- */
- static with(options = {}) {
- return new HarnessPredicate(MatDateRangeInputHarness, options).addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value));
- }
- /** Gets the combined value of the start and end inputs, including the separator. */
- async getValue() {
- const [start, end, separator] = await parallel(() => [
- this.getStartInput().then(input => input.getValue()),
- this.getEndInput().then(input => input.getValue()),
- this.getSeparator(),
- ]);
- return start + `${end ? ` ${separator} ${end}` : ''}`;
- }
- /** Gets the inner start date input inside the range input. */
- async getStartInput() {
- // Don't pass in filters here since the start input is required and there can only be one.
- return this.locatorFor(MatStartDateHarness)();
- }
- /** Gets the inner start date input inside the range input. */
- async getEndInput() {
- // Don't pass in filters here since the end input is required and there can only be one.
- return this.locatorFor(MatEndDateHarness)();
- }
- /** Gets the separator text between the values of the two inputs. */
- async getSeparator() {
- return (await this.locatorFor('.mat-date-range-input-separator')()).text();
- }
- /** Gets whether the range input is disabled. */
- async isDisabled() {
- // We consider the input as disabled if both of the sub-inputs are disabled.
- const [startDisabled, endDisabled] = await parallel(() => [
- this.getStartInput().then(input => input.isDisabled()),
- this.getEndInput().then(input => input.isDisabled()),
- ]);
- return startDisabled && endDisabled;
- }
- /** Gets whether the range input is required. */
- async isRequired() {
- return (await this.host()).hasClass('mat-date-range-input-required');
- }
- /** Opens the calendar associated with the input. */
- async isCalendarOpen() {
- // `aria-owns` is set on both inputs only if there's an
- // open range picker so we can use it as an indicator.
- const startHost = await (await this.getStartInput()).host();
- return (await startHost.getAttribute('aria-owns')) != null;
- }
- async _openCalendar() {
- // Alt + down arrow is the combination for opening the calendar with the keyboard.
- const startHost = await (await this.getStartInput()).host();
- return startHost.sendKeys({ alt: true }, TestKey.DOWN_ARROW);
- }
- }
- export { CalendarView as C, DatepickerTriggerHarnessBase as D, MatDatepickerInputHarness as M, MatStartDateHarness as a, MatEndDateHarness as b, MatDateRangeInputHarness as c, MatCalendarHarness as d, MatCalendarCellHarness as e };
- //# sourceMappingURL=date-range-input-harness-Bp1T4oUe.mjs.map
|