Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
26 changes: 24 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,33 @@ jobs:
name: Integration tests
needs: lite
runs-on: ubuntu-latest
permissions:
contents: read

env:
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/pw-browsers

steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Base Setup
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1

- name: Set up Docker Compose
uses: docker/setup-compose-action@v1
with:
cache-binary: true

- name: Checkout CKHub sharing service
uses: actions/checkout@v4
with:
repository: jupytereverywhere/sharing-service
path: ckhub-sharing-service
persist-credentials: false

- name: Download lite app (test mode)
uses: actions/download-artifact@v4
with:
Expand Down Expand Up @@ -252,9 +268,15 @@ jobs:
run: jlpm playwright install chromium
working-directory: ui-tests

- name: Execute integration tests
working-directory: ui-tests
- name: Start CKHub sharing service and execute integration tests
run: |
echo "::group::Starting CKHub sharing service"
cd ckhub-sharing-service
docker compose up --build --detach
cd ..
echo "::endgroup::"

cd ui-tests
jlpm playwright test --browser chromium

- name: Upload Playwright Test report
Expand Down
113 changes: 113 additions & 0 deletions ui-tests/tests/sharing-service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { test, expect, Page } from '@playwright/test';
import type { JupyterLab } from '@jupyterlab/application';
import type { JSONObject } from '@lumino/coreutils';

declare global {
interface Window {
jupyterapp: JupyterLab;
}
}

async function runCommand(page: Page, command: string, args: JSONObject = {}) {
await page.evaluate(

Check failure on line 12 in ui-tests/tests/sharing-service.spec.ts

View workflow job for this annotation

GitHub Actions / Integration tests

[chromium] › tests/sharing-service.spec.ts:95:7 › A functional test for the sharing service › Perform round-trip with sharing service

1) [chromium] › tests/sharing-service.spec.ts:95:7 › A functional test for the sharing service › Perform round-trip with sharing service Error: page.evaluate: Test timeout of 60000ms exceeded. 10 | 11 | async function runCommand(page: Page, command: string, args: JSONObject = {}) { > 12 | await page.evaluate( | ^ 13 | async ({ command, args }) => { 14 | await window.jupyterapp.commands.execute(command, args); 15 | }, at runCommand (/home/runner/work/jupyterlite-extension/jupyterlite-extension/ui-tests/tests/sharing-service.spec.ts:12:14) at /home/runner/work/jupyterlite-extension/jupyterlite-extension/ui-tests/tests/sharing-service.spec.ts:98:11
async ({ command, args }) => {
await window.jupyterapp.commands.execute(command, args);
},
{ command, args }
);
}

const TEST_NOTEBOOK = {
cells: [
{
cell_type: 'code',
execution_count: null,
id: 'test-cell-1',
outputs: [],
metadata: {},
source: ['print("Hello from CKHub shared notebook!")']
},
{
cell_type: 'markdown',
id: 'test-cell-2',
metadata: {},
source: ['# Test Markdown Cell\n\nThis is a test notebook for sharing.']
}
],
metadata: {
kernelspec: {
display_name: 'Python 3 (ipykernel)',
language: 'python',
name: 'python3'
},
language_info: {
name: 'python',
version: '3.8.0'
}
},
nbformat: 4,
nbformat_minor: 5
};

async function createTestNotebook(page: Page): Promise<void> {
await page.evaluate(notebookContent => {
const { serviceManager } = window.jupyterapp;
return serviceManager.contents.save('test-notebook.ipynb', {
type: 'notebook',
format: 'json',
content: notebookContent
});
}, TEST_NOTEBOOK);
}

async function openTestNotebook(page: Page): Promise<void> {
await runCommand(page, 'docmanager:open', { path: 'test-notebook.ipynb' });
}

async function extractShareUrlFromDialog(page: Page): Promise<string> {
const shareUrlElement = await page.waitForSelector('.je-share-link', { timeout: 10000 });
const shareUrl = await shareUrlElement.textContent();

if (!shareUrl) {
throw new Error('Share URL not found in dialog');
}

return shareUrl.trim();
}

async function getCellContent(page: Page, cellIndex: number = 0): Promise<string> {
return await page.evaluate(index => {
const cells = document.querySelectorAll('.jp-Cell');
const cell = cells[index];
if (!cell) return '';

const content = cell.querySelector('.cm-content');
return content?.textContent || '';
}, cellIndex);
}

test.beforeEach(async ({ page }) => {
await page.goto('lab/index.html');
await page.waitForSelector('.jp-LabShell');
});

test.describe('A functional test for the sharing service', () => {
test('Perform round-trip with sharing service', async ({ page, context }) => {
await createTestNotebook(page);
await openTestNotebook(page);
await runCommand(page, 'jupytereverywhere:share-notebook');

const shareUrl = await extractShareUrlFromDialog(page);

const sharedPage = await context.newPage();
await sharedPage.goto(shareUrl);
await sharedPage.waitForSelector('.jp-LabShell');

await runCommand(sharedPage, 'jupytereverywhere:create-copy-notebook');

// Wait for view-only header to disappear
await expect(sharedPage.locator('.je-ViewOnlyHeader')).toBeHidden({ timeout: 10000 });

await sharedPage.close();
});
});
Loading