diff --git a/website/src/scripts/analytics.ts b/website/src/scripts/analytics.ts index 63fe9946..2dbb60f6 100644 --- a/website/src/scripts/analytics.ts +++ b/website/src/scripts/analytics.ts @@ -141,6 +141,7 @@ async function initializeConsent(allowAnalytics: boolean): Promise { }); }; const theme = () => document.documentElement.dataset.theme === 'light' ? 'light' : 'dark'; + const initialTheme = theme(); wcp.init(document.documentElement.lang || 'en-US', banner, (error, consent) => { if (error || !consent) { console.error('Scope cookie consent could not initialize', error ?? new Error('Missing consent manager')); @@ -150,10 +151,19 @@ async function initializeConsent(allowAnalytics: boolean): Promise { manageCookies.hidden = false; manageCookies.addEventListener('click', () => consent.manageConsent()); onConsentChanged(); - new MutationObserver(() => { - consent.applyTheme(theme()); - }).observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); - }, onConsentChanged, theme()); + let appliedTheme = initialTheme; + const applyTheme = () => { + const nextTheme = theme(); + if (nextTheme === appliedTheme) return; + appliedTheme = nextTheme; + consent.applyTheme(nextTheme); + }; + applyTheme(); + new MutationObserver(applyTheme).observe( + document.documentElement, + { attributes: true, attributeFilter: ['data-theme'] }, + ); + }, onConsentChanged, initialTheme); } const base = import.meta.env.BASE_URL.replace(/\/?$/, '/'); diff --git a/website/tests/analytics.spec.ts b/website/tests/analytics.spec.ts index 24f17ef0..6df61ba7 100644 --- a/website/tests/analytics.spec.ts +++ b/website/tests/analytics.spec.ts @@ -161,10 +161,18 @@ test('stored consent initializes once and can be withdrawn and granted again', a test('consent UI follows Starlight theme changes', async ({ page }) => { await mockPublishedSite(page); await page.goto('https://microsoft.github.io/scope/'); - await page.evaluate(() => { document.documentElement.dataset.theme = 'light'; }); - await expect.poll(() => page.evaluate(() => window.scopeAnalyticsTest.appliedThemes)).toEqual(['light']); - await page.evaluate(() => { document.documentElement.dataset.theme = 'dark'; }); - await expect.poll(() => page.evaluate(() => window.scopeAnalyticsTest.appliedThemes)).toEqual(['light', 'dark']); + await expect.poll(() => page.evaluate(() => Boolean(window.scopeAnalyticsTest.changeConsent))).toBe(true); + const initialTheme = await page.evaluate( + () => document.documentElement.dataset.theme === 'light' ? 'light' : 'dark', + ); + const nextTheme = initialTheme === 'light' ? 'dark' : 'light'; + + await page.evaluate((theme) => { document.documentElement.dataset.theme = theme; }, initialTheme); + await expect.poll(() => page.evaluate(() => window.scopeAnalyticsTest.appliedThemes)).toEqual([]); + await page.evaluate((theme) => { document.documentElement.dataset.theme = theme; }, nextTheme); + await expect.poll(() => page.evaluate(() => window.scopeAnalyticsTest.appliedThemes)).toEqual([nextTheme]); + await page.evaluate((theme) => { document.documentElement.dataset.theme = theme; }, initialTheme); + await expect.poll(() => page.evaluate(() => window.scopeAnalyticsTest.appliedThemes)).toEqual([nextTheme, initialTheme]); }); for (const options of [