Galata*
Galata is a set of helpers and fixtures for JupyterLab UI Testing using [Playwright Test Runner](https://playwright.dev/docs/intro) that provides:
- **[Rich High Level API](src/jupyterlabpage.ts)** to control and inspect JupyterLab UI programmatically
- **[Dedicated fixtures](src/fixtures.ts)** to hooks the helpers in the Playwright Page and ensure state isolation between tests.
## Getting Started
### Installation
Add Galata to your project:
```bash
jlpm add -D @jupyterlab/galata
# Install playwright supported browser
jlpm playwright install
```
Create a Playwright configuration file `playwright.config.js` containing:
```js
module.exports = require('@jupyterlab/galata/lib/playwright-config');
```
### First test
Create `ui-tests/foo.spec.ts` to define your test.
```typescript
import { expect, test } from '@jupyterlab/galata';
test.describe('Notebook Tests', () => {
test('Create New Notebook', async ({ page, tmpPath }) => {
const fileName = 'create_test.ipynb';
await page.notebook.createNew(fileName);
expect(
await page.waitForSelector(`[role="main"] >> text=${fileName}`)
).toBeTruthy();
expect(await page.contents.fileExists(`${tmpPath}/${fileName}`)).toEqual(
true
);
});
});
```
This will create a notebook, open it and check it exists.
### Launch JupyterLab
Before running the test, you will need to launch the JupyterLab server with some
specific options.
Create `jupyter_server_test_config.py` with the following content.
```py
from tempfile import mkdtemp
c.ServerApp.port = 8888
c.ServerApp.port_retries = 0
c.ServerApp.open_browser = False
c.ServerApp.root_dir = mkdtemp(prefix='galata-test-')
c.ServerApp.token = ""
c.ServerApp.password = ""
c.ServerApp.disable_check_xsrf = True
c.LabApp.expose_app_in_browser = True
```
Then start the server with:
```bash
jupyter lab --config jupyter_server_test_config.py
```
### Run test project
```bash
jlpm playwright test
```
Galata should generate console output similar to following
```bash
Using config at .../playwright.config.js
Running 1 test using 1 worker
✓ ui-tests/foo.spec.ts:5:3 › Notebook Tests Create New Notebook (13s)
1 passed (15s)
```
Playwright Test just ran a test using Chromium browser, in a headless manner. You can use headed browser to see what is going on during the test:
```bash
jlpm playwright test --headed
```
Test assets (including test videos) will be saved in a `test-results` folder and by default a HTML
report will be created in `playwright-report` folder. That report can be see by running:
```bash
http-server ./playwright-report -a localhost -o
```
## User advices
### Create tests
To create tests, the easiest way is to use the code generator tool of playwright:
```
jupyter lab --config jupyter_server_test_config.py &
jlpm playwright codegen localhost:8888
```
### Debug tests
To debug tests, a good way is to use the inspector tool of playwright:
```
jupyter lab --config jupyter_server_test_config.py &
PWDEBUG=1 jlpm playwright test
```
## Fixtures
Here are the new test fixture introduced by Galata on top of [Playwright fixtures](https://playwright.dev/docs/api/class-fixtures).
### baseURL
- type: < string >
Application base URL without `/lab`. It defaults to environment variable `TARGET_URL` or `http://localhost:8888` if nothing
is defined.
### appPath
- type: < string >
Application URL path fragment; default `"/lab"`
### autoGoto
- type: < boolean >
Whether to go to JupyterLab page within the fixture or not; default `true`.
If set to `false`, it allows you to add route mock before loading JupyterLab.
Example:
```ts
test.use({ autoGoto: false });
test('Open language menu', async ({ page }) => {
await page.route(/.*\/api\/translation.*/, (route, request) => {
if (request.method() === 'GET') {
return route.fulfill({
status: 200,
body:
'{"data": {"en": {"displayName": "English", "nativeName": "English"}}, "message": ""}'
});
} else {
return route.continue();
}
});
await page.goto();
// ...
});
```
### serverFiles
- type: <'on' | 'off' | 'only-on-failure'>
Galata can keep the uploaded and created files in `tmpPath` on
the server root for debugging purpose. By default the files are kept
on failure.
- 'off' - `tmpPath` is deleted after each tests
- 'on' - `tmpPath` is never deleted
- 'only-on-failure' - `tmpPath` is deleted except if a test failed or timed out.
### mockState
- type: < boolean | Record >
Mock JupyterLab state in-memory or not.
Possible values are:
- true (default): JupyterLab state will be mocked on a per test basis
- false: JupyterLab state won't be mocked (Be careful it will write state in local files)
- Record: Initial JupyterLab data state - Mapping (state key, value).
By default the state is stored in-memory.
Example:
```ts
test.use({
mockState: {
'layout-restorer:data': {
main: {
dock: {
type: 'tab-area',
currentIndex: 0,
widgets: []
}
},
down: {
size: 0,
widgets: []
},
left: {
collapsed: false,
visible: true,
current: 'running-sessions',
widgets: [
'filebrowser',
'jp-property-inspector',
'running-sessions',
'@jupyterlab/toc:plugin',
'debugger-sidebar',
'extensionmanager.main-view'
]
},
right: {
collapsed: true,
visible: true,
widgets: []
},
relativeSizes: [0.4, 0.6, 0]
}
} as any
});
test('should return the mocked state', async ({ page }) => {
expect(
await page.waitForSelector(
'[aria-label="Running Sessions section"] >> text=Open Tabs'
)
).toBeTruthy();
});
```
### mockSettings
- type: < boolean | Record >
Mock JupyterLab settings in-memory or not.
Possible values are:
- true: JupyterLab settings will be mocked on a per test basis
- false: JupyterLab settings won't be mocked (Be careful it will read & write settings local files)
- Record: Mapping {pluginId: settings} that will be default user settings
The default value is `galata.DEFAULT_SETTINGS`
By default the settings are stored in-memory. However the
they are still initialized with the hard drive values.
Example:
```ts
test.use({
mockSettings: {
...galata.DEFAULT_SETTINGS,
'@jupyterlab/apputils-extension:themes': {
theme: 'JupyterLab Dark'
}
}
});
test('should return mocked settings', async ({ page }) => {
expect(await page.theme.getTheme()).toEqual('JupyterLab Dark');
});
```
### sessions
- type: