Skip to content

Commit

Permalink
Add some playwright ui tests
Browse files Browse the repository at this point in the history
  • Loading branch information
foo authored and ianthomas23 committed Nov 6, 2024
1 parent a6edb81 commit 8161829
Show file tree
Hide file tree
Showing 13 changed files with 4,464 additions and 44 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ jobs:
env:
YARN_ENABLE_IMMUTABLE_INSTALLS: 0
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
run: jlpm install
run: |
jlpm install
jlpm build
- name: Set up browser cache
uses: actions/cache@v4
Expand All @@ -129,7 +131,7 @@ jobs:
- name: Execute integration tests
working-directory: ui-tests
run: |
jlpm playwright test
jlpm test
- name: Upload Playwright Test report
if: always()
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const terminalsPlugin: JupyterLiteServerPlugin<ITerminals> = {
provides: ITerminals,
activate: async (app: JupyterLiteServer) => {
console.log(
'JupyterLab extension @jupyterlite/terminal:plugin is activated!'
'JupyterLite extension @jupyterlite/terminal:plugin is activated!'
);

const { serviceManager } = app;
Expand All @@ -46,7 +46,7 @@ const terminalsRoutesPlugin: JupyterLiteServerPlugin<void> = {
requires: [ITerminals],
activate: (app: JupyterLiteServer, terminals: ITerminals) => {
console.log(
'JupyterLab extension @jupyterlite/terminal:routes-plugin is activated!',
'JupyterLite extension @jupyterlite/terminal:routes-plugin is activated!',
terminals
);

Expand Down
13 changes: 7 additions & 6 deletions ui-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ and [Galata](https://github.com/jupyterlab/jupyterlab/tree/main/galata) helper.

The Playwright configuration is defined in [playwright.config.js](./playwright.config.js).

The JupyterLab server configuration to use for the integration test is defined
in [jupyter_server_test_config.py](./jupyter_server_test_config.py).

The default configuration will produce video for failing tests and an HTML report.

> There is a new experimental UI mode that you may fall in love with; see [that video](https://www.youtube.com/watch?v=jF0yA-JLQW0).
Expand All @@ -34,6 +31,7 @@ jlpm build:prod
```sh
cd ./ui-tests
jlpm install
jlpm build
jlpm playwright install
cd ..
```
Expand All @@ -42,7 +40,7 @@ cd ..

```sh
cd ./ui-tests
jlpm playwright test
jlpm test
```

Test results will be shown in the terminal. In case of any test failures, the test report
Expand Down Expand Up @@ -71,6 +69,7 @@ jlpm build:prod
```sh
cd ./ui-tests
jlpm install
jlpm build
jlpm playwright install
cd ..
```
Expand All @@ -79,7 +78,7 @@ cd ..

```sh
cd ./ui-tests
jlpm playwright test -u
jlpm test -u
```

> Some discrepancy may occurs between the snapshots generated on your computer and
Expand Down Expand Up @@ -107,6 +106,7 @@ jlpm build:prod
```sh
cd ./ui-tests
jlpm install
jlpm build
jlpm playwright install
cd ..
```
Expand Down Expand Up @@ -145,6 +145,7 @@ jlpm build:prod
```sh
cd ./ui-tests
jlpm install
jlpm build
jlpm playwright install
cd ..
```
Expand All @@ -153,7 +154,7 @@ cd ..

```sh
cd ./ui-tests
jlpm playwright test --debug
jlpm test --debug
```

## Upgrade Playwright and the browsers
Expand Down
17 changes: 17 additions & 0 deletions ui-tests/build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Build jupyterlite deployment containing terminal extension for playwright tests.

from pathlib import Path
from subprocess import run

import jupyterlab

extra_labextensions_path = str(Path(jupyterlab.__file__).parent / "galata")
cmd = [
"jupyter",
"lite",
"build",
"--contents",
"../demo/contents",
f"--FederatedExtensionAddon.extra_labextensions_path={extra_labextensions_path}",
]
run(cmd, check=True)
8 changes: 8 additions & 0 deletions ui-tests/jupyter-lite.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"jupyter-lite-schema-version": 0,
"jupyter-config-data": {
"appName": "JupyterLite terminal UI Tests",
"exposeAppInBrowser": true,
"terminalsAvailable": true
}
}
5 changes: 5 additions & 0 deletions ui-tests/jupyter_lite_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"LiteBuildConfig": {
"output_dir": "dist"
}
}
12 changes: 0 additions & 12 deletions ui-tests/jupyter_server_test_config.py

This file was deleted.

8 changes: 6 additions & 2 deletions ui-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
"description": "JupyterLab jupyterlite-terminal Integration Tests",
"private": true,
"scripts": {
"start": "jupyter lab --config jupyter_server_test_config.py",
"build": "jlpm clean && python build.py",
"clean": "rimraf dist",
"clean:all": "jlpm clean && rimraf cockle_wasm_env .cockle_temp",
"start": "npx static-handler -p 8000 --cors --coop --coep --corp ./dist",
"test": "jlpm playwright test",
"test:update": "jlpm playwright test --update-snapshots"
},
"devDependencies": {
"@jupyterlab/galata": "^5.0.5",
"@playwright/test": "^1.37.0"
"@playwright/test": "^1.37.0",
"rimraf": "^6.0.1"
}
}
9 changes: 7 additions & 2 deletions ui-tests/playwright.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ const baseConfig = require('@jupyterlab/galata/lib/playwright-config');

module.exports = {
...baseConfig,
use: {
acceptDownloads: true,
autoGoto: false,
baseURL: 'http://localhost:8000'
},
webServer: {
command: 'jlpm start',
url: 'http://localhost:8888/lab',
port: 8000,
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI
reuseExistingServer: true
}
};
127 changes: 109 additions & 18 deletions ui-tests/tests/jupyterlite_terminal.spec.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,115 @@
import { expect, test } from '@jupyterlab/galata';

/**
* Don't load JupyterLab webpage before running the tests.
* This is required to ensure we capture all log messages.
*/
test.use({ autoGoto: false });

// TODO: re-enable when testing with JupyterLite
test.skip('should emit an activation console message', async ({ page }) => {
const logs: string[] = [];

page.on('console', message => {
logs.push(message.text());
const TERMINAL_SELECTOR = '.jp-Terminal';

async function inputLine(page, text: string) {
for (const char of text) {
await page.keyboard.type(char);
await page.waitForTimeout(10);
}
await page.keyboard.press('Enter');
}

test.describe('Terminal extension', () => {
test('should emit activation console messages', async ({ page }) => {
const logs: string[] = [];
page.on('console', message => {
logs.push(message.text());
});

await page.goto();

expect(
logs.filter(s =>
s.match(/^JupyterLite extension @jupyterlite\/terminal:.*is activated!/)
)
).toHaveLength(2);
});
});

test.describe('Terminal', () => {
test('should open via File menu', async ({ page }) => {
await page.goto();
await page.menu.clickMenuItem('File>New>Terminal');
await page.locator(TERMINAL_SELECTOR).waitFor();
});

test('should appear in sidebar', async ({ page }) => {
await page.goto();
await page.menu.clickMenuItem('File>New>Terminal');
await page.locator(TERMINAL_SELECTOR).waitFor();
await page.sidebar.openTab('jp-running-sessions');
await expect(page.locator('text=terminals/1')).toBeVisible();
});

test('should open via launcher', async ({ page }) => {
await page.goto();
await page
.locator('.jp-LauncherCard-label >> p:has-text("Terminal")')
.click();
await page.locator(TERMINAL_SELECTOR).waitFor();
});

test('should create a new file', async ({ page }) => {
await page.goto();
await page.menu.clickMenuItem('File>New>Terminal');
await page.locator(TERMINAL_SELECTOR).waitFor();
await page.locator('div.xterm-screen').click(); // sets focus for keyboard input

await inputLine(page, 'echo Hello > out.txt');
await page.getByTitle('Name: out.txt').waitFor();
});
});

await page.goto();
test.describe('Images', () => {
test('initial', async ({ page }) => {
await page.goto();
await page.menu.clickMenuItem('File>New>Terminal');
await page.locator(TERMINAL_SELECTOR).waitFor();
await page.locator('div.xterm-screen').click(); // sets focus for keyboard input

expect(
logs.filter(
s => s === 'JupyterLab extension @jupyterlite/terminal is activated!'
)
).toHaveLength(1);
// Hide modification times.
const modified = page.locator('span.jp-DirListing-itemModified');
await modified.evaluateAll(els => els.map(el => el.innerHTML = ''));

const imageName = 'initial.png';
expect(await page.screenshot()).toMatchSnapshot(imageName.toLowerCase());
});

test('various commands', async ({ page }) => {
await page.goto();
await page.menu.clickMenuItem('File>New>Terminal');
await page.locator(TERMINAL_SELECTOR).waitFor();
await page.locator('div.xterm-screen').click(); // sets focus for keyboard input

const wait = 100; // milliseconds

await inputLine(page, 'ls'); // avoid timestamps
await page.waitForTimeout(wait);

await inputLine(page, 'cp months.txt other.txt');
await page.waitForTimeout(wait);

await inputLine(page, 'ls'); // avoid timestamps
await page.waitForTimeout(wait);

await inputLine(page, 'una\t'); // tab complete command name
await page.waitForTimeout(wait);

await inputLine(page, 'grep ember mon\t'); // tab complete filename
await page.waitForTimeout(wait);

await page.keyboard.press('Tab'); // list all commands
await page.waitForTimeout(wait);

await inputLine(page, 'abc'); // no such command
await page.waitForTimeout(wait);

// Hide modification times.
const modified = page.locator('span.jp-DirListing-itemModified');
await modified.evaluateAll(els => els.map(el => el.innerHTML = ''));

const imageName = 'various-commands.png';
expect(await page.screenshot()).toMatchSnapshot(imageName.toLowerCase());
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 8161829

Please sign in to comment.