IMipServiceTest.php 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OCA\DAV\Tests\unit\CalDAV\Schedule;
  8. use OC\L10N\L10N;
  9. use OC\L10N\LazyL10N;
  10. use OC\URLGenerator;
  11. use OCA\DAV\CalDAV\EventReader;
  12. use OCA\DAV\CalDAV\Schedule\IMipService;
  13. use OCP\AppFramework\Utility\ITimeFactory;
  14. use OCP\IConfig;
  15. use OCP\IDBConnection;
  16. use OCP\L10N\IFactory as L10NFactory;
  17. use OCP\Security\ISecureRandom;
  18. use PHPUnit\Framework\MockObject\MockObject;
  19. use Sabre\VObject\Component\VCalendar;
  20. use Sabre\VObject\Property\ICalendar\DateTime;
  21. use Test\TestCase;
  22. class IMipServiceTest extends TestCase {
  23. /** @var URLGenerator|MockObject */
  24. private $urlGenerator;
  25. /** @var IConfig|MockObject */
  26. private $config;
  27. /** @var IDBConnection|MockObject */
  28. private $db;
  29. /** @var ISecureRandom|MockObject */
  30. private $random;
  31. /** @var L10NFactory|MockObject */
  32. private $l10nFactory;
  33. /** @var L10N|MockObject */
  34. private $l10n;
  35. /** @var ITimeFactory|MockObject */
  36. private $timeFactory;
  37. /** @var IMipService */
  38. private $service;
  39. /** @var VCalendar */
  40. private $vCalendar1a;
  41. /** @var VCalendar */
  42. private $vCalendar1b;
  43. /** @var VCalendar */
  44. private $vCalendar2;
  45. /** @var VCalendar */
  46. private $vCalendar3;
  47. /** @var DateTime DateTime object that will be returned by DateTime() or DateTime('now') */
  48. public static $datetimeNow;
  49. protected function setUp(): void {
  50. $this->urlGenerator = $this->createMock(URLGenerator::class);
  51. $this->config = $this->createMock(IConfig::class);
  52. $this->db = $this->createMock(IDBConnection::class);
  53. $this->random = $this->createMock(ISecureRandom::class);
  54. $this->l10nFactory = $this->createMock(L10NFactory::class);
  55. $this->l10n = $this->createMock(LazyL10N::class);
  56. $this->timeFactory = $this->createMock(ITimeFactory::class);
  57. $this->l10nFactory->expects(self::once())
  58. ->method('findGenericLanguage')
  59. ->willReturn('en');
  60. $this->l10nFactory->expects(self::once())
  61. ->method('get')
  62. ->with('dav', 'en')
  63. ->willReturn($this->l10n);
  64. $this->service = new IMipService(
  65. $this->urlGenerator,
  66. $this->config,
  67. $this->db,
  68. $this->random,
  69. $this->l10nFactory,
  70. $this->timeFactory
  71. );
  72. // construct calendar with a 1 hour event and same start/end time zones
  73. $this->vCalendar1a = new VCalendar();
  74. $vEvent = $this->vCalendar1a->add('VEVENT', []);
  75. $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc');
  76. $vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']);
  77. $vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Toronto']);
  78. $vEvent->add('SUMMARY', 'Testing Event');
  79. $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']);
  80. $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [
  81. 'CN' => 'Attendee One',
  82. 'CUTYPE' => 'INDIVIDUAL',
  83. 'PARTSTAT' => 'NEEDS-ACTION',
  84. 'ROLE' => 'REQ-PARTICIPANT',
  85. 'RSVP' => 'TRUE'
  86. ]);
  87. // construct calendar with a 1 hour event and different start/end time zones
  88. $this->vCalendar1b = new VCalendar();
  89. $vEvent = $this->vCalendar1b->add('VEVENT', []);
  90. $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc');
  91. $vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']);
  92. $vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Vancouver']);
  93. $vEvent->add('SUMMARY', 'Testing Event');
  94. $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']);
  95. $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [
  96. 'CN' => 'Attendee One',
  97. 'CUTYPE' => 'INDIVIDUAL',
  98. 'PARTSTAT' => 'NEEDS-ACTION',
  99. 'ROLE' => 'REQ-PARTICIPANT',
  100. 'RSVP' => 'TRUE'
  101. ]);
  102. // construct calendar with a full day event
  103. $this->vCalendar2 = new VCalendar();
  104. // time zone component
  105. $vTimeZone = $this->vCalendar2->add('VTIMEZONE');
  106. $vTimeZone->add('TZID', 'America/Toronto');
  107. // event component
  108. $vEvent = $this->vCalendar2->add('VEVENT', []);
  109. $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc');
  110. $vEvent->add('DTSTART', '20240701');
  111. $vEvent->add('DTEND', '20240702');
  112. $vEvent->add('SUMMARY', 'Testing Event');
  113. $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']);
  114. $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [
  115. 'CN' => 'Attendee One',
  116. 'CUTYPE' => 'INDIVIDUAL',
  117. 'PARTSTAT' => 'NEEDS-ACTION',
  118. 'ROLE' => 'REQ-PARTICIPANT',
  119. 'RSVP' => 'TRUE'
  120. ]);
  121. // construct calendar with a multi day event
  122. $this->vCalendar3 = new VCalendar();
  123. // time zone component
  124. $vTimeZone = $this->vCalendar3->add('VTIMEZONE');
  125. $vTimeZone->add('TZID', 'America/Toronto');
  126. // event component
  127. $vEvent = $this->vCalendar3->add('VEVENT', []);
  128. $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc');
  129. $vEvent->add('DTSTART', '20240701');
  130. $vEvent->add('DTEND', '20240706');
  131. $vEvent->add('SUMMARY', 'Testing Event');
  132. $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']);
  133. $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [
  134. 'CN' => 'Attendee One',
  135. 'CUTYPE' => 'INDIVIDUAL',
  136. 'PARTSTAT' => 'NEEDS-ACTION',
  137. 'ROLE' => 'REQ-PARTICIPANT',
  138. 'RSVP' => 'TRUE'
  139. ]);
  140. }
  141. public function testGetFrom(): void {
  142. $senderName = 'Detective McQueen';
  143. $default = 'Twin Lakes Police Department - Darkside Division';
  144. $expected = 'Detective McQueen via Twin Lakes Police Department - Darkside Division';
  145. $this->l10n->expects(self::once())
  146. ->method('t')
  147. ->willReturn($expected);
  148. $actual = $this->service->getFrom($senderName, $default);
  149. $this->assertEquals($expected, $actual);
  150. }
  151. public function testBuildBodyDataCreated(): void {
  152. // construct l10n return(s)
  153. $this->l10n->method('l')->willReturnCallback(
  154. function ($v1, $v2, $v3) {
  155. return match (true) {
  156. $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM',
  157. $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM',
  158. $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'full'] => 'July 1, 2024'
  159. };
  160. }
  161. );
  162. $this->l10n->method('t')->willReturnMap([
  163. ['In a %1$s on %2$s between %3$s - %4$s', ['day', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a day on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)']
  164. ]);
  165. // construct time factory return(s)
  166. $this->timeFactory->method('getDateTime')->willReturnCallback(
  167. function ($v1, $v2) {
  168. return match (true) {
  169. $v1 == 'now' && $v2 == null => (new \DateTime('20240630T000000'))
  170. };
  171. }
  172. );
  173. /** test singleton partial day event*/
  174. $vCalendar = clone $this->vCalendar1a;
  175. // construct event reader
  176. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  177. // define expected output
  178. $expected = [
  179. 'meeting_when' => $this->service->generateWhenString($eventReader),
  180. 'meeting_description' => '',
  181. 'meeting_title' => 'Testing Event',
  182. 'meeting_location' => '',
  183. 'meeting_url' => '',
  184. 'meeting_url_html' => '',
  185. ];
  186. // generate actual output
  187. $actual = $this->service->buildBodyData($vCalendar->VEVENT[0], null);
  188. // test output
  189. $this->assertEquals($expected, $actual);
  190. }
  191. public function testBuildBodyDataUpdate(): void {
  192. // construct l10n return(s)
  193. $this->l10n->method('l')->willReturnCallback(
  194. function ($v1, $v2, $v3) {
  195. return match (true) {
  196. $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM',
  197. $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM',
  198. $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'full'] => 'July 1, 2024'
  199. };
  200. }
  201. );
  202. $this->l10n->method('t')->willReturnMap([
  203. ['In a %1$s on %2$s between %3$s - %4$s', ['day', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a day on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)']
  204. ]);
  205. // construct time factory return(s)
  206. $this->timeFactory->method('getDateTime')->willReturnCallback(
  207. function ($v1, $v2) {
  208. return match (true) {
  209. $v1 == 'now' && $v2 == null => (new \DateTime('20240630T000000'))
  210. };
  211. }
  212. );
  213. /** test singleton partial day event*/
  214. $vCalendarNew = clone $this->vCalendar1a;
  215. $vCalendarOld = clone $this->vCalendar1a;
  216. // construct event reader
  217. $eventReaderNew = new EventReader($vCalendarNew, $vCalendarNew->VEVENT[0]->UID->getValue());
  218. // alter old event label/title
  219. $vCalendarOld->VEVENT[0]->SUMMARY->setValue('Testing Singleton Event');
  220. // define expected output
  221. $expected = [
  222. 'meeting_when' => $this->service->generateWhenString($eventReaderNew),
  223. 'meeting_description' => '',
  224. 'meeting_title' => 'Testing Event',
  225. 'meeting_location' => '',
  226. 'meeting_url' => '',
  227. 'meeting_url_html' => '',
  228. 'meeting_when_html' => $this->service->generateWhenString($eventReaderNew),
  229. 'meeting_title_html' => sprintf("<span style='text-decoration: line-through'>%s</span><br />%s", 'Testing Singleton Event', 'Testing Event'),
  230. 'meeting_description_html' => '',
  231. 'meeting_location_html' => ''
  232. ];
  233. // generate actual output
  234. $actual = $this->service->buildBodyData($vCalendarNew->VEVENT[0], $vCalendarOld->VEVENT[0]);
  235. // test output
  236. $this->assertEquals($expected, $actual);
  237. }
  238. public function testGetLastOccurrenceRRULE(): void {
  239. $vCalendar = new VCalendar();
  240. $vCalendar->add('VEVENT', [
  241. 'UID' => 'uid-1234',
  242. 'LAST-MODIFIED' => 123456,
  243. 'SEQUENCE' => 2,
  244. 'SUMMARY' => 'Fellowship meeting',
  245. 'DTSTART' => new \DateTime('2016-01-01 00:00:00'),
  246. 'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z',
  247. ]);
  248. $occurrence = $this->service->getLastOccurrence($vCalendar);
  249. $this->assertEquals(1454284800, $occurrence);
  250. }
  251. public function testGetLastOccurrenceEndDate(): void {
  252. $vCalendar = new VCalendar();
  253. $vCalendar->add('VEVENT', [
  254. 'UID' => 'uid-1234',
  255. 'LAST-MODIFIED' => 123456,
  256. 'SEQUENCE' => 2,
  257. 'SUMMARY' => 'Fellowship meeting',
  258. 'DTSTART' => new \DateTime('2016-01-01 00:00:00'),
  259. 'DTEND' => new \DateTime('2017-01-01 00:00:00'),
  260. ]);
  261. $occurrence = $this->service->getLastOccurrence($vCalendar);
  262. $this->assertEquals(1483228800, $occurrence);
  263. }
  264. public function testGetLastOccurrenceDuration(): void {
  265. $vCalendar = new VCalendar();
  266. $vCalendar->add('VEVENT', [
  267. 'UID' => 'uid-1234',
  268. 'LAST-MODIFIED' => 123456,
  269. 'SEQUENCE' => 2,
  270. 'SUMMARY' => 'Fellowship meeting',
  271. 'DTSTART' => new \DateTime('2016-01-01 00:00:00'),
  272. 'DURATION' => 'P12W',
  273. ]);
  274. $occurrence = $this->service->getLastOccurrence($vCalendar);
  275. $this->assertEquals(1458864000, $occurrence);
  276. }
  277. public function testGetLastOccurrenceAllDay(): void {
  278. $vCalendar = new VCalendar();
  279. $vEvent = $vCalendar->add('VEVENT', [
  280. 'UID' => 'uid-1234',
  281. 'LAST-MODIFIED' => 123456,
  282. 'SEQUENCE' => 2,
  283. 'SUMMARY' => 'Fellowship meeting',
  284. 'DTSTART' => new \DateTime('2016-01-01 00:00:00'),
  285. ]);
  286. // rewrite from DateTime to Date
  287. $vEvent->DTSTART['VALUE'] = 'DATE';
  288. $occurrence = $this->service->getLastOccurrence($vCalendar);
  289. $this->assertEquals(1451692800, $occurrence);
  290. }
  291. public function testGetLastOccurrenceFallback(): void {
  292. $vCalendar = new VCalendar();
  293. $vCalendar->add('VEVENT', [
  294. 'UID' => 'uid-1234',
  295. 'LAST-MODIFIED' => 123456,
  296. 'SEQUENCE' => 2,
  297. 'SUMMARY' => 'Fellowship meeting',
  298. 'DTSTART' => new \DateTime('2016-01-01 00:00:00'),
  299. ]);
  300. $occurrence = $this->service->getLastOccurrence($vCalendar);
  301. $this->assertEquals(1451606400, $occurrence);
  302. }
  303. public function testGenerateWhenStringSingular(): void {
  304. // construct l10n return(s)
  305. $this->l10n->method('l')->willReturnCallback(
  306. function ($v1, $v2, $v3) {
  307. return match (true) {
  308. $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM',
  309. $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM',
  310. $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'full'] => 'July 1, 2024',
  311. $v1 === 'date' && $v2 == (new \DateTime('20240701T000000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'full'] => 'July 1, 2024'
  312. };
  313. }
  314. );
  315. $this->l10n->method('t')->willReturnMap([
  316. ['In a %1$s on %2$s for the entire day', ['day', 'July 1, 2024'], 'In a day on July 1, 2024 for the entire day'],
  317. ['In a %1$s on %2$s between %3$s - %4$s', ['day', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a day on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'],
  318. ['In %1$s %2$s on %3$s for the entire day', [2, 'days', 'July 1, 2024'], 'In 2 days on July 1, 2024 for the entire day'],
  319. ['In %1$s %2$s on %3$s between %4$s - %5$s', [2, 'days', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In 2 days on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'],
  320. ['In a %1$s on %2$s for the entire day', ['week', 'July 1, 2024'], 'In a week on July 1, 2024 for the entire day'],
  321. ['In a %1$s on %2$s between %3$s - %4$s', ['week', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a week on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'],
  322. ['In %1$s %2$s on %3$s for the entire day', [2, 'weeks', 'July 1, 2024'], 'In 2 weeks on July 1, 2024 for the entire day'],
  323. ['In %1$s %2$s on %3$s between %4$s - %5$s', [2, 'weeks', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In 2 weeks on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'],
  324. ['In a %1$s on %2$s for the entire day', ['month', 'July 1, 2024'], 'In a month on July 1, 2024 for the entire day'],
  325. ['In a %1$s on %2$s between %3$s - %4$s', ['month', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a month on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'],
  326. ['In %1$s %2$s on %3$s for the entire day', [2, 'months', 'July 1, 2024'], 'In 2 months on July 1, 2024 for the entire day'],
  327. ['In %1$s %2$s on %3$s between %4$s - %5$s', [2, 'months', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In 2 months on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'],
  328. ['In a %1$s on %2$s for the entire day', ['year', 'July 1, 2024'], 'In a year on July 1, 2024 for the entire day'],
  329. ['In a %1$s on %2$s between %3$s - %4$s', ['year', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a year on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'],
  330. ['In %1$s %2$s on %3$s for the entire day', [2, 'years', 'July 1, 2024'], 'In 2 years on July 1, 2024 for the entire day'],
  331. ['In %1$s %2$s on %3$s between %4$s - %5$s', [2, 'years', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In 2 years on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)']
  332. ]);
  333. // construct time factory return(s)
  334. $this->timeFactory->method('getDateTime')->willReturnOnConsecutiveCalls(
  335. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  336. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  337. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  338. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  339. (new \DateTime('20240621T170000', (new \DateTimeZone('America/Toronto')))),
  340. (new \DateTime('20240621T170000', (new \DateTimeZone('America/Toronto')))),
  341. (new \DateTime('20240614T170000', (new \DateTimeZone('America/Toronto')))),
  342. (new \DateTime('20240614T170000', (new \DateTimeZone('America/Toronto')))),
  343. (new \DateTime('20240530T170000', (new \DateTimeZone('America/Toronto')))),
  344. (new \DateTime('20240530T170000', (new \DateTimeZone('America/Toronto')))),
  345. (new \DateTime('20240430T170000', (new \DateTimeZone('America/Toronto')))),
  346. (new \DateTime('20240430T170000', (new \DateTimeZone('America/Toronto')))),
  347. (new \DateTime('20230630T170000', (new \DateTimeZone('America/Toronto')))),
  348. (new \DateTime('20230630T170000', (new \DateTimeZone('America/Toronto')))),
  349. (new \DateTime('20220630T170000', (new \DateTimeZone('America/Toronto')))),
  350. (new \DateTime('20220630T170000', (new \DateTimeZone('America/Toronto'))))
  351. );
  352. /** test patrial day event in 1 day*/
  353. $vCalendar = clone $this->vCalendar1a;
  354. // construct event reader
  355. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  356. // test output
  357. $this->assertEquals(
  358. 'In a day on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)',
  359. $this->service->generateWhenString($eventReader)
  360. );
  361. /** test entire day event in 1 day*/
  362. $vCalendar = clone $this->vCalendar2;
  363. // construct event reader
  364. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  365. // test output
  366. $this->assertEquals(
  367. 'In a day on July 1, 2024 for the entire day',
  368. $this->service->generateWhenString($eventReader)
  369. );
  370. /** test patrial day event in 2 days*/
  371. $vCalendar = clone $this->vCalendar1a;
  372. // construct event reader
  373. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  374. // test output
  375. $this->assertEquals(
  376. 'In 2 days on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)',
  377. $this->service->generateWhenString($eventReader)
  378. );
  379. /** test entire day event in 2 days*/
  380. $vCalendar = clone $this->vCalendar2;
  381. // construct event reader
  382. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  383. // test output
  384. $this->assertEquals(
  385. 'In 2 days on July 1, 2024 for the entire day',
  386. $this->service->generateWhenString($eventReader)
  387. );
  388. /** test patrial day event in 1 week*/
  389. $vCalendar = clone $this->vCalendar1a;
  390. // construct event reader
  391. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  392. // test output
  393. $this->assertEquals(
  394. 'In a week on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)',
  395. $this->service->generateWhenString($eventReader)
  396. );
  397. /** test entire day event in 1 week*/
  398. $vCalendar = clone $this->vCalendar2;
  399. // construct event reader
  400. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  401. // test output
  402. $this->assertEquals(
  403. 'In a week on July 1, 2024 for the entire day',
  404. $this->service->generateWhenString($eventReader)
  405. );
  406. /** test patrial day event in 2 weeks*/
  407. $vCalendar = clone $this->vCalendar1a;
  408. // construct event reader
  409. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  410. // test output
  411. $this->assertEquals(
  412. 'In 2 weeks on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)',
  413. $this->service->generateWhenString($eventReader)
  414. );
  415. /** test entire day event in 2 weeks*/
  416. $vCalendar = clone $this->vCalendar2;
  417. // construct event reader
  418. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  419. // test output
  420. $this->assertEquals(
  421. 'In 2 weeks on July 1, 2024 for the entire day',
  422. $this->service->generateWhenString($eventReader)
  423. );
  424. /** test patrial day event in 1 month*/
  425. $vCalendar = clone $this->vCalendar1a;
  426. // construct event reader
  427. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  428. // test output
  429. $this->assertEquals(
  430. 'In a month on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)',
  431. $this->service->generateWhenString($eventReader)
  432. );
  433. /** test entire day event in 1 month*/
  434. $vCalendar = clone $this->vCalendar2;
  435. // construct event reader
  436. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  437. // test output
  438. $this->assertEquals(
  439. 'In a month on July 1, 2024 for the entire day',
  440. $this->service->generateWhenString($eventReader)
  441. );
  442. /** test patrial day event in 2 months*/
  443. $vCalendar = clone $this->vCalendar1a;
  444. // construct event reader
  445. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  446. // test output
  447. $this->assertEquals(
  448. 'In 2 months on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)',
  449. $this->service->generateWhenString($eventReader)
  450. );
  451. /** test entire day event in 2 months*/
  452. $vCalendar = clone $this->vCalendar2;
  453. // construct event reader
  454. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  455. // test output
  456. $this->assertEquals(
  457. 'In 2 months on July 1, 2024 for the entire day',
  458. $this->service->generateWhenString($eventReader)
  459. );
  460. /** test patrial day event in 1 year*/
  461. $vCalendar = clone $this->vCalendar1a;
  462. // construct event reader
  463. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  464. // test output
  465. $this->assertEquals(
  466. 'In a year on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)',
  467. $this->service->generateWhenString($eventReader)
  468. );
  469. /** test entire day event in 1 year*/
  470. $vCalendar = clone $this->vCalendar2;
  471. // construct event reader
  472. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  473. // test output
  474. $this->assertEquals(
  475. 'In a year on July 1, 2024 for the entire day',
  476. $this->service->generateWhenString($eventReader)
  477. );
  478. /** test patrial day event in 2 years*/
  479. $vCalendar = clone $this->vCalendar1a;
  480. // construct event reader
  481. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  482. // test output
  483. $this->assertEquals(
  484. 'In 2 years on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)',
  485. $this->service->generateWhenString($eventReader)
  486. );
  487. /** test entire day event in 2 years*/
  488. $vCalendar = clone $this->vCalendar2;
  489. // construct event reader
  490. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  491. // test output
  492. $this->assertEquals(
  493. 'In 2 years on July 1, 2024 for the entire day',
  494. $this->service->generateWhenString($eventReader)
  495. );
  496. }
  497. public function testGenerateWhenStringRecurringDaily(): void {
  498. // construct l10n return maps
  499. $this->l10n->method('l')->willReturnCallback(
  500. function ($v1, $v2, $v3) {
  501. return match (true) {
  502. $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM',
  503. $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM',
  504. $v1 === 'date' && $v2 == (new \DateTime('20240713T080000', (new \DateTimeZone('UTC')))) && $v3 == ['width' => 'long'] => 'July 13, 2024'
  505. };
  506. }
  507. );
  508. $this->l10n->method('t')->willReturnMap([
  509. ['Every Day for the entire day', [], 'Every Day for the entire day'],
  510. ['Every Day for the entire day until %1$s', ['July 13, 2024'], 'Every Day for the entire day until July 13, 2024'],
  511. ['Every Day between %1$s - %2$s', ['8:00 AM', '9:00 AM (America/Toronto)'], 'Every Day between 8:00 AM - 9:00 AM (America/Toronto)'],
  512. ['Every Day between %1$s - %2$s until %3$s', ['8:00 AM', '9:00 AM (America/Toronto)', 'July 13, 2024'], 'Every Day between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024'],
  513. ['Every %1$d Days for the entire day', [3], 'Every 3 Days for the entire day'],
  514. ['Every %1$d Days for the entire day until %2$s', [3, 'July 13, 2024'], 'Every 3 Days for the entire day until July 13, 2024'],
  515. ['Every %1$d Days between %2$s - %3$s', [3, '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 3 Days between 8:00 AM - 9:00 AM (America/Toronto)'],
  516. ['Every %1$d Days between %2$s - %3$s until %4$s', [3, '8:00 AM', '9:00 AM (America/Toronto)', 'July 13, 2024'], 'Every 3 Days between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024'],
  517. ['Could not generate event recurrence statement', [], 'Could not generate event recurrence statement'],
  518. ]);
  519. /** test partial day event with every day interval and no conclusion*/
  520. $vCalendar = clone $this->vCalendar1a;
  521. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=1;');
  522. // construct event reader
  523. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  524. // test output
  525. $this->assertEquals(
  526. 'Every Day between 8:00 AM - 9:00 AM (America/Toronto)',
  527. $this->service->generateWhenString($eventReader)
  528. );
  529. /** test partial day event with every day interval and conclusion*/
  530. $vCalendar = clone $this->vCalendar1a;
  531. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=1;UNTIL=20240713T080000Z');
  532. // construct event reader
  533. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  534. // test output
  535. $this->assertEquals(
  536. 'Every Day between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024',
  537. $this->service->generateWhenString($eventReader)
  538. );
  539. /** test partial day event every 3rd day interval and no conclusion*/
  540. $vCalendar = clone $this->vCalendar1a;
  541. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=3;');
  542. // construct event reader
  543. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  544. // test output
  545. $this->assertEquals(
  546. 'Every 3 Days between 8:00 AM - 9:00 AM (America/Toronto)',
  547. $this->service->generateWhenString($eventReader)
  548. );
  549. /** test partial day event with every 3rd day interval and conclusion*/
  550. $vCalendar = clone $this->vCalendar1a;
  551. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=3;UNTIL=20240713T080000Z');
  552. // construct event reader
  553. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  554. // test output
  555. $this->assertEquals(
  556. 'Every 3 Days between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024',
  557. $this->service->generateWhenString($eventReader)
  558. );
  559. /** test entire day event with every day interval and no conclusion*/
  560. $vCalendar = clone $this->vCalendar2;
  561. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=1;');
  562. // construct event reader
  563. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  564. // test output
  565. $this->assertEquals(
  566. 'Every Day for the entire day',
  567. $this->service->generateWhenString($eventReader)
  568. );
  569. /** test entire day event with every day interval and conclusion*/
  570. $vCalendar = clone $this->vCalendar2;
  571. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=1;UNTIL=20240713T080000Z');
  572. // construct event reader
  573. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  574. // test output
  575. $this->assertEquals(
  576. 'Every Day for the entire day until July 13, 2024',
  577. $this->service->generateWhenString($eventReader)
  578. );
  579. /** test entire day event with every 3rd day interval and no conclusion*/
  580. $vCalendar = clone $this->vCalendar2;
  581. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=3;');
  582. // construct event reader
  583. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  584. // test output
  585. $this->assertEquals(
  586. 'Every 3 Days for the entire day',
  587. $this->service->generateWhenString($eventReader)
  588. );
  589. /** test entire day event with every 3rd day interval and conclusion*/
  590. $vCalendar = clone $this->vCalendar2;
  591. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=3;UNTIL=20240713T080000Z');
  592. // construct event reader
  593. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  594. // test output
  595. $this->assertEquals(
  596. 'Every 3 Days for the entire day until July 13, 2024',
  597. $this->service->generateWhenString($eventReader)
  598. );
  599. }
  600. public function testGenerateWhenStringRecurringWeekly(): void {
  601. // construct l10n return maps
  602. $this->l10n->method('l')->willReturnCallback(
  603. function ($v1, $v2, $v3) {
  604. return match (true) {
  605. $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM',
  606. $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM',
  607. $v1 === 'date' && $v2 == (new \DateTime('20240722T080000', (new \DateTimeZone('UTC')))) && $v3 == ['width' => 'long'] => 'July 13, 2024'
  608. };
  609. }
  610. );
  611. $this->l10n->method('t')->willReturnMap([
  612. ['Every Week on %1$s for the entire day', ['Monday, Wednesday, Friday'], 'Every Week on Monday, Wednesday, Friday for the entire day'],
  613. ['Every Week on %1$s for the entire day until %2$s', ['Monday, Wednesday, Friday', 'July 13, 2024'], 'Every Week on Monday, Wednesday, Friday for the entire day until July 13, 2024'],
  614. ['Every Week on %1$s between %2$s - %3$s', ['Monday, Wednesday, Friday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every Week on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto)'],
  615. ['Every Week on %1$s between %2$s - %3$s until %4$s', ['Monday, Wednesday, Friday', '8:00 AM', '9:00 AM (America/Toronto)', 'July 13, 2024'], 'Every Week on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024'],
  616. ['Every %1$d Weeks on %2$s for the entire day', [2, 'Monday, Wednesday, Friday'], 'Every 2 Weeks on Monday, Wednesday, Friday for the entire day'],
  617. ['Every %1$d Weeks on %2$s for the entire day until %3$s', [2, 'Monday, Wednesday, Friday', 'July 13, 2024'], 'Every 2 Weeks on Monday, Wednesday, Friday for the entire day until July 13, 2024'],
  618. ['Every %1$d Weeks on %2$s between %3$s - %4$s', [2, 'Monday, Wednesday, Friday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 2 Weeks on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto)'],
  619. ['Every %1$d Weeks on %2$s between %3$s - %4$s until %5$s', [2, 'Monday, Wednesday, Friday', '8:00 AM', '9:00 AM (America/Toronto)', 'July 13, 2024'], 'Every 2 Weeks on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024'],
  620. ['Could not generate event recurrence statement', [], 'Could not generate event recurrence statement'],
  621. ['Monday', [], 'Monday'],
  622. ['Wednesday', [], 'Wednesday'],
  623. ['Friday', [], 'Friday'],
  624. ]);
  625. /** test partial day event with every week interval on Mon, Wed, Fri and no conclusion*/
  626. $vCalendar = clone $this->vCalendar1a;
  627. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR');
  628. // construct event reader
  629. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  630. // test output
  631. $this->assertEquals(
  632. 'Every Week on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto)',
  633. $this->service->generateWhenString($eventReader)
  634. );
  635. /** test partial day event with every week interval on Mon, Wed, Fri and conclusion*/
  636. $vCalendar = clone $this->vCalendar1a;
  637. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20240722T080000Z;');
  638. // construct event reader
  639. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  640. // test output
  641. $this->assertEquals(
  642. 'Every Week on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024',
  643. $this->service->generateWhenString($eventReader)
  644. );
  645. /** test partial day event with every 2nd week interval on Mon, Wed, Fri and no conclusion*/
  646. $vCalendar = clone $this->vCalendar1a;
  647. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;INTERVAL=2;');
  648. // construct event reader
  649. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  650. // test output
  651. $this->assertEquals(
  652. 'Every 2 Weeks on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto)',
  653. $this->service->generateWhenString($eventReader)
  654. );
  655. /** test partial day event with every 2nd week interval on Mon, Wed, Fri and conclusion*/
  656. $vCalendar = clone $this->vCalendar1a;
  657. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;INTERVAL=2;UNTIL=20240722T080000Z;');
  658. // construct event reader
  659. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  660. // test output
  661. $this->assertEquals(
  662. 'Every 2 Weeks on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024',
  663. $this->service->generateWhenString($eventReader)
  664. );
  665. /** test entire day event with every week interval on Mon, Wed, Fri and no conclusion*/
  666. $vCalendar = clone $this->vCalendar2;
  667. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;');
  668. // construct event reader
  669. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  670. // test output
  671. $this->assertEquals(
  672. 'Every Week on Monday, Wednesday, Friday for the entire day',
  673. $this->service->generateWhenString($eventReader)
  674. );
  675. /** test entire day event with every week interval on Mon, Wed, Fri and conclusion*/
  676. $vCalendar = clone $this->vCalendar2;
  677. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20240722T080000Z;');
  678. // construct event reader
  679. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  680. // test output
  681. $this->assertEquals(
  682. 'Every Week on Monday, Wednesday, Friday for the entire day until July 13, 2024',
  683. $this->service->generateWhenString($eventReader)
  684. );
  685. /** test entire day event with every 2nd week interval on Mon, Wed, Fri and no conclusion*/
  686. $vCalendar = clone $this->vCalendar2;
  687. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;INTERVAL=2;');
  688. // construct event reader
  689. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  690. // test output
  691. $this->assertEquals(
  692. 'Every 2 Weeks on Monday, Wednesday, Friday for the entire day',
  693. $this->service->generateWhenString($eventReader)
  694. );
  695. /** test entire day event with every 2nd week interval on Mon, Wed, Fri and conclusion*/
  696. $vCalendar = clone $this->vCalendar2;
  697. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;INTERVAL=2;UNTIL=20240722T080000Z;');
  698. // construct event reader
  699. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  700. // test output
  701. $this->assertEquals(
  702. 'Every 2 Weeks on Monday, Wednesday, Friday for the entire day until July 13, 2024',
  703. $this->service->generateWhenString($eventReader)
  704. );
  705. }
  706. public function testGenerateWhenStringRecurringMonthly(): void {
  707. // construct l10n return maps
  708. $this->l10n->method('l')->willReturnCallback(
  709. function ($v1, $v2, $v3) {
  710. return match (true) {
  711. $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM',
  712. $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM',
  713. $v1 === 'date' && $v2 == (new \DateTime('20241231T080000', (new \DateTimeZone('UTC')))) && $v3 == ['width' => 'long'] => 'December 31, 2024'
  714. };
  715. }
  716. );
  717. $this->l10n->method('t')->willReturnMap([
  718. ['Every Month on the %1$s for the entire day', ['1, 8'], 'Every Month on the 1, 8 for the entire day'],
  719. ['Every Month on the %1$s for the entire day until %2$s', ['1, 8', 'December 31, 2024'], 'Every Month on the 1, 8 for the entire day until December 31, 2024'],
  720. ['Every Month on the %1$s between %2$s - %3$s', ['1, 8', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every Month on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto)'],
  721. ['Every Month on the %1$s between %2$s - %3$s until %4$s', ['1, 8', '8:00 AM', '9:00 AM (America/Toronto)', 'December 31, 2024'], 'Every Month on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024'],
  722. ['Every %1$d Months on the %2$s for the entire day', [2, '1, 8'], 'Every 2 Months on the 1, 8 for the entire day'],
  723. ['Every %1$d Months on the %2$s for the entire day until %3$s', [2, '1, 8', 'December 31, 2024'], 'Every 2 Months on the 1, 8 for the entire day until December 31, 2024'],
  724. ['Every %1$d Months on the %2$s between %3$s - %4$s', [2, '1, 8', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 2 Months on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto)'],
  725. ['Every %1$d Months on the %2$s between %3$s - %4$s until %5$s', [2, '1, 8', '8:00 AM', '9:00 AM (America/Toronto)', 'December 31, 2024'], 'Every 2 Months on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024'],
  726. ['Every Month on the %1$s for the entire day', ['First Sunday, Saturday'], 'Every Month on the First Sunday, Saturday for the entire day'],
  727. ['Every Month on the %1$s for the entire day until %2$s', ['First Sunday, Saturday', 'December 31, 2024'], 'Every Month on the First Sunday, Saturday for the entire day until December 31, 2024'],
  728. ['Every Month on the %1$s between %2$s - %3$s', ['First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every Month on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)'],
  729. ['Every Month on the %1$s between %2$s - %3$s until %4$s', ['First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)', 'December 31, 2024'], 'Every Month on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024'],
  730. ['Every %1$d Months on the %2$s for the entire day', [2, 'First Sunday, Saturday'], 'Every 2 Months on the First Sunday, Saturday for the entire day'],
  731. ['Every %1$d Months on the %2$s for the entire day until %3$s', [2, 'First Sunday, Saturday', 'December 31, 2024'], 'Every 2 Months on the First Sunday, Saturday for the entire day until December 31, 2024'],
  732. ['Every %1$d Months on the %2$s between %3$s - %4$s', [2, 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 2 Months on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)'],
  733. ['Every %1$d Months on the %2$s between %3$s - %4$s until %5$s', [2, 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)', 'December 31, 2024'], 'Every 2 Months on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024'],
  734. ['Could not generate event recurrence statement', [], 'Could not generate event recurrence statement'],
  735. ['Saturday', [], 'Saturday'],
  736. ['Sunday', [], 'Sunday'],
  737. ['First', [], 'First'],
  738. ]);
  739. /** test absolute partial day event with every month interval on 1st, 8th and no conclusion*/
  740. $vCalendar = clone $this->vCalendar1a;
  741. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;');
  742. // construct event reader
  743. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  744. // test output
  745. $this->assertEquals(
  746. 'Every Month on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto)',
  747. $this->service->generateWhenString($eventReader)
  748. );
  749. /** test absolute partial day event with every Month interval on 1st, 8th and conclusion*/
  750. $vCalendar = clone $this->vCalendar1a;
  751. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;UNTIL=20241231T080000Z;');
  752. // construct event reader
  753. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  754. // test output
  755. $this->assertEquals(
  756. 'Every Month on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024',
  757. $this->service->generateWhenString($eventReader)
  758. );
  759. /** test absolute partial day event with every 2nd Month interval on 1st, 8th and no conclusion*/
  760. $vCalendar = clone $this->vCalendar1a;
  761. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;INTERVAL=2;');
  762. // construct event reader
  763. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  764. // test output
  765. $this->assertEquals(
  766. 'Every 2 Months on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto)',
  767. $this->service->generateWhenString($eventReader)
  768. );
  769. /** test absolute partial day event with every 2nd Month interval on 1st, 8th and conclusion*/
  770. $vCalendar = clone $this->vCalendar1a;
  771. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;INTERVAL=2;UNTIL=20241231T080000Z;');
  772. // construct event reader
  773. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  774. // test output
  775. $this->assertEquals(
  776. 'Every 2 Months on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024',
  777. $this->service->generateWhenString($eventReader)
  778. );
  779. /** test absolute entire day event with every Month interval on 1st, 8th and no conclusion*/
  780. $vCalendar = clone $this->vCalendar2;
  781. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;');
  782. // construct event reader
  783. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  784. // test output
  785. $this->assertEquals(
  786. 'Every Month on the 1, 8 for the entire day',
  787. $this->service->generateWhenString($eventReader)
  788. );
  789. /** test absolute entire day event with every Month interval on 1st, 8th and conclusion*/
  790. $vCalendar = clone $this->vCalendar2;
  791. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;UNTIL=20241231T080000Z;');
  792. // construct event reader
  793. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  794. // test output
  795. $this->assertEquals(
  796. 'Every Month on the 1, 8 for the entire day until December 31, 2024',
  797. $this->service->generateWhenString($eventReader)
  798. );
  799. /** test absolute entire day event with every 2nd Month interval on 1st, 8th and no conclusion*/
  800. $vCalendar = clone $this->vCalendar2;
  801. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;INTERVAL=2;');
  802. // construct event reader
  803. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  804. // test output
  805. $this->assertEquals(
  806. 'Every 2 Months on the 1, 8 for the entire day',
  807. $this->service->generateWhenString($eventReader)
  808. );
  809. /** test absolute entire day event with every 2nd Month interval on 1st, 8th and conclusion*/
  810. $vCalendar = clone $this->vCalendar2;
  811. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;INTERVAL=2;UNTIL=20241231T080000Z;');
  812. // construct event reader
  813. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  814. // test output
  815. $this->assertEquals(
  816. 'Every 2 Months on the 1, 8 for the entire day until December 31, 2024',
  817. $this->service->generateWhenString($eventReader)
  818. );
  819. /** test relative partial day event with every month interval on the 1st Saturday, Sunday and no conclusion*/
  820. $vCalendar = clone $this->vCalendar1a;
  821. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;');
  822. // construct event reader
  823. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  824. // test output
  825. $this->assertEquals(
  826. 'Every Month on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)',
  827. $this->service->generateWhenString($eventReader)
  828. );
  829. /** test relative partial day event with every Month interval on the 1st Saturday, Sunday and conclusion*/
  830. $vCalendar = clone $this->vCalendar1a;
  831. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;UNTIL=20241231T080000Z;');
  832. // construct event reader
  833. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  834. // test output
  835. $this->assertEquals(
  836. 'Every Month on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024',
  837. $this->service->generateWhenString($eventReader)
  838. );
  839. /** test relative partial day event with every 2nd Month interval on the 1st Saturday, Sunday and no conclusion*/
  840. $vCalendar = clone $this->vCalendar1a;
  841. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;');
  842. // construct event reader
  843. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  844. // test output
  845. $this->assertEquals(
  846. 'Every 2 Months on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)',
  847. $this->service->generateWhenString($eventReader)
  848. );
  849. /** test relative partial day event with every 2nd Month interval on the 1st Saturday, Sunday and conclusion*/
  850. $vCalendar = clone $this->vCalendar1a;
  851. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;UNTIL=20241231T080000Z;');
  852. // construct event reader
  853. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  854. // test output
  855. $this->assertEquals(
  856. 'Every 2 Months on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024',
  857. $this->service->generateWhenString($eventReader)
  858. );
  859. /** test relative entire day event with every Month interval on the 1st Saturday, Sunday and no conclusion*/
  860. $vCalendar = clone $this->vCalendar2;
  861. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;');
  862. // construct event reader
  863. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  864. // test output
  865. $this->assertEquals(
  866. 'Every Month on the First Sunday, Saturday for the entire day',
  867. $this->service->generateWhenString($eventReader)
  868. );
  869. /** test relative entire day event with every Month interval on the 1st Saturday, Sunday and conclusion*/
  870. $vCalendar = clone $this->vCalendar2;
  871. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;UNTIL=20241231T080000Z;');
  872. // construct event reader
  873. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  874. // test output
  875. $this->assertEquals(
  876. 'Every Month on the First Sunday, Saturday for the entire day until December 31, 2024',
  877. $this->service->generateWhenString($eventReader)
  878. );
  879. /** test relative entire day event with every 2nd Month interval on the 1st Saturday, Sunday and no conclusion*/
  880. $vCalendar = clone $this->vCalendar2;
  881. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;');
  882. // construct event reader
  883. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  884. // test output
  885. $this->assertEquals(
  886. 'Every 2 Months on the First Sunday, Saturday for the entire day',
  887. $this->service->generateWhenString($eventReader)
  888. );
  889. /** test relative entire day event with every 2nd Month interval on the 1st Saturday, Sunday and conclusion*/
  890. $vCalendar = clone $this->vCalendar2;
  891. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;UNTIL=20241231T080000Z;');
  892. // construct event reader
  893. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  894. // test output
  895. $this->assertEquals(
  896. 'Every 2 Months on the First Sunday, Saturday for the entire day until December 31, 2024',
  897. $this->service->generateWhenString($eventReader)
  898. );
  899. }
  900. public function testGenerateWhenStringRecurringYearly(): void {
  901. // construct l10n return maps
  902. $this->l10n->method('l')->willReturnCallback(
  903. function ($v1, $v2, $v3) {
  904. return match (true) {
  905. $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM',
  906. $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM',
  907. $v1 === 'date' && $v2 == (new \DateTime('20260731T040000', (new \DateTimeZone('UTC')))) && $v3 == ['width' => 'long'] => 'July 31, 2026'
  908. };
  909. }
  910. );
  911. $this->l10n->method('t')->willReturnMap([
  912. ['Every Year in %1$s on the %2$s for the entire day', ['July', '1st'], 'Every Year in July on the 1st for the entire day'],
  913. ['Every Year in %1$s on the %2$s for the entire day until %3$s', ['July', '1st', 'July 31, 2026'], 'Every Year in July on the 1st for the entire day until July 31, 2026'],
  914. ['Every Year in %1$s on the %2$s between %3$s - %4$s', ['July', '1st', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every Year in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto)'],
  915. ['Every Year in %1$s on the %2$s between %3$s - %4$s until %5$s', ['July', '1st', '8:00 AM', '9:00 AM (America/Toronto)', 'July 31, 2026'], 'Every Year in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026'],
  916. ['Every %1$d Years in %2$s on the %3$s for the entire day', [2, 'July', '1st'], 'Every 2 Years in July on the 1st for the entire day'],
  917. ['Every %1$d Years in %2$s on the %3$s for the entire day until %4$s', [2, 'July', '1st', 'July 31, 2026'], 'Every 2 Years in July on the 1st for the entire day until July 31, 2026'],
  918. ['Every %1$d Years in %2$s on the %3$s between %4$s - %5$s', [2, 'July', '1st', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 2 Years in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto)'],
  919. ['Every %1$d Years in %2$s on the %3$s between %4$s - %5$s until %6$s', [2, 'July', '1st', '8:00 AM', '9:00 AM (America/Toronto)', 'July 31, 2026'], 'Every 2 Years in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026'],
  920. ['Every Year in %1$s on the %2$s for the entire day', ['July', 'First Sunday, Saturday'], 'Every Year in July on the First Sunday, Saturday for the entire day'],
  921. ['Every Year in %1$s on the %2$s for the entire day until %3$s', ['July', 'First Sunday, Saturday', 'July 31, 2026'], 'Every Year in July on the First Sunday, Saturday for the entire day until July 31, 2026'],
  922. ['Every Year in %1$s on the %2$s between %3$s - %4$s', ['July', 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every Year in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)'],
  923. ['Every Year in %1$s on the %2$s between %3$s - %4$s until %5$s', ['July', 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)', 'July 31, 2026'], 'Every Year in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026'],
  924. ['Every %1$d Years in %2$s on the %3$s for the entire day', [2, 'July', 'First Sunday, Saturday'], 'Every 2 Years in July on the First Sunday, Saturday for the entire day'],
  925. ['Every %1$d Years in %2$s on the %3$s for the entire day until %4$s', [2, 'July', 'First Sunday, Saturday', 'July 31, 2026'], 'Every 2 Years in July on the First Sunday, Saturday for the entire day until July 31, 2026'],
  926. ['Every %1$d Years in %2$s on the %3$s between %4$s - %5$s', [2, 'July', 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 2 Years in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)'],
  927. ['Every %1$d Years in %2$s on the %3$s between %4$s - %5$s until %6$s', [2, 'July', 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)', 'July 31, 2026'], 'Every 2 Years in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026'],
  928. ['Could not generate event recurrence statement', [], 'Could not generate event recurrence statement'],
  929. ['July', [], 'July'],
  930. ['Saturday', [], 'Saturday'],
  931. ['Sunday', [], 'Sunday'],
  932. ['First', [], 'First'],
  933. ]);
  934. /** test absolute partial day event with every year interval on July 1 and no conclusion*/
  935. $vCalendar = clone $this->vCalendar1a;
  936. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;');
  937. // construct event reader
  938. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  939. // test output
  940. $this->assertEquals(
  941. 'Every Year in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto)',
  942. $this->service->generateWhenString($eventReader)
  943. );
  944. /** test absolute partial day event with every year interval on July 1 and conclusion*/
  945. $vCalendar = clone $this->vCalendar1a;
  946. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;UNTIL=20260731T040000Z');
  947. // construct event reader
  948. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  949. // test output
  950. $this->assertEquals(
  951. 'Every Year in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026',
  952. $this->service->generateWhenString($eventReader)
  953. );
  954. /** test absolute partial day event with every 2nd year interval on July 1 and no conclusion*/
  955. $vCalendar = clone $this->vCalendar1a;
  956. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;INTERVAL=2;');
  957. // construct event reader
  958. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  959. // test output
  960. $this->assertEquals(
  961. 'Every 2 Years in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto)',
  962. $this->service->generateWhenString($eventReader)
  963. );
  964. /** test absolute partial day event with every 2nd year interval on July 1 and conclusion*/
  965. $vCalendar = clone $this->vCalendar1a;
  966. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;INTERVAL=2;UNTIL=20260731T040000Z;');
  967. // construct event reader
  968. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  969. // test output
  970. $this->assertEquals(
  971. 'Every 2 Years in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026',
  972. $this->service->generateWhenString($eventReader)
  973. );
  974. /** test absolute entire day event with every year interval on July 1 and no conclusion*/
  975. $vCalendar = clone $this->vCalendar2;
  976. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;');
  977. // construct event reader
  978. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  979. // test output
  980. $this->assertEquals(
  981. 'Every Year in July on the 1st for the entire day',
  982. $this->service->generateWhenString($eventReader)
  983. );
  984. /** test absolute entire day event with every year interval on July 1 and conclusion*/
  985. $vCalendar = clone $this->vCalendar2;
  986. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;UNTIL=20260731T040000Z;');
  987. // construct event reader
  988. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  989. // test output
  990. $this->assertEquals(
  991. 'Every Year in July on the 1st for the entire day until July 31, 2026',
  992. $this->service->generateWhenString($eventReader)
  993. );
  994. /** test absolute entire day event with every 2nd year interval on July 1 and no conclusion*/
  995. $vCalendar = clone $this->vCalendar2;
  996. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;INTERVAL=2;');
  997. // construct event reader
  998. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  999. // test output
  1000. $this->assertEquals(
  1001. 'Every 2 Years in July on the 1st for the entire day',
  1002. $this->service->generateWhenString($eventReader)
  1003. );
  1004. /** test absolute entire day event with every 2nd year interval on July 1 and conclusion*/
  1005. $vCalendar = clone $this->vCalendar2;
  1006. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;INTERVAL=2;UNTIL=20260731T040000Z;');
  1007. // construct event reader
  1008. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1009. // test output
  1010. $this->assertEquals(
  1011. 'Every 2 Years in July on the 1st for the entire day until July 31, 2026',
  1012. $this->service->generateWhenString($eventReader)
  1013. );
  1014. /** test relative partial day event with every year interval on the 1st Saturday, Sunday in July and no conclusion*/
  1015. $vCalendar = clone $this->vCalendar1a;
  1016. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;');
  1017. // construct event reader
  1018. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1019. // test output
  1020. $this->assertEquals(
  1021. 'Every Year in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)',
  1022. $this->service->generateWhenString($eventReader)
  1023. );
  1024. /** test relative partial day event with every year interval on the 1st Saturday, Sunday in July and conclusion*/
  1025. $vCalendar = clone $this->vCalendar1a;
  1026. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;UNTIL=20260731T040000Z;');
  1027. // construct event reader
  1028. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1029. // test output
  1030. $this->assertEquals(
  1031. 'Every Year in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026',
  1032. $this->service->generateWhenString($eventReader)
  1033. );
  1034. /** test relative partial day event with every 2nd year interval on the 1st Saturday, Sunday in July and no conclusion*/
  1035. $vCalendar = clone $this->vCalendar1a;
  1036. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;');
  1037. // construct event reader
  1038. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1039. // test output
  1040. $this->assertEquals(
  1041. 'Every 2 Years in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)',
  1042. $this->service->generateWhenString($eventReader)
  1043. );
  1044. /** test relative partial day event with every 2nd year interval on the 1st Saturday, Sunday in July and conclusion*/
  1045. $vCalendar = clone $this->vCalendar1a;
  1046. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;UNTIL=20260731T040000Z;');
  1047. // construct event reader
  1048. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1049. // test output
  1050. $this->assertEquals(
  1051. 'Every 2 Years in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026',
  1052. $this->service->generateWhenString($eventReader)
  1053. );
  1054. /** test relative entire day event with every year interval on the 1st Saturday, Sunday in July and no conclusion*/
  1055. $vCalendar = clone $this->vCalendar2;
  1056. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;');
  1057. // construct event reader
  1058. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1059. // test output
  1060. $this->assertEquals(
  1061. 'Every Year in July on the First Sunday, Saturday for the entire day',
  1062. $this->service->generateWhenString($eventReader)
  1063. );
  1064. /** test relative entire day event with every year interval on the 1st Saturday, Sunday in July and conclusion*/
  1065. $vCalendar = clone $this->vCalendar2;
  1066. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;UNTIL=20260731T040000Z;');
  1067. // construct event reader
  1068. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1069. // test output
  1070. $this->assertEquals(
  1071. 'Every Year in July on the First Sunday, Saturday for the entire day until July 31, 2026',
  1072. $this->service->generateWhenString($eventReader)
  1073. );
  1074. /** test relative entire day event with every 2nd year interval on the 1st Saturday, Sunday in July and no conclusion*/
  1075. $vCalendar = clone $this->vCalendar2;
  1076. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;');
  1077. // construct event reader
  1078. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1079. // test output
  1080. $this->assertEquals(
  1081. 'Every 2 Years in July on the First Sunday, Saturday for the entire day',
  1082. $this->service->generateWhenString($eventReader)
  1083. );
  1084. /** test relative entire day event with every 2nd year interval on the 1st Saturday, Sunday in July and conclusion*/
  1085. $vCalendar = clone $this->vCalendar2;
  1086. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;UNTIL=20260731T040000Z;');
  1087. // construct event reader
  1088. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1089. // test output
  1090. $this->assertEquals(
  1091. 'Every 2 Years in July on the First Sunday, Saturday for the entire day until July 31, 2026',
  1092. $this->service->generateWhenString($eventReader)
  1093. );
  1094. }
  1095. public function testGenerateWhenStringRecurringFixed(): void {
  1096. // construct l10n return maps
  1097. $this->l10n->method('l')->willReturnCallback(
  1098. function ($v1, $v2, $v3) {
  1099. return match (true) {
  1100. $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM',
  1101. $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM',
  1102. $v1 === 'date' && $v2 == (new \DateTime('20240713T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 13, 2024'
  1103. };
  1104. }
  1105. );
  1106. $this->l10n->method('t')->willReturnMap([
  1107. ['On specific dates for the entire day until %1$s', ['July 13, 2024'], 'On specific dates for the entire day until July 13, 2024'],
  1108. ['On specific dates between %1$s - %2$s until %3$s', ['8:00 AM', '9:00 AM (America/Toronto)', 'July 13, 2024'], 'On specific dates between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024'],
  1109. ]);
  1110. /** test partial day event with every day interval and conclusion*/
  1111. $vCalendar = clone $this->vCalendar1a;
  1112. $vCalendar->VEVENT[0]->add('RDATE', '20240703T080000,20240709T080000,20240713T080000');
  1113. // construct event reader
  1114. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1115. // test output
  1116. $this->assertEquals(
  1117. 'On specific dates between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024',
  1118. $this->service->generateWhenString($eventReader)
  1119. );
  1120. /** test entire day event with every day interval and no conclusion*/
  1121. $vCalendar = clone $this->vCalendar2;
  1122. $vCalendar->VEVENT[0]->add('RDATE', '20240703T080000,20240709T080000,20240713T080000');
  1123. // construct event reader
  1124. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1125. // test output
  1126. $this->assertEquals(
  1127. 'On specific dates for the entire day until July 13, 2024',
  1128. $this->service->generateWhenString($eventReader)
  1129. );
  1130. }
  1131. public function testGenerateOccurringStringWithRrule(): void {
  1132. // construct l10n return(s)
  1133. $this->l10n->method('l')->willReturnCallback(
  1134. function ($v1, $v2, $v3) {
  1135. return match (true) {
  1136. $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 1, 2024',
  1137. $v1 === 'date' && $v2 == (new \DateTime('20240703T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 3, 2024',
  1138. $v1 === 'date' && $v2 == (new \DateTime('20240705T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 5, 2024'
  1139. };
  1140. }
  1141. );
  1142. $this->l10n->method('t')->willReturnMap([
  1143. ['In a %1$s on %2$s', ['day', 'July 1, 2024'], 'In a day on July 1, 2024'],
  1144. ['In a %1$s on %2$s then on %3$s', ['day', 'July 1, 2024', 'July 3, 2024'], 'In a day on July 1, 2024 then on July 3, 2024'],
  1145. ['In a %1$s on %2$s then on %3$s and %4$s', ['day', 'July 1, 2024', 'July 3, 2024', 'July 5, 2024'], 'In a day on July 1, 2024 then on July 3, 2024 and July 5, 2024'],
  1146. ['In %1$s %2$s on %3$s', [2, 'days', 'July 1, 2024'], 'In 2 days on July 1, 2024'],
  1147. ['In %1$s %2$s on %3$s then on %4$s', [2, 'days', 'July 1, 2024', 'July 3, 2024'], 'In 2 days on July 1, 2024 then on July 3, 2024'],
  1148. ['In %1$s %2$s on %3$s then on %4$s and %5$s', [2, 'days', 'July 1, 2024', 'July 3, 2024', 'July 5, 2024'], 'In 2 days on July 1, 2024 then on July 3, 2024 and July 5, 2024'],
  1149. ]);
  1150. // construct time factory return(s)
  1151. $this->timeFactory->method('getDateTime')->willReturnOnConsecutiveCalls(
  1152. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1153. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1154. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1155. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1156. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1157. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1158. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1159. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1160. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1161. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1162. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1163. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1164. );
  1165. /** test patrial day recurring event in 1 day with single occurrence remaining */
  1166. $vCalendar = clone $this->vCalendar1a;
  1167. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=1');
  1168. // construct event reader
  1169. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1170. // test output
  1171. $this->assertEquals(
  1172. 'In a day on July 1, 2024',
  1173. $this->service->generateOccurringString($eventReader)
  1174. );
  1175. /** test patrial day recurring event in 1 day with two occurrences remaining */
  1176. $vCalendar = clone $this->vCalendar1a;
  1177. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=2');
  1178. // construct event reader
  1179. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1180. // test output
  1181. $this->assertEquals(
  1182. 'In a day on July 1, 2024 then on July 3, 2024',
  1183. $this->service->generateOccurringString($eventReader)
  1184. );
  1185. /** test patrial day recurring event in 1 day with three occurrences remaining */
  1186. $vCalendar = clone $this->vCalendar1a;
  1187. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=3');
  1188. // construct event reader
  1189. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1190. // test output
  1191. $this->assertEquals(
  1192. 'In a day on July 1, 2024 then on July 3, 2024 and July 5, 2024',
  1193. $this->service->generateOccurringString($eventReader)
  1194. );
  1195. /** test patrial day recurring event in 2 days with single occurrence remaining */
  1196. $vCalendar = clone $this->vCalendar1a;
  1197. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=1');
  1198. // construct event reader
  1199. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1200. // test output
  1201. $this->assertEquals(
  1202. 'In 2 days on July 1, 2024',
  1203. $this->service->generateOccurringString($eventReader)
  1204. );
  1205. /** test patrial day recurring event in 2 days with two occurrences remaining */
  1206. $vCalendar = clone $this->vCalendar1a;
  1207. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=2');
  1208. // construct event reader
  1209. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1210. // test output
  1211. $this->assertEquals(
  1212. 'In 2 days on July 1, 2024 then on July 3, 2024',
  1213. $this->service->generateOccurringString($eventReader)
  1214. );
  1215. /** test patrial day recurring event in 2 days with three occurrences remaining */
  1216. $vCalendar = clone $this->vCalendar1a;
  1217. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=3');
  1218. // construct event reader
  1219. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1220. // test output
  1221. $this->assertEquals(
  1222. 'In 2 days on July 1, 2024 then on July 3, 2024 and July 5, 2024',
  1223. $this->service->generateOccurringString($eventReader)
  1224. );
  1225. }
  1226. public function testGenerateOccurringStringWithRdate(): void {
  1227. // construct l10n return(s)
  1228. $this->l10n->method('l')->willReturnCallback(
  1229. function ($v1, $v2, $v3) {
  1230. return match (true) {
  1231. $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 1, 2024',
  1232. $v1 === 'date' && $v2 == (new \DateTime('20240703T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 3, 2024',
  1233. $v1 === 'date' && $v2 == (new \DateTime('20240705T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 5, 2024'
  1234. };
  1235. }
  1236. );
  1237. $this->l10n->method('t')->willReturnMap([
  1238. ['In a %1$s on %2$s', ['day', 'July 1, 2024'], 'In a day on July 1, 2024'],
  1239. ['In a %1$s on %2$s then on %3$s', ['day', 'July 1, 2024', 'July 3, 2024'], 'In a day on July 1, 2024 then on July 3, 2024'],
  1240. ['In a %1$s on %2$s then on %3$s and %4$s', ['day', 'July 1, 2024', 'July 3, 2024', 'July 5, 2024'], 'In a day on July 1, 2024 then on July 3, 2024 and July 5, 2024'],
  1241. ['In %1$s %2$s on %3$s', [2, 'days', 'July 1, 2024'], 'In 2 days on July 1, 2024'],
  1242. ['In %1$s %2$s on %3$s then on %4$s', [2, 'days', 'July 1, 2024', 'July 3, 2024'], 'In 2 days on July 1, 2024 then on July 3, 2024'],
  1243. ['In %1$s %2$s on %3$s then on %4$s and %5$s', [2, 'days', 'July 1, 2024', 'July 3, 2024', 'July 5, 2024'], 'In 2 days on July 1, 2024 then on July 3, 2024 and July 5, 2024'],
  1244. ]);
  1245. // construct time factory return(s)
  1246. $this->timeFactory->method('getDateTime')->willReturnOnConsecutiveCalls(
  1247. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1248. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1249. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1250. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1251. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1252. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1253. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1254. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1255. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1256. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1257. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1258. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1259. );
  1260. /** test patrial day recurring event in 1 day with single occurrence remaining */
  1261. $vCalendar = clone $this->vCalendar1a;
  1262. $vCalendar->VEVENT[0]->add('RDATE', '20240701T080000');
  1263. // construct event reader
  1264. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1265. // test output
  1266. $this->assertEquals(
  1267. 'In a day on July 1, 2024',
  1268. $this->service->generateOccurringString($eventReader),
  1269. 'test patrial day recurring event in 1 day with single occurrence remaining'
  1270. );
  1271. /** test patrial day recurring event in 1 day with two occurrences remaining */
  1272. $vCalendar = clone $this->vCalendar1a;
  1273. $vCalendar->VEVENT[0]->add('RDATE', '20240701T080000,20240703T080000');
  1274. // construct event reader
  1275. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1276. // test output
  1277. $this->assertEquals(
  1278. 'In a day on July 1, 2024 then on July 3, 2024',
  1279. $this->service->generateOccurringString($eventReader),
  1280. 'test patrial day recurring event in 1 day with two occurrences remaining'
  1281. );
  1282. /** test patrial day recurring event in 1 day with three occurrences remaining */
  1283. $vCalendar = clone $this->vCalendar1a;
  1284. $vCalendar->VEVENT[0]->add('RDATE', '20240701T080000,20240703T080000,20240705T080000');
  1285. // construct event reader
  1286. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1287. // test output
  1288. $this->assertEquals(
  1289. 'In a day on July 1, 2024 then on July 3, 2024 and July 5, 2024',
  1290. $this->service->generateOccurringString($eventReader),
  1291. ''
  1292. );
  1293. /** test patrial day recurring event in 2 days with single occurrences remaining */
  1294. $vCalendar = clone $this->vCalendar1a;
  1295. $vCalendar->VEVENT[0]->add('RDATE', '20240701T080000');
  1296. // construct event reader
  1297. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1298. // test output
  1299. $this->assertEquals(
  1300. 'In 2 days on July 1, 2024',
  1301. $this->service->generateOccurringString($eventReader),
  1302. ''
  1303. );
  1304. /** test patrial day recurring event in 2 days with two occurrences remaining */
  1305. $vCalendar = clone $this->vCalendar1a;
  1306. $vCalendar->VEVENT[0]->add('RDATE', '20240701T080000');
  1307. $vCalendar->VEVENT[0]->add('RDATE', '20240703T080000');
  1308. // construct event reader
  1309. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1310. // test output
  1311. $this->assertEquals(
  1312. 'In 2 days on July 1, 2024 then on July 3, 2024',
  1313. $this->service->generateOccurringString($eventReader),
  1314. ''
  1315. );
  1316. /** test patrial day recurring event in 2 days with three occurrences remaining */
  1317. $vCalendar = clone $this->vCalendar1a;
  1318. $vCalendar->VEVENT[0]->add('RDATE', '20240701T080000');
  1319. $vCalendar->VEVENT[0]->add('RDATE', '20240703T080000');
  1320. $vCalendar->VEVENT[0]->add('RDATE', '20240705T080000');
  1321. // construct event reader
  1322. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1323. // test output
  1324. $this->assertEquals(
  1325. 'In 2 days on July 1, 2024 then on July 3, 2024 and July 5, 2024',
  1326. $this->service->generateOccurringString($eventReader),
  1327. 'test patrial day recurring event in 2 days with three occurrences remaining'
  1328. );
  1329. }
  1330. public function testGenerateOccurringStringWithOneExdate(): void {
  1331. // construct l10n return(s)
  1332. $this->l10n->method('l')->willReturnCallback(
  1333. function ($v1, $v2, $v3) {
  1334. return match (true) {
  1335. $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 1, 2024',
  1336. $v1 === 'date' && $v2 == (new \DateTime('20240705T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 5, 2024',
  1337. $v1 === 'date' && $v2 == (new \DateTime('20240707T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 7, 2024'
  1338. };
  1339. }
  1340. );
  1341. $this->l10n->method('t')->willReturnMap([
  1342. ['In a %1$s on %2$s', ['day', 'July 1, 2024'], 'In a day on July 1, 2024'],
  1343. ['In a %1$s on %2$s then on %3$s', ['day', 'July 1, 2024', 'July 5, 2024'], 'In a day on July 1, 2024 then on July 5, 2024'],
  1344. ['In a %1$s on %2$s then on %3$s and %4$s', ['day', 'July 1, 2024', 'July 5, 2024', 'July 7, 2024'], 'In a day on July 1, 2024 then on July 5, 2024 and July 7, 2024'],
  1345. ['In %1$s %2$s on %3$s', [2, 'days', 'July 1, 2024'], 'In 2 days on July 1, 2024'],
  1346. ['In %1$s %2$s on %3$s then on %4$s', [2, 'days', 'July 1, 2024', 'July 5, 2024'], 'In 2 days on July 1, 2024 then on July 5, 2024'],
  1347. ['In %1$s %2$s on %3$s then on %4$s and %5$s', [2, 'days', 'July 1, 2024', 'July 5, 2024', 'July 7, 2024'], 'In 2 days on July 1, 2024 then on July 5, 2024 and July 7, 2024'],
  1348. ]);
  1349. // construct time factory return(s)
  1350. $this->timeFactory->method('getDateTime')->willReturnOnConsecutiveCalls(
  1351. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1352. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1353. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1354. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1355. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1356. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1357. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1358. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1359. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1360. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1361. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1362. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1363. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1364. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1365. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1366. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1367. );
  1368. /** test patrial day recurring event in 1 day with single occurrence remaining and one exception */
  1369. $vCalendar = clone $this->vCalendar1a;
  1370. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=1');
  1371. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1372. // construct event reader
  1373. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1374. // test output
  1375. $this->assertEquals(
  1376. 'In a day on July 1, 2024',
  1377. $this->service->generateOccurringString($eventReader),
  1378. 'test patrial day recurring event in 1 day with single occurrence remaining and one exception'
  1379. );
  1380. /** test patrial day recurring event in 1 day with two occurrences remaining and one exception */
  1381. $vCalendar = clone $this->vCalendar1a;
  1382. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=2');
  1383. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1384. // construct event reader
  1385. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1386. // test output
  1387. $this->assertEquals(
  1388. 'In a day on July 1, 2024',
  1389. $this->service->generateOccurringString($eventReader),
  1390. 'test patrial day recurring event in 1 day with two occurrences remaining and one exception'
  1391. );
  1392. /** test patrial day recurring event in 1 day with three occurrences remaining and one exception */
  1393. $vCalendar = clone $this->vCalendar1a;
  1394. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=3');
  1395. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1396. // construct event reader
  1397. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1398. // test output
  1399. $this->assertEquals(
  1400. 'In a day on July 1, 2024 then on July 5, 2024',
  1401. $this->service->generateOccurringString($eventReader),
  1402. 'test patrial day recurring event in 1 day with three occurrences remaining and one exception'
  1403. );
  1404. /** test patrial day recurring event in 1 day with four occurrences remaining and one exception */
  1405. $vCalendar = clone $this->vCalendar1a;
  1406. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=4');
  1407. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1408. // construct event reader
  1409. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1410. // test output
  1411. $this->assertEquals(
  1412. 'In a day on July 1, 2024 then on July 5, 2024 and July 7, 2024',
  1413. $this->service->generateOccurringString($eventReader),
  1414. 'test patrial day recurring event in 1 day with four occurrences remaining and one exception'
  1415. );
  1416. /** test patrial day recurring event in 2 days with single occurrences remaining and one exception */
  1417. $vCalendar = clone $this->vCalendar1a;
  1418. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=1');
  1419. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1420. // construct event reader
  1421. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1422. // test output
  1423. $this->assertEquals(
  1424. 'In 2 days on July 1, 2024',
  1425. $this->service->generateOccurringString($eventReader),
  1426. 'test patrial day recurring event in 2 days with single occurrences remaining and one exception'
  1427. );
  1428. /** test patrial day recurring event in 2 days with two occurrences remaining and one exception */
  1429. $vCalendar = clone $this->vCalendar1a;
  1430. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=2');
  1431. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1432. // construct event reader
  1433. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1434. // test output
  1435. $this->assertEquals(
  1436. 'In 2 days on July 1, 2024',
  1437. $this->service->generateOccurringString($eventReader),
  1438. 'test patrial day recurring event in 2 days with two occurrences remaining and one exception'
  1439. );
  1440. /** test patrial day recurring event in 2 days with three occurrences remaining and one exception */
  1441. $vCalendar = clone $this->vCalendar1a;
  1442. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=3');
  1443. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1444. // construct event reader
  1445. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1446. // test output
  1447. $this->assertEquals(
  1448. 'In 2 days on July 1, 2024 then on July 5, 2024',
  1449. $this->service->generateOccurringString($eventReader),
  1450. 'test patrial day recurring event in 2 days with three occurrences remaining and one exception'
  1451. );
  1452. /** test patrial day recurring event in 2 days with four occurrences remaining and one exception */
  1453. $vCalendar = clone $this->vCalendar1a;
  1454. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=4');
  1455. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1456. // construct event reader
  1457. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1458. // test output
  1459. $this->assertEquals(
  1460. 'In 2 days on July 1, 2024 then on July 5, 2024 and July 7, 2024',
  1461. $this->service->generateOccurringString($eventReader),
  1462. 'test patrial day recurring event in 2 days with four occurrences remaining and one exception'
  1463. );
  1464. }
  1465. public function testGenerateOccurringStringWithTwoExdate(): void {
  1466. // construct l10n return(s)
  1467. $this->l10n->method('l')->willReturnCallback(
  1468. function ($v1, $v2, $v3) {
  1469. return match (true) {
  1470. $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 1, 2024',
  1471. $v1 === 'date' && $v2 == (new \DateTime('20240705T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 5, 2024',
  1472. $v1 === 'date' && $v2 == (new \DateTime('20240709T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 9, 2024'
  1473. };
  1474. }
  1475. );
  1476. $this->l10n->method('t')->willReturnMap([
  1477. ['In a %1$s on %2$s', ['day', 'July 1, 2024'], 'In a day on July 1, 2024'],
  1478. ['In a %1$s on %2$s then on %3$s', ['day', 'July 1, 2024', 'July 5, 2024'], 'In a day on July 1, 2024 then on July 5, 2024'],
  1479. ['In a %1$s on %2$s then on %3$s and %4$s', ['day', 'July 1, 2024', 'July 5, 2024', 'July 9, 2024'], 'In a day on July 1, 2024 then on July 5, 2024 and July 9, 2024'],
  1480. ['In %1$s %2$s on %3$s', [2, 'days', 'July 1, 2024'], 'In 2 days on July 1, 2024'],
  1481. ['In %1$s %2$s on %3$s then on %4$s', [2, 'days', 'July 1, 2024', 'July 5, 2024'], 'In 2 days on July 1, 2024 then on July 5, 2024'],
  1482. ['In %1$s %2$s on %3$s then on %4$s and %5$s', [2, 'days', 'July 1, 2024', 'July 5, 2024', 'July 9, 2024'], 'In 2 days on July 1, 2024 then on July 5, 2024 and July 9, 2024'],
  1483. ]);
  1484. // construct time factory return(s)
  1485. $this->timeFactory->method('getDateTime')->willReturnOnConsecutiveCalls(
  1486. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1487. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1488. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1489. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1490. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1491. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1492. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1493. (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))),
  1494. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1495. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1496. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1497. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1498. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1499. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1500. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1501. (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))),
  1502. );
  1503. /** test patrial day recurring event in 1 day with single occurrence remaining and two exception */
  1504. $vCalendar = clone $this->vCalendar1a;
  1505. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=1');
  1506. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1507. $vCalendar->VEVENT[0]->add('EXDATE', '20240707T080000');
  1508. // construct event reader
  1509. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1510. // test output
  1511. $this->assertEquals(
  1512. 'In a day on July 1, 2024',
  1513. $this->service->generateOccurringString($eventReader),
  1514. 'test patrial day recurring event in 1 day with single occurrence remaining and two exception'
  1515. );
  1516. /** test patrial day recurring event in 1 day with two occurrences remaining and two exception */
  1517. $vCalendar = clone $this->vCalendar1a;
  1518. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=2');
  1519. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1520. $vCalendar->VEVENT[0]->add('EXDATE', '20240707T080000');
  1521. // construct event reader
  1522. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1523. // test output
  1524. $this->assertEquals(
  1525. 'In a day on July 1, 2024',
  1526. $this->service->generateOccurringString($eventReader),
  1527. 'test patrial day recurring event in 1 day with two occurrences remaining and two exception'
  1528. );
  1529. /** test patrial day recurring event in 1 day with three occurrences remaining and two exception */
  1530. $vCalendar = clone $this->vCalendar1a;
  1531. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=3');
  1532. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1533. $vCalendar->VEVENT[0]->add('EXDATE', '20240707T080000');
  1534. // construct event reader
  1535. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1536. // test output
  1537. $this->assertEquals(
  1538. 'In a day on July 1, 2024 then on July 5, 2024',
  1539. $this->service->generateOccurringString($eventReader),
  1540. 'test patrial day recurring event in 1 day with three occurrences remaining and two exception'
  1541. );
  1542. /** test patrial day recurring event in 1 day with four occurrences remaining and two exception */
  1543. $vCalendar = clone $this->vCalendar1a;
  1544. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=5');
  1545. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1546. $vCalendar->VEVENT[0]->add('EXDATE', '20240707T080000');
  1547. // construct event reader
  1548. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1549. // test output
  1550. $this->assertEquals(
  1551. 'In a day on July 1, 2024 then on July 5, 2024 and July 9, 2024',
  1552. $this->service->generateOccurringString($eventReader),
  1553. 'test patrial day recurring event in 1 day with four occurrences remaining and two exception'
  1554. );
  1555. /** test patrial day recurring event in 2 days with single occurrences remaining and two exception */
  1556. $vCalendar = clone $this->vCalendar1a;
  1557. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=1');
  1558. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1559. $vCalendar->VEVENT[0]->add('EXDATE', '20240707T080000');
  1560. // construct event reader
  1561. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1562. // test output
  1563. $this->assertEquals(
  1564. 'In 2 days on July 1, 2024',
  1565. $this->service->generateOccurringString($eventReader),
  1566. 'test patrial day recurring event in 2 days with single occurrences remaining and two exception'
  1567. );
  1568. /** test patrial day recurring event in 2 days with two occurrences remaining and two exception */
  1569. $vCalendar = clone $this->vCalendar1a;
  1570. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=2');
  1571. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1572. $vCalendar->VEVENT[0]->add('EXDATE', '20240707T080000');
  1573. // construct event reader
  1574. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1575. // test output
  1576. $this->assertEquals(
  1577. 'In 2 days on July 1, 2024',
  1578. $this->service->generateOccurringString($eventReader),
  1579. 'test patrial day recurring event in 2 days with two occurrences remaining and two exception'
  1580. );
  1581. /** test patrial day recurring event in 2 days with three occurrences remaining and two exception */
  1582. $vCalendar = clone $this->vCalendar1a;
  1583. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=3');
  1584. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1585. $vCalendar->VEVENT[0]->add('EXDATE', '20240707T080000');
  1586. // construct event reader
  1587. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1588. // test output
  1589. $this->assertEquals(
  1590. 'In 2 days on July 1, 2024 then on July 5, 2024',
  1591. $this->service->generateOccurringString($eventReader),
  1592. 'test patrial day recurring event in 2 days with three occurrences remaining and two exception'
  1593. );
  1594. /** test patrial day recurring event in 2 days with five occurrences remaining and two exception */
  1595. $vCalendar = clone $this->vCalendar1a;
  1596. $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=5');
  1597. $vCalendar->VEVENT[0]->add('EXDATE', '20240703T080000');
  1598. $vCalendar->VEVENT[0]->add('EXDATE', '20240707T080000');
  1599. // construct event reader
  1600. $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue());
  1601. // test output
  1602. $this->assertEquals(
  1603. 'In 2 days on July 1, 2024 then on July 5, 2024 and July 9, 2024',
  1604. $this->service->generateOccurringString($eventReader),
  1605. 'test patrial day recurring event in 2 days with five occurrences remaining and two exception'
  1606. );
  1607. }
  1608. }