Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
test(e2e): add Playwright setup and notebook load spec
  • Loading branch information
vgeorge committed Oct 6, 2025
commit 5f8b799a1953648d2715df3cf7cc2e017ea088e2
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ node_modules
.env
.env.local
.env.*.local

# Playwright test artifacts
test-results/
playwright-report/
64 changes: 64 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
},
"dependencies": {
"@anywidget/react": "^0.2.0",
"@babel/runtime": "^7.28.4",
"@deck.gl/core": "^9.1.14",
"@deck.gl/extensions": "^9.1.14",
"@deck.gl/layers": "^9.1.14",
"@deck.gl/react": "^9.1.14",
"@geoarrow/deck.gl-layers": "^0.3.1",
"@babel/runtime": "^7.28.4",
"@nextui-org/react": "^2.4.8",
"@xstate/react": "^6.0.0",
"apache-arrow": "^21.0.0",
Expand All @@ -31,6 +31,7 @@
"@anywidget/types": "^0.2.0",
"@eslint/js": "^9.36.0",
"@jupyter-widgets/base": "^6.0.10",
"@playwright/test": "^1.55.1",
"@statelyai/inspect": "^0.4.0",
"@types/lodash": "^4.17.13",
"@types/lodash.debounce": "^4.0.9",
Expand Down Expand Up @@ -63,7 +64,9 @@
"prettier:check": "prettier './src/**/*.{ts,tsx,css}' --check",
"prettier": "prettier './src/**/*.{ts,tsx,css}' --write",
"lint": "eslint src",
"test": "vitest run"
"test": "vitest run",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui"
},
"volta": {
"node": "18.18.2",
Expand Down
37 changes: 37 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests/e2e',
testMatch: '**/*.spec.ts',
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: 1,
timeout: 60000,
expect: {
timeout: 30000,
},
reporter: 'list',

use: {
baseURL: 'http://localhost:8889',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
navigationTimeout: 30000,
browserName: 'chromium',
},

projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],

webServer: {
command: 'uv run --group dev jupyter lab --no-browser --port=8889 --notebook-dir=tests/e2e/fixtures --IdentityProvider.token=""',
url: 'http://localhost:8889',
reuseExistingServer: false, // Always restart for clean state
timeout: 30000,
},
});
30 changes: 30 additions & 0 deletions tests/e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# End-to-End Tests

Playwright-based end-to-end tests for Lonboard widgets in JupyterLab.

## Running Tests

```bash
# Run all e2e tests
npm run test:e2e

# Run with UI mode
npm run test:e2e:ui
```

## Architecture

- **JupyterLab**: Runs on port 8889 (isolated from dev instances on 8888)
- **Working Directory**: `tests/e2e/fixtures/` (only test notebooks visible)
- **Clean State**: JupyterLab server restarts for each test run (`reuseExistingServer: false`)
- Fresh kernel state on every run
- No session persistence between test runs
- No interference with development sessions

## Test Fixtures

Test notebooks are stored in `tests/e2e/fixtures/` and committed to the repository. They provide scaffolding to replicate correct user workflows.

### simple-map.ipynb

Basic test notebook with 4 points in a grid displaying a simple scatterplot map.
1 change: 1 addition & 0 deletions tests/e2e/fixtures/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.ipynb_checkpoints/
63 changes: 63 additions & 0 deletions tests/e2e/fixtures/simple-map.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import geopandas as gpd\n",
"from shapely.geometry import Point\n",
"from lonboard import Map, ScatterplotLayer"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Simple test data - 4 points in a grid\n",
"points = [\n",
" Point(-10, -10),\n",
" Point(-10, 10),\n",
" Point(10, -10),\n",
" Point(10, 10),\n",
"]\n",
"gdf = gpd.GeoDataFrame(geometry=points, crs='EPSG:4326')\n",
"\n",
"layer = ScatterplotLayer.from_geopandas(\n",
" gdf,\n",
" get_fill_color=[255, 0, 0],\n",
" get_radius=100000,\n",
")\n",
"\n",
"m = Map(layer, view_state={\"longitude\": 0, \"latitude\": 0, \"zoom\": 2})\n",
"m"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Check selected bounds (run after bbox selection)\n",
"print(f\"Selected bounds: {m.selected_bounds}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
14 changes: 14 additions & 0 deletions tests/e2e/notebook-load.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { test, expect } from '@playwright/test';

test.describe('Notebook Load', () => {
test('JupyterLab starts and loads notebook', async ({ page }) => {
// Open a simple map notebook
await page.goto('/lab/tree/simple-map.ipynb');

// Verify the correct notebook tab is active
await expect(page.locator('.jp-mod-current[role="tab"]:has-text("simple-map.ipynb")')).toBeVisible({ timeout: 10000 });

// Verify kernel status shows in footer
await expect(page.locator('text=/Python 3.*Idle/')).toBeVisible({ timeout: 30000 });
});
});