test(range): migrate tests to playwright (#25299)
@@ -1,64 +0,0 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
import { dragElementBy } from '@utils/test';
|
||||
|
||||
test('range: basic', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/range/test/basic?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
|
||||
test('range:rtl: basic', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/range/test/basic?ionic:_testing=true&rtl=true',
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
|
||||
test('range: start/end events', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/range/test/basic?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const rangeStart = await page.spyOnEvent('ionKnobMoveStart');
|
||||
const rangeEnd = await page.spyOnEvent('ionKnobMoveEnd');
|
||||
const rangeEl = await page.$('#basic');
|
||||
|
||||
await dragElementBy(rangeEl, page, 300, 0);
|
||||
await page.waitForChanges();
|
||||
|
||||
/**
|
||||
* dragElementBy defaults to starting the drag from the middle of the el,
|
||||
* so the start value should jump to 50 despite the range defaulting to 20.
|
||||
*/
|
||||
expect(rangeStart).toHaveReceivedEventDetail({ value: 50 });
|
||||
expect(rangeEnd).toHaveReceivedEventDetail({ value: 91 });
|
||||
|
||||
/**
|
||||
* Verify both events fire if range is clicked without dragging.
|
||||
*/
|
||||
await dragElementBy(rangeEl, page, 0, 0);
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(rangeStart).toHaveReceivedEventDetail({ value: 50 });
|
||||
expect(rangeEnd).toHaveReceivedEventDetail({ value: 50 });
|
||||
});
|
||||
|
||||
test('range: start/end events, keyboard', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/range/test/basic?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const rangeStart = await page.spyOnEvent('ionKnobMoveStart');
|
||||
const rangeEnd = await page.spyOnEvent('ionKnobMoveEnd');
|
||||
|
||||
await page.keyboard.press('Tab'); // focus first range
|
||||
await page.keyboard.press('ArrowRight');
|
||||
|
||||
expect(rangeStart).toHaveReceivedEventDetail({ value: 20 });
|
||||
expect(rangeEnd).toHaveReceivedEventDetail({ value: 21 });
|
||||
});
|
||||
56
core/src/components/range/test/basic/range.e2e.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { dragElementBy, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('range: basic', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/range/test/basic`);
|
||||
|
||||
await page.setIonViewport();
|
||||
|
||||
expect(await page.screenshot()).toMatchSnapshot(`range-diff-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
|
||||
test('should emit start/end events', async ({ page }) => {
|
||||
await page.setContent(`<ion-range value="20"></ion-range>`);
|
||||
|
||||
const rangeStart = await page.spyOnEvent('ionKnobMoveStart');
|
||||
const rangeEnd = await page.spyOnEvent('ionKnobMoveEnd');
|
||||
|
||||
const rangeEl = page.locator('ion-range');
|
||||
|
||||
await dragElementBy(rangeEl, page, 300, 0);
|
||||
await page.waitForChanges();
|
||||
|
||||
/**
|
||||
* dragElementBy defaults to starting the drag from the middle of the el,
|
||||
* so the start value should jump to 50 despite the range defaulting to 20.
|
||||
*/
|
||||
expect(rangeStart).toHaveReceivedEventDetail({ value: 50 });
|
||||
expect(rangeEnd).toHaveReceivedEventDetail({ value: 100 });
|
||||
|
||||
/**
|
||||
* Verify both events fire if range is clicked without dragging.
|
||||
*/
|
||||
await dragElementBy(rangeEl, page, 0, 0);
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(rangeStart).toHaveReceivedEventDetail({ value: 50 });
|
||||
expect(rangeEnd).toHaveReceivedEventDetail({ value: 50 });
|
||||
});
|
||||
|
||||
test('should emit start/end events, keyboard', async ({ page }) => {
|
||||
await page.setContent(`<ion-range value="20"></ion-range>`);
|
||||
|
||||
const rangeStart = await page.spyOnEvent('ionKnobMoveStart');
|
||||
const rangeEnd = await page.spyOnEvent('ionKnobMoveEnd');
|
||||
|
||||
await page.keyboard.press('Tab'); // focus first range
|
||||
await page.keyboard.press('ArrowRight');
|
||||
|
||||
await rangeStart.next();
|
||||
await rangeEnd.next();
|
||||
|
||||
expect(rangeStart).toHaveReceivedEventDetail({ value: 20 });
|
||||
expect(rangeEnd).toHaveReceivedEventDetail({ value: 21 });
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 215 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 114 KiB |
@@ -1,10 +0,0 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('range: RTL', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/range/test/rtl?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
@@ -1,37 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Range - RTL</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Range - RTL</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content">
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<ion-label> Range </ion-label>
|
||||
</ion-list-header>
|
||||
<ion-item>
|
||||
<ion-range dir="rtl" value="20" aria-label="Default Range"></ion-range>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,10 +0,0 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('range: standalone', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/range/test/standalone?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
12
core/src/components/range/test/standalone/range.e2e.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('range: standalone', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/range/test/standalone`);
|
||||
|
||||
await page.setIonViewport();
|
||||
|
||||
expect(await page.screenshot()).toMatchSnapshot(`range-diff-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 23 KiB |
@@ -1,5 +1,7 @@
|
||||
import { toHaveReceivedEvent } from './toHaveReceivedEvent';
|
||||
import { toHaveReceivedEventDetail } from './toHaveReceivedEventDetail';
|
||||
|
||||
export const matchers = {
|
||||
toHaveReceivedEvent,
|
||||
toHaveReceivedEventDetail,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
|
||||
import type { EventSpy } from '../page/event-spy';
|
||||
|
||||
export function toHaveReceivedEventDetail(eventSpy: EventSpy, eventDetail: any) {
|
||||
if (!eventSpy) {
|
||||
return {
|
||||
message: () => `toHaveReceivedEventDetail event spy is null`,
|
||||
pass: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof (eventSpy as any).then === 'function') {
|
||||
return {
|
||||
message: () =>
|
||||
`expected spy to have received event, but it was not resolved (did you forget an await operator?).`,
|
||||
pass: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (!eventSpy.eventName) {
|
||||
return {
|
||||
message: () => `toHaveReceivedEventDetail did not receive an event spy`,
|
||||
pass: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (!eventSpy.lastEvent) {
|
||||
return {
|
||||
message: () => `event "${eventSpy.eventName}" was not received`,
|
||||
pass: false,
|
||||
};
|
||||
}
|
||||
|
||||
const pass = deepEqual(eventSpy.lastEvent.detail, eventDetail);
|
||||
|
||||
expect(eventSpy.lastEvent.detail).toEqual(eventDetail);
|
||||
|
||||
return {
|
||||
message: () => `expected event "${eventSpy.eventName}" detail to ${pass ? 'not ' : ''}equal`,
|
||||
pass: pass,
|
||||
};
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export class EventSpy {
|
||||
*/
|
||||
private cursor = 0;
|
||||
private queuedHandler: (() => void)[] = [];
|
||||
public events: Event[] = [];
|
||||
public events: CustomEvent[] = [];
|
||||
|
||||
constructor(public eventName: string) {}
|
||||
|
||||
@@ -24,6 +24,14 @@ export class EventSpy {
|
||||
return this.events.length;
|
||||
}
|
||||
|
||||
get firstEvent() {
|
||||
return this.events[0] || null;
|
||||
}
|
||||
|
||||
get lastEvent() {
|
||||
return this.events[this.events.length - 1] || null;
|
||||
}
|
||||
|
||||
public next() {
|
||||
const { cursor } = this;
|
||||
this.cursor++;
|
||||
@@ -48,7 +56,7 @@ export class EventSpy {
|
||||
}
|
||||
}
|
||||
|
||||
public push(ev: Event) {
|
||||
public push(ev: CustomEvent) {
|
||||
this.events.push(ev);
|
||||
const next = this.queuedHandler.shift();
|
||||
if (next) {
|
||||
@@ -91,8 +99,55 @@ export const addE2EListener = async (page: E2EPage, eventName: string, callback:
|
||||
|
||||
await page.evaluate(
|
||||
([eventName, id]) => {
|
||||
(window as any).stencilSerializeEventTarget = (target: any) => {
|
||||
// BROWSER CONTEXT
|
||||
if (!target) {
|
||||
return null;
|
||||
}
|
||||
if (target === window) {
|
||||
return { serializedWindow: true };
|
||||
}
|
||||
if (target === document) {
|
||||
return { serializedDocument: true };
|
||||
}
|
||||
if (target.nodeType != null) {
|
||||
const serializedElement: any = {
|
||||
serializedElement: true,
|
||||
nodeName: target.nodeName,
|
||||
nodeValue: target.nodeValue,
|
||||
nodeType: target.nodeType,
|
||||
tagName: target.tagName,
|
||||
className: target.className,
|
||||
id: target.id,
|
||||
};
|
||||
return serializedElement;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
(window as any).serializeStencilEvent = (orgEv: CustomEvent) => {
|
||||
const serializedEvent = {
|
||||
bubbles: orgEv.bubbles,
|
||||
cancelBubble: orgEv.cancelBubble,
|
||||
cancelable: orgEv.cancelable,
|
||||
composed: orgEv.composed,
|
||||
currentTarget: (window as any).stencilSerializeEventTarget(orgEv.currentTarget),
|
||||
defaultPrevented: orgEv.defaultPrevented,
|
||||
detail: orgEv.detail,
|
||||
eventPhase: orgEv.eventPhase,
|
||||
isTrusted: orgEv.isTrusted,
|
||||
returnValue: orgEv.returnValue,
|
||||
srcElement: (window as any).stencilSerializeEventTarget(orgEv.srcElement),
|
||||
target: (window as any).stencilSerializeEventTarget(orgEv.target),
|
||||
timeStamp: orgEv.timeStamp,
|
||||
type: orgEv.type,
|
||||
isSerializedEvent: true,
|
||||
};
|
||||
return serializedEvent;
|
||||
};
|
||||
|
||||
window.addEventListener(eventName as string, (ev: Event) => {
|
||||
(window as any).ionicOnEvent(id, ev);
|
||||
(window as any).ionicOnEvent(id, (window as any).serializeStencilEvent(ev));
|
||||
});
|
||||
},
|
||||
[eventName, id]
|
||||
|
||||
@@ -13,8 +13,13 @@ import type { Page } from '@playwright/test';
|
||||
*/
|
||||
export const setIonViewport = async (page: Page) => {
|
||||
const currentViewport = page.viewportSize();
|
||||
const ionContent = await page.$('ion-content');
|
||||
|
||||
const pixelAmountRenderedOffscreen = await page.evaluate(() => {
|
||||
if (ionContent) {
|
||||
await ionContent.waitForElementState('stable');
|
||||
}
|
||||
|
||||
const pixelAmountRenderedOffscreen = await page.evaluate(async () => {
|
||||
const content = document.querySelector('ion-content');
|
||||
if (content) {
|
||||
const innerScroll = content.shadowRoot!.querySelector('.inner-scroll')!;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { addE2EListener, EventSpy } from '../event-spy';
|
||||
export const spyOnEvent = async (page: E2EPage, eventName: string): Promise<EventSpy> => {
|
||||
const spy = new EventSpy(eventName);
|
||||
|
||||
await addE2EListener(page, eventName, (ev: Event) => spy.push(ev));
|
||||
await addE2EListener(page, eventName, (ev: CustomEvent) => spy.push(ev));
|
||||
|
||||
return spy;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,11 @@ interface CustomMatchers<R = unknown> {
|
||||
* Will check if the event spy received the expected event.
|
||||
*/
|
||||
toHaveReceivedEvent(): R;
|
||||
/**
|
||||
* Will check if the event spy received the expected event with the expected detail.
|
||||
* @param eventDetail The expected detail of the event.
|
||||
*/
|
||||
toHaveReceivedEventDetail(eventDetail: any): R;
|
||||
}
|
||||
|
||||
declare namespace PlaywrightTest {
|
||||
|
||||