Skip to content

Commit 7237fdc

Browse files
mbektasSteven Silvesterjtpio
authored
Automated UI testing using Galata (jupyterlab#10331)
* UI testing using Galata * add toolbar capture * notebook edit tests * use galata 3.0.7-4 * add TOC test * use galata 3.0.7-5, add helper scripts * add notebook toolbar test * run markdown cells, remove unnecessary steps * fixes for CI failures * fix typedoc failure * use jlpm * add ui-tests workflow * full build * launch jlab in empty directory * delete jlab_root after tests finish * compare menu and sidebar captures * install ipywidgets * refactor * documentation and repeated run script * fix lint error * use galata 3.0.7-6, simplify jlab build step * wait for export menu * fix lint errors * use --dev-mode, remove ipywidgets dependency * capture only tags panel in property inspector * remove wait for export menu and file menu capture * enable visual regression, add reference images * Update ui-tests/package.json Co-authored-by: Jeremy Tuloup <[email protected]> Co-authored-by: Steven Silvester <[email protected]> Co-authored-by: Jeremy Tuloup <[email protected]>
1 parent 9b5a6fd commit 7237fdc

File tree

88 files changed

+5660
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+5660
-3
lines changed

.github/workflows/ui-tests.yml

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: UI Tests
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
ui-test:
7+
name: Visual Regression
8+
runs-on: ubuntu-latest
9+
strategy:
10+
matrix:
11+
group: [ui_test]
12+
python: [3.8]
13+
fail-fast: false
14+
15+
steps:
16+
- uses: actions/checkout@v2
17+
- name: Set up Python
18+
uses: actions/setup-python@v1
19+
with:
20+
python-version: ${{ matrix.python }}
21+
22+
- name: Set up Node
23+
uses: actions/setup-node@v1
24+
with:
25+
node-version: '12.x'
26+
27+
- name: Cache pip on Linux
28+
uses: actions/cache@v2
29+
if: startsWith(runner.os, 'Linux')
30+
with:
31+
path: ~/.cache/pip
32+
key: ${{ runner.os }}-pip-${{ matrix.python }}-${{ hashFiles('**/requirements.txt', 'setup.py') }}
33+
restore-keys: |
34+
${{ runner.os }}-pip-${{ matrix.python }}
35+
36+
- name: Get yarn cache directory path
37+
id: yarn-cache-dir-path
38+
run: echo "::set-output name=dir::$(yarn cache dir)"
39+
- name: Cache yarn
40+
uses: actions/cache@v2
41+
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
42+
with:
43+
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
44+
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
45+
restore-keys: |
46+
${{ runner.os }}-yarn-
47+
48+
- name: Install dependencies
49+
env:
50+
GROUP: ${{ matrix.group }}
51+
run: |
52+
bash ./scripts/ci_install.sh
53+
jlpm
54+
jlpm build
55+
jupyter lab build
56+
57+
- name: Install Galata
58+
run: |
59+
cd ui-tests
60+
jlpm install --frozen-lockfile
61+
62+
- name: Launch JupyterLab
63+
run: |
64+
cd ui-tests
65+
mkdir jlab_root
66+
jlpm run start-jlab:detached
67+
68+
- name: Wait for JupyterLab
69+
uses: ifaxity/wait-on-action@v1
70+
with:
71+
resource: http-get://localhost:8888/api
72+
timeout: 20000
73+
74+
- name: Run UI Tests
75+
run: |
76+
cd ui-tests
77+
jlpm run test
78+
rm -rf jlab_root
79+
80+
- name: Upload UI Test artifacts
81+
if: always()
82+
uses: actions/upload-artifact@v2
83+
with:
84+
name: ui-test-output
85+
path: |
86+
ui-tests/test-output

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,5 @@ junit.xml
111111
*.code-workspace
112112
.history
113113
.vscode
114+
115+
ui-tests/test-output

MANIFEST.in

+2
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@ prune jupyterlab/staging/build
3131

3232
recursive-exclude jupyterlab *.pyc
3333
recursive-exclude jupyterlab *.js.map
34+
35+
prune ui-tests

tsconfig.eslint.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"scripts/*",
2121
"jupyterlab/*",
2222
"jupyterlab/staging/*",
23-
"testutils/**/*"
23+
"testutils/**/*",
24+
"ui-tests/**/*"
2425
]
2526
}

tsconfigdoc.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"extends": "./tsconfigbase",
44
"exclude": [
55
"**/test/**",
6-
"**/testutils/**"
6+
"**/testutils/**",
7+
"**/ui-tests/**"
78
],
89
"compilerOptions": {
910
"paths": {

typedoc.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ const entryPoints = packages
103103
const exclude =
104104
packages.flatMap(p => [`packages/${p}/test`]) +
105105
[
106-
'packages/application-extension/src/index.tsx'
106+
'packages/application-extension/src/index.tsx',
107+
'ui-tests/**/*'
107108
//'packages/*/test/*.spec.ts',
108109
];
109110

ui-tests/README.md

+47

ui-tests/galata-config.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"testId": "test"
3+
}

ui-tests/jupyter_server_config.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
c.ServerApp.port = 8888
2+
c.ServerApp.token = ""
3+
c.ServerApp.password = ""
4+
c.ServerApp.disable_check_xsrf = True
5+
c.ServerApp.open_browser = False
6+
c.ServerApp.root_dir = 'jlab_root'
7+
c.LabApp.dev_mode = True
8+
c.LabApp.open_browser = False
9+
c.LabApp.expose_app_in_browser = True

ui-tests/package.json

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "@jupyterlab/ui-tests",
3+
"version": "1.0.0",
4+
"description": "JupyterLab UI Tests",
5+
"private": true,
6+
"scripts": {
7+
"start-jlab": "jupyter lab --config ./jupyter_server_config.py",
8+
"start-jlab:detached": "jlpm run start-jlab&",
9+
"test:create-references": "galata --skip-visual-regression --skip-html-regression",
10+
"test:debug": "galata --no-headless --no-discard-matched-captures --slow-mo 200 --result-server --include notebook-edit",
11+
"test:launch-last-report": "galata --launch-result-server",
12+
"test": "galata"
13+
},
14+
"author": "Project Jupyter",
15+
"license": "BSD-3-Clause",
16+
"dependencies": {
17+
"@jupyterlab/galata": "3.0.7-6"
18+
}
19+
}

ui-tests/repeated_test_run.sh

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/bash
2+
3+
i=0
4+
5+
while true; do
6+
i=$((i+1))
7+
jlpm run test
8+
if [ $? -eq 0 ]
9+
then
10+
echo "Test run succeeded $i times..."
11+
else
12+
echo "Test run failed! Run 'jlpm run test:launch-last-report' to inspect test results."
13+
exit 0
14+
fi
15+
done

ui-tests/tests/general.test.ts

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (c) Jupyter Development Team.
2+
// Distributed under the terms of the Modified BSD License.
3+
4+
import { galata, describe, test } from '@jupyterlab/galata';
5+
import { runMenuOpenTest, runSidebarOpenTest } from './util';
6+
7+
jest.setTimeout(60000);
8+
9+
describe('General Tests', () => {
10+
beforeAll(async () => {
11+
await galata.resetUI();
12+
galata.context.capturePrefix = 'general';
13+
});
14+
15+
afterAll(async () => {
16+
galata.context.capturePrefix = '';
17+
});
18+
19+
test('Launch Screen', async () => {
20+
const imageName = 'launch';
21+
await galata.capture.screenshot(imageName);
22+
expect(await galata.capture.compareScreenshot(imageName)).toBe('same');
23+
});
24+
25+
runSidebarOpenTest();
26+
27+
test('Enter Simple Mode', async () => {
28+
await galata.toggleSimpleMode(true);
29+
expect(await galata.isInSimpleMode()).toBeTruthy();
30+
31+
const imageName = 'simple-mode';
32+
await galata.capture.screenshot(imageName);
33+
expect(await galata.capture.compareScreenshot(imageName)).toBe('same');
34+
});
35+
36+
test('Leave Simple Mode', async () => {
37+
await galata.toggleSimpleMode(false);
38+
expect(await galata.isInSimpleMode()).toBeFalsy();
39+
});
40+
41+
test('Toggle Dark theme', async () => {
42+
await galata.theme.setDarkTheme();
43+
const imageName = 'dark-theme';
44+
await galata.capture.screenshot(imageName);
45+
expect(await galata.capture.compareScreenshot(imageName)).toBe('same');
46+
});
47+
48+
test('Toggle Light theme', async () => {
49+
await galata.theme.setLightTheme();
50+
});
51+
52+
test('Move File Browser to right', async () => {
53+
await galata.sidebar.moveTabToRight('filebrowser');
54+
expect(await galata.sidebar.getTabPosition('filebrowser')).toBe('right');
55+
});
56+
57+
test('Open File Browser on right', async () => {
58+
await galata.sidebar.openTab('filebrowser');
59+
expect(await galata.sidebar.isTabOpen('filebrowser')).toBeTruthy();
60+
});
61+
62+
test('Close Sidebar on right', async () => {
63+
await galata.sidebar.close('right');
64+
expect(await galata.sidebar.isOpen('right')).toBeFalsy();
65+
});
66+
67+
test('Open Sidebar on right', async () => {
68+
await galata.sidebar.open('right');
69+
expect(await galata.sidebar.isOpen('right')).toBeTruthy();
70+
});
71+
72+
test('Capture File Browser on right', async () => {
73+
let imageName = 'filebrowser-right';
74+
await galata.capture.screenshot(imageName);
75+
expect(await galata.capture.compareScreenshot(imageName)).toBe('same');
76+
});
77+
78+
test('Move File Browser to left', async () => {
79+
await galata.sidebar.moveTabToLeft('filebrowser');
80+
expect(await galata.sidebar.getTabPosition('filebrowser')).toBe('left');
81+
});
82+
83+
test('Open File Browser on left', async () => {
84+
await galata.sidebar.openTab('filebrowser');
85+
expect(await galata.sidebar.isTabOpen('filebrowser')).toBeTruthy();
86+
});
87+
88+
runMenuOpenTest();
89+
});
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) Jupyter Development Team.
2+
// Distributed under the terms of the Modified BSD License.
3+
4+
import { galata, describe, test } from '@jupyterlab/galata';
5+
import { runMenuOpenTest } from './util';
6+
7+
const fileName = 'notebook.ipynb';
8+
9+
jest.setTimeout(60000);
10+
11+
describe('Notebook Create', () => {
12+
beforeAll(async () => {
13+
await galata.resetUI();
14+
galata.context.capturePrefix = 'notebook-create';
15+
});
16+
17+
afterAll(() => {
18+
galata.context.capturePrefix = '';
19+
});
20+
21+
test('Create new Notebook', async () => {
22+
await galata.notebook.createNew(fileName);
23+
});
24+
25+
test('Create a Raw cell', async () => {
26+
await galata.notebook.setCell(0, 'raw', 'Just a raw cell');
27+
expect(await galata.notebook.getCellCount()).toBe(1);
28+
expect(await galata.notebook.getCellType(0)).toBe('raw');
29+
});
30+
31+
test('Create a Markdown cell', async () => {
32+
await galata.notebook.addCell(
33+
'markdown',
34+
'## This is **bold** and *italic* [link to jupyter.org!](http://jupyter.org)'
35+
);
36+
await galata.notebook.runCell(1, true);
37+
expect(await galata.notebook.getCellCount()).toBe(2);
38+
expect(await galata.notebook.getCellType(1)).toBe('markdown');
39+
});
40+
41+
test('Create a Code cell', async () => {
42+
await galata.notebook.addCell('code', '2 ** 3');
43+
expect(await galata.notebook.getCellCount()).toBe(3);
44+
expect(await galata.notebook.getCellType(2)).toBe('code');
45+
});
46+
47+
test('Save Notebook', async () => {
48+
await galata.notebook.save();
49+
expect(await galata.contents.fileExists(fileName)).toBeTruthy();
50+
});
51+
52+
runMenuOpenTest();
53+
54+
test('Run cells', async () => {
55+
await galata.notebook.run();
56+
await galata.notebook.save();
57+
const imageName = 'run-cells';
58+
59+
expect((await galata.notebook.getCellTextOutput(2))[0]).toBe('8');
60+
61+
const nbPanel = await galata.notebook.getNotebookInPanel();
62+
await galata.capture.screenshot(imageName, nbPanel);
63+
expect(await galata.capture.compareScreenshot(imageName)).toBe('same');
64+
});
65+
66+
test('Toggle Dark theme', async () => {
67+
await galata.theme.setDarkTheme();
68+
const nbPanel = await galata.notebook.getNotebookInPanel();
69+
const imageName = 'dark-theme';
70+
await galata.capture.screenshot(imageName, nbPanel);
71+
expect(await galata.capture.compareScreenshot(imageName)).toBe('same');
72+
});
73+
74+
test('Toggle Light theme', async () => {
75+
await galata.theme.setLightTheme();
76+
});
77+
78+
test('Delete Notebook', async () => {
79+
await galata.contents.deleteFile(fileName);
80+
expect(await galata.contents.fileExists(fileName)).toBeFalsy();
81+
});
82+
});

0 commit comments

Comments
 (0)