Skip to content

Commit

Permalink
[TEST] Fix panning tests on webkit (#1237)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbouffard authored May 4, 2021
1 parent ac539c5 commit 23264bd
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 46 deletions.
76 changes: 53 additions & 23 deletions test/e2e/bpmn.navigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,20 @@
*/
import 'jest-playwright-preset';
import { join } from 'path';
import { defaultChromiumFailureThreshold, ImageSnapshotConfigurator, ImageSnapshotThresholdConfig } from './helpers/visu/image-snapshot-config';
import { chromiumZoom, itMouseWheel, itPanning, mousePanning } from './helpers/test-utils';
import { ImageSnapshotConfigurator, ImageSnapshotThresholdConfig, MultiBrowserImageSnapshotThresholds } from './helpers/visu/image-snapshot-config';
import { chromiumZoom, getContainerCenter, itMouseWheel, mousePanning, Point } from './helpers/test-utils';
import { PageTester } from './helpers/visu/PageTester';
import { ElementHandle } from 'playwright';

describe('diagram navigation', () => {
const imageSnapshotConfigurator = new ImageSnapshotConfigurator(
// if no dedicated information, set minimal threshold to make test pass on Github Workflow on Chromium
// linux threshold are set for Ubuntu
new Map<string, ImageSnapshotThresholdConfig>([
class ImageSnapshotThresholds extends MultiBrowserImageSnapshotThresholds {
constructor() {
super({ chromium: 0.000005, firefox: 0.0004, webkit: 0 });
}

// if no dedicated information, set minimal threshold to make test pass on Github Workflow
// linux threshold are set for Ubuntu
getChromiumThresholds(): Map<string, ImageSnapshotThresholdConfig> {
return new Map<string, ImageSnapshotThresholdConfig>([
[
'simple.2.start.events.1.task',
{
Expand All @@ -32,26 +37,51 @@ describe('diagram navigation', () => {
windows: 0.0000095, // 0.0009247488045871499%
},
],
]),
'navigation',
defaultChromiumFailureThreshold,
);
]);
}

getFirefoxThresholds(): Map<string, ImageSnapshotThresholdConfig> {
return new Map<string, ImageSnapshotThresholdConfig>([
[
'simple.2.start.events.1.task',
{
linux: 0.0000095, // 0.0009247488045871499%
macos: 0.0000095, // 0.0009247488045871499%
windows: 0.0000095, // 0.0009247488045871499%
},
],
]);
}

protected getWebkitThresholds(): Map<string, ImageSnapshotThresholdConfig> {
return new Map<string, ImageSnapshotThresholdConfig>([
[
'simple.2.start.events.1.task',
{
macos: 0.00007, // 0.006752338394599988%
},
],
]);
}
}

describe('diagram navigation', () => {
const imageSnapshotThresholds = new ImageSnapshotThresholds();
const imageSnapshotConfigurator = new ImageSnapshotConfigurator(imageSnapshotThresholds.getThresholds(), 'navigation', imageSnapshotThresholds.getDefault());
// to have mouse pointer visible during headless test - add 'showMousePointer: true' as parameter
const pageTester = new PageTester({ pageFileName: 'rendering-diagram', expectedPageTitle: 'BPMN Visualization - Diagram Rendering' });

const bpmnDiagramName = 'simple.2.start.events.1.task';
let containerCenterX: number;
let containerCenterY: number;
let bpmnContainerElementHandle: ElementHandle<SVGElement | HTMLElement>;
let containerCenter: Point;

beforeEach(async () => {
const bpmnContainerElementHandle = await pageTester.loadBPMNDiagramInRefreshedPage(bpmnDiagramName);
const bounding_box = await bpmnContainerElementHandle.boundingBox();
containerCenterX = bounding_box.x + bounding_box.width / 2;
containerCenterY = bounding_box.y + bounding_box.height / 2;
bpmnContainerElementHandle = await pageTester.loadBPMNDiagramInRefreshedPage(bpmnDiagramName);
containerCenter = await getContainerCenter(bpmnContainerElementHandle);
});

itPanning('mouse panning', async () => {
await mousePanning(containerCenterX, containerCenterY);
it('mouse panning', async () => {
await mousePanning({ containerElement: bpmnContainerElementHandle, originPoint: containerCenter, destinationPoint: { x: containerCenter.x + 150, y: containerCenter.y + 40 } });

const image = await page.screenshot({ fullPage: true });
const config = imageSnapshotConfigurator.getConfig(bpmnDiagramName);
Expand All @@ -63,7 +93,7 @@ describe('diagram navigation', () => {

itMouseWheel.each(['zoom in', 'zoom out'])(`ctrl + mouse: %s`, async (zoomMode: string) => {
const deltaX = zoomMode === 'zoom in' ? -100 : 100;
await chromiumZoom(1, containerCenterX + 200, containerCenterY, deltaX);
await chromiumZoom(1, { x: containerCenter.x + 200, y: containerCenter.y }, deltaX);

const image = await page.screenshot({ fullPage: true });
const config = imageSnapshotConfigurator.getConfig(bpmnDiagramName);
Expand All @@ -76,9 +106,9 @@ describe('diagram navigation', () => {
itMouseWheel.each([3, 5])(`ctrl + mouse: initial scale after zoom in and zoom out [%s times]`, async (xTimes: number) => {
const deltaX = -100;
// simulate mouse+ctrl zoom
await page.mouse.move(containerCenterX + 200, containerCenterY);
await chromiumZoom(xTimes, containerCenterX + 200, containerCenterY, deltaX);
await chromiumZoom(xTimes, containerCenterX + 200, containerCenterY, -deltaX);
await page.mouse.move(containerCenter.x + 200, containerCenter.y);
await chromiumZoom(xTimes, { x: containerCenter.x + 200, y: containerCenter.y }, deltaX);
await chromiumZoom(xTimes, { x: containerCenter.x + 200, y: containerCenter.y }, -deltaX);

const image = await page.screenshot({ fullPage: true });
const config = imageSnapshotConfigurator.getConfig(bpmnDiagramName);
Expand Down
31 changes: 22 additions & 9 deletions test/e2e/helpers/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import debugLogger from 'debug';
import { findFiles } from '../../helpers/file-helper';
import { join } from 'path';
import 'jest-playwright-preset';
import { chromiumMouseWheel } from './visu/playwright-utils';
import { chromiumMouseWheel, PanningOptions, webkitMousePanning } from './visu/playwright-utils';
import { ElementHandle } from 'playwright';

export interface Point {
x: number;
y: number;
}

export const configLog = debugLogger('bv:test:config');

Expand Down Expand Up @@ -56,23 +62,33 @@ export function getBpmnDiagramNames(directoryName: string): string[] {
});
}

export async function getContainerCenter(containerElement: ElementHandle<SVGElement | HTMLElement>): Promise<Point> {
const rect = await containerElement.boundingBox();
return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
}

export async function clickOnButton(buttonId: string): Promise<void> {
await page.click(`#${buttonId}`);
// To unselect the button
await page.mouse.click(0, 0);
}

export async function mousePanning(containerCenterX: number, containerCenterY: number): Promise<void> {
export async function mousePanning(panningOptions: PanningOptions): Promise<void> {
const testedBrowserFamily = getTestedBrowserFamily();
testedBrowserFamily === 'webkit' ? await webkitMousePanning(panningOptions) : await chromiumAndFirefoxMousePanning(panningOptions);
}

async function chromiumAndFirefoxMousePanning({ originPoint, destinationPoint }: PanningOptions): Promise<void> {
// simulate mouse panning
await page.mouse.move(containerCenterX, containerCenterY);
await page.mouse.move(originPoint.x, originPoint.y);
await page.mouse.down();
await page.mouse.move(containerCenterX + 150, containerCenterY + 40);
await page.mouse.move(destinationPoint.x, destinationPoint.y);
await page.mouse.up();
}

export async function chromiumZoom(xTimes: number, x: number, y: number, deltaX: number): Promise<void> {
export async function chromiumZoom(xTimes: number, point: Point, deltaX: number): Promise<void> {
for (let i = 0; i < xTimes; i++) {
await chromiumMouseWheel(x, y, deltaX);
await chromiumMouseWheel(point.x, point.y, deltaX);
// delay here is needed to make the tests pass on MacOS, delay must be greater than debounce timing so it surely gets triggered
await delay(100);
}
Expand All @@ -82,6 +98,3 @@ export async function chromiumZoom(xTimes: number, x: number, y: number, deltaX:
// inspired from https://github.com/xtermjs/xterm.js/commit/7400b888df698d15864ab2c41ad0ed0262f812fb#diff-23460af115aa97331c36c0ce462cbc4dd8067c0ddbca1e9d3de560ebf44024ee
// Wheel events are hacked using private API that is only available in Chromium
export const itMouseWheel = getTestedBrowserFamily() === 'chromium' ? it : it.skip;

// TODO enable panning tests on webkit (see https://github.com/process-analytics/bpmn-visualization-js/pull/1197)
export const itPanning = getTestedBrowserFamily() === 'webkit' ? it.skip : it;
2 changes: 1 addition & 1 deletion test/e2e/helpers/visu/PageTester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class PageTester {
this.bpmnPage = new BpmnPage('bpmn-container', page);
}

async loadBPMNDiagramInRefreshedPage(bpmnDiagramName: string, loadOptions?: LoadOptions): Promise<ElementHandle<Element>> {
async loadBPMNDiagramInRefreshedPage(bpmnDiagramName: string, loadOptions?: LoadOptions): Promise<ElementHandle<SVGElement | HTMLElement>> {
const url = this.getPageUrl(bpmnDiagramName, loadOptions);
const response = await page.goto(url);
// Uncomment the following in case of http error 400 (probably because of a too large bpmn file)
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/helpers/visu/bpmn-page-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class BpmnPage {
this.bpmnQuerySelectors = new BpmnQuerySelectorsForTests(this.bpmnContainerId);
}

async expectAvailableBpmnContainer(options?: PageWaitForSelectorOptions): Promise<ElementHandle<Element>> {
async expectAvailableBpmnContainer(options?: PageWaitForSelectorOptions): Promise<ElementHandle<SVGElement | HTMLElement>> {
return await this.currentPage.waitForSelector(`#${this.bpmnContainerId}`, options);
}

Expand Down
42 changes: 41 additions & 1 deletion test/e2e/helpers/visu/playwright-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@
* limitations under the License.
*/
import 'jest-playwright-preset';
import { ChromiumBrowserContext } from 'playwright';
import { ChromiumBrowserContext, ElementHandle } from 'playwright';
import { Point } from '../test-utils';

export interface PanningOptions {
containerElement: ElementHandle<SVGElement | HTMLElement>;
originPoint: Point;
destinationPoint: Point;
}

// workaround for https://github.com/microsoft/playwright/issues/1115 that only works with chromium
// inspired from https://github.com/microsoft/playwright/issues/2642#issuecomment-647846972
Expand All @@ -41,3 +48,36 @@ export async function chromiumMouseWheel(x: number, y: number, deltaX: number):
// detach is it not reused outside this function
await client.detach();
}

// TODO To remove when https://github.com/microsoft/playwright/issues/1094 is fixed
export async function webkitMousePanning(panningOptions: PanningOptions): Promise<void> {
await page.evaluate(panningOptions => {
function getMouseEventInit(point: Point): MouseEventInit {
return {
bubbles: true,
cancelable: true,
clientX: point.x,
clientY: point.y,
screenX: point.x,
screenY: point.y,
button: 0,
buttons: 1,
};
}

const originMouseEventInit = getMouseEventInit(panningOptions.originPoint);
const destinationMouseEventInit = getMouseEventInit(panningOptions.destinationPoint);

// simulate mouse panning
const containerElement: SVGElement | HTMLElement = panningOptions.containerElement;
containerElement.dispatchEvent(new MouseEvent('mousemove', originMouseEventInit));
containerElement.dispatchEvent(new MouseEvent('mousedown', originMouseEventInit));
setTimeout(() => {
// Nothing to do
}, 2000);
containerElement.dispatchEvent(new MouseEvent('mousemove', destinationMouseEventInit));
containerElement.dispatchEvent(new MouseEvent('mouseup', destinationMouseEventInit));

return Promise.resolve({ x: destinationMouseEventInit.clientX, y: destinationMouseEventInit.clientY });
}, panningOptions);
}
20 changes: 9 additions & 11 deletions test/e2e/overlays.rendering.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import { ImageSnapshotConfigurator, ImageSnapshotThresholdConfig, MultiBrowserIm
import { PageTester } from './helpers/visu/PageTester';
import { join } from 'path';
import { OverlayEdgePosition, OverlayPosition, OverlayShapePosition } from '../../src/component/registry';
import { chromiumZoom, clickOnButton, itMouseWheel, itPanning, mousePanning } from './helpers/test-utils';
import { chromiumZoom, clickOnButton, getContainerCenter, itMouseWheel, mousePanning, Point } from './helpers/test-utils';
import { overlayEdgePositionValues, overlayShapePositionValues } from '../helpers/overlays';
import { ensureIsArray } from '../../src/component/helpers/array-utils';
import { ElementHandle } from 'playwright';

class ImageSnapshotThresholds extends MultiBrowserImageSnapshotThresholds {
constructor() {
Expand Down Expand Up @@ -263,24 +264,21 @@ describe('BPMN Edges with overlays', () => {

describe('Overlay navigation', () => {
const bpmnDiagramName = 'overlays.start.flow.task.gateway';

let containerCenterX: number;
let containerCenterY: number;
let bpmnContainerElementHandle: ElementHandle<SVGElement | HTMLElement>;
let containerCenter: Point;

beforeEach(async () => {
const bpmnContainerElementHandle = await pageTester.loadBPMNDiagramInRefreshedPage(bpmnDiagramName);
const bounding_box = await bpmnContainerElementHandle.boundingBox();
containerCenterX = bounding_box.x + bounding_box.width / 2;
containerCenterY = bounding_box.y + bounding_box.height / 2;
bpmnContainerElementHandle = await pageTester.loadBPMNDiagramInRefreshedPage(bpmnDiagramName);
containerCenter = await getContainerCenter(bpmnContainerElementHandle);

await addOverlays('StartEvent_1', 'bottom-center');
await addOverlays('Activity_1', 'middle-right');
await addOverlays('Gateway_1', 'top-right');
await addOverlays('Flow_1', 'start');
});

itPanning('panning', async () => {
await mousePanning(containerCenterX, containerCenterY);
it('panning', async () => {
await mousePanning({ containerElement: bpmnContainerElementHandle, originPoint: containerCenter, destinationPoint: { x: containerCenter.x + 150, y: containerCenter.y + 40 } });

const image = await page.screenshot({ fullPage: true });
const config = imageSnapshotConfigurator.getConfig(bpmnDiagramName);
Expand All @@ -291,7 +289,7 @@ describe('Overlay navigation', () => {
});

itMouseWheel(`zoom out`, async () => {
await chromiumZoom(1, containerCenterX + 200, containerCenterY, 100);
await chromiumZoom(1, { x: containerCenter.x + 200, y: containerCenter.y }, 100);

const image = await page.screenshot({ fullPage: true });
const config = imageSnapshotConfigurator.getConfig(bpmnDiagramName);
Expand Down

0 comments on commit 23264bd

Please sign in to comment.