1+ import * as path from 'path' ;
12import { assert } from 'chai' ;
23import Sinon , * as sinon from 'sinon' ;
34import { SemVer } from 'semver' ;
4- import { ViewColumn , workspace , WorkspaceConfiguration } from 'vscode' ;
5+ import { Uri , ViewColumn , window , workspace , WorkspaceConfiguration } from 'vscode' ;
56import {
67 IExperimentService ,
78 IInstaller ,
@@ -14,14 +15,20 @@ import { IApplicationShell, ICommandManager } from '../../client/common/applicat
1415import { IServiceManager } from '../../client/ioc/types' ;
1516import { TensorBoardEntrypoint , TensorBoardEntrypointTrigger } from '../../client/tensorBoard/constants' ;
1617import { TensorBoardSession } from '../../client/tensorBoard/tensorBoardSession' ;
17- import { closeActiveWindows , initialize } from '../initialize' ;
18+ import { closeActiveWindows , EXTENSION_ROOT_DIR_FOR_TESTS , initialize } from '../initialize' ;
1819import * as ExperimentHelpers from '../../client/common/experiments/helpers' ;
1920import { IInterpreterService } from '../../client/interpreter/contracts' ;
2021import { Architecture } from '../../client/common/utils/platform' ;
2122import { PythonEnvironment , EnvironmentType } from '../../client/pythonEnvironments/info' ;
2223import { PYTHON_PATH } from '../common' ;
2324import { TorchProfiler } from '../../client/common/experiments/groups' ;
2425import { ImportTracker } from '../../client/telemetry/importTracker' ;
26+ import { IMultiStepInput , IMultiStepInputFactory } from '../../client/common/utils/multiStepInput' ;
27+
28+ // Class methods exposed just for testing purposes
29+ interface ITensorBoardSessionTestAPI {
30+ jumpToSource ( fsPath : string , line : number ) : Promise < void > ;
31+ }
2532
2633const info : PythonEnvironment = {
2734 architecture : Architecture . Unknown ,
@@ -99,47 +106,35 @@ suite('TensorBoard session creation', async () => {
99106 errorMessageStub = sandbox . stub ( applicationShell , 'showErrorMessage' ) ;
100107 errorMessageStub . resolves ( installPromptSelection ) ;
101108 }
102- suite ( 'Core functionality' , async ( ) => {
103- test ( 'Golden path: TensorBoard session starts successfully and webview is shown' , async ( ) => {
104- sandbox . stub ( experimentService , 'inExperiment' ) . resolves ( true ) ;
105- errorMessageStub = sandbox . stub ( applicationShell , 'showErrorMessage' ) ;
106- // Stub user selections
107- sandbox
108- . stub ( applicationShell , 'showQuickPick' )
109- . resolves ( { label : TensorBoard . useCurrentWorkingDirectory ( ) } ) ;
109+ async function createSession ( ) {
110+ errorMessageStub = sandbox . stub ( applicationShell , 'showErrorMessage' ) ;
111+ // Stub user selections
112+ sandbox . stub ( applicationShell , 'showQuickPick' ) . resolves ( { label : TensorBoard . useCurrentWorkingDirectory ( ) } ) ;
110113
111- const session = ( await commandManager . executeCommand (
112- 'python.launchTensorBoard' ,
113- TensorBoardEntrypoint . palette ,
114- TensorBoardEntrypointTrigger . palette ,
115- ) ) as TensorBoardSession ;
114+ const session = ( await commandManager . executeCommand (
115+ 'python.launchTensorBoard' ,
116+ TensorBoardEntrypoint . palette ,
117+ TensorBoardEntrypointTrigger . palette ,
118+ ) ) as TensorBoardSession ;
116119
117- assert . ok ( session . panel ?. viewColumn === ViewColumn . One , 'Panel opened in wrong group' ) ;
118- assert . ok ( session . panel ?. visible , 'Webview panel not shown on session creation golden path' ) ;
119- assert . ok ( errorMessageStub . notCalled , 'Error message shown on session creation golden path' ) ;
120+ assert . ok ( session . panel ?. viewColumn === ViewColumn . One , 'Panel opened in wrong group' ) ;
121+ assert . ok ( session . panel ?. visible , 'Webview panel not shown on session creation golden path' ) ;
122+ assert . ok ( errorMessageStub . notCalled , 'Error message shown on session creation golden path' ) ;
123+ return session ;
124+ }
125+ suite ( 'Core functionality' , async ( ) => {
126+ test ( 'Golden path: TensorBoard session starts successfully and webview is shown' , async ( ) => {
127+ await createSession ( ) ;
120128 } ) ;
121129 test ( 'When webview is closed, session is killed' , async ( ) => {
122- sandbox . stub ( experimentService , 'inExperiment' ) . resolves ( true ) ;
123- errorMessageStub = sandbox . stub ( applicationShell , 'showErrorMessage' ) ;
124- // Stub user selections
125- sandbox
126- . stub ( applicationShell , 'showQuickPick' )
127- . resolves ( { label : TensorBoard . useCurrentWorkingDirectory ( ) } ) ;
128-
129- const session = ( await commandManager . executeCommand (
130- 'python.launchTensorBoard' ,
131- TensorBoardEntrypoint . palette ,
132- TensorBoardEntrypointTrigger . palette ,
133- ) ) as TensorBoardSession ;
134-
130+ const session = await createSession ( ) ;
135131 const { daemon, panel } = session ;
136132 assert . ok ( panel ?. visible , 'Webview panel not shown' ) ;
137133 panel ?. dispose ( ) ;
138134 assert . ok ( session . panel === undefined , 'Webview still visible' ) ;
139135 assert . ok ( daemon ?. killed , 'TensorBoard session process not killed after webview closed' ) ;
140136 } ) ;
141137 test ( 'When user selects file picker, display file picker' , async ( ) => {
142- sandbox . stub ( experimentService , 'inExperiment' ) . resolves ( true ) ;
143138 errorMessageStub = sandbox . stub ( applicationShell , 'showErrorMessage' ) ;
144139 // Stub user selections
145140 sandbox . stub ( applicationShell , 'showQuickPick' ) . resolves ( { label : TensorBoard . selectAnotherFolder ( ) } ) ;
@@ -440,4 +435,59 @@ suite('TensorBoard session creation', async () => {
440435 'Prompted user to select log directory although setting was specified' ,
441436 ) ;
442437 } ) ;
438+ suite ( 'Jump to source' , async ( ) => {
439+ // We can't test a full E2E scenario with the TB profiler plugin because we can't
440+ // accurately target simulated clicks at iframed content. This only tests
441+ // code from the moment that the VS Code webview posts a message back
442+ // to the extension.
443+ const fsPath = path . join (
444+ EXTENSION_ROOT_DIR_FOR_TESTS ,
445+ 'src' ,
446+ 'test' ,
447+ 'pythonFiles' ,
448+ 'tensorBoard' ,
449+ 'sourcefile.py' ,
450+ ) ;
451+ teardown ( ( ) => {
452+ sandbox . restore ( ) ;
453+ } ) ;
454+ function setupStubsForMultiStepInput ( ) {
455+ // Stub the factory to return our stubbed multistep input when it's asked to create one
456+ const multiStepFactory = serviceManager . get < IMultiStepInputFactory > ( IMultiStepInputFactory ) ;
457+ const inputInstance = multiStepFactory . create ( ) ;
458+ // Create a multistep input with stubs for methods
459+ const showQuickPickStub = sandbox . stub ( inputInstance , 'showQuickPick' ) . resolves ( {
460+ label : TensorBoard . selectMissingSourceFile ( ) ,
461+ description : TensorBoard . selectMissingSourceFileDescription ( ) ,
462+ } ) ;
463+ const createInputStub = sandbox
464+ . stub ( multiStepFactory , 'create' )
465+ . returns ( inputInstance as IMultiStepInput < unknown > ) ;
466+ // Stub the system file picker
467+ const filePickerStub = sandbox . stub ( applicationShell , 'showOpenDialog' ) . resolves ( [ Uri . file ( fsPath ) ] ) ;
468+ return [ showQuickPickStub , createInputStub , filePickerStub ] ;
469+ }
470+ test ( 'Resolves filepaths without displaying prompt' , async ( ) => {
471+ const session = ( ( await createSession ( ) ) as unknown ) as ITensorBoardSessionTestAPI ;
472+ const stubs = setupStubsForMultiStepInput ( ) ;
473+ await session . jumpToSource ( fsPath , 0 ) ;
474+ assert . ok ( window . activeTextEditor !== undefined , 'Source file not resolved' ) ;
475+ assert . ok ( window . activeTextEditor ?. document . uri . fsPath === fsPath , 'Wrong source file opened' ) ;
476+ assert . ok (
477+ stubs . reduce ( ( prev , current ) => current . notCalled && prev , true ) ,
478+ 'Stubs were called when file is present' ,
479+ ) ;
480+ } ) ;
481+ test ( 'Display quickpick to user if filepath is not on disk' , async ( ) => {
482+ const session = ( ( await createSession ( ) ) as unknown ) as ITensorBoardSessionTestAPI ;
483+ const stubs = setupStubsForMultiStepInput ( ) ;
484+ await session . jumpToSource ( '/nonexistent/file/path.py' , 0 ) ;
485+ assert . ok ( window . activeTextEditor !== undefined , 'Source file not resolved' ) ;
486+ assert . ok ( window . activeTextEditor ?. document . uri . fsPath === fsPath , 'Wrong source file opened' ) ;
487+ assert . ok (
488+ stubs . reduce ( ( prev , current ) => current . calledOnce && prev , true ) ,
489+ 'Stubs called an unexpected number of times' ,
490+ ) ;
491+ } ) ;
492+ } ) ;
443493} ) ;
0 commit comments