diff --git a/bundle/playwright/tests/mobile-sticky-slot.spec.ts b/bundle/playwright/tests/mobile-sticky-slot.spec.ts new file mode 100644 index 000000000..b67c41c8b --- /dev/null +++ b/bundle/playwright/tests/mobile-sticky-slot.spec.ts @@ -0,0 +1,63 @@ +import { expect, test } from '@playwright/test'; +import { testAtBreakpoints } from '../lib/breakpoints'; +import { loadPage } from '../lib/load-page'; +import { cmpAcceptAll } from '../lib/cmp'; +import { articles } from '../fixtures/pages'; +import { GuPage } from '../fixtures/pages/Page'; + +const { path } = articles[0] as unknown as GuPage; +testAtBreakpoints(['mobile']).forEach(({ width, height }) => { + test(`mobile sticky ad waits for StickyBottomBanner to be dismissed before loading`, async ({ + page, + }) => { + await page.setViewportSize({ width, height }); + + await loadPage({ page, path, region: 'US' }); + await cmpAcceptAll(page); + await loadPage({ + page, + path, + region: 'US', + queryParams: { 'force-banner': '', adtest: 'mobileStickyTest' }, + }); + + // Banner should be visible, ad slot should not exist + await expect( + page.locator('[name="StickyBottomBanner"] > *'), + ).toBeVisible(); + await expect(page.locator('#dfp-ad--mobile-sticky')).not.toBeAttached(); + + // Dismiss banner + await page.getByRole('button', { name: 'Collapse banner' }).click(); + await page.getByText('Maybe later').click(); + + // Banner hidden, ad slot should now appear + await expect( + page.locator('[name="StickyBottomBanner"]'), + ).not.toBeVisible(); + await expect(page.locator('#dfp-ad--mobile-sticky')).toBeAttached(); + }); +}); + +testAtBreakpoints(['mobile']).forEach(({ width, height }) => { + test(`mobile sticky responds to banner:none event at mobile breakpoint`, async ({ + page, + }) => { + await page.setViewportSize({ width, height }); + await loadPage({ + page, + path, + region: 'US', + queryParams: { + adtest: 'mobileStickyTest', + }, + }); + await cmpAcceptAll(page); + + await page.evaluate(() => { + document.dispatchEvent(new Event('banner:none')); + }); + + await expect(page.locator('#dfp-ad--mobile-sticky')).toBeAttached(); + }); +}); diff --git a/bundle/src/insert/mobile-sticky.ts b/bundle/src/insert/mobile-sticky.ts index b742a758d..d2846bb07 100644 --- a/bundle/src/insert/mobile-sticky.ts +++ b/bundle/src/insert/mobile-sticky.ts @@ -1,3 +1,4 @@ +import { log } from '@guardian/libs'; import { createAdSlot } from '../lib/create-ad-slot'; import fastdom from '../lib/fastdom-promise'; import { shouldIncludeMobileSticky } from '../lib/header-bidding/utils'; @@ -31,27 +32,36 @@ const createAdWrapper = () => { * Initialise mobile sticky ad slot * @returns Promise */ + +const renderMobileStickySlot = async () => { + log('commercial', '🪵 Rendering MobileSticky'); + const mobileStickyWrapper = createAdWrapper(); + await fastdom.mutate(() => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Is body really always defined? + if (document.body && mobileStickyWrapper) { + document.body.appendChild(mobileStickyWrapper); + } + }); + if (mobileStickyWrapper) { + const mobileStickyAdSlot = + mobileStickyWrapper.querySelector( + '#dfp-ad--mobile-sticky', + ); + if (mobileStickyAdSlot) { + void fillDynamicAdSlot(mobileStickyAdSlot, true); + } + } +}; + export const init = (): Promise => { + const handleBannerEvent = () => { + log('commercial', '🪵 Handle Banner Event'); + void renderMobileStickySlot(); + }; + if (shouldIncludeMobileSticky()) { - const mobileStickyWrapper = createAdWrapper(); - return fastdom - .mutate(() => { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Is body really always defined? - if (document.body && mobileStickyWrapper) { - document.body.appendChild(mobileStickyWrapper); - } - }) - .then(() => { - if (mobileStickyWrapper) { - const mobileStickyAdSlot = - mobileStickyWrapper.querySelector( - '#dfp-ad--mobile-sticky', - ); - if (mobileStickyAdSlot) { - void fillDynamicAdSlot(mobileStickyAdSlot, true); - } - } - }); + document.addEventListener('banner:close', handleBannerEvent); + document.addEventListener('banner:none', handleBannerEvent); } return Promise.resolve();