@jupyterlab/galata 5.0.0-alpha.2 → 5.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -31
- package/lib/benchmarkReporter.d.ts +1 -0
- package/lib/benchmarkReporter.js +34 -39
- package/lib/benchmarkReporter.js.map +1 -1
- package/lib/benchmarkVLTpl.js +19 -5
- package/lib/benchmarkVLTpl.js.map +1 -1
- package/lib/contents.d.ts +5 -5
- package/lib/contents.js +32 -36
- package/lib/contents.js.map +1 -1
- package/lib/extension/global.d.ts +197 -0
- package/lib/extension/global.js +601 -0
- package/lib/extension/global.js.map +1 -0
- package/lib/extension/index.d.ts +6 -0
- package/lib/extension/index.js +27 -0
- package/lib/extension/index.js.map +1 -0
- package/lib/extension/tokens.d.ts +232 -0
- package/lib/extension/tokens.js +13 -0
- package/lib/extension/tokens.js.map +1 -0
- package/lib/extension.d.ts +223 -0
- package/lib/{global.js → extension.js} +1 -2
- package/lib/extension.js.map +1 -0
- package/lib/fixtures.d.ts +32 -10
- package/lib/fixtures.js +64 -17
- package/lib/fixtures.js.map +1 -1
- package/lib/galata.d.ts +140 -19
- package/lib/galata.js +272 -87
- package/lib/galata.js.map +1 -1
- package/lib/helpers/activity.d.ts +6 -0
- package/lib/helpers/activity.js +19 -5
- package/lib/helpers/activity.js.map +1 -1
- package/lib/helpers/debuggerpanel.d.ts +4 -0
- package/lib/helpers/debuggerpanel.js +16 -0
- package/lib/helpers/debuggerpanel.js.map +1 -1
- package/lib/helpers/filebrowser.js +8 -2
- package/lib/helpers/filebrowser.js.map +1 -1
- package/lib/helpers/index.d.ts +1 -0
- package/lib/helpers/index.js +6 -1
- package/lib/helpers/index.js.map +1 -1
- package/lib/helpers/kernel.js +7 -7
- package/lib/helpers/kernel.js.map +1 -1
- package/lib/helpers/menu.d.ts +7 -0
- package/lib/helpers/menu.js +17 -1
- package/lib/helpers/menu.js.map +1 -1
- package/lib/helpers/notebook.d.ts +6 -4
- package/lib/helpers/notebook.js +127 -31
- package/lib/helpers/notebook.js.map +1 -1
- package/lib/helpers/sidebar.d.ts +8 -1
- package/lib/helpers/sidebar.js +33 -15
- package/lib/helpers/sidebar.js.map +1 -1
- package/lib/helpers/statusbar.js +1 -1
- package/lib/helpers/statusbar.js.map +1 -1
- package/lib/helpers/style.d.ts +42 -0
- package/lib/helpers/style.js +50 -0
- package/lib/helpers/style.js.map +1 -0
- package/lib/helpers/theme.js +1 -1
- package/lib/helpers/theme.js.map +1 -1
- package/lib/index.d.ts +5 -2
- package/lib/index.js +12 -3
- package/lib/index.js.map +1 -1
- package/lib/jupyterlabpage.d.ts +29 -4
- package/lib/jupyterlabpage.js +38 -22
- package/lib/jupyterlabpage.js.map +1 -1
- package/lib/playwright-config.js +5 -1
- package/lib/playwright-config.js.map +1 -1
- package/lib/utils.js +5 -1
- package/lib/utils.js.map +1 -1
- package/package.json +31 -47
- package/src/benchmarkReporter.ts +756 -0
- package/src/benchmarkVLTpl.ts +91 -0
- package/src/contents.ts +472 -0
- package/src/extension.ts +281 -0
- package/src/fixtures.ts +387 -0
- package/src/galata.ts +1035 -0
- package/src/helpers/activity.ts +115 -0
- package/src/helpers/debuggerpanel.ts +159 -0
- package/src/helpers/filebrowser.ts +228 -0
- package/src/helpers/index.ts +15 -0
- package/src/helpers/kernel.ts +39 -0
- package/src/helpers/logconsole.ts +32 -0
- package/src/helpers/menu.ts +228 -0
- package/src/helpers/notebook.ts +1217 -0
- package/src/helpers/performance.ts +57 -0
- package/src/helpers/sidebar.ts +289 -0
- package/src/helpers/statusbar.ts +56 -0
- package/src/helpers/style.ts +100 -0
- package/src/helpers/theme.ts +50 -0
- package/src/index.ts +19 -0
- package/src/jupyterlabpage.ts +704 -0
- package/src/playwright-config.ts +26 -0
- package/src/utils.ts +264 -0
- package/src/vega-statistics.d.ts +15 -0
- package/lib/global.d.ts +0 -23
- package/lib/global.js.map +0 -1
- package/lib/inpage/tokens.d.ts +0 -135
- package/lib/inpage/tokens.js +0 -9
- package/lib/inpage/tokens.js.map +0 -1
- package/lib/lib-inpage/inpage.js +0 -3957
- package/lib/lib-inpage/inpage.js.map +0 -1
- package/style/index.css +0 -10
- package/style/index.js +0 -10
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { ElementHandle, Page } from '@playwright/test';
|
|
5
|
+
import * as Utils from '../utils';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Activity helper
|
|
9
|
+
*/
|
|
10
|
+
export class ActivityHelper {
|
|
11
|
+
constructor(readonly page: Page) {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* JupyterLab launcher selector
|
|
15
|
+
*/
|
|
16
|
+
get launcherSelector(): string {
|
|
17
|
+
return Utils.xpBuildActivityTabSelector('Launcher');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Close all widgets in the main area
|
|
22
|
+
*/
|
|
23
|
+
async closeAll(): Promise<void> {
|
|
24
|
+
await this.page.evaluate(async (launcherSelector: string) => {
|
|
25
|
+
await window.jupyterapp.commands.execute('application:close-all');
|
|
26
|
+
await window.galata.waitForXPath(launcherSelector);
|
|
27
|
+
}, this.launcherSelector);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Whether a tab is active or not
|
|
32
|
+
*
|
|
33
|
+
* @param name Activity name
|
|
34
|
+
* @returns Active status
|
|
35
|
+
*/
|
|
36
|
+
async isTabActive(name: string): Promise<boolean> {
|
|
37
|
+
const tab = await this.getTab(name);
|
|
38
|
+
return (
|
|
39
|
+
(tab &&
|
|
40
|
+
(await tab.evaluate((tab: Element) =>
|
|
41
|
+
tab.classList.contains('lm-mod-current')
|
|
42
|
+
))) ??
|
|
43
|
+
false
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get a handle on a tab
|
|
49
|
+
*
|
|
50
|
+
* @param name Activity name
|
|
51
|
+
* @returns Handle on the tab or null if the tab is not found
|
|
52
|
+
*/
|
|
53
|
+
getTab(name?: string): Promise<ElementHandle<Element> | null> {
|
|
54
|
+
const page = this.page;
|
|
55
|
+
const tabSelector = name
|
|
56
|
+
? Utils.xpBuildActivityTabSelector(name)
|
|
57
|
+
: Utils.xpBuildActiveActivityTabSelector();
|
|
58
|
+
return page.$(`xpath=${tabSelector}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get a handle on a panel
|
|
63
|
+
*
|
|
64
|
+
* @param name Activity name
|
|
65
|
+
* @returns Handle on the tab or null if the tab is not found
|
|
66
|
+
*/
|
|
67
|
+
async getPanel(name?: string): Promise<ElementHandle<Element> | null> {
|
|
68
|
+
const page = this.page;
|
|
69
|
+
const tab = await this.getTab(name);
|
|
70
|
+
if (tab) {
|
|
71
|
+
const id = await tab.evaluate((tab: Element) =>
|
|
72
|
+
tab.getAttribute('data-id')
|
|
73
|
+
);
|
|
74
|
+
return await page.$(`xpath=${Utils.xpBuildActivityPanelSelector(id!)}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Close a panel from its tab name
|
|
82
|
+
*
|
|
83
|
+
* @param name Activity name
|
|
84
|
+
*/
|
|
85
|
+
async closePanel(name: string): Promise<void> {
|
|
86
|
+
await this.activateTab(name);
|
|
87
|
+
await this.page.evaluate(async (launcherSelector: string) => {
|
|
88
|
+
await window.jupyterapp.commands.execute('application:close');
|
|
89
|
+
await window.galata.waitForXPath(launcherSelector);
|
|
90
|
+
}, this.launcherSelector);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Activate a tab is active
|
|
95
|
+
*
|
|
96
|
+
* @param name Activity name
|
|
97
|
+
* @returns Whether the action is successful
|
|
98
|
+
*/
|
|
99
|
+
async activateTab(name: string): Promise<boolean> {
|
|
100
|
+
const tab = await this.getTab(name);
|
|
101
|
+
if (tab) {
|
|
102
|
+
await tab.click();
|
|
103
|
+
await this.page.waitForFunction(
|
|
104
|
+
({ tab }) => {
|
|
105
|
+
return tab.classList.contains('jp-mod-current');
|
|
106
|
+
},
|
|
107
|
+
{ tab }
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ElementHandle, Page } from '@playwright/test';
|
|
7
|
+
import { SidebarHelper } from './sidebar';
|
|
8
|
+
import { NotebookHelper } from './notebook';
|
|
9
|
+
import { waitForCondition } from '../utils';
|
|
10
|
+
|
|
11
|
+
const DEBUGGER_ITEM = 'debugger-icon';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Debugger Helper
|
|
15
|
+
*/
|
|
16
|
+
export class DebuggerHelper {
|
|
17
|
+
constructor(
|
|
18
|
+
readonly page: Page,
|
|
19
|
+
readonly sidebar: SidebarHelper,
|
|
20
|
+
readonly notebook: NotebookHelper
|
|
21
|
+
) {}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns true if debugger toolbar item is enabled, false otherwise
|
|
25
|
+
*/
|
|
26
|
+
async isOn(): Promise<boolean> {
|
|
27
|
+
if (!(await this.notebook.isAnyActive())) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const item = await this.notebook.getToolbarItem(DEBUGGER_ITEM);
|
|
31
|
+
if (item) {
|
|
32
|
+
const button = await item.$('button');
|
|
33
|
+
if (button) {
|
|
34
|
+
return (await button.getAttribute('aria-pressed')) === 'true';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Enables the debugger toolbar item
|
|
42
|
+
*/
|
|
43
|
+
async switchOn(): Promise<void> {
|
|
44
|
+
await waitForCondition(async () => {
|
|
45
|
+
const item = await this.notebook.getToolbarItem(DEBUGGER_ITEM);
|
|
46
|
+
if (item) {
|
|
47
|
+
const button = await item.$('button');
|
|
48
|
+
if (button) {
|
|
49
|
+
return (await button.getAttribute('aria-disabled')) !== 'true';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}, 2000);
|
|
54
|
+
if (!(await this.isOn())) {
|
|
55
|
+
await this.notebook.clickToolbarItem(DEBUGGER_ITEM);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Disables the debugger toolbar item
|
|
61
|
+
*/
|
|
62
|
+
async switchOff(): Promise<void> {
|
|
63
|
+
if (await this.isOn()) {
|
|
64
|
+
await this.notebook.clickToolbarItem(DEBUGGER_ITEM);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Returns true if debugger panel is open, false otherwise
|
|
70
|
+
*/
|
|
71
|
+
async isOpen(): Promise<boolean> {
|
|
72
|
+
return await this.sidebar.isTabOpen('jp-debugger-sidebar');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns handle to the variables panel content
|
|
77
|
+
*/
|
|
78
|
+
async getVariablesPanel(): Promise<ElementHandle<Element> | null> {
|
|
79
|
+
return this._getPanel('.jp-DebuggerVariables');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Waits for variables to be populated in the variables panel
|
|
84
|
+
*/
|
|
85
|
+
async waitForVariables(): Promise<void> {
|
|
86
|
+
await this.page.waitForSelector('.jp-DebuggerVariables-body ul');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* render variable
|
|
91
|
+
*/
|
|
92
|
+
async renderVariable(name: string): Promise<void> {
|
|
93
|
+
await this.page
|
|
94
|
+
.locator(`.jp-DebuggerVariables :text("${name}")`)
|
|
95
|
+
.click({ button: 'right' });
|
|
96
|
+
await this.page
|
|
97
|
+
.locator('.lm-Menu-itemLabel:text("Render Variable")')
|
|
98
|
+
.click();
|
|
99
|
+
await this.page.waitForSelector('.jp-VariableRendererPanel-renderer');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Returns handle to callstack panel content
|
|
104
|
+
*/
|
|
105
|
+
async getCallStackPanel(): Promise<ElementHandle<Element> | null> {
|
|
106
|
+
return this._getPanel('.jp-DebuggerCallstack');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Waits for the callstack body to populate in the callstack panel
|
|
111
|
+
*/
|
|
112
|
+
async waitForCallStack(): Promise<void> {
|
|
113
|
+
await this.page.waitForSelector(
|
|
114
|
+
'.jp-DebuggerCallstack-body >> .jp-DebuggerCallstackFrame'
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Returns handle to breakpoints panel content
|
|
120
|
+
*/
|
|
121
|
+
async getBreakPointsPanel(): Promise<ElementHandle<Element> | null> {
|
|
122
|
+
return this._getPanel('.jp-DebuggerBreakpoints');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Waits for the breakpoints to appear in the breakpoints panel
|
|
127
|
+
*/
|
|
128
|
+
async waitForBreakPoints(): Promise<void> {
|
|
129
|
+
await this.page.waitForSelector(
|
|
130
|
+
'.jp-DebuggerBreakpoints >> .jp-DebuggerBreakpoint'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Returns handle to sources panel content
|
|
136
|
+
*/
|
|
137
|
+
async getSourcePanel(): Promise<ElementHandle<Element> | null> {
|
|
138
|
+
return this._getPanel('.jp-DebuggerSources');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Waits for sources to be populated in the sources panel
|
|
143
|
+
*/
|
|
144
|
+
async waitForSources(): Promise<void> {
|
|
145
|
+
await this.page.waitForSelector('.jp-DebuggerSources-body >> .jp-Editor', {
|
|
146
|
+
state: 'visible'
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private async _getPanel(
|
|
151
|
+
selector: string
|
|
152
|
+
): Promise<ElementHandle<Element> | null> {
|
|
153
|
+
const panel = await this.sidebar.getContentPanel('right');
|
|
154
|
+
if (panel) {
|
|
155
|
+
return panel.$(selector);
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { Page } from '@playwright/test';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { ContentsHelper } from '../contents';
|
|
7
|
+
import * as Utils from '../utils';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* File Browser Helpers
|
|
11
|
+
*/
|
|
12
|
+
export class FileBrowserHelper {
|
|
13
|
+
constructor(readonly page: Page, readonly contents: ContentsHelper) {}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create the selector for a file in the file browser
|
|
17
|
+
*
|
|
18
|
+
* @param fileName File name
|
|
19
|
+
* @returns XPath to file in file browser
|
|
20
|
+
*/
|
|
21
|
+
xpBuildFileSelector(fileName: string): string {
|
|
22
|
+
return `//div[@id='filebrowser']//li[./span[${Utils.xpContainsClass(
|
|
23
|
+
'jp-DirListing-itemText'
|
|
24
|
+
)} and ./span[text()="${fileName}"]]]`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create the selector for a directory in the file browser
|
|
29
|
+
*
|
|
30
|
+
* @param dirName Directory name
|
|
31
|
+
* @returns XPath to directory in file browser
|
|
32
|
+
*/
|
|
33
|
+
xpBuildDirectorySelector(dirName: string): string {
|
|
34
|
+
return `//div[@id='filebrowser']//li[@data-isdir='true' and ./span[${Utils.xpContainsClass(
|
|
35
|
+
'jp-DirListing-itemText'
|
|
36
|
+
)} and ./span[text()="${dirName}"]]]`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Reveal a file in the file browser.
|
|
41
|
+
*
|
|
42
|
+
* It will open intermediate folders if needed.
|
|
43
|
+
*
|
|
44
|
+
* @param filePath File path
|
|
45
|
+
*/
|
|
46
|
+
async revealFileInBrowser(filePath: string): Promise<void> {
|
|
47
|
+
const pos = filePath.lastIndexOf('/');
|
|
48
|
+
const fileName = path.basename(filePath);
|
|
49
|
+
if (pos >= 0) {
|
|
50
|
+
const dirPath = filePath.substring(0, pos);
|
|
51
|
+
await this.openDirectory(dirPath);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
await Utils.waitForCondition(async () => {
|
|
55
|
+
return await this.isFileListedInBrowser(fileName);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Whether the file is listed in the file browser or not.
|
|
61
|
+
*
|
|
62
|
+
* @param fileName File name
|
|
63
|
+
* @returns File status
|
|
64
|
+
*/
|
|
65
|
+
async isFileListedInBrowser(fileName: string): Promise<boolean> {
|
|
66
|
+
const item = await this.page.$(
|
|
67
|
+
`xpath=${this.xpBuildFileSelector(fileName)}`
|
|
68
|
+
);
|
|
69
|
+
return item !== null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the full path of the currently opened directory
|
|
74
|
+
*
|
|
75
|
+
* @returns Directory full path
|
|
76
|
+
*/
|
|
77
|
+
async getCurrentDirectory(): Promise<string> {
|
|
78
|
+
return await this.page.evaluate(() => {
|
|
79
|
+
let directory = '';
|
|
80
|
+
const spans = document.querySelectorAll(
|
|
81
|
+
'.jp-FileBrowser .jp-FileBrowser-crumbs span'
|
|
82
|
+
);
|
|
83
|
+
const numSpans = spans.length;
|
|
84
|
+
if (numSpans > 1) {
|
|
85
|
+
directory = spans[numSpans - 2].getAttribute('title') ?? '';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return directory;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Open a file
|
|
94
|
+
*
|
|
95
|
+
* Note: This will double click on the file;
|
|
96
|
+
* an editor needs to be available for the given file type.
|
|
97
|
+
*
|
|
98
|
+
* @param filePath Notebook path
|
|
99
|
+
* @returns Action success status
|
|
100
|
+
*/
|
|
101
|
+
async open(filePath: string): Promise<boolean> {
|
|
102
|
+
await this.revealFileInBrowser(filePath);
|
|
103
|
+
const name = path.basename(filePath);
|
|
104
|
+
|
|
105
|
+
const fileItem = await this.page.$(
|
|
106
|
+
`xpath=${this.xpBuildFileSelector(name)}`
|
|
107
|
+
);
|
|
108
|
+
if (fileItem) {
|
|
109
|
+
await fileItem.click({ clickCount: 2 });
|
|
110
|
+
await this.page.waitForSelector(Utils.xpBuildActivityTabSelector(name), {
|
|
111
|
+
state: 'visible'
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Open the Home directory.
|
|
122
|
+
*
|
|
123
|
+
* @returns Action success status
|
|
124
|
+
*/
|
|
125
|
+
async openHomeDirectory(): Promise<boolean> {
|
|
126
|
+
const homeButton = await this.page.$(
|
|
127
|
+
'.jp-FileBrowser .jp-FileBrowser-crumbs span'
|
|
128
|
+
);
|
|
129
|
+
if (!homeButton) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
await homeButton.click();
|
|
133
|
+
|
|
134
|
+
await this.page.waitForFunction(() => {
|
|
135
|
+
const spans = document.querySelectorAll(
|
|
136
|
+
'.jp-FileBrowser .jp-FileBrowser-crumbs span'
|
|
137
|
+
);
|
|
138
|
+
return (
|
|
139
|
+
// The home is the root if no preferred dir is defined.
|
|
140
|
+
spans.length === 2 && spans[0].classList.contains('jp-BreadCrumbs-home')
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// wait for DOM rerender
|
|
145
|
+
await this.page.waitForTimeout(200);
|
|
146
|
+
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Open a given directory in the file browser
|
|
152
|
+
*
|
|
153
|
+
* @param dirPath Directory path
|
|
154
|
+
* @returns Action success status
|
|
155
|
+
*/
|
|
156
|
+
async openDirectory(dirPath: string): Promise<boolean> {
|
|
157
|
+
if (!(await this.openHomeDirectory())) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const directories = dirPath.split('/');
|
|
162
|
+
let path = '';
|
|
163
|
+
|
|
164
|
+
for (const directory of directories) {
|
|
165
|
+
if (directory.trim() === '') {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (path !== '') {
|
|
169
|
+
path += '/';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
path += directory;
|
|
173
|
+
|
|
174
|
+
if (!(await this._openDirectory(directory))) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
await Utils.waitForCondition(async () => {
|
|
179
|
+
return (await this.getCurrentDirectory()) === path;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Trigger a file browser refresh
|
|
188
|
+
*/
|
|
189
|
+
async refresh(): Promise<void> {
|
|
190
|
+
const page = this.page;
|
|
191
|
+
const item = await page.$(
|
|
192
|
+
`xpath=//div[@id='filebrowser']//button[${Utils.xpContainsClass(
|
|
193
|
+
'jp-ToolbarButtonComponent'
|
|
194
|
+
)} and .//*[@data-icon='ui-components:refresh']]`
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
if (item) {
|
|
198
|
+
// wait for network response or timeout
|
|
199
|
+
await Promise.race([
|
|
200
|
+
page.waitForTimeout(2000),
|
|
201
|
+
this.contents.waitForAPIResponse(async () => {
|
|
202
|
+
await item.click();
|
|
203
|
+
})
|
|
204
|
+
]);
|
|
205
|
+
// wait for DOM rerender
|
|
206
|
+
await page.waitForTimeout(200);
|
|
207
|
+
} else {
|
|
208
|
+
throw new Error('Could not find refresh toolbar item');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
protected async _openDirectory(dirName: string): Promise<boolean> {
|
|
213
|
+
const item = await this.page.$(
|
|
214
|
+
`xpath=${this.xpBuildDirectorySelector(dirName)}`
|
|
215
|
+
);
|
|
216
|
+
if (item === null) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
await this.contents.waitForAPIResponse(async () => {
|
|
221
|
+
await item.click({ clickCount: 2 });
|
|
222
|
+
});
|
|
223
|
+
// wait for DOM rerender
|
|
224
|
+
await this.page.waitForTimeout(200);
|
|
225
|
+
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
export * from './activity';
|
|
5
|
+
export * from './filebrowser';
|
|
6
|
+
export * from './kernel';
|
|
7
|
+
export * from './logconsole';
|
|
8
|
+
export * from './menu';
|
|
9
|
+
export * from './notebook';
|
|
10
|
+
export * from './performance';
|
|
11
|
+
export * from './sidebar';
|
|
12
|
+
export * from './statusbar';
|
|
13
|
+
export * from './style';
|
|
14
|
+
export * from './theme';
|
|
15
|
+
export * from './debuggerpanel';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { Page } from '@playwright/test';
|
|
5
|
+
import * as Utils from '../utils';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Kernels and sessions helpers
|
|
9
|
+
*
|
|
10
|
+
* These helpers are using JupyterLab serviceManager in Javascript. There
|
|
11
|
+
* are therefore not available if the page is not loaded.
|
|
12
|
+
*/
|
|
13
|
+
export class KernelHelper {
|
|
14
|
+
constructor(readonly page: Page) {}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Whether a sessions is running or not.
|
|
18
|
+
*
|
|
19
|
+
* @returns Running status
|
|
20
|
+
*/
|
|
21
|
+
async isAnyRunning(): Promise<boolean> {
|
|
22
|
+
return await this.page.evaluate(() => {
|
|
23
|
+
return !window.jupyterapp.serviceManager.sessions.running().next().done;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Shutdown all sessions.
|
|
29
|
+
*/
|
|
30
|
+
async shutdownAll(): Promise<void> {
|
|
31
|
+
await this.page.evaluate(async () => {
|
|
32
|
+
await window.jupyterapp.serviceManager.sessions.shutdownAll();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await Utils.waitForCondition(async () => {
|
|
36
|
+
return (await this.isAnyRunning()) === false;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { Page } from '@playwright/test';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* LogConsole helpers
|
|
8
|
+
*/
|
|
9
|
+
export class LogConsoleHelper {
|
|
10
|
+
constructor(readonly page: Page) {}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get the number of log messages in the log console panel.
|
|
14
|
+
*
|
|
15
|
+
* @returns Number of log messages
|
|
16
|
+
*/
|
|
17
|
+
async logCount(): Promise<number> {
|
|
18
|
+
return await this.page.evaluate(() => {
|
|
19
|
+
let count = 0;
|
|
20
|
+
const logPanels = document.querySelectorAll(
|
|
21
|
+
'.jp-LogConsolePanel .lm-StackedPanel-child'
|
|
22
|
+
);
|
|
23
|
+
logPanels.forEach(logPanel => {
|
|
24
|
+
if (!logPanel.classList.contains('lm-mod-hidden')) {
|
|
25
|
+
count += logPanel.querySelectorAll('.jp-OutputArea-child').length;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return count;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|