dev: initial implementation of notebook-snapshot extension
Manual-commit notebook snapshot extension (JupyterLab 4):
Backend
- Storage adapter layer (Local + S3 via boto3, pip extra)
- Dumb put/get/list/delete contract; key validation
- S3Storage: lazy boto3 import, S3ConnectionError with friendly
messages + connectivity check at startup
- SnapshotStore
- Strip code outputs/execution_count, MD5 on cleaned JSON
- Gzipped envelope (id/timestamp/name/description/hash/size/notebook)
- Append-only manifest.json (rebuildable from version files)
- SnapshotUnchangedError when new hash matches most recent version
- REST API (commit / list / content) under /snapshot/ namespace
- traitlets config (storage_type, local_root, s3_* + S3_* env fallback)
Frontend
- snapshot:commit command (toolbar button + command palette)
- Dialog for name + description
- SnapshotPanel (left sidebar timeline) + DiffWidget (main area)
- Cell diff: id-match first, LCS fallback; jsdiff for line diff
- Restore via model.fromJSON() + context.save() (no refresh)
- All AGENTS.md hard conventions enforced
Tests: 33 backend pytest passing (storage + store + routes)
Docs: AGENTS.md, design.md, README.md synced with implementation.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
# Integration Testing
|
||||
|
||||
This folder contains the integration tests of the extension.
|
||||
|
||||
They are defined using [Playwright](https://playwright.dev/docs/intro) test runner
|
||||
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 UI mode that you may like; see [that video](https://www.youtube.com/watch?v=jF0yA-JLQW0).
|
||||
|
||||
## Run the tests
|
||||
|
||||
> All commands are assumed to be executed from the root directory
|
||||
|
||||
To run the tests, you need to:
|
||||
|
||||
1. Compile the extension:
|
||||
|
||||
```sh
|
||||
jlpm install
|
||||
jlpm build:prod
|
||||
```
|
||||
|
||||
> Check the extension is installed in JupyterLab.
|
||||
|
||||
2. Install test dependencies (needed only once):
|
||||
|
||||
```sh
|
||||
cd ./ui-tests
|
||||
jlpm install
|
||||
jlpm playwright install
|
||||
cd ..
|
||||
```
|
||||
|
||||
3. Execute the [Playwright](https://playwright.dev/docs/intro) tests:
|
||||
|
||||
```sh
|
||||
cd ./ui-tests
|
||||
jlpm playwright test
|
||||
```
|
||||
|
||||
Test results will be shown in the terminal. In case of any test failures, the test report
|
||||
will be opened in your browser at the end of the tests execution; see
|
||||
[Playwright documentation](https://playwright.dev/docs/test-reporters#html-reporter)
|
||||
for configuring that behavior.
|
||||
|
||||
## Update the tests snapshots
|
||||
|
||||
> All commands are assumed to be executed from the root directory
|
||||
|
||||
If you are comparing snapshots to validate your tests, you may need to update
|
||||
the reference snapshots stored in the repository. To do that, you need to:
|
||||
|
||||
1. Compile the extension:
|
||||
|
||||
```sh
|
||||
jlpm install
|
||||
jlpm build:prod
|
||||
```
|
||||
|
||||
> Check the extension is installed in JupyterLab.
|
||||
|
||||
2. Install test dependencies (needed only once):
|
||||
|
||||
```sh
|
||||
cd ./ui-tests
|
||||
jlpm install
|
||||
jlpm playwright install
|
||||
cd ..
|
||||
```
|
||||
|
||||
3. Execute the [Playwright](https://playwright.dev/docs/intro) command:
|
||||
|
||||
```sh
|
||||
cd ./ui-tests
|
||||
jlpm playwright test -u
|
||||
```
|
||||
|
||||
> Some discrepancy may occurs between the snapshots generated on your computer and
|
||||
> the one generated on the CI. To ease updating the snapshots on a PR, you can
|
||||
> type `please update playwright snapshots` to trigger the update by a bot on the CI.
|
||||
> Once the bot has computed new snapshots, it will commit them to the PR branch.
|
||||
|
||||
## Create tests
|
||||
|
||||
> All commands are assumed to be executed from the root directory
|
||||
|
||||
To create tests, the easiest way is to use the code generator tool of playwright:
|
||||
|
||||
1. Compile the extension:
|
||||
|
||||
```sh
|
||||
jlpm install
|
||||
jlpm build:prod
|
||||
```
|
||||
|
||||
> Check the extension is installed in JupyterLab.
|
||||
|
||||
2. Install test dependencies (needed only once):
|
||||
|
||||
```sh
|
||||
cd ./ui-tests
|
||||
jlpm install
|
||||
jlpm playwright install
|
||||
cd ..
|
||||
```
|
||||
|
||||
3. Start the server:
|
||||
|
||||
```sh
|
||||
cd ./ui-tests
|
||||
jlpm start
|
||||
```
|
||||
|
||||
4. Execute the [Playwright code generator](https://playwright.dev/docs/codegen) in **another terminal**:
|
||||
|
||||
```sh
|
||||
cd ./ui-tests
|
||||
jlpm playwright codegen localhost:8888
|
||||
```
|
||||
|
||||
## Debug tests
|
||||
|
||||
> All commands are assumed to be executed from the root directory
|
||||
|
||||
To debug tests, a good way is to use the inspector tool of playwright:
|
||||
|
||||
1. Compile the extension:
|
||||
|
||||
```sh
|
||||
jlpm install
|
||||
jlpm build:prod
|
||||
```
|
||||
|
||||
> Check the extension is installed in JupyterLab.
|
||||
|
||||
2. Install test dependencies (needed only once):
|
||||
|
||||
```sh
|
||||
cd ./ui-tests
|
||||
jlpm install
|
||||
jlpm playwright install
|
||||
cd ..
|
||||
```
|
||||
|
||||
3. Execute the Playwright tests in [debug mode](https://playwright.dev/docs/debug):
|
||||
|
||||
```sh
|
||||
cd ./ui-tests
|
||||
jlpm playwright test --debug
|
||||
```
|
||||
|
||||
## Upgrade Playwright and the browsers
|
||||
|
||||
To update the web browser versions, you must update the package `@playwright/test`:
|
||||
|
||||
```sh
|
||||
cd ./ui-tests
|
||||
jlpm up "@playwright/test"
|
||||
jlpm playwright install
|
||||
```
|
||||
@@ -0,0 +1,12 @@
|
||||
"""Server configuration for integration tests.
|
||||
|
||||
!! Never use this configuration in production because it
|
||||
opens the server to the world and provide access to JupyterLab
|
||||
JavaScript objects through the global window variable.
|
||||
"""
|
||||
from jupyterlab.galata import configure_jupyter_server
|
||||
|
||||
configure_jupyter_server(c)
|
||||
|
||||
# Uncomment to set server log level to debug level
|
||||
# c.ServerApp.log_level = "DEBUG"
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "snapshot-ui-tests",
|
||||
"version": "1.0.0",
|
||||
"description": "JupyterLab snapshot Integration Tests",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "jupyter lab --config jupyter_server_test_config.py",
|
||||
"test": "jlpm playwright test",
|
||||
"test:update": "jlpm playwright test --update-snapshots"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jupyterlab/galata": "^5.0.5",
|
||||
"@playwright/test": "^1.60.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Configuration for Playwright using default from @jupyterlab/galata
|
||||
*/
|
||||
const baseConfig = require('@jupyterlab/galata/lib/playwright-config');
|
||||
|
||||
module.exports = {
|
||||
...baseConfig,
|
||||
webServer: {
|
||||
command: 'jlpm start',
|
||||
url: 'http://localhost:8888/lab',
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: !process.env.CI
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
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 });
|
||||
|
||||
test('should emit an activation console message', async ({ page }) => {
|
||||
const logs: string[] = [];
|
||||
|
||||
page.on('console', message => {
|
||||
logs.push(message.text());
|
||||
});
|
||||
|
||||
await page.goto();
|
||||
|
||||
expect(
|
||||
logs.filter(s => s === 'JupyterLab extension snapshot is activated!')
|
||||
).toHaveLength(1);
|
||||
});
|
||||
Reference in New Issue
Block a user