@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,281 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Copyright (c) Bloomberg Finance LP.
3
+ // Distributed under the terms of the Modified BSD License.
4
+
5
+ // This export the types (and only the types) from extension/token.ts
6
+ // Required for typedoc to be happy otherwise we could have used `from '@jupyterlab/extension/lib/token';`
7
+
8
+ import type { JupyterFrontEnd } from '@jupyterlab/application';
9
+ import type { IRouter } from '@jupyterlab/application';
10
+ import type {
11
+ Dialog,
12
+ Notification,
13
+ NotificationManager,
14
+ WidgetTracker
15
+ } from '@jupyterlab/apputils';
16
+ import type { IDocumentManager } from '@jupyterlab/docmanager';
17
+ import type { ISettingRegistry } from '@jupyterlab/settingregistry';
18
+
19
+ declare global {
20
+ // eslint-disable-next-line @typescript-eslint/naming-convention
21
+ interface Window {
22
+ /**
23
+ * Access Jupyter Application object
24
+ */
25
+ jupyterapp: JupyterFrontEnd;
26
+ /**
27
+ * Access to Galata In-Page helpers
28
+ *
29
+ * Those helpers are injected through a JupyterLab extension
30
+ */
31
+ galata: IGalataInpage;
32
+ /**
33
+ * Access to Galata In-Page helpers
34
+ *
35
+ * @deprecated since v4
36
+ * Those helpers are injected through a JupyterLab extension
37
+ */
38
+ galataip: IGalataInpage;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Static objects exposed.
44
+ */
45
+ export interface IGalataHelpers {
46
+ /**
47
+ * JupyterLab dialogs tracker.
48
+ */
49
+ readonly dialogs: WidgetTracker<Dialog<any>>;
50
+ /**
51
+ * JupyterLab notifications manager.
52
+ */
53
+ readonly notifications: NotificationManager;
54
+ }
55
+
56
+ /**
57
+ * Cell execution callbacks interface
58
+ */
59
+ export interface INotebookRunCallback {
60
+ /**
61
+ * Callback before scrolling to the cell
62
+ */
63
+ onBeforeScroll?: () => Promise<void>;
64
+ /**
65
+ * Callback after scrolling to the cell
66
+ */
67
+ onAfterScroll?: () => Promise<void>;
68
+ /**
69
+ * Callback after cell execution
70
+ */
71
+ onAfterCellRun?: (cellIndex: number) => Promise<void>;
72
+ }
73
+
74
+ /**
75
+ * waitForSelector options
76
+ */
77
+ export interface IWaitForSelectorOptions {
78
+ /**
79
+ * Test for the element to be hidden.
80
+ */
81
+ hidden?: boolean;
82
+ }
83
+
84
+ export interface IPluginNameToInterfaceMap {
85
+ '@jupyterlab/galata-extension:helpers': IGalataHelpers;
86
+ '@jupyterlab/application-extension:router': IRouter;
87
+ '@jupyterlab/docmanager-extension:manager': IDocumentManager;
88
+ '@jupyterlab/apputils-extension:settings': ISettingRegistry;
89
+ }
90
+
91
+ /**
92
+ * Galata In-Page interface
93
+ */
94
+ export interface IGalataInpage {
95
+ /**
96
+ * Delete all cells of the active notebook
97
+ */
98
+ deleteNotebookCells(): Promise<void>;
99
+
100
+ /**
101
+ * Get the index of a toolbar item
102
+ *
103
+ * @param itemName Item name
104
+ * @returns Index
105
+ */
106
+ getNotebookToolbarItemIndex(itemName: string): number;
107
+
108
+ /**
109
+ * Get an application plugin
110
+ *
111
+ * @param pluginId Plugin ID
112
+ * @returns Application plugin
113
+ */
114
+ getPlugin<K extends keyof IPluginNameToInterfaceMap>(
115
+ pluginId: K
116
+ ): Promise<IPluginNameToInterfaceMap[K] | undefined>;
117
+
118
+ /**
119
+ * Get the Jupyter notifications.
120
+ *
121
+ * @returns Jupyter Notifications
122
+ */
123
+ getNotifications(): Promise<Notification.INotification[]>;
124
+
125
+ /**
126
+ * Test if one or all cells have an execution number.
127
+ *
128
+ * @param cellIndex Cell index
129
+ * @returns Whether the cell was executed or not
130
+ *
131
+ * ### Notes
132
+ * It checks that no cells have a `null` execution count.
133
+ */
134
+ haveBeenExecuted(cellIndex?: number): boolean;
135
+
136
+ /**
137
+ * Test if a cell is selected in the active notebook
138
+ *
139
+ * @param cellIndex Cell index
140
+ * @returns Whether the cell is selected or not
141
+ */
142
+ isNotebookCellSelected(cellIndex: number): boolean;
143
+
144
+ /**
145
+ * Test if a element is visible or not
146
+ *
147
+ * @param el Element
148
+ * @returns Test result
149
+ */
150
+ isElementVisible(el: HTMLElement): boolean;
151
+
152
+ /**
153
+ * Disconnect a listener to new Jupyter dialog events.
154
+ *
155
+ * @param event Event type
156
+ * @param listener Event listener
157
+ */
158
+ off(event: 'dialog', listener: (dialog: Dialog<any> | null) => void): void;
159
+ /**
160
+ * Disconnect a listener to new or updated Jupyter notification events.
161
+ *
162
+ * @param event Event type
163
+ * @param listener Event listener
164
+ */
165
+ off(
166
+ event: 'notification',
167
+ listener: (notification: Notification.INotification) => void
168
+ ): void;
169
+
170
+ /**
171
+ * Connect a listener to new Jupyter dialog events.
172
+ *
173
+ * @param event Event type
174
+ * @param listener Event listener
175
+ */
176
+ on(event: 'dialog', listener: (dialog: Dialog<any> | null) => void): void;
177
+
178
+ /**
179
+ * Connect a listener to new or updated Jupyter notification events.
180
+ *
181
+ * @param event Event type
182
+ * @param listener Event listener
183
+ */
184
+ on(
185
+ event: 'notification',
186
+ listener: (notification: Notification.INotification) => void
187
+ ): void;
188
+
189
+ /**
190
+ * Connect a listener to the next new Jupyter dialog event.
191
+ *
192
+ * @param event Event type
193
+ * @param listener Event listener
194
+ */
195
+ once(event: 'dialog', listener: (dialog: Dialog<any> | null) => void): void;
196
+
197
+ /**
198
+ * Connect a listener to the next new or updated Jupyter notification event.
199
+ *
200
+ * @param event Event type
201
+ * @param listener Event listener
202
+ */
203
+ once(
204
+ event: 'notification',
205
+ listener: (notification: Notification.INotification) => void
206
+ ): void;
207
+
208
+ /**
209
+ * Save the active notebook
210
+ */
211
+ saveActiveNotebook(): Promise<void>;
212
+
213
+ /**
214
+ * Set the application theme
215
+ *
216
+ * @param themeName Theme name
217
+ */
218
+ setTheme(themeName: string): Promise<void>;
219
+
220
+ /**
221
+ * Reset execution count of one or all cells.
222
+ *
223
+ * @param cellIndex Cell index
224
+ */
225
+ resetExecutionCount(cellIndex?: number): void;
226
+
227
+ /**
228
+ * Run the active notebook cell by cell
229
+ * and execute the callback after each cell execution
230
+ *
231
+ * @param callback Callback
232
+ */
233
+ runActiveNotebookCellByCell(callback?: INotebookRunCallback): Promise<void>;
234
+
235
+ /**
236
+ * Wait for the route to be on path and close all documents
237
+ *
238
+ * @param path Path to monitor
239
+ */
240
+ waitForLaunch(path?: string): Promise<void>;
241
+
242
+ /**
243
+ * Wait for an element to be found from a CSS selector
244
+ *
245
+ * @param selector CSS selector
246
+ * @param node Element
247
+ * @param options Options
248
+ * @returns Selected element
249
+ */
250
+ waitForSelector(
251
+ selector: string,
252
+ node?: Element,
253
+ options?: IWaitForSelectorOptions
254
+ ): Promise<Node | null>;
255
+
256
+ /**
257
+ * Waits for the given `timeout` in milliseconds.
258
+ *
259
+ * @param timeout A timeout to wait for
260
+ */
261
+ waitForTimeout(duration: number): Promise<void>;
262
+
263
+ /**
264
+ * Wait for an element to be found from a XPath
265
+ *
266
+ * @param selector CSS selector
267
+ * @param node Element
268
+ * @param options Options
269
+ * @returns Selected element
270
+ */
271
+ waitForXPath(
272
+ selector: string,
273
+ node?: Element,
274
+ options?: IWaitForSelectorOptions
275
+ ): Promise<Node | null>;
276
+
277
+ /**
278
+ * Application object
279
+ */
280
+ readonly app: JupyterFrontEnd;
281
+ }
@@ -0,0 +1,387 @@
1
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
2
+ // Copyright (c) Jupyter Development Team.
3
+ // Distributed under the terms of the Modified BSD License.
4
+
5
+ import type { Session, TerminalAPI, User } from '@jupyterlab/services';
6
+ import {
7
+ test as base,
8
+ Page,
9
+ PlaywrightTestArgs,
10
+ PlaywrightTestOptions,
11
+ PlaywrightWorkerArgs,
12
+ PlaywrightWorkerOptions,
13
+ TestType
14
+ } from '@playwright/test';
15
+ import * as path from 'path';
16
+ import { ContentsHelper } from './contents';
17
+ import { galata } from './galata';
18
+ import { IJupyterLabPage, IJupyterLabPageFixture } from './jupyterlabpage';
19
+
20
+ /**
21
+ * Galata test arguments
22
+ */
23
+ export interface IGalataTestArgs extends PlaywrightTestArgs {
24
+ /**
25
+ * JupyterLab test page.
26
+ *
27
+ * It brings the following feature on top of Playwright Page object:
28
+ * - Goto to JupyterLab URL and wait for the application to be ready
29
+ * - Helpers for JupyterLab
30
+ * - Settings mock-up
31
+ * - State mock-up
32
+ * - Track sessions and terminals opened during a test to close them at the end
33
+ *
34
+ * Note: If autoGoto is true, the filebrowser will be set inside tmpPath.
35
+ * Nothing is preventing you to navigate to some other folders.
36
+ * So you must avoid creating files outside that directory to avoid
37
+ * coupling effects between tests.
38
+ *
39
+ */
40
+ page: IJupyterLabPageFixture;
41
+ }
42
+
43
+ /**
44
+ * Galata test configuration
45
+ */
46
+ export type GalataOptions = {
47
+ /**
48
+ * Application URL path fragment.
49
+ *
50
+ * Default: /lab
51
+ */
52
+ appPath: string;
53
+ /**
54
+ * Whether to go to JupyterLab page within the fixture or not.
55
+ *
56
+ * Default: true
57
+ */
58
+ autoGoto: boolean;
59
+ /**
60
+ * Mock JupyterLab config in-memory or not.
61
+ *
62
+ * Possible values are:
63
+ * - true (default): JupyterLab config will be mocked on a per test basis
64
+ * - false: JupyterLab config won't be mocked (Be careful it will write config in local files)
65
+ * - Record<string, JSONObject>: Initial JupyterLab data config - Mapping (config section, value).
66
+ *
67
+ * By default the config is stored in-memory.
68
+ */
69
+ mockConfig: boolean | Record<string, unknown>;
70
+ /**
71
+ * Mock JupyterLab state in-memory or not.
72
+ *
73
+ * Possible values are:
74
+ * - true (default): JupyterLab state will be mocked on a per test basis
75
+ * - false: JupyterLab state won't be mocked (Be careful it will write state in local files)
76
+ * - Record<string, unknown>: Initial JupyterLab data state - Mapping (state key, value).
77
+ *
78
+ * By default the state is stored in-memory.
79
+ */
80
+ mockState: boolean | Record<string, unknown>;
81
+ /**
82
+ * Mock JupyterLab settings in-memory or not.
83
+ *
84
+ * Possible values are:
85
+ * - true: JupyterLab settings will be mocked on a per test basis
86
+ * - false: JupyterLab settings won't be mocked (Be careful it will read & write settings local files)
87
+ * - Record<string, unknown>: Mapping {pluginId: settings} that will be default user settings
88
+ *
89
+ * The default value is `galata.DEFAULT_SETTINGS`
90
+ *
91
+ * By default the settings are stored in-memory. However the
92
+ * they are still initialized with the hard drive values.
93
+ */
94
+ mockSettings: boolean | Record<string, unknown>;
95
+ /**
96
+ * Mock JupyterLab user in-memory or not.
97
+ *
98
+ * Possible values are:
99
+ * - true (default): JupyterLab user will be mocked on a per test basis
100
+ * - false: JupyterLab user won't be mocked (It will be a random user so snapshots won't match)
101
+ * - Record<string, unknown>: Initial JupyterLab user - Mapping (user attribute, value).
102
+ *
103
+ * By default the user is stored in-memory.
104
+ */
105
+ mockUser: boolean | Partial<User.IUser>;
106
+ /**
107
+ * Galata can keep the uploaded and created files in ``tmpPath`` on
108
+ * the server root for debugging purpose. By default the files are
109
+ * always deleted
110
+ *
111
+ * - 'off' - ``tmpPath`` is deleted after each tests
112
+ * - 'on' - ``tmpPath`` is never deleted
113
+ * - 'only-on-failure' - ``tmpPath`` is deleted except if a test failed or timed out.
114
+ */
115
+ serverFiles: 'on' | 'off' | 'only-on-failure';
116
+ /**
117
+ * Sessions created during the test.
118
+ *
119
+ * Possible values are:
120
+ * - null: The sessions API won't be mocked
121
+ * - Map<string, Session.IModel>: The sessions created during a test.
122
+ *
123
+ * By default the sessions created during a test will be tracked and disposed at the end.
124
+ */
125
+ sessions: Map<string, Session.IModel> | null;
126
+ /**
127
+ * Terminals created during the test.
128
+ *
129
+ * Possible values are:
130
+ * - null: The Terminals API won't be mocked
131
+ * - Map<string, TerminalsAPI.IModel>: The Terminals created during a test.
132
+ *
133
+ * By default the Terminals created during a test will be tracked and disposed at the end.
134
+ */
135
+ terminals: Map<string, TerminalAPI.IModel> | null;
136
+ /**
137
+ * Unique test temporary path created on the server.
138
+ *
139
+ * Note: if you override this string, you will need to take care of creating the
140
+ * folder and cleaning it.
141
+ */
142
+ tmpPath: string;
143
+ /**
144
+ * Wait for the application page to be ready.
145
+ *
146
+ * @param page Playwright Page model
147
+ * @param helpers JupyterLab helpers
148
+ */
149
+ waitForApplication: (page: Page, helpers: IJupyterLabPage) => Promise<void>;
150
+ };
151
+
152
+ /**
153
+ * JupyterLab customized test.
154
+ */
155
+ // @ts-ignore
156
+ export const test: TestType<
157
+ IGalataTestArgs & GalataOptions & PlaywrightTestOptions,
158
+ PlaywrightWorkerArgs & PlaywrightWorkerOptions
159
+ > = base.extend<GalataOptions>({
160
+ /**
161
+ * `baseURL` used for all pages in the test. Takes priority over `contextOptions`.
162
+ * @see BrowserContextOptions
163
+ *
164
+ * It can also be set with `TARGET_URL` environment variable and default to `http://localhost:8888`.
165
+ */
166
+ baseURL: async ({ baseURL }, use) => {
167
+ await use(baseURL ?? process.env.TARGET_URL ?? 'http://localhost:8888');
168
+ },
169
+ /**
170
+ * Application URL path fragment.
171
+ *
172
+ * Default: /lab
173
+ */
174
+ appPath: ['/lab', { option: true }],
175
+ /**
176
+ * Whether to go to JupyterLab page within the fixture or not.
177
+ *
178
+ * Default: true.
179
+ *
180
+ * Note: Setting it to false allows to register new route mock-ups for example.
181
+ */
182
+ autoGoto: [true, { option: true }],
183
+ /**
184
+ * Mock JupyterLab config in-memory or not.
185
+ *
186
+ * Possible values are:
187
+ * - true (default): JupyterLab config will be mocked on a per test basis
188
+ * - false: JupyterLab config won't be mocked (Be careful it will write config in local files)
189
+ * - Record<string, JSONObject>: Initial JupyterLab data config - Mapping (config section, value).
190
+ *
191
+ * By default the config is stored in-memory.
192
+ */
193
+ mockConfig: [true, { option: true }],
194
+ /**
195
+ * Mock JupyterLab state in-memory or not.
196
+ *
197
+ * Possible values are:
198
+ * - true (default): JupyterLab state will be mocked on a per test basis
199
+ * - false: JupyterLab state won't be mocked (Be careful it will write state in local files)
200
+ * - Record<string, unknown>: Initial JupyterLab data state - Mapping (state key, value).
201
+ *
202
+ * By default the state is stored in-memory
203
+ */
204
+ mockState: [true, { option: true }],
205
+ /**
206
+ * Mock JupyterLab settings in-memory or not.
207
+ *
208
+ * Possible values are:
209
+ * - true: JupyterLab settings will be mocked on a per test basis
210
+ * - false: JupyterLab settings won't be mocked (Be careful it may write settings local files)
211
+ * - Record<string, unknown>: Mapping {pluginId: settings} that will be default user settings
212
+ *
213
+ * The default value is `galata.DEFAULT_SETTINGS`
214
+ *
215
+ * By default the settings are stored in-memory. However the
216
+ * they are still initialized with the hard drive values.
217
+ */
218
+ mockSettings: [galata.DEFAULT_SETTINGS, { option: true }],
219
+ /**
220
+ * Mock JupyterLab user in-memory or not.
221
+ *
222
+ * Possible values are:
223
+ * - true (default): JupyterLab user will be mocked on a per test basis
224
+ * - false: JupyterLab user won't be mocked (It will be a random user so snapshots won't match)
225
+ * - Record<string, unknown>: Initial JupyterLab user - Mapping (user attribute, value).
226
+ *
227
+ * By default the user is stored in-memory.
228
+ */
229
+ mockUser: [true, { option: true }],
230
+ /**
231
+ * Galata can keep the uploaded and created files in ``tmpPath`` on
232
+ * the server root for debugging purpose. By default the files are
233
+ * always deleted.
234
+ *
235
+ * - 'off' - ``tmpPath`` is deleted after each tests
236
+ * - 'on' - ``tmpPath`` is never deleted
237
+ * - 'only-on-failure' - ``tmpPath`` is deleted except if a test failed or timed out.
238
+ */
239
+ serverFiles: ['off', { option: true }],
240
+ /**
241
+ * Sessions created during the test.
242
+ *
243
+ * Possible values are:
244
+ * - null: The sessions API won't be mocked
245
+ * - Map<string, Session.IModel>: The sessions created during a test.
246
+ *
247
+ * By default the sessions created during a test will be tracked and disposed at the end.
248
+ */
249
+ sessions: async ({ request }, use) => {
250
+ const sessions = new Map<string, Session.IModel>();
251
+
252
+ await use(sessions);
253
+
254
+ if (sessions.size > 0) {
255
+ await galata.Mock.clearRunners(request, [...sessions.keys()], 'sessions');
256
+ }
257
+ },
258
+ /**
259
+ * Terminals created during the test.
260
+ *
261
+ * Possible values are:
262
+ * - null: The Terminals API won't be mocked
263
+ * - Map<string, TerminalAPI.IModel>: The Terminals created during a test.
264
+ *
265
+ * By default the Terminals created during a test will be tracked and disposed at the end.
266
+ */
267
+ terminals: async ({ request }, use) => {
268
+ const terminals = new Map<string, TerminalAPI.IModel>();
269
+
270
+ await use(terminals);
271
+
272
+ if (terminals.size > 0) {
273
+ await galata.Mock.clearRunners(
274
+ request,
275
+ [...terminals.keys()],
276
+ 'terminals'
277
+ );
278
+ }
279
+ },
280
+ /**
281
+ * Unique test temporary path created on the server.
282
+ *
283
+ * Note: if you override this string, you will need to take care of creating the
284
+ * folder and cleaning it.
285
+ */
286
+ tmpPath: async ({ request, serverFiles }, use, testInfo) => {
287
+ // Remove appended retry part for reproducibility
288
+ const testFolder = path
289
+ .basename(testInfo.outputDir)
290
+ .replace(/-retry\d+$/i, '');
291
+
292
+ const contents = new ContentsHelper(request);
293
+
294
+ if (await contents.directoryExists(testFolder)) {
295
+ await contents.deleteDirectory(testFolder);
296
+ }
297
+
298
+ // Create the test folder on the server
299
+ await contents.createDirectory(testFolder);
300
+
301
+ await use(testFolder);
302
+
303
+ // Delete the test folder on the server
304
+ // If serverFiles is 'on' or 'only-on-failure', keep the server files for the test
305
+ if (
306
+ serverFiles === 'off' ||
307
+ (serverFiles === 'only-on-failure' &&
308
+ (testInfo.status === 'passed' || testInfo.status === 'skipped'))
309
+ ) {
310
+ await contents.deleteDirectory(testFolder);
311
+ }
312
+ },
313
+ /**
314
+ * Wait for the application page to be ready
315
+ *
316
+ * @param page Playwright Page model
317
+ * @param helpers JupyterLab helpers
318
+ */
319
+ waitForApplication: async ({ baseURL }, use) => {
320
+ const waitIsReady = async (
321
+ page: Page,
322
+ helpers: IJupyterLabPage
323
+ ): Promise<void> => {
324
+ await page.waitForSelector('#jupyterlab-splash', {
325
+ state: 'detached'
326
+ });
327
+ await helpers.waitForCondition(() => {
328
+ return helpers.activity.isTabActive('Launcher');
329
+ });
330
+ // Oddly current tab is not always set to active
331
+ if (!(await helpers.isInSimpleMode())) {
332
+ await helpers.activity.activateTab('Launcher');
333
+ }
334
+ };
335
+ await use(waitIsReady);
336
+ },
337
+ /**
338
+ * JupyterLab test page.
339
+ *
340
+ * It brings the following feature on top of Playwright Page object:
341
+ * - Goto to JupyterLab URL and wait for the application to be ready (autoGoto == true)
342
+ * - Helpers for JupyterLab
343
+ * - Settings mock-up
344
+ * - State mock-up
345
+ * - Track sessions and terminals opened during a test to close them at the end
346
+ *
347
+ * Note: If autoGoto is true, the filebrowser will be set inside tmpPath.
348
+ * Nothing is preventing you to navigate to some other folders.
349
+ * So you must avoid creating files outside that directory to avoid
350
+ * coupling effects between tests.
351
+ */
352
+ // @ts-ignore
353
+ page: async (
354
+ {
355
+ appPath,
356
+ autoGoto,
357
+ baseURL,
358
+ mockConfig,
359
+ mockSettings,
360
+ mockState,
361
+ mockUser,
362
+ page,
363
+ sessions,
364
+ terminals,
365
+ tmpPath,
366
+ waitForApplication
367
+ },
368
+ use
369
+ ) => {
370
+ await use(
371
+ await galata.initTestPage(
372
+ appPath,
373
+ autoGoto,
374
+ baseURL!,
375
+ mockConfig,
376
+ mockSettings,
377
+ mockState,
378
+ mockUser,
379
+ page,
380
+ sessions,
381
+ terminals,
382
+ tmpPath,
383
+ waitForApplication
384
+ )
385
+ );
386
+ }
387
+ });