Skip to content

Commit 3492d2e

Browse files
paddymulclaude
andauthored
ci: move JupyterLab Playwright tests to dedicated CI job (#534)
* ci: move JupyterLab Playwright tests from build.yml to ci.yml Extract the inline JupyterLab Playwright test step from the monolithic build job into its own dedicated job in ci.yml, matching the pattern used by the Marimo, WASM Marimo, Server, and Storybook Playwright jobs. The new job downloads the pre-built wheel artifact and runs the test script directly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: set JupyterLab Playwright timeout to 5 minutes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: restore integration test notebooks accidentally removed in abd5e85 These were removed in the bulk cleanup of generated files but are needed by the JupyterLab Playwright tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: add error diagnostics to test_polars_dfviewer notebook Print the actual traceback when PolarsDFViewer fails so we can see the root cause in CI logs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle non-class items in find_most_specific_styling PolarsBuckarooWidget.analysis_klasses contains @stat-decorated functions (from PL_ANALYSIS_V2) alongside classes. The find_most_specific_styling helper called issubclass() on every item without checking if it was actually a class first, causing "issubclass() arg 1 must be a class" when PolarsDFViewer was used. Add isinstance(x, type) guard before the issubclass() check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent eb00f96 commit 3492d2e

14 files changed

Lines changed: 425 additions & 27 deletions

.github/workflows/build.yml

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -82,32 +82,6 @@ jobs:
8282
run: |
8383
bash scripts/test_playwright_storybook.sh
8484
85-
- name: Run JupyterLab Playwright Tests
86-
continue-on-error: true
87-
if: ${{ matrix.os == 'depot-ubuntu-latest' }}
88-
run: |
89-
# Ensure uv environment is set up
90-
uv sync --all-extras --group dev
91-
92-
# Install the built buckaroo wheel
93-
uv pip install --force-reinstall dist/*.whl
94-
95-
# Install test dependencies (polars, jupyterlab)
96-
uv pip install polars jupyterlab
97-
98-
# Detect venv location (uv sync creates .venv by default, but check if it exists)
99-
if [ -d ".venv" ]; then
100-
VENV_LOCATION=".venv"
101-
else
102-
# Fallback: try to detect from uv's Python environment
103-
VENV_LOCATION=$(uv run python -c "import sys; print(sys.prefix)" 2>/dev/null || echo ".venv")
104-
fi
105-
106-
# Run the integration test script with venv location
107-
bash scripts/test_playwright_jupyter.sh --venv-location="$VENV_LOCATION"
108-
env:
109-
UV_PYTHON: ${{ matrix.python-version }}
110-
11185
11286
# ui-test:
11387
# needs: [build]

.github/workflows/ci.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,50 @@ jobs:
274274
- name: Run Marimo Playwright Tests
275275
run: bash scripts/test_playwright_marimo.sh
276276

277+
TestJupyterLab:
278+
name: JupyterLab Playwright Tests
279+
needs: [BuildWheel]
280+
runs-on: depot-ubuntu-latest
281+
timeout-minutes: 5
282+
continue-on-error: true
283+
steps:
284+
- uses: actions/checkout@v4
285+
- name: Install uv
286+
uses: astral-sh/setup-uv@v7
287+
with:
288+
enable-cache: true
289+
python-version: "3.13"
290+
prune-cache: false
291+
- name: Setup pnpm
292+
uses: pnpm/action-setup@v4
293+
with:
294+
version: 9.10.0
295+
- name: Setup Node.js with pnpm cache
296+
uses: actions/setup-node@v4
297+
with:
298+
cache: 'pnpm'
299+
cache-dependency-path: 'packages/pnpm-lock.yaml'
300+
- name: Install pnpm dependencies
301+
working-directory: packages
302+
run: pnpm install --frozen-lockfile
303+
- name: Download build artifacts
304+
uses: actions/download-artifact@v4
305+
with:
306+
name: buckaroo-build
307+
- name: Install the project
308+
run: uv sync --all-extras --dev
309+
- name: Install built wheel and test dependencies
310+
run: |
311+
uv pip install --force-reinstall dist/*.whl
312+
uv pip install polars jupyterlab
313+
- name: Cache Playwright browsers
314+
uses: actions/cache@v4
315+
with:
316+
path: ~/.cache/ms-playwright
317+
key: playwright-${{ hashFiles('packages/buckaroo-js-core/package.json') }}
318+
- name: Run JupyterLab Playwright Tests
319+
run: bash scripts/test_playwright_jupyter.sh --venv-location=".venv"
320+
277321
TestWASMMarimo:
278322
name: WASM Marimo Playwright Tests
279323
needs: [BuildWheel]

buckaroo/dataflow/widget_extension_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def find_most_specific_styling(klasses, df_display_name='main'):
1313
1414
https://stackoverflow.com/questions/23660447/how-can-i-sort-a-list-of-python-classes-by-inheritance-depth
1515
"""
16-
styling_klasses = filter(lambda x: issubclass(x, DefaultMainStyling), klasses)
16+
styling_klasses = filter(lambda x: isinstance(x, type) and issubclass(x, DefaultMainStyling), klasses)
1717
klasses_by_depth = sorted(styling_klasses, key=lambda x: len(x.__mro__))
1818
return klasses_by_depth[-1]
1919

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"id": "cell-0",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"import pandas as pd\n",
11+
"import buckaroo\n",
12+
"\n",
13+
"# Create a predictable 200-row DataFrame\n",
14+
"# Each row has: index=i, int_col=i+10, str_col=f'foo_{i}'\n",
15+
"N = 200\n",
16+
"df = pd.DataFrame({\n",
17+
" 'int_col': [i + 10 for i in range(N)],\n",
18+
" 'str_col': [f'foo_{i}' for i in range(N)],\n",
19+
" 'float_col': [i * 1.5 for i in range(N)],\n",
20+
"})\n",
21+
"print(f'Created DataFrame with {len(df)} rows')\n",
22+
"df"
23+
]
24+
}
25+
],
26+
"metadata": {
27+
"kernelspec": {
28+
"display_name": "Python 3 (ipykernel)",
29+
"language": "python",
30+
"name": "python3"
31+
},
32+
"language_info": {
33+
"name": "python",
34+
"version": "3.11.0"
35+
}
36+
},
37+
"nbformat": 4,
38+
"nbformat_minor": 5
39+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"import buckaroo\n",
10+
"import pandas as pd\n",
11+
"from buckaroo import BuckarooInfiniteWidget\n",
12+
"\n",
13+
"# Create test data\n",
14+
"df = pd.DataFrame({\n",
15+
" 'name': ['Alice', 'Bob', 'Charlie'],\n",
16+
" 'age': [25, 30, 35],\n",
17+
" 'score': [85.5, 92.0, 78.3]\n",
18+
"})\n",
19+
"print(f\"✅ Created DataFrame with shape: {df.shape}\")\n",
20+
"\n",
21+
"# Display the widget\n",
22+
"widget = BuckarooInfiniteWidget(df)\n",
23+
"print(\"✅ BuckarooInfiniteWidget created successfully\")\n",
24+
"widget\n"
25+
]
26+
}
27+
],
28+
"metadata": {
29+
"language_info": {
30+
"name": "python"
31+
}
32+
},
33+
"nbformat": 4,
34+
"nbformat_minor": 2
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"import buckaroo\n",
10+
"import pandas as pd\n",
11+
"from buckaroo import BuckarooWidget\n",
12+
"\n",
13+
"# Create test data\n",
14+
"df = pd.DataFrame({\n",
15+
" 'name': ['Alice', 'Bob', 'Charlie'],\n",
16+
" 'age': [25, 30, 35],\n",
17+
" 'score': [85.5, 92.0, 78.3]\n",
18+
"})\n",
19+
"print(f\"✅ Created DataFrame with shape: {df.shape}\")\n",
20+
"\n",
21+
"# Display the widget\n",
22+
"widget = BuckarooWidget(df)\n",
23+
"print(\"✅ BuckarooWidget created successfully\")\n",
24+
"widget\n"
25+
]
26+
}
27+
],
28+
"metadata": {
29+
"language_info": {
30+
"name": "python"
31+
}
32+
},
33+
"nbformat": 4,
34+
"nbformat_minor": 2
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"import buckaroo\n",
10+
"import pandas as pd\n",
11+
"from buckaroo import DFViewer\n",
12+
"\n",
13+
"# Create test data\n",
14+
"df = pd.DataFrame({\n",
15+
" 'name': ['Alice', 'Bob', 'Charlie'],\n",
16+
" 'age': [25, 30, 35],\n",
17+
" 'score': [85.5, 92.0, 78.3]\n",
18+
"})\n",
19+
"print(f\"✅ Created DataFrame with shape: {df.shape}\")\n",
20+
"\n",
21+
"# Display the widget\n",
22+
"widget = DFViewer(df)\n",
23+
"print(\"✅ DFViewer created successfully\")\n",
24+
"widget\n"
25+
]
26+
}
27+
],
28+
"metadata": {
29+
"language_info": {
30+
"name": "python"
31+
}
32+
},
33+
"nbformat": 4,
34+
"nbformat_minor": 2
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"import buckaroo\n",
10+
"import pandas as pd\n",
11+
"from buckaroo.buckaroo_widget import DFViewerInfinite\n",
12+
"\n",
13+
"# Create test data\n",
14+
"df = pd.DataFrame({\n",
15+
" 'name': ['Alice', 'Bob', 'Charlie'],\n",
16+
" 'age': [25, 30, 35],\n",
17+
" 'score': [85.5, 92.0, 78.3]\n",
18+
"})\n",
19+
"print(f\"✅ Created DataFrame with shape: {df.shape}\")\n",
20+
"\n",
21+
"# Display the widget\n",
22+
"widget = DFViewerInfinite(df)\n",
23+
"print(\"✅ DFViewerInfinite created successfully\")\n",
24+
"widget\n"
25+
]
26+
}
27+
],
28+
"metadata": {
29+
"language_info": {
30+
"name": "python"
31+
}
32+
},
33+
"nbformat": 4,
34+
"nbformat_minor": 2
35+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"import buckaroo\n",
10+
"import polars as pl\n",
11+
"from buckaroo.polars_buckaroo import PolarsBuckarooInfiniteWidget\n",
12+
"\n",
13+
"# Create a large predictable DataFrame for testing infinite scroll\n",
14+
"# Row i has: int_col = i + 10, str_col = f\"foo_{i + 10}\"\n",
15+
"# 2000 rows ensures we need multiple data fetches to satisfy scroll requests\n",
16+
"N_ROWS = 2000\n",
17+
"df = pl.DataFrame({\n",
18+
" 'row_num': list(range(N_ROWS)),\n",
19+
" 'int_col': [i + 10 for i in range(N_ROWS)],\n",
20+
" 'str_col': [f'foo_{i + 10}' for i in range(N_ROWS)]\n",
21+
"})\n",
22+
"print(f\"✅ Created DataFrame with shape: {df.shape}\")\n",
23+
"print(f\" First row: row_num=0, int_col=10, str_col='foo_10'\")\n",
24+
"print(f\" Last row: row_num={N_ROWS-1}, int_col={N_ROWS-1+10}, str_col='foo_{N_ROWS-1+10}'\")\n",
25+
"\n",
26+
"# Display the widget with transcript recording enabled\n",
27+
"widget = PolarsBuckarooInfiniteWidget(df, record_transcript=True)\n",
28+
"print(\"✅ PolarsBuckarooInfiniteWidget created successfully with transcript recording enabled\")\n",
29+
"widget\n"
30+
]
31+
}
32+
],
33+
"metadata": {
34+
"language_info": {
35+
"name": "python"
36+
}
37+
},
38+
"nbformat": 4,
39+
"nbformat_minor": 2
40+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"import buckaroo\n",
10+
"import polars as pl\n",
11+
"from buckaroo.lazy_infinite_polars_widget import LazyInfinitePolarsBuckarooWidget\n",
12+
"\n",
13+
"# Create test data\n",
14+
"df = pl.DataFrame({\n",
15+
" 'name': ['Alice', 'Bob', 'Charlie'],\n",
16+
" 'age': [25, 30, 35],\n",
17+
" 'score': [85.5, 92.0, 78.3]\n",
18+
"})\n",
19+
"print(f\"✅ Created DataFrame with shape: {df.shape}\")\n",
20+
"\n",
21+
"# Display the widget (using lazy frame)\n",
22+
"ldf = df.lazy()\n",
23+
"widget = LazyInfinitePolarsBuckarooWidget(ldf)\n",
24+
"print(\"✅ LazyInfinitePolarsBuckarooWidget created successfully\")\n",
25+
"widget\n"
26+
]
27+
}
28+
],
29+
"metadata": {
30+
"language_info": {
31+
"name": "python"
32+
}
33+
},
34+
"nbformat": 4,
35+
"nbformat_minor": 2
36+
}

0 commit comments

Comments
 (0)