1+ /**
2+ * Calendar component tests
3+ * Covers the dedicated testing fixture for the Add to Calendar menu and download actions.
4+ */
5+
6+ import { readFile } from 'node:fs/promises'
7+ import { BasePage , expect , test } from '@test/e2e/helpers'
8+
9+ const fixturePath = '/testing/calendar'
10+ const componentSelector = '[data-testid="add-to-calendar"]'
11+ const triggerSelector = `${ componentSelector } [data-calendar-trigger]`
12+ const menuSelector = `${ componentSelector } [data-calendar-menu]`
13+ const googleLinkSelector = `${ componentSelector } [data-calendar-link="google"]`
14+ const outlookLinkSelector = `${ componentSelector } [data-calendar-link="outlook"]`
15+ const downloadButtonSelector = `${ componentSelector } [data-calendar-download-ics]`
16+
17+ const loadCalendarFixture = async ( playwrightPage : Parameters < typeof BasePage . init > [ 0 ] ) => {
18+ const page = await BasePage . init ( playwrightPage )
19+ await page . goto ( fixturePath )
20+ await expect ( page . locator ( componentSelector ) ) . toBeVisible ( )
21+ return page
22+ }
23+
24+ test . describe ( 'Calendar Component' , ( ) => {
25+ test ( '@ready renders the testing fixture with a collapsed calendar menu' , async ( { page : playwrightPage } ) => {
26+ const page = await loadCalendarFixture ( playwrightPage )
27+
28+ await expect ( playwrightPage . getByRole ( 'heading' , { name : 'Calendar Component Test Fixture' } ) ) . toBeVisible ( )
29+ await expect ( page . locator ( triggerSelector ) ) . toHaveText ( / A d d t o c a l e n d a r / i)
30+ await expect ( page . locator ( menuSelector ) ) . toHaveClass ( / h i d d e n / )
31+ await expect ( page . locator ( triggerSelector ) ) . toHaveAttribute ( 'aria-expanded' , 'false' )
32+ } )
33+
34+ test ( '@ready toggles the menu and populates Google and Outlook links from fixture metadata' , async ( {
35+ page : playwrightPage ,
36+ } ) => {
37+ const page = await loadCalendarFixture ( playwrightPage )
38+
39+ await page . locator ( triggerSelector ) . click ( )
40+
41+ await expect ( page . locator ( menuSelector ) ) . not . toHaveClass ( / h i d d e n / )
42+ await expect ( page . locator ( triggerSelector ) ) . toHaveAttribute ( 'aria-expanded' , 'true' )
43+
44+ const googleHref = await page . locator ( googleLinkSelector ) . getAttribute ( 'href' )
45+ expect ( googleHref ) . toBeTruthy ( )
46+
47+ if ( googleHref ) {
48+ const googleUrl = new URL ( googleHref )
49+ expect ( googleUrl . hostname ) . toContain ( 'calendar.google.com' )
50+ expect ( googleUrl . pathname ) . toBe ( '/calendar/render' )
51+ expect ( googleUrl . searchParams . get ( 'action' ) ) . toBe ( 'TEMPLATE' )
52+ expect ( googleUrl . searchParams . get ( 'text' ) ) . toBe ( 'Team sync' )
53+ expect ( googleUrl . searchParams . get ( 'dates' ) ) . toBe ( '20300102T130000Z/20300102T140000Z' )
54+ expect ( googleUrl . searchParams . get ( 'details' ) ) . toBe ( 'Weekly team sync' )
55+ expect ( googleUrl . searchParams . get ( 'location' ) ) . toBe ( 'Remote' )
56+ }
57+
58+ const outlookHref = await page . locator ( outlookLinkSelector ) . getAttribute ( 'href' )
59+ expect ( outlookHref ) . toBeTruthy ( )
60+
61+ if ( outlookHref ) {
62+ const outlookUrl = new URL ( outlookHref )
63+ expect ( outlookUrl . hostname ) . toContain ( 'outlook.office.com' )
64+ expect ( outlookUrl . pathname ) . toBe ( '/calendar/0/deeplink/compose' )
65+ expect ( outlookUrl . searchParams . get ( 'path' ) ) . toBe ( '/calendar/action/compose' )
66+ expect ( outlookUrl . searchParams . get ( 'rru' ) ) . toBe ( 'addevent' )
67+ expect ( outlookUrl . searchParams . get ( 'subject' ) ) . toBe ( 'Team sync' )
68+ expect ( outlookUrl . searchParams . get ( 'startdt' ) ) . toBe ( '2030-01-02T13:00:00.000Z' )
69+ expect ( outlookUrl . searchParams . get ( 'enddt' ) ) . toBe ( '2030-01-02T14:00:00.000Z' )
70+ expect ( outlookUrl . searchParams . get ( 'body' ) ) . toBe ( 'Weekly team sync' )
71+ expect ( outlookUrl . searchParams . get ( 'location' ) ) . toBe ( 'Remote' )
72+ }
73+
74+ await page . locator ( triggerSelector ) . click ( )
75+ await expect ( page . locator ( menuSelector ) ) . toHaveClass ( / h i d d e n / )
76+ await expect ( page . locator ( triggerSelector ) ) . toHaveAttribute ( 'aria-expanded' , 'false' )
77+ } )
78+
79+ test ( '@ready downloads an .ics file with the fixture event details' , async ( { page : playwrightPage } ) => {
80+ const page = await loadCalendarFixture ( playwrightPage )
81+
82+ await page . locator ( triggerSelector ) . click ( )
83+ const downloadPromise = playwrightPage . waitForEvent ( 'download' )
84+ await page . locator ( downloadButtonSelector ) . click ( )
85+ const download = await downloadPromise
86+
87+ expect ( download . suggestedFilename ( ) ) . toBe ( 'event.ics' )
88+ const downloadPath = await download . path ( )
89+ expect ( downloadPath ) . toBeTruthy ( )
90+
91+ if ( ! downloadPath ) {
92+ return
93+ }
94+
95+ const icsText = await readFile ( downloadPath , 'utf8' )
96+ expect ( icsText ) . toContain ( 'BEGIN:VCALENDAR' )
97+ expect ( icsText ) . toContain ( 'SUMMARY:Team sync' )
98+ expect ( icsText ) . toContain ( 'DESCRIPTION:Weekly team sync' )
99+ expect ( icsText ) . toContain ( 'LOCATION:Remote' )
100+ expect ( icsText ) . toContain ( 'DTSTART:20300102T130000Z' )
101+ expect ( icsText ) . toContain ( 'DTEND:20300102T140000Z' )
102+ expect ( icsText ) . toContain ( 'END:VCALENDAR' )
103+
104+ await expect ( page . locator ( menuSelector ) ) . toHaveClass ( / h i d d e n / )
105+ await expect ( page . locator ( triggerSelector ) ) . toHaveAttribute ( 'aria-expanded' , 'false' )
106+ } )
107+ } )
0 commit comments