mirror of
https://github.com/ansible/awx.git
synced 2026-02-26 15:36:04 -03:30
uses pf date and time picker to schedule form
This commit is contained in:
@@ -35,6 +35,8 @@
|
|||||||
{
|
{
|
||||||
"markupOnly": true,
|
"markupOnly": true,
|
||||||
"ignoreAttribute": [
|
"ignoreAttribute": [
|
||||||
|
"dateFieldName",
|
||||||
|
"timeFieldName",
|
||||||
"to",
|
"to",
|
||||||
"streamType",
|
"streamType",
|
||||||
"path",
|
"path",
|
||||||
@@ -85,7 +87,7 @@
|
|||||||
"data-cy",
|
"data-cy",
|
||||||
"fieldName"
|
"fieldName"
|
||||||
],
|
],
|
||||||
"ignore": ["Ansible", "Tower", "JSON", "YAML", "lg"],
|
"ignore": ["Ansible", "Tower", "JSON", "YAML", "lg", "hh:mm AM/PM"],
|
||||||
"ignoreComponent": [
|
"ignoreComponent": [
|
||||||
"AboutModal",
|
"AboutModal",
|
||||||
"code",
|
"code",
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ function ScheduleAdd({
|
|||||||
end,
|
end,
|
||||||
frequency,
|
frequency,
|
||||||
interval,
|
interval,
|
||||||
startDateTime,
|
|
||||||
timezone,
|
timezone,
|
||||||
occurrences,
|
occurrences,
|
||||||
runOn,
|
runOn,
|
||||||
@@ -49,7 +48,6 @@ function ScheduleAdd({
|
|||||||
runOnTheMonth,
|
runOnTheMonth,
|
||||||
runOnDayMonth,
|
runOnDayMonth,
|
||||||
runOnDayNumber,
|
runOnDayNumber,
|
||||||
endDateTime,
|
|
||||||
runOnTheOccurrence,
|
runOnTheOccurrence,
|
||||||
credentials,
|
credentials,
|
||||||
daysOfWeek,
|
daysOfWeek,
|
||||||
@@ -100,6 +98,10 @@ function ScheduleAdd({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delete requestData.startDate;
|
||||||
|
delete requestData.startTime;
|
||||||
|
delete requestData.endDate;
|
||||||
|
delete requestData.endTime;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { id: scheduleId },
|
data: { id: scheduleId },
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
frequency: 'none',
|
frequency: 'none',
|
||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run once schedule',
|
name: 'Run once schedule',
|
||||||
startDateTime: '2020-03-25T10:00:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:00:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -103,7 +104,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
interval: 10,
|
interval: 10,
|
||||||
name: 'Run every 10 minutes 10 times',
|
name: 'Run every 10 minutes 10 times',
|
||||||
occurrences: 10,
|
occurrences: 10,
|
||||||
startDateTime: '2020-03-25T10:30:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:30:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -120,11 +122,13 @@ describe('<ScheduleAdd />', () => {
|
|||||||
wrapper.find('Formik').invoke('onSubmit')({
|
wrapper.find('Formik').invoke('onSubmit')({
|
||||||
description: 'test description',
|
description: 'test description',
|
||||||
end: 'onDate',
|
end: 'onDate',
|
||||||
endDateTime: '2020-03-26T10:45:00',
|
endDate: '2020-03-26',
|
||||||
|
endTime: '10:45:00',
|
||||||
frequency: 'hour',
|
frequency: 'hour',
|
||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run every hour until date',
|
name: 'Run every hour until date',
|
||||||
startDateTime: '2020-03-25T10:45:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:45:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -144,7 +148,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
frequency: 'day',
|
frequency: 'day',
|
||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run daily',
|
name: 'Run daily',
|
||||||
startDateTime: '2020-03-25T10:45:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:45:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -166,7 +171,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run weekly on mon/wed/fri',
|
name: 'Run weekly on mon/wed/fri',
|
||||||
occurrences: 1,
|
occurrences: 1,
|
||||||
startDateTime: '2020-03-25T10:45:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:45:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -188,7 +194,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
occurrences: 1,
|
occurrences: 1,
|
||||||
runOn: 'day',
|
runOn: 'day',
|
||||||
runOnDayNumber: 1,
|
runOnDayNumber: 1,
|
||||||
startDateTime: '2020-04-01T10:45',
|
startTime: '10:45',
|
||||||
|
startDate: '2020-04-01',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -205,7 +212,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
wrapper.find('Formik').invoke('onSubmit')({
|
wrapper.find('Formik').invoke('onSubmit')({
|
||||||
description: 'test description',
|
description: 'test description',
|
||||||
end: 'never',
|
end: 'never',
|
||||||
endDateTime: '2020-03-26T11:00:00',
|
endDate: '2020-03-26',
|
||||||
|
endTime: '11:00:00',
|
||||||
frequency: 'month',
|
frequency: 'month',
|
||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run monthly on the last Tuesday',
|
name: 'Run monthly on the last Tuesday',
|
||||||
@@ -213,7 +221,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
runOn: 'the',
|
runOn: 'the',
|
||||||
runOnTheDay: 'tuesday',
|
runOnTheDay: 'tuesday',
|
||||||
runOnTheOccurrence: -1,
|
runOnTheOccurrence: -1,
|
||||||
startDateTime: '2020-03-31T11:00',
|
startDate: '2020-03-31',
|
||||||
|
startTime: '11:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -237,7 +246,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
runOn: 'day',
|
runOn: 'day',
|
||||||
runOnDayMonth: 3,
|
runOnDayMonth: 3,
|
||||||
runOnDayNumber: 1,
|
runOnDayNumber: 1,
|
||||||
startDateTime: '2020-03-01T00:00',
|
startDate: '2020-03-01',
|
||||||
|
startTime: '00:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -262,7 +272,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
runOnTheOccurrence: 2,
|
runOnTheOccurrence: 2,
|
||||||
runOnTheDay: 'friday',
|
runOnTheDay: 'friday',
|
||||||
runOnTheMonth: 4,
|
runOnTheMonth: 4,
|
||||||
startDateTime: '2020-04-10T11:15',
|
startDate: '2020-04-10',
|
||||||
|
startTime: '11:15',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -287,7 +298,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
runOnTheOccurrence: 1,
|
runOnTheOccurrence: 1,
|
||||||
runOnTheDay: 'weekday',
|
runOnTheDay: 'weekday',
|
||||||
runOnTheMonth: 10,
|
runOnTheMonth: 10,
|
||||||
startDateTime: '2020-04-10T11:15',
|
startDate: '2020-04-10',
|
||||||
|
startTime: '11:15',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -371,7 +383,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
wrapper.find('Formik').invoke('onSubmit')({
|
wrapper.find('Formik').invoke('onSubmit')({
|
||||||
name: 'Schedule',
|
name: 'Schedule',
|
||||||
end: 'never',
|
end: 'never',
|
||||||
endDateTime: '2021-01-29T14:15:00',
|
endDate: '2021-01-29',
|
||||||
|
endTime: '14:15:00',
|
||||||
frequency: 'none',
|
frequency: 'none',
|
||||||
occurrences: 1,
|
occurrences: 1,
|
||||||
runOn: 'day',
|
runOn: 'day',
|
||||||
@@ -386,7 +399,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
{ name: 'cred 1', id: 10 },
|
{ name: 'cred 1', id: 10 },
|
||||||
{ name: 'cred 2', id: 20 },
|
{ name: 'cred 2', id: 20 },
|
||||||
],
|
],
|
||||||
startDateTime: '2021-01-28T14:15:00',
|
startDate: '2021-01-28',
|
||||||
|
startTime: '14:15:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -457,7 +471,8 @@ describe('<ScheduleAdd />', () => {
|
|||||||
frequency: 'none',
|
frequency: 'none',
|
||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run once schedule',
|
name: 'Run once schedule',
|
||||||
startDateTime: '2020-03-25T10:00:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:00:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ function ScheduleEdit({
|
|||||||
end,
|
end,
|
||||||
frequency,
|
frequency,
|
||||||
interval,
|
interval,
|
||||||
startDateTime,
|
|
||||||
timezone,
|
timezone,
|
||||||
occurences,
|
occurences,
|
||||||
runOn,
|
runOn,
|
||||||
@@ -49,7 +48,6 @@ function ScheduleEdit({
|
|||||||
runOnTheMonth,
|
runOnTheMonth,
|
||||||
runOnDayMonth,
|
runOnDayMonth,
|
||||||
runOnDayNumber,
|
runOnDayNumber,
|
||||||
endDateTime,
|
|
||||||
runOnTheOccurence,
|
runOnTheOccurence,
|
||||||
daysOfWeek,
|
daysOfWeek,
|
||||||
...submitValues
|
...submitValues
|
||||||
@@ -98,6 +96,10 @@ function ScheduleEdit({
|
|||||||
...submitValues,
|
...submitValues,
|
||||||
rrule: rule.toString().replace(/\n/g, ' '),
|
rrule: rule.toString().replace(/\n/g, ' '),
|
||||||
};
|
};
|
||||||
|
delete requestData.startDate;
|
||||||
|
delete requestData.startTime;
|
||||||
|
delete requestData.endDate;
|
||||||
|
delete requestData.endTime;
|
||||||
|
|
||||||
if (Object.keys(values).includes('daysToKeep')) {
|
if (Object.keys(values).includes('daysToKeep')) {
|
||||||
if (!requestData.extra_data) {
|
if (!requestData.extra_data) {
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ import ScheduleEdit from './ScheduleEdit';
|
|||||||
jest.mock('../../../api');
|
jest.mock('../../../api');
|
||||||
|
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
const now = new Date();
|
||||||
|
const closestQuarterHour = new Date(Math.ceil(now.getTime() / 900000) * 900000);
|
||||||
|
const tomorrow = new Date(closestQuarterHour);
|
||||||
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||||
const mockSchedule = {
|
const mockSchedule = {
|
||||||
rrule:
|
rrule:
|
||||||
'DTSTART;TZID=America/New_York:20200402T144500 RRULE:INTERVAL=1;COUNT=1;FREQ=MINUTELY',
|
'DTSTART;TZID=America/New_York:20200402T144500 RRULE:INTERVAL=1;COUNT=1;FREQ=MINUTELY',
|
||||||
@@ -202,7 +205,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
frequency: 'none',
|
frequency: 'none',
|
||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run once schedule',
|
name: 'Run once schedule',
|
||||||
startDateTime: '2020-03-25T10:00:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:00:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -223,7 +227,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
interval: 10,
|
interval: 10,
|
||||||
name: 'Run every 10 minutes 10 times',
|
name: 'Run every 10 minutes 10 times',
|
||||||
occurrences: 10,
|
occurrences: 10,
|
||||||
startDateTime: '2020-03-25T10:30:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:30:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -241,11 +246,13 @@ describe('<ScheduleEdit />', () => {
|
|||||||
wrapper.find('Formik').invoke('onSubmit')({
|
wrapper.find('Formik').invoke('onSubmit')({
|
||||||
description: 'test description',
|
description: 'test description',
|
||||||
end: 'onDate',
|
end: 'onDate',
|
||||||
endDateTime: '2020-03-26T10:45:00',
|
endDate: '2020-03-26',
|
||||||
|
endTime: '10:45:00',
|
||||||
frequency: 'hour',
|
frequency: 'hour',
|
||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run every hour until date',
|
name: 'Run every hour until date',
|
||||||
startDateTime: '2020-03-25T10:45:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:45:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -265,7 +272,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
frequency: 'day',
|
frequency: 'day',
|
||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run daily',
|
name: 'Run daily',
|
||||||
startDateTime: '2020-03-25T10:45:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:45:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -287,7 +295,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run weekly on mon/wed/fri',
|
name: 'Run weekly on mon/wed/fri',
|
||||||
occurrences: 1,
|
occurrences: 1,
|
||||||
startDateTime: '2020-03-25T10:45:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:45:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -310,7 +319,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
occurrences: 1,
|
occurrences: 1,
|
||||||
runOn: 'day',
|
runOn: 'day',
|
||||||
runOnDayNumber: 1,
|
runOnDayNumber: 1,
|
||||||
startDateTime: '2020-04-01T10:45',
|
startDate: '2020-04-01',
|
||||||
|
startTime: '10:45',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -336,12 +346,14 @@ describe('<ScheduleEdit />', () => {
|
|||||||
runOn: 'the',
|
runOn: 'the',
|
||||||
runOnTheDay: 'tuesday',
|
runOnTheDay: 'tuesday',
|
||||||
runOnTheOccurrence: -1,
|
runOnTheOccurrence: -1,
|
||||||
startDateTime: '2020-03-31T11:00',
|
startDate: '2020-03-31',
|
||||||
|
startTime: '11:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
expect(SchedulesAPI.update).toHaveBeenCalledWith(27, {
|
expect(SchedulesAPI.update).toHaveBeenCalledWith(27, {
|
||||||
description: 'test description',
|
description: 'test description',
|
||||||
|
endDateTime: '2020-03-26T11:00:00',
|
||||||
name: 'Run monthly on the last Tuesday',
|
name: 'Run monthly on the last Tuesday',
|
||||||
extra_data: {},
|
extra_data: {},
|
||||||
occurrences: 1,
|
occurrences: 1,
|
||||||
@@ -362,7 +374,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
runOn: 'day',
|
runOn: 'day',
|
||||||
runOnDayMonth: 3,
|
runOnDayMonth: 3,
|
||||||
runOnDayNumber: 1,
|
runOnDayNumber: 1,
|
||||||
startDateTime: '2020-03-01T00:00',
|
startTime: '00:00',
|
||||||
|
startDate: '2020-03-01',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -388,7 +401,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
runOnTheOccurrence: 2,
|
runOnTheOccurrence: 2,
|
||||||
runOnTheDay: 'friday',
|
runOnTheDay: 'friday',
|
||||||
runOnTheMonth: 4,
|
runOnTheMonth: 4,
|
||||||
startDateTime: '2020-04-10T11:15',
|
startTime: '11:15',
|
||||||
|
startDate: '2020-04-10',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -415,7 +429,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
runOnTheOccurrence: 1,
|
runOnTheOccurrence: 1,
|
||||||
runOnTheDay: 'weekday',
|
runOnTheDay: 'weekday',
|
||||||
runOnTheMonth: 10,
|
runOnTheMonth: 10,
|
||||||
startDateTime: '2020-04-10T11:15',
|
startTime: '11:15',
|
||||||
|
startDate: '2020-04-10',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -526,7 +541,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
wrapper.find('Formik').invoke('onSubmit')({
|
wrapper.find('Formik').invoke('onSubmit')({
|
||||||
name: mockSchedule.name,
|
name: mockSchedule.name,
|
||||||
end: 'never',
|
end: 'never',
|
||||||
endDateTime: '2021-01-29T14:15:00',
|
endDate: '2021-01-29',
|
||||||
|
endTime: '14:15:00',
|
||||||
frequency: 'none',
|
frequency: 'none',
|
||||||
occurrences: 1,
|
occurrences: 1,
|
||||||
runOn: 'day',
|
runOn: 'day',
|
||||||
@@ -536,7 +552,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
runOnTheMonth: 1,
|
runOnTheMonth: 1,
|
||||||
runOnTheOccurrence: 1,
|
runOnTheOccurrence: 1,
|
||||||
skip_tags: '',
|
skip_tags: '',
|
||||||
startDateTime: '2021-01-28T14:15:00',
|
startDate: '2021-01-28',
|
||||||
|
startTime: '14:15:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
credentials: [
|
credentials: [
|
||||||
{ id: 3, name: 'Credential 3', kind: 'ssh', url: '' },
|
{ id: 3, name: 'Credential 3', kind: 'ssh', url: '' },
|
||||||
@@ -622,7 +639,10 @@ describe('<ScheduleEdit />', () => {
|
|||||||
await act(async () =>
|
await act(async () =>
|
||||||
wrapper.find('Button[aria-label="Save"]').prop('onClick')()
|
wrapper.find('Button[aria-label="Save"]').prop('onClick')()
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(SchedulesAPI.update).toBeCalledWith(27, {
|
expect(SchedulesAPI.update).toBeCalledWith(27, {
|
||||||
|
endDateTime: undefined,
|
||||||
|
startDateTime: undefined,
|
||||||
description: '',
|
description: '',
|
||||||
extra_data: {},
|
extra_data: {},
|
||||||
occurrences: 1,
|
occurrences: 1,
|
||||||
@@ -630,7 +650,7 @@ describe('<ScheduleEdit />', () => {
|
|||||||
name: 'foo',
|
name: 'foo',
|
||||||
inventory: 702,
|
inventory: 702,
|
||||||
rrule:
|
rrule:
|
||||||
'DTSTART;TZID=America/New_York:20200402T144500 RRULE:INTERVAL=1;COUNT=1;FREQ=MINUTELY',
|
'DTSTART;TZID=America/New_York:20200402T184500 RRULE:INTERVAL=1;COUNT=1;FREQ=MINUTELY',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
test('should submit survey with default values properly, without opening prompt wizard', async () => {
|
test('should submit survey with default values properly, without opening prompt wizard', async () => {
|
||||||
@@ -728,7 +748,8 @@ describe('<ScheduleEdit />', () => {
|
|||||||
frequency: 'none',
|
frequency: 'none',
|
||||||
interval: 1,
|
interval: 1,
|
||||||
name: 'Run once schedule',
|
name: 'Run once schedule',
|
||||||
startDateTime: '2020-03-25T10:00:00',
|
startDate: '2020-03-25',
|
||||||
|
startTime: '10:00:00',
|
||||||
timezone: 'America/New_York',
|
timezone: 'America/New_York',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { useField } from 'formik';
|
||||||
|
import {
|
||||||
|
DatePicker,
|
||||||
|
isValidDate,
|
||||||
|
yyyyMMddFormat,
|
||||||
|
TimePicker,
|
||||||
|
FormGroup,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { required, validateTime, combine } from '../../../util/validators';
|
||||||
|
|
||||||
|
const DateTimeGroup = styled.span`
|
||||||
|
display: flex;
|
||||||
|
`;
|
||||||
|
function DateTimePicker({ dateFieldName, timeFieldName, label }) {
|
||||||
|
const [dateField, dateMeta, dateHelpers] = useField({
|
||||||
|
name: `${dateFieldName}`,
|
||||||
|
validate: combine([required(null), isValidDate]),
|
||||||
|
});
|
||||||
|
const [timeField, timeMeta, timeHelpers] = useField({
|
||||||
|
name: `${timeFieldName}`,
|
||||||
|
validate: combine([required(null), validateTime()]),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onDateChange = (inputDate, newDate) => {
|
||||||
|
dateHelpers.setTouched();
|
||||||
|
if (isValidDate(newDate) && inputDate === yyyyMMddFormat(newDate)) {
|
||||||
|
dateHelpers.setValue(new Date(newDate).toISOString().split('T')[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormGroup
|
||||||
|
fieldId={`schedule-${label}-datetime`}
|
||||||
|
helperTextInvalid={dateMeta.error || timeMeta.error}
|
||||||
|
isRequired
|
||||||
|
validated={
|
||||||
|
(!dateMeta.touched || !dateMeta.error) &&
|
||||||
|
(!timeMeta.touched || !timeMeta.error)
|
||||||
|
? 'default'
|
||||||
|
: 'error'
|
||||||
|
}
|
||||||
|
label={`${label} date/time`}
|
||||||
|
>
|
||||||
|
<DateTimeGroup>
|
||||||
|
<DatePicker
|
||||||
|
aria-label={t`${label} date`}
|
||||||
|
{...dateField}
|
||||||
|
value={dateField.value.split('T')[0]}
|
||||||
|
onChange={onDateChange}
|
||||||
|
/>
|
||||||
|
<TimePicker
|
||||||
|
placeholder="hh:mm AM/PM"
|
||||||
|
stepMinutes={15}
|
||||||
|
aria-label={t`${label} time`}
|
||||||
|
time={timeField.value}
|
||||||
|
{...timeField}
|
||||||
|
onChange={time => timeHelpers.setValue(time)}
|
||||||
|
/>
|
||||||
|
</DateTimeGroup>
|
||||||
|
</FormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DateTimePicker;
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { Formik } from 'formik';
|
||||||
|
|
||||||
|
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||||
|
import DateTimePicker from './DateTimePicker';
|
||||||
|
|
||||||
|
describe('<DateTimePicker/>', () => {
|
||||||
|
let wrapper;
|
||||||
|
test('should render properly', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<Formik
|
||||||
|
initialValues={{ startDate: '2021-05-26', startTime: '2:15 PM' }}
|
||||||
|
>
|
||||||
|
<DateTimePicker
|
||||||
|
dateFieldName="startDate"
|
||||||
|
timeFieldName="startTime"
|
||||||
|
label="Start"
|
||||||
|
/>
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(wrapper.find('DatePicker')).toHaveLength(1);
|
||||||
|
expect(wrapper.find('DatePicker').prop('value')).toBe('2021-05-26');
|
||||||
|
expect(wrapper.find('TimePicker')).toHaveLength(1);
|
||||||
|
expect(wrapper.find('TimePicker').prop('value')).toBe('2:15 PM');
|
||||||
|
});
|
||||||
|
test('should update values properly', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<Formik
|
||||||
|
initialValues={{ startDate: '2021-05-26', startTime: '2:15 PM' }}
|
||||||
|
>
|
||||||
|
<DateTimePicker
|
||||||
|
dateFieldName="startDate"
|
||||||
|
timeFieldName="startTime"
|
||||||
|
label="Start"
|
||||||
|
/>
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('DatePicker').prop('onChange')(
|
||||||
|
'2021-05-29',
|
||||||
|
new Date('Sat May 29 2021 00:00:00 GMT-0400 (Eastern Daylight Time)')
|
||||||
|
);
|
||||||
|
wrapper.find('TimePicker').prop('onChange')('7:15 PM');
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('DatePicker').prop('value')).toBe('2021-05-29');
|
||||||
|
expect(wrapper.find('TimePicker').prop('value')).toBe('7:15 PM');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
import AnsibleSelect from '../../AnsibleSelect';
|
import AnsibleSelect from '../../AnsibleSelect';
|
||||||
import FormField from '../../FormField';
|
import FormField from '../../FormField';
|
||||||
import { required } from '../../../util/validators';
|
import { required } from '../../../util/validators';
|
||||||
|
import DateTimePicker from './DateTimePicker';
|
||||||
|
|
||||||
const RunOnRadio = styled(Radio)`
|
const RunOnRadio = styled(Radio)`
|
||||||
label {
|
label {
|
||||||
@@ -74,9 +75,8 @@ const FrequencyDetailSubform = () => {
|
|||||||
const [runOnTheMonth] = useField({
|
const [runOnTheMonth] = useField({
|
||||||
name: 'runOnTheMonth',
|
name: 'runOnTheMonth',
|
||||||
});
|
});
|
||||||
const [startDateTime] = useField({
|
const [startDate] = useField('startDate');
|
||||||
name: 'startDateTime',
|
|
||||||
});
|
|
||||||
const [daysOfWeek, daysOfWeekMeta, daysOfWeekHelpers] = useField({
|
const [daysOfWeek, daysOfWeekMeta, daysOfWeekHelpers] = useField({
|
||||||
name: 'daysOfWeek',
|
name: 'daysOfWeek',
|
||||||
validate: required(t`Select a value for this field`),
|
validate: required(t`Select a value for this field`),
|
||||||
@@ -93,10 +93,6 @@ const FrequencyDetailSubform = () => {
|
|||||||
name: 'runOn',
|
name: 'runOn',
|
||||||
validate: required(t`Select a value for this field`),
|
validate: required(t`Select a value for this field`),
|
||||||
});
|
});
|
||||||
const [endDateTime, endDateTimeMeta] = useField({
|
|
||||||
name: 'endDateTime',
|
|
||||||
validate: required(t`Select a value for this field`),
|
|
||||||
});
|
|
||||||
const [frequency] = useField({
|
const [frequency] = useField({
|
||||||
name: 'frequency',
|
name: 'frequency',
|
||||||
});
|
});
|
||||||
@@ -317,7 +313,7 @@ const FrequencyDetailSubform = () => {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
{(frequency?.value === 'month' || frequency?.value === 'year') &&
|
{(frequency?.value === 'month' || frequency?.value === 'year') &&
|
||||||
!isNaN(new Date(startDateTime.value)) && (
|
!isNaN(new Date(startDate.value)) && (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
name="runOn"
|
name="runOn"
|
||||||
fieldId="schedule-run-on"
|
fieldId="schedule-run-on"
|
||||||
@@ -538,29 +534,14 @@ const FrequencyDetailSubform = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{end?.value === 'onDate' && (
|
{end?.value === 'onDate' && (
|
||||||
<FormGroup
|
<DateTimePicker
|
||||||
fieldId="schedule-end-datetime"
|
dateFieldName="endDate"
|
||||||
helperTextInvalid={endDateTimeMeta.error}
|
timeFieldName="endTime"
|
||||||
isRequired
|
label={t`End`}
|
||||||
validated={
|
/>
|
||||||
!endDateTimeMeta.touched || !endDateTimeMeta.error
|
|
||||||
? 'default'
|
|
||||||
: 'error'
|
|
||||||
}
|
|
||||||
label={t`End date/time`}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
className="pf-c-form-control"
|
|
||||||
type="datetime-local"
|
|
||||||
id="schedule-end-datetime"
|
|
||||||
step="1"
|
|
||||||
{...endDateTime}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
/* eslint-enable no-restricted-globals */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FrequencyDetailSubform;
|
export default FrequencyDetailSubform;
|
||||||
|
|||||||
@@ -22,12 +22,13 @@ import {
|
|||||||
SubFormLayout,
|
SubFormLayout,
|
||||||
FormFullWidthLayout,
|
FormFullWidthLayout,
|
||||||
} from '../../FormLayout';
|
} from '../../FormLayout';
|
||||||
import { dateToInputDateTime, formatDateStringUTC } from '../../../util/dates';
|
import { dateToInputDateTime } from '../../../util/dates';
|
||||||
import useRequest from '../../../util/useRequest';
|
import useRequest from '../../../util/useRequest';
|
||||||
import { required } from '../../../util/validators';
|
import { required } from '../../../util/validators';
|
||||||
import { parseVariableField } from '../../../util/yaml';
|
import { parseVariableField } from '../../../util/yaml';
|
||||||
import FrequencyDetailSubform from './FrequencyDetailSubform';
|
import FrequencyDetailSubform from './FrequencyDetailSubform';
|
||||||
import SchedulePromptableFields from './SchedulePromptableFields';
|
import SchedulePromptableFields from './SchedulePromptableFields';
|
||||||
|
import DateTimePicker from './DateTimePicker';
|
||||||
|
|
||||||
const generateRunOnTheDay = (days = []) => {
|
const generateRunOnTheDay = (days = []) => {
|
||||||
if (
|
if (
|
||||||
@@ -79,10 +80,6 @@ const generateRunOnTheDay = (days = []) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function ScheduleFormFields({ hasDaysToKeepField, zoneOptions }) {
|
function ScheduleFormFields({ hasDaysToKeepField, zoneOptions }) {
|
||||||
const [startDateTime, startDateTimeMeta] = useField({
|
|
||||||
name: 'startDateTime',
|
|
||||||
validate: required(t`Select a valid date and time for this field`),
|
|
||||||
});
|
|
||||||
const [timezone, timezoneMeta] = useField({
|
const [timezone, timezoneMeta] = useField({
|
||||||
name: 'timezone',
|
name: 'timezone',
|
||||||
validate: required(t`Select a value for this field`),
|
validate: required(t`Select a value for this field`),
|
||||||
@@ -108,25 +105,11 @@ function ScheduleFormFields({ hasDaysToKeepField, zoneOptions }) {
|
|||||||
name="description"
|
name="description"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
<FormGroup
|
<DateTimePicker
|
||||||
fieldId="schedule-start-datetime"
|
dateFieldName="startDate"
|
||||||
helperTextInvalid={startDateTimeMeta.error}
|
timeFieldName="startTime"
|
||||||
isRequired
|
label={t`Start`}
|
||||||
validated={
|
/>
|
||||||
!startDateTimeMeta.touched || !startDateTimeMeta.error
|
|
||||||
? 'default'
|
|
||||||
: 'error'
|
|
||||||
}
|
|
||||||
label={t`Start date/time`}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
className="pf-c-form-control"
|
|
||||||
type="datetime-local"
|
|
||||||
id="schedule-start-datetime"
|
|
||||||
step="1"
|
|
||||||
{...startDateTime}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
name="timezone"
|
name="timezone"
|
||||||
fieldId="schedule-timezone"
|
fieldId="schedule-timezone"
|
||||||
@@ -394,12 +377,15 @@ function ScheduleForm({
|
|||||||
) {
|
) {
|
||||||
showPromptButton = true;
|
showPromptButton = true;
|
||||||
}
|
}
|
||||||
|
const [currentDate, time] = dateToInputDateTime(closestQuarterHour);
|
||||||
|
|
||||||
|
const [tomorrowDate] = dateToInputDateTime(tomorrow);
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
daysOfWeek: [],
|
daysOfWeek: [],
|
||||||
description: schedule.description || '',
|
description: schedule.description || '',
|
||||||
end: 'never',
|
end: 'never',
|
||||||
endDateTime: dateToInputDateTime(tomorrow),
|
endDate: tomorrowDate,
|
||||||
|
endTime: time,
|
||||||
frequency: 'none',
|
frequency: 'none',
|
||||||
interval: 1,
|
interval: 1,
|
||||||
name: schedule.name || '',
|
name: schedule.name || '',
|
||||||
@@ -410,7 +396,8 @@ function ScheduleForm({
|
|||||||
runOnTheDay: 'sunday',
|
runOnTheDay: 'sunday',
|
||||||
runOnTheMonth: 1,
|
runOnTheMonth: 1,
|
||||||
runOnTheOccurrence: 1,
|
runOnTheOccurrence: 1,
|
||||||
startDateTime: dateToInputDateTime(closestQuarterHour),
|
startDate: currentDate,
|
||||||
|
startTime: time,
|
||||||
timezone: schedule.timezone || 'America/New_York',
|
timezone: schedule.timezone || 'America/New_York',
|
||||||
};
|
};
|
||||||
const submitSchedule = (
|
const submitSchedule = (
|
||||||
@@ -462,14 +449,19 @@ function ScheduleForm({
|
|||||||
} = RRule.fromString(schedule.rrule.replace(' ', '\n'));
|
} = RRule.fromString(schedule.rrule.replace(' ', '\n'));
|
||||||
|
|
||||||
if (dtstart) {
|
if (dtstart) {
|
||||||
overriddenValues.startDateTime = dateToInputDateTime(
|
const [startDate, startTime] = dateToInputDateTime(schedule.dtstart);
|
||||||
new Date(formatDateStringUTC(dtstart))
|
|
||||||
);
|
overriddenValues.startDate = startDate;
|
||||||
|
overriddenValues.startTime = startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schedule.until) {
|
if (schedule.until) {
|
||||||
overriddenValues.end = 'onDate';
|
overriddenValues.end = 'onDate';
|
||||||
overriddenValues.endDateTime = schedule.until;
|
|
||||||
|
const [endDate, endTime] = dateToInputDateTime(schedule.until);
|
||||||
|
|
||||||
|
overriddenValues.endDate = endDate;
|
||||||
|
overriddenValues.endTime = endTime;
|
||||||
} else if (count) {
|
} else if (count) {
|
||||||
overriddenValues.end = 'after';
|
overriddenValues.end = 'after';
|
||||||
overriddenValues.occurrences = count;
|
overriddenValues.occurrences = count;
|
||||||
@@ -553,18 +545,18 @@ function ScheduleForm({
|
|||||||
const errors = {};
|
const errors = {};
|
||||||
const {
|
const {
|
||||||
end,
|
end,
|
||||||
endDateTime,
|
endDate,
|
||||||
frequency,
|
frequency,
|
||||||
runOn,
|
runOn,
|
||||||
runOnDayNumber,
|
runOnDayNumber,
|
||||||
startDateTime,
|
startDate,
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
end === 'onDate' &&
|
end === 'onDate' &&
|
||||||
new Date(startDateTime) > new Date(endDateTime)
|
new Date(startDate) >= new Date(endDate)
|
||||||
) {
|
) {
|
||||||
errors.endDateTime = t`Please select an end date/time that comes after the start date/time.`;
|
errors.endDate = t`Please select an end date/time that comes after the start date/time.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
mountWithContexts,
|
mountWithContexts,
|
||||||
waitForElement,
|
waitForElement,
|
||||||
} from '../../../../testUtils/enzymeHelpers';
|
} from '../../../../testUtils/enzymeHelpers';
|
||||||
|
import { dateToInputDateTime } from '../../../util/dates';
|
||||||
import { SchedulesAPI, JobTemplatesAPI, InventoriesAPI } from '../../../api';
|
import { SchedulesAPI, JobTemplatesAPI, InventoriesAPI } from '../../../api';
|
||||||
import ScheduleForm from './ScheduleForm';
|
import ScheduleForm from './ScheduleForm';
|
||||||
|
|
||||||
@@ -99,8 +100,12 @@ const nonRRuleValuesMatch = () => {
|
|||||||
expect(wrapper.find('input#schedule-description').prop('value')).toBe(
|
expect(wrapper.find('input#schedule-description').prop('value')).toBe(
|
||||||
'test description'
|
'test description'
|
||||||
);
|
);
|
||||||
expect(wrapper.find('input#schedule-start-datetime').prop('value')).toBe(
|
|
||||||
'2020-04-02T14:45:00'
|
expect(
|
||||||
|
wrapper.find('DatePicker[aria-label="Start date"]').prop('value')
|
||||||
|
).toBe('2020-04-02');
|
||||||
|
expect(wrapper.find('TimePicker[aria-label="Start time"]').prop('time')).toBe(
|
||||||
|
'6:45 PM'
|
||||||
);
|
);
|
||||||
expect(wrapper.find('select#schedule-timezone').prop('value')).toBe(
|
expect(wrapper.find('select#schedule-timezone').prop('value')).toBe(
|
||||||
'America/New_York'
|
'America/New_York'
|
||||||
@@ -472,6 +477,11 @@ describe('<ScheduleForm />', () => {
|
|||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
test('initially renders expected fields and values', () => {
|
test('initially renders expected fields and values', () => {
|
||||||
|
const now = new Date();
|
||||||
|
const closestQuarterHour = new Date(
|
||||||
|
Math.ceil(now.getTime() / 900000) * 900000
|
||||||
|
);
|
||||||
|
const [date, time] = dateToInputDateTime(closestQuarterHour);
|
||||||
expect(wrapper.find('ScheduleForm').length).toBe(1);
|
expect(wrapper.find('ScheduleForm').length).toBe(1);
|
||||||
defaultFieldsVisible();
|
defaultFieldsVisible();
|
||||||
expect(wrapper.find('FormGroup[label="Run every"]').length).toBe(0);
|
expect(wrapper.find('FormGroup[label="Run every"]').length).toBe(0);
|
||||||
@@ -483,9 +493,9 @@ describe('<ScheduleForm />', () => {
|
|||||||
|
|
||||||
expect(wrapper.find('input#schedule-name').prop('value')).toBe('');
|
expect(wrapper.find('input#schedule-name').prop('value')).toBe('');
|
||||||
expect(wrapper.find('input#schedule-description').prop('value')).toBe('');
|
expect(wrapper.find('input#schedule-description').prop('value')).toBe('');
|
||||||
expect(
|
|
||||||
wrapper.find('input#schedule-start-datetime').prop('value')
|
expect(wrapper.find('DatePicker').prop('value')).toMatch(`${date}`);
|
||||||
).toMatch(/\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d/);
|
expect(wrapper.find('TimePicker').prop('time')).toMatch(`${time}`);
|
||||||
expect(wrapper.find('select#schedule-timezone').prop('value')).toBe(
|
expect(wrapper.find('select#schedule-timezone').prop('value')).toBe(
|
||||||
'America/New_York'
|
'America/New_York'
|
||||||
);
|
);
|
||||||
@@ -703,18 +713,18 @@ describe('<ScheduleForm />', () => {
|
|||||||
expect(wrapper.find('input#end-on-date').prop('checked')).toBe(true);
|
expect(wrapper.find('input#end-on-date').prop('checked')).toBe(true);
|
||||||
expect(wrapper.find('#schedule-end-datetime-helper').length).toBe(0);
|
expect(wrapper.find('#schedule-end-datetime-helper').length).toBe(0);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('input#schedule-end-datetime').simulate('change', {
|
wrapper.find('DatePicker[aria-label="End date"]').prop('onChange')(
|
||||||
target: { name: 'endDateTime', value: '2020-03-14T01:45:00' },
|
'2020-03-14',
|
||||||
});
|
new Date('2020-03-14')
|
||||||
|
);
|
||||||
});
|
});
|
||||||
wrapper.update();
|
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('input#schedule-end-datetime').simulate('blur');
|
wrapper.find('DatePicker[aria-label="End date"]').simulate('blur');
|
||||||
});
|
});
|
||||||
wrapper.update();
|
|
||||||
|
|
||||||
expect(wrapper.find('#schedule-end-datetime-helper').text()).toBe(
|
wrapper.update();
|
||||||
|
expect(wrapper.find('#schedule-End-datetime-helper').text()).toBe(
|
||||||
'Please select an end date/time that comes after the start date/time.'
|
'Please select an end date/time that comes after the start date/time.'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -1041,7 +1051,7 @@ describe('<ScheduleForm />', () => {
|
|||||||
rrule:
|
rrule:
|
||||||
'DTSTART;TZID=America/New_York:20200402T144500 RRULE:INTERVAL=1;FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20210101T050000Z',
|
'DTSTART;TZID=America/New_York:20200402T144500 RRULE:INTERVAL=1;FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20210101T050000Z',
|
||||||
dtend: '2020-10-30T18:45:00Z',
|
dtend: '2020-10-30T18:45:00Z',
|
||||||
until: '2021-01-01T00:00:00',
|
until: '2021-01-01T01:00:00',
|
||||||
})}
|
})}
|
||||||
resource={{
|
resource={{
|
||||||
id: 23,
|
id: 23,
|
||||||
@@ -1090,9 +1100,12 @@ describe('<ScheduleForm />', () => {
|
|||||||
expect(
|
expect(
|
||||||
wrapper.find('input#schedule-days-of-week-sat').prop('checked')
|
wrapper.find('input#schedule-days-of-week-sat').prop('checked')
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
expect(wrapper.find('input#schedule-end-datetime').prop('value')).toBe(
|
expect(
|
||||||
'2021-01-01T00:00:00'
|
wrapper.find('DatePicker[aria-label="End date"]').prop('value')
|
||||||
);
|
).toBe('2021-01-01');
|
||||||
|
expect(
|
||||||
|
wrapper.find('TimePicker[aria-label="End time"]').prop('value')
|
||||||
|
).toBe('1:00 AM');
|
||||||
});
|
});
|
||||||
test('initially renders expected fields and values with existing schedule that runs every month on the last weekday', async () => {
|
test('initially renders expected fields and values with existing schedule that runs every month on the last weekday', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
|||||||
@@ -2,15 +2,20 @@ import { t } from '@lingui/macro';
|
|||||||
import { RRule } from 'rrule';
|
import { RRule } from 'rrule';
|
||||||
import { getRRuleDayConstants } from '../../../util/dates';
|
import { getRRuleDayConstants } from '../../../util/dates';
|
||||||
|
|
||||||
|
const parseTime = time => {
|
||||||
|
const [hour, minute, ampm] = time.split(/[: ]/);
|
||||||
|
const timeHour =
|
||||||
|
ampm === 'PM' && hour !== '12' ? `${parseInt(hour, 10) + 12}` : `${hour}`;
|
||||||
|
|
||||||
|
return [timeHour, minute];
|
||||||
|
};
|
||||||
|
|
||||||
export default function buildRuleObj(values) {
|
export default function buildRuleObj(values) {
|
||||||
const [startDate, startTime] = values.startDateTime.split('T');
|
|
||||||
// Dates are formatted like "YYYY-MM-DD"
|
// Dates are formatted like "YYYY-MM-DD"
|
||||||
const [startYear, startMonth, startDay] = startDate.split('-');
|
const [startYear, startMonth, startDay] = values.startDate.split('-');
|
||||||
// Times are formatted like "HH:MM:SS" or "HH:MM" if no seconds
|
// Times are formatted like "HH:MM:SS" or "HH:MM" if no seconds
|
||||||
// have been specified
|
// have been specified
|
||||||
const [startHour = 0, startMinute = 0, startSecond = 0] = startTime.split(
|
const [startHour, startMinute] = parseTime(values.startTime);
|
||||||
':'
|
|
||||||
);
|
|
||||||
|
|
||||||
const ruleObj = {
|
const ruleObj = {
|
||||||
interval: values.interval,
|
interval: values.interval,
|
||||||
@@ -20,8 +25,7 @@ export default function buildRuleObj(values) {
|
|||||||
parseInt(startMonth, 10) - 1,
|
parseInt(startMonth, 10) - 1,
|
||||||
startDay,
|
startDay,
|
||||||
startHour,
|
startHour,
|
||||||
startMinute,
|
startMinute
|
||||||
startSecond
|
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
tzid: values.timezone,
|
tzid: values.timezone,
|
||||||
@@ -77,17 +81,16 @@ export default function buildRuleObj(values) {
|
|||||||
ruleObj.count = values.occurrences;
|
ruleObj.count = values.occurrences;
|
||||||
break;
|
break;
|
||||||
case 'onDate': {
|
case 'onDate': {
|
||||||
const [endDate, endTime] = values.endDateTime.split('T');
|
const [endYear, endMonth, endDay] = values.endDate.split('-');
|
||||||
const [endYear, endMonth, endDay] = endDate.split('-');
|
|
||||||
const [endHour = 0, endMinute = 0, endSecond = 0] = endTime.split(':');
|
const [endHour, endMinute] = parseTime(values.endTime);
|
||||||
ruleObj.until = new Date(
|
ruleObj.until = new Date(
|
||||||
Date.UTC(
|
Date.UTC(
|
||||||
endYear,
|
endYear,
|
||||||
parseInt(endMonth, 10) - 1,
|
parseInt(endMonth, 10) - 1,
|
||||||
endDay,
|
endDay,
|
||||||
endHour,
|
endHour,
|
||||||
endMinute,
|
endMinute
|
||||||
endSecond
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -44,15 +44,19 @@ export function timeOfDay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function dateToInputDateTime(dateObj) {
|
export function dateToInputDateTime(dateObj) {
|
||||||
// input type="date-time" expects values to be formatted
|
let date = dateObj;
|
||||||
// like: YYYY-MM-DDTHH-MM-SS
|
if (typeof dateObj === 'string') {
|
||||||
const year = dateObj.getFullYear();
|
date = new Date(dateObj);
|
||||||
const month = prependZeros(dateObj.getMonth() + 1);
|
}
|
||||||
const day = prependZeros(dateObj.getDate());
|
const year = date.getFullYear();
|
||||||
const hour = prependZeros(dateObj.getHours());
|
const month = prependZeros(date.getMonth() + 1);
|
||||||
const minute = prependZeros(dateObj.getMinutes());
|
const day = prependZeros(date.getDate());
|
||||||
const second = prependZeros(dateObj.getSeconds());
|
const hour =
|
||||||
return `${year}-${month}-${day}T${hour}:${minute}:${second}`;
|
date.getHours() > 12 ? parseInt(date.getHours(), 10) - 12 : date.getHours();
|
||||||
|
const minute = prependZeros(date.getMinutes());
|
||||||
|
const amPmText = date.getHours() > 11 ? 'PM' : 'AM';
|
||||||
|
|
||||||
|
return [`${year}-${month}-${day}`, `${hour}:${minute} ${amPmText}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRRuleDayConstants(dayString) {
|
export function getRRuleDayConstants(dayString) {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ describe('dateToInputDateTime', () => {
|
|||||||
test('it returns the expected value', () => {
|
test('it returns the expected value', () => {
|
||||||
expect(
|
expect(
|
||||||
dateToInputDateTime(new Date('2018-01-31T01:14:52.969227Z'))
|
dateToInputDateTime(new Date('2018-01-31T01:14:52.969227Z'))
|
||||||
).toEqual('2018-01-31T01:14:52');
|
).toEqual(['2018-01-31', '1:14 AM']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
import { isValidDate } from '@patternfly/react-core';
|
||||||
|
|
||||||
export function required(message) {
|
export function required(message) {
|
||||||
const errorMessage = message || t`This field must not be blank`;
|
const errorMessage = message || t`This field must not be blank`;
|
||||||
@@ -15,6 +16,25 @@ export function required(message) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
export function validateTime() {
|
||||||
|
return value => {
|
||||||
|
const timeRegex = new RegExp(
|
||||||
|
`^\\s*(\\d\\d?):([0-5])(\\d)\\s*([AaPp][Mm])?\\s*$`
|
||||||
|
);
|
||||||
|
let message;
|
||||||
|
const timeComponents = value.split(':');
|
||||||
|
|
||||||
|
const date = new Date();
|
||||||
|
date.setHours(parseInt(timeComponents[0], 10));
|
||||||
|
date.setMinutes(parseInt(timeComponents[1], 10));
|
||||||
|
|
||||||
|
if (!isValidDate(date) || !timeRegex.test(value)) {
|
||||||
|
message = t`Invalid time format`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function maxLength(max) {
|
export function maxLength(max) {
|
||||||
return value => {
|
return value => {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
combine,
|
combine,
|
||||||
regExp,
|
regExp,
|
||||||
requiredEmail,
|
requiredEmail,
|
||||||
|
validateTime,
|
||||||
} from './validators';
|
} from './validators';
|
||||||
|
|
||||||
describe('validators', () => {
|
describe('validators', () => {
|
||||||
@@ -168,4 +169,13 @@ describe('validators', () => {
|
|||||||
test('bob has email', () => {
|
test('bob has email', () => {
|
||||||
expect(requiredEmail()('bob@localhost')).toBeUndefined();
|
expect(requiredEmail()('bob@localhost')).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
test('validate time validates properly', () => {
|
||||||
|
expect(validateTime()('12:15 PM')).toBeUndefined();
|
||||||
|
expect(validateTime()('1:15 PM')).toBeUndefined();
|
||||||
|
expect(validateTime()('01:15 PM')).toBeUndefined();
|
||||||
|
expect(validateTime()('12:15')).toBeUndefined();
|
||||||
|
expect(validateTime()('12:15: PM')).toEqual('Invalid time format');
|
||||||
|
expect(validateTime()('12.15 PM')).toEqual('Invalid time format');
|
||||||
|
expect(validateTime()('12;15 PM')).toEqual('Invalid time format');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user