Repository: ionic-team/ionic-framework
File: core/src/components/modal/test/sheet/modal.e2e.ts (TypeScript)

Code before:
    await ionBreakpointDidChange.next();

    await dismissButton.click();
    await ionModalDidDismiss.next();

    await openButton.click();
    await ionModalDidPresent.next();

    const breakpoint = await modal.evaluate((el: HTMLIonModalElement) => el.getCurrentBreakpoint());

    expect(breakpoint).toBe(0.25);

    await setBreakpointButton.click();
    await ionBreakpointDidChange.next();

    const updatedBreakpoint = await modal.evaluate((el: HTMLIonModalElement) => el.getCurrentBreakpoint());

    expect(updatedBreakpoint).toBe(0.5);
  });
});

test.describe('sheet modal: handleBehavior cycle: focus trap', () => {
  test.beforeEach(async ({ page, browserName }, testInfo) => {
    test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.');
    test.skip(browserName === 'firefox', 'Firefox incorrectly allows keyboard focus to move to ion-content');
    test.skip(browserName === 'webkit', 'Safari incorrectly allows keyboard focus to move to the slotted content');

    await page.goto('/src/components/modal/test/sheet');
  });

  test('should trap focus on the modal handle', async ({ page, browserName }) => {
    const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
    const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
    const handle = page.locator('ion-modal .modal-handle');
    const modal = page.locator('ion-modal');

    await page.click('#handle-behavior-cycle-modal');
    await ionModalDidPresent.next();

    await page.waitForChanges();

    await page.keyboard.press(tabKey);

    /**
     * toBeFocused looks at document.activeElement, which does not include elements
     * that are focused within the shadow DOM.
     */
    const activeShadowEl = await modal.evaluate((el) => el.shadowRoot?.activeElement);
    const handleEl = await handle.evaluate((el) => el);

    await expect(activeShadowEl).toBe(handleEl);

Review comment (bug):
Do we need tech debt tickets for either of these? The `webkit` one almost just sounds like a bug, but there may be context I'm missing.

Code after:
    await ionBreakpointDidChange.next();

    await dismissButton.click();
    await ionModalDidDismiss.next();

    await openButton.click();
    await ionModalDidPresent.next();

    const breakpoint = await modal.evaluate((el: HTMLIonModalElement) => el.getCurrentBreakpoint());

    expect(breakpoint).toBe(0.25);

    await setBreakpointButton.click();
    await ionBreakpointDidChange.next();

    const updatedBreakpoint = await modal.evaluate((el: HTMLIonModalElement) => el.getCurrentBreakpoint());

    expect(updatedBreakpoint).toBe(0.5);
  });
});

test.describe('sheet modal: clicking the handle', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/src/components/modal/test/sheet');
  });

  test('should advance to the next breakpoint when handleBehavior is cycle', async ({ page }) => {
    const ionBreakpointDidChange = await page.spyOnEvent('ionBreakpointDidChange');
    const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
    const modal = page.locator('ion-modal');

    await page.click('#handle-behavior-cycle-modal');
    await ionModalDidPresent.next();

    const handle = page.locator('ion-modal .modal-handle');

    await handle.click();
    await ionBreakpointDidChange.next();

    await expect(await modal.evaluate((el: HTMLIonModalElement) => el.getCurrentBreakpoint())).toBe(0.5);

    await handle.click();
    await ionBreakpointDidChange.next();

    await expect(await modal.evaluate((el: HTMLIonModalElement) => el.getCurrentBreakpoint())).toBe(0.75);

    await handle.click();
    await ionBreakpointDidChange.next();

    await expect(await modal.evaluate((el: HTMLIonModalElement) => el.getCurrentBreakpoint())).toBe(1);