mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Improved timezone support date picker and improved tests (#15545)
fixes https://github.com/TryGhost/Team/issues/1946 Problem: - When running the admin tests in a timezone that is later than UTC, the tests failed. Causes: - Some tests needed some adjustements - The DateTimePicker did not always use the correct timezone. - Test models createdAt times sometimes depended on the timezone of the test runner Solution: - All the input DateTimePicker gets should be processed in the blog's timezone. - Make sure that all communication (properties, setters, minDate...) with `PowerDatepicker` happens in the local timezone. When setting, convert that date to the blog timezone and use that as the real value.
This commit is contained in:
parent
e679bb4187
commit
07cb542b97
9 changed files with 220 additions and 74 deletions
|
@ -953,3 +953,5 @@ remove|ember-template-lint|no-action|132|15|132|15|c7db9d737e3f06cc754d7a49a3e35
|
|||
remove|ember-template-lint|no-passed-in-event-handlers|11|28|11|28|02c81dfb804e41f5b3c10730e431d1e4b0958e6f|1665014400000|1675386000000|1680566400000|app/components/settings/members/stripe-settings-form.hbs
|
||||
remove|ember-template-lint|no-passed-in-event-handlers|20|28|20|28|1591adfb7d0dbab4321126ada2e2c5a4a8c66516|1665014400000|1675386000000|1680566400000|app/components/settings/members/stripe-settings-form.hbs
|
||||
remove|ember-template-lint|no-passed-in-event-handlers|93|28|93|28|55dadf0e7dc5e2ed57771f46ca3cb82607d1799c|1665014400000|1675386000000|1680566400000|app/components/settings/members/stripe-settings-form.hbs
|
||||
remove|ember-template-lint|no-action|5|18|5|18|7c796afb78a976ab2411eab292c02c4250e429c7|1662681600000|1673053200000|1678237200000|app/components/gh-date-time-picker.hbs
|
||||
add|ember-template-lint|no-action|5|18|5|18|0c80a75b2a80d404755333991c266c81c97c9cda|1665100800000|1675472400000|1680652800000|app/components/gh-date-time-picker.hbs
|
||||
|
|
|
@ -8,7 +8,8 @@ export default class PublishAtOption extends Component {
|
|||
|
||||
@action
|
||||
setDate(selectedDate) {
|
||||
const selectedMoment = moment(selectedDate);
|
||||
// selectedDate is a Date object that contains the correct date string in the blog timezone
|
||||
const selectedMoment = moment.tz(selectedDate, this.settings.get('timezone'));
|
||||
const {years, months, date} = selectedMoment.toObject();
|
||||
|
||||
// Create a new moment from existing scheduledAtUTC _in site timezone_.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<div class="gh-date-time-picker" data-test-component="gh-date-time-picker">
|
||||
<PowerDatepicker
|
||||
@selected={{this._date}}
|
||||
@center={{this._date}}
|
||||
@onSelect={{action "setDateInternal" value="date"}}
|
||||
@selected={{readonly this.localDateValue}}
|
||||
@center={{readonly this.localDateValue}}
|
||||
@onSelect={{action "setLocalDate" value="date"}}
|
||||
@renderInPlace={{this.renderInPlaceWithFallback}}
|
||||
@disabled={{this.disabled}} as |dp|
|
||||
>
|
||||
|
|
|
@ -22,9 +22,10 @@ export default class GhDateTimePicker extends Component {
|
|||
timeErrorProperty = null;
|
||||
isActive = true;
|
||||
_time = '';
|
||||
// _date is always a moment object in the blog's timezone
|
||||
_previousTime = '';
|
||||
_minDate = null;
|
||||
_maxDate = null;
|
||||
_minDate = null; // Always set to a Date object
|
||||
_maxDate = null; // Always set to a Date object
|
||||
_scratchDate = null;
|
||||
_scratchDateError = null;
|
||||
|
||||
|
@ -46,9 +47,15 @@ export default class GhDateTimePicker extends Component {
|
|||
if (this._scratchDate !== null) {
|
||||
return this._scratchDate;
|
||||
} else {
|
||||
return moment(this._date).format(DATE_FORMAT);
|
||||
return this._date?.format(DATE_FORMAT);
|
||||
}
|
||||
}
|
||||
|
||||
@computed('_date')
|
||||
get localDateValue() {
|
||||
// Convert the selected date to a new date in the local timezone, purely to please PowerDatepicker
|
||||
return new Date(this._date.format(DATE_FORMAT));
|
||||
}
|
||||
|
||||
@computed('blogTimezone')
|
||||
get timezone() {
|
||||
|
@ -94,7 +101,8 @@ export default class GhDateTimePicker extends Component {
|
|||
let blogTimezone = this.blogTimezone;
|
||||
|
||||
if (!isBlank(date)) {
|
||||
this.set('_date', moment(date));
|
||||
// Note: input date as a string is expected to be in the blog's timezone
|
||||
this.set('_date', moment.tz(date, blogTimezone));
|
||||
} else {
|
||||
this.set('_date', moment().tz(blogTimezone));
|
||||
}
|
||||
|
@ -113,7 +121,7 @@ export default class GhDateTimePicker extends Component {
|
|||
this._lastDate = this.date;
|
||||
|
||||
if (isBlank(time)) {
|
||||
this.set('_time', moment(this._date).format('HH:mm'));
|
||||
this.set('_time', this._date.format('HH:mm'));
|
||||
} else {
|
||||
this.set('_time', this.time);
|
||||
}
|
||||
|
@ -121,17 +129,17 @@ export default class GhDateTimePicker extends Component {
|
|||
|
||||
// unless min/max date is at midnight moment will disable that day
|
||||
if (minDate === 'now') {
|
||||
this.set('_minDate', moment.tz(moment().tz(blogTimezone).format(DATE_FORMAT), blogTimezone));
|
||||
this.set('_minDate', moment(moment().tz(blogTimezone).format(DATE_FORMAT)).toDate());
|
||||
} else if (!isBlank(minDate)) {
|
||||
this.set('_minDate', moment(moment(minDate).format(DATE_FORMAT)));
|
||||
this.set('_minDate', moment(moment.tz(minDate, blogTimezone).format(DATE_FORMAT)).toDate());
|
||||
} else {
|
||||
this.set('_minDate', null);
|
||||
}
|
||||
|
||||
if (maxDate === 'now') {
|
||||
this.set('_maxDate', moment.tz(moment().tz(blogTimezone).format(DATE_FORMAT), blogTimezone));
|
||||
this.set('_maxDate', moment(moment().tz(blogTimezone).format(DATE_FORMAT)).toDate());
|
||||
} else if (!isBlank(maxDate)) {
|
||||
this.set('_maxDate', moment(moment(maxDate).format(DATE_FORMAT)));
|
||||
this.set('_maxDate', moment(moment.tz(maxDate, blogTimezone).format(DATE_FORMAT)).toDate());
|
||||
} else {
|
||||
this.set('_maxDate', null);
|
||||
}
|
||||
|
@ -155,6 +163,19 @@ export default class GhDateTimePicker extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by `PowerDatepicker` when a user selected a date. It is constructed like
|
||||
* The difference here is that the Date object that is passed contains the date, but only when viewed in the local timezone.
|
||||
* This timezone can differ between the timezone of the blog. We need to convert the date to a new date in the blog's timezone on the same day that was selected.
|
||||
* Example: new Date('2000-01-01') -> a user selected 2000-01-01. In the blog timezone, this could be 1999-12-31 23:00, which is wrong.
|
||||
*/
|
||||
@action
|
||||
setLocalDate(date) {
|
||||
// Convert to a date string in the local timezone (moment is in local timezone by default)
|
||||
const dateString = moment(date).format(DATE_FORMAT);
|
||||
this._setDate(dateString);
|
||||
}
|
||||
|
||||
@action
|
||||
setTimeInternal(time, event) {
|
||||
if (time.match(/^\d:\d\d$/)) {
|
||||
|
@ -166,7 +187,7 @@ export default class GhDateTimePicker extends Component {
|
|||
this.set('_previousTime', time);
|
||||
|
||||
if (isBlank(this.date)) {
|
||||
this.setDate(this._date);
|
||||
this.setDate(this._date.toDate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +213,7 @@ export default class GhDateTimePicker extends Component {
|
|||
onDateBlur(event) {
|
||||
// make sure we're not doing anything just because the calendar dropdown
|
||||
// is opened and clicked
|
||||
if (event.target.value === moment(this._date).format('YYYY-MM-DD')) {
|
||||
if (event.target.value === this._date.format('YYYY-MM-DD')) {
|
||||
this._resetScratchDate();
|
||||
return;
|
||||
}
|
||||
|
@ -258,7 +279,7 @@ export default class GhDateTimePicker extends Component {
|
|||
return false;
|
||||
}
|
||||
|
||||
let date = moment(dateStr, DATE_FORMAT);
|
||||
let date = moment.tz(dateStr, DATE_FORMAT, this.blogTimezone);
|
||||
if (!date.isValid()) {
|
||||
this._setScratchDateError('Invalid date');
|
||||
return false;
|
||||
|
|
|
@ -209,8 +209,9 @@ export default class GhPostSettingsMenu extends Component {
|
|||
|
||||
@action
|
||||
setPublishedAtBlogDate(date) {
|
||||
// date is a Date object that contains the correct date string in the blog timezone
|
||||
let post = this.post;
|
||||
let dateString = moment(date).format('YYYY-MM-DD');
|
||||
let dateString = moment.tz(date, this.settings.get('timezone')).format('YYYY-MM-DD');
|
||||
|
||||
post.get('errors').remove('publishedAtBlogDate');
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@ import moment from 'moment-timezone';
|
|||
import {Factory} from 'miragejs';
|
||||
|
||||
export default Factory.extend({
|
||||
createdAt() { return moment().toISOString(); },
|
||||
createdAt() { return moment.utc().toISOString(); },
|
||||
createdBy: 1,
|
||||
name(i) { return `Label ${i}`; },
|
||||
slug(i) { return `label-${i}`; },
|
||||
updatedAt() { return moment().toISOString(); },
|
||||
updatedAt() { return moment.utc().toISOString(); },
|
||||
updatedBy: 1,
|
||||
count() {
|
||||
// this gets updated automatically by the label serializer
|
||||
|
|
|
@ -10,7 +10,7 @@ export default Factory.extend({
|
|||
name() { return `${faker.name.firstName()} ${faker.name.lastName()}`; },
|
||||
email: faker.internet.email,
|
||||
status: 'free',
|
||||
createdAt() { return moment(randomDate()).format('YYYY-MM-DD HH:mm:ss'); },
|
||||
createdAt() { return moment.utc(randomDate()).format('YYYY-MM-DD HH:mm:ss'); },
|
||||
|
||||
free: trait({
|
||||
status: 'free'
|
||||
|
|
|
@ -38,7 +38,7 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
|
||||
it('defaults to now when @value is empty', async function () {
|
||||
clock = sinon.useFakeTimers({
|
||||
now: moment('2022-02-22 22:22:22.000Z').toDate()
|
||||
now: moment('2022-02-22 22:22:22.000').toDate()
|
||||
});
|
||||
|
||||
await render(hbs`<GhDatePicker />`);
|
||||
|
@ -46,14 +46,14 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
});
|
||||
|
||||
it('shows passed in @value value', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
await render(hbs`<GhDatePicker @value={{this.date}} />`);
|
||||
expect(find('[data-test-date-picker-input]'), 'date input').to.have.value('2022-02-22');
|
||||
});
|
||||
|
||||
it('updates date via input blur', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
const changeSpy = sinon.spy();
|
||||
this.set('onChange', changeSpy);
|
||||
|
@ -64,11 +64,11 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
|
||||
expect(changeSpy.callCount).to.equal(1);
|
||||
expect(changeSpy.firstCall.args[0]).to.be.an.instanceof(Date);
|
||||
expect(changeSpy.firstCall.args[0].toISOString()).to.equal(moment('2022-02-28T00:00:00.000Z').toISOString());
|
||||
expect(changeSpy.firstCall.args[0].toISOString()).to.equal(moment('2022-02-28T00:00:00.000').toISOString());
|
||||
});
|
||||
|
||||
it('updates date via input Enter keydown', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
const changeSpy = sinon.spy();
|
||||
this.set('onChange', changeSpy);
|
||||
|
@ -79,11 +79,11 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
|
||||
expect(changeSpy.callCount).to.equal(1);
|
||||
expect(changeSpy.firstCall.args[0]).to.be.an.instanceof(Date);
|
||||
expect(changeSpy.firstCall.args[0].toISOString()).to.equal(moment('2022-02-28T00:00:00.000Z').toISOString());
|
||||
expect(changeSpy.firstCall.args[0].toISOString()).to.equal(moment('2022-02-28T00:00:00.000').toISOString());
|
||||
});
|
||||
|
||||
it('updates date via datepicker selection', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
const onChange = (newDate) => {
|
||||
this.set('date', newDate);
|
||||
|
@ -92,28 +92,28 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
this.set('onChange', changeSpy);
|
||||
|
||||
await render(hbs`<GhDatePicker @value={{this.date}} @onChange={{this.onChange}} />`);
|
||||
await datepickerSelect('[data-test-date-picker-trigger]', moment('2022-02-27T13:00:00.000Z').toDate());
|
||||
await datepickerSelect('[data-test-date-picker-trigger]', moment('2022-02-27T13:00:00.000').toDate());
|
||||
|
||||
expect(find('[data-test-date-picker-input]')).to.have.value('2022-02-27');
|
||||
|
||||
expect(changeSpy.callCount).to.equal(1);
|
||||
expect(changeSpy.firstCall.args[0]).to.be.an.instanceof(Date);
|
||||
expect(changeSpy.firstCall.args[0].toISOString()).to.equal(moment('2022-02-27T00:00:00.000Z').toISOString());
|
||||
expect(changeSpy.firstCall.args[0].toISOString()).to.equal(moment('2022-02-27T00:00:00.000').toISOString());
|
||||
});
|
||||
|
||||
it('updates when @value is changed externally', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
await render(hbs`<GhDatePicker @value={{this.date}} />`);
|
||||
expect(find('[data-test-date-picker-input]'), 'date input').to.have.value('2022-02-22');
|
||||
|
||||
this.set('date', moment('2022-02-28 10:00:00.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-28 10:00:00.000')).toDate();
|
||||
|
||||
expect(find('[data-test-date-picker-input]'), 'date input').to.have.value('2022-02-28');
|
||||
});
|
||||
|
||||
it('updates when @value is changed externally when we have a scratch date', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
await render(hbs`<GhDatePicker @value={{this.date}} />`);
|
||||
expect(find('[data-test-date-picker-input]'), 'date input').to.have.value('2022-02-22');
|
||||
|
@ -121,12 +121,12 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
await fillIn('[data-test-date-picker-input]', '2022-02-27');
|
||||
expect(find('[data-test-date-picker-input]'), 'date input').to.have.value('2022-02-27');
|
||||
|
||||
this.set('date', moment('2022-02-28 10:00:00.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-28 10:00:00.000')).toDate();
|
||||
expect(find('[data-test-date-picker-input]'), 'date input').to.have.value('2022-02-28');
|
||||
});
|
||||
|
||||
it('calls @onInput on input events', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
const inputSpy = sinon.spy();
|
||||
this.set('onInput', inputSpy);
|
||||
|
@ -139,7 +139,7 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
});
|
||||
|
||||
it('calls @onKeydown on input keydown events', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
const keydownSpy = sinon.spy();
|
||||
this.set('onKeydown', keydownSpy);
|
||||
|
@ -152,7 +152,7 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
});
|
||||
|
||||
it('calls @onBlur on input blur events', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
const blurSpy = sinon.spy();
|
||||
this.set('onBlur', blurSpy);
|
||||
|
@ -166,7 +166,7 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
});
|
||||
|
||||
it('resets input value on Escape', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
const changeSpy = sinon.spy();
|
||||
this.set('onChange', changeSpy);
|
||||
|
@ -180,7 +180,7 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
});
|
||||
|
||||
it('handles invalid date input', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
const changeSpy = sinon.spy();
|
||||
this.set('onChange', changeSpy);
|
||||
|
@ -201,7 +201,7 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
});
|
||||
|
||||
it('handles invalid date format input', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
|
||||
const changeSpy = sinon.spy();
|
||||
this.set('onChange', changeSpy);
|
||||
|
@ -260,9 +260,9 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
|
||||
describe('min/max', function () {
|
||||
it('disables datepicker dates outside of range', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('minDate', moment('2022-02-11 12:00:00.000Z').toDate());
|
||||
this.set('maxDate', moment('2022-02-24 12:00:00.000Z').toDate());
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
this.set('minDate', moment('2022-02-11 12:00:00.000').toDate());
|
||||
this.set('maxDate', moment('2022-02-24 12:00:00.000').toDate());
|
||||
|
||||
await render(hbs`<GhDatePicker @value={{this.date}} @minDate={{this.minDate}} @maxDate={{this.maxDate}} />`);
|
||||
await click('[data-test-date-picker-trigger]');
|
||||
|
@ -272,8 +272,8 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
});
|
||||
|
||||
it('errors when date input is earlier than min', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('minDate', moment('2022-02-11 12:00:00.000Z').toDate());
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
this.set('minDate', moment('2022-02-11 12:00:00.000').toDate());
|
||||
|
||||
const changeSpy = sinon.spy();
|
||||
this.set('onChange', changeSpy);
|
||||
|
@ -295,8 +295,8 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
});
|
||||
|
||||
it('allows for min date error override', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('minDate', moment('2022-02-11 12:00:00.000Z').toDate());
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
this.set('minDate', moment('2022-02-11 12:00:00.000').toDate());
|
||||
|
||||
await render(hbs`<GhDatePicker @value={{this.date}} @minDate={{this.minDate}} @minDateError="Must be in the future" @onChange={{this.onChange}} />`);
|
||||
|
||||
|
@ -307,8 +307,8 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
});
|
||||
|
||||
it('errors when date input is later than max', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('maxDate', moment('2022-02-25 12:00:00.000Z').toDate());
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
this.set('maxDate', moment('2022-02-25 12:00:00.000').toDate());
|
||||
|
||||
const changeSpy = sinon.spy();
|
||||
this.set('onChange', changeSpy);
|
||||
|
@ -330,8 +330,8 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
});
|
||||
|
||||
it('allows for max date error override', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('maxDate', moment('2022-02-25 12:00:00.000Z').toDate());
|
||||
this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
this.set('maxDate', moment('2022-02-25 12:00:00.000').toDate());
|
||||
|
||||
await render(hbs`<GhDatePicker @value={{this.date}} @maxDate={{this.maxDate}} @maxDateError="Must be in the past" @onChange={{this.onChange}} />`);
|
||||
|
||||
|
@ -345,11 +345,11 @@ describe('Integration: Component: gh-date-picker', function () {
|
|||
describe('block invocation', function () {
|
||||
it('exposes Nav and Days components', async function () {
|
||||
clock = sinon.useFakeTimers({
|
||||
now: moment('2022-02-02 22:22:22.000Z').toDate()
|
||||
now: moment('2022-02-02 22:22:22.000').toDate()
|
||||
});
|
||||
|
||||
this.set('date', moment('2022-02-02 22:22:22.000Z')).toDate();
|
||||
this.set('maxDate', moment('2022-02-05 12:00:00.000Z').toDate());
|
||||
this.set('date', moment('2022-02-02 22:22:22.000')).toDate();
|
||||
this.set('maxDate', moment('2022-02-05 12:00:00.000').toDate());
|
||||
|
||||
await render(hbs`<GhDatePicker @value={{this.date}} @maxDate={{this.maxDate}} as |dp|><dp.Nav /><dp.Days /></GhDatePicker>`);
|
||||
|
||||
|
|
|
@ -8,12 +8,15 @@ import {describe, it} from 'mocha';
|
|||
import {expect} from 'chai';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
let timezone = 'UTC';
|
||||
class SettingsStub extends Service {
|
||||
timezone = 'Etc/UTC';
|
||||
get timezone() {
|
||||
return timezone;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
if (key === 'timezone') {
|
||||
return this.timezone;
|
||||
return timezone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +26,7 @@ describe('Integration: Component: gh-date-time-picker', function () {
|
|||
let clock;
|
||||
|
||||
beforeEach(async function () {
|
||||
timezone = 'UTC';
|
||||
this.owner.register('service:settings', SettingsStub);
|
||||
});
|
||||
|
||||
|
@ -50,7 +54,7 @@ describe('Integration: Component: gh-date-time-picker', function () {
|
|||
});
|
||||
|
||||
it('shows passed in @date value', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', '2022-02-22 22:22');
|
||||
|
||||
await render(hbs`<GhDateTimePicker @date={{this.date}} />`);
|
||||
expect(find('[data-test-date-time-picker-date-input]'), 'date input').to.have.value('2022-02-22');
|
||||
|
@ -67,11 +71,13 @@ describe('Integration: Component: gh-date-time-picker', function () {
|
|||
});
|
||||
|
||||
it('can update date via date input', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('time', '22:22');
|
||||
this.set('date','2022-02-22');
|
||||
this.set('time', '22:22'); // = blog timezone
|
||||
|
||||
this.set('updateDate', (newDate) => {
|
||||
expect(moment(newDate).toISOString()).to.equal('2022-02-28T00:00:00.000Z');
|
||||
// Note: the newDate should be 2022-02-28 in the current blog timezone, this is not the same timezone as the user timezone
|
||||
// Blog timezone is UTC, so ending of Z is needed here
|
||||
expect(moment.utc(newDate).toISOString()).to.equal('2022-02-28T00:00:00.000Z');
|
||||
this.set('date', newDate);
|
||||
});
|
||||
this.set('updateTime', (newTime) => {
|
||||
|
@ -84,12 +90,38 @@ describe('Integration: Component: gh-date-time-picker', function () {
|
|||
await blur('[data-test-date-time-picker-date-input]');
|
||||
});
|
||||
|
||||
it('can update time via time input', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
it('can update date via date input with +12 timezone', async function () {
|
||||
timezone = 'Pacific/Kwajalein'; // +12
|
||||
|
||||
// Current date/time doesn't really matter
|
||||
this.set('date','2022-02-22');
|
||||
this.set('time', '22:22');
|
||||
|
||||
this.set('updateDate', (newDate) => {
|
||||
expect(moment(newDate).toISOString()).to.equal('2022-02-28T00:00:00.000Z');
|
||||
// Note: the newDate should be 2022-02-28 in the current blog timezone, this is not the same timezone as the user timezone
|
||||
// Blog timezone is +12
|
||||
expect(moment.utc(newDate).toISOString()).to.equal('2022-02-27T12:00:00.000Z');
|
||||
this.set('date', newDate);
|
||||
});
|
||||
|
||||
this.set('updateTime', (newTime) => {
|
||||
expect(newTime).to.equal('22:22');
|
||||
this.set('time', newTime);
|
||||
});
|
||||
|
||||
await render(hbs`<GhDateTimePicker @date={{this.date}} @time={{this.time}} @setDate={{this.updateDate}} @setTime={{this.updateTime}} />`);
|
||||
|
||||
// We enter 2022-02-28. In UTC this is 2022-02-28 00:00:00.000Z. But we should call updateDate to be 2022-02-28 in the blog timezone
|
||||
await fillIn('[data-test-date-time-picker-date-input]', '2022-02-28');
|
||||
await blur('[data-test-date-time-picker-date-input]');
|
||||
});
|
||||
|
||||
it('can update time via time input', async function () {
|
||||
this.set('date', '2022-02-22');
|
||||
this.set('time', '22:22');
|
||||
|
||||
this.set('updateDate', (newDate) => {
|
||||
expect(moment.utc(newDate).toISOString()).to.equal('2022-02-28T00:00:00.000');
|
||||
this.set('date', newDate);
|
||||
});
|
||||
this.set('updateTime', (newTime) => {
|
||||
|
@ -103,11 +135,31 @@ describe('Integration: Component: gh-date-time-picker', function () {
|
|||
});
|
||||
|
||||
it('can update date via datepicker', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', '2022-02-22');
|
||||
this.set('time', '12:00');
|
||||
|
||||
this.set('updateDate', (newDate) => {
|
||||
expect(moment(newDate).toISOString()).to.equal('2022-02-27T00:00:00.000Z');
|
||||
expect(moment.utc(newDate).toISOString()).to.equal('2022-02-27T00:00:00.000Z');
|
||||
this.set('date', newDate);
|
||||
});
|
||||
this.set('updateTime', (newTime) => {
|
||||
expect(newTime).to.equal('12:00');
|
||||
this.set('time', newTime);
|
||||
});
|
||||
|
||||
await render(hbs`<GhDateTimePicker @date={{this.date}} @time={{this.time}} @setDate={{this.updateDate}} @setTime={{this.updateTime}} />`);
|
||||
await datepickerSelect('[data-test-date-time-picker-datepicker]', moment('2022-02-27T13:00:00.000Z').toDate());
|
||||
});
|
||||
|
||||
it('can update date via datepicker with +12 timezone', async function () {
|
||||
timezone = 'Pacific/Kwajalein'; // +12
|
||||
|
||||
this.set('date', '2022-02-22');
|
||||
this.set('time', '12:00');
|
||||
|
||||
this.set('updateDate', (newDate) => {
|
||||
// 12 hours earlier in UTC
|
||||
expect(moment.utc(newDate).toISOString()).to.equal('2022-02-26T12:00:00.000Z');
|
||||
this.set('date', newDate);
|
||||
});
|
||||
this.set('updateTime', (newTime) => {
|
||||
|
@ -120,21 +172,53 @@ describe('Integration: Component: gh-date-time-picker', function () {
|
|||
});
|
||||
|
||||
it('updates when @date is changed externally', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date','2022-02-22');
|
||||
this.set('time', '12:00');
|
||||
|
||||
await render(hbs`<GhDateTimePicker @date={{this.date}} @time={{this.time}} />`);
|
||||
expect(find('[data-test-date-time-picker-date-input]'), 'date input').to.have.value('2022-02-22');
|
||||
expect(find('[data-test-date-time-picker-time-input]'), 'time input').to.have.value('12:00');
|
||||
|
||||
this.set('date', moment('2022-02-28 10:00:00.000Z')).toDate();
|
||||
this.set('date', '2022-02-28');
|
||||
|
||||
expect(find('[data-test-date-time-picker-date-input]'), 'date input').to.have.value('2022-02-28');
|
||||
expect(find('[data-test-date-time-picker-time-input]'), 'time input').to.have.value('12:00');
|
||||
});
|
||||
|
||||
it('updates when @date is changed externally with +12 timezone', async function () {
|
||||
timezone = 'Pacific/Kwajalein'; // +12
|
||||
|
||||
this.set('date','2022-02-22');
|
||||
this.set('time', '12:00');
|
||||
|
||||
await render(hbs`<GhDateTimePicker @date={{this.date}} @time={{this.time}} />`);
|
||||
expect(find('[data-test-date-time-picker-date-input]'), 'date input').to.have.value('2022-02-22');
|
||||
expect(find('[data-test-date-time-picker-time-input]'), 'time input').to.have.value('12:00');
|
||||
|
||||
this.set('date', '2022-02-28');
|
||||
|
||||
expect(find('[data-test-date-time-picker-date-input]'), 'date input').to.have.value('2022-02-28');
|
||||
expect(find('[data-test-date-time-picker-time-input]'), 'time input').to.have.value('12:00');
|
||||
});
|
||||
|
||||
it('updates when @date is changed externally with -11 timezone', async function () {
|
||||
timezone = 'Pacific/Pago_Pago'; // -11
|
||||
|
||||
this.set('date','2022-02-22');
|
||||
this.set('time', '12:00');
|
||||
|
||||
await render(hbs`<GhDateTimePicker @date={{this.date}} @time={{this.time}} />`);
|
||||
expect(find('[data-test-date-time-picker-date-input]'), 'date input').to.have.value('2022-02-22');
|
||||
expect(find('[data-test-date-time-picker-time-input]'), 'time input').to.have.value('12:00');
|
||||
|
||||
this.set('date', '2022-02-28');
|
||||
|
||||
expect(find('[data-test-date-time-picker-date-input]'), 'date input').to.have.value('2022-02-28');
|
||||
expect(find('[data-test-date-time-picker-time-input]'), 'time input').to.have.value('12:00');
|
||||
});
|
||||
|
||||
it('updates when @time is changed externally', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', '2022-02-22');
|
||||
this.set('time', '12:00');
|
||||
|
||||
await render(hbs`<GhDateTimePicker @date={{this.date}} @time={{this.time}} />`);
|
||||
|
@ -148,7 +232,7 @@ describe('Integration: Component: gh-date-time-picker', function () {
|
|||
});
|
||||
|
||||
it('handles invalid date input', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', '2022-02-22');
|
||||
this.set('time', '12:00');
|
||||
|
||||
const dateSpy = sinon.spy();
|
||||
|
@ -174,7 +258,7 @@ describe('Integration: Component: gh-date-time-picker', function () {
|
|||
|
||||
// TODO: move time format handling into component?
|
||||
// it('handles invalid time input', async function () {
|
||||
// this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
// this.set('date', '2022-02-22');
|
||||
// this.set('time', '12:00');
|
||||
|
||||
// const dateSpy = sinon.spy();
|
||||
|
@ -200,10 +284,10 @@ describe('Integration: Component: gh-date-time-picker', function () {
|
|||
|
||||
describe('min/max', function () {
|
||||
it('disables datepicker dates outside of range', async function () {
|
||||
this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
this.set('date', '2022-02-22');
|
||||
this.set('time', '12:00');
|
||||
this.set('minDate', moment('2022-02-11 12:00:00.000Z').toDate());
|
||||
this.set('maxDate', moment('2022-02-24 12:00:00.000Z').toDate());
|
||||
this.set('minDate', moment('2022-02-11 12:00:00.000').toDate());
|
||||
this.set('maxDate', moment('2022-02-24 12:00:00.000').toDate());
|
||||
|
||||
await render(hbs`<GhDateTimePicker @date={{this.date}} @time={{this.time}} @minDate={{this.minDate}} @maxDate={{this.maxDate}} />`);
|
||||
await click('[data-test-date-time-picker-datepicker]');
|
||||
|
@ -212,11 +296,48 @@ describe('Integration: Component: gh-date-time-picker', function () {
|
|||
expect(find('[data-date="2022-02-25"]')).to.have.attribute('disabled');
|
||||
});
|
||||
|
||||
it('Handles timezone of minimum date correctly', async function () {
|
||||
clock = sinon.useFakeTimers({
|
||||
now: moment('2022-01-01 10:00:00.000Z').toDate()
|
||||
});
|
||||
|
||||
// Blog timezone is -11, so current date over there is 2021-12-31
|
||||
timezone = 'Pacific/Pago_Pago'; // GMT-11
|
||||
|
||||
this.set('date', '2021-12-31');
|
||||
this.set('time', '22:00');
|
||||
this.set('minDate', 'now');
|
||||
|
||||
await render(hbs`<GhDateTimePicker @date={{this.date}} @time={{this.time}} @minDate={{this.minDate}} />`);
|
||||
await click('[data-test-date-time-picker-datepicker]');
|
||||
|
||||
expect(find('[data-date="2021-12-30"]')).to.have.attribute('disabled');
|
||||
expect(find('[data-date="2021-12-31"]')).not.to.have.attribute('disabled');
|
||||
});
|
||||
|
||||
it('Handles timezone of maximum date correctly', async function () {
|
||||
clock = sinon.useFakeTimers({
|
||||
now: moment('2021-12-31 10:00:00.000Z').toDate()
|
||||
});
|
||||
|
||||
// Blog timezone is -11, so current date over there is 2021-12-30
|
||||
timezone = 'Pacific/Pago_Pago'; // GMT-11
|
||||
|
||||
this.set('date', '2021-12-30');
|
||||
this.set('time', '22:00');
|
||||
this.set('maxDate', 'now');
|
||||
|
||||
await render(hbs`<GhDateTimePicker @date={{this.date}} @time={{this.time}} @maxDate={{this.maxDate}} />`);
|
||||
await click('[data-test-date-time-picker-datepicker]');
|
||||
expect(find('[data-date="2021-12-30"]')).not.to.have.attribute('disabled');
|
||||
expect(find('[data-date="2021-12-31"]')).to.have.attribute('disabled');
|
||||
});
|
||||
|
||||
// TODO: move date validation into component?
|
||||
// it('errors when date input is earlier than min', async function () {
|
||||
// this.set('date', moment('2022-02-22 22:22:22.000Z')).toDate();
|
||||
// this.set('date', moment('2022-02-22 22:22:22.000')).toDate();
|
||||
// this.set('time', '12:00');
|
||||
// this.set('minDate', moment('2022-02-11 12:00:00.000Z').toDate());
|
||||
// this.set('minDate', moment('2022-02-11 12:00:00.000').toDate());
|
||||
|
||||
// const dateSpy = sinon.spy();
|
||||
// this.set('updateDate', dateSpy);
|
||||
|
|
Loading…
Add table
Reference in a new issue