@@ -3,59 +3,86 @@ import path from 'path';
33import type { JupyterLab } from '@jupyterlab/application' ;
44import type { JSONObject } from '@lumino/coreutils' ;
55
6- import { SharingService } from '../../src/sharing-service' ;
7-
86declare global {
97 interface Window {
108 jupyterapp : JupyterLab ;
119 }
1210}
1311
14- declare global {
15- interface Window {
16- sharingService ?: SharingService ;
17- }
18- }
19-
2012async function runCommand ( page : Page , command : string , args : JSONObject = { } ) {
2113 await page . evaluate (
2214 async ( { command, args } ) => {
23- window . jupyterapp . commands . execute ( command , args ) ;
15+ await window . jupyterapp . commands . execute ( command , args ) ;
2416 } ,
2517 { command, args }
2618 ) ;
2719}
2820
29- async function dismissKernelSelectDialog ( page : Page ) {
30- const kernelDialogHeader = page . locator ( '.jp-Dialog .jp-Dialog-header' , {
31- hasText : 'Select Kernel'
21+ const TEST_NOTEBOOK = {
22+ cells : [
23+ {
24+ cell_type : 'code' ,
25+ execution_count : null ,
26+ id : '55eb9a2d-401d-4abd-b0eb-373ded5b408d' ,
27+ outputs : [ ] ,
28+ metadata : { } ,
29+ source : [ `# This is a test notebook` ]
30+ }
31+ ] ,
32+ metadata : {
33+ kernelspec : {
34+ display_name : 'Python 3 (ipykernel)' ,
35+ language : 'python' ,
36+ name : 'python3'
37+ } ,
38+ language_info : {
39+ codemirror_mode : {
40+ name : 'ipython' ,
41+ version : 3
42+ } ,
43+ file_extension : '.py' ,
44+ mimetype : 'text/x-python' ,
45+ name : 'python' ,
46+ nbconvert_exporter : 'python' ,
47+ pygments_lexer : 'ipython3'
48+ }
49+ } ,
50+ nbformat : 4 ,
51+ nbformat_minor : 5
52+ } ;
53+
54+ async function mockTokenRoute ( page : Page ) {
55+ await page . route ( '**/api/v1/auth/issue' , async route => {
56+ const json = { token : 'test-token' } ;
57+ await route . fulfill ( { json } ) ;
3258 } ) ;
59+ }
3360
34- if ( ( await kernelDialogHeader . count ( ) ) === 0 ) {
35- return ;
36- }
37-
38- const selectButtonLabel = kernelDialogHeader . locator (
39- 'xpath=../../..//div[@aria-label="Select Kernel"]'
40- ) ;
41-
42- if ( await selectButtonLabel . count ( ) ) {
43- await selectButtonLabel . first ( ) . click ( ) ;
44- }
61+ async function mockGetSharedNotebook ( page : Page , notebookId : string ) {
62+ await page . route ( '**/api/v1/notebooks/*' , async route => {
63+ const json = {
64+ id : notebookId ,
65+ domain_id : 'domain' ,
66+ readable_id : null ,
67+ content : TEST_NOTEBOOK
68+ } ;
69+ await route . fulfill ( { json } ) ;
70+ } ) ;
4571}
4672
47- async function getSharedNotebookID ( page : Page ) {
48- return new URL ( page . url ( ) ) . searchParams . get ( 'notebook' ) ;
73+ async function mockShareNotebookResponse ( page : Page , notebookId : string ) {
74+ await page . route ( '**/api/v1/notebooks' , async route => {
75+ const json = {
76+ message : 'Shared!' ,
77+ notebook : { id : notebookId , readable_id : null }
78+ } ;
79+ await route . fulfill ( { json } ) ;
80+ } ) ;
4981}
5082
5183test . beforeEach ( async ( { page } ) => {
52- await page . goto ( 'lab/index.html?kernel=python ' ) ;
84+ await page . goto ( 'lab/index.html' ) ;
5385 await page . waitForSelector ( '.jp-LabShell' ) ;
54-
55- // Clear token before each test
56- await page . evaluate ( ( ) => {
57- window . sharingService ?. resetToken ( ) ;
58- } ) ;
5986} ) ;
6087
6188test . describe ( 'General' , ( ) => {
@@ -70,13 +97,11 @@ test.describe('General', () => {
7097 } ) ;
7198
7299 test ( 'Dialog windows should shade the notebook area only' , async ( { page } ) => {
73- await dismissKernelSelectDialog ( page ) ;
74100 const firstCell = page . locator ( '.jp-Cell' ) ;
75101 await firstCell
76102 . getByRole ( 'textbox' )
77103 . fill ( 'The shaded area should cover the notebook content, but not the toolbar.' ) ;
78104 const promise = runCommand ( page , 'notebook:restart-kernel' ) ;
79- await dismissKernelSelectDialog ( page ) ;
80105 const dialog = page . locator ( '.jp-Dialog' ) ;
81106
82107 expect (
@@ -92,13 +117,20 @@ test.describe('General', () => {
92117 } ) ;
93118
94119 test ( 'Should load a view-only notebook' , async ( { page } ) => {
95- await runCommand ( page , 'jupytereverywhere:share-notebook' ) ;
96-
97- const notebookId = await getSharedNotebookID ( page ) ;
98- expect ( notebookId ) . not . toBeNull ( ) ;
120+ await mockTokenRoute ( page ) ;
121+ const notebookId = 'e3b0c442-98fc-1fc2-9c9f-8b6d6ed08a1d' ;
122+
123+ await page . route ( '**/api/v1/notebooks/*' , async route => {
124+ const json = {
125+ id : notebookId ,
126+ domain_id : 'domain' ,
127+ readable_id : null ,
128+ content : TEST_NOTEBOOK
129+ } ;
130+ await route . fulfill ( { json } ) ;
131+ } ) ;
99132
100- await page . goto ( `lab/index.html?notebook=${ notebookId } &kernel=python` ) ;
101- dismissKernelSelectDialog ( page ) ;
133+ await page . goto ( `lab/index.html?notebook=${ notebookId } ` ) ;
102134
103135 expect (
104136 await page . locator ( '.jp-NotebookPanel' ) . screenshot ( {
@@ -110,101 +142,56 @@ test.describe('General', () => {
110142
111143 test ( 'Should open files page' , async ( { page } ) => {
112144 await page . locator ( '.jp-SideBar' ) . getByTitle ( 'Files' ) . click ( ) ;
113- await dismissKernelSelectDialog ( page ) ;
114145 expect ( await page . locator ( '#je-files' ) . screenshot ( ) ) . toMatchSnapshot ( 'files.png' ) ;
115146 } ) ;
116147} ) ;
117148
118149test . describe ( 'Sharing' , ( ) => {
119150 test ( 'Should open share dialog in interactive notebook' , async ( { page } ) => {
120- await runCommand ( page , 'jupytereverywhere:share-notebook' ) ;
151+ await mockTokenRoute ( page ) ;
152+ await mockShareNotebookResponse ( page , 'e3b0c442-98fc-1fc2-9c9f-8b6d6ed08a1d' ) ;
153+ const shareButton = page . locator ( '.jp-ToolbarButton' ) . getByTitle ( 'Share this notebook' ) ;
154+ await shareButton . click ( ) ;
121155 const dialog = page . locator ( '.jp-Dialog-content' ) ;
122156 expect ( await dialog . screenshot ( ) ) . toMatchSnapshot ( 'share-dialog.png' ) ;
123157 } ) ;
124158
125159 test ( 'Should open share dialog in view-only mode' , async ( { page } ) => {
160+ await mockTokenRoute ( page ) ;
161+
126162 // Load view-only (shared) notebook
127- await runCommand ( page , 'jupytereverywhere:share-notebook' ) ;
128- const notebookId = await getSharedNotebookID ( page ) ;
129- expect ( notebookId ) . not . toBeNull ( ) ;
163+ const notebookId = 'e3b0c442-98fc-1fc2-9c9f-8b6d6ed08a1d' ;
164+ await mockGetSharedNotebook ( page , notebookId ) ;
165+ await page . goto ( `lab/index.html?notebook= ${ notebookId } ` ) ;
130166
131- // Re-share it as a new notebook
132- await page . goto ( `lab/index.html?notebook= ${ notebookId } &kernel=python` ) ;
133- dismissKernelSelectDialog ( page ) ;
167+ // Re-Share it as a new notebook
168+ const newNotebookId = '104931f8-fd96-489e-8520-c1793cbba6ce' ;
169+ await mockShareNotebookResponse ( page , newNotebookId ) ;
134170
171+ const shareButton = page . locator ( '.jp-ToolbarButton' ) . getByTitle ( 'Share this notebook' ) ;
135172 const dialog = page . locator ( '.jp-Dialog-content' ) ;
136173 await expect ( dialog ) . toHaveCount ( 0 ) ;
137- await runCommand ( page , 'jupytereverywhere:share-notebook' ) ;
174+ await shareButton . click ( ) ;
138175 await expect ( dialog ) . toHaveCount ( 1 ) ;
139176 } ) ;
140-
141- test ( 'Should show share dialog on Accel+S in interactive notebook' , async ( { page } ) => {
142- await mockTokenRoute ( page ) ;
143- await mockShareNotebookResponse ( page , 'e3b0c442-98fc-1fc2-9c9f-8b6d6ed08a1d' ) ;
144- await runCommand ( page , 'jupytereverywhere:save-and-share' ) ;
145- const dialog = page . locator ( '.jp-Dialog-content' ) ;
146- await expect ( dialog ) . toBeVisible ( ) ;
147- expect ( await dialog . screenshot ( ) ) . toMatchSnapshot ( 'share-dialog.png' ) ;
148- } ) ;
149177} ) ;
150178
151179test . describe ( 'Download' , ( ) => {
152180 test ( 'Should open download Menu' , async ( { page } ) => {
153181 const downloadButton = page . locator ( '.je-DownloadButton' ) ;
154182 await downloadButton . click ( ) ;
155- expect ( await page . locator ( '.je -DownloadDropdownButton-menu' ) . screenshot ( ) ) . toMatchSnapshot (
183+ expect ( await page . locator ( '.jp -DownloadDropdownButton-menu' ) . screenshot ( ) ) . toMatchSnapshot (
156184 'download-menu.png'
157185 ) ;
158186 } ) ;
159-
160- test ( 'Should download a notebook as IPyNB and PDF' , async ( { page, context } ) => {
161- dismissKernelSelectDialog ( page ) ;
162- await runCommand ( page , 'jupytereverywhere:share-notebook' ) ;
163- await getSharedNotebookID ( page ) ;
164- dismissKernelSelectDialog ( page ) ;
165-
166- const ipynbDownload = page . waitForEvent ( 'download' ) ;
167- await runCommand ( page , 'jupytereverywhere:download-notebook' ) ;
168- const ipynbPath = await ( await ipynbDownload ) . path ( ) ;
169- expect ( ipynbPath ) . not . toBeNull ( ) ;
170-
171- const pdfDownload = page . waitForEvent ( 'download' ) ;
172- await runCommand ( page , 'jupytereverywhere:download-pdf' ) ;
173- const pdfPath = await ( await pdfDownload ) . path ( ) ;
174- expect ( pdfPath ) . not . toBeNull ( ) ;
175- } ) ;
176-
177- test ( 'Should download view-only notebook as IPyNB and PDF' , async ( { page } ) => {
178- await runCommand ( page , 'jupytereverywhere:share-notebook' ) ;
179- const notebookId = await getSharedNotebookID ( page ) ;
180- expect ( notebookId ) . not . toBeNull ( ) ;
181-
182- await page . goto ( `lab/index.html?notebook=${ notebookId } &kernel=python` ) ;
183- dismissKernelSelectDialog ( page ) ;
184-
185- // Wait until view-only notebook loads, and assert it is a view-only notebook.
186- await page . locator ( '.jp-NotebookPanel' ) . waitFor ( ) ;
187- await expect ( page . locator ( '.je-ViewOnlyHeader' ) ) . toBeVisible ( ) ;
188-
189- const ipynbDownload = page . waitForEvent ( 'download' ) ;
190- await runCommand ( page , 'jupytereverywhere:download-notebook' ) ;
191- const ipynbPath = await ( await ipynbDownload ) . path ( ) ;
192- expect ( ipynbPath ) . not . toBeNull ( ) ;
193-
194- const pdfDownload = page . waitForEvent ( 'download' ) ;
195- await runCommand ( page , 'jupytereverywhere:download-pdf' ) ;
196- const pdfPath = await ( await pdfDownload ) . path ( ) ;
197- expect ( pdfPath ) . not . toBeNull ( ) ;
198- } ) ;
199187} ) ;
200188
201189test . describe ( 'Files' , ( ) => {
202190 test ( 'Should upload two files and display their thumbnails' , async ( { page } ) => {
203- await page . goto ( 'lab/index.html?kernel=python ' ) ;
191+ await page . goto ( 'lab/index.html' ) ;
204192 await page . waitForSelector ( '.jp-LabShell' ) ;
205193
206194 await page . locator ( '.jp-SideBar' ) . getByTitle ( 'Files' ) . click ( ) ;
207- await dismissKernelSelectDialog ( page ) ;
208195
209196 await page . locator ( '.je-FileTile' ) . first ( ) . click ( ) ; // the first tile will always be the "add new" one
210197
@@ -232,15 +219,17 @@ test.describe('Files', () => {
232219} ) ;
233220
234221test ( 'Should remove View Only banner when the Create Copy button is clicked' , async ( { page } ) => {
235- await runCommand ( page , 'jupytereverywhere:share-notebook' ) ;
236- const notebookId = await getSharedNotebookID ( page ) ;
237- expect ( notebookId ) . not . toBeNull ( ) ;
222+ await mockTokenRoute ( page ) ;
223+
224+ const notebookId = 'e3b0c442-98fc-1fc2-9c9f-8b6d6ed08a1d' ;
225+ await mockGetSharedNotebook ( page , notebookId ) ;
238226
239227 // Open view-only notebook
240- await page . goto ( `lab/index.html?notebook=${ notebookId } &kernel=python ` ) ;
228+ await page . goto ( `lab/index.html?notebook=${ notebookId } ` ) ;
241229 await expect ( page . locator ( '.je-ViewOnlyHeader' ) ) . toBeVisible ( ) ;
242230
243- await runCommand ( page , 'jupytereverywhere:create-copy-notebook' ) ;
231+ const createCopyButton = page . locator ( '.jp-ToolbarButtonComponent.je-CreateCopyButton' ) ;
232+ await createCopyButton . click ( ) ;
244233 await expect ( page . locator ( '.je-ViewOnlyHeader' ) ) . toBeHidden ( {
245234 timeout : 10000
246235 } ) ;
@@ -251,32 +240,3 @@ test('Should remove View Only banner when the Create Copy button is clicked', as
251240 page . locator ( '.jp-NotebookPanel-toolbar [data-jp-item-name="insert"]' )
252241 ) . toBeVisible ( ) ;
253242} ) ;
254-
255- test . describe ( 'Landing page' , ( ) => {
256- test ( 'Should render the landing page as expected' , async ( { page } ) => {
257- await page . goto ( 'index.html' ) ;
258- await page . waitForSelector ( '.je-hero' ) ;
259-
260- // Find the scroll height because the landing page is long and we want to
261- // capture the full page screenshot without the rest of it being empty; as
262- // we use a viewport to handle the hero section.
263- const scrollHeight = await page . evaluate ( ( ) => document . body . scrollHeight ) ;
264-
265- // Override the hero section's height so that we don't get blank sections
266- // after the viewport.
267- await page . addStyleTag ( {
268- content : '.je-hero { min-height: auto !important; height: auto !important; }'
269- } ) ;
270-
271- await page . setViewportSize ( {
272- width : 1440 ,
273- height : scrollHeight
274- } ) ;
275-
276- const screenshot = await page . screenshot ( {
277- fullPage : true
278- } ) ;
279-
280- expect ( screenshot ) . toMatchSnapshot ( 'landing-page.png' ) ;
281- } ) ;
282- } ) ;
0 commit comments