Skip to content

Commit 9802fd3

Browse files
Fix timeline feature error when base url is set (jupyterlab#402)
* fixed error with base url * modified filename extraction method. * ui test timeline * ui test config done. * ui test for baseurl issue * ui test for baseurl * pre-commit * test info * modified script for ui tests * removed unused cnd * clarify condition
1 parent dd23608 commit 9802fd3

File tree

7 files changed

+93
-11
lines changed

7 files changed

+93
-11
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ jobs:
295295
working-directory: ui-tests
296296
run: |
297297
jlpm test
298+
TIMELINE_FEATURE=1 jlpm test:timeline
298299
- name: Upload Playwright Test report
299300
if: always()
300301
uses: actions/upload-artifact@v3

packages/docprovider-extension/src/filebrowser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ export const statusBarTimeline: JupyterFrontEndPlugin<void> = {
182182
fullPath,
183183
provider,
184184
provider.contentType,
185-
provider.format
185+
provider.format,
186+
DOCUMENT_TIMELINE_URL
186187
);
187188

188189
const elt = document.getElementById('jp-slider-status-bar');

packages/docprovider/src/TimelineSlider.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,21 @@ export class TimelineWidget extends ReactWidget {
1313
private provider: IForkProvider;
1414
private contentType: string;
1515
private format: string;
16+
private documentTimelineUrl: string;
1617

1718
constructor(
1819
apiURL: string,
1920
provider: IForkProvider,
2021
contentType: string,
21-
format: string
22+
format: string,
23+
documentTimelineUrl: string
2224
) {
2325
super();
2426
this.apiURL = apiURL;
2527
this.provider = provider;
2628
this.contentType = contentType;
2729
this.format = format;
30+
this.documentTimelineUrl = documentTimelineUrl;
2831
this.addClass('jp-timelineSliderWrapper');
2932
}
3033

@@ -36,6 +39,7 @@ export class TimelineWidget extends ReactWidget {
3639
provider={this.provider}
3740
contentType={this.contentType}
3841
format={this.format}
42+
documentTimelineUrl={this.documentTimelineUrl}
3943
/>
4044
);
4145
}

packages/docprovider/src/component.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ type Props = {
1919
provider: IForkProvider;
2020
contentType: string;
2121
format: string;
22+
documentTimelineUrl: string;
2223
};
2324

2425
export const TimelineSliderComponent: React.FC<Props> = ({
2526
apiURL,
2627
provider,
2728
contentType,
28-
format
29+
format,
30+
documentTimelineUrl
2931
}) => {
3032
const [data, setData] = useState({
3133
roomId: '',
@@ -147,16 +149,21 @@ export const TimelineSliderComponent: React.FC<Props> = ({
147149
function determineAction(currentTimestamp: number): 'undo' | 'redo' {
148150
return currentTimestamp < currentTimestampIndex ? 'undo' : 'redo';
149151
}
150-
151152
function extractFilenameFromURL(url: string): string {
152153
try {
153154
const parsedURL = new URL(url);
154155
const pathname = parsedURL.pathname;
155-
const segments = pathname.split('/');
156156

157-
return segments.slice(4 - segments.length).join('/');
157+
const apiIndex = pathname.lastIndexOf(documentTimelineUrl);
158+
if (apiIndex === -1) {
159+
throw new Error(
160+
`API segment "${documentTimelineUrl}" not found in URL.`
161+
);
162+
}
163+
164+
return pathname.slice(apiIndex + documentTimelineUrl.length);
158165
} catch (error) {
159-
console.error('Invalid URL:', error);
166+
console.error('Invalid URL or unable to extract filename:', error);
160167
return '';
161168
}
162169
}

ui-tests/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
"private": true,
66
"scripts": {
77
"start": "jupyter lab --config jupyter_server_test_config.py",
8-
"test": "npx playwright test",
8+
"start:timeline": "jupyter lab --config jupyter_server_test_config.py --ServerApp.base_url=/api/collaboration/timeline/",
9+
"test": "npx playwright test --config=playwright.config.js",
10+
"test:timeline": "npx playwright test --config=playwright.timeline.config.js",
911
"test:update": "npx playwright test --update-snapshots"
1012
},
1113
"devDependencies": {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (c) Jupyter Development Team.
3+
* Distributed under the terms of the Modified BSD License.
4+
*/
5+
6+
/**
7+
* Configuration for Playwright using default from @jupyterlab/galata
8+
*/
9+
const baseConfig = require('@jupyterlab/galata/lib/playwright-config');
10+
11+
module.exports = {
12+
...baseConfig,
13+
workers: 1,
14+
webServer: {
15+
command: 'jlpm start:timeline',
16+
url: 'http://localhost:8888/api/collaboration/timeline/lab',
17+
timeout: 120 * 1000,
18+
reuseExistingServer: !process.env.CI
19+
},
20+
expect: {
21+
toMatchSnapshot: {
22+
maxDiffPixelRatio: 0.01
23+
}
24+
},
25+
projects: [
26+
{
27+
name: 'timeline-tests',
28+
testMatch: 'tests/**/timeline-*.spec.ts',
29+
testIgnore: '**/.ipynb_checkpoints/**',
30+
timeout: 120 * 1000
31+
}
32+
]
33+
};

ui-tests/tests/timeline-slider.spec.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,51 @@ async function openNotebook(page: Page, notebookPath: string) {
2323
await page.waitForSelector('.jp-Notebook', { state: 'visible' });
2424
}
2525

26-
test.describe('Open from Path', () => {
2726

28-
test('should fail if there are console errors', async ({ page, tmpPath }) => {
27+
const isTimelineEnv = process.env.TIMELINE_FEATURE || "0";
28+
const isTimeline = parseInt(isTimelineEnv)
29+
30+
test.describe('Timeline Slider', () => {
31+
32+
if (isTimeline) {
33+
test.use({ autoGoto: false });
34+
}
35+
test('should fail if there are console errors when opening from path', async ({ page, tmpPath }) => {
36+
if (isTimeline) {
37+
console.log('Skipping this test.');
38+
return;
39+
}
2940
const pageErrors = await capturePageErrors(page);
3041

3142
await page.notebook.createNew();
32-
await page.notebook.save();
3343
await page.notebook.close();
3444

3545
await openNotebook(page, `${tmpPath}/Untitled.ipynb`);
3646

3747
expect(pageErrors).toHaveLength(0);
3848
});
49+
50+
test('should display in status bar without console errors when baseUrl is set', async ({ page, baseURL }) => {
51+
52+
if (!isTimeline) {
53+
console.log('Skipping this test.');
54+
return;
55+
}
56+
57+
await page.goto('http://localhost:8888/api/collaboration/timeline')
58+
59+
const pageErrors = await capturePageErrors(page);
60+
61+
await page.notebook.createNew();
62+
63+
const historyIcon = page.locator('.jp-mod-highlighted[title="Document Timeline"]');
64+
await expect(historyIcon).toBeVisible();
65+
66+
await historyIcon.click();
67+
68+
const slider = page.locator('.jp-timestampDisplay');
69+
await expect(slider).toBeVisible();
70+
71+
expect(pageErrors).toHaveLength(0);
72+
});
3973
});

0 commit comments

Comments
 (0)