5.1.6 • Published 8 days ago

@jupyterlab/galata v5.1.6

Weekly downloads
-
License
BSD-3-Clause
Repository
github
Last release
8 days ago

Galata is a set of helpers and fixtures for JupyterLab UI Testing using Playwright Test Runner that provides:

screencast

Getting Started

Installation

Add Galata to your project:

jlpm add -D @jupyterlab/galata
# Install playwright supported browser
jlpm playwright install

Create a Playwright configuration file playwright.config.js containing:

module.exports = require('@jupyterlab/galata/lib/playwright-config');

First test

Create ui-tests/foo.spec.ts to define your test.

import { test } from '@jupyterlab/galata';
import { expect } from '@playwright/test';

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.

from tempfile import mkdtemp

c.ServerApp.port = 8888
c.ServerApp.open_browser = False
c.LabApp.dev_mode = True

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:

jupyter lab --config jupyter_server_test_config.py

Run test project

jlpm playwright test

Galata should generate console output similar to following

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:

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:

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.

appPath

  • type: ;

Application URL path fragment; default "/lab"

autoGoto

  • type:

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:

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<string, unknown>>;

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<string, unknown>: Initial JupyterLab data state - Mapping (state key, value). By default the state is stored in-memory.

Example:

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<string, unknown>>;

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<string, unknown>: 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:

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: <Map<string, Session.IModel> | null>;

Sessions created during the test. Possible values are:

  • null: The sessions API won't be mocked
  • Map<string, Session.IModel>: The sessions created during a test. By default the sessions created during a test will be tracked and disposed at the end.

Example:

test('should return the active sessions', async ({ page, sessions }) => {
  await page.notebook.createNew();

  // Wait for the poll to tick
  await page.waitForResponse(
    async response =>
      response.url().includes('api/sessions') &&
      response.request().method() === 'GET' &&
      ((await response.json()) as any[]).length === 1
  );

  expect(sessions.size).toEqual(1);
  // You can introspect the sessions.values()[0] if needed
});

terminals

  • type: <Map<string, TerminalAPI.IModel> | null>;

Terminals created during the test. Possible values are:

  • null: The Terminals API won't be mocked
  • Map<string, TerminalsAPI.IModel>: The Terminals created during a test. By default the Terminals created during a test will be tracked and disposed at the end.

Example:

test('should return the active terminals', async ({ page, terminals }) => {
  await Promise.all([
    page.waitForResponse(
      response =>
        response.request().method() === 'POST' &&
        response.url().includes('api/terminals')
    ),
    page.menu.clickMenuItem('File>New>Terminal')
  ]);

  // Wait for the poll to tick
  await page.waitForResponse(
    async response =>
      response.url().includes('api/terminals') &&
      response.request().method() === 'GET' &&
      ((await response.json()) as any[]).length === 1
  );

  expect(terminals.size).toEqual(1);
  // You can introspect the [...terminals.values()][0] if needed
});

tmpPath

  • type: ;

Unique test temporary path created on the server.

Note: if you override this string, you will need to take care of creating the folder and cleaning it.

Example:

test.use({ tmpPath: 'test-toc' });

test.describe.serial('Table of Contents', () => {
  test.beforeAll(async ({ baseURL, tmpPath }) => {
    const contents = galata.newContentsHelper(baseURL);
    await contents.uploadFile(
      path.resolve(__dirname, `./notebooks/${fileName}`),
      `${tmpPath}/${fileName}`
    );
  });

  test.afterAll(async ({ baseURL, tmpPath }) => {
    const contents = galata.newContentsHelper(baseURL);
    await contents.deleteDirectory(tmpPath);
  });
});

Development

Build

Install dependencies and build

cd galata
jlpm
jlpm run build

For tests to be run, a JupyterLab instance must be up and running. Launch it without credentials. Tests expect to connect JupyterLab from localhost:8888 by default. If a different URL is to be used, it can be specified by defining TARGET_URL environment variable or setting the Playwright baseURL fixture.

jlpm run start

The JupyterLab root directory is randomly generated in the temporary folder (prefixed with galata-test-).

Running tests

Tests are grouped in two projects: galata and jupyterlab. The first one is testing Galata helpers and fixtures when the other one is running all tests for Jupyterlab. By default, both projects will be executed when running jlpm run test. But you can select one project with the CLI option --project <project-id>.

Configuration

Galata can be configured by using command line arguments or using playwright.config.js file. Full list of config options can be accessed using jlpm playwright test --help.

Reference Image Captures

Reference image are saved next to test files in <test-file-name>-snapshots folders. If a reference screenshots does not exist, it will be generated at the first execution of a test. You can also update them by running jlpm playwright test --update-snapshots.

About Galata Name

Galata framework is named after Galata Tower in Istanbul. Centuries ago, Galata Tower was used to spot fires in the city. Tower was also used as astronomical observatory in the past.

Acknowledgement

Development of this project began under Bloomberg organization by Mehmet Bektas, then it was transferred to JupyterLab organization. We gratefully acknowledge Bloomberg for the generous contribution and supporting open-source software community.

5.2.0-beta.3

8 days ago

5.2.0-beta.2

9 days ago

5.2.0-beta.1

18 days ago

5.1.6

19 days ago

5.2.0-beta.0

25 days ago

5.2.0-alpha.2

1 month ago

5.1.5

1 month ago

5.2.0-alpha.1

1 month ago

5.1.4

2 months ago

5.1.3

2 months ago

5.2.0-alpha.0

2 months ago

5.0.13

2 months ago

4.5.7

2 months ago

5.1.2

2 months ago

5.1.1

2 months ago

5.1.0

3 months ago

5.1.0-rc.1

3 months ago

5.0.12

3 months ago

5.1.0-rc.0

3 months ago

5.0.11

3 months ago

5.1.0-beta.2

3 months ago

5.1.0-beta.1

3 months ago

5.0.10

4 months ago

5.1.0-beta.0

4 months ago

4.5.6

7 months ago

5.0.9

5 months ago

5.0.8

6 months ago

5.0.7

7 months ago

5.0.6

8 months ago

5.0.5

9 months ago

5.0.4

9 months ago

5.0.3

10 months ago

5.1.0-alpha.1

8 months ago

5.1.0-alpha.2

7 months ago

5.1.0-alpha.3

6 months ago

5.1.0-alpha.4

5 months ago

4.5.5

10 months ago

4.5.4

11 months ago

5.0.0-rc.1

12 months ago

5.0.2

11 months ago

5.0.1

11 months ago

5.0.0

12 months ago

4.5.3

1 year ago

5.0.0-beta.2

1 year ago

5.0.0-rc.0

1 year ago

5.0.0-beta.0

1 year ago

5.0.0-beta.1

1 year ago

5.0.0-alpha.19

1 year ago

5.0.0-alpha.18

1 year ago

5.0.0-alpha.22

1 year ago

5.0.0-alpha.20

1 year ago

5.0.0-alpha.21

1 year ago

4.5.0

1 year ago

4.5.2

1 year ago

4.5.1

1 year ago

5.0.0-alpha.17

1 year ago

5.0.0-alpha.16

1 year ago

4.5.0-beta.0

1 year ago

4.5.0-rc.1

1 year ago

4.4.1

1 year ago

4.4.3

1 year ago

4.4.2

1 year ago

4.5.0-rc.0

1 year ago

4.5.0-alpha.5

1 year ago

4.5.0-alpha.4

1 year ago

4.5.0-alpha.3

1 year ago

4.4.0-beta.0

2 years ago

4.4.0-alpha.0

2 years ago

4.3.8

2 years ago

5.0.0-alpha.15

2 years ago

5.0.0-alpha.14

2 years ago

4.4.0

2 years ago

4.4.0-rc.0

2 years ago

4.5.0-alpha.2

1 year ago

4.5.0-alpha.1

1 year ago

4.5.0-alpha.0

2 years ago

4.3.6

2 years ago

4.3.7

2 years ago

5.0.0-alpha.13

2 years ago

4.3.5

2 years ago

5.0.0-alpha.12

2 years ago

4.3.4

2 years ago

5.0.0-alpha.11

2 years ago

5.0.0-alpha.10

2 years ago

4.3.2

2 years ago

4.3.1

2 years ago

4.3.3

2 years ago

4.3.0

2 years ago

5.0.0-alpha.9

2 years ago

5.0.0-alpha.8

2 years ago

4.3.0-alpha.0

2 years ago

4.2.3

2 years ago

4.3.0-rc.0

2 years ago

4.2.4

2 years ago

4.3.0-beta.0

2 years ago

5.0.0-alpha.7

2 years ago

5.0.0-alpha.6

2 years ago

4.2.2

2 years ago

4.2.1

2 years ago

4.2.0

2 years ago

5.0.0-alpha.5

2 years ago

5.0.0-alpha.4

2 years ago

4.2.0-beta.0

2 years ago

5.0.0-alpha.3

2 years ago

4.2.0-alpha.16

2 years ago

4.1.4

2 years ago

4.2.0-rc.0

2 years ago

4.2.0-alpha.17

2 years ago

4.1.3

2 years ago

4.1.6

2 years ago

4.2.0-alpha.18

2 years ago

4.1.5

2 years ago

5.0.0-alpha.2

2 years ago

5.0.0-alpha.1

2 years ago

4.3.0-alpha.15

2 years ago

4.1.2

2 years ago

4.0.3

2 years ago

4.1.1

2 years ago

4.0.2

2 years ago

4.3.0-alpha.13

3 years ago

4.3.0-alpha.14

3 years ago

4.3.0-alpha.12

3 years ago

4.0.0-rc.0

3 years ago

4.0.1

3 years ago

4.0.0

3 years ago

4.3.0-alpha.11

3 years ago

4.3.0-alpha.10

3 years ago

4.3.0-alpha.9

3 years ago

4.3.0-alpha.8

3 years ago

3.0.11-4

3 years ago

4.3.0-alpha.7

3 years ago

3.0.11-3

3 years ago

3.0.11-2

3 years ago

3.0.11-1

3 years ago

3.0.7-7

3 years ago

3.0.7-6

3 years ago

3.0.7-2

3 years ago

3.0.7-4

3 years ago

3.0.7-3

3 years ago

3.0.7-5

3 years ago

3.0.7-1

3 years ago

3.0.3-3

3 years ago

3.0.3-2

3 years ago

3.0.3-1

3 years ago