@@ -15,12 +15,13 @@ import {
1515 IExtensionContext
1616} from '../../../platform/common/types' ;
1717import { IS_REMOTE_NATIVE_TEST , initialize } from '../../initialize.node' ;
18- import { startJupyterServer , closeNotebooksAndCleanUpAfterTests } from '../notebook/helper.node' ;
18+ import { startJupyterServer , closeNotebooksAndCleanUpAfterTests , hijackPrompt } from '../notebook/helper.node' ;
1919import {
2020 SecureConnectionValidator ,
2121 UserJupyterServerDisplayName ,
2222 UserJupyterServerUriInput ,
2323 UserJupyterServerUrlProvider ,
24+ getBaseJupyterUrl ,
2425 parseUri
2526} from '../../../standalone/userJupyterServer/userServerUrlProvider' ;
2627import {
@@ -33,7 +34,7 @@ import {
3334import { JupyterConnection } from '../../../kernels/jupyter/connection/jupyterConnection' ;
3435import { dispose } from '../../../platform/common/helpers' ;
3536import { anything , instance , mock , when } from 'ts-mockito' ;
36- import { CancellationTokenSource , Disposable , EventEmitter , InputBox , Memento } from 'vscode' ;
37+ import { CancellationTokenSource , Disposable , EventEmitter , InputBox , Memento , workspace } from 'vscode' ;
3738import { noop } from '../../../platform/common/utils/misc' ;
3839import { DataScience } from '../../../platform/common/utils/localize' ;
3940import * as sinon from 'sinon' ;
@@ -42,9 +43,12 @@ import { createDeferred, createDeferredFromPromise } from '../../../platform/com
4243import { IMultiStepInputFactory } from '../../../platform/common/utils/multiStepInput' ;
4344import { IFileSystem } from '../../../platform/common/platform/types' ;
4445
45- suite ( 'Connect to Remote Jupyter Servers' , function ( ) {
46+ suite ( 'Connect to Remote Jupyter Servers @mandatory ' , function ( ) {
4647 // On conda these take longer for some reason.
4748 this . timeout ( 120_000 ) ;
49+ let jupyterNotebookWithAutoGeneratedToken = { url : '' , dispose : noop } ;
50+ let jupyterLabWithAutoGeneratedToken = { url : '' , dispose : noop } ;
51+ let jupyterNotebookWithCerts = { url : '' , dispose : noop } ;
4852 let jupyterNotebookWithHelloPassword = { url : '' , dispose : noop } ;
4953 let jupyterLabWithHelloPasswordAndWorldToken = { url : '' , dispose : noop } ;
5054 let jupyterNotebookWithHelloToken = { url : '' , dispose : noop } ;
@@ -57,12 +61,28 @@ suite('Connect to Remote Jupyter Servers', function () {
5761 this . timeout ( 120_000 ) ;
5862 await initialize ( ) ;
5963 [
64+ jupyterNotebookWithAutoGeneratedToken ,
65+ jupyterLabWithAutoGeneratedToken ,
66+ jupyterNotebookWithCerts ,
6067 jupyterNotebookWithHelloPassword ,
6168 jupyterLabWithHelloPasswordAndWorldToken ,
6269 jupyterNotebookWithHelloToken ,
6370 jupyterNotebookWithEmptyPasswordToken ,
6471 jupyterLabWithHelloPasswordAndEmptyToken
6572 ] = await Promise . all ( [
73+ startJupyterServer ( {
74+ jupyterLab : false ,
75+ standalone : true
76+ } ) ,
77+ startJupyterServer ( {
78+ jupyterLab : true ,
79+ standalone : true
80+ } ) ,
81+ startJupyterServer ( {
82+ jupyterLab : false ,
83+ standalone : true ,
84+ useCert : true
85+ } ) ,
6686 startJupyterServer ( {
6787 jupyterLab : false ,
6888 password : 'Hello' ,
@@ -95,6 +115,8 @@ suite('Connect to Remote Jupyter Servers', function () {
95115 } ) ;
96116 suiteTeardown ( ( ) => {
97117 dispose ( [
118+ jupyterNotebookWithAutoGeneratedToken ,
119+ jupyterLabWithAutoGeneratedToken ,
98120 jupyterNotebookWithHelloPassword ,
99121 jupyterLabWithHelloPasswordAndWorldToken ,
100122 jupyterNotebookWithHelloToken ,
@@ -111,6 +133,7 @@ suite('Connect to Remote Jupyter Servers', function () {
111133 let commands : ICommandManager ;
112134 let inputBox : InputBox ;
113135 let token : CancellationTokenSource ;
136+ let requestCreator : IJupyterRequestCreator ;
114137 setup ( async function ( ) {
115138 if ( ! IS_REMOTE_NATIVE_TEST ( ) ) {
116139 return this . skip ( ) ;
@@ -165,6 +188,7 @@ suite('Connect to Remote Jupyter Servers', function () {
165188 const onDidRemoveUriStorage = new EventEmitter < JupyterServerProviderHandle [ ] > ( ) ;
166189 disposables . push ( onDidRemoveUriStorage ) ;
167190 when ( serverUriStorage . onDidRemove ) . thenReturn ( onDidRemoveUriStorage . event ) ;
191+ requestCreator = api . serviceContainer . get < IJupyterRequestCreator > ( IJupyterRequestCreator ) ;
168192
169193 userUriProvider = new UserJupyterServerUrlProvider (
170194 instance ( clipboard ) ,
@@ -198,7 +222,7 @@ suite('Connect to Remote Jupyter Servers', function () {
198222 } ) ;
199223 suiteTeardown ( ( ) => closeNotebooksAndCleanUpAfterTests ( disposables ) ) ;
200224
201- async function testConnection ( {
225+ async function testConnectionAndVerifyBaseUrl ( {
202226 password,
203227 userUri,
204228 failWithInvalidPassword
@@ -207,12 +231,23 @@ suite('Connect to Remote Jupyter Servers', function () {
207231 userUri : string ;
208232 failWithInvalidPassword ?: boolean ;
209233 } ) {
234+ const config = workspace . getConfiguration ( 'jupyter' ) ;
235+ await config . update ( 'allowUnauthorizedRemoteConnection' , false ) ;
236+ const prompt = await hijackPrompt (
237+ 'showErrorMessage' ,
238+ { contains : 'certificate' } ,
239+ { result : DataScience . jupyterSelfCertEnable , clickImmediately : true }
240+ ) ;
241+ disposables . push ( prompt ) ;
210242 const displayName = 'Test Remove Server Name' ;
211243 when ( clipboard . readText ( ) ) . thenResolve ( userUri ) ;
212244 sinon . stub ( UserJupyterServerUriInput . prototype , 'getUrlFromUser' ) . resolves ( {
213245 url : userUri ,
214246 jupyterServerUri : parseUri ( userUri , '' ) !
215247 } ) ;
248+ const baseUrl = `${ new URL ( userUri ) . protocol } //localhost:${ new URL ( userUri ) . port } /` ;
249+ const computedBaseUrl = await getBaseJupyterUrl ( userUri , requestCreator ) ;
250+ assert . strictEqual ( computedBaseUrl ?. endsWith ( '/' ) ? computedBaseUrl : `${ computedBaseUrl } /` , baseUrl ) ;
216251 sinon . stub ( SecureConnectionValidator . prototype , 'promptToUseInsecureConnections' ) . resolves ( true ) ;
217252 sinon . stub ( UserJupyterServerDisplayName . prototype , 'getDisplayName' ) . resolves ( displayName ) ;
218253 const errorMessageDisplayed = createDeferred < string > ( ) ;
@@ -226,6 +261,9 @@ suite('Connect to Remote Jupyter Servers', function () {
226261 assert . strictEqual ( errorMessageDisplayed . value , DataScience . passwordFailure ) ;
227262 assert . ok ( ! handlePromise . completed ) ;
228263 } else {
264+ if ( new URL ( userUri ) . protocol . includes ( 'https' ) ) {
265+ assert . ok ( await prompt . displayed , 'Prompt for trusting certs not displayed' ) ;
266+ }
229267 assert . equal ( errorMessageDisplayed . value || '' , '' , `Password should be valid, ${ errorMessageDisplayed } ` ) ;
230268 assert . ok ( handlePromise . completed , 'Did not complete' ) ;
231269 const value = handlePromise . value ;
@@ -255,42 +293,80 @@ suite('Connect to Remote Jupyter Servers', function () {
255293 // assert.strictEqual(serverInfo.displayName, `Title of Server`, 'Invalid Title');
256294 }
257295 }
296+ test ( 'Connect to server with auto generated Token in URL' , ( ) =>
297+ testConnectionAndVerifyBaseUrl ( { userUri : jupyterNotebookWithAutoGeneratedToken . url , password : undefined } ) ) ;
298+ test ( 'Connect to JuyterLab server with auto generated Token in URL' , ( ) =>
299+ testConnectionAndVerifyBaseUrl ( { userUri : jupyterLabWithAutoGeneratedToken . url , password : undefined } ) ) ;
300+ test ( 'Connect to server with certificates' , ( ) =>
301+ testConnectionAndVerifyBaseUrl ( { userUri : jupyterNotebookWithCerts . url , password : undefined } ) ) ;
302+ test ( 'Connect to server with auto generated Token in URL and path has tree in it' , async ( ) => {
303+ const token = new URL ( jupyterNotebookWithAutoGeneratedToken . url ) . searchParams . get ( 'token' ) ! ;
304+ const port = new URL ( jupyterNotebookWithAutoGeneratedToken . url ) . port ;
305+ await testConnectionAndVerifyBaseUrl ( {
306+ userUri : `http://localhost:${ port } /tree?token=${ token } ` ,
307+ password : undefined
308+ } ) ;
309+ } ) ;
310+ test ( 'Connect to server with auto generated Token in URL and custom path' , async ( ) => {
311+ const token = new URL ( jupyterLabWithAutoGeneratedToken . url ) . searchParams . get ( 'token' ) ! ;
312+ const port = new URL ( jupyterLabWithAutoGeneratedToken . url ) . port ;
313+ await testConnectionAndVerifyBaseUrl ( {
314+ userUri : `http://localhost:${ port } /notebooks/Untitled.ipynb?kernel_name=python3&token=${ token } ` ,
315+ password : undefined
316+ } ) ;
317+ } ) ;
318+ test ( 'Connect to Jupyter Lab server with auto generated Token in URL and path has lab in it' , async ( ) => {
319+ const token = new URL ( jupyterLabWithAutoGeneratedToken . url ) . searchParams . get ( 'token' ) ! ;
320+ const port = new URL ( jupyterLabWithAutoGeneratedToken . url ) . port ;
321+ await testConnectionAndVerifyBaseUrl ( {
322+ userUri : `http://localhost:${ port } /lab?token=${ token } ` ,
323+ password : undefined
324+ } ) ;
325+ } ) ;
326+ test ( 'Connect to Jupyter Lab server with auto generated Token in URL and custom path' , async ( ) => {
327+ const token = new URL ( jupyterLabWithAutoGeneratedToken . url ) . searchParams . get ( 'token' ) ! ;
328+ const port = new URL ( jupyterLabWithAutoGeneratedToken . url ) . port ;
329+ await testConnectionAndVerifyBaseUrl ( {
330+ userUri : `http://localhost:${ port } /lab/workspaces/auto-R?token=${ token } ` ,
331+ password : undefined
332+ } ) ;
333+ } ) ;
258334 test ( 'Connect to server with Token in URL' , ( ) =>
259- testConnection ( { userUri : jupyterNotebookWithHelloToken . url , password : undefined } ) ) ;
335+ testConnectionAndVerifyBaseUrl ( { userUri : jupyterNotebookWithHelloToken . url , password : undefined } ) ) ;
260336 test ( 'Connect to server with Password and Token in URL' , ( ) =>
261- testConnection ( { userUri : jupyterNotebookWithHelloPassword . url , password : 'Hello' } ) ) ;
337+ testConnectionAndVerifyBaseUrl ( { userUri : jupyterNotebookWithHelloPassword . url , password : 'Hello' } ) ) ;
262338 test ( 'Connect to Notebook server with Password and no Token in URL' , ( ) =>
263- testConnection ( {
339+ testConnectionAndVerifyBaseUrl ( {
264340 userUri : `http://localhost:${ new URL ( jupyterNotebookWithHelloPassword . url ) . port } /` ,
265341 password : 'Hello'
266342 } ) ) ;
267343 test ( 'Connect to Lab server with Password and no Token in URL' , ( ) =>
268- testConnection ( {
344+ testConnectionAndVerifyBaseUrl ( {
269345 userUri : `http://localhost:${ new URL ( jupyterLabWithHelloPasswordAndWorldToken . url ) . port } /` ,
270346 password : 'Hello'
271347 } ) ) ;
272348 test ( 'Connect to server with Invalid Password' , ( ) =>
273- testConnection ( {
349+ testConnectionAndVerifyBaseUrl ( {
274350 userUri : `http://localhost:${ new URL ( jupyterNotebookWithHelloPassword . url ) . port } /` ,
275351 password : 'Bogus' ,
276352 failWithInvalidPassword : true
277353 } ) ) ;
278354 test ( 'Connect to Lab server with Password & Token in URL' , async ( ) =>
279- testConnection ( { userUri : jupyterLabWithHelloPasswordAndWorldToken . url , password : 'Hello' } ) ) ;
355+ testConnectionAndVerifyBaseUrl ( { userUri : jupyterLabWithHelloPasswordAndWorldToken . url , password : 'Hello' } ) ) ;
280356 test ( 'Connect to server with empty Password & empty Token in URL' , ( ) =>
281- testConnection ( { userUri : jupyterNotebookWithEmptyPasswordToken . url , password : '' } ) ) ;
357+ testConnectionAndVerifyBaseUrl ( { userUri : jupyterNotebookWithEmptyPasswordToken . url , password : '' } ) ) ;
282358 test ( 'Connect to server with empty Password & empty Token (nothing in URL)' , ( ) =>
283- testConnection ( {
359+ testConnectionAndVerifyBaseUrl ( {
284360 userUri : `http://localhost:${ new URL ( jupyterNotebookWithEmptyPasswordToken . url ) . port } /` ,
285361 password : ''
286362 } ) ) ;
287363 test ( 'Connect to Lab server with Hello Password & empty Token (not even in URL)' , ( ) =>
288- testConnection ( {
364+ testConnectionAndVerifyBaseUrl ( {
289365 userUri : `http://localhost:${ new URL ( jupyterLabWithHelloPasswordAndEmptyToken . url ) . port } /` ,
290366 password : 'Hello'
291367 } ) ) ;
292368 test ( 'Connect to Lab server with bogus Password & empty Token (not even in URL)' , ( ) =>
293- testConnection ( {
369+ testConnectionAndVerifyBaseUrl ( {
294370 userUri : `http://localhost:${ new URL ( jupyterLabWithHelloPasswordAndEmptyToken . url ) . port } /` ,
295371 password : 'Bogus' ,
296372 failWithInvalidPassword : true
0 commit comments