test(range): migrate tests to playwright (#25299)

This commit is contained in:
Sean Perkins
2022-05-18 12:24:27 -04:00
committed by GitHub
parent 05ae8e2072
commit 08587bcd9f
36 changed files with 184 additions and 126 deletions

View File

@@ -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 });
});

View 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 });
});
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

@@ -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();
});

View File

@@ -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>

View File

@@ -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();
});

View 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`);
});
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -1,5 +1,7 @@
import { toHaveReceivedEvent } from './toHaveReceivedEvent';
import { toHaveReceivedEventDetail } from './toHaveReceivedEventDetail';
export const matchers = {
toHaveReceivedEvent,
toHaveReceivedEventDetail,
};

View File

@@ -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,
};
}

View File

@@ -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]

View File

@@ -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')!;

View File

@@ -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;
};

View File

@@ -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 {