Skip to content

Commit

Permalink
test: add tests for AppDatePickerDialog
Browse files Browse the repository at this point in the history
  • Loading branch information
motss committed Nov 26, 2021
1 parent c8f2e6e commit a11a61a
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 26 deletions.
232 changes: 232 additions & 0 deletions src/__tests__/date-picker-dialog/app-date-picker-dialog.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import '../../date-picker-dialog/app-date-picker-dialog';

import type { Button } from '@material/mwc-button';
import { expect, fixture, html } from '@open-wc/testing';

import { DateTimeFormat } from '../../constants';
import type { AppDatePicker } from '../../date-picker/app-date-picker';
import { appDatePickerName } from '../../date-picker/constants';
import type { AppDatePickerDialog } from '../../date-picker-dialog/app-date-picker-dialog';
import type { AppDatePickerDialogBase } from '../../date-picker-dialog/app-date-picker-dialog-base';
import { appDatePickerDialogBaseName, appDatePickerDialogName } from '../../date-picker-dialog/constants';
import type { DialogClosedEventDetail, DialogClosingEventDetailAction } from '../../date-picker-dialog/typings';
import { toDateString } from '../../helpers/to-date-string';
import { toResolvedDate } from '../../helpers/to-resolved-date';
import type { AppMonthCalendar } from '../../month-calendar/app-month-calendar';
import { appMonthCalendarName } from '../../month-calendar/constants';
import type { CustomEventDetail } from '../../typings';
import { promiseTimeout } from '../constants';
import { eventOnce } from '../test-utils/event-once';
import { messageFormatter } from '../test-utils/message-formatter';

describe(appDatePickerDialogName, () => {
const elementSelectors = {
calendarDay: (label: string) => `td.calendar-day[aria-label="${label}"]`,
datePickerDialogBase: appDatePickerDialogBaseName,
datePicker: appDatePickerName,
dialogActionReset: `mwc-button[data-dialog-action="reset"]`,
dialogActionCancel: `mwc-button[dialogaction="cancel"]`,
dialogActionSet: `mwc-button[dialogaction="set"]`,
monthCalendar: appMonthCalendarName,
} as const;
const formatter = DateTimeFormat('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
});
const max = '2100-12-31';
const min = '1970-01-01';
const value = '2020-02-02';

it('renders', async () => {
const el = await new Promise<AppDatePickerDialog>(async (resolve) => {
const element = await fixture<AppDatePickerDialog>(
html`<app-date-picker-dialog
.max=${max}
.min=${min}
.open=${true}
.value=${value}
@opened=${() => resolve(element)}
></app-date-picker-dialog>`
);

globalThis.setTimeout(() => resolve(element), promiseTimeout);
});

const datePickerDialogBase =
el.querySelector<AppDatePickerDialogBase>(elementSelectors.datePickerDialogBase);
const datePicker = el.querySelector(elementSelectors.datePicker);

expect(datePickerDialogBase).exist;
expect(datePicker).exist;
expect(datePickerDialogBase?.hasAttribute('open')).true;
expect(el.valueAsDate).deep.equal(new Date(value));
expect(el.valueAsNumber).equal(+new Date(value));

const dialogActionReset = el.querySelector(elementSelectors.dialogActionReset);
const dialogActionCancel = el.querySelector(elementSelectors.dialogActionCancel);
const dialogActionSet = el.querySelector(elementSelectors.dialogActionSet);

expect(dialogActionReset).exist;
expect(dialogActionCancel).exist;
expect(dialogActionSet).exist;
});

it('opens and closes date picker dialog', async () => {
const el = await fixture<AppDatePickerDialog>(
html`<app-date-picker-dialog></app-date-picker-dialog>`
);

const openedTask = eventOnce<
typeof el,
'opened',
CustomEvent
>(el, 'opened');

const datePickerDialogBase =
el.querySelector<AppDatePickerDialogBase>(elementSelectors.datePickerDialogBase);
let datePicker = el.querySelector(elementSelectors.datePicker);

expect(datePickerDialogBase).exist;
expect(datePicker).exist;
expect(datePickerDialogBase?.hasAttribute('open')).false;

el.show();
await openedTask;
await el.updateComplete;

datePicker = el.querySelector(elementSelectors.datePicker);

expect(datePickerDialogBase?.hasAttribute('open')).true;

const closedTask = eventOnce<
typeof el,
'closed',
CustomEvent<DialogClosedEventDetail>
>(el, 'closed');

el.hide();
await closedTask;
await el.updateComplete;

datePicker = el.querySelector(elementSelectors.datePicker);

expect(datePickerDialogBase?.hasAttribute('open')).false;
});

type CaseSelectsAndConfirmsNewDate = [string, string, DialogClosingEventDetailAction, boolean, string];
const casesSelectsAndConfirmsNewDate: CaseSelectsAndConfirmsNewDate[] = [
['but does not confirm a new date', '2020-02-04', 'cancel', false, value],
['and confirms a new date', '2020-02-04', 'set', false, '2020-02-04'],
['a new date but resets it', '2020-02-04', 'reset', true, toDateString(toResolvedDate())],
];

casesSelectsAndConfirmsNewDate.forEach((a) => {
const [
,
testNewValue,
testDialogAction,
expectedDatePickerDialogHasAttributeOpen,
expectedDatePickerDialogValue,
] = a;

it(
messageFormatter(`selects %s`, a),
async () => {
const el = await fixture<AppDatePickerDialog>(
html`<app-date-picker-dialog
.max=${max}
.min=${min}
.value=${value}
></app-date-picker-dialog>`
);

const openedTask = eventOnce<
typeof el,
'opened',
CustomEvent
>(el, 'opened');

const datePickerDialogBase =
el.querySelector<AppDatePickerDialogBase>(elementSelectors.datePickerDialogBase);
const datePicker =
el.querySelector<AppDatePicker>(elementSelectors.datePicker);
const monthCalendar =
datePicker?.query<AppMonthCalendar>(elementSelectors.monthCalendar);
const dialogActionReset =
el.querySelector<Button>(elementSelectors.dialogActionReset);
const dialogActionCancel =
el.querySelector<Button>(elementSelectors.dialogActionCancel);
const dialogActionSet =
el.querySelector<Button>(elementSelectors.dialogActionSet);

el.show();
await openedTask;
await el.updateComplete;

expect(datePickerDialogBase).exist;
expect(datePicker).exist;
expect(dialogActionSet).exist;
expect(datePickerDialogBase?.hasAttribute('open')).true;
expect(el.value).equal(value);

const newValueDate = new Date(testNewValue);
const newSelectedDate =
monthCalendar?.query(elementSelectors.calendarDay(formatter.format(newValueDate)))

const dateUpdatedTask = eventOnce<
AppDatePicker,
'date-updated',
CustomEvent<CustomEventDetail['date-updated']>

>(datePicker as AppDatePicker, 'date-updated');

expect(newSelectedDate).exist;

newSelectedDate?.click();
await dateUpdatedTask;
await datePicker?.updateComplete;
await el.updateComplete;

expect(el.value).equal(value);
expect(datePicker?.value).equal(testNewValue);
expect(datePicker?.valueAsDate).deep.equal(newValueDate);
expect(datePicker?.valueAsNumber).equal(+newValueDate);

const closedTask = eventOnce<
typeof el,
'closed',
CustomEvent<DialogClosedEventDetail>
>(el, 'closed');

switch (testDialogAction) {
case 'cancel': {
dialogActionCancel?.click();
break;
}
case 'reset': {
dialogActionReset?.click();
break;
}
case 'set': {
dialogActionSet?.click();
break;
}
default:
}

await closedTask;
await datePicker?.updateComplete;
await el.updateComplete;

expect(
datePickerDialogBase?.hasAttribute('open')
).equal(expectedDatePickerDialogHasAttributeOpen);
expect(el.value).equal(expectedDatePickerDialogValue);
expect(el.valueAsDate).deep.equal(new Date(expectedDatePickerDialogValue));
expect(el.valueAsNumber).equal(+new Date(expectedDatePickerDialogValue));
}
)
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { expect, fixture, html } from '@open-wc/testing';
import type { DialogClosedEventDetail } from '../../date-picker-dialog/typings';
import type { AppDatePickerInputSurface } from '../../date-picker-input-surface/app-date-picker-input-surface';
import { appDatePickerInputSurfaceName } from '../../date-picker-input-surface/constants';
import { promiseTimeout } from '../constants';
import { eventOnce } from '../test-utils/event-once';

describe(appDatePickerInputSurfaceName, () => {
Expand All @@ -21,6 +22,8 @@ describe(appDatePickerInputSurfaceName, () => {
<h1 class=test>Test</h1>
</app-date-picker-input-surface>`
);

globalThis.setTimeout(() => resolve(element), promiseTimeout);
});

const mdcMenuSurface = el.query(elementSelectors.mdcMenuSurface);
Expand All @@ -35,6 +38,8 @@ describe(appDatePickerInputSurfaceName, () => {
<h1 class=test>Test</h1>
</app-date-picker-input-surface>`
);

globalThis.setTimeout(() => resolve(element), promiseTimeout);
});

const mdcMenuSurface = el.query(elementSelectors.mdcMenuSurface);
Expand Down
3 changes: 2 additions & 1 deletion src/date-picker-dialog/date-picker-dialog-base.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Dialog } from '@material/mwc-dialog';

import { ElementMixin } from '../mixins/element-mixin.js';
import { datePickerDialogBaseStyling } from './stylings.js';

export class DatePickerDialogBase extends Dialog {
export class DatePickerDialogBase extends ElementMixin(Dialog) {
public static override styles = [
...Dialog.styles,
datePickerDialogBaseStyling,
Expand Down
51 changes: 31 additions & 20 deletions src/date-picker-dialog/date-picker-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,22 @@ import { datePickerDialogStyling } from './stylings.js';
import type { DatePickerDialogProperties, DialogClosingEventDetail } from './typings.js';

export class DatePickerDialog extends DatePickerMixin(DatePickerMinMaxMixin(RootElement)) implements DatePickerDialogProperties {
public override get valueAsDate(): Date {
return this.#valueAsDate;
}

public override get valueAsNumber(): number {
return +this.#valueAsDate;
}

@property({ type: String }) public confirmLabel = 'set';
@property({ type: String }) public dismissLabel = 'cancel';
@property({ type: String }) public resetLabel = 'reset';
@property({ type: Boolean }) public open = false;

#disconnect: () => void = () => void 0;
#valueAsDate: Date = toResolvedDate();
#isResetAction = false;
#selectedDate: Date;
#valueAsDate: Date;

public static override styles = [
datePickerDialogStyling,
Expand All @@ -32,21 +41,7 @@ export class DatePickerDialog extends DatePickerMixin(DatePickerMinMaxMixin(Root
public constructor() {
super();

const closing = (ev: CustomEvent<DialogClosingEventDetail>) => {
this.#onClosing(ev);
};

this.addEventListener('closing' as never, closing);

this.#disconnect = () => {
this.removeEventListener('closing' as never, closing);
};
}

public override disconnectedCallback() {
super.disconnectedCallback();

this.#disconnect();
this.#selectedDate = this.#valueAsDate = toResolvedDate();
}

protected override createRenderRoot(): Element | ShadowRoot {
Expand Down Expand Up @@ -91,6 +86,7 @@ export class DatePickerDialog extends DatePickerMixin(DatePickerMinMaxMixin(Root
<div class=secondary-actions slot=secondaryAction>
<mwc-button
@click=${this.#onResetClick}
data-dialog-action=reset
>${this.resetLabel}</mwc-button>
<mwc-button
dialogAction=cancel
Expand Down Expand Up @@ -123,7 +119,11 @@ export class DatePickerDialog extends DatePickerMixin(DatePickerMinMaxMixin(Root
},
}: CustomEvent<DialogClosingEventDetail>): void => {
if (action === 'set') {
this.value = toDateString(this.#valueAsDate);
const selectedDate = this.#selectedDate;

this.#valueAsDate = new Date(selectedDate);

this.value = toDateString(selectedDate);
}
};

Expand All @@ -132,18 +132,29 @@ export class DatePickerDialog extends DatePickerMixin(DatePickerMinMaxMixin(Root
valueAsDate,
},
}: CustomEvent<CustomEventDetail['date-updated']['detail']>) {
this.#valueAsDate = valueAsDate;
this.#selectedDate = new Date(valueAsDate);

/**
* Reset `value` when it is a reset action or `value` is nullish
*/
if (this.#isResetAction || !this.value) {
this.#isResetAction = false;
this.#valueAsDate = new Date(valueAsDate);

this.value = toDateString(valueAsDate);
}
}

#onDatePickerFirstUpdated({
detail: {
valueAsDate,
},
}: CustomEvent<CustomEventDetail['first-updated']['detail']>) {
this.#valueAsDate = valueAsDate;
this.#selectedDate = this.#valueAsDate = valueAsDate;
}

#onResetClick() {
this.#isResetAction = true;
this.value = undefined;
}
}
8 changes: 4 additions & 4 deletions src/date-picker/date-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ import { datePickerStyling } from './stylings.js';
import type { DatePickerChangedProperties } from './typings.js';

export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(RootElement)) implements DatePickerProperties {
public get valueAsDate(): Date {
public override get valueAsDate(): Date {
return this.#valueAsDate;
}

public get valueAsNumber(): number {
public override get valueAsNumber(): number {
return +this.#valueAsDate;
}

Expand Down Expand Up @@ -147,9 +147,9 @@ export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(RootElemen
this.#valueAsDate = new Date(valueDate);

/**
* Always override `value` when its value is nullish and dispatch `date-updated` event.
* Always override `value` when its value is not a string and dispatch `date-updated` event.
*/
if (this.value === null) {
if (!this.value) {
const valueStr = toDateString(valueDate);

this.value = valueStr;
Expand Down
Loading

0 comments on commit a11a61a

Please sign in to comment.