Skip to content

Commit 6e09cdc

Browse files
committed
Add ui tests
1 parent fa8afcf commit 6e09cdc

File tree

7 files changed

+4857
-0
lines changed

7 files changed

+4857
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,7 @@ package-lock.json
161161

162162
# Untitled notebooks
163163
*Untitled*.ipynb
164+
165+
# UI tests
166+
ui-tests/tests/notebooks/smoke_texture0.png
167+
ui-tests/tests/notebooks/smoke_texture1.png

ui-tests/jupyter_server_config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from tempfile import mkdtemp
2+
3+
c.ServerApp.port = 8888 # noqa: F821
4+
c.ServerApp.token = "" # noqa: F821
5+
c.ServerApp.password = "" # noqa: F821
6+
c.ServerApp.disable_check_xsrf = True # noqa: F821
7+
c.ServerApp.open_browser = False # noqa: F821
8+
c.ServerApp.root_dir = mkdtemp(prefix="galata-test-") # noqa: F821
9+
10+
c.LabApp.expose_app_in_browser = True # noqa: F821

ui-tests/package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "ipycanvas-ui-tests",
3+
"version": "1.0.0",
4+
"description": "ipycanvas UI Tests",
5+
"private": true,
6+
"scripts": {
7+
"start": "jupyter lab --config ./jupyter_server_config.py",
8+
"start:detached": "yarn run start&",
9+
"test": "playwright test",
10+
"test:debug": "PWDEBUG=1 playwright test",
11+
"test:report": "http-server ./playwright-report -a localhost -o",
12+
"test:update": "playwright test --update-snapshots"
13+
},
14+
"author": "ipycanvas",
15+
"license": "Apache-2.0",
16+
"dependencies": {
17+
"@jupyterlab/galata": "~4.0.2",
18+
"klaw-sync": "^6.0.0",
19+
"rimraf": "^3.0.2"
20+
}
21+
}

ui-tests/playwright.config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const baseConfig = require('@jupyterlab/galata/lib/playwright-config');
2+
3+
module.exports = {
4+
...baseConfig,
5+
timeout: 600000,
6+
retries: 1,
7+
};

ui-tests/tests/ipycanvas.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright (c) Jupyter Development Team.
2+
// Distributed under the terms of the Modified BSD License.
3+
4+
import { IJupyterLabPageFixture, test } from '@jupyterlab/galata';
5+
import { expect } from '@playwright/test';
6+
import * as path from 'path';
7+
const klaw = require('klaw-sync');
8+
9+
10+
const filterUpdateNotebooks = item => {
11+
const basename = path.basename(item.path);
12+
return basename.includes('_update');
13+
}
14+
15+
const testCellOutputs = async (page: IJupyterLabPageFixture, tmpPath: string) => {
16+
const paths = klaw(path.resolve(__dirname, './notebooks'), { filter: item => !filterUpdateNotebooks(item), nodir: true });
17+
const notebooks = paths.map(item => path.basename(item.path));
18+
19+
for (const notebook of notebooks) {
20+
let results = [];
21+
22+
await page.notebook.openByPath(`${tmpPath}/${notebook}`);
23+
await page.notebook.activate(notebook);
24+
25+
let numCellImages = 0;
26+
27+
const getCaptureImageName = (notebook: string, id: number): string => {
28+
return `${notebook}-cell-${id}.png`;
29+
};
30+
31+
await page.notebook.runCellByCell({
32+
onAfterCellRun: async (cellIndex: number) => {
33+
const cell = await page.notebook.getCellOutput(cellIndex);
34+
if (cell) {
35+
results.push(await cell.screenshot());
36+
numCellImages++;
37+
}
38+
}
39+
});
40+
41+
await page.notebook.save();
42+
43+
for (let c = 0; c < numCellImages; ++c) {
44+
expect(results[c]).toMatchSnapshot(getCaptureImageName(notebook, c));
45+
}
46+
47+
await page.notebook.close(true);
48+
}
49+
}
50+
51+
const testUpdates = async (page: IJupyterLabPageFixture, tmpPath: string) => {
52+
const paths = klaw(path.resolve(__dirname, './notebooks'), { filter: item => filterUpdateNotebooks(item), nodir: true });
53+
const notebooks = paths.map(item => path.basename(item.path));
54+
55+
for (const notebook of notebooks) {
56+
let results = [];
57+
58+
await page.notebook.openByPath(`${tmpPath}/${notebook}`);
59+
await page.notebook.activate(notebook);
60+
61+
const getCaptureImageName = (notebook: string, id: number): string => {
62+
return `${notebook}-cell-${id}.png`;
63+
};
64+
65+
let cellCount = 0;
66+
await page.notebook.runCellByCell({
67+
onAfterCellRun: async (cellIndex: number) => {
68+
// Always get first cell output which must contain the plot
69+
const cell = await page.notebook.getCellOutput(0);
70+
if (cell) {
71+
results.push(await cell.screenshot());
72+
cellCount++;
73+
}
74+
}
75+
});
76+
77+
await page.notebook.save();
78+
79+
for (let i = 0; i < cellCount; i++) {
80+
expect(results[i]).toMatchSnapshot(getCaptureImageName(notebook, i));
81+
}
82+
83+
await page.notebook.close(true);
84+
}
85+
};
86+
87+
test.describe('ipycanvas Visual Regression', () => {
88+
test.beforeEach(async ({ page, tmpPath }) => {
89+
await page.contents.uploadDirectory(
90+
path.resolve(__dirname, './notebooks'),
91+
tmpPath
92+
);
93+
await page.filebrowser.openDirectory(tmpPath);
94+
});
95+
96+
test('Check ipycanvas first renders', async ({
97+
page,
98+
tmpPath,
99+
}) => {
100+
await testCellOutputs(page, tmpPath);
101+
});
102+
103+
test('Check ipycanvas update', async ({
104+
page,
105+
tmpPath,
106+
}) => {
107+
await testUpdates(page, tmpPath);
108+
});
109+
});

0 commit comments

Comments
 (0)