diff --git a/src/Plugin/Field/FieldWidget/DateRecurModularAlphaWidget.php b/src/Plugin/Field/FieldWidget/DateRecurModularAlphaWidget.php index 191e5ec..76c0004 100644 --- a/src/Plugin/Field/FieldWidget/DateRecurModularAlphaWidget.php +++ b/src/Plugin/Field/FieldWidget/DateRecurModularAlphaWidget.php @@ -268,6 +268,9 @@ class DateRecurModularAlphaWidget extends DateRecurModularWidgetBase { /** @var string|null $timeZone */ $timeZone = $form_state->getValue(array_merge($element['#parents'], ['time_zone'])); + if ($start instanceof DrupalDateTime && $end instanceof DrupalDateTime && ($start->getPhpDateTime() > $end->getPhpDateTime())) { + $form_state->setError($element['end'], \t('End time must be after start time.')); + } if ($start && !$timeZone) { $form_state->setError($element['start'], \t('Time zone must be set if start date is set.')); } diff --git a/tests/src/Functional/DateRecurModularAlphaTest.php b/tests/src/Functional/DateRecurModularAlphaTest.php new file mode 100644 index 0000000..a7fb374 --- /dev/null +++ b/tests/src/Functional/DateRecurModularAlphaTest.php @@ -0,0 +1,486 @@ +getComponent('dr'); + $component['region'] = 'content'; + $component['type'] = 'date_recur_modular_alpha'; + $component['settings'] = []; + $display->setComponent('dr', $component); + $display->save(); + + $user = $this->drupalCreateUser(['administer entity_test content']); + $user->timezone = 'Asia/Singapore'; + $user->save(); + $this->drupalLogin($user); + } + + /** + * Tests field widget input is converted to appropriate database values. + * + * @param array $values + * Array of form fields to submit. + * @param array $expected + * Array of expected field normalized values. + * @param bool $clickAllDay + * Whether to click the all day toggle. + * + * @dataProvider providerTestWidget + */ + public function testWidget(array $values, array $expected, $clickAllDay = FALSE): void { + $entity = DrEntityTest::create(); + $entity->save(); + $this->drupalGet($entity->toUrl('edit-form')); + + if ($clickAllDay) { + $this->getSession()->getPage()->find('css', '.parts--is-all-day .form-radios> *:nth-child(1) label')->click(); + } + + $this->drupalPostForm(NULL, $values, 'Save'); + $this->assertSession()->pageTextContains('has been updated.'); + + $entity = DrEntityTest::load($entity->id()); + $this->assertEquals($expected, $entity->dr[0]->getValue()); + } + + /** + * Data provider for testWidget() + * + * @return array + * Data for testing. + */ + public function providerTestWidget(): array { + $data = []; + + $data['once'] = [ + [ + 'dr[0][mode]' => 'once', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => NULL, + 'infinite' => FALSE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + $data['multi'] = [ + [ + 'dr[0][mode]' => 'multiday', + 'dr[0][daily_count]' => 3, + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '5:00:00pm', + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=DAILY;INTERVAL=1;COUNT=3', + 'infinite' => FALSE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + $data['weekly'] = [ + [ + 'dr[0][mode]' => 'weekly', + 'dr[0][weekdays][MO]' => TRUE, + 'dr[0][weekdays][WE]' => TRUE, + 'dr[0][weekdays][FR]' => TRUE, + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,FR', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + $data['fortnightly'] = [ + [ + 'dr[0][mode]' => 'fortnightly', + 'dr[0][weekdays][MO]' => TRUE, + 'dr[0][weekdays][WE]' => TRUE, + 'dr[0][weekdays][FR]' => TRUE, + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + $data['allday'] = [ + [ + 'dr[0][mode]' => 'once', + 'dr[0][day_start]' => '04/14/2015', + ], + [ + 'value' => '2015-04-13T16:00:00', + 'end_value' => '2015-04-14T15:59:59', + 'rrule' => NULL, + 'infinite' => FALSE, + 'timezone' => 'Asia/Singapore', + ], + TRUE, + ]; + + // First Friday of the month. + $data['monthly 1 ordinal 1 weekday'] = [ + [ + 'dr[0][mode]' => 'monthly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + // Set weekday first, ordinals will appear after it. + 'dr[0][weekdays][FR]' => TRUE, + 'dr[0][ordinals][1]' => TRUE, + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=FR;BYSETPOS=1', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + // First Thursday and Friday of the month. + $data['monthly 1 ordinal 2 weekday'] = [ + [ + 'dr[0][mode]' => 'monthly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + 'dr[0][weekdays][TH]' => TRUE, + 'dr[0][weekdays][FR]' => TRUE, + 'dr[0][ordinals][1]' => TRUE, + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=TH,FR;BYSETPOS=1,2', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + // First and second Friday of the month. + $data['monthly 1,2 ordinal 1 weekday'] = [ + [ + 'dr[0][mode]' => 'monthly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + 'dr[0][weekdays][FR]' => TRUE, + 'dr[0][ordinals][1]' => TRUE, + 'dr[0][ordinals][2]' => TRUE, + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=FR;BYSETPOS=1,2', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + // First and second Thursday and Friday of the month. + $data['monthly 1,2 ordinal 2 weekday'] = [ + [ + 'dr[0][mode]' => 'monthly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + 'dr[0][weekdays][TH]' => TRUE, + 'dr[0][weekdays][FR]' => TRUE, + 'dr[0][ordinals][1]' => TRUE, + 'dr[0][ordinals][2]' => TRUE, + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=TH,FR;BYSETPOS=1,2,3,4', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + // Last Thursday of the month. + $data['monthly -1 ordinal 1 weekday'] = [ + [ + 'dr[0][mode]' => 'monthly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + 'dr[0][weekdays][TH]' => TRUE, + 'dr[0][ordinals][-1]' => TRUE, + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=TH;BYSETPOS=-1', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + // Last Thursday and Friday of the month. + $data['monthly -1 ordinal 2 weekday'] = [ + [ + 'dr[0][mode]' => 'monthly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + 'dr[0][weekdays][TH]' => TRUE, + 'dr[0][weekdays][FR]' => TRUE, + 'dr[0][ordinals][-1]' => TRUE, + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=TH,FR;BYSETPOS=-2,-1', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + // Second to last Thursday of the month. + $data['monthly -2 ordinal 1 weekday'] = [ + [ + 'dr[0][mode]' => 'monthly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + 'dr[0][weekdays][TH]' => TRUE, + 'dr[0][ordinals][-2]' => TRUE, + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=TH;BYSETPOS=-2', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + // Second to last Thursday and Friday of the month. + $data['monthly -4,-3 ordinal 2 weekday'] = [ + [ + 'dr[0][mode]' => 'monthly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + 'dr[0][weekdays][TH]' => TRUE, + 'dr[0][weekdays][FR]' => TRUE, + 'dr[0][ordinals][-2]' => TRUE, + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=TH,FR;BYSETPOS=-4,-3', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + // Last and Second to last Thursday and Friday of the month. + $data['monthly -4,-3-2,-1 ordinal 2 weekday'] = [ + [ + 'dr[0][mode]' => 'monthly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + 'dr[0][weekdays][TH]' => TRUE, + 'dr[0][weekdays][FR]' => TRUE, + 'dr[0][ordinals][-1]' => TRUE, + 'dr[0][ordinals][-2]' => TRUE, + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=TH,FR;BYSETPOS=-4,-3,-2,-1', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + // Combination second and second to last Thursday and Friday of the month. + $data['monthly -4,-3,3,4 ordinal 2 weekday'] = [ + [ + 'dr[0][mode]' => 'monthly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '05:00:00pm', + 'dr[0][weekdays][TH]' => TRUE, + 'dr[0][weekdays][FR]' => TRUE, + 'dr[0][ordinals][2]' => TRUE, + 'dr[0][ordinals][-2]' => TRUE, + ], + [ + 'value' => '2015-04-14T01:00:00', + 'end_value' => '2015-04-14T09:00:00', + 'rrule' => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=TH,FR;BYSETPOS=-4,-3,3,4', + 'infinite' => TRUE, + 'timezone' => 'Asia/Singapore', + ], + ]; + + return $data; + } + + /** + * Tests times fields end before start. + */ + public function testTimesEndBeforeStart(): void { + $entity = DrEntityTest::create(); + $entity->save(); + + $edit = [ + 'dr[0][mode]' => 'weekly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '08:00:00am', + 'dr[0][weekdays][MO]' => TRUE, + ]; + $this->drupalPostForm($entity->toUrl('edit-form'), $edit, 'Save'); + $this->assertSession()->pageTextContains('End time must be after start time.'); + } + + /** + * Tests times fields end same as start. + */ + public function testTimesEndEqualStart(): void { + $entity = DrEntityTest::create(); + $entity->save(); + + $edit = [ + 'dr[0][mode]' => 'weekly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '09:00:00am', + 'dr[0][weekdays][MO]' => TRUE, + ]; + $this->drupalPostForm($entity->toUrl('edit-form'), $edit, 'Save'); + $this->assertSession()->pageTextContains('has been updated.'); + } + + /** + * Tests times fields end after start. + */ + public function testTimesEndAfterStart(): void { + $entity = DrEntityTest::create(); + $entity->save(); + + $edit = [ + 'dr[0][mode]' => 'weekly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '10:00:00am', + 'dr[0][weekdays][MO]' => TRUE, + ]; + $this->drupalPostForm($entity->toUrl('edit-form'), $edit, 'Save'); + $this->assertSession()->pageTextContains('has been updated.'); + } + + /** + * Tests times fields end not set. + */ + public function testTimesStartNotSet(): void { + $entity = DrEntityTest::create(); + $entity->save(); + + $edit = [ + 'dr[0][mode]' => 'weekly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_end]' => '09:00:00am', + 'dr[0][weekdays][MO]' => TRUE, + ]; + $this->drupalPostForm($entity->toUrl('edit-form'), $edit, 'Save'); + $this->assertSession()->pageTextContains('Invalid start time.'); + } + + /** + * Tests times fields end not set. + */ + public function testTimesEndNotSet(): void { + $entity = DrEntityTest::create(); + $entity->save(); + + $edit = [ + 'dr[0][mode]' => 'weekly', + 'dr[0][day_start]' => '04/14/2015', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][weekdays][MO]' => TRUE, + ]; + $this->drupalPostForm($entity->toUrl('edit-form'), $edit, 'Save'); + $this->assertSession()->pageTextContains('Invalid end time.'); + } + + /** + * Tests times fields end not set. + */ + public function testTimesDayNotSet(): void { + $entity = DrEntityTest::create(); + $entity->save(); + + $edit = [ + 'dr[0][mode]' => 'weekly', + 'dr[0][times][time_start]' => '09:00:00am', + 'dr[0][times][time_end]' => '09:00:00am', + 'dr[0][weekdays][MO]' => TRUE, + ]; + $this->drupalPostForm($entity->toUrl('edit-form'), $edit, 'Save'); + $this->assertSession()->pageTextContains('Invalid start day.'); + } + +}