@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,57 @@
|
|
|
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 { UUID } from '@lumino/coreutils';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Performance helper
|
|
9
|
+
*/
|
|
10
|
+
export class PerformanceHelper {
|
|
11
|
+
constructor(readonly page: Page) {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Clear all measures and place a mark
|
|
15
|
+
*
|
|
16
|
+
* @param name Mark
|
|
17
|
+
*/
|
|
18
|
+
startTimer(name: string = 'start'): Promise<void> {
|
|
19
|
+
return this.page.evaluate(`{
|
|
20
|
+
performance.clearMeasures();
|
|
21
|
+
performance.mark('${name}');
|
|
22
|
+
}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the duration since the mark has been created
|
|
27
|
+
*
|
|
28
|
+
* @param startMark Mark
|
|
29
|
+
* @param name Measure
|
|
30
|
+
* @returns Measure value
|
|
31
|
+
*/
|
|
32
|
+
async endTimer(
|
|
33
|
+
startMark: string = 'start',
|
|
34
|
+
name: string = 'duration'
|
|
35
|
+
): Promise<number> {
|
|
36
|
+
await this.page.evaluate(`performance.measure('${name}', '${startMark}')`);
|
|
37
|
+
const time: number = await this.page.evaluate(
|
|
38
|
+
`performance.getEntriesByName('${name}')[0].duration`
|
|
39
|
+
);
|
|
40
|
+
return time;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Measure the time to execute a function using web browser performance API.
|
|
45
|
+
*
|
|
46
|
+
* @param fn Function to measure
|
|
47
|
+
* @returns The duration to execute the function
|
|
48
|
+
*/
|
|
49
|
+
async measure(fn: () => Promise<void>): Promise<number> {
|
|
50
|
+
const mark = UUID.uuid4();
|
|
51
|
+
await this.startTimer(mark);
|
|
52
|
+
|
|
53
|
+
await fn();
|
|
54
|
+
|
|
55
|
+
return this.endTimer(mark);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import type { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
5
|
+
import { ElementHandle, Page } from '@playwright/test';
|
|
6
|
+
import type { IPluginNameToInterfaceMap } from '../extension';
|
|
7
|
+
import { galata } from '../galata';
|
|
8
|
+
import * as Utils from '../utils';
|
|
9
|
+
import { MenuHelper } from './menu';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Sidebar helpers
|
|
13
|
+
*/
|
|
14
|
+
export class SidebarHelper {
|
|
15
|
+
constructor(readonly page: Page, readonly menu: MenuHelper) {}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Whether a sidebar is opened or not
|
|
19
|
+
*
|
|
20
|
+
* @param side Sidebar side
|
|
21
|
+
* @returns Opened status
|
|
22
|
+
*/
|
|
23
|
+
isOpen = async (side: galata.SidebarPosition = 'left'): Promise<boolean> => {
|
|
24
|
+
return (await this.getContentPanel(side)) !== null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Whether a given tab is opened or not
|
|
29
|
+
*
|
|
30
|
+
* @param id Tab id
|
|
31
|
+
* @returns Tab opened status
|
|
32
|
+
*/
|
|
33
|
+
async isTabOpen(id: galata.SidebarTabId): Promise<boolean> {
|
|
34
|
+
const tabButton = await this.page.$(
|
|
35
|
+
`${this.buildTabSelector(id)}.lm-mod-current`
|
|
36
|
+
);
|
|
37
|
+
return tabButton !== null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the position of a given tab
|
|
42
|
+
*
|
|
43
|
+
* @param id Tab id
|
|
44
|
+
* @returns Tab position
|
|
45
|
+
*/
|
|
46
|
+
getTabPosition = async (
|
|
47
|
+
id: galata.SidebarTabId
|
|
48
|
+
): Promise<galata.SidebarPosition | null> => {
|
|
49
|
+
return await this.page.evaluate(
|
|
50
|
+
async ({ tabSelector }) => {
|
|
51
|
+
const tabButton = document.querySelector(tabSelector);
|
|
52
|
+
if (!tabButton) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const sideBar = tabButton.closest('.jp-SideBar');
|
|
57
|
+
if (!sideBar) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return sideBar.classList.contains('jp-mod-right') ? 'right' : 'left';
|
|
62
|
+
},
|
|
63
|
+
{ tabSelector: this.buildTabSelector(id) }
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Move a given tab to left side
|
|
69
|
+
*
|
|
70
|
+
* @param id Tab id
|
|
71
|
+
*/
|
|
72
|
+
async moveTabToLeft(id: galata.SidebarTabId): Promise<void> {
|
|
73
|
+
await this.setTabPosition(id, 'left');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Move a given tab to the right side
|
|
78
|
+
*
|
|
79
|
+
* @param id Tab id
|
|
80
|
+
*/
|
|
81
|
+
async moveTabToRight(id: galata.SidebarTabId): Promise<void> {
|
|
82
|
+
await this.setTabPosition(id, 'right');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Set the position of a given tab
|
|
87
|
+
*
|
|
88
|
+
* @param id Tab id
|
|
89
|
+
* @param side Sidebar side
|
|
90
|
+
*/
|
|
91
|
+
async setTabPosition(
|
|
92
|
+
id: galata.SidebarTabId,
|
|
93
|
+
side: galata.SidebarPosition
|
|
94
|
+
): Promise<void> {
|
|
95
|
+
const position = await this.getTabPosition(id);
|
|
96
|
+
|
|
97
|
+
if (position === side) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await this.toggleTabPosition(id);
|
|
102
|
+
|
|
103
|
+
await Utils.waitForCondition(async () => {
|
|
104
|
+
return (await this.getTabPosition(id)) === side;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Toggle a given tab position
|
|
110
|
+
*
|
|
111
|
+
* @param id Tab id
|
|
112
|
+
*/
|
|
113
|
+
async toggleTabPosition(id: galata.SidebarTabId): Promise<void> {
|
|
114
|
+
const tab = await this.getTab(id);
|
|
115
|
+
|
|
116
|
+
if (!tab) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await tab.click({ button: 'right' });
|
|
121
|
+
|
|
122
|
+
const switchMenuItem = await this.page.waitForSelector(
|
|
123
|
+
'.lm-Menu-content .lm-Menu-item[data-command="sidebar:switch"]',
|
|
124
|
+
{ state: 'visible' }
|
|
125
|
+
);
|
|
126
|
+
if (switchMenuItem) {
|
|
127
|
+
await switchMenuItem.click();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Move all tabs to the left side
|
|
133
|
+
*/
|
|
134
|
+
async moveAllTabsToLeft(): Promise<void> {
|
|
135
|
+
await this.page.evaluate(
|
|
136
|
+
async ({ pluginId }) => {
|
|
137
|
+
const settingRegistry = (await window.galata.getPlugin(
|
|
138
|
+
pluginId
|
|
139
|
+
)) as ISettingRegistry;
|
|
140
|
+
const SHELL_ID = '@jupyterlab/application-extension:shell';
|
|
141
|
+
const sidebars = {
|
|
142
|
+
Debugger: 'left',
|
|
143
|
+
'Property Inspector': 'left',
|
|
144
|
+
'Extension Manager': 'left',
|
|
145
|
+
'File Browser': 'left',
|
|
146
|
+
'Sessions and Tabs': 'left',
|
|
147
|
+
'Table of Contents': 'left'
|
|
148
|
+
};
|
|
149
|
+
const currentLayout = (await settingRegistry.get(
|
|
150
|
+
SHELL_ID,
|
|
151
|
+
'layout'
|
|
152
|
+
)) as any;
|
|
153
|
+
await settingRegistry.set(SHELL_ID, 'layout', {
|
|
154
|
+
single: { ...currentLayout.single, ...sidebars },
|
|
155
|
+
multiple: { ...currentLayout.multiple, ...sidebars }
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
pluginId:
|
|
160
|
+
'@jupyterlab/apputils-extension:settings' as keyof IPluginNameToInterfaceMap
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
await this.page.waitForFunction(() => {
|
|
165
|
+
const rightStack = document.getElementById('jp-right-stack');
|
|
166
|
+
return rightStack?.childElementCount === 0;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get the handle on a given tab
|
|
172
|
+
*
|
|
173
|
+
* @param id Tab id
|
|
174
|
+
* @returns Tab handle
|
|
175
|
+
*/
|
|
176
|
+
async getTab(
|
|
177
|
+
id: galata.SidebarTabId
|
|
178
|
+
): Promise<ElementHandle<Element> | null> {
|
|
179
|
+
return await this.page.$(this.buildTabSelector(id));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Open a given tab
|
|
184
|
+
*
|
|
185
|
+
* @param id Tab id
|
|
186
|
+
*/
|
|
187
|
+
async openTab(id: galata.SidebarTabId): Promise<void> {
|
|
188
|
+
const isOpen = await this.isTabOpen(id);
|
|
189
|
+
if (isOpen) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const tabButton = await this.page.$(this.buildTabSelector(id));
|
|
194
|
+
if (tabButton === null) {
|
|
195
|
+
throw new Error(`Unable to find the tab ${id} button`);
|
|
196
|
+
}
|
|
197
|
+
await tabButton.click();
|
|
198
|
+
await this._waitForTabActivate(tabButton);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get the handle on a sidebar content panel
|
|
203
|
+
*
|
|
204
|
+
* @param side Position
|
|
205
|
+
* @returns Panel handle
|
|
206
|
+
*/
|
|
207
|
+
async getContentPanel(
|
|
208
|
+
side: galata.SidebarPosition = 'left'
|
|
209
|
+
): Promise<ElementHandle<Element> | null> {
|
|
210
|
+
return await this.page.$(
|
|
211
|
+
`#jp-${side}-stack .lm-StackedPanel-child:not(.lm-mod-hidden)`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get the tab bar of the sidebar
|
|
217
|
+
*
|
|
218
|
+
* @param side Position
|
|
219
|
+
* @returns Tab bar handle
|
|
220
|
+
*/
|
|
221
|
+
async getTabBar(
|
|
222
|
+
side: galata.SidebarPosition = 'left'
|
|
223
|
+
): Promise<ElementHandle<Element> | null> {
|
|
224
|
+
return await this.page.$(`.jp-SideBar.jp-mod-${side}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Open a given sidebar
|
|
229
|
+
*
|
|
230
|
+
* @param side Position
|
|
231
|
+
*/
|
|
232
|
+
async open(side: galata.SidebarPosition = 'left'): Promise<void> {
|
|
233
|
+
const isOpen = await this.isOpen(side);
|
|
234
|
+
if (isOpen) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
await this.menu.clickMenuItem(
|
|
239
|
+
`View>Appearance>Show ${side === 'left' ? 'Left' : 'Right'} Sidebar`
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
await Utils.waitForCondition(async () => {
|
|
243
|
+
return await this.isOpen(side);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Close a given sidebar
|
|
249
|
+
*
|
|
250
|
+
* @param side Position
|
|
251
|
+
*/
|
|
252
|
+
async close(side: galata.SidebarPosition = 'left'): Promise<void> {
|
|
253
|
+
const isOpen = await this.isOpen(side);
|
|
254
|
+
if (!isOpen) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
await this.menu.clickMenuItem(
|
|
259
|
+
`View>Appearance>Show ${side === 'left' ? 'Left' : 'Right'} Sidebar`
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
await Utils.waitForCondition(async () => {
|
|
263
|
+
return !(await this.isOpen(side));
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Get the selector for a given tab
|
|
269
|
+
*
|
|
270
|
+
* @param id Tab id
|
|
271
|
+
* @returns Selector
|
|
272
|
+
*/
|
|
273
|
+
buildTabSelector(id: galata.SidebarTabId): string {
|
|
274
|
+
return `.lm-TabBar.jp-SideBar .lm-TabBar-content li.lm-TabBar-tab[data-id="${id}"]`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
protected async _waitForTabActivate(
|
|
278
|
+
tab: ElementHandle<Element>,
|
|
279
|
+
activate = true
|
|
280
|
+
): Promise<void> {
|
|
281
|
+
await this.page.waitForFunction(
|
|
282
|
+
({ tab, activate }) => {
|
|
283
|
+
const current = tab.classList.contains('lm-mod-current');
|
|
284
|
+
return activate ? current : !current;
|
|
285
|
+
},
|
|
286
|
+
{ tab, activate }
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
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 { MenuHelper } from './menu';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Status Bar helpers
|
|
9
|
+
*/
|
|
10
|
+
export class StatusBarHelper {
|
|
11
|
+
constructor(readonly page: Page, readonly menu: MenuHelper) {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Whether the status bar is visible or not
|
|
15
|
+
*
|
|
16
|
+
* @returns Visibility status
|
|
17
|
+
*/
|
|
18
|
+
async isVisible(): Promise<boolean> {
|
|
19
|
+
return await this.page.evaluate(() => {
|
|
20
|
+
const statusBar = document.querySelector(
|
|
21
|
+
'#jp-main-statusbar'
|
|
22
|
+
) as HTMLElement;
|
|
23
|
+
return window.galata.isElementVisible(statusBar);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Show the status bar
|
|
29
|
+
*/
|
|
30
|
+
async show(): Promise<void> {
|
|
31
|
+
const visible = await this.isVisible();
|
|
32
|
+
if (visible) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await this.menu.clickMenuItem('View>Show Status Bar');
|
|
37
|
+
await this.page.waitForSelector('#jp-main-statusbar', {
|
|
38
|
+
state: 'visible'
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Hide the status bar
|
|
44
|
+
*/
|
|
45
|
+
async hide(): Promise<void> {
|
|
46
|
+
const visible = await this.isVisible();
|
|
47
|
+
if (!visible) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await this.menu.clickMenuItem('View>Show Status Bar');
|
|
52
|
+
await this.page.waitForSelector('#jp-main-statusbar', {
|
|
53
|
+
state: 'hidden'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
interface IUnusedStyleCheckOptions {
|
|
7
|
+
/**
|
|
8
|
+
* List of rule fragments to match rules for runtime checks.
|
|
9
|
+
*/
|
|
10
|
+
fragments: string[];
|
|
11
|
+
/**
|
|
12
|
+
* List of fragments to filter out rules which cannot (easily) be checked at runtime.
|
|
13
|
+
*/
|
|
14
|
+
exclude?: string[];
|
|
15
|
+
/**
|
|
16
|
+
* Whether to check rules with pseudo-classes.
|
|
17
|
+
*
|
|
18
|
+
* Default: false.
|
|
19
|
+
*/
|
|
20
|
+
includePseudoClasses?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Whether to include rules matching mod classes (`jp-mod-` and `.lm-mod-`).
|
|
23
|
+
*
|
|
24
|
+
* Default: false.
|
|
25
|
+
*/
|
|
26
|
+
includeModifiers?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* CSS Style analysis helpers
|
|
31
|
+
*/
|
|
32
|
+
export class StyleHelper {
|
|
33
|
+
constructor(readonly page: Page) {}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Collect all CSS selectors on page.
|
|
37
|
+
*/
|
|
38
|
+
async collectAllSelectors(): Promise<string[]> {
|
|
39
|
+
return this.page.evaluate(() =>
|
|
40
|
+
[...document.querySelectorAll('style')]
|
|
41
|
+
.filter(style => style.sheet !== null)
|
|
42
|
+
.map(style => [...style.sheet!.cssRules])
|
|
43
|
+
.flat()
|
|
44
|
+
.filter((rule: CSSRule) => rule instanceof CSSStyleRule)
|
|
45
|
+
.map((rule: CSSStyleRule) => rule.selectorText)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Find unused CSS rules.
|
|
51
|
+
*
|
|
52
|
+
* @param options spcify which rules to include/exclude.
|
|
53
|
+
* @returns List of rules with no matching elements on the page.
|
|
54
|
+
*/
|
|
55
|
+
async findUnusedStyleRules(
|
|
56
|
+
options: IUnusedStyleCheckOptions
|
|
57
|
+
): Promise<string[]> {
|
|
58
|
+
let exclude = typeof options.exclude !== 'undefined' ? options.exclude : [];
|
|
59
|
+
if (!options.includeModifiers) {
|
|
60
|
+
exclude = [...exclude, ...['.jp-mod-', '.lm-mod-']];
|
|
61
|
+
}
|
|
62
|
+
const relevantRules = (await this.collectAllSelectors())
|
|
63
|
+
// detection of pseudo-elements with `document.querySelector` is impossible,
|
|
64
|
+
// so we just check their parents
|
|
65
|
+
.map(selector =>
|
|
66
|
+
selector.replace(
|
|
67
|
+
/::?(after|before|backdrop|cue|cue-region|first-letter|first-line|file-selector-button|marker|placeholder|selection)/,
|
|
68
|
+
''
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
.filter(selector =>
|
|
72
|
+
options.includePseudoClasses ? true : !selector.match(/:\w+/)
|
|
73
|
+
)
|
|
74
|
+
.filter(selector =>
|
|
75
|
+
options.fragments.some(fragment => selector.includes(fragment))
|
|
76
|
+
)
|
|
77
|
+
.filter(
|
|
78
|
+
selector => !exclude.some(fragment => selector.includes(fragment))
|
|
79
|
+
);
|
|
80
|
+
const potentiallyUnusedRules = await this.page.evaluate(
|
|
81
|
+
relevantRules =>
|
|
82
|
+
relevantRules.filter(
|
|
83
|
+
selector => document.querySelector(selector) == null
|
|
84
|
+
),
|
|
85
|
+
relevantRules
|
|
86
|
+
);
|
|
87
|
+
if (potentiallyUnusedRules.length !== 0) {
|
|
88
|
+
console.log(
|
|
89
|
+
potentiallyUnusedRules.length,
|
|
90
|
+
'out of',
|
|
91
|
+
relevantRules.length,
|
|
92
|
+
'CSS rules for',
|
|
93
|
+
options.fragments,
|
|
94
|
+
'may be unused:',
|
|
95
|
+
potentiallyUnusedRules
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return potentiallyUnusedRules;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
* Theme helpers
|
|
8
|
+
*/
|
|
9
|
+
export class ThemeHelper {
|
|
10
|
+
constructor(readonly page: Page) {}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Set JupyterLab theme to Dark
|
|
14
|
+
*/
|
|
15
|
+
async setDarkTheme(): Promise<void> {
|
|
16
|
+
await this.setTheme('JupyterLab Dark');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Set JupyterLab theme to Light
|
|
21
|
+
*/
|
|
22
|
+
async setLightTheme(): Promise<void> {
|
|
23
|
+
await this.setTheme('JupyterLab Light');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get JupyterLab theme name
|
|
28
|
+
*
|
|
29
|
+
* @returns Theme name
|
|
30
|
+
*/
|
|
31
|
+
async getTheme(): Promise<string> {
|
|
32
|
+
return await this.page.evaluate(() => {
|
|
33
|
+
return document.body.dataset.jpThemeName as string;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Set JupyterLab theme
|
|
39
|
+
*
|
|
40
|
+
* @param themeName Theme name
|
|
41
|
+
*/
|
|
42
|
+
async setTheme(themeName: string): Promise<void> {
|
|
43
|
+
const page = this.page;
|
|
44
|
+
await page.evaluate(async (themeName: string) => {
|
|
45
|
+
await window.galata.setTheme(themeName);
|
|
46
|
+
}, themeName);
|
|
47
|
+
|
|
48
|
+
await page.waitForSelector('#jupyterlab-splash', { state: 'detached' });
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Copyright (c) Bloomberg Finance LP.
|
|
3
|
+
// Distributed under the terms of the Modified BSD License.
|
|
4
|
+
/**
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
* @module galata
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Export expect from playwright to simplify the import in tests
|
|
11
|
+
*/
|
|
12
|
+
export { expect } from '@playwright/test';
|
|
13
|
+
|
|
14
|
+
export * from './extension';
|
|
15
|
+
|
|
16
|
+
export * from './benchmarkReporter';
|
|
17
|
+
export * from './galata';
|
|
18
|
+
export * from './fixtures';
|
|
19
|
+
export * from './jupyterlabpage';
|