Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用 playwright 进行截图测试 #2161

Merged
merged 11 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions .github/workflows/build_test.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
name: lint
name: Build & Test

on: [pull_request]

jobs:
lint:
ci:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: yarn
- run: yarn build
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}

- name: Install Playwright browsers
run: npx playwright install --with-deps

- run: yarn
- run: yarn build
- run: yarn test:e2e

- name: Upload snapshots to GitHub Actions Artifacts
if: ${{ always() }}
uses: actions/upload-artifact@v3
with:
name: snapshots
path: |
__tests__/e2e/snapshots
retention-days: 1
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,6 @@ docs-dist
/packages/leaflet
site_temp
/packages/site/server

*-actual.png
*-diff.png
5 changes: 5 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@ali:registry=https://registry.npm.alibaba-inc.com
@alipay:registry=https://registry.npm.alibaba-inc.com
@alife:registry=https://registry.npm.alibaba-inc.com

registry=https://registry.npm.taobao.org/
5 changes: 3 additions & 2 deletions .umirc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ export default defineConfig({
logo: 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
outputPath: 'docs-dist',
base: '/',
define:{
define: {
'process.env.renderer': process.env.renderer?.replace(/\'/g, ''),
'process.env.CI': process.env.CI?.replace(/\'/g, ''),
},

devServer: {
port: '6006',
},
Expand Down
36 changes: 36 additions & 0 deletions __tests__/e2e/snapshot.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { chromium, devices } from 'playwright';
import { tests } from './tests';
import { sleep } from './utils/sleep';
import './utils/useSnapshotMatchers';

describe('Snapshots', () => {
Object.keys(tests).forEach((key) => {
it(key, async () => {
// Setup
const browser = await chromium.launch({
args: ['--headless', '--no-sandbox'],
});
const context = await browser.newContext(devices['Desktop Chrome']);
const page = await context.newPage();

// Go to test page served by vite devServer.
const url = `http://localhost:6006/${tests[key]}`;
await page.goto(url);

await sleep(1500);

// Chart already rendered, capture into buffer.
const buffer = await page.locator('canvas').screenshot();

const dir = `${__dirname}/snapshots`;

const maxError = 0;
try {
expect(buffer).toMatchCanvasSnapshot(dir, key, { maxError });
} finally {
await context.close();
await browser.close();
}
});
});
});
Binary file added __tests__/e2e/snapshots/citybuilding-amap1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __tests__/e2e/snapshots/point-billboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __tests__/e2e/snapshots/point-circle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __tests__/e2e/snapshots/point-column.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __tests__/e2e/snapshots/point-fill-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __tests__/e2e/snapshots/point-normal-device.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions __tests__/e2e/tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* TODO: extract url from dumi md & tsx
*/
export const tests = {
'point-circle': 'features/point/circle',
'point-billboard': 'features/point/point-billboard',
'point-column': 'features/point/point-column',
'point-fill-image': 'features/point/point-fill-image',
'point-normal-device': 'features/point/point-normal-device',
'citybuilding-amap1': 'features/citybuilding/amap1',
};
5 changes: 5 additions & 0 deletions __tests__/e2e/utils/sleep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function sleep(n: number) {
return new Promise((resolve) => {
setTimeout(resolve, n);
});
}
92 changes: 92 additions & 0 deletions __tests__/e2e/utils/toMatchCanvasSnapshot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import * as fs from 'fs';
import * as path from 'path';
import pixelmatch from 'pixelmatch';
import { PNG } from 'pngjs';

export type ToMatchCanvasSnapshotOptions = {
maxError?: number;
};

function writePNG(buffer: Buffer, path: string) {
const png = PNG.sync.read(buffer);
fs.writeFileSync(path, PNG.sync.write(png));
}

/**
* diff between PNGs
*/
function diff(
src: string,
target: string,
diff: string,
maxError = 0,
showMismatchedPixels = true,
) {
const img1 = PNG.sync.read(fs.readFileSync(src));
const img2 = PNG.sync.read(fs.readFileSync(target));
const { width, height } = img1;

let diffPNG: PNG | null = null;
let output: Buffer | null = null;
if (showMismatchedPixels) {
diffPNG = new PNG({ width, height });
output = diffPNG.data;
}

// @see https://github.com/mapbox/pixelmatch#pixelmatchimg1-img2-output-width-height-options
const mismatch = pixelmatch(img1.data, img2.data, output, width, height, {
threshold: 0.1,
});

if (showMismatchedPixels && mismatch > maxError && diffPNG) {
fs.writeFileSync(diff, PNG.sync.write(diffPNG));
}

return mismatch;
}

// @see https://jestjs.io/docs/26.x/expect#expectextendmatchers
export function toMatchCanvasSnapshot(
buffer: Buffer,
dir: string,
name: string,
options: ToMatchCanvasSnapshotOptions = {},
): { message: () => string; pass: boolean } {
const { maxError = 0 } = options;
const namePath = path.join(dir, name);
const actualPath = path.join(dir, `${name}-actual.png`);
const expectedPath = path.join(dir, `${name}.png`);
const diffPath = path.join(dir, `${name}-diff.png`);

try {
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
if (!fs.existsSync(expectedPath)) {
console.warn(`! generate ${namePath}`);
writePNG(buffer, expectedPath);
return {
message: () => `generate ${namePath}`,
pass: true,
};
} else {
writePNG(buffer, actualPath);
const error = diff(actualPath, expectedPath, diffPath, maxError);
if (error <= maxError) {
if (fs.existsSync(diffPath)) fs.unlinkSync(diffPath);
fs.unlinkSync(actualPath);
return {
message: () => `match ${namePath}`,
pass: true,
};
}
return {
message: () => `mismatch ${namePath} (error: ${error}) `,
pass: false,
};
}
} catch (e) {
return {
message: () => `${e}`,
pass: false,
};
}
}
14 changes: 14 additions & 0 deletions __tests__/e2e/utils/useSnapshotMatchers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { toMatchCanvasSnapshot, ToMatchCanvasSnapshotOptions } from "./toMatchCanvasSnapshot";

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace jest {
interface Matchers<R> {
toMatchCanvasSnapshot(dir: string, name: string, options?: ToMatchCanvasSnapshotOptions): R;
}
}
}

expect.extend({
toMatchCanvasSnapshot,
});
8 changes: 4 additions & 4 deletions dev-demos/features/citybuilding/amap1.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// @ts-ignore
import { Scene,CityBuildingLayer } from '@antv/l7';
import { CityBuildingLayer, Scene } from '@antv/l7';
// @ts-ignore
import { GaodeMap } from '@antv/l7-maps';
import { GaodeMap, Map } from '@antv/l7-maps';
import React, { useEffect } from 'react';

export default () => {
useEffect(() => {
const scene = new Scene({
id: 'map',
renderer: process.env.renderer,
map: new GaodeMap({
map: new (process.env.CI ? Map : GaodeMap)({
style: 'dark',
center: [120.145, 30.238915],
pitch: 60,
Expand All @@ -25,7 +25,7 @@ export default () => {
.size('floor', [0, 500])
.color('rgba(242,246,250,1.0)')
.animate({
enable: true,
enable: !process.env.CI,
})
.active({
color: '#0ff',
Expand Down
4 changes: 2 additions & 2 deletions dev-demos/features/citybuilding/amap2.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// @ts-ignore
import { CityBuildingLayer, Scene } from '@antv/l7';
// @ts-ignore
import { GaodeMap } from '@antv/l7-maps';
import { GaodeMap, Map } from '@antv/l7-maps';
import React, { useEffect } from 'react';

export default () => {
useEffect(() => {
const scene = new Scene({
id: 'map',
renderer: process.env.renderer,
map: new GaodeMap({
map: new (process.env.CI ? Map : GaodeMap)({
style: 'dark',
center: [120.145, 30.238915],
pitch: 60,
Expand Down
Loading
Loading