@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.
Files changed (100) hide show
  1. package/README.md +192 -31
  2. package/lib/benchmarkReporter.d.ts +1 -0
  3. package/lib/benchmarkReporter.js +34 -39
  4. package/lib/benchmarkReporter.js.map +1 -1
  5. package/lib/benchmarkVLTpl.js +19 -5
  6. package/lib/benchmarkVLTpl.js.map +1 -1
  7. package/lib/contents.d.ts +5 -5
  8. package/lib/contents.js +32 -36
  9. package/lib/contents.js.map +1 -1
  10. package/lib/extension/global.d.ts +197 -0
  11. package/lib/extension/global.js +601 -0
  12. package/lib/extension/global.js.map +1 -0
  13. package/lib/extension/index.d.ts +6 -0
  14. package/lib/extension/index.js +27 -0
  15. package/lib/extension/index.js.map +1 -0
  16. package/lib/extension/tokens.d.ts +232 -0
  17. package/lib/extension/tokens.js +13 -0
  18. package/lib/extension/tokens.js.map +1 -0
  19. package/lib/extension.d.ts +223 -0
  20. package/lib/{global.js → extension.js} +1 -2
  21. package/lib/extension.js.map +1 -0
  22. package/lib/fixtures.d.ts +32 -10
  23. package/lib/fixtures.js +64 -17
  24. package/lib/fixtures.js.map +1 -1
  25. package/lib/galata.d.ts +140 -19
  26. package/lib/galata.js +272 -87
  27. package/lib/galata.js.map +1 -1
  28. package/lib/helpers/activity.d.ts +6 -0
  29. package/lib/helpers/activity.js +19 -5
  30. package/lib/helpers/activity.js.map +1 -1
  31. package/lib/helpers/debuggerpanel.d.ts +4 -0
  32. package/lib/helpers/debuggerpanel.js +16 -0
  33. package/lib/helpers/debuggerpanel.js.map +1 -1
  34. package/lib/helpers/filebrowser.js +8 -2
  35. package/lib/helpers/filebrowser.js.map +1 -1
  36. package/lib/helpers/index.d.ts +1 -0
  37. package/lib/helpers/index.js +6 -1
  38. package/lib/helpers/index.js.map +1 -1
  39. package/lib/helpers/kernel.js +7 -7
  40. package/lib/helpers/kernel.js.map +1 -1
  41. package/lib/helpers/menu.d.ts +7 -0
  42. package/lib/helpers/menu.js +17 -1
  43. package/lib/helpers/menu.js.map +1 -1
  44. package/lib/helpers/notebook.d.ts +6 -4
  45. package/lib/helpers/notebook.js +127 -31
  46. package/lib/helpers/notebook.js.map +1 -1
  47. package/lib/helpers/sidebar.d.ts +8 -1
  48. package/lib/helpers/sidebar.js +33 -15
  49. package/lib/helpers/sidebar.js.map +1 -1
  50. package/lib/helpers/statusbar.js +1 -1
  51. package/lib/helpers/statusbar.js.map +1 -1
  52. package/lib/helpers/style.d.ts +42 -0
  53. package/lib/helpers/style.js +50 -0
  54. package/lib/helpers/style.js.map +1 -0
  55. package/lib/helpers/theme.js +1 -1
  56. package/lib/helpers/theme.js.map +1 -1
  57. package/lib/index.d.ts +5 -2
  58. package/lib/index.js +12 -3
  59. package/lib/index.js.map +1 -1
  60. package/lib/jupyterlabpage.d.ts +29 -4
  61. package/lib/jupyterlabpage.js +38 -22
  62. package/lib/jupyterlabpage.js.map +1 -1
  63. package/lib/playwright-config.js +5 -1
  64. package/lib/playwright-config.js.map +1 -1
  65. package/lib/utils.js +5 -1
  66. package/lib/utils.js.map +1 -1
  67. package/package.json +31 -47
  68. package/src/benchmarkReporter.ts +756 -0
  69. package/src/benchmarkVLTpl.ts +91 -0
  70. package/src/contents.ts +472 -0
  71. package/src/extension.ts +281 -0
  72. package/src/fixtures.ts +387 -0
  73. package/src/galata.ts +1035 -0
  74. package/src/helpers/activity.ts +115 -0
  75. package/src/helpers/debuggerpanel.ts +159 -0
  76. package/src/helpers/filebrowser.ts +228 -0
  77. package/src/helpers/index.ts +15 -0
  78. package/src/helpers/kernel.ts +39 -0
  79. package/src/helpers/logconsole.ts +32 -0
  80. package/src/helpers/menu.ts +228 -0
  81. package/src/helpers/notebook.ts +1217 -0
  82. package/src/helpers/performance.ts +57 -0
  83. package/src/helpers/sidebar.ts +289 -0
  84. package/src/helpers/statusbar.ts +56 -0
  85. package/src/helpers/style.ts +100 -0
  86. package/src/helpers/theme.ts +50 -0
  87. package/src/index.ts +19 -0
  88. package/src/jupyterlabpage.ts +704 -0
  89. package/src/playwright-config.ts +26 -0
  90. package/src/utils.ts +264 -0
  91. package/src/vega-statistics.d.ts +15 -0
  92. package/lib/global.d.ts +0 -23
  93. package/lib/global.js.map +0 -1
  94. package/lib/inpage/tokens.d.ts +0 -135
  95. package/lib/inpage/tokens.js +0 -9
  96. package/lib/inpage/tokens.js.map +0 -1
  97. package/lib/lib-inpage/inpage.js +0 -3957
  98. package/lib/lib-inpage/inpage.js.map +0 -1
  99. package/style/index.css +0 -10
  100. 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';