From 61b179b2092050709e3c373a6738abad8ce581c4 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 30 Mar 2020 17:10:42 -0700 Subject: [PATCH] Update prettier to latest version (#10838) * Update prettier * News entry * Revert changes --- .prettierrc.js | 1 + .../ci/performance/checkPerformanceResults.js | 8 +- .../createNewPerformanceBenchmark.js | 4 +- .../ci/performance/savePerformanceResults.js | 8 +- .../messagesMustBeLocalizedRule.js | 4 +- build/util.js | 2 +- build/webpack/common.js | 122 +- .../loaders/externalizeDependencies.js | 40 +- build/webpack/loaders/fixNodeFetch.js | 2 +- build/webpack/loaders/jsonloader.js | 2 +- build/webpack/loaders/remarkLoader.js | 2 +- .../webpack.extension.dependencies.config.js | 134 +- news/3 Code Health/10837.md | 1 + package-lock.json | 6 +- package.json | 2 +- src/client/activation/aaTesting.ts | 38 +- src/client/activation/activationManager.ts | 8 +- src/client/activation/activationService.ts | 14 +- src/client/activation/common/activatorBase.ts | 2 +- src/client/activation/common/downloader.ts | 6 +- .../common/languageServerFolderService.ts | 4 +- .../common/languageServerPackageService.ts | 4 +- src/client/activation/jedi.ts | 6 +- .../activation/languageClientMiddleware.ts | 4 +- .../languageServer/analysisOptions.ts | 12 +- .../languageServer/languageServerExtension.ts | 2 +- .../languageServerPackageService.ts | 84 +- .../languageServer/languageServerProxy.ts | 6 +- .../activation/languageServer/manager.ts | 2 +- .../languageServer/outputChannel.ts | 78 +- .../activation/node/languageServerProxy.ts | 4 +- src/client/activation/node/manager.ts | 2 +- src/client/activation/progress.ts | 6 +- src/client/activation/types.ts | 458 +- src/client/api.ts | 2 +- .../diagnostics/applicationDiagnostics.ts | 8 +- src/client/application/diagnostics/base.ts | 4 +- .../diagnostics/checks/envPathVariable.ts | 2 +- .../checks/invalidLaunchJsonDebugger.ts | 4 +- .../checks/invalidPythonPathInDebugger.ts | 4 +- .../checks/macPythonInterpreter.ts | 12 +- .../checks/powerShellActivation.ts | 2 +- .../diagnostics/checks/pythonInterpreter.ts | 2 +- .../application/diagnostics/promptHandler.ts | 4 +- .../common/application/activeResource.ts | 54 +- .../common/application/applicationShell.ts | 272 +- .../common/application/webPanels/webPanel.ts | 20 +- .../application/webPanels/webPanelServer.ts | 2 +- src/client/common/asyncDisposableRegistry.ts | 54 +- src/client/common/cancellation.ts | 4 +- src/client/common/configSettings.ts | 1234 +- src/client/common/configuration/service.ts | 2 +- src/client/common/constants.ts | 212 +- src/client/common/crypto.ts | 8 +- src/client/common/editor.ts | 28 +- src/client/common/experiments.ts | 10 +- src/client/common/extensions.ts | 16 +- .../common/featureDeprecationManager.ts | 10 +- src/client/common/helpers.ts | 4 +- .../insidersBuild/downloadChannelService.ts | 114 +- .../insidersBuild/insidersExtensionService.ts | 270 +- src/client/common/installer/channelManager.ts | 2 +- .../installer/extensionBuildInstaller.ts | 2 +- .../common/installer/moduleInstaller.ts | 4 +- src/client/common/installer/pipInstaller.ts | 2 +- .../common/installer/productInstaller.ts | 10 +- src/client/common/installer/types.ts | 112 +- src/client/common/logger.ts | 18 +- .../common/markdown/restTextConverter.ts | 6 +- src/client/common/net/fileDownloader.ts | 6 +- src/client/common/net/socket/socketServer.ts | 2 +- .../nuget/azureBlobStoreNugetRepository.ts | 2 +- src/client/common/nuget/nugetRepository.ts | 68 +- src/client/common/open.ts | 4 +- src/client/common/platform/registry.ts | 6 +- src/client/common/process/logger.ts | 2 +- src/client/common/process/proc.ts | 20 +- src/client/common/process/pythonDaemon.ts | 16 +- src/client/common/process/pythonDaemonPool.ts | 16 +- .../common/process/pythonExecutionFactory.ts | 2 +- src/client/common/process/pythonProcess.ts | 2 +- src/client/common/terminal/activator/index.ts | 2 +- .../condaActivationProvider.ts | 300 +- src/client/common/terminal/helper.ts | 2 +- src/client/common/terminal/service.ts | 2 +- .../common/terminal/syncTerminalService.ts | 2 +- src/client/common/utils/async.ts | 6 +- src/client/common/utils/cacheUtils.ts | 2 +- src/client/common/utils/decorators.ts | 36 +- src/client/common/utils/enum.ts | 8 +- src/client/common/utils/misc.ts | 126 +- src/client/common/utils/multiStepInput.ts | 12 +- src/client/common/utils/sysTypes.ts | 2 +- src/client/common/utils/version.ts | 4 +- src/client/common/variables/environment.ts | 6 +- .../variables/environmentVariablesProvider.ts | 4 +- .../common/variables/systemVariables.ts | 10 +- src/client/datascience/cellFactory.ts | 4 +- src/client/datascience/cellMatcher.ts | 176 +- src/client/datascience/codeCssGenerator.ts | 1038 +- .../commands/commandLineSelector.ts | 2 +- .../datascience/commands/commandRegistry.ts | 4 +- .../datascience/commands/kernelSwitcher.ts | 2 +- .../datascience/commands/serverSelector.ts | 2 +- src/client/datascience/common.ts | 50 +- .../context/activeEditorContext.ts | 2 +- .../datascience/data-viewing/dataViewer.ts | 378 +- .../data-viewing/dataViewerMessageListener.ts | 92 +- .../data-viewing/dataViewerProvider.ts | 98 +- src/client/datascience/data-viewing/types.ts | 104 +- .../editor-integration/cellhashLogger.ts | 2 +- .../editor-integration/cellhashprovider.ts | 22 +- .../editor-integration/codeLensFactory.ts | 12 +- .../editor-integration/codelensprovider.ts | 12 +- .../editor-integration/codewatcher.ts | 20 +- .../editor-integration/decorator.ts | 262 +- .../datascience/errorHandler/errorHandler.ts | 2 +- src/client/datascience/gather/gather.ts | 2 +- .../datascience/gather/gatherListener.ts | 8 +- .../intellisense/conversion.ts | 6 +- .../intellisense/intellisenseDocument.ts | 24 +- .../intellisense/intellisenseProvider.ts | 32 +- .../interactive-common/interactiveBase.ts | 40 +- .../interactiveWindowMessageListener.ts | 8 +- .../interactive-common/linkProvider.ts | 8 +- .../interactive-common/notebookProvider.ts | 10 +- .../interactive-ipynb/autoSaveService.ts | 4 +- .../interactive-ipynb/nativeEditor.ts | 16 +- .../interactive-ipynb/nativeEditorProvider.ts | 24 +- .../nativeEditorProviderOld.ts | 8 +- .../interactive-ipynb/nativeEditorStorage.ts | 24 +- .../interactive-window/interactiveWindow.ts | 832 +- .../interactiveWindowCommandListener.ts | 1000 +- .../interactiveWindowProvider.ts | 4 +- .../ipywidgets/ipyWidgetMessageDispatcher.ts | 6 +- .../ipyWidgetMessageDispatcherFactory.ts | 8 +- .../ipywidgets/ipywidgetHandler.ts | 6 +- .../jupyter/interpreter/jupyterCommand.ts | 681 +- .../interpreter/jupyterCommandFinder.ts | 20 +- ...yterCommandInterpreterDependencyService.ts | 6 +- ...pyterCommandInterpreterExecutionService.ts | 4 +- .../jupyterInterpreterDependencyService.ts | 20 +- .../interpreter/jupyterInterpreterService.ts | 2 +- ...erInterpreterSubCommandExecutionService.ts | 12 +- .../jupyterCellOutputMimeTypeTracker.ts | 8 +- .../jupyter/jupyterConnectError.ts | 20 +- .../datascience/jupyter/jupyterConnection.ts | 498 +- .../datascience/jupyter/jupyterDebugger.ts | 4 +- .../datascience/jupyter/jupyterExecution.ts | 804 +- .../jupyter/jupyterExecutionFactory.ts | 316 +- .../datascience/jupyter/jupyterExporter.ts | 524 +- .../jupyter/jupyterInstallError.ts | 32 +- .../jupyter/jupyterInterruptError.ts | 18 +- .../datascience/jupyter/jupyterNotebook.ts | 56 +- .../jupyter/jupyterPasswordConnect.ts | 2 +- .../datascience/jupyter/jupyterServer.ts | 510 +- .../datascience/jupyter/jupyterSession.ts | 1230 +- .../jupyter/jupyterSessionManager.ts | 504 +- .../datascience/jupyter/jupyterUtils.ts | 2 +- .../datascience/jupyter/jupyterVariables.ts | 8 +- .../jupyter/jupyterWaitForIdleError.ts | 18 +- .../jupyter/kernels/jupyterKernelSpec.ts | 4 +- .../jupyter/kernels/kernelSelections.ts | 30 +- .../jupyter/kernels/kernelSelector.ts | 4 +- .../jupyter/kernels/kernelService.ts | 16 +- .../liveshare/guestJupyterExecution.ts | 316 +- .../jupyter/liveshare/guestJupyterNotebook.ts | 4 +- .../jupyter/liveshare/guestJupyterServer.ts | 318 +- .../liveshare/guestJupyterSessionManager.ts | 98 +- .../jupyter/liveshare/hostJupyterExecution.ts | 336 +- .../jupyter/liveshare/hostJupyterNotebook.ts | 18 +- .../jupyter/liveshare/hostJupyterServer.ts | 678 +- .../liveshare/liveShareParticipantMixin.ts | 318 +- .../jupyter/liveshare/responseQueue.ts | 180 +- .../jupyter/liveshare/roleBasedFactory.ts | 224 +- .../jupyter/liveshare/serverCache.ts | 332 +- .../datascience/jupyter/liveshare/types.ts | 104 +- .../datascience/jupyter/liveshare/utils.ts | 90 +- .../datascience/jupyter/notebookStarter.ts | 6 +- .../datascience/jupyter/serverSelector.ts | 2 +- src/client/datascience/liveshare/liveshare.ts | 124 +- .../datascience/liveshare/liveshareProxy.ts | 364 +- .../datascience/liveshare/postOffice.ts | 562 +- .../datascience/liveshare/serviceProxy.ts | 68 +- src/client/datascience/progress/decorator.ts | 6 +- .../datascience/progress/progressReporter.ts | 2 +- src/client/datascience/serviceRegistry.ts | 436 +- src/client/datascience/statusProvider.ts | 256 +- src/client/datascience/themeFinder.ts | 446 +- src/client/datascience/types.ts | 1864 +- src/client/datascience/webViewHost.ts | 758 +- .../Common/debugStreamProvider.ts | 6 +- .../debugAdapter/Common/protocolLogger.ts | 2 +- .../DebugClients/LocalDebugClient.ts | 14 +- .../DebugServers/LocalDebugServerV2.ts | 4 +- .../DebugServers/RemoteDebugServerv2.ts | 2 +- src/client/debugger/debugAdapter/main.ts | 12 +- .../adapter/outdatedDebuggerPrompt.ts | 2 +- .../extension/attachQuickPick/picker.ts | 4 +- .../extension/attachQuickPick/provider.ts | 2 +- src/client/debugger/extension/banner.ts | 4 +- .../configuration/providers/djangoLaunch.ts | 9 +- .../configuration/providers/flaskLaunch.ts | 2 +- .../configuration/providers/moduleLaunch.ts | 2 +- .../configuration/providers/pyramidLaunch.ts | 9 +- .../configuration/providers/remoteAttach.ts | 6 +- .../resolvers/launchConfigExperiment.ts | 2 +- .../hooks/childProcessAttachService.ts | 2 +- .../extension/hooks/eventHandlerDispatcher.ts | 8 +- src/client/extensionActivation.ts | 4 +- src/client/formatters/baseFormatter.ts | 10 +- src/client/interpreter/activation/service.ts | 13 +- .../wrapperEnvironmentActivationService.ts | 12 +- src/client/interpreter/autoSelection/index.ts | 2 +- .../interpreter/autoSelection/rules/cached.ts | 6 +- .../interpreter/autoSelection/rules/system.ts | 2 +- .../autoSelection/rules/workspaceEnv.ts | 2 +- .../configuration/interpreterSelector.ts | 4 +- .../configuration/pythonPathUpdaterService.ts | 4 +- .../display/shebangCodeLensProvider.ts | 6 +- src/client/interpreter/helpers.ts | 8 +- src/client/interpreter/interpreterService.ts | 14 +- src/client/interpreter/interpreterVersion.ts | 4 +- src/client/interpreter/locators/helpers.ts | 10 +- src/client/interpreter/locators/index.ts | 18 +- .../interpreter/locators/progressService.ts | 6 +- .../locators/services/KnownPathsService.ts | 22 +- .../services/baseVirtualEnvService.ts | 30 +- .../services/cacheableLocatorService.ts | 12 +- .../locators/services/condaEnvFileService.ts | 18 +- .../locators/services/condaEnvService.ts | 12 +- .../locators/services/condaHelper.ts | 6 +- .../locators/services/condaService.ts | 26 +- .../locators/services/currentPathService.ts | 22 +- .../services/globalVirtualEnvService.ts | 2 +- .../locators/services/pipEnvService.ts | 2 +- .../locators/services/pipEnvServiceHelper.ts | 8 +- .../services/windowsRegistryService.ts | 26 +- .../workspaceVirtualEnvWatcherService.ts | 4 +- src/client/interpreter/virtualEnvs/index.ts | 6 +- src/client/ioc/serviceManager.ts | 35 +- src/client/language/tokenizer.ts | 5 +- .../languageServices/jediProxyFactory.ts | 2 +- src/client/linters/bandit.ts | 2 +- src/client/linters/baseLinter.ts | 2 +- .../linters/errorHandlers/notInstalled.ts | 2 +- src/client/linters/flake8.ts | 2 +- src/client/linters/linterCommands.ts | 6 +- src/client/linters/linterManager.ts | 10 +- src/client/linters/lintingEngine.ts | 10 +- src/client/linters/mypy.ts | 2 +- src/client/linters/prospector.ts | 2 +- src/client/linters/pycodestyle.ts | 2 +- src/client/linters/pydocstyle.ts | 12 +- src/client/linters/pylama.ts | 2 +- src/client/linters/pylint.ts | 2 +- .../launchJsonCodeActionProvider.ts | 4 +- src/client/providers/completionSource.ts | 2 +- src/client/providers/definitionProvider.ts | 2 +- src/client/providers/formatProvider.ts | 4 +- src/client/providers/hoverProvider.ts | 2 +- src/client/providers/itemInfoSource.ts | 4 +- src/client/providers/jediProxy.ts | 54 +- src/client/providers/linterProvider.ts | 16 +- src/client/providers/referenceProvider.ts | 4 +- src/client/providers/renameProvider.ts | 8 +- src/client/providers/replProvider.ts | 2 +- src/client/providers/signatureProvider.ts | 10 +- .../providers/simpleRefactorProvider.ts | 22 +- src/client/providers/symbolProvider.ts | 10 +- src/client/providers/terminalProvider.ts | 2 +- src/client/refactor/proxy.ts | 12 +- src/client/sourceMapSupport.ts | 4 +- src/client/startupTelemetry.ts | 4 +- src/client/telemetry/importTracker.ts | 416 +- src/client/telemetry/index.ts | 14 +- .../codeExecution/codeExecutionManager.ts | 4 +- .../terminals/codeExecution/djangoContext.ts | 4 +- src/client/terminals/codeExecution/helper.ts | 2 +- .../codeExecution/terminalCodeExecution.ts | 2 +- src/client/testing/codeLenses/main.ts | 54 +- src/client/testing/codeLenses/testFiles.ts | 34 +- src/client/testing/common/argumentsHelper.ts | 10 +- src/client/testing/common/debugLauncher.ts | 4 +- .../testing/common/enablementTracker.ts | 6 +- .../common/managers/baseTestManager.ts | 20 +- .../managers/testConfigurationManager.ts | 18 +- src/client/testing/common/runner.ts | 8 +- .../testing/common/services/contextService.ts | 2 +- .../common/services/discoveredTestParser.ts | 54 +- .../testing/common/services/storageService.ts | 4 +- .../common/services/testManagerService.ts | 2 +- .../common/services/testResultsService.ts | 14 +- .../common/services/testsStatusService.ts | 18 +- .../services/workspaceTestManagerService.ts | 2 +- src/client/testing/common/testUtils.ts | 65 +- .../common/testVisitors/flatteningVisitor.ts | 8 +- .../testing/common/testVisitors/visitor.ts | 2 +- .../testing/common/updateTestSettings.ts | 6 +- src/client/testing/common/xUnitParser.ts | 10 +- src/client/testing/configuration.ts | 4 +- src/client/testing/display/main.ts | 10 +- src/client/testing/display/picker.ts | 22 +- .../testing/explorer/commandHandlers.ts | 2 +- .../testing/explorer/failedTestHandler.ts | 2 +- .../testing/explorer/testTreeViewProvider.ts | 6 +- src/client/testing/explorer/treeView.ts | 2 +- src/client/testing/main.ts | 22 +- .../testing/navigation/commandHandler.ts | 2 +- .../testing/navigation/symbolProvider.ts | 6 +- src/client/testing/nosetest/runner.ts | 8 +- .../testing/nosetest/services/argsService.ts | 18 +- .../nosetest/services/parserService.ts | 6 +- src/client/testing/pytest/runner.ts | 8 +- .../testing/pytest/services/argsService.ts | 6 +- .../pytest/services/discoveryService.ts | 2 +- .../pytest/testConfigurationManager.ts | 4 +- src/client/testing/serviceRegistry.ts | 4 +- src/client/testing/unittest/helper.ts | 10 +- src/client/testing/unittest/main.ts | 4 +- src/client/testing/unittest/runner.ts | 14 +- .../testing/unittest/services/argsService.ts | 4 +- .../unittest/services/parserService.ts | 10 +- src/client/testing/unittest/socketServer.ts | 6 +- .../typeFormatters/blockFormatProvider.ts | 2 +- .../typeFormatters/codeBlockFormatProvider.ts | 4 +- src/client/workspaceSymbols/generator.ts | 6 +- src/client/workspaceSymbols/main.ts | 10 +- src/client/workspaceSymbols/parser.ts | 4 +- src/client/workspaceSymbols/provider.ts | 12 +- src/datascience-ui/common/index.ts | 10 +- .../data-explorer/cellFormatter.tsx | 158 +- .../data-explorer/emptyRowsView.tsx | 30 +- src/datascience-ui/data-explorer/index.tsx | 56 +- .../data-explorer/mainPanel.tsx | 666 +- .../data-explorer/reactSlickGrid.tsx | 22 +- src/datascience-ui/data-explorer/testData.ts | 25036 ++++++++-------- src/datascience-ui/history-react/index.tsx | 90 +- .../history-react/redux/reducers/creation.ts | 2 +- .../history-react/redux/reducers/effects.ts | 10 +- .../interactive-common/cellOutput.tsx | 8 +- .../interactive-common/editor.tsx | 2 +- .../intellisenseProvider.ts | 8 +- .../interactive-common/mainState.ts | 2 +- .../interactive-common/redux/postOffice.ts | 2 +- .../redux/reducers/helpers.ts | 4 +- .../redux/reducers/monaco.ts | 2 +- .../redux/reducers/transfer.ts | 22 +- .../interactive-common/redux/store.ts | 16 +- .../interactive-common/tokenizer.ts | 2 +- .../interactive-common/transforms.tsx | 10 +- .../interactive-common/variableExplorer.tsx | 4 +- .../variableExplorerRowRenderer.tsx | 2 +- src/datascience-ui/ipywidgets/classicComm.ts | 4 +- src/datascience-ui/ipywidgets/container.tsx | 2 +- src/datascience-ui/ipywidgets/kernel.ts | 4 +- src/datascience-ui/ipywidgets/manager.ts | 2 +- .../native-editor/redux/reducers/creation.ts | 28 +- .../native-editor/redux/reducers/effects.ts | 16 +- .../native-editor/redux/reducers/execution.ts | 22 +- .../native-editor/redux/reducers/movement.ts | 12 +- src/datascience-ui/plot/mainPanel.tsx | 4 +- .../react-common/errorBoundary.tsx | 72 +- src/datascience-ui/react-common/event.ts | 2 +- .../react-common/locReactSide.ts | 40 +- .../react-common/monacoEditor.tsx | 14 +- .../react-common/monacoHelpers.ts | 4 +- src/datascience-ui/react-common/postOffice.ts | 232 +- src/datascience-ui/react-common/progress.tsx | 42 +- src/datascience-ui/react-common/reduxUtils.ts | 4 +- .../react-common/relativeImage.tsx | 68 +- .../react-common/styleInjector.tsx | 240 +- .../react-common/themeDetector.ts | 48 +- src/ipywidgets/src/embed.ts | 8 +- src/ipywidgets/src/index.ts | 2 +- src/ipywidgets/src/libembed.ts | 10 +- src/ipywidgets/src/manager.ts | 4 +- src/ipywidgets/src/signal.ts | 2 +- src/test/activation/aaTesting.unit.test.ts | 62 +- .../activation/activationManager.unit.test.ts | 64 +- .../activation/activationService.unit.test.ts | 418 +- .../activation/activeResource.unit.test.ts | 202 +- .../activation/extensionSurvey.unit.test.ts | 92 +- .../analysisOptions.unit.test.ts | 14 +- .../downloadChannelRules.unit.test.ts | 8 +- .../languageServer/downloader.unit.test.ts | 44 +- .../languageServer.unit.test.ts | 110 +- ...ageServerCompatibilityService.unit.test.ts | 6 +- .../languageServerExtension.unit.test.ts | 2 +- .../languageServerFolderService.unit.test.ts | 540 +- .../languageServerPackageService.test.ts | 16 +- .../languageServerPackageService.unit.test.ts | 526 +- .../languageServer/manager.unit.test.ts | 2 +- .../languageServer/outputChannel.unit.test.ts | 240 +- .../languageServer/platformData.unit.test.ts | 20 +- .../applicationDiagnostics.unit.test.ts | 42 +- .../checks/envPathVariable.unit.test.ts | 76 +- .../invalidLaunchJsonDebugger.unit.test.ts | 104 +- .../invalidPythonPathInDebugger.unit.test.ts | 82 +- .../checks/lsNotSupported.unit.test.ts | 30 +- .../checks/macPythonInterpreter.unit.test.ts | 116 +- .../checks/powerShellActivation.unit.test.ts | 40 +- .../checks/pythonInterpreter.unit.test.ts | 68 +- .../checks/updateTestSettings.unit.test.ts | 12 +- .../commands/execVSCCommands.unit.test.ts | 4 +- .../diagnostics/commands/ignore.unit.test.ts | 6 +- .../commands/launchBrowser.unit.test.ts | 4 +- .../diagnostics/filter.unit.test.ts | 38 +- .../diagnostics/promptHandler.unit.test.ts | 24 +- src/test/common.ts | 10 +- src/test/common/asyncDump.ts | 18 +- src/test/common/configSettings.test.ts | 4 +- .../configSettings.pythonPath.unit.test.ts | 12 +- .../configSettings.unit.test.ts | 54 +- src/test/common/crypto.unit.test.ts | 288 +- .../dotnet/compatibilityService.unit.test.ts | 4 +- .../dotnet/serviceRegistry.unit.test.ts | 124 +- src/test/common/exitCIAfterTestReporter.ts | 2 +- src/test/common/experiments.unit.test.ts | 122 +- .../featureDeprecationManager.unit.test.ts | 26 +- src/test/common/helpers.test.ts | 2 +- .../downloadChannelRules.unit.test.ts | 28 +- .../downloadChannelService.unit.test.ts | 18 +- .../insidersExtensionPrompt.unit.test.ts | 310 +- .../insidersExtensionService.unit.test.ts | 1134 +- src/test/common/installer.test.ts | 8 +- .../installer/channelManager.unit.test.ts | 104 +- .../extensionBuildInstaller.unit.test.ts | 200 +- .../installer.invalidPath.unit.test.ts | 36 +- .../common/installer/installer.unit.test.ts | 172 +- .../installer/moduleInstaller.unit.test.ts | 122 +- .../installer/pipEnvInstaller.unit.test.ts | 156 +- .../installer/pipInstaller.unit.test.ts | 260 +- .../common/installer/productPath.unit.test.ts | 42 +- .../installer/serviceRegistry.unit.test.ts | 234 +- src/test/common/moduleInstaller.test.ts | 72 +- .../common/net/fileDownloader.unit.test.ts | 4 +- src/test/common/net/httpClient.unit.test.ts | 10 +- ...azureBobStoreRepository.functional.test.ts | 136 +- .../azureBobStoreRepository.unit.test.ts | 14 +- .../common/nuget/nugetRepository.unit.test.ts | 134 +- .../platform/filesystem.functional.test.ts | 24 +- src/test/common/platform/filesystem.test.ts | 42 +- .../common/platform/filesystem.unit.test.ts | 330 +- .../common/platform/fs-paths.unit.test.ts | 6 +- .../platform/fs-temp.functional.test.ts | 2 +- src/test/common/platform/fs-temp.unit.test.ts | 4 +- .../common/platform/platformService.test.ts | 4 +- .../platform/serviceRegistry.unit.test.ts | 56 +- src/test/common/platform/utils.ts | 4 +- .../condaExecutionService.unit.test.ts | 6 +- src/test/common/process/decoder.test.ts | 2 +- src/test/common/process/execFactory.test.ts | 38 +- src/test/common/process/logger.unit.test.ts | 4 +- src/test/common/process/proc.exec.test.ts | 36 +- .../common/process/proc.observable.test.ts | 36 +- src/test/common/process/proc.unit.test.ts | 4 +- .../process/processFactory.unit.test.ts | 4 +- .../process/pythonDaemon.functional.test.ts | 10 +- .../pythonDaemonPool.functional.test.ts | 26 +- .../process/pythonDaemonPool.unit.test.ts | 2 +- .../pythonExecutionFactory.unit.test.ts | 12 +- .../pythonProc.simple.multiroot.test.ts | 6 +- .../common/process/pythonProcess.unit.test.ts | 52 +- .../process/serviceRegistry.unit.test.ts | 88 +- src/test/common/serviceRegistry.unit.test.ts | 372 +- src/test/common/socketStream.test.ts | 24 +- .../terminals/activation.bash.unit.test.ts | 18 +- .../activation.commandPrompt.unit.test.ts | 66 +- .../terminals/activation.conda.unit.test.ts | 150 +- .../terminals/activator/base.unit.test.ts | 26 +- .../terminals/activator/index.unit.test.ts | 6 +- .../powerShellFailedHandler.unit.test.ts | 16 +- .../terminals/commandPrompt.unit.test.ts | 8 +- .../pipEnvActivationProvider.unit.test.ts | 6 +- .../common/terminals/factory.unit.test.ts | 22 +- src/test/common/terminals/helper.unit.test.ts | 16 +- .../pyenvActivationProvider.unit.test.ts | 10 +- .../common/terminals/service.unit.test.ts | 104 +- .../terminals/serviceRegistry.unit.test.ts | 128 +- .../terminals/shellDetector.unit.test.ts | 2 +- .../shellDetectors.unit.test.ts | 4 +- .../synchronousTerminalService.unit.test.ts | 4 +- src/test/common/utils/async.unit.test.ts | 10 +- src/test/common/utils/cacheUtils.unit.test.ts | 2 +- src/test/common/utils/decorators.unit.test.ts | 6 +- .../common/utils/localize.functional.test.ts | 454 +- .../envVarsProvider.multiroot.test.ts | 8 +- .../variables/envVarsService.unit.test.ts | 16 +- .../environmentVariablesProvider.unit.test.ts | 32 +- .../variables/serviceRegistry.unit.test.ts | 72 +- .../common/webPanel/webPanel.unit.test.ts | 12 +- .../interpreterSelector.unit.test.ts | 48 +- src/test/constants.ts | 84 +- src/test/core.ts | 2 +- src/test/datascience/color.test.ts | 354 +- .../commands/commandRegistry.unit.test.ts | 2 +- .../datascience/dataScienceIocContainer.ts | 2994 +- .../datascienceSurveyBanner.unit.test.ts | 32 +- .../dataviewer.functional.test.tsx | 482 +- .../datascience/debugger.functional.test.tsx | 14 +- .../cellhashprovider.unit.test.ts | 14 +- .../codelensprovider.unit.test.ts | 56 +- .../codewatcher.unit.test.ts | 70 +- .../gotocell.functional.test.ts | 10 +- .../datascience/editor-integration/helpers.ts | 144 +- .../errorHandler.functional.test.tsx | 4 +- .../datascience/errorHandler.unit.test.ts | 10 +- src/test/datascience/execution.unit.test.ts | 2442 +- src/test/datascience/executionServiceMock.ts | 166 +- .../datascience/gather/gather.unit.test.ts | 12 +- .../intellisense.functional.test.tsx | 14 +- .../datascience/intellisense.unit.test.ts | 18 +- .../nativeEditorProvider.unit.test.ts | 32 +- .../nativeEditorStorage.unit.test.ts | 14 +- .../interactiveWindow.functional.test.tsx | 2169 +- ...eractiveWindowCommandListener.unit.test.ts | 18 +- .../interactiveWindowTestHelpers.tsx | 2 +- .../jupyterInterpreterService.unit.test.ts | 2 +- ...yterCellOutputMimeTypeTracker.unit.test.ts | 14 +- .../jupyter/jupyterSession.unit.test.ts | 10 +- .../kernels/kernelSelections.unit.test.ts | 6 +- .../kernels/kernelSelector.unit.test.ts | 8 +- .../kernels/kernelSwitcher.unit.test.ts | 6 +- .../jupyter/serverSelector.unit.test.ts | 12 +- .../jupyterPasswordConnect.unit.test.ts | 42 +- .../datascience/jupyterUtils.unit.test.ts | 2 +- .../datascience/jupyterVariables.unit.test.ts | 18 +- .../datascience/liveshare.functional.test.tsx | 776 +- src/test/datascience/mockCommandManager.ts | 110 +- .../datascience/mockCustomEditorService.ts | 10 +- src/test/datascience/mockDebugService.ts | 8 +- src/test/datascience/mockDocumentManager.ts | 280 +- src/test/datascience/mockExtensions.ts | 42 +- src/test/datascience/mockJupyterManager.ts | 1756 +- src/test/datascience/mockJupyterRequest.ts | 702 +- src/test/datascience/mockJupyterSession.ts | 568 +- src/test/datascience/mockLanguageClient.ts | 2 +- src/test/datascience/mockLanguageServer.ts | 2 +- src/test/datascience/mockLiveShare.ts | 888 +- src/test/datascience/mockProcessService.ts | 216 +- .../datascience/mockProtocol2CodeConverter.ts | 2 +- src/test/datascience/mockPythonService.ts | 166 +- src/test/datascience/mockQuickPick.ts | 2 +- src/test/datascience/mockTextEditor.ts | 2 +- src/test/datascience/mockWorkspaceConfig.ts | 2 +- .../nativeEditor.functional.test.tsx | 165 +- .../nativeEditor.toolbar.functional.test.tsx | 2 +- .../datascience/nativeEditorTestHelpers.tsx | 2 +- .../datascience/notebook.functional.test.ts | 3056 +- .../plotViewer.functional.test.tsx | 4 +- src/test/datascience/reactHelpers.ts | 10 +- .../datascience/shiftEnterBanner.unit.test.ts | 20 +- src/test/datascience/testHelpers.tsx | 9 +- .../datascience/testPersistentStateFactory.ts | 4 +- src/test/datascience/uiTests/helpers.ts | 2 +- .../uiTests/ipywidget.ui.functional.test.ts | 22 +- .../datascience/uiTests/notebookHelpers.ts | 4 +- src/test/datascience/uiTests/notebookUi.ts | 4 +- src/test/datascience/uiTests/recorder.ts | 4 +- .../datascience/uiTests/webBrowserPanel.ts | 14 +- .../variableexplorer.functional.test.tsx | 12 +- src/test/debugger/attach.ptvsd.test.ts | 14 +- src/test/debugger/capabilities.test.ts | 14 +- .../common/debugStreamProvider.test.ts | 10 +- .../debugger/common/protocoloLogger.test.ts | 20 +- .../debugger/common/protocolparser.test.ts | 8 +- .../launcherProvider.unit.test.ts | 4 +- .../debugAdapter/serviceRegistry.unit.test.ts | 114 +- src/test/debugger/envVars.test.ts | 10 +- .../extension/adapter/logging.unit.test.ts | 2 +- .../outdatedDebuggerPrompt.unit.test.ts | 2 +- .../debugger/extension/banner.unit.test.ts | 110 +- .../debugConfigurationService.unit.test.ts | 18 +- .../completionProvider.unit.test.ts | 20 +- .../launch.json/updaterServer.unit.test.ts | 70 +- .../providers/providerFactory.unit.test.ts | 4 +- .../resolvers/attach.unit.test.ts | 66 +- .../configuration/resolvers/base.unit.test.ts | 14 +- .../configuration/resolvers/common.ts | 8 +- .../resolvers/launch.unit.test.ts | 56 +- .../launchConfigExperiments.unit.test.ts | 12 +- src/test/debugger/misc.test.ts | 8 +- src/test/debugger/portAndHost.test.ts | 10 +- src/test/debugger/run.test.ts | 4 +- src/test/debugger/utils.ts | 4 +- src/test/debuggerTest.ts | 2 +- src/test/extension-version.functional.test.ts | 8 +- src/test/format/extension.dispatch.test.ts | 2 +- src/test/format/extension.format.test.ts | 16 +- .../format/extension.lineFormatter.test.ts | 22 +- .../format/extension.onTypeFormat.test.ts | 36 +- src/test/format/extension.sort.test.ts | 14 +- src/test/format/format.helper.test.ts | 14 +- src/test/format/formatter.unit.test.ts | 8 +- src/test/index.ts | 6 +- src/test/initialize.ts | 2 +- .../install/channelManager.channels.test.ts | 14 +- .../install/channelManager.messages.test.ts | 32 +- .../activation/service.unit.test.ts | 12 +- ...lEnvironmentActivationService.unit.test.ts | 4 +- ...rEnvironmentActivationService.unit.test.ts | 12 +- .../autoSelection/proxy.unit.test.ts | 2 +- .../autoSelection/rules/cached.unit.test.ts | 4 +- .../rules/currentPath.unit.test.ts | 4 +- .../rules/winRegistry.unit.test.ts | 4 +- .../rules/workspaceEnv.unit.test.ts | 12 +- .../condaEnvFileService.unit.test.ts | 40 +- .../interpreters/condaEnvService.unit.test.ts | 120 +- .../interpreters/condaService.unit.test.ts | 290 +- .../currentPathService.unit.test.ts | 48 +- src/test/interpreters/display.unit.test.ts | 76 +- src/test/interpreters/helpers.unit.test.ts | 36 +- .../interpreterService.unit.test.ts | 102 +- .../interpreterVersion.unit.test.ts | 10 +- .../knownPathService.unit.test.ts | 42 +- .../locators/helpers.unit.test.ts | 42 +- .../interpreters/locators/index.unit.test.ts | 60 +- ...terpreterLocatorService.testvirtualenvs.ts | 188 +- .../workspaceVirtualEnvService.test.ts | 8 +- ...spaceVirtualEnvWatcherService.unit.test.ts | 4 +- src/test/interpreters/mocks.ts | 4 +- .../interpreters/pipEnvService.unit.test.ts | 96 +- .../interpreters/pythonPathUpdater.test.ts | 36 +- .../interpreters/serviceRegistry.unit.test.ts | 2 +- src/test/interpreters/venv.unit.test.ts | 38 +- .../virtualEnvManager.unit.test.ts | 38 +- .../condaInheritEnvPrompt.unit.test.ts | 1048 +- .../virtualEnvs/index.unit.test.ts | 70 +- .../virtualEnvs/virtualEnvPrompt.unit.test.ts | 14 +- .../windowsRegistryService.unit.test.ts | 51 +- src/test/language/braceCounter.unit.test.ts | 4 +- .../languageConfiguration.unit.test.ts | 4 +- src/test/language/tokenizer.unit.test.ts | 10 +- .../jedi/autocomplete/base.test.ts | 70 +- .../jedi/autocomplete/pep484.test.ts | 12 +- .../jedi/autocomplete/pep526.test.ts | 26 +- .../jedi/completionSource.unit.test.ts | 28 +- .../jedi/definitions/hover.jedi.test.ts | 88 +- .../jedi/definitions/parallel.jedi.test.ts | 2 +- .../jedi/pythonSignatureProvider.unit.test.ts | 34 +- .../jedi/signature/signature.jedi.test.ts | 2 +- .../jedi/symbolProvider.unit.test.ts | 44 +- src/test/linters/common.ts | 48 +- src/test/linters/lint.args.test.ts | 22 +- src/test/linters/lint.functional.test.ts | 43 +- src/test/linters/lint.manager.unit.test.ts | 4 +- src/test/linters/lint.multilinter.test.ts | 6 +- src/test/linters/lint.multiroot.test.ts | 4 +- src/test/linters/lint.provider.test.ts | 64 +- src/test/linters/lint.test.ts | 6 +- src/test/linters/lint.unit.test.ts | 26 +- src/test/linters/lintengine.test.ts | 50 +- .../linters/linter.availability.unit.test.ts | 40 +- src/test/linters/linterManager.unit.test.ts | 32 +- src/test/linters/linterinfo.unit.test.ts | 2 +- src/test/linters/pylint.test.ts | 64 +- src/test/linters/pylint.unit.test.ts | 144 +- src/test/linters/serviceRegistry.unit.test.ts | 70 +- src/test/mocks/proc.ts | 2 +- src/test/mocks/vsc/arrays.ts | 10 +- src/test/mocks/vsc/extHostedTypes.ts | 4 +- src/test/multiRootTest.ts | 2 +- src/test/performance/load.perf.test.ts | 13 +- src/test/performanceTest.ts | 4 +- .../launchJsonCodeActionProvider.unit.test.ts | 12 +- .../pythonCodeActionsProvider.unit.test.ts | 2 +- src/test/providers/foldingProvider.test.ts | 6 +- .../providers/importSortProvider.unit.test.ts | 126 +- src/test/providers/repl.unit.test.ts | 24 +- .../providers/serviceRegistry.unit.test.ts | 74 +- .../shebangCodeLenseProvider.unit.test.ts | 38 +- src/test/providers/terminal.unit.test.ts | 62 +- .../extension.refactor.extract.method.test.ts | 4 +- .../extension.refactor.extract.var.test.ts | 6 +- src/test/refactor/rename.test.ts | 30 +- src/test/serviceRegistry.ts | 10 +- src/test/smoke/common.ts | 4 +- src/test/smoke/datascience.smoke.test.ts | 2 +- src/test/smoke/debugger.smoke.test.ts | 2 +- src/test/smoke/languageServer.smoke.test.ts | 2 +- src/test/smoke/runInTerminal.smoke.test.ts | 2 +- src/test/smokeTest.ts | 4 +- src/test/sourceMapSupport.test.ts | 2 +- src/test/sourceMapSupport.unit.test.ts | 2 +- src/test/standardTest.ts | 2 +- src/test/telemetry/importTracker.unit.test.ts | 599 +- src/test/telemetry/index.unit.test.ts | 4 +- src/test/terminals/activation.unit.test.ts | 14 +- .../codeExecutionManager.unit.test.ts | 76 +- .../djangoShellCodeExect.unit.test.ts | 34 +- .../terminals/codeExecution/helper.test.ts | 134 +- .../terminalCodeExec.unit.test.ts | 163 +- .../terminals/serviceRegistry.unit.test.ts | 10 +- src/test/testBootstrap.ts | 12 +- src/test/testRunner.ts | 204 +- src/test/testing/argsService.test.ts | 18 +- .../banners/languageServerSurvey.unit.test.ts | 28 +- ...roposeNewLanguageServerBanner.unit.test.ts | 12 +- .../testing/codeLenses/testFiles.unit.test.ts | 314 +- .../testing/common/debugLauncher.unit.test.ts | 69 +- .../managers/baseTestManager.unit.test.ts | 8 +- .../testConfigurationManager.unit.test.ts | 16 +- .../configSettingService.unit.test.ts | 58 +- .../discoveredTestParser.unit.test.ts | 240 +- .../common/services/discovery.unit.test.ts | 2 +- .../services/testStatusService.unit.test.ts | 18 +- .../testing/common/testUtils.unit.test.ts | 12 +- .../resultResetVisitor.unit.test.ts | 224 +- .../common/trackEnablement.unit.test.ts | 2 +- .../testing/common/xUnitParser.unit.test.ts | 8 +- src/test/testing/configuration.unit.test.ts | 154 +- .../testing/configurationFactory.unit.test.ts | 6 +- src/test/testing/debugger.test.ts | 14 +- src/test/testing/display/main.unit.test.ts | 176 +- .../testing/display/picker.functional.test.ts | 4 +- src/test/testing/display/picker.unit.test.ts | 8 +- src/test/testing/explorer/explorerTestData.ts | 14 +- .../testExplorerCommandHandler.unit.test.ts | 6 +- .../testTreeViewProvider.unit.test.ts | 6 +- .../testing/explorer/treeView.unit.test.ts | 2 +- src/test/testing/helper.ts | 4 +- src/test/testing/mocks.ts | 2 +- .../navigation/commandHandlers.unit.test.ts | 6 +- .../navigation/functionNavigator.unit.test.ts | 4 +- .../navigation/suiteNavigator.unit.test.ts | 6 +- .../navigation/symbolNavigator.unit.test.ts | 28 +- .../nosetest.argsService.unit.test.ts | 2 +- .../nosetest/nosetest.discovery.unit.test.ts | 28 +- .../nosetest/nosetest.disovery.test.ts | 6 +- .../testing/nosetest/nosetest.run.test.ts | 12 +- src/test/testing/nosetest/nosetest.test.ts | 4 +- .../pytest/pytest.argsService.unit.test.ts | 6 +- .../testing/pytest/pytest.discovery.test.ts | 34 +- src/test/testing/pytest/pytest.run.test.ts | 20 +- src/test/testing/pytest/pytest.test.ts | 4 +- .../pytest/pytest.testMessageService.test.ts | 10 +- .../testing/pytest/pytest_run_tests_data.ts | 8 +- .../services/discoveryService.unit.test.ts | 2 +- src/test/testing/rediscover.test.ts | 2 +- src/test/testing/serviceRegistry.ts | 4 +- .../testing/stoppingDiscoverAndTest.test.ts | 8 +- .../unittest.argsService.unit.test.ts | 2 +- .../unittest/unittest.discovery.test.ts | 18 +- .../unittest/unittest.discovery.unit.test.ts | 176 +- .../testing/unittest/unittest.run.test.ts | 12 +- src/test/testing/unittest/unittest.test.ts | 16 +- src/test/unittests.ts | 168 +- src/test/vscode-mock.ts | 14 +- .../workspaceSymbols/generator.unit.test.ts | 12 +- .../workspaceSymbols/provider.unit.test.ts | 10 +- 751 files changed, 43037 insertions(+), 43023 deletions(-) create mode 100644 news/3 Code Health/10837.md diff --git a/.prettierrc.js b/.prettierrc.js index 9c5f4642ba85..c55335249956 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -2,6 +2,7 @@ module.exports = { singleQuote: true, printWidth: 120, tabWidth: 4, + trailingComma: 'none', overrides: [ { files: ['*.yml', '*.yaml'], diff --git a/build/ci/performance/checkPerformanceResults.js b/build/ci/performance/checkPerformanceResults.js index feccdc6be711..426e84fb677e 100644 --- a/build/ci/performance/checkPerformanceResults.js +++ b/build/ci/performance/checkPerformanceResults.js @@ -24,13 +24,13 @@ fs.readFile(performanceResultsFile, 'utf8', (performanceResultsFileError, perfor const benchmarkJson = JSON.parse(benchmark); const performanceJson = JSON.parse(performanceData); - performanceJson.forEach(result => { - const cleanTimes = result.times.filter(x => x !== -1); + performanceJson.forEach((result) => { + const cleanTimes = result.times.filter((x) => x !== -1); const avg = cleanTimes.length === 0 ? 999 : cleanTimes.reduce((a, b) => parseFloat(a) + parseFloat(b)) / cleanTimes.length; - const testcase = benchmarkJson.find(x => x.name === result.name); + const testcase = benchmarkJson.find((x) => x.name === result.name); // compare the average result to the base JSON if (testcase && testcase.time !== -1 && avg > parseFloat(testcase.time) + errorMargin) { @@ -46,7 +46,7 @@ fs.readFile(performanceResultsFile, 'utf8', (performanceResultsFileError, perfor }); // Delete performance-results.json - fs.unlink(performanceResultsFile, deleteError => { + fs.unlink(performanceResultsFile, (deleteError) => { if (deleteError) { if (failedTests.length > 0) { console.log(failedTests); diff --git a/build/ci/performance/createNewPerformanceBenchmark.js b/build/ci/performance/createNewPerformanceBenchmark.js index e5f6e6b5025e..e2d10275bac6 100644 --- a/build/ci/performance/createNewPerformanceBenchmark.js +++ b/build/ci/performance/createNewPerformanceBenchmark.js @@ -21,7 +21,7 @@ fs.readFile(xmlFile, 'utf8', (xmlReadError, xmlData) => { }; const jsonObj = fastXmlParser.parse(xmlData, defaultOptions); - jsonObj.testsuite.testcase.forEach(testcase => { + jsonObj.testsuite.testcase.forEach((testcase) => { const test = { name: testcase.classname + ' ' + testcase.name, time: testcase.failure || testcase.skipped === '' ? -1 : parseFloat(testcase.time) @@ -33,7 +33,7 @@ fs.readFile(xmlFile, 'utf8', (xmlReadError, xmlData) => { fs.writeFile( path.join(constants.ExtensionRootDir, 'build', 'ci', 'performance', 'DS_test_benchmark.json'), JSON.stringify(performanceData, null, 2), - writeResultsError => { + (writeResultsError) => { if (writeResultsError) { throw writeResultsError; } diff --git a/build/ci/performance/savePerformanceResults.js b/build/ci/performance/savePerformanceResults.js index 11693a91ffeb..9052c981d14a 100644 --- a/build/ci/performance/savePerformanceResults.js +++ b/build/ci/performance/savePerformanceResults.js @@ -25,7 +25,7 @@ fs.readFile(xmlFile, 'utf8', (xmlReadError, xmlData) => { fs.readFile(jsonFile, 'utf8', (jsonReadError, data) => { if (jsonReadError) { // File doesn't exist, so we create it - jsonObj.testsuite.testcase.forEach(testcase => { + jsonObj.testsuite.testcase.forEach((testcase) => { const test = { name: testcase.classname + ' ' + testcase.name, times: [testcase.failure || testcase.skipped === '' ? -1 : parseFloat(testcase.time)] @@ -36,8 +36,8 @@ fs.readFile(xmlFile, 'utf8', (xmlReadError, xmlData) => { } else { performanceData = JSON.parse(data); - jsonObj.testsuite.testcase.forEach(testcase => { - let test = performanceData.find(x => x.name === testcase.classname + ' ' + testcase.name); + jsonObj.testsuite.testcase.forEach((testcase) => { + let test = performanceData.find((x) => x.name === testcase.classname + ' ' + testcase.name); let time = testcase.failure || testcase.skipped === '' ? -1 : parseFloat(testcase.time); if (test) { @@ -58,7 +58,7 @@ fs.readFile(xmlFile, 'utf8', (xmlReadError, xmlData) => { fs.writeFile( path.join(constants.ExtensionRootDir, 'build', 'ci', 'performance', 'performance-results.json'), JSON.stringify(performanceData, null, 2), - writeResultsError => { + (writeResultsError) => { if (writeResultsError) { throw writeResultsError; } diff --git a/build/tslint-rules/messagesMustBeLocalizedRule.js b/build/tslint-rules/messagesMustBeLocalizedRule.js index 76c892de9653..acf4beaba811 100644 --- a/build/tslint-rules/messagesMustBeLocalizedRule.js +++ b/build/tslint-rules/messagesMustBeLocalizedRule.js @@ -26,8 +26,8 @@ class NoStringLiteralsInMessages extends baseRuleWalker.BaseRuleWalker { visitCallExpression(node) { if (!this.shouldIgnoreNode(node)) { node.arguments - .filter(arg => ts.isStringLiteral(arg) || ts.isTemplateLiteral(arg)) - .forEach(arg => { + .filter((arg) => ts.isStringLiteral(arg) || ts.isTemplateLiteral(arg)) + .forEach((arg) => { this.addFailureAtNode(arg, failureMessage); }); } diff --git a/build/util.js b/build/util.js index 8a5ce3daf7d5..93a60e7fb19b 100644 --- a/build/util.js +++ b/build/util.js @@ -12,7 +12,7 @@ function getListOfFiles(filename) { } const data = fs.readFileSync(filename).toString(); const files = JSON.parse(data); - return files.map(file => { + return files.map((file) => { return path.join(exports.ExtensionRootDir, file.replace(/\//g, path.sep)); }); } diff --git a/build/webpack/common.js b/build/webpack/common.js index 8150caebf015..d189d65e26a2 100644 --- a/build/webpack/common.js +++ b/build/webpack/common.js @@ -1,61 +1,61 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -const glob = require('glob'); -const path = require('path'); -const webpack_bundle_analyzer = require('webpack-bundle-analyzer'); -const constants = require('../constants'); -exports.nodeModulesToExternalize = [ - 'unicode/category/Lu', - 'unicode/category/Ll', - 'unicode/category/Lt', - 'unicode/category/Lo', - 'unicode/category/Lm', - 'unicode/category/Nl', - 'unicode/category/Mn', - 'unicode/category/Mc', - 'unicode/category/Nd', - 'unicode/category/Pc', - '@jupyterlab/services', - 'azure-storage', - 'request', - 'request-progress', - 'source-map-support', - 'diff-match-patch', - 'sudo-prompt', - 'node-stream-zip', - 'xml2js', - 'vsls/vscode', - 'pdfkit', - 'crypto-js', - 'fontkit', - 'linebreak', - 'png-js', - '@koa/cors', - 'koa', - 'koa-compress', - 'koa-logger', - 'zeromq' -]; -exports.nodeModulesToReplacePaths = [...exports.nodeModulesToExternalize]; -function getDefaultPlugins(name) { - const plugins = []; - plugins.push( - new webpack_bundle_analyzer.BundleAnalyzerPlugin({ - analyzerMode: 'static', - reportFilename: `${name}.analyzer.html`, - generateStatsFile: true, - statsFilename: `${name}.stats.json`, - openAnalyzer: false // Open file manually if you want to see it :) - }) - ); - return plugins; -} -exports.getDefaultPlugins = getDefaultPlugins; -function getListOfExistingModulesInOutDir() { - const outDir = path.join(constants.ExtensionRootDir, 'out', 'client'); - const files = glob.sync('**/*.js', { sync: true, cwd: outDir }); - return files.map(filePath => `./${filePath.slice(0, -3)}`); -} -exports.getListOfExistingModulesInOutDir = getListOfExistingModulesInOutDir; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +const glob = require('glob'); +const path = require('path'); +const webpack_bundle_analyzer = require('webpack-bundle-analyzer'); +const constants = require('../constants'); +exports.nodeModulesToExternalize = [ + 'unicode/category/Lu', + 'unicode/category/Ll', + 'unicode/category/Lt', + 'unicode/category/Lo', + 'unicode/category/Lm', + 'unicode/category/Nl', + 'unicode/category/Mn', + 'unicode/category/Mc', + 'unicode/category/Nd', + 'unicode/category/Pc', + '@jupyterlab/services', + 'azure-storage', + 'request', + 'request-progress', + 'source-map-support', + 'diff-match-patch', + 'sudo-prompt', + 'node-stream-zip', + 'xml2js', + 'vsls/vscode', + 'pdfkit', + 'crypto-js', + 'fontkit', + 'linebreak', + 'png-js', + '@koa/cors', + 'koa', + 'koa-compress', + 'koa-logger', + 'zeromq' +]; +exports.nodeModulesToReplacePaths = [...exports.nodeModulesToExternalize]; +function getDefaultPlugins(name) { + const plugins = []; + plugins.push( + new webpack_bundle_analyzer.BundleAnalyzerPlugin({ + analyzerMode: 'static', + reportFilename: `${name}.analyzer.html`, + generateStatsFile: true, + statsFilename: `${name}.stats.json`, + openAnalyzer: false // Open file manually if you want to see it :) + }) + ); + return plugins; +} +exports.getDefaultPlugins = getDefaultPlugins; +function getListOfExistingModulesInOutDir() { + const outDir = path.join(constants.ExtensionRootDir, 'out', 'client'); + const files = glob.sync('**/*.js', { sync: true, cwd: outDir }); + return files.map((filePath) => `./${filePath.slice(0, -3)}`); +} +exports.getListOfExistingModulesInOutDir = getListOfExistingModulesInOutDir; diff --git a/build/webpack/loaders/externalizeDependencies.js b/build/webpack/loaders/externalizeDependencies.js index 654da53163ea..1fe6f0d10487 100644 --- a/build/webpack/loaders/externalizeDependencies.js +++ b/build/webpack/loaders/externalizeDependencies.js @@ -1,20 +1,20 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const common = require('../common'); -function replaceModule(contents, moduleName, quotes) { - const stringToSearch = `${quotes}${moduleName}${quotes}`; - const stringToReplaceWith = `${quotes}./node_modules/${moduleName}${quotes}`; - return contents.replace(new RegExp(stringToSearch, 'gm'), stringToReplaceWith); -} -// tslint:disable:no-default-export no-invalid-this -function default_1(source) { - common.nodeModulesToReplacePaths.forEach(moduleName => { - if (source.indexOf(moduleName) > 0) { - source = replaceModule(source, moduleName, '"'); - source = replaceModule(source, moduleName, "'"); - } - }); - return source; -} -exports.default = default_1; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +const common = require('../common'); +function replaceModule(contents, moduleName, quotes) { + const stringToSearch = `${quotes}${moduleName}${quotes}`; + const stringToReplaceWith = `${quotes}./node_modules/${moduleName}${quotes}`; + return contents.replace(new RegExp(stringToSearch, 'gm'), stringToReplaceWith); +} +// tslint:disable:no-default-export no-invalid-this +function default_1(source) { + common.nodeModulesToReplacePaths.forEach((moduleName) => { + if (source.indexOf(moduleName) > 0) { + source = replaceModule(source, moduleName, '"'); + source = replaceModule(source, moduleName, "'"); + } + }); + return source; +} +exports.default = default_1; diff --git a/build/webpack/loaders/fixNodeFetch.js b/build/webpack/loaders/fixNodeFetch.js index 512fdc05db64..0d648d131349 100644 --- a/build/webpack/loaders/fixNodeFetch.js +++ b/build/webpack/loaders/fixNodeFetch.js @@ -28,7 +28,7 @@ const nodeFetchFile = constants.isWindows ? nodeFetchIndexFile.replace(/\\/g, '\ * @param {string} source * @returns */ -exports.default = function(source) { +exports.default = function (source) { if (source.indexOf("require('node-fetch')") > 0) { source = source.replace(/require\('node-fetch'\)/g, `require('${nodeFetchFile}')`); } diff --git a/build/webpack/loaders/jsonloader.js b/build/webpack/loaders/jsonloader.js index b9bbcce75400..5ec3c7038681 100644 --- a/build/webpack/loaders/jsonloader.js +++ b/build/webpack/loaders/jsonloader.js @@ -1,6 +1,6 @@ // For some reason this has to be in commonjs format -module.exports = function(source) { +module.exports = function (source) { // Just inline the source and fix up defaults so that they don't // mess up the logic in the setOptions.js file return `module.exports = ${source}\nmodule.exports.default = false`; diff --git a/build/webpack/loaders/remarkLoader.js b/build/webpack/loaders/remarkLoader.js index b9bbcce75400..5ec3c7038681 100644 --- a/build/webpack/loaders/remarkLoader.js +++ b/build/webpack/loaders/remarkLoader.js @@ -1,6 +1,6 @@ // For some reason this has to be in commonjs format -module.exports = function(source) { +module.exports = function (source) { // Just inline the source and fix up defaults so that they don't // mess up the logic in the setOptions.js file return `module.exports = ${source}\nmodule.exports.default = false`; diff --git a/build/webpack/webpack.extension.dependencies.config.js b/build/webpack/webpack.extension.dependencies.config.js index 75cdf50a9c37..3ef870b67fd3 100644 --- a/build/webpack/webpack.extension.dependencies.config.js +++ b/build/webpack/webpack.extension.dependencies.config.js @@ -1,67 +1,67 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -// tslint:disable-next-line: no-require-imports -const copyWebpackPlugin = require('copy-webpack-plugin'); -const path = require('path'); -const constants = require('../constants'); -const common = require('./common'); -const entryItems = {}; -common.nodeModulesToExternalize.forEach(moduleName => { - entryItems[`node_modules/${moduleName}`] = `./node_modules/${moduleName}`; -}); -const config = { - mode: 'production', - target: 'node', - context: constants.ExtensionRootDir, - entry: entryItems, - devtool: 'source-map', - node: { - __dirname: false - }, - module: { - rules: [ - { - // JupyterServices imports node-fetch. - test: /@jupyterlab[\\\/]services[\\\/].*js$/, - use: [ - { - loader: path.join(__dirname, 'loaders', 'fixNodeFetch.js') - } - ] - }, - { enforce: 'post', test: /unicode-properties[\/\\]index.js$/, loader: 'transform-loader?brfs' }, - { enforce: 'post', test: /fontkit[\/\\]index.js$/, loader: 'transform-loader?brfs' }, - { enforce: 'post', test: /linebreak[\/\\]src[\/\\]linebreaker.js/, loader: 'transform-loader?brfs' } - ] - }, - externals: ['vscode', 'commonjs'], - plugins: [ - ...common.getDefaultPlugins('dependencies'), - // vsls requires our package.json to be next to node_modules. It's how they - // 'find' the calling extension. - new copyWebpackPlugin([{ from: './package.json', to: '.' }]), - // onigasm requires our onigasm.wasm to be in node_modules - new copyWebpackPlugin([ - { from: './node_modules/onigasm/lib/onigasm.wasm', to: './node_modules/onigasm/lib/onigasm.wasm' } - ]) - ], - resolve: { - alias: { - // Pointing pdfkit to a dummy js file so webpack doesn't fall over. - // Since pdfkit has been externalized (it gets updated with the valid code by copying the pdfkit files - // into the right destination). - pdfkit: path.resolve(__dirname, 'pdfkit.js') - }, - extensions: ['.js'] - }, - output: { - filename: '[name].js', - path: path.resolve(constants.ExtensionRootDir, 'out', 'client'), - libraryTarget: 'commonjs2', - devtoolModuleFilenameTemplate: '../../[resource-path]' - } -}; -// tslint:disable-next-line:no-default-export -exports.default = config; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +// tslint:disable-next-line: no-require-imports +const copyWebpackPlugin = require('copy-webpack-plugin'); +const path = require('path'); +const constants = require('../constants'); +const common = require('./common'); +const entryItems = {}; +common.nodeModulesToExternalize.forEach((moduleName) => { + entryItems[`node_modules/${moduleName}`] = `./node_modules/${moduleName}`; +}); +const config = { + mode: 'production', + target: 'node', + context: constants.ExtensionRootDir, + entry: entryItems, + devtool: 'source-map', + node: { + __dirname: false + }, + module: { + rules: [ + { + // JupyterServices imports node-fetch. + test: /@jupyterlab[\\\/]services[\\\/].*js$/, + use: [ + { + loader: path.join(__dirname, 'loaders', 'fixNodeFetch.js') + } + ] + }, + { enforce: 'post', test: /unicode-properties[\/\\]index.js$/, loader: 'transform-loader?brfs' }, + { enforce: 'post', test: /fontkit[\/\\]index.js$/, loader: 'transform-loader?brfs' }, + { enforce: 'post', test: /linebreak[\/\\]src[\/\\]linebreaker.js/, loader: 'transform-loader?brfs' } + ] + }, + externals: ['vscode', 'commonjs'], + plugins: [ + ...common.getDefaultPlugins('dependencies'), + // vsls requires our package.json to be next to node_modules. It's how they + // 'find' the calling extension. + new copyWebpackPlugin([{ from: './package.json', to: '.' }]), + // onigasm requires our onigasm.wasm to be in node_modules + new copyWebpackPlugin([ + { from: './node_modules/onigasm/lib/onigasm.wasm', to: './node_modules/onigasm/lib/onigasm.wasm' } + ]) + ], + resolve: { + alias: { + // Pointing pdfkit to a dummy js file so webpack doesn't fall over. + // Since pdfkit has been externalized (it gets updated with the valid code by copying the pdfkit files + // into the right destination). + pdfkit: path.resolve(__dirname, 'pdfkit.js') + }, + extensions: ['.js'] + }, + output: { + filename: '[name].js', + path: path.resolve(constants.ExtensionRootDir, 'out', 'client'), + libraryTarget: 'commonjs2', + devtoolModuleFilenameTemplate: '../../[resource-path]' + } +}; +// tslint:disable-next-line:no-default-export +exports.default = config; diff --git a/news/3 Code Health/10837.md b/news/3 Code Health/10837.md new file mode 100644 index 000000000000..af3b1f23e30f --- /dev/null +++ b/news/3 Code Health/10837.md @@ -0,0 +1 @@ +Update prettier to latest version. diff --git a/package-lock.json b/package-lock.json index fcd63b323abf..a8155b905f37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18057,9 +18057,9 @@ "dev": true }, "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.2.tgz", + "integrity": "sha512-5xJQIPT8BraI7ZnaDwSbu5zLrB6vvi8hVV58yHQ+QK64qrY40dULy0HSRlQ2/2IdzeBpjhDkqdcFBnFeDEMVdg==", "dev": true }, "prettier-linter-helpers": { diff --git a/package.json b/package.json index 46a098e42226..b562c909b35d 100644 --- a/package.json +++ b/package.json @@ -3136,7 +3136,7 @@ "postcss-cssnext": "^3.1.0", "postcss-import": "^12.0.1", "postcss-loader": "^3.0.0", - "prettier": "^1.19.1", + "prettier": "^2.0.2", "range-inclusive": "^1.0.2", "raw-loader": "^0.5.1", "react": "^16.5.2", diff --git a/src/client/activation/aaTesting.ts b/src/client/activation/aaTesting.ts index c6b4765f25a9..e37ddb31e4d0 100644 --- a/src/client/activation/aaTesting.ts +++ b/src/client/activation/aaTesting.ts @@ -1,19 +1,19 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { inject, injectable } from 'inversify'; -import { ValidateABTesting } from '../common/experimentGroups'; -import { IExperimentsManager } from '../common/types'; -import { IExtensionSingleActivationService } from './types'; - -@injectable() -export class AATesting implements IExtensionSingleActivationService { - constructor(@inject(IExperimentsManager) private experiments: IExperimentsManager) {} - - public async activate(): Promise { - this.experiments.sendTelemetryIfInExperiment(ValidateABTesting.experiment); - this.experiments.sendTelemetryIfInExperiment(ValidateABTesting.control); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { inject, injectable } from 'inversify'; +import { ValidateABTesting } from '../common/experimentGroups'; +import { IExperimentsManager } from '../common/types'; +import { IExtensionSingleActivationService } from './types'; + +@injectable() +export class AATesting implements IExtensionSingleActivationService { + constructor(@inject(IExperimentsManager) private experiments: IExperimentsManager) {} + + public async activate(): Promise { + this.experiments.sendTelemetryIfInExperiment(ValidateABTesting.experiment); + this.experiments.sendTelemetryIfInExperiment(ValidateABTesting.control); + } +} diff --git a/src/client/activation/activationManager.ts b/src/client/activation/activationManager.ts index b47f7b2e9620..d46119457fbf 100644 --- a/src/client/activation/activationManager.ts +++ b/src/client/activation/activationManager.ts @@ -45,7 +45,7 @@ export class ExtensionActivationManager implements IExtensionActivationManager { await this.initialize(); // Activate all activation services together. await Promise.all([ - Promise.all(this.singleActivationServices.map(item => item.activate())), + Promise.all(this.singleActivationServices.map((item) => item.activate())), this.activateWorkspace(this.activeResourceService.getActiveResource()) ]); await this.autoSelection.autoSelectInterpreter(undefined); @@ -61,7 +61,7 @@ export class ExtensionActivationManager implements IExtensionActivationManager { this.interpreterService.getInterpreters(resource).ignoreErrors(); await this.autoSelection.autoSelectInterpreter(resource); - await Promise.all(this.activationServices.map(item => item.activate(resource))); + await Promise.all(this.activationServices.map((item) => item.activate(resource))); await this.appDiagnostics.performPreStartupHealthCheck(resource); } public async initialize() { @@ -100,11 +100,11 @@ export class ExtensionActivationManager implements IExtensionActivationManager { } protected onWorkspaceFoldersChanged() { //If an activated workspace folder was removed, delete its key - const workspaceKeys = this.workspaceService.workspaceFolders!.map(workspaceFolder => + const workspaceKeys = this.workspaceService.workspaceFolders!.map((workspaceFolder) => this.getWorkspaceKey(workspaceFolder.uri) ); const activatedWkspcKeys = Array.from(this.activatedWorkspaces.keys()); - const activatedWkspcFoldersRemoved = activatedWkspcKeys.filter(item => workspaceKeys.indexOf(item) < 0); + const activatedWkspcFoldersRemoved = activatedWkspcKeys.filter((item) => workspaceKeys.indexOf(item) < 0); if (activatedWkspcFoldersRemoved.length > 0) { for (const folder of activatedWkspcFoldersRemoved) { this.activatedWorkspaces.delete(folder); diff --git a/src/client/activation/activationService.ts b/src/client/activation/activationService.ts index 3275e164835e..0d525499362d 100644 --- a/src/client/activation/activationService.ts +++ b/src/client/activation/activationService.ts @@ -116,7 +116,7 @@ export class LanguageServerExtensionActivationService this.cache.set(key, result); } else { // Increment ref count if already exists. - result = result.then(r => { + result = result.then((r) => { r.increment(); return r; }); @@ -188,10 +188,10 @@ export class LanguageServerExtensionActivationService protected async onWorkspaceFoldersChanged() { //If an activated workspace folder was removed, dispose its activator const workspaceKeys = await Promise.all( - this.workspaceService.workspaceFolders!.map(workspaceFolder => this.getKey(workspaceFolder.uri)) + this.workspaceService.workspaceFolders!.map((workspaceFolder) => this.getKey(workspaceFolder.uri)) ); const activatedWkspcKeys = Array.from(this.cache.keys()); - const activatedWkspcFoldersRemoved = activatedWkspcKeys.filter(item => workspaceKeys.indexOf(item) < 0); + const activatedWkspcFoldersRemoved = activatedWkspcKeys.filter((item) => workspaceKeys.indexOf(item) < 0); if (activatedWkspcFoldersRemoved.length > 0) { for (const folder of activatedWkspcFoldersRemoved) { const server = await this.cache.get(folder); @@ -293,11 +293,11 @@ export class LanguageServerExtensionActivationService private async onDidChangeConfiguration(event: ConfigurationChangeEvent) { const workspacesUris: (Uri | undefined)[] = this.workspaceService.hasWorkspaceFolders - ? this.workspaceService.workspaceFolders!.map(workspace => workspace.uri) + ? this.workspaceService.workspaceFolders!.map((workspace) => workspace.uri) : [undefined]; if ( - workspacesUris.findIndex(uri => event.affectsConfiguration(`python.${jediEnabledSetting}`, uri)) === -1 && - workspacesUris.findIndex(uri => event.affectsConfiguration(`python.${languageServerSetting}`, uri)) === -1 + workspacesUris.findIndex((uri) => event.affectsConfiguration(`python.${jediEnabledSetting}`, uri)) === -1 && + workspacesUris.findIndex((uri) => event.affectsConfiguration(`python.${languageServerSetting}`, uri)) === -1 ) { return; } @@ -333,6 +333,6 @@ export class LanguageServerExtensionActivationService private async onClearAnalysisCaches() { const values = await Promise.all([...this.cache.values()]); - values.forEach(v => (v.clearAnalysisCache ? v.clearAnalysisCache() : noop())); + values.forEach((v) => (v.clearAnalysisCache ? v.clearAnalysisCache() : noop())); } } diff --git a/src/client/activation/common/activatorBase.ts b/src/client/activation/common/activatorBase.ts index fa3956498a89..32a532900a25 100644 --- a/src/client/activation/common/activatorBase.ts +++ b/src/client/activation/common/activatorBase.ts @@ -256,7 +256,7 @@ export abstract class LanguageServerActivatorBase implements ILanguageServerActi const result = await languageClient.sendRequest(vscodeLanguageClient.ReferencesRequest.type, args, token); if (result) { // Remove undefined part. - return result.map(l => { + return result.map((l) => { const r = languageClient!.protocol2CodeConverter.asLocation(l); return r!; }); diff --git a/src/client/activation/common/downloader.ts b/src/client/activation/common/downloader.ts index 64019326620d..6f304bea24f7 100644 --- a/src/client/activation/common/downloader.ts +++ b/src/client/activation/common/downloader.ts @@ -43,7 +43,7 @@ export class LanguageServerDownloader implements ILanguageServerDownloader { } public async getDownloadInfo(resource: Resource) { - const info = await this.lsFolderService.getLatestLanguageServerVersion(resource).then(item => item!); + const info = await this.lsFolderService.getLatestLanguageServerVersion(resource).then((item) => item!); let uri = info.uri; if (uri.startsWith('https:')) { @@ -129,7 +129,7 @@ export class LanguageServerDownloader implements ILanguageServerDownloader { outputChannel: this.output, progressMessagePrefix: title }; - return this.fileDownloader.downloadFile(uri, downloadOptions).then(file => { + return this.fileDownloader.downloadFile(uri, downloadOptions).then((file) => { this.output.appendLine(LanguageService.extractionCompletedOutputMessage()); return file; }); @@ -145,7 +145,7 @@ export class LanguageServerDownloader implements ILanguageServerDownloader { { location: ProgressLocation.Window }, - progress => { + (progress) => { // tslint:disable-next-line:no-require-imports no-var-requires const StreamZip = require('node-stream-zip'); const zip = new StreamZip({ diff --git a/src/client/activation/common/languageServerFolderService.ts b/src/client/activation/common/languageServerFolderService.ts index a04fe1c5323c..3aa99f8e20e4 100644 --- a/src/client/activation/common/languageServerFolderService.ts +++ b/src/client/activation/common/languageServerFolderService.ts @@ -87,8 +87,8 @@ export abstract class LanguageServerFolderService implements ILanguageServerFold const fs = this.serviceContainer.get(IFileSystem); const subDirs = await fs.getSubDirectories(EXTENSION_ROOT_DIR); return subDirs - .filter(dir => path.basename(dir).startsWith(this.languageServerFolder)) - .map(dir => { + .filter((dir) => path.basename(dir).startsWith(this.languageServerFolder)) + .map((dir) => { return { path: dir, version: this.getFolderVersion(path.basename(dir)) }; }); } diff --git a/src/client/activation/common/languageServerPackageService.ts b/src/client/activation/common/languageServerPackageService.ts index 10b60cf40f39..feed13c03b53 100644 --- a/src/client/activation/common/languageServerPackageService.ts +++ b/src/client/activation/common/languageServerPackageService.ts @@ -63,8 +63,8 @@ export abstract class LanguageServerPackageService implements ILanguageServerPac protected getValidPackage(packages: NugetPackage[], minimumVersion?: string): NugetPackage { const nugetService = this.serviceContainer.get(INugetService); const validPackages = packages - .filter(item => item.version.major === this.maxMajorVersion) - .filter(item => nugetService.isReleaseVersion(item.version)) + .filter((item) => item.version.major === this.maxMajorVersion) + .filter((item) => nugetService.isReleaseVersion(item.version)) .sort((a, b) => a.version.compare(b.version)); const pkg = validPackages[validPackages.length - 1]; diff --git a/src/client/activation/jedi.ts b/src/client/activation/jedi.ts index 58e123ca64da..a67a3b72a385 100644 --- a/src/client/activation/jedi.ts +++ b/src/client/activation/jedi.ts @@ -100,11 +100,11 @@ export class JediExtensionActivator implements ILanguageServerActivator { const testManagementService = this.serviceManager.get(ITestManagementService); testManagementService .activate(this.symbolProvider) - .catch(ex => traceError('Failed to activate Unit Tests', ex)); + .catch((ex) => traceError('Failed to activate Unit Tests', ex)); } public deactivate() { - this.registrations.forEach(r => r.dispose()); + this.registrations.forEach((r) => r.dispose()); this.registrations = []; } @@ -243,7 +243,7 @@ export class JediExtensionActivator implements ILanguageServerActivator { } public dispose(): void { - this.registrations.forEach(r => r.dispose()); + this.registrations.forEach((r) => r.dispose()); if (this.jediFactory) { this.jediFactory.dispose(); } diff --git a/src/client/activation/languageClientMiddleware.ts b/src/client/activation/languageClientMiddleware.ts index cfa1c6a699b1..f162b8034ce7 100644 --- a/src/client/activation/languageClientMiddleware.ts +++ b/src/client/activation/languageClientMiddleware.ts @@ -372,11 +372,11 @@ export class LanguageClientMiddleware implements Middleware { function captureTelemetryForLSPMethod(method: string, debounceMilliseconds: number) { // tslint:disable-next-line:no-function-expression no-any - return function(_target: Object, _propertyKey: string, descriptor: TypedPropertyDescriptor) { + return function (_target: Object, _propertyKey: string, descriptor: TypedPropertyDescriptor) { const originalMethod = descriptor.value; // tslint:disable-next-line:no-any - descriptor.value = function(this: LanguageClientMiddleware, ...args: any[]) { + descriptor.value = function (this: LanguageClientMiddleware, ...args: any[]) { const eventName = this.eventName; if (!eventName) { return originalMethod.apply(this, args); diff --git a/src/client/activation/languageServer/analysisOptions.ts b/src/client/activation/languageServer/analysisOptions.ts index 1fffcd1473aa..89bd7353b2ea 100644 --- a/src/client/activation/languageServer/analysisOptions.ts +++ b/src/client/activation/languageServer/analysisOptions.ts @@ -51,7 +51,7 @@ export class LanguageServerAnalysisOptions implements ILanguageServerAnalysisOpt return this.didChange.event; } public dispose(): void { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); this.didChange.dispose(); } // tslint:disable-next-line: max-func-body-length @@ -95,11 +95,11 @@ export class LanguageServerAnalysisOptions implements ILanguageServerAnalysisOpt const vars = await this.envVarsProvider.getEnvironmentVariables(); this.envPythonPath = vars.PYTHONPATH || ''; if (this.envPythonPath !== '') { - const paths = this.envPythonPath.split(this.pathUtils.delimiter).filter(item => item.trim().length > 0); + const paths = this.envPythonPath.split(this.pathUtils.delimiter).filter((item) => item.trim().length > 0); searchPaths.push(...paths); } - searchPaths = searchPaths.map(p => path.normalize(p)); + searchPaths = searchPaths.map((p) => path.normalize(p)); this.excludedFiles = this.getExcludedFiles(); this.typeshedPaths = this.getTypeshedPaths(); @@ -166,15 +166,15 @@ export class LanguageServerAnalysisOptions implements ILanguageServerAnalysisOpt const states = this.workspace.getConfiguration(setting); if (states) { Object.keys(states) - .filter(k => (k.indexOf('*') >= 0 || k.indexOf('/') >= 0) && states[k]) - .forEach(p => list.push(p)); + .filter((k) => (k.indexOf('*') >= 0 || k.indexOf('/') >= 0) && states[k]) + .forEach((p) => list.push(p)); } } protected getPythonExcludeSection(list: string[]): void { const pythonSettings = this.configuration.getSettings(this.resource); const paths = pythonSettings && pythonSettings.linting ? pythonSettings.linting.ignorePatterns : undefined; if (paths && Array.isArray(paths)) { - paths.filter(p => p && p.length > 0).forEach(p => list.push(p)); + paths.filter((p) => p && p.length > 0).forEach((p) => list.push(p)); } } protected getTypeshedPaths(): string[] { diff --git a/src/client/activation/languageServer/languageServerExtension.ts b/src/client/activation/languageServer/languageServerExtension.ts index 1bcaa0e5be03..b2f69abadbee 100644 --- a/src/client/activation/languageServer/languageServerExtension.ts +++ b/src/client/activation/languageServer/languageServerExtension.ts @@ -27,7 +27,7 @@ export class LanguageServerExtension implements ILanguageServerExtension { if (this.disposable) { return; } - this.disposable = this.commandManager.registerCommand(loadExtensionCommand, args => { + this.disposable = this.commandManager.registerCommand(loadExtensionCommand, (args) => { this.loadExtensionArgs = args; this._invoked.fire(); }); diff --git a/src/client/activation/languageServer/languageServerPackageService.ts b/src/client/activation/languageServer/languageServerPackageService.ts index 46998e8cc9cb..9a912645fb4f 100644 --- a/src/client/activation/languageServer/languageServerPackageService.ts +++ b/src/client/activation/languageServer/languageServerPackageService.ts @@ -1,42 +1,42 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { inject, injectable } from 'inversify'; -import { IApplicationEnvironment } from '../../common/application/types'; -import { IPlatformService } from '../../common/platform/types'; -import { OSType } from '../../common/utils/platform'; -import { IServiceContainer } from '../../ioc/types'; -import { LanguageServerPackageService } from '../common/languageServerPackageService'; -import { PlatformName } from '../types'; - -const downloadBaseFileName = 'Python-Language-Server'; -export const PackageNames = { - [PlatformName.Windows32Bit]: `${downloadBaseFileName}-${PlatformName.Windows32Bit}`, - [PlatformName.Windows64Bit]: `${downloadBaseFileName}-${PlatformName.Windows64Bit}`, - [PlatformName.Linux64Bit]: `${downloadBaseFileName}-${PlatformName.Linux64Bit}`, - [PlatformName.Mac64Bit]: `${downloadBaseFileName}-${PlatformName.Mac64Bit}` -}; - -@injectable() -export class DotNetLanguageServerPackageService extends LanguageServerPackageService { - constructor( - @inject(IServiceContainer) serviceContainer: IServiceContainer, - @inject(IApplicationEnvironment) appEnv: IApplicationEnvironment, - @inject(IPlatformService) platform: IPlatformService - ) { - super(serviceContainer, appEnv, platform); - } - - public getNugetPackageName(): string { - switch (this.platform.osType) { - case OSType.Windows: - return PackageNames[this.platform.is64bit ? PlatformName.Windows64Bit : PlatformName.Windows32Bit]; - case OSType.OSX: - return PackageNames[PlatformName.Mac64Bit]; - default: - return PackageNames[PlatformName.Linux64Bit]; - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { inject, injectable } from 'inversify'; +import { IApplicationEnvironment } from '../../common/application/types'; +import { IPlatformService } from '../../common/platform/types'; +import { OSType } from '../../common/utils/platform'; +import { IServiceContainer } from '../../ioc/types'; +import { LanguageServerPackageService } from '../common/languageServerPackageService'; +import { PlatformName } from '../types'; + +const downloadBaseFileName = 'Python-Language-Server'; +export const PackageNames = { + [PlatformName.Windows32Bit]: `${downloadBaseFileName}-${PlatformName.Windows32Bit}`, + [PlatformName.Windows64Bit]: `${downloadBaseFileName}-${PlatformName.Windows64Bit}`, + [PlatformName.Linux64Bit]: `${downloadBaseFileName}-${PlatformName.Linux64Bit}`, + [PlatformName.Mac64Bit]: `${downloadBaseFileName}-${PlatformName.Mac64Bit}` +}; + +@injectable() +export class DotNetLanguageServerPackageService extends LanguageServerPackageService { + constructor( + @inject(IServiceContainer) serviceContainer: IServiceContainer, + @inject(IApplicationEnvironment) appEnv: IApplicationEnvironment, + @inject(IPlatformService) platform: IPlatformService + ) { + super(serviceContainer, appEnv, platform); + } + + public getNugetPackageName(): string { + switch (this.platform.osType) { + case OSType.Windows: + return PackageNames[this.platform.is64bit ? PlatformName.Windows64Bit : PlatformName.Windows32Bit]; + case OSType.OSX: + return PackageNames[PlatformName.Mac64Bit]; + default: + return PackageNames[PlatformName.Linux64Bit]; + } + } +} diff --git a/src/client/activation/languageServer/languageServerProxy.ts b/src/client/activation/languageServer/languageServerProxy.ts index 52576457d3a2..29d2e318eb96 100644 --- a/src/client/activation/languageServer/languageServerProxy.ts +++ b/src/client/activation/languageServer/languageServerProxy.ts @@ -37,7 +37,7 @@ export class DotNetLanguageServerProxy implements ILanguageServerProxy { public dispose() { if (this.languageClient) { // Do not await on this. - this.languageClient.stop().then(noop, ex => traceError('Stopping language client failed', ex)); + this.languageClient.stop().then(noop, (ex) => traceError('Stopping language client failed', ex)); this.languageClient = undefined; } while (this.disposables.length > 0) { @@ -71,7 +71,7 @@ export class DotNetLanguageServerProxy implements ILanguageServerProxy { const settings = this.configurationService.getSettings(resource); if (settings.downloadLanguageServer) { - this.languageClient.onTelemetry(telemetryEvent => { + this.languageClient.onTelemetry((telemetryEvent) => { const eventName = telemetryEvent.EventName || EventName.PYTHON_LANGUAGE_SERVER_TELEMETRY; sendTelemetryEvent(eventName, telemetryEvent.Measurements, telemetryEvent.Properties); }); @@ -89,7 +89,7 @@ export class DotNetLanguageServerProxy implements ILanguageServerProxy { this.extensionLoadedArgs.add(args || ''); this.startupCompleted.promise .then(() => - this.languageClient!.sendRequest('python/loadExtension', args).then(noop, ex => + this.languageClient!.sendRequest('python/loadExtension', args).then(noop, (ex) => traceError('Request python/loadExtension failed', ex) ) ) diff --git a/src/client/activation/languageServer/manager.ts b/src/client/activation/languageServer/manager.ts index 216915b240ea..32d8da88e9af 100644 --- a/src/client/activation/languageServer/manager.ts +++ b/src/client/activation/languageServer/manager.ts @@ -49,7 +49,7 @@ export class DotNetLanguageServerManager implements ILanguageServerManager { if (this.languageProxy) { this.languageProxy.dispose(); } - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } public get languageProxy() { diff --git a/src/client/activation/languageServer/outputChannel.ts b/src/client/activation/languageServer/outputChannel.ts index f37ad51db496..2d4815b3af97 100644 --- a/src/client/activation/languageServer/outputChannel.ts +++ b/src/client/activation/languageServer/outputChannel.ts @@ -1,39 +1,39 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { inject, injectable } from 'inversify'; -import { IApplicationShell, ICommandManager } from '../../common/application/types'; -import '../../common/extensions'; -import { IOutputChannel } from '../../common/types'; -import { OutputChannelNames } from '../../common/utils/localize'; -import { ILanguageServerOutputChannel } from '../types'; - -@injectable() -export class LanguageServerOutputChannel implements ILanguageServerOutputChannel { - public output: IOutputChannel | undefined; - private registered = false; - constructor( - @inject(IApplicationShell) private readonly appShell: IApplicationShell, - @inject(ICommandManager) private readonly commandManager: ICommandManager - ) {} - - public get channel() { - if (!this.output) { - this.output = this.appShell.createOutputChannel(OutputChannelNames.languageServer()); - this.registerCommand().ignoreErrors(); - } - return this.output; - } - private async registerCommand() { - if (this.registered) { - return; - } - this.registered = true; - // This controls the visibility of the command used to display the LS Output panel. - // We don't want to display it when Jedi is used instead of LS. - await this.commandManager.executeCommand('setContext', 'python.hasLanguageServerOutputChannel', true); - this.commandManager.registerCommand('python.viewLanguageServerOutput', () => this.output!.show(true)); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { inject, injectable } from 'inversify'; +import { IApplicationShell, ICommandManager } from '../../common/application/types'; +import '../../common/extensions'; +import { IOutputChannel } from '../../common/types'; +import { OutputChannelNames } from '../../common/utils/localize'; +import { ILanguageServerOutputChannel } from '../types'; + +@injectable() +export class LanguageServerOutputChannel implements ILanguageServerOutputChannel { + public output: IOutputChannel | undefined; + private registered = false; + constructor( + @inject(IApplicationShell) private readonly appShell: IApplicationShell, + @inject(ICommandManager) private readonly commandManager: ICommandManager + ) {} + + public get channel() { + if (!this.output) { + this.output = this.appShell.createOutputChannel(OutputChannelNames.languageServer()); + this.registerCommand().ignoreErrors(); + } + return this.output; + } + private async registerCommand() { + if (this.registered) { + return; + } + this.registered = true; + // This controls the visibility of the command used to display the LS Output panel. + // We don't want to display it when Jedi is used instead of LS. + await this.commandManager.executeCommand('setContext', 'python.hasLanguageServerOutputChannel', true); + this.commandManager.registerCommand('python.viewLanguageServerOutput', () => this.output!.show(true)); + } +} diff --git a/src/client/activation/node/languageServerProxy.ts b/src/client/activation/node/languageServerProxy.ts index 1d8753065c6f..45c9fdcdd068 100644 --- a/src/client/activation/node/languageServerProxy.ts +++ b/src/client/activation/node/languageServerProxy.ts @@ -37,7 +37,7 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy { public dispose() { if (this.languageClient) { // Do not await on this. - this.languageClient.stop().then(noop, ex => traceError('Stopping language client failed', ex)); + this.languageClient.stop().then(noop, (ex) => traceError('Stopping language client failed', ex)); this.languageClient = undefined; } while (this.disposables.length > 0) { @@ -71,7 +71,7 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy { const settings = this.configurationService.getSettings(resource); if (settings.downloadLanguageServer) { - this.languageClient.onTelemetry(telemetryEvent => { + this.languageClient.onTelemetry((telemetryEvent) => { const eventName = telemetryEvent.EventName || EventName.PYTHON_LANGUAGE_SERVER_TELEMETRY; sendTelemetryEvent(eventName, telemetryEvent.Measurements, telemetryEvent.Properties); }); diff --git a/src/client/activation/node/manager.ts b/src/client/activation/node/manager.ts index cf09c6d0dcce..67a89f16b630 100644 --- a/src/client/activation/node/manager.ts +++ b/src/client/activation/node/manager.ts @@ -48,7 +48,7 @@ export class NodeLanguageServerManager implements ILanguageServerManager { if (this.languageProxy) { this.languageProxy.dispose(); } - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } public get languageProxy() { diff --git a/src/client/activation/progress.ts b/src/client/activation/progress.ts index 4158670fa7e7..8042377d821e 100644 --- a/src/client/activation/progress.ts +++ b/src/client/activation/progress.ts @@ -20,7 +20,7 @@ export class ProgressReporting implements Disposable { this.statusBarMessage = window.setStatusBarMessage(m); }); - this.languageClient.onNotification('python/beginProgress', _ => { + this.languageClient.onNotification('python/beginProgress', (_) => { if (this.progressDeferred) { return; } @@ -34,7 +34,7 @@ export class ProgressReporting implements Disposable { this.progress!.report({ message: m }); }); - this.languageClient.onNotification('python/endProgress', _ => { + this.languageClient.onNotification('python/endProgress', (_) => { if (this.progressDeferred) { this.progressDeferred.resolve(); this.progressDeferred = undefined; @@ -57,7 +57,7 @@ export class ProgressReporting implements Disposable { location: ProgressLocation.Window, title: '' }, - progress => { + (progress) => { this.progress = progress; return this.progressDeferred!.promise; } diff --git a/src/client/activation/types.ts b/src/client/activation/types.ts index c5e685cfd498..4fce2850ffd3 100644 --- a/src/client/activation/types.ts +++ b/src/client/activation/types.ts @@ -1,229 +1,229 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { SemVer } from 'semver'; -import { - CodeLensProvider, - CompletionItemProvider, - DefinitionProvider, - DocumentSymbolProvider, - Event, - HoverProvider, - ReferenceProvider, - RenameProvider, - SignatureHelpProvider, - TextDocument, - TextDocumentContentChangeEvent -} from 'vscode'; -import { LanguageClient, LanguageClientOptions } from 'vscode-languageclient'; -import { NugetPackage } from '../common/nuget/types'; -import { IDisposable, IOutputChannel, LanguageServerDownloadChannels, Resource } from '../common/types'; -import { PythonInterpreter } from '../interpreter/contracts'; - -export const IExtensionActivationManager = Symbol('IExtensionActivationManager'); -/** - * Responsible for activation of extension. - * - * @export - * @interface IExtensionActivationManager - * @extends {IDisposable} - */ -export interface IExtensionActivationManager extends IDisposable { - /** - * Method invoked when extension activates (invoked once). - * - * @returns {Promise} - * @memberof IExtensionActivationManager - */ - activate(): Promise; - /** - * Method invoked when a workspace is loaded. - * This is where we place initialization scripts for each workspace. - * (e.g. if we need to run code for each workspace, then this is where that happens). - * - * @param {Resource} resource - * @returns {Promise} - * @memberof IExtensionActivationManager - */ - activateWorkspace(resource: Resource): Promise; -} - -export const IExtensionActivationService = Symbol('IExtensionActivationService'); -/** - * Classes implementing this interface will have their `activate` methods - * invoked for every workspace folder (in multi-root workspace folders) during the activation of the extension. - * This is a great hook for extension activation code, i.e. you don't need to modify - * the `extension.ts` file to invoke some code when extension gets activated. - * @export - * @interface IExtensionActivationService - */ -export interface IExtensionActivationService { - activate(resource: Resource): Promise; -} - -export enum LanguageServerType { - Jedi = 'Jedi', - Microsoft = 'Microsoft', - Node = 'Node', - None = 'None' -} - -// tslint:disable-next-line: interface-name -export interface DocumentHandler { - handleOpen(document: TextDocument): void; - handleChanges(document: TextDocument, changes: TextDocumentContentChangeEvent[]): void; -} - -// tslint:disable-next-line: interface-name -export interface LanguageServerCommandHandler { - clearAnalysisCache(): void; -} - -export interface ILanguageServer - extends RenameProvider, - DefinitionProvider, - HoverProvider, - ReferenceProvider, - CompletionItemProvider, - CodeLensProvider, - DocumentSymbolProvider, - SignatureHelpProvider, - Partial, - Partial, - IDisposable {} - -export const ILanguageServerActivator = Symbol('ILanguageServerActivator'); -export interface ILanguageServerActivator extends ILanguageServer { - start(resource: Resource, interpreter: PythonInterpreter | undefined): Promise; - activate(): void; - deactivate(): void; -} - -export const ILanguageServerCache = Symbol('ILanguageServerCache'); -export interface ILanguageServerCache { - get(resource: Resource, interpreter?: PythonInterpreter): Promise; -} - -export type FolderVersionPair = { path: string; version: SemVer }; -export const ILanguageServerFolderService = Symbol('ILanguageServerFolderService'); - -export interface ILanguageServerFolderService { - getLanguageServerFolderName(resource: Resource): Promise; - getLatestLanguageServerVersion(resource: Resource): Promise; - getCurrentLanguageServerDirectory(): Promise; -} - -export const ILanguageServerDownloader = Symbol('ILanguageServerDownloader'); - -export interface ILanguageServerDownloader { - downloadLanguageServer(destinationFolder: string, resource: Resource): Promise; -} - -export const ILanguageServerPackageService = Symbol('ILanguageServerPackageService'); -export interface ILanguageServerPackageService { - getNugetPackageName(): string; - getLatestNugetPackageVersion(resource: Resource, minVersion?: string): Promise; - getLanguageServerDownloadChannel(): LanguageServerDownloadChannels; -} - -export const MajorLanguageServerVersion = Symbol('MajorLanguageServerVersion'); -export const IDownloadChannelRule = Symbol('IDownloadChannelRule'); -export interface IDownloadChannelRule { - shouldLookForNewLanguageServer(currentFolder?: FolderVersionPair): Promise; -} -export const ILanguageServerCompatibilityService = Symbol('ILanguageServerCompatibilityService'); -export interface ILanguageServerCompatibilityService { - isSupported(): Promise; -} -export enum LanguageClientFactory { - base = 'base', - simple = 'simple', - downloaded = 'downloaded' -} -export const ILanguageClientFactory = Symbol('ILanguageClientFactory'); -export interface ILanguageClientFactory { - createLanguageClient( - resource: Resource, - interpreter: PythonInterpreter | undefined, - clientOptions: LanguageClientOptions, - env?: NodeJS.ProcessEnv - ): Promise; -} -export const ILanguageServerAnalysisOptions = Symbol('ILanguageServerAnalysisOptions'); -export interface ILanguageServerAnalysisOptions extends IDisposable { - readonly onDidChange: Event; - initialize(resource: Resource, interpreter: PythonInterpreter | undefined): Promise; - getAnalysisOptions(): Promise; -} -export const ILanguageServerManager = Symbol('ILanguageServerManager'); -export interface ILanguageServerManager extends IDisposable { - readonly languageProxy: ILanguageServerProxy | undefined; - start(resource: Resource, interpreter: PythonInterpreter | undefined): Promise; - connect(): void; - disconnect(): void; -} -export const ILanguageServerExtension = Symbol('ILanguageServerExtension'); -export interface ILanguageServerExtension extends IDisposable { - readonly invoked: Event; - loadExtensionArgs?: {}; - register(): void; -} -export const ILanguageServerProxy = Symbol('ILanguageServerProxy'); -export interface ILanguageServerProxy extends IDisposable { - /** - * LanguageClient in use - */ - languageClient: LanguageClient | undefined; - start( - resource: Resource, - interpreter: PythonInterpreter | undefined, - options: LanguageClientOptions - ): Promise; - /** - * Sends a request to LS so as to load other extensions. - * This is used as a plugin loader mechanism. - * Anyone (such as intellicode) wanting to interact with LS, needs to send this request to LS. - * @param {{}} [args] - * @memberof ILanguageServerProxy - */ - loadExtension(args?: {}): void; -} - -export enum PlatformName { - Windows32Bit = 'win-x86', - Windows64Bit = 'win-x64', - Mac64Bit = 'osx-x64', - Linux64Bit = 'linux-x64' -} -export const IPlatformData = Symbol('IPlatformData'); -export interface IPlatformData { - readonly platformName: PlatformName; - readonly engineDllName: string; - readonly engineExecutableName: string; -} - -export const ILanguageServerOutputChannel = Symbol('ILanguageServerOutputChannel'); -export interface ILanguageServerOutputChannel { - /** - * Creates output channel if necessary and returns it - * - * @type {IOutputChannel} - * @memberof ILanguageServerOutputChannel - */ - readonly channel: IOutputChannel; -} - -export const IExtensionSingleActivationService = Symbol('IExtensionSingleActivationService'); -/** - * Classes implementing this interface will have their `activate` methods - * invoked during the activation of the extension. - * This is a great hook for extension activation code, i.e. you don't need to modify - * the `extension.ts` file to invoke some code when extension gets activated. - * @export - * @interface IExtensionSingleActivationService - */ -export interface IExtensionSingleActivationService { - activate(): Promise; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { SemVer } from 'semver'; +import { + CodeLensProvider, + CompletionItemProvider, + DefinitionProvider, + DocumentSymbolProvider, + Event, + HoverProvider, + ReferenceProvider, + RenameProvider, + SignatureHelpProvider, + TextDocument, + TextDocumentContentChangeEvent +} from 'vscode'; +import { LanguageClient, LanguageClientOptions } from 'vscode-languageclient'; +import { NugetPackage } from '../common/nuget/types'; +import { IDisposable, IOutputChannel, LanguageServerDownloadChannels, Resource } from '../common/types'; +import { PythonInterpreter } from '../interpreter/contracts'; + +export const IExtensionActivationManager = Symbol('IExtensionActivationManager'); +/** + * Responsible for activation of extension. + * + * @export + * @interface IExtensionActivationManager + * @extends {IDisposable} + */ +export interface IExtensionActivationManager extends IDisposable { + /** + * Method invoked when extension activates (invoked once). + * + * @returns {Promise} + * @memberof IExtensionActivationManager + */ + activate(): Promise; + /** + * Method invoked when a workspace is loaded. + * This is where we place initialization scripts for each workspace. + * (e.g. if we need to run code for each workspace, then this is where that happens). + * + * @param {Resource} resource + * @returns {Promise} + * @memberof IExtensionActivationManager + */ + activateWorkspace(resource: Resource): Promise; +} + +export const IExtensionActivationService = Symbol('IExtensionActivationService'); +/** + * Classes implementing this interface will have their `activate` methods + * invoked for every workspace folder (in multi-root workspace folders) during the activation of the extension. + * This is a great hook for extension activation code, i.e. you don't need to modify + * the `extension.ts` file to invoke some code when extension gets activated. + * @export + * @interface IExtensionActivationService + */ +export interface IExtensionActivationService { + activate(resource: Resource): Promise; +} + +export enum LanguageServerType { + Jedi = 'Jedi', + Microsoft = 'Microsoft', + Node = 'Node', + None = 'None' +} + +// tslint:disable-next-line: interface-name +export interface DocumentHandler { + handleOpen(document: TextDocument): void; + handleChanges(document: TextDocument, changes: TextDocumentContentChangeEvent[]): void; +} + +// tslint:disable-next-line: interface-name +export interface LanguageServerCommandHandler { + clearAnalysisCache(): void; +} + +export interface ILanguageServer + extends RenameProvider, + DefinitionProvider, + HoverProvider, + ReferenceProvider, + CompletionItemProvider, + CodeLensProvider, + DocumentSymbolProvider, + SignatureHelpProvider, + Partial, + Partial, + IDisposable {} + +export const ILanguageServerActivator = Symbol('ILanguageServerActivator'); +export interface ILanguageServerActivator extends ILanguageServer { + start(resource: Resource, interpreter: PythonInterpreter | undefined): Promise; + activate(): void; + deactivate(): void; +} + +export const ILanguageServerCache = Symbol('ILanguageServerCache'); +export interface ILanguageServerCache { + get(resource: Resource, interpreter?: PythonInterpreter): Promise; +} + +export type FolderVersionPair = { path: string; version: SemVer }; +export const ILanguageServerFolderService = Symbol('ILanguageServerFolderService'); + +export interface ILanguageServerFolderService { + getLanguageServerFolderName(resource: Resource): Promise; + getLatestLanguageServerVersion(resource: Resource): Promise; + getCurrentLanguageServerDirectory(): Promise; +} + +export const ILanguageServerDownloader = Symbol('ILanguageServerDownloader'); + +export interface ILanguageServerDownloader { + downloadLanguageServer(destinationFolder: string, resource: Resource): Promise; +} + +export const ILanguageServerPackageService = Symbol('ILanguageServerPackageService'); +export interface ILanguageServerPackageService { + getNugetPackageName(): string; + getLatestNugetPackageVersion(resource: Resource, minVersion?: string): Promise; + getLanguageServerDownloadChannel(): LanguageServerDownloadChannels; +} + +export const MajorLanguageServerVersion = Symbol('MajorLanguageServerVersion'); +export const IDownloadChannelRule = Symbol('IDownloadChannelRule'); +export interface IDownloadChannelRule { + shouldLookForNewLanguageServer(currentFolder?: FolderVersionPair): Promise; +} +export const ILanguageServerCompatibilityService = Symbol('ILanguageServerCompatibilityService'); +export interface ILanguageServerCompatibilityService { + isSupported(): Promise; +} +export enum LanguageClientFactory { + base = 'base', + simple = 'simple', + downloaded = 'downloaded' +} +export const ILanguageClientFactory = Symbol('ILanguageClientFactory'); +export interface ILanguageClientFactory { + createLanguageClient( + resource: Resource, + interpreter: PythonInterpreter | undefined, + clientOptions: LanguageClientOptions, + env?: NodeJS.ProcessEnv + ): Promise; +} +export const ILanguageServerAnalysisOptions = Symbol('ILanguageServerAnalysisOptions'); +export interface ILanguageServerAnalysisOptions extends IDisposable { + readonly onDidChange: Event; + initialize(resource: Resource, interpreter: PythonInterpreter | undefined): Promise; + getAnalysisOptions(): Promise; +} +export const ILanguageServerManager = Symbol('ILanguageServerManager'); +export interface ILanguageServerManager extends IDisposable { + readonly languageProxy: ILanguageServerProxy | undefined; + start(resource: Resource, interpreter: PythonInterpreter | undefined): Promise; + connect(): void; + disconnect(): void; +} +export const ILanguageServerExtension = Symbol('ILanguageServerExtension'); +export interface ILanguageServerExtension extends IDisposable { + readonly invoked: Event; + loadExtensionArgs?: {}; + register(): void; +} +export const ILanguageServerProxy = Symbol('ILanguageServerProxy'); +export interface ILanguageServerProxy extends IDisposable { + /** + * LanguageClient in use + */ + languageClient: LanguageClient | undefined; + start( + resource: Resource, + interpreter: PythonInterpreter | undefined, + options: LanguageClientOptions + ): Promise; + /** + * Sends a request to LS so as to load other extensions. + * This is used as a plugin loader mechanism. + * Anyone (such as intellicode) wanting to interact with LS, needs to send this request to LS. + * @param {{}} [args] + * @memberof ILanguageServerProxy + */ + loadExtension(args?: {}): void; +} + +export enum PlatformName { + Windows32Bit = 'win-x86', + Windows64Bit = 'win-x64', + Mac64Bit = 'osx-x64', + Linux64Bit = 'linux-x64' +} +export const IPlatformData = Symbol('IPlatformData'); +export interface IPlatformData { + readonly platformName: PlatformName; + readonly engineDllName: string; + readonly engineExecutableName: string; +} + +export const ILanguageServerOutputChannel = Symbol('ILanguageServerOutputChannel'); +export interface ILanguageServerOutputChannel { + /** + * Creates output channel if necessary and returns it + * + * @type {IOutputChannel} + * @memberof ILanguageServerOutputChannel + */ + readonly channel: IOutputChannel; +} + +export const IExtensionSingleActivationService = Symbol('IExtensionSingleActivationService'); +/** + * Classes implementing this interface will have their `activate` methods + * invoked during the activation of the extension. + * This is a great hook for extension activation code, i.e. you don't need to modify + * the `extension.ts` file to invoke some code when extension gets activated. + * @export + * @interface IExtensionSingleActivationService + */ +export interface IExtensionSingleActivationService { + activate(): Promise; +} diff --git a/src/client/api.ts b/src/client/api.ts index 85b8b6b162b0..1a55c0265210 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -48,7 +48,7 @@ export function buildApi( const api = { // 'ready' will propagate the exception, but we must log it here first. - ready: ready.catch(ex => { + ready: ready.catch((ex) => { traceError('Failure during activation.', ex); return Promise.reject(ex); }), diff --git a/src/client/application/diagnostics/applicationDiagnostics.ts b/src/client/application/diagnostics/applicationDiagnostics.ts index cb676c486c81..aebfa942edb7 100644 --- a/src/client/application/diagnostics/applicationDiagnostics.ts +++ b/src/client/application/diagnostics/applicationDiagnostics.ts @@ -29,18 +29,18 @@ export class ApplicationDiagnostics implements IApplicationDiagnostics { const services = this.serviceContainer.getAll(IDiagnosticsService); // Perform these validation checks in the foreground. await this.runDiagnostics( - services.filter(item => !item.runInBackground), + services.filter((item) => !item.runInBackground), resource ); // Perform these validation checks in the background. this.runDiagnostics( - services.filter(item => item.runInBackground), + services.filter((item) => item.runInBackground), resource ).ignoreErrors(); } private async runDiagnostics(diagnosticServices: IDiagnosticsService[], resource: Resource): Promise { await Promise.all( - diagnosticServices.map(async diagnosticService => { + diagnosticServices.map(async (diagnosticService) => { const diagnostics = await diagnosticService.diagnose(resource); if (diagnostics.length > 0) { this.log(diagnostics); @@ -50,7 +50,7 @@ export class ApplicationDiagnostics implements IApplicationDiagnostics { ); } private log(diagnostics: IDiagnostic[]): void { - diagnostics.forEach(item => { + diagnostics.forEach((item) => { const message = `Diagnostic Code: ${item.code}, Message: ${item.message}`; switch (item.severity) { case DiagnosticSeverity.Error: { diff --git a/src/client/application/diagnostics/base.ts b/src/client/application/diagnostics/base.ts index 85af2e18aaed..64d5967819c6 100644 --- a/src/client/application/diagnostics/base.ts +++ b/src/client/application/diagnostics/base.ts @@ -46,7 +46,7 @@ export abstract class BaseDiagnosticsService implements IDiagnosticsService, IDi if (diagnostics.length === 0) { return; } - const diagnosticsToHandle = diagnostics.filter(item => { + const diagnosticsToHandle = diagnostics.filter((item) => { if (item.invokeHandler && item.invokeHandler === 'always') { return true; } @@ -61,7 +61,7 @@ export abstract class BaseDiagnosticsService implements IDiagnosticsService, IDi } public async canHandle(diagnostic: IDiagnostic): Promise { sendTelemetryEvent(EventName.DIAGNOSTICS_MESSAGE, undefined, { code: diagnostic.code }); - return this.supportedDiagnosticCodes.filter(item => item === diagnostic.code).length > 0; + return this.supportedDiagnosticCodes.filter((item) => item === diagnostic.code).length > 0; } protected abstract onHandle(diagnostics: IDiagnostic[]): Promise; /** diff --git a/src/client/application/diagnostics/checks/envPathVariable.ts b/src/client/application/diagnostics/checks/envPathVariable.ts index ef31aa3f8601..2c137662055e 100644 --- a/src/client/application/diagnostics/checks/envPathVariable.ts +++ b/src/client/application/diagnostics/checks/envPathVariable.ts @@ -89,6 +89,6 @@ export class EnvironmentPathVariableDiagnosticsService extends BaseDiagnosticsSe const pathValue = currentProc.env[this.platform.pathVariableName]; const pathSeparator = this.serviceContainer.get(IPathUtils).delimiter; const paths = (pathValue || '').split(pathSeparator); - return paths.filter(item => item.indexOf('"') >= 0).length > 0; + return paths.filter((item) => item.indexOf('"') >= 0).length > 0; } } diff --git a/src/client/application/diagnostics/checks/invalidLaunchJsonDebugger.ts b/src/client/application/diagnostics/checks/invalidLaunchJsonDebugger.ts index b41221e7e604..902da9549b4a 100644 --- a/src/client/application/diagnostics/checks/invalidLaunchJsonDebugger.ts +++ b/src/client/application/diagnostics/checks/invalidLaunchJsonDebugger.ts @@ -69,7 +69,7 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService { return this.diagnoseWorkspace(workspaceFolder, resource); } protected async onHandle(diagnostics: IDiagnostic[]): Promise { - diagnostics.forEach(diagnostic => this.handleDiagnostic(diagnostic)); + diagnostics.forEach((diagnostic) => this.handleDiagnostic(diagnostic)); } protected async fixLaunchJson(code: DiagnosticCodes) { if (!this.workspaceService.hasWorkspaceFolders) { @@ -77,7 +77,7 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService { } await Promise.all( - this.workspaceService.workspaceFolders!.map(workspaceFolder => + this.workspaceService.workspaceFolders!.map((workspaceFolder) => this.fixLaunchJsonInWorkspace(code, workspaceFolder) ) ); diff --git a/src/client/application/diagnostics/checks/invalidPythonPathInDebugger.ts b/src/client/application/diagnostics/checks/invalidPythonPathInDebugger.ts index 492bbf9c06c8..7582e2530fd2 100644 --- a/src/client/application/diagnostics/checks/invalidPythonPathInDebugger.ts +++ b/src/client/application/diagnostics/checks/invalidPythonPathInDebugger.ts @@ -90,7 +90,7 @@ export class InvalidPythonPathInDebuggerService extends BaseDiagnosticsService resource ) ]) - .catch(ex => traceError('Failed to handle invalid python path in launch.json debugger', ex)) + .catch((ex) => traceError('Failed to handle invalid python path in launch.json debugger', ex)) .ignoreErrors(); } else { this.handle([ @@ -99,7 +99,7 @@ export class InvalidPythonPathInDebuggerService extends BaseDiagnosticsService resource ) ]) - .catch(ex => traceError('Failed to handle invalid python path in settings.json debugger', ex)) + .catch((ex) => traceError('Failed to handle invalid python path in settings.json debugger', ex)) .ignoreErrors(); } return false; diff --git a/src/client/application/diagnostics/checks/macPythonInterpreter.ts b/src/client/application/diagnostics/checks/macPythonInterpreter.ts index baa59aa22dc2..ec1e21010935 100644 --- a/src/client/application/diagnostics/checks/macPythonInterpreter.ts +++ b/src/client/application/diagnostics/checks/macPythonInterpreter.ts @@ -94,7 +94,7 @@ export class InvalidMacPythonInterpreterService extends BaseDiagnosticsService { } const interpreters = await this.interpreterService.getInterpreters(resource); - if (interpreters.filter(i => !this.helper.isMacDefaultPythonPath(i.path)).length === 0) { + if (interpreters.filter((i) => !this.helper.isMacDefaultPythonPath(i.path)).length === 0) { return [ new InvalidMacPythonInterpreterDiagnostic( DiagnosticCodes.MacInterpreterSelectedAndNoOtherInterpretersDiagnostic, @@ -119,7 +119,7 @@ export class InvalidMacPythonInterpreterService extends BaseDiagnosticsService { DiagnosticCommandPromptHandlerServiceId ); await Promise.all( - diagnostics.map(async diagnostic => { + diagnostics.map(async (diagnostic) => { const canHandle = await this.canHandle(diagnostic); const shouldIgnore = await this.filterService.shouldIgnoreDiagnostic(diagnostic.code); if (!canHandle || shouldIgnore) { @@ -138,9 +138,11 @@ export class InvalidMacPythonInterpreterService extends BaseDiagnosticsService { protected async onDidChangeConfiguration(event: ConfigurationChangeEvent) { const workspaceService = this.serviceContainer.get(IWorkspaceService); const workspacesUris: (Uri | undefined)[] = workspaceService.hasWorkspaceFolders - ? workspaceService.workspaceFolders!.map(workspace => workspace.uri) + ? workspaceService.workspaceFolders!.map((workspace) => workspace.uri) : [undefined]; - const workspaceUriIndex = workspacesUris.findIndex(uri => event.affectsConfiguration('python.pythonPath', uri)); + const workspaceUriIndex = workspacesUris.findIndex((uri) => + event.affectsConfiguration('python.pythonPath', uri) + ); if (workspaceUriIndex === -1) { return; } @@ -153,7 +155,7 @@ export class InvalidMacPythonInterpreterService extends BaseDiagnosticsService { this.timeOut = setTimeout(() => { this.timeOut = undefined; this.diagnose(workspacesUris[workspaceUriIndex]) - .then(diagnostics => this.handle(diagnostics)) + .then((diagnostics) => this.handle(diagnostics)) .ignoreErrors(); }, this.changeThrottleTimeout); } diff --git a/src/client/application/diagnostics/checks/powerShellActivation.ts b/src/client/application/diagnostics/checks/powerShellActivation.ts index 99f2326f9f9a..a2e8c08ff5c5 100644 --- a/src/client/application/diagnostics/checks/powerShellActivation.ts +++ b/src/client/application/diagnostics/checks/powerShellActivation.ts @@ -80,7 +80,7 @@ export class PowerShellActivationHackDiagnosticsService extends BaseDiagnosticsS sendTelemetryEvent(EventName.DIAGNOSTICS_ACTION, undefined, { action: 'switchToCommandPrompt' }); - useCommandPromptAsDefaultShell(currentProcess, configurationService).catch(ex => + useCommandPromptAsDefaultShell(currentProcess, configurationService).catch((ex) => traceError('Use Command Prompt as default shell', ex) ); } diff --git a/src/client/application/diagnostics/checks/pythonInterpreter.ts b/src/client/application/diagnostics/checks/pythonInterpreter.ts index e87a537036fe..11aaba9671a2 100644 --- a/src/client/application/diagnostics/checks/pythonInterpreter.ts +++ b/src/client/application/diagnostics/checks/pythonInterpreter.ts @@ -86,7 +86,7 @@ export class InvalidPythonInterpreterService extends BaseDiagnosticsService { DiagnosticCommandPromptHandlerServiceId ); await Promise.all( - diagnostics.map(async diagnostic => { + diagnostics.map(async (diagnostic) => { if (!this.canHandle(diagnostic)) { return; } diff --git a/src/client/application/diagnostics/promptHandler.ts b/src/client/application/diagnostics/promptHandler.ts index 2504e0c2ff71..6f244d3d4bc4 100644 --- a/src/client/application/diagnostics/promptHandler.ts +++ b/src/client/application/diagnostics/promptHandler.ts @@ -29,7 +29,7 @@ export class DiagnosticCommandPromptHandlerService implements IDiagnosticHandler diagnostic: IDiagnostic, options: MessageCommandPrompt = { commandPrompts: [] } ): Promise { - const prompts = options.commandPrompts.map(option => option.prompt); + const prompts = options.commandPrompts.map((option) => option.prompt); const response = await this.displayMessage( options.message ? options.message : diagnostic.message, diagnostic.severity, @@ -38,7 +38,7 @@ export class DiagnosticCommandPromptHandlerService implements IDiagnosticHandler if (!response) { return; } - const selectedOption = options.commandPrompts.find(option => option.prompt === response); + const selectedOption = options.commandPrompts.find((option) => option.prompt === response); if (selectedOption && selectedOption.command) { await selectedOption.command.invoke(); } diff --git a/src/client/common/application/activeResource.ts b/src/client/common/application/activeResource.ts index 972a78963a22..a89386b0d9cf 100644 --- a/src/client/common/application/activeResource.ts +++ b/src/client/common/application/activeResource.ts @@ -1,27 +1,27 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { inject, injectable } from 'inversify'; -import { Resource } from '../types'; -import { IActiveResourceService, IDocumentManager, IWorkspaceService } from './types'; - -@injectable() -export class ActiveResourceService implements IActiveResourceService { - constructor( - @inject(IDocumentManager) private readonly documentManager: IDocumentManager, - @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService - ) {} - - public getActiveResource(): Resource { - const editor = this.documentManager.activeTextEditor; - if (editor && !editor.document.isUntitled) { - return editor.document.uri; - } - return Array.isArray(this.workspaceService.workspaceFolders) && - this.workspaceService.workspaceFolders.length > 0 - ? this.workspaceService.workspaceFolders[0].uri - : undefined; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { inject, injectable } from 'inversify'; +import { Resource } from '../types'; +import { IActiveResourceService, IDocumentManager, IWorkspaceService } from './types'; + +@injectable() +export class ActiveResourceService implements IActiveResourceService { + constructor( + @inject(IDocumentManager) private readonly documentManager: IDocumentManager, + @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService + ) {} + + public getActiveResource(): Resource { + const editor = this.documentManager.activeTextEditor; + if (editor && !editor.document.isUntitled) { + return editor.document.uri; + } + return Array.isArray(this.workspaceService.workspaceFolders) && + this.workspaceService.workspaceFolders.length > 0 + ? this.workspaceService.workspaceFolders[0].uri + : undefined; + } +} diff --git a/src/client/common/application/applicationShell.ts b/src/client/common/application/applicationShell.ts index 1ed917cc3c86..d7be74bfd103 100644 --- a/src/client/common/application/applicationShell.ts +++ b/src/client/common/application/applicationShell.ts @@ -1,136 +1,136 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -// tslint:disable:no-var-requires no-any unified-signatures - -import { injectable } from 'inversify'; -import { - CancellationToken, - Disposable, - env, - Event, - InputBox, - InputBoxOptions, - MessageItem, - MessageOptions, - OpenDialogOptions, - OutputChannel, - Progress, - ProgressOptions, - QuickPick, - QuickPickItem, - QuickPickOptions, - SaveDialogOptions, - StatusBarAlignment, - StatusBarItem, - TreeView, - TreeViewOptions, - Uri, - window, - WindowState, - WorkspaceFolder, - WorkspaceFolderPickOptions -} from 'vscode'; -import { IApplicationShell } from './types'; - -@injectable() -export class ApplicationShell implements IApplicationShell { - public get onDidChangeWindowState(): Event { - return window.onDidChangeWindowState; - } - public showInformationMessage(message: string, ...items: string[]): Thenable; - public showInformationMessage(message: string, options: MessageOptions, ...items: string[]): Thenable; - public showInformationMessage(message: string, ...items: T[]): Thenable; - public showInformationMessage( - message: string, - options: MessageOptions, - ...items: T[] - ): Thenable; - public showInformationMessage(message: string, options?: any, ...items: any[]): Thenable { - return window.showInformationMessage(message, options, ...items); - } - - public showWarningMessage(message: string, ...items: string[]): Thenable; - public showWarningMessage(message: string, options: MessageOptions, ...items: string[]): Thenable; - public showWarningMessage(message: string, ...items: T[]): Thenable; - public showWarningMessage( - message: string, - options: MessageOptions, - ...items: T[] - ): Thenable; - public showWarningMessage(message: any, options?: any, ...items: any[]) { - return window.showWarningMessage(message, options, ...items); - } - - public showErrorMessage(message: string, ...items: string[]): Thenable; - public showErrorMessage(message: string, options: MessageOptions, ...items: string[]): Thenable; - public showErrorMessage(message: string, ...items: T[]): Thenable; - public showErrorMessage( - message: string, - options: MessageOptions, - ...items: T[] - ): Thenable; - public showErrorMessage(message: any, options?: any, ...items: any[]) { - return window.showErrorMessage(message, options, ...items); - } - - public showQuickPick( - items: string[] | Thenable, - options?: QuickPickOptions, - token?: CancellationToken - ): Thenable; - public showQuickPick( - items: T[] | Thenable, - options?: QuickPickOptions, - token?: CancellationToken - ): Thenable; - public showQuickPick(items: any, options?: any, token?: any): Thenable { - return window.showQuickPick(items, options, token); - } - - public showOpenDialog(options: OpenDialogOptions): Thenable { - return window.showOpenDialog(options); - } - public showSaveDialog(options: SaveDialogOptions): Thenable { - return window.showSaveDialog(options); - } - public showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable { - return window.showInputBox(options, token); - } - public openUrl(url: string): void { - env.openExternal(Uri.parse(url)); - } - - public setStatusBarMessage(text: string, hideAfterTimeout: number): Disposable; - public setStatusBarMessage(text: string, hideWhenDone: Thenable): Disposable; - public setStatusBarMessage(text: string): Disposable; - public setStatusBarMessage(text: string, arg?: any): Disposable { - return window.setStatusBarMessage(text, arg); - } - - public createStatusBarItem(alignment?: StatusBarAlignment, priority?: number): StatusBarItem { - return window.createStatusBarItem(alignment, priority); - } - public showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions): Thenable { - return window.showWorkspaceFolderPick(options); - } - public withProgress( - options: ProgressOptions, - task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => Thenable - ): Thenable { - return window.withProgress(options, task); - } - public createQuickPick(): QuickPick { - return window.createQuickPick(); - } - public createInputBox(): InputBox { - return window.createInputBox(); - } - public createTreeView(viewId: string, options: TreeViewOptions): TreeView { - return window.createTreeView(viewId, options); - } - public createOutputChannel(name: string): OutputChannel { - return window.createOutputChannel(name); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +// tslint:disable:no-var-requires no-any unified-signatures + +import { injectable } from 'inversify'; +import { + CancellationToken, + Disposable, + env, + Event, + InputBox, + InputBoxOptions, + MessageItem, + MessageOptions, + OpenDialogOptions, + OutputChannel, + Progress, + ProgressOptions, + QuickPick, + QuickPickItem, + QuickPickOptions, + SaveDialogOptions, + StatusBarAlignment, + StatusBarItem, + TreeView, + TreeViewOptions, + Uri, + window, + WindowState, + WorkspaceFolder, + WorkspaceFolderPickOptions +} from 'vscode'; +import { IApplicationShell } from './types'; + +@injectable() +export class ApplicationShell implements IApplicationShell { + public get onDidChangeWindowState(): Event { + return window.onDidChangeWindowState; + } + public showInformationMessage(message: string, ...items: string[]): Thenable; + public showInformationMessage(message: string, options: MessageOptions, ...items: string[]): Thenable; + public showInformationMessage(message: string, ...items: T[]): Thenable; + public showInformationMessage( + message: string, + options: MessageOptions, + ...items: T[] + ): Thenable; + public showInformationMessage(message: string, options?: any, ...items: any[]): Thenable { + return window.showInformationMessage(message, options, ...items); + } + + public showWarningMessage(message: string, ...items: string[]): Thenable; + public showWarningMessage(message: string, options: MessageOptions, ...items: string[]): Thenable; + public showWarningMessage(message: string, ...items: T[]): Thenable; + public showWarningMessage( + message: string, + options: MessageOptions, + ...items: T[] + ): Thenable; + public showWarningMessage(message: any, options?: any, ...items: any[]) { + return window.showWarningMessage(message, options, ...items); + } + + public showErrorMessage(message: string, ...items: string[]): Thenable; + public showErrorMessage(message: string, options: MessageOptions, ...items: string[]): Thenable; + public showErrorMessage(message: string, ...items: T[]): Thenable; + public showErrorMessage( + message: string, + options: MessageOptions, + ...items: T[] + ): Thenable; + public showErrorMessage(message: any, options?: any, ...items: any[]) { + return window.showErrorMessage(message, options, ...items); + } + + public showQuickPick( + items: string[] | Thenable, + options?: QuickPickOptions, + token?: CancellationToken + ): Thenable; + public showQuickPick( + items: T[] | Thenable, + options?: QuickPickOptions, + token?: CancellationToken + ): Thenable; + public showQuickPick(items: any, options?: any, token?: any): Thenable { + return window.showQuickPick(items, options, token); + } + + public showOpenDialog(options: OpenDialogOptions): Thenable { + return window.showOpenDialog(options); + } + public showSaveDialog(options: SaveDialogOptions): Thenable { + return window.showSaveDialog(options); + } + public showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable { + return window.showInputBox(options, token); + } + public openUrl(url: string): void { + env.openExternal(Uri.parse(url)); + } + + public setStatusBarMessage(text: string, hideAfterTimeout: number): Disposable; + public setStatusBarMessage(text: string, hideWhenDone: Thenable): Disposable; + public setStatusBarMessage(text: string): Disposable; + public setStatusBarMessage(text: string, arg?: any): Disposable { + return window.setStatusBarMessage(text, arg); + } + + public createStatusBarItem(alignment?: StatusBarAlignment, priority?: number): StatusBarItem { + return window.createStatusBarItem(alignment, priority); + } + public showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions): Thenable { + return window.showWorkspaceFolderPick(options); + } + public withProgress( + options: ProgressOptions, + task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => Thenable + ): Thenable { + return window.withProgress(options, task); + } + public createQuickPick(): QuickPick { + return window.createQuickPick(); + } + public createInputBox(): InputBox { + return window.createInputBox(); + } + public createTreeView(viewId: string, options: TreeViewOptions): TreeView { + return window.createTreeView(viewId, options); + } + public createOutputChannel(name: string): OutputChannel { + return window.createOutputChannel(name); + } +} diff --git a/src/client/common/application/webPanels/webPanel.ts b/src/client/common/application/webPanels/webPanel.ts index f81b7f3337a8..8e9a7f1f2658 100644 --- a/src/client/common/application/webPanels/webPanel.ts +++ b/src/client/common/application/webPanels/webPanel.ts @@ -96,8 +96,8 @@ export class WebPanel implements IWebPanel { // tslint:disable-next-line:no-any private async load() { if (this.panel) { - const localFilesExist = await Promise.all(this.options.scripts.map(s => this.fs.fileExists(s))); - if (localFilesExist.every(exists => exists === true)) { + const localFilesExist = await Promise.all(this.options.scripts.map((s) => this.fs.fileExists(s))); + if (localFilesExist.every((exists) => exists === true)) { // Call our special function that sticks this script inside of an html page // and translates all of the paths to vscode-resource URIs this.panel.webview.html = this.options.startHttpServer @@ -113,14 +113,14 @@ export class WebPanel implements IWebPanel { ); this.disposableRegistry.push( - this.panel.webview.onDidReceiveMessage(message => { + this.panel.webview.onDidReceiveMessage((message) => { // Pass the message onto our listener this.options.listener.onMessage(message.type, message.payload); }) ); this.disposableRegistry.push( - this.panel.onDidChangeViewState(_e => { + this.panel.onDidChangeViewState((_e) => { // Pass the state change onto our listener this.options.listener.onChangeViewState(this); }) @@ -139,14 +139,14 @@ export class WebPanel implements IWebPanel { // tslint:disable-next-line:no-any private async generateLocalReactHtml(webView: Webview) { const uriBase = webView.asWebviewUri(Uri.file(this.options.cwd)).toString(); - const uris = this.options.scripts.map(script => webView.asWebviewUri(Uri.file(script))); + const uris = this.options.scripts.map((script) => webView.asWebviewUri(Uri.file(script))); const testFiles = await this.fs.getFiles(this.options.rootPath); // This method must be called so VSC is aware of files that can be pulled. // Allow js and js.map files to be loaded by webpack in the webview. testFiles - .filter(f => f.toLowerCase().endsWith('.js') || f.toLowerCase().endsWith('.js.map')) - .forEach(f => webView.asWebviewUri(Uri.file(f))); + .filter((f) => f.toLowerCase().endsWith('.js') || f.toLowerCase().endsWith('.js.map')) + .forEach((f) => webView.asWebviewUri(Uri.file(f))); const rootPath = webView.asWebviewUri(Uri.file(this.options.rootPath)).toString(); return ` @@ -176,7 +176,7 @@ export class WebPanel implements IWebPanel { return "${uriBase}" + relativePath; } - ${uris.map(uri => ``).join('\n')} + ${uris.map((uri) => ``).join('\n')} `; } @@ -184,8 +184,8 @@ export class WebPanel implements IWebPanel { // tslint:disable-next-line:no-any private generateServerReactHtml(webView: Webview) { const uriBase = webView.asWebviewUri(Uri.file(this.options.rootPath)); - const relativeScripts = this.options.scripts.map(s => `.${s.substr(this.options.rootPath.length)}`); - const encoded = relativeScripts.map(s => + const relativeScripts = this.options.scripts.map((s) => `.${s.substr(this.options.rootPath.length)}`); + const encoded = relativeScripts.map((s) => encodeURIComponent(s.replace(/\\/g, '/').replace('index_bundle.js', 'index_chunked_bundle.js')) ); diff --git a/src/client/common/application/webPanels/webPanelServer.ts b/src/client/common/application/webPanels/webPanelServer.ts index 19d3f865e62a..660478add619 100644 --- a/src/client/common/application/webPanels/webPanelServer.ts +++ b/src/client/common/application/webPanels/webPanelServer.ts @@ -26,7 +26,7 @@ export class WebPanelServer { this.app.use(Cors()); this.app.use(compress()); this.app.use(logger()); - this.app.use(async ctx => { + this.app.use(async (ctx) => { try { // Either token is passed or cookie exists, otherwise insecure connection if (ctx.query.token) { diff --git a/src/client/common/asyncDisposableRegistry.ts b/src/client/common/asyncDisposableRegistry.ts index 590727a445f1..10bd9492b31e 100644 --- a/src/client/common/asyncDisposableRegistry.ts +++ b/src/client/common/asyncDisposableRegistry.ts @@ -1,27 +1,27 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { injectable } from 'inversify'; -import { IAsyncDisposable, IAsyncDisposableRegistry, IDisposable } from './types'; - -// List of disposables that need to run a promise. -@injectable() -export class AsyncDisposableRegistry implements IAsyncDisposableRegistry { - private _list: (IDisposable | IAsyncDisposable)[] = []; - - public async dispose(): Promise { - const promises = this._list.map(l => l.dispose()); - await Promise.all(promises); - this._list = []; - } - - public push(disposable?: IDisposable | IAsyncDisposable) { - if (disposable) { - this._list.push(disposable); - } - } - - public get list(): (IDisposable | IAsyncDisposable)[] { - return this._list; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { injectable } from 'inversify'; +import { IAsyncDisposable, IAsyncDisposableRegistry, IDisposable } from './types'; + +// List of disposables that need to run a promise. +@injectable() +export class AsyncDisposableRegistry implements IAsyncDisposableRegistry { + private _list: (IDisposable | IAsyncDisposable)[] = []; + + public async dispose(): Promise { + const promises = this._list.map((l) => l.dispose()); + await Promise.all(promises); + this._list = []; + } + + public push(disposable?: IDisposable | IAsyncDisposable) { + if (disposable) { + this._list.push(disposable); + } + } + + public get list(): (IDisposable | IAsyncDisposable)[] { + return this._list; + } +} diff --git a/src/client/common/cancellation.ts b/src/client/common/cancellation.ts index 6ed29c22eb0a..601ac933979d 100644 --- a/src/client/common/cancellation.ts +++ b/src/client/common/cancellation.ts @@ -95,12 +95,12 @@ export namespace Cancellation { // Not canceled yet. When the work finishes // either resolve our promise or cancel. work(token) - .then(v => { + .then((v) => { if (!deferred.completed) { deferred.resolve(v); } }) - .catch(e => { + .catch((e) => { if (!deferred.completed) { deferred.reject(e); } diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index cfe32028cea7..8a5030d5ba5e 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -1,617 +1,617 @@ -'use strict'; - -import * as child_process from 'child_process'; -import * as path from 'path'; -import { - ConfigurationChangeEvent, - ConfigurationTarget, - DiagnosticSeverity, - Disposable, - Event, - EventEmitter, - Uri, - WorkspaceConfiguration -} from 'vscode'; -import { LanguageServerType } from '../activation/types'; -import '../common/extensions'; -import { IInterpreterAutoSeletionProxyService } from '../interpreter/autoSelection/types'; -import { sendTelemetryEvent } from '../telemetry'; -import { EventName } from '../telemetry/constants'; -import { IWorkspaceService } from './application/types'; -import { WorkspaceService } from './application/workspace'; -import { isTestExecution } from './constants'; -import { ExtensionChannels } from './insidersBuild/types'; -import { IS_WINDOWS } from './platform/constants'; -import { - IAnalysisSettings, - IAutoCompleteSettings, - IDataScienceSettings, - IExperiments, - IFormattingSettings, - ILintingSettings, - IPythonSettings, - ISortImportSettings, - ITerminalSettings, - ITestingSettings, - IWorkspaceSymbolSettings, - Resource -} from './types'; -import { debounceSync } from './utils/decorators'; -import { SystemVariables } from './variables/systemVariables'; - -// tslint:disable:no-require-imports no-var-requires -const untildify = require('untildify'); - -// tslint:disable-next-line:completed-docs -export class PythonSettings implements IPythonSettings { - private static pythonSettings: Map = new Map(); - public downloadLanguageServer = true; - public jediEnabled = true; - public jediPath = ''; - public jediMemoryLimit = 1024; - public envFile = ''; - public venvPath = ''; - public venvFolders: string[] = []; - public condaPath = ''; - public pipenvPath = ''; - public poetryPath = ''; - public devOptions: string[] = []; - public linting!: ILintingSettings; - public formatting!: IFormattingSettings; - public autoComplete!: IAutoCompleteSettings; - public testing!: ITestingSettings; - public terminal!: ITerminalSettings; - public sortImports!: ISortImportSettings; - public workspaceSymbols!: IWorkspaceSymbolSettings; - public disableInstallationChecks = false; - public globalModuleInstallation = false; - public analysis!: IAnalysisSettings; - public autoUpdateLanguageServer: boolean = true; - public datascience!: IDataScienceSettings; - public insidersChannel!: ExtensionChannels; - public experiments!: IExperiments; - public languageServer: LanguageServerType = LanguageServerType.Microsoft; - - protected readonly changed = new EventEmitter(); - private workspaceRoot: Uri; - private disposables: Disposable[] = []; - // tslint:disable-next-line:variable-name - private _pythonPath = ''; - private readonly workspace: IWorkspaceService; - public get onDidChange(): Event { - return this.changed.event; - } - - constructor( - workspaceFolder: Resource, - private readonly interpreterAutoSelectionService: IInterpreterAutoSeletionProxyService, - workspace?: IWorkspaceService - ) { - this.workspace = workspace || new WorkspaceService(); - this.workspaceRoot = workspaceFolder ? workspaceFolder : Uri.file(__dirname); - this.initialize(); - } - // tslint:disable-next-line:function-name - public static getInstance( - resource: Uri | undefined, - interpreterAutoSelectionService: IInterpreterAutoSeletionProxyService, - workspace?: IWorkspaceService - ): PythonSettings { - workspace = workspace || new WorkspaceService(); - const workspaceFolderUri = PythonSettings.getSettingsUriAndTarget(resource, workspace).uri; - const workspaceFolderKey = workspaceFolderUri ? workspaceFolderUri.fsPath : ''; - - if (!PythonSettings.pythonSettings.has(workspaceFolderKey)) { - const settings = new PythonSettings(workspaceFolderUri, interpreterAutoSelectionService, workspace); - PythonSettings.pythonSettings.set(workspaceFolderKey, settings); - // Pass null to avoid VSC from complaining about not passing in a value. - // tslint:disable-next-line:no-any - const config = workspace.getConfiguration('editor', resource ? resource : (null as any)); - const formatOnType = config ? config.get('formatOnType', false) : false; - sendTelemetryEvent(EventName.COMPLETION_ADD_BRACKETS, undefined, { - enabled: settings.autoComplete ? settings.autoComplete.addBrackets : false - }); - sendTelemetryEvent(EventName.FORMAT_ON_TYPE, undefined, { enabled: formatOnType }); - } - // tslint:disable-next-line:no-non-null-assertion - return PythonSettings.pythonSettings.get(workspaceFolderKey)!; - } - - // tslint:disable-next-line:type-literal-delimiter - public static getSettingsUriAndTarget( - resource: Uri | undefined, - workspace?: IWorkspaceService - ): { uri: Uri | undefined; target: ConfigurationTarget } { - workspace = workspace || new WorkspaceService(); - const workspaceFolder = resource ? workspace.getWorkspaceFolder(resource) : undefined; - let workspaceFolderUri: Uri | undefined = workspaceFolder ? workspaceFolder.uri : undefined; - - if (!workspaceFolderUri && Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 0) { - workspaceFolderUri = workspace.workspaceFolders[0].uri; - } - - const target = workspaceFolderUri ? ConfigurationTarget.WorkspaceFolder : ConfigurationTarget.Global; - return { uri: workspaceFolderUri, target }; - } - - // tslint:disable-next-line:function-name - public static dispose() { - if (!isTestExecution()) { - throw new Error('Dispose can only be called from unit tests'); - } - // tslint:disable-next-line:no-void-expression - PythonSettings.pythonSettings.forEach(item => item && item.dispose()); - PythonSettings.pythonSettings.clear(); - } - public dispose() { - // tslint:disable-next-line:no-unsafe-any - this.disposables.forEach(disposable => disposable && disposable.dispose()); - this.disposables = []; - } - // tslint:disable-next-line:cyclomatic-complexity max-func-body-length - protected update(pythonSettings: WorkspaceConfiguration) { - const workspaceRoot = this.workspaceRoot.fsPath; - const systemVariables: SystemVariables = new SystemVariables(undefined, workspaceRoot, this.workspace); - - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - this.pythonPath = systemVariables.resolveAny(pythonSettings.get('pythonPath'))!; - // If user has defined a custom value, use it else try to get the best interpreter ourselves. - if (this.pythonPath.length === 0 || this.pythonPath === 'python') { - const autoSelectedPythonInterpreter = this.interpreterAutoSelectionService.getAutoSelectedInterpreter( - this.workspaceRoot - ); - if (autoSelectedPythonInterpreter) { - this.interpreterAutoSelectionService - .setWorkspaceInterpreter(this.workspaceRoot, autoSelectedPythonInterpreter) - .ignoreErrors(); - } - this.pythonPath = autoSelectedPythonInterpreter ? autoSelectedPythonInterpreter.path : this.pythonPath; - } - this.pythonPath = getAbsolutePath(this.pythonPath, workspaceRoot); - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - this.venvPath = systemVariables.resolveAny(pythonSettings.get('venvPath'))!; - this.venvFolders = systemVariables.resolveAny(pythonSettings.get('venvFolders'))!; - const condaPath = systemVariables.resolveAny(pythonSettings.get('condaPath'))!; - this.condaPath = condaPath && condaPath.length > 0 ? getAbsolutePath(condaPath, workspaceRoot) : condaPath; - const pipenvPath = systemVariables.resolveAny(pythonSettings.get('pipenvPath'))!; - this.pipenvPath = pipenvPath && pipenvPath.length > 0 ? getAbsolutePath(pipenvPath, workspaceRoot) : pipenvPath; - const poetryPath = systemVariables.resolveAny(pythonSettings.get('poetryPath'))!; - this.poetryPath = poetryPath && poetryPath.length > 0 ? getAbsolutePath(poetryPath, workspaceRoot) : poetryPath; - - this.downloadLanguageServer = systemVariables.resolveAny( - pythonSettings.get('downloadLanguageServer', true) - )!; - this.jediEnabled = systemVariables.resolveAny(pythonSettings.get('jediEnabled', true))!; - this.autoUpdateLanguageServer = systemVariables.resolveAny( - pythonSettings.get('autoUpdateLanguageServer', true) - )!; - if (this.jediEnabled) { - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - this.jediPath = systemVariables.resolveAny(pythonSettings.get('jediPath'))!; - if (typeof this.jediPath === 'string' && this.jediPath.length > 0) { - this.jediPath = getAbsolutePath(systemVariables.resolveAny(this.jediPath), workspaceRoot); - } else { - this.jediPath = ''; - } - this.jediMemoryLimit = pythonSettings.get('jediMemoryLimit')!; - } - - let ls = pythonSettings.get('languageServer'); - if (!ls) { - ls = LanguageServerType.Jedi; - } - this.languageServer = systemVariables.resolveAny(ls)!; - - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - this.envFile = systemVariables.resolveAny(pythonSettings.get('envFile'))!; - // tslint:disable-next-line:no-any - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion no-any - this.devOptions = systemVariables.resolveAny(pythonSettings.get('devOptions'))!; - this.devOptions = Array.isArray(this.devOptions) ? this.devOptions : []; - - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - const lintingSettings = systemVariables.resolveAny(pythonSettings.get('linting'))!; - if (this.linting) { - Object.assign(this.linting, lintingSettings); - } else { - this.linting = lintingSettings; - } - - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - const analysisSettings = systemVariables.resolveAny(pythonSettings.get('analysis'))!; - if (this.analysis) { - Object.assign(this.analysis, analysisSettings); - } else { - this.analysis = analysisSettings; - } - - this.disableInstallationChecks = pythonSettings.get('disableInstallationCheck') === true; - this.globalModuleInstallation = pythonSettings.get('globalModuleInstallation') === true; - - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - const sortImportSettings = systemVariables.resolveAny(pythonSettings.get('sortImports'))!; - if (this.sortImports) { - Object.assign(this.sortImports, sortImportSettings); - } else { - this.sortImports = sortImportSettings; - } - // Support for travis. - this.sortImports = this.sortImports ? this.sortImports : { path: '', args: [] }; - // Support for travis. - this.linting = this.linting - ? this.linting - : { - enabled: false, - ignorePatterns: [], - flake8Args: [], - flake8Enabled: false, - flake8Path: 'flake', - lintOnSave: false, - maxNumberOfProblems: 100, - mypyArgs: [], - mypyEnabled: false, - mypyPath: 'mypy', - banditArgs: [], - banditEnabled: false, - banditPath: 'bandit', - pycodestyleArgs: [], - pycodestyleEnabled: false, - pycodestylePath: 'pycodestyle', - pylamaArgs: [], - pylamaEnabled: false, - pylamaPath: 'pylama', - prospectorArgs: [], - prospectorEnabled: false, - prospectorPath: 'prospector', - pydocstyleArgs: [], - pydocstyleEnabled: false, - pydocstylePath: 'pydocstyle', - pylintArgs: [], - pylintEnabled: false, - pylintPath: 'pylint', - pylintCategorySeverity: { - convention: DiagnosticSeverity.Hint, - error: DiagnosticSeverity.Error, - fatal: DiagnosticSeverity.Error, - refactor: DiagnosticSeverity.Hint, - warning: DiagnosticSeverity.Warning - }, - pycodestyleCategorySeverity: { - E: DiagnosticSeverity.Error, - W: DiagnosticSeverity.Warning - }, - flake8CategorySeverity: { - E: DiagnosticSeverity.Error, - W: DiagnosticSeverity.Warning, - // Per http://flake8.pycqa.org/en/latest/glossary.html#term-error-code - // 'F' does not mean 'fatal as in PyLint but rather 'pyflakes' such as - // unused imports, variables, etc. - F: DiagnosticSeverity.Warning - }, - mypyCategorySeverity: { - error: DiagnosticSeverity.Error, - note: DiagnosticSeverity.Hint - }, - pylintUseMinimalCheckers: false - }; - this.linting.pylintPath = getAbsolutePath(systemVariables.resolveAny(this.linting.pylintPath), workspaceRoot); - this.linting.flake8Path = getAbsolutePath(systemVariables.resolveAny(this.linting.flake8Path), workspaceRoot); - this.linting.pycodestylePath = getAbsolutePath( - systemVariables.resolveAny(this.linting.pycodestylePath), - workspaceRoot - ); - this.linting.pylamaPath = getAbsolutePath(systemVariables.resolveAny(this.linting.pylamaPath), workspaceRoot); - this.linting.prospectorPath = getAbsolutePath( - systemVariables.resolveAny(this.linting.prospectorPath), - workspaceRoot - ); - this.linting.pydocstylePath = getAbsolutePath( - systemVariables.resolveAny(this.linting.pydocstylePath), - workspaceRoot - ); - this.linting.mypyPath = getAbsolutePath(systemVariables.resolveAny(this.linting.mypyPath), workspaceRoot); - this.linting.banditPath = getAbsolutePath(systemVariables.resolveAny(this.linting.banditPath), workspaceRoot); - - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - const formattingSettings = systemVariables.resolveAny(pythonSettings.get('formatting'))!; - if (this.formatting) { - Object.assign(this.formatting, formattingSettings); - } else { - this.formatting = formattingSettings; - } - // Support for travis. - this.formatting = this.formatting - ? this.formatting - : { - autopep8Args: [], - autopep8Path: 'autopep8', - provider: 'autopep8', - blackArgs: [], - blackPath: 'black', - yapfArgs: [], - yapfPath: 'yapf' - }; - this.formatting.autopep8Path = getAbsolutePath( - systemVariables.resolveAny(this.formatting.autopep8Path), - workspaceRoot - ); - this.formatting.yapfPath = getAbsolutePath(systemVariables.resolveAny(this.formatting.yapfPath), workspaceRoot); - this.formatting.blackPath = getAbsolutePath( - systemVariables.resolveAny(this.formatting.blackPath), - workspaceRoot - ); - - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - const autoCompleteSettings = systemVariables.resolveAny( - pythonSettings.get('autoComplete') - )!; - if (this.autoComplete) { - Object.assign(this.autoComplete, autoCompleteSettings); - } else { - this.autoComplete = autoCompleteSettings; - } - // Support for travis. - this.autoComplete = this.autoComplete - ? this.autoComplete - : { - extraPaths: [], - addBrackets: false, - showAdvancedMembers: false, - typeshedPaths: [] - }; - - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - const workspaceSymbolsSettings = systemVariables.resolveAny( - pythonSettings.get('workspaceSymbols') - )!; - if (this.workspaceSymbols) { - Object.assign( - this.workspaceSymbols, - workspaceSymbolsSettings - ); - } else { - this.workspaceSymbols = workspaceSymbolsSettings; - } - // Support for travis. - this.workspaceSymbols = this.workspaceSymbols - ? this.workspaceSymbols - : { - ctagsPath: 'ctags', - enabled: true, - exclusionPatterns: [], - rebuildOnFileSave: true, - rebuildOnStart: true, - tagFilePath: path.join(workspaceRoot, 'tags') - }; - this.workspaceSymbols.tagFilePath = getAbsolutePath( - systemVariables.resolveAny(this.workspaceSymbols.tagFilePath), - workspaceRoot - ); - - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - const testSettings = systemVariables.resolveAny(pythonSettings.get('testing'))!; - if (this.testing) { - Object.assign(this.testing, testSettings); - } else { - this.testing = testSettings; - if (isTestExecution() && !this.testing) { - // tslint:disable-next-line:prefer-type-cast - // tslint:disable-next-line:no-object-literal-type-assertion - this.testing = { - nosetestArgs: [], - pytestArgs: [], - unittestArgs: [], - promptToConfigure: true, - debugPort: 3000, - nosetestsEnabled: false, - pytestEnabled: false, - unittestEnabled: false, - nosetestPath: 'nosetests', - pytestPath: 'pytest', - autoTestDiscoverOnSaveEnabled: true - } as ITestingSettings; - } - } - - // Support for travis. - this.testing = this.testing - ? this.testing - : { - promptToConfigure: true, - debugPort: 3000, - nosetestArgs: [], - nosetestPath: 'nosetest', - nosetestsEnabled: false, - pytestArgs: [], - pytestEnabled: false, - pytestPath: 'pytest', - unittestArgs: [], - unittestEnabled: false, - autoTestDiscoverOnSaveEnabled: true - }; - this.testing.pytestPath = getAbsolutePath(systemVariables.resolveAny(this.testing.pytestPath), workspaceRoot); - this.testing.nosetestPath = getAbsolutePath( - systemVariables.resolveAny(this.testing.nosetestPath), - workspaceRoot - ); - if (this.testing.cwd) { - this.testing.cwd = getAbsolutePath(systemVariables.resolveAny(this.testing.cwd), workspaceRoot); - } - - // Resolve any variables found in the test arguments. - this.testing.nosetestArgs = this.testing.nosetestArgs.map(arg => systemVariables.resolveAny(arg)); - this.testing.pytestArgs = this.testing.pytestArgs.map(arg => systemVariables.resolveAny(arg)); - this.testing.unittestArgs = this.testing.unittestArgs.map(arg => systemVariables.resolveAny(arg)); - - // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion - const terminalSettings = systemVariables.resolveAny(pythonSettings.get('terminal'))!; - if (this.terminal) { - Object.assign(this.terminal, terminalSettings); - } else { - this.terminal = terminalSettings; - if (isTestExecution() && !this.terminal) { - // tslint:disable-next-line:prefer-type-cast - // tslint:disable-next-line:no-object-literal-type-assertion - this.terminal = {} as ITerminalSettings; - } - } - // Support for travis. - this.terminal = this.terminal - ? this.terminal - : { - executeInFileDir: true, - launchArgs: [], - activateEnvironment: true, - activateEnvInCurrentTerminal: false - }; - - const experiments = systemVariables.resolveAny(pythonSettings.get('experiments'))!; - if (this.experiments) { - Object.assign(this.experiments, experiments); - } else { - this.experiments = experiments; - } - this.experiments = this.experiments - ? this.experiments - : { - enabled: true, - optInto: [], - optOutFrom: [] - }; - - const dataScienceSettings = systemVariables.resolveAny( - pythonSettings.get('dataScience') - )!; - if (this.datascience) { - Object.assign(this.datascience, dataScienceSettings); - } else { - this.datascience = dataScienceSettings; - } - - this.insidersChannel = pythonSettings.get('insidersChannel')!; - } - - public get pythonPath(): string { - return this._pythonPath; - } - public set pythonPath(value: string) { - if (this._pythonPath === value) { - return; - } - // Add support for specifying just the directory where the python executable will be located. - // E.g. virtual directory name. - try { - this._pythonPath = this.getPythonExecutable(value); - } catch (ex) { - this._pythonPath = value; - } - } - protected getPythonExecutable(pythonPath: string) { - return getPythonExecutable(pythonPath); - } - protected onWorkspaceFoldersChanged() { - //If an activated workspace folder was removed, delete its key - const workspaceKeys = this.workspace.workspaceFolders!.map(workspaceFolder => workspaceFolder.uri.fsPath); - const activatedWkspcKeys = Array.from(PythonSettings.pythonSettings.keys()); - const activatedWkspcFoldersRemoved = activatedWkspcKeys.filter(item => workspaceKeys.indexOf(item) < 0); - if (activatedWkspcFoldersRemoved.length > 0) { - for (const folder of activatedWkspcFoldersRemoved) { - PythonSettings.pythonSettings.delete(folder); - } - } - } - protected initialize(): void { - const onDidChange = () => { - const currentConfig = this.workspace.getConfiguration('python', this.workspaceRoot); - this.update(currentConfig); - - // If workspace config changes, then we could have a cascading effect of on change events. - // Let's defer the change notification. - this.debounceChangeNotification(); - }; - this.disposables.push(this.workspace.onDidChangeWorkspaceFolders(this.onWorkspaceFoldersChanged, this)); - this.disposables.push( - this.interpreterAutoSelectionService.onDidChangeAutoSelectedInterpreter(onDidChange.bind(this)) - ); - this.disposables.push( - this.workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => { - if (event.affectsConfiguration('python')) { - onDidChange(); - } - }) - ); - - const initialConfig = this.workspace.getConfiguration('python', this.workspaceRoot); - if (initialConfig) { - this.update(initialConfig); - } - } - @debounceSync(1) - protected debounceChangeNotification() { - this.changed.fire(); - } -} - -function getAbsolutePath(pathToCheck: string, rootDir: string): string { - // tslint:disable-next-line:prefer-type-cast no-unsafe-any - pathToCheck = untildify(pathToCheck) as string; - if (isTestExecution() && !pathToCheck) { - return rootDir; - } - if (pathToCheck.indexOf(path.sep) === -1) { - return pathToCheck; - } - return path.isAbsolute(pathToCheck) ? pathToCheck : path.resolve(rootDir, pathToCheck); -} - -function getPythonExecutable(pythonPath: string): string { - // tslint:disable-next-line:prefer-type-cast no-unsafe-any - pythonPath = untildify(pythonPath) as string; - - // If only 'python'. - if ( - pythonPath === 'python' || - pythonPath.indexOf(path.sep) === -1 || - path.basename(pythonPath) === path.dirname(pythonPath) - ) { - return pythonPath; - } - - if (isValidPythonPath(pythonPath)) { - return pythonPath; - } - // Keep python right on top, for backwards compatibility. - // tslint:disable-next-line:variable-name - const KnownPythonExecutables = ['python', 'python4', 'python3.6', 'python3.5', 'python3', 'python2.7', 'python2']; - - for (let executableName of KnownPythonExecutables) { - // Suffix with 'python' for linux and 'osx', and 'python.exe' for 'windows'. - if (IS_WINDOWS) { - executableName = `${executableName}.exe`; - if (isValidPythonPath(path.join(pythonPath, executableName))) { - return path.join(pythonPath, executableName); - } - if (isValidPythonPath(path.join(pythonPath, 'scripts', executableName))) { - return path.join(pythonPath, 'scripts', executableName); - } - } else { - if (isValidPythonPath(path.join(pythonPath, executableName))) { - return path.join(pythonPath, executableName); - } - if (isValidPythonPath(path.join(pythonPath, 'bin', executableName))) { - return path.join(pythonPath, 'bin', executableName); - } - } - } - - return pythonPath; -} - -function isValidPythonPath(pythonPath: string): boolean { - try { - const output = child_process.execFileSync(pythonPath, ['-c', 'print(1234)'], { encoding: 'utf8' }); - return output.startsWith('1234'); - } catch (ex) { - return false; - } -} +'use strict'; + +import * as child_process from 'child_process'; +import * as path from 'path'; +import { + ConfigurationChangeEvent, + ConfigurationTarget, + DiagnosticSeverity, + Disposable, + Event, + EventEmitter, + Uri, + WorkspaceConfiguration +} from 'vscode'; +import { LanguageServerType } from '../activation/types'; +import '../common/extensions'; +import { IInterpreterAutoSeletionProxyService } from '../interpreter/autoSelection/types'; +import { sendTelemetryEvent } from '../telemetry'; +import { EventName } from '../telemetry/constants'; +import { IWorkspaceService } from './application/types'; +import { WorkspaceService } from './application/workspace'; +import { isTestExecution } from './constants'; +import { ExtensionChannels } from './insidersBuild/types'; +import { IS_WINDOWS } from './platform/constants'; +import { + IAnalysisSettings, + IAutoCompleteSettings, + IDataScienceSettings, + IExperiments, + IFormattingSettings, + ILintingSettings, + IPythonSettings, + ISortImportSettings, + ITerminalSettings, + ITestingSettings, + IWorkspaceSymbolSettings, + Resource +} from './types'; +import { debounceSync } from './utils/decorators'; +import { SystemVariables } from './variables/systemVariables'; + +// tslint:disable:no-require-imports no-var-requires +const untildify = require('untildify'); + +// tslint:disable-next-line:completed-docs +export class PythonSettings implements IPythonSettings { + private static pythonSettings: Map = new Map(); + public downloadLanguageServer = true; + public jediEnabled = true; + public jediPath = ''; + public jediMemoryLimit = 1024; + public envFile = ''; + public venvPath = ''; + public venvFolders: string[] = []; + public condaPath = ''; + public pipenvPath = ''; + public poetryPath = ''; + public devOptions: string[] = []; + public linting!: ILintingSettings; + public formatting!: IFormattingSettings; + public autoComplete!: IAutoCompleteSettings; + public testing!: ITestingSettings; + public terminal!: ITerminalSettings; + public sortImports!: ISortImportSettings; + public workspaceSymbols!: IWorkspaceSymbolSettings; + public disableInstallationChecks = false; + public globalModuleInstallation = false; + public analysis!: IAnalysisSettings; + public autoUpdateLanguageServer: boolean = true; + public datascience!: IDataScienceSettings; + public insidersChannel!: ExtensionChannels; + public experiments!: IExperiments; + public languageServer: LanguageServerType = LanguageServerType.Microsoft; + + protected readonly changed = new EventEmitter(); + private workspaceRoot: Uri; + private disposables: Disposable[] = []; + // tslint:disable-next-line:variable-name + private _pythonPath = ''; + private readonly workspace: IWorkspaceService; + public get onDidChange(): Event { + return this.changed.event; + } + + constructor( + workspaceFolder: Resource, + private readonly interpreterAutoSelectionService: IInterpreterAutoSeletionProxyService, + workspace?: IWorkspaceService + ) { + this.workspace = workspace || new WorkspaceService(); + this.workspaceRoot = workspaceFolder ? workspaceFolder : Uri.file(__dirname); + this.initialize(); + } + // tslint:disable-next-line:function-name + public static getInstance( + resource: Uri | undefined, + interpreterAutoSelectionService: IInterpreterAutoSeletionProxyService, + workspace?: IWorkspaceService + ): PythonSettings { + workspace = workspace || new WorkspaceService(); + const workspaceFolderUri = PythonSettings.getSettingsUriAndTarget(resource, workspace).uri; + const workspaceFolderKey = workspaceFolderUri ? workspaceFolderUri.fsPath : ''; + + if (!PythonSettings.pythonSettings.has(workspaceFolderKey)) { + const settings = new PythonSettings(workspaceFolderUri, interpreterAutoSelectionService, workspace); + PythonSettings.pythonSettings.set(workspaceFolderKey, settings); + // Pass null to avoid VSC from complaining about not passing in a value. + // tslint:disable-next-line:no-any + const config = workspace.getConfiguration('editor', resource ? resource : (null as any)); + const formatOnType = config ? config.get('formatOnType', false) : false; + sendTelemetryEvent(EventName.COMPLETION_ADD_BRACKETS, undefined, { + enabled: settings.autoComplete ? settings.autoComplete.addBrackets : false + }); + sendTelemetryEvent(EventName.FORMAT_ON_TYPE, undefined, { enabled: formatOnType }); + } + // tslint:disable-next-line:no-non-null-assertion + return PythonSettings.pythonSettings.get(workspaceFolderKey)!; + } + + // tslint:disable-next-line:type-literal-delimiter + public static getSettingsUriAndTarget( + resource: Uri | undefined, + workspace?: IWorkspaceService + ): { uri: Uri | undefined; target: ConfigurationTarget } { + workspace = workspace || new WorkspaceService(); + const workspaceFolder = resource ? workspace.getWorkspaceFolder(resource) : undefined; + let workspaceFolderUri: Uri | undefined = workspaceFolder ? workspaceFolder.uri : undefined; + + if (!workspaceFolderUri && Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 0) { + workspaceFolderUri = workspace.workspaceFolders[0].uri; + } + + const target = workspaceFolderUri ? ConfigurationTarget.WorkspaceFolder : ConfigurationTarget.Global; + return { uri: workspaceFolderUri, target }; + } + + // tslint:disable-next-line:function-name + public static dispose() { + if (!isTestExecution()) { + throw new Error('Dispose can only be called from unit tests'); + } + // tslint:disable-next-line:no-void-expression + PythonSettings.pythonSettings.forEach((item) => item && item.dispose()); + PythonSettings.pythonSettings.clear(); + } + public dispose() { + // tslint:disable-next-line:no-unsafe-any + this.disposables.forEach((disposable) => disposable && disposable.dispose()); + this.disposables = []; + } + // tslint:disable-next-line:cyclomatic-complexity max-func-body-length + protected update(pythonSettings: WorkspaceConfiguration) { + const workspaceRoot = this.workspaceRoot.fsPath; + const systemVariables: SystemVariables = new SystemVariables(undefined, workspaceRoot, this.workspace); + + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + this.pythonPath = systemVariables.resolveAny(pythonSettings.get('pythonPath'))!; + // If user has defined a custom value, use it else try to get the best interpreter ourselves. + if (this.pythonPath.length === 0 || this.pythonPath === 'python') { + const autoSelectedPythonInterpreter = this.interpreterAutoSelectionService.getAutoSelectedInterpreter( + this.workspaceRoot + ); + if (autoSelectedPythonInterpreter) { + this.interpreterAutoSelectionService + .setWorkspaceInterpreter(this.workspaceRoot, autoSelectedPythonInterpreter) + .ignoreErrors(); + } + this.pythonPath = autoSelectedPythonInterpreter ? autoSelectedPythonInterpreter.path : this.pythonPath; + } + this.pythonPath = getAbsolutePath(this.pythonPath, workspaceRoot); + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + this.venvPath = systemVariables.resolveAny(pythonSettings.get('venvPath'))!; + this.venvFolders = systemVariables.resolveAny(pythonSettings.get('venvFolders'))!; + const condaPath = systemVariables.resolveAny(pythonSettings.get('condaPath'))!; + this.condaPath = condaPath && condaPath.length > 0 ? getAbsolutePath(condaPath, workspaceRoot) : condaPath; + const pipenvPath = systemVariables.resolveAny(pythonSettings.get('pipenvPath'))!; + this.pipenvPath = pipenvPath && pipenvPath.length > 0 ? getAbsolutePath(pipenvPath, workspaceRoot) : pipenvPath; + const poetryPath = systemVariables.resolveAny(pythonSettings.get('poetryPath'))!; + this.poetryPath = poetryPath && poetryPath.length > 0 ? getAbsolutePath(poetryPath, workspaceRoot) : poetryPath; + + this.downloadLanguageServer = systemVariables.resolveAny( + pythonSettings.get('downloadLanguageServer', true) + )!; + this.jediEnabled = systemVariables.resolveAny(pythonSettings.get('jediEnabled', true))!; + this.autoUpdateLanguageServer = systemVariables.resolveAny( + pythonSettings.get('autoUpdateLanguageServer', true) + )!; + if (this.jediEnabled) { + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + this.jediPath = systemVariables.resolveAny(pythonSettings.get('jediPath'))!; + if (typeof this.jediPath === 'string' && this.jediPath.length > 0) { + this.jediPath = getAbsolutePath(systemVariables.resolveAny(this.jediPath), workspaceRoot); + } else { + this.jediPath = ''; + } + this.jediMemoryLimit = pythonSettings.get('jediMemoryLimit')!; + } + + let ls = pythonSettings.get('languageServer'); + if (!ls) { + ls = LanguageServerType.Jedi; + } + this.languageServer = systemVariables.resolveAny(ls)!; + + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + this.envFile = systemVariables.resolveAny(pythonSettings.get('envFile'))!; + // tslint:disable-next-line:no-any + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion no-any + this.devOptions = systemVariables.resolveAny(pythonSettings.get('devOptions'))!; + this.devOptions = Array.isArray(this.devOptions) ? this.devOptions : []; + + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + const lintingSettings = systemVariables.resolveAny(pythonSettings.get('linting'))!; + if (this.linting) { + Object.assign(this.linting, lintingSettings); + } else { + this.linting = lintingSettings; + } + + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + const analysisSettings = systemVariables.resolveAny(pythonSettings.get('analysis'))!; + if (this.analysis) { + Object.assign(this.analysis, analysisSettings); + } else { + this.analysis = analysisSettings; + } + + this.disableInstallationChecks = pythonSettings.get('disableInstallationCheck') === true; + this.globalModuleInstallation = pythonSettings.get('globalModuleInstallation') === true; + + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + const sortImportSettings = systemVariables.resolveAny(pythonSettings.get('sortImports'))!; + if (this.sortImports) { + Object.assign(this.sortImports, sortImportSettings); + } else { + this.sortImports = sortImportSettings; + } + // Support for travis. + this.sortImports = this.sortImports ? this.sortImports : { path: '', args: [] }; + // Support for travis. + this.linting = this.linting + ? this.linting + : { + enabled: false, + ignorePatterns: [], + flake8Args: [], + flake8Enabled: false, + flake8Path: 'flake', + lintOnSave: false, + maxNumberOfProblems: 100, + mypyArgs: [], + mypyEnabled: false, + mypyPath: 'mypy', + banditArgs: [], + banditEnabled: false, + banditPath: 'bandit', + pycodestyleArgs: [], + pycodestyleEnabled: false, + pycodestylePath: 'pycodestyle', + pylamaArgs: [], + pylamaEnabled: false, + pylamaPath: 'pylama', + prospectorArgs: [], + prospectorEnabled: false, + prospectorPath: 'prospector', + pydocstyleArgs: [], + pydocstyleEnabled: false, + pydocstylePath: 'pydocstyle', + pylintArgs: [], + pylintEnabled: false, + pylintPath: 'pylint', + pylintCategorySeverity: { + convention: DiagnosticSeverity.Hint, + error: DiagnosticSeverity.Error, + fatal: DiagnosticSeverity.Error, + refactor: DiagnosticSeverity.Hint, + warning: DiagnosticSeverity.Warning + }, + pycodestyleCategorySeverity: { + E: DiagnosticSeverity.Error, + W: DiagnosticSeverity.Warning + }, + flake8CategorySeverity: { + E: DiagnosticSeverity.Error, + W: DiagnosticSeverity.Warning, + // Per http://flake8.pycqa.org/en/latest/glossary.html#term-error-code + // 'F' does not mean 'fatal as in PyLint but rather 'pyflakes' such as + // unused imports, variables, etc. + F: DiagnosticSeverity.Warning + }, + mypyCategorySeverity: { + error: DiagnosticSeverity.Error, + note: DiagnosticSeverity.Hint + }, + pylintUseMinimalCheckers: false + }; + this.linting.pylintPath = getAbsolutePath(systemVariables.resolveAny(this.linting.pylintPath), workspaceRoot); + this.linting.flake8Path = getAbsolutePath(systemVariables.resolveAny(this.linting.flake8Path), workspaceRoot); + this.linting.pycodestylePath = getAbsolutePath( + systemVariables.resolveAny(this.linting.pycodestylePath), + workspaceRoot + ); + this.linting.pylamaPath = getAbsolutePath(systemVariables.resolveAny(this.linting.pylamaPath), workspaceRoot); + this.linting.prospectorPath = getAbsolutePath( + systemVariables.resolveAny(this.linting.prospectorPath), + workspaceRoot + ); + this.linting.pydocstylePath = getAbsolutePath( + systemVariables.resolveAny(this.linting.pydocstylePath), + workspaceRoot + ); + this.linting.mypyPath = getAbsolutePath(systemVariables.resolveAny(this.linting.mypyPath), workspaceRoot); + this.linting.banditPath = getAbsolutePath(systemVariables.resolveAny(this.linting.banditPath), workspaceRoot); + + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + const formattingSettings = systemVariables.resolveAny(pythonSettings.get('formatting'))!; + if (this.formatting) { + Object.assign(this.formatting, formattingSettings); + } else { + this.formatting = formattingSettings; + } + // Support for travis. + this.formatting = this.formatting + ? this.formatting + : { + autopep8Args: [], + autopep8Path: 'autopep8', + provider: 'autopep8', + blackArgs: [], + blackPath: 'black', + yapfArgs: [], + yapfPath: 'yapf' + }; + this.formatting.autopep8Path = getAbsolutePath( + systemVariables.resolveAny(this.formatting.autopep8Path), + workspaceRoot + ); + this.formatting.yapfPath = getAbsolutePath(systemVariables.resolveAny(this.formatting.yapfPath), workspaceRoot); + this.formatting.blackPath = getAbsolutePath( + systemVariables.resolveAny(this.formatting.blackPath), + workspaceRoot + ); + + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + const autoCompleteSettings = systemVariables.resolveAny( + pythonSettings.get('autoComplete') + )!; + if (this.autoComplete) { + Object.assign(this.autoComplete, autoCompleteSettings); + } else { + this.autoComplete = autoCompleteSettings; + } + // Support for travis. + this.autoComplete = this.autoComplete + ? this.autoComplete + : { + extraPaths: [], + addBrackets: false, + showAdvancedMembers: false, + typeshedPaths: [] + }; + + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + const workspaceSymbolsSettings = systemVariables.resolveAny( + pythonSettings.get('workspaceSymbols') + )!; + if (this.workspaceSymbols) { + Object.assign( + this.workspaceSymbols, + workspaceSymbolsSettings + ); + } else { + this.workspaceSymbols = workspaceSymbolsSettings; + } + // Support for travis. + this.workspaceSymbols = this.workspaceSymbols + ? this.workspaceSymbols + : { + ctagsPath: 'ctags', + enabled: true, + exclusionPatterns: [], + rebuildOnFileSave: true, + rebuildOnStart: true, + tagFilePath: path.join(workspaceRoot, 'tags') + }; + this.workspaceSymbols.tagFilePath = getAbsolutePath( + systemVariables.resolveAny(this.workspaceSymbols.tagFilePath), + workspaceRoot + ); + + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + const testSettings = systemVariables.resolveAny(pythonSettings.get('testing'))!; + if (this.testing) { + Object.assign(this.testing, testSettings); + } else { + this.testing = testSettings; + if (isTestExecution() && !this.testing) { + // tslint:disable-next-line:prefer-type-cast + // tslint:disable-next-line:no-object-literal-type-assertion + this.testing = { + nosetestArgs: [], + pytestArgs: [], + unittestArgs: [], + promptToConfigure: true, + debugPort: 3000, + nosetestsEnabled: false, + pytestEnabled: false, + unittestEnabled: false, + nosetestPath: 'nosetests', + pytestPath: 'pytest', + autoTestDiscoverOnSaveEnabled: true + } as ITestingSettings; + } + } + + // Support for travis. + this.testing = this.testing + ? this.testing + : { + promptToConfigure: true, + debugPort: 3000, + nosetestArgs: [], + nosetestPath: 'nosetest', + nosetestsEnabled: false, + pytestArgs: [], + pytestEnabled: false, + pytestPath: 'pytest', + unittestArgs: [], + unittestEnabled: false, + autoTestDiscoverOnSaveEnabled: true + }; + this.testing.pytestPath = getAbsolutePath(systemVariables.resolveAny(this.testing.pytestPath), workspaceRoot); + this.testing.nosetestPath = getAbsolutePath( + systemVariables.resolveAny(this.testing.nosetestPath), + workspaceRoot + ); + if (this.testing.cwd) { + this.testing.cwd = getAbsolutePath(systemVariables.resolveAny(this.testing.cwd), workspaceRoot); + } + + // Resolve any variables found in the test arguments. + this.testing.nosetestArgs = this.testing.nosetestArgs.map((arg) => systemVariables.resolveAny(arg)); + this.testing.pytestArgs = this.testing.pytestArgs.map((arg) => systemVariables.resolveAny(arg)); + this.testing.unittestArgs = this.testing.unittestArgs.map((arg) => systemVariables.resolveAny(arg)); + + // tslint:disable-next-line:no-backbone-get-set-outside-model no-non-null-assertion + const terminalSettings = systemVariables.resolveAny(pythonSettings.get('terminal'))!; + if (this.terminal) { + Object.assign(this.terminal, terminalSettings); + } else { + this.terminal = terminalSettings; + if (isTestExecution() && !this.terminal) { + // tslint:disable-next-line:prefer-type-cast + // tslint:disable-next-line:no-object-literal-type-assertion + this.terminal = {} as ITerminalSettings; + } + } + // Support for travis. + this.terminal = this.terminal + ? this.terminal + : { + executeInFileDir: true, + launchArgs: [], + activateEnvironment: true, + activateEnvInCurrentTerminal: false + }; + + const experiments = systemVariables.resolveAny(pythonSettings.get('experiments'))!; + if (this.experiments) { + Object.assign(this.experiments, experiments); + } else { + this.experiments = experiments; + } + this.experiments = this.experiments + ? this.experiments + : { + enabled: true, + optInto: [], + optOutFrom: [] + }; + + const dataScienceSettings = systemVariables.resolveAny( + pythonSettings.get('dataScience') + )!; + if (this.datascience) { + Object.assign(this.datascience, dataScienceSettings); + } else { + this.datascience = dataScienceSettings; + } + + this.insidersChannel = pythonSettings.get('insidersChannel')!; + } + + public get pythonPath(): string { + return this._pythonPath; + } + public set pythonPath(value: string) { + if (this._pythonPath === value) { + return; + } + // Add support for specifying just the directory where the python executable will be located. + // E.g. virtual directory name. + try { + this._pythonPath = this.getPythonExecutable(value); + } catch (ex) { + this._pythonPath = value; + } + } + protected getPythonExecutable(pythonPath: string) { + return getPythonExecutable(pythonPath); + } + protected onWorkspaceFoldersChanged() { + //If an activated workspace folder was removed, delete its key + const workspaceKeys = this.workspace.workspaceFolders!.map((workspaceFolder) => workspaceFolder.uri.fsPath); + const activatedWkspcKeys = Array.from(PythonSettings.pythonSettings.keys()); + const activatedWkspcFoldersRemoved = activatedWkspcKeys.filter((item) => workspaceKeys.indexOf(item) < 0); + if (activatedWkspcFoldersRemoved.length > 0) { + for (const folder of activatedWkspcFoldersRemoved) { + PythonSettings.pythonSettings.delete(folder); + } + } + } + protected initialize(): void { + const onDidChange = () => { + const currentConfig = this.workspace.getConfiguration('python', this.workspaceRoot); + this.update(currentConfig); + + // If workspace config changes, then we could have a cascading effect of on change events. + // Let's defer the change notification. + this.debounceChangeNotification(); + }; + this.disposables.push(this.workspace.onDidChangeWorkspaceFolders(this.onWorkspaceFoldersChanged, this)); + this.disposables.push( + this.interpreterAutoSelectionService.onDidChangeAutoSelectedInterpreter(onDidChange.bind(this)) + ); + this.disposables.push( + this.workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => { + if (event.affectsConfiguration('python')) { + onDidChange(); + } + }) + ); + + const initialConfig = this.workspace.getConfiguration('python', this.workspaceRoot); + if (initialConfig) { + this.update(initialConfig); + } + } + @debounceSync(1) + protected debounceChangeNotification() { + this.changed.fire(); + } +} + +function getAbsolutePath(pathToCheck: string, rootDir: string): string { + // tslint:disable-next-line:prefer-type-cast no-unsafe-any + pathToCheck = untildify(pathToCheck) as string; + if (isTestExecution() && !pathToCheck) { + return rootDir; + } + if (pathToCheck.indexOf(path.sep) === -1) { + return pathToCheck; + } + return path.isAbsolute(pathToCheck) ? pathToCheck : path.resolve(rootDir, pathToCheck); +} + +function getPythonExecutable(pythonPath: string): string { + // tslint:disable-next-line:prefer-type-cast no-unsafe-any + pythonPath = untildify(pythonPath) as string; + + // If only 'python'. + if ( + pythonPath === 'python' || + pythonPath.indexOf(path.sep) === -1 || + path.basename(pythonPath) === path.dirname(pythonPath) + ) { + return pythonPath; + } + + if (isValidPythonPath(pythonPath)) { + return pythonPath; + } + // Keep python right on top, for backwards compatibility. + // tslint:disable-next-line:variable-name + const KnownPythonExecutables = ['python', 'python4', 'python3.6', 'python3.5', 'python3', 'python2.7', 'python2']; + + for (let executableName of KnownPythonExecutables) { + // Suffix with 'python' for linux and 'osx', and 'python.exe' for 'windows'. + if (IS_WINDOWS) { + executableName = `${executableName}.exe`; + if (isValidPythonPath(path.join(pythonPath, executableName))) { + return path.join(pythonPath, executableName); + } + if (isValidPythonPath(path.join(pythonPath, 'scripts', executableName))) { + return path.join(pythonPath, 'scripts', executableName); + } + } else { + if (isValidPythonPath(path.join(pythonPath, executableName))) { + return path.join(pythonPath, executableName); + } + if (isValidPythonPath(path.join(pythonPath, 'bin', executableName))) { + return path.join(pythonPath, 'bin', executableName); + } + } + } + + return pythonPath; +} + +function isValidPythonPath(pythonPath: string): boolean { + try { + const output = child_process.execFileSync(pythonPath, ['-c', 'print(1234)'], { encoding: 'utf8' }); + return output.startsWith('1234'); + } catch (ex) { + return false; + } +} diff --git a/src/client/common/configuration/service.ts b/src/client/common/configuration/service.ts index bd6aa34959eb..e05200105f55 100644 --- a/src/client/common/configuration/service.ts +++ b/src/client/common/configuration/service.ts @@ -94,7 +94,7 @@ export class ConfigurationService implements IConfigurationService { } } // Wait for settings to get refreshed. - await new Promise(resolve => setTimeout(resolve, 250)); + await new Promise((resolve) => setTimeout(resolve, 250)); retries += 1; } while (retries < 20); } diff --git a/src/client/common/constants.ts b/src/client/common/constants.ts index 81e72a6aadcb..b42df5466293 100644 --- a/src/client/common/constants.ts +++ b/src/client/common/constants.ts @@ -1,106 +1,106 @@ -import { DocumentFilter } from 'vscode'; - -export const PYTHON_LANGUAGE = 'python'; - -export const JUPYTER_LANGUAGE = 'jupyter'; - -export const PYTHON_WARNINGS = 'PYTHONWARNINGS'; - -export const PYTHON: DocumentFilter[] = [ - { scheme: 'file', language: PYTHON_LANGUAGE }, - { scheme: 'untitled', language: PYTHON_LANGUAGE } -]; -export const PYTHON_ALLFILES = [{ language: PYTHON_LANGUAGE }]; - -export const PVSC_EXTENSION_ID = 'ms-python.python'; -export const CODE_RUNNER_EXTENSION_ID = 'formulahendry.code-runner'; -export const AppinsightsKey = 'AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217'; - -export namespace Commands { - export const Set_Interpreter = 'python.setInterpreter'; - export const Set_ShebangInterpreter = 'python.setShebangInterpreter'; - export const Exec_In_Terminal = 'python.execInTerminal'; - export const Exec_In_Terminal_Icon = 'python.execInTerminal-icon'; - export const Exec_Selection_In_Terminal = 'python.execSelectionInTerminal'; - export const Exec_Selection_In_Django_Shell = 'python.execSelectionInDjangoShell'; - export const Tests_View_UI = 'python.viewTestUI'; - export const Tests_Picker_UI = 'python.selectTestToRun'; - export const Tests_Picker_UI_Debug = 'python.selectTestToDebug'; - export const Tests_Configure = 'python.configureTests'; - export const Tests_Discover = 'python.discoverTests'; - export const Tests_Discovering = 'python.discoveringTests'; - export const Tests_Run_Failed = 'python.runFailedTests'; - export const Sort_Imports = 'python.sortImports'; - export const Tests_Run = 'python.runtests'; - export const Tests_Run_Parametrized = 'python.runParametrizedTests'; - export const Tests_Debug = 'python.debugtests'; - export const Tests_Ask_To_Stop_Test = 'python.askToStopTests'; - export const Tests_Ask_To_Stop_Discovery = 'python.askToStopTestDiscovery'; - export const Tests_Stop = 'python.stopTests'; - export const Test_Reveal_Test_Item = 'python.revealTestItem'; - export const ViewOutput = 'python.viewOutput'; - export const Tests_ViewOutput = 'python.viewTestOutput'; - export const Tests_Select_And_Run_Method = 'python.selectAndRunTestMethod'; - export const Tests_Select_And_Debug_Method = 'python.selectAndDebugTestMethod'; - export const Tests_Select_And_Run_File = 'python.selectAndRunTestFile'; - export const Tests_Run_Current_File = 'python.runCurrentTestFile'; - export const Refactor_Extract_Variable = 'python.refactorExtractVariable'; - export const Refactor_Extract_Method = 'python.refactorExtractMethod'; - export const Build_Workspace_Symbols = 'python.buildWorkspaceSymbols'; - export const Start_REPL = 'python.startREPL'; - export const Create_Terminal = 'python.createTerminal'; - export const Set_Linter = 'python.setLinter'; - export const Enable_Linter = 'python.enableLinting'; - export const Run_Linter = 'python.runLinting'; - export const Enable_SourceMap_Support = 'python.enableSourceMapSupport'; - export const navigateToTestFunction = 'navigateToTestFunction'; - export const navigateToTestSuite = 'navigateToTestSuite'; - export const navigateToTestFile = 'navigateToTestFile'; - export const openTestNodeInEditor = 'python.openTestNodeInEditor'; - export const runTestNode = 'python.runTestNode'; - export const debugTestNode = 'python.debugTestNode'; - export const SwitchOffInsidersChannel = 'python.switchOffInsidersChannel'; - export const SwitchToInsidersDaily = 'python.switchToDailyChannel'; - export const SwitchToInsidersWeekly = 'python.switchToWeeklyChannel'; - export const PickLocalProcess = 'python.pickLocalProcess'; -} -export namespace Octicons { - export const Test_Pass = '$(check)'; - export const Test_Fail = '$(alert)'; - export const Test_Error = '$(x)'; - export const Test_Skip = '$(circle-slash)'; -} - -export const Button_Text_Tests_View_Output = 'View Output'; - -export namespace Text { - export const CodeLensRunUnitTest = 'Run Test'; - export const CodeLensDebugUnitTest = 'Debug Test'; -} -export namespace Delays { - // Max time to wait before aborting the generation of code lenses for unit tests - export const MaxUnitTestCodeLensDelay = 5000; -} - -export const STANDARD_OUTPUT_CHANNEL = 'STANDARD_OUTPUT_CHANNEL'; - -export const isCI = process.env.TRAVIS === 'true' || process.env.TF_BUILD !== undefined; - -export function isTestExecution(): boolean { - return process.env.VSC_PYTHON_CI_TEST === '1' || isUnitTestExecution(); -} - -/** - * Whether we're running unit tests (*.unit.test.ts). - * These tests have a speacial meaning, they run fast. - * @export - * @returns {boolean} - */ -export function isUnitTestExecution(): boolean { - return process.env.VSC_PYTHON_UNIT_TEST === '1'; -} - -// Temporary constant, used to indicate whether we're using custom editor api or not. -export const UseCustomEditorApi = Symbol('USE_CUSTOM_EDITOR'); - -export * from '../constants'; +import { DocumentFilter } from 'vscode'; + +export const PYTHON_LANGUAGE = 'python'; + +export const JUPYTER_LANGUAGE = 'jupyter'; + +export const PYTHON_WARNINGS = 'PYTHONWARNINGS'; + +export const PYTHON: DocumentFilter[] = [ + { scheme: 'file', language: PYTHON_LANGUAGE }, + { scheme: 'untitled', language: PYTHON_LANGUAGE } +]; +export const PYTHON_ALLFILES = [{ language: PYTHON_LANGUAGE }]; + +export const PVSC_EXTENSION_ID = 'ms-python.python'; +export const CODE_RUNNER_EXTENSION_ID = 'formulahendry.code-runner'; +export const AppinsightsKey = 'AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217'; + +export namespace Commands { + export const Set_Interpreter = 'python.setInterpreter'; + export const Set_ShebangInterpreter = 'python.setShebangInterpreter'; + export const Exec_In_Terminal = 'python.execInTerminal'; + export const Exec_In_Terminal_Icon = 'python.execInTerminal-icon'; + export const Exec_Selection_In_Terminal = 'python.execSelectionInTerminal'; + export const Exec_Selection_In_Django_Shell = 'python.execSelectionInDjangoShell'; + export const Tests_View_UI = 'python.viewTestUI'; + export const Tests_Picker_UI = 'python.selectTestToRun'; + export const Tests_Picker_UI_Debug = 'python.selectTestToDebug'; + export const Tests_Configure = 'python.configureTests'; + export const Tests_Discover = 'python.discoverTests'; + export const Tests_Discovering = 'python.discoveringTests'; + export const Tests_Run_Failed = 'python.runFailedTests'; + export const Sort_Imports = 'python.sortImports'; + export const Tests_Run = 'python.runtests'; + export const Tests_Run_Parametrized = 'python.runParametrizedTests'; + export const Tests_Debug = 'python.debugtests'; + export const Tests_Ask_To_Stop_Test = 'python.askToStopTests'; + export const Tests_Ask_To_Stop_Discovery = 'python.askToStopTestDiscovery'; + export const Tests_Stop = 'python.stopTests'; + export const Test_Reveal_Test_Item = 'python.revealTestItem'; + export const ViewOutput = 'python.viewOutput'; + export const Tests_ViewOutput = 'python.viewTestOutput'; + export const Tests_Select_And_Run_Method = 'python.selectAndRunTestMethod'; + export const Tests_Select_And_Debug_Method = 'python.selectAndDebugTestMethod'; + export const Tests_Select_And_Run_File = 'python.selectAndRunTestFile'; + export const Tests_Run_Current_File = 'python.runCurrentTestFile'; + export const Refactor_Extract_Variable = 'python.refactorExtractVariable'; + export const Refactor_Extract_Method = 'python.refactorExtractMethod'; + export const Build_Workspace_Symbols = 'python.buildWorkspaceSymbols'; + export const Start_REPL = 'python.startREPL'; + export const Create_Terminal = 'python.createTerminal'; + export const Set_Linter = 'python.setLinter'; + export const Enable_Linter = 'python.enableLinting'; + export const Run_Linter = 'python.runLinting'; + export const Enable_SourceMap_Support = 'python.enableSourceMapSupport'; + export const navigateToTestFunction = 'navigateToTestFunction'; + export const navigateToTestSuite = 'navigateToTestSuite'; + export const navigateToTestFile = 'navigateToTestFile'; + export const openTestNodeInEditor = 'python.openTestNodeInEditor'; + export const runTestNode = 'python.runTestNode'; + export const debugTestNode = 'python.debugTestNode'; + export const SwitchOffInsidersChannel = 'python.switchOffInsidersChannel'; + export const SwitchToInsidersDaily = 'python.switchToDailyChannel'; + export const SwitchToInsidersWeekly = 'python.switchToWeeklyChannel'; + export const PickLocalProcess = 'python.pickLocalProcess'; +} +export namespace Octicons { + export const Test_Pass = '$(check)'; + export const Test_Fail = '$(alert)'; + export const Test_Error = '$(x)'; + export const Test_Skip = '$(circle-slash)'; +} + +export const Button_Text_Tests_View_Output = 'View Output'; + +export namespace Text { + export const CodeLensRunUnitTest = 'Run Test'; + export const CodeLensDebugUnitTest = 'Debug Test'; +} +export namespace Delays { + // Max time to wait before aborting the generation of code lenses for unit tests + export const MaxUnitTestCodeLensDelay = 5000; +} + +export const STANDARD_OUTPUT_CHANNEL = 'STANDARD_OUTPUT_CHANNEL'; + +export const isCI = process.env.TRAVIS === 'true' || process.env.TF_BUILD !== undefined; + +export function isTestExecution(): boolean { + return process.env.VSC_PYTHON_CI_TEST === '1' || isUnitTestExecution(); +} + +/** + * Whether we're running unit tests (*.unit.test.ts). + * These tests have a speacial meaning, they run fast. + * @export + * @returns {boolean} + */ +export function isUnitTestExecution(): boolean { + return process.env.VSC_PYTHON_UNIT_TEST === '1'; +} + +// Temporary constant, used to indicate whether we're using custom editor api or not. +export const UseCustomEditorApi = Symbol('USE_CUSTOM_EDITOR'); + +export * from '../constants'; diff --git a/src/client/common/crypto.ts b/src/client/common/crypto.ts index b9842d345deb..3067ebb4a801 100644 --- a/src/client/common/crypto.ts +++ b/src/client/common/crypto.ts @@ -25,13 +25,9 @@ export class CryptoUtils implements ICryptoUtils { const fnv = require('@enonic/fnv-plus'); hash = fnv.fast1a32hex(data) as string; } else if (algorithm === 'SHA256') { - hash = createHash('sha256') - .update(data) - .digest('hex'); + hash = createHash('sha256').update(data).digest('hex'); } else { - hash = createHash('sha512') - .update(data) - .digest('hex'); + hash = createHash('sha512').update(data).digest('hex'); } if (hashFormat === 'number') { const result = parseInt(hash, 16); diff --git a/src/client/common/editor.ts b/src/client/common/editor.ts index 36b7d667827a..8b5356c379d0 100644 --- a/src/client/common/editor.ts +++ b/src/client/common/editor.ts @@ -71,11 +71,11 @@ export function getTextEditsFromPatch(before: string, patch: string): TextEdit[] const textEdits: TextEdit[] = []; // Add line feeds and build the text edits - patches.forEach(p => { - p.diffs.forEach(diff => { + patches.forEach((p) => { + p.diffs.forEach((diff) => { diff[1] += EOL; }); - getTextEditsInternal(before, p.diffs, p.start1).forEach(edit => textEdits.push(edit.apply())); + getTextEditsInternal(before, p.diffs, p.start1).forEach((edit) => textEdits.push(edit.apply())); }); return textEdits; @@ -86,7 +86,7 @@ export function getWorkspaceEditsFromPatch( fs: IFileSystem ): WorkspaceEdit { const workspaceEdit = new WorkspaceEdit(); - filePatches.forEach(patch => { + filePatches.forEach((patch) => { const indexOfAtAt = patch.indexOf('@@'); if (indexOfAtAt === -1) { return; @@ -94,8 +94,8 @@ export function getWorkspaceEditsFromPatch( const fileNameLines = patch .substring(0, indexOfAtAt) .split(/\r?\n/g) - .map(line => line.trim()) - .filter(line => line.length > 0 && line.toLowerCase().endsWith('.py') && line.indexOf(' a') > 0); + .map((line) => line.trim()) + .filter((line) => line.length > 0 && line.toLowerCase().endsWith('.py') && line.indexOf(' a') > 0); if (patch.startsWith('---')) { // Strip the first two lines @@ -131,12 +131,12 @@ export function getWorkspaceEditsFromPatch( const fileUri = Uri.file(fileName); // Add line feeds and build the text edits - patches.forEach(p => { - p.diffs.forEach(diff => { + patches.forEach((p) => { + p.diffs.forEach((diff) => { diff[1] += EOL; }); - getTextEditsInternal(fileSource, p.diffs, p.start1).forEach(edit => { + getTextEditsInternal(fileSource, p.diffs, p.start1).forEach((edit) => { switch (edit.action) { case EditAction.Delete: workspaceEdit.delete(fileUri, new Range(edit.start, edit.end)); @@ -161,14 +161,14 @@ export function getTextEdits(before: string, after: string): TextEdit[] { const dmp = require('diff-match-patch') as typeof import('diff-match-patch'); const d = new dmp.diff_match_patch(); const diffs = d.diff_main(before, after); - return getTextEditsInternal(before, diffs).map(edit => edit.apply()); + return getTextEditsInternal(before, diffs).map((edit) => edit.apply()); } function getTextEditsInternal(before: string, diffs: [number, string][], startLine: number = 0): Edit[] { let line = startLine; let character = 0; const beforeLines = before.split(/\r?\n/g); if (line > 0) { - beforeLines.filter((_l, i) => i < line).forEach(l => (character += l.length + NEW_LINE_LENGTH)); + beforeLines.filter((_l, i) => i < line).forEach((l) => (character += l.length + NEW_LINE_LENGTH)); } const edits: Edit[] = []; let edit: Edit | null = null; @@ -369,11 +369,11 @@ export class EditorUtils implements IEditorUtils { } // Add line feeds and build the text edits - patches.forEach(p => { - p.diffs.forEach(diff => { + patches.forEach((p) => { + p.diffs.forEach((diff) => { diff[1] += EOL; }); - getTextEditsInternal(originalContents, p.diffs, p.start1).forEach(edit => { + getTextEditsInternal(originalContents, p.diffs, p.start1).forEach((edit) => { switch (edit.action) { case EditAction.Delete: workspaceEdit.delete(uri, new Range(edit.start, edit.end)); diff --git a/src/client/common/experiments.ts b/src/client/common/experiments.ts index d367f43da764..462fb6818a0f 100644 --- a/src/client/common/experiments.ts +++ b/src/client/common/experiments.ts @@ -139,7 +139,7 @@ export class ExperimentsManager implements IExperimentsManager { return false; } this.sendTelemetryIfInExperiment(experimentName); - return this.userExperiments.find(exp => exp.name === experimentName) ? true : false; + return this.userExperiments.find((exp) => exp.name === experimentName) ? true : false; } /** @@ -180,7 +180,7 @@ export class ExperimentsManager implements IExperimentsManager { @traceDecorators.error('Failed to send telemetry when user is in experiment') public sendTelemetryIfInExperiment(experimentName: string): void { - if (this.userExperiments.find(exp => exp.name === experimentName)) { + if (this.userExperiments.find((exp) => exp.name === experimentName)) { sendTelemetryEvent(EventName.PYTHON_EXPERIMENTS, undefined, { expName: experimentName }); } } @@ -223,7 +223,7 @@ export class ExperimentsManager implements IExperimentsManager { throw new Error('Machine ID should be a string'); } let hash: number; - if (oldExperimentSalts.find(oldSalt => oldSalt === salt)) { + if (oldExperimentSalts.find((oldSalt) => oldSalt === salt)) { hash = this.crypto.createHash(`${this.appEnvironment.machineId}+${salt}`, 'number', 'SHA512'); } else { hash = this.crypto.createHash(`${this.appEnvironment.machineId}+${salt}`, 'number', 'FNV'); @@ -333,7 +333,7 @@ export class ExperimentsManager implements IExperimentsManager { this._experimentsOptedOutFrom[i] = ''; } } - this._experimentsOptedInto = this._experimentsOptedInto.filter(exp => exp !== ''); - this._experimentsOptedOutFrom = this._experimentsOptedOutFrom.filter(exp => exp !== ''); + this._experimentsOptedInto = this._experimentsOptedInto.filter((exp) => exp !== ''); + this._experimentsOptedOutFrom = this._experimentsOptedOutFrom.filter((exp) => exp !== ''); } } diff --git a/src/client/common/extensions.ts b/src/client/common/extensions.ts index 717e09ff6252..a41675749ab9 100644 --- a/src/client/common/extensions.ts +++ b/src/client/common/extensions.ts @@ -44,16 +44,16 @@ declare interface String { * By default lines are trimmed and empty lines are removed. * @param {SplitLinesOptions=} splitOptions - Options used for splitting the string. */ -String.prototype.splitLines = function( +String.prototype.splitLines = function ( this: string, splitOptions: { trim: boolean; removeEmptyEntries: boolean } = { removeEmptyEntries: true, trim: true } ): string[] { let lines = this.split(/\r?\n/g); if (splitOptions && splitOptions.trim) { - lines = lines.map(line => line.trim()); + lines = lines.map((line) => line.trim()); } if (splitOptions && splitOptions.removeEmptyEntries) { - lines = lines.filter(line => line.length > 0); + lines = lines.filter((line) => line.length > 0); } return lines; }; @@ -63,7 +63,7 @@ String.prototype.splitLines = function( * E.g. if an argument contains a space, then it will be enclosed within double quotes. * @param {String} value. */ -String.prototype.toCommandArgument = function(this: string): string { +String.prototype.toCommandArgument = function (this: string): string { if (!this) { return this; } @@ -74,7 +74,7 @@ String.prototype.toCommandArgument = function(this: string): string { * Appropriately formats a a file path so it can be used as an argument for a command in a shell. * E.g. if an argument contains a space, then it will be enclosed within double quotes. */ -String.prototype.fileToCommandArgument = function(this: string): string { +String.prototype.fileToCommandArgument = function (this: string): string { if (!this) { return this; } @@ -85,7 +85,7 @@ String.prototype.fileToCommandArgument = function(this: string): string { * String.trimQuotes implementation * Removes leading and trailing quotes from a string */ -String.prototype.trimQuotes = function(this: string): string { +String.prototype.trimQuotes = function (this: string): string { if (!this) { return this; } @@ -103,13 +103,13 @@ declare interface Promise { /** * Explicitly tells that promise should be run asynchonously. */ -Promise.prototype.ignoreErrors = function(this: Promise) { +Promise.prototype.ignoreErrors = function (this: Promise) { // tslint:disable-next-line:no-empty this.catch(() => {}); }; if (!String.prototype.format) { - String.prototype.format = function(this: string) { + String.prototype.format = function (this: string) { const args = arguments; return this.replace(/{(\d+)}/g, (match, number) => (args[number] === undefined ? match : args[number])); }; diff --git a/src/client/common/featureDeprecationManager.ts b/src/client/common/featureDeprecationManager.ts index 58d02070c2bc..51f923698eb6 100644 --- a/src/client/common/featureDeprecationManager.ts +++ b/src/client/common/featureDeprecationManager.ts @@ -47,7 +47,7 @@ export class FeatureDeprecationManager implements IFeatureDeprecationManager { ) {} public dispose() { - this.disposables.forEach(disposable => disposable.dispose()); + this.disposables.forEach((disposable) => disposable.dispose()); } public initialize() { @@ -56,7 +56,7 @@ export class FeatureDeprecationManager implements IFeatureDeprecationManager { public registerDeprecation(deprecatedInfo: DeprecatedFeatureInfo): void { if (Array.isArray(deprecatedInfo.commands)) { - deprecatedInfo.commands.forEach(cmd => { + deprecatedInfo.commands.forEach((cmd) => { this.disposables.push( this.cmdMgr.registerCommand(cmd, () => this.notifyDeprecation(deprecatedInfo), this) ); @@ -100,7 +100,7 @@ export class FeatureDeprecationManager implements IFeatureDeprecationManager { public checkAndNotifyDeprecatedSetting(deprecatedInfo: DeprecatedFeatureInfo) { let notify = false; if (Array.isArray(this.workspace.workspaceFolders) && this.workspace.workspaceFolders.length > 0) { - this.workspace.workspaceFolders.forEach(workspaceFolder => { + this.workspace.workspaceFolders.forEach((workspaceFolder) => { if (notify) { return; } @@ -117,7 +117,9 @@ export class FeatureDeprecationManager implements IFeatureDeprecationManager { } if (notify) { - this.notifyDeprecation(deprecatedInfo).catch(ex => traceVerbose('Python Extension: notifyDeprecation', ex)); + this.notifyDeprecation(deprecatedInfo).catch((ex) => + traceVerbose('Python Extension: notifyDeprecation', ex) + ); } } diff --git a/src/client/common/helpers.ts b/src/client/common/helpers.ts index d28c33d19f55..2fd69900bd32 100644 --- a/src/client/common/helpers.ts +++ b/src/client/common/helpers.ts @@ -23,10 +23,10 @@ export function isNotInstalledError(error: Error): boolean { export function skipIfTest(isAsyncFunction: boolean) { // tslint:disable-next-line:no-function-expression no-any - return function(_: Object, __: string, descriptor: TypedPropertyDescriptor) { + return function (_: Object, __: string, descriptor: TypedPropertyDescriptor) { const originalMethod = descriptor.value; // tslint:disable-next-line:no-function-expression no-any - descriptor.value = function(...args: any[]) { + descriptor.value = function (...args: any[]) { if (isTestExecution()) { return isAsyncFunction ? Promise.resolve() : undefined; } diff --git a/src/client/common/insidersBuild/downloadChannelService.ts b/src/client/common/insidersBuild/downloadChannelService.ts index 82c62d6e936b..c0096c080045 100644 --- a/src/client/common/insidersBuild/downloadChannelService.ts +++ b/src/client/common/insidersBuild/downloadChannelService.ts @@ -1,57 +1,57 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { inject, injectable } from 'inversify'; -import { ConfigurationChangeEvent, ConfigurationTarget, Event, EventEmitter } from 'vscode'; -import { IWorkspaceService } from '../application/types'; -import { traceDecorators } from '../logger'; -import { IConfigurationService, IDisposable, IDisposableRegistry, IPythonSettings } from '../types'; -import { ExtensionChannels, IExtensionChannelService } from './types'; - -export const insidersChannelSetting: keyof IPythonSettings = 'insidersChannel'; - -@injectable() -export class ExtensionChannelService implements IExtensionChannelService { - public _onDidChannelChange: EventEmitter = new EventEmitter(); - constructor( - @inject(IConfigurationService) private readonly configService: IConfigurationService, - @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService, - @inject(IDisposableRegistry) disposables: IDisposable[] - ) { - disposables.push(this.workspaceService.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this))); - } - public getChannel(): ExtensionChannels { - const settings = this.configService.getSettings(); - return settings.insidersChannel; - } - - public get isChannelUsingDefaultConfiguration(): boolean { - const settings = this.workspaceService - .getConfiguration('python') - .inspect(insidersChannelSetting); - if (!settings) { - throw new Error( - `WorkspaceConfiguration.inspect returns 'undefined' for setting 'python.${insidersChannelSetting}'` - ); - } - return !settings.globalValue; - } - - @traceDecorators.error('Updating channel failed') - public async updateChannel(value: ExtensionChannels): Promise { - await this.configService.updateSetting(insidersChannelSetting, value, undefined, ConfigurationTarget.Global); - } - - public get onDidChannelChange(): Event { - return this._onDidChannelChange.event; - } - - public async onDidChangeConfiguration(event: ConfigurationChangeEvent) { - if (event.affectsConfiguration(`python.${insidersChannelSetting}`)) { - const settings = this.configService.getSettings(); - this._onDidChannelChange.fire(settings.insidersChannel); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { inject, injectable } from 'inversify'; +import { ConfigurationChangeEvent, ConfigurationTarget, Event, EventEmitter } from 'vscode'; +import { IWorkspaceService } from '../application/types'; +import { traceDecorators } from '../logger'; +import { IConfigurationService, IDisposable, IDisposableRegistry, IPythonSettings } from '../types'; +import { ExtensionChannels, IExtensionChannelService } from './types'; + +export const insidersChannelSetting: keyof IPythonSettings = 'insidersChannel'; + +@injectable() +export class ExtensionChannelService implements IExtensionChannelService { + public _onDidChannelChange: EventEmitter = new EventEmitter(); + constructor( + @inject(IConfigurationService) private readonly configService: IConfigurationService, + @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService, + @inject(IDisposableRegistry) disposables: IDisposable[] + ) { + disposables.push(this.workspaceService.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this))); + } + public getChannel(): ExtensionChannels { + const settings = this.configService.getSettings(); + return settings.insidersChannel; + } + + public get isChannelUsingDefaultConfiguration(): boolean { + const settings = this.workspaceService + .getConfiguration('python') + .inspect(insidersChannelSetting); + if (!settings) { + throw new Error( + `WorkspaceConfiguration.inspect returns 'undefined' for setting 'python.${insidersChannelSetting}'` + ); + } + return !settings.globalValue; + } + + @traceDecorators.error('Updating channel failed') + public async updateChannel(value: ExtensionChannels): Promise { + await this.configService.updateSetting(insidersChannelSetting, value, undefined, ConfigurationTarget.Global); + } + + public get onDidChannelChange(): Event { + return this._onDidChannelChange.event; + } + + public async onDidChangeConfiguration(event: ConfigurationChangeEvent) { + if (event.affectsConfiguration(`python.${insidersChannelSetting}`)) { + const settings = this.configService.getSettings(); + this._onDidChannelChange.fire(settings.insidersChannel); + } + } +} diff --git a/src/client/common/insidersBuild/insidersExtensionService.ts b/src/client/common/insidersBuild/insidersExtensionService.ts index 00f34c151793..f7896cd3e4f7 100644 --- a/src/client/common/insidersBuild/insidersExtensionService.ts +++ b/src/client/common/insidersBuild/insidersExtensionService.ts @@ -1,135 +1,135 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import '../extensions'; - -import { inject, injectable, named } from 'inversify'; -import { IExtensionSingleActivationService } from '../../../client/activation/types'; -import { IServiceContainer } from '../../ioc/types'; -import { IApplicationEnvironment, ICommandManager } from '../application/types'; -import { Commands } from '../constants'; -import { IExtensionBuildInstaller, INSIDERS_INSTALLER } from '../installer/types'; -import { traceDecorators } from '../logger'; -import { IDisposable, IDisposableRegistry } from '../types'; -import { ExtensionChannels, IExtensionChannelRule, IExtensionChannelService, IInsiderExtensionPrompt } from './types'; - -@injectable() -export class InsidersExtensionService implements IExtensionSingleActivationService { - constructor( - @inject(IExtensionChannelService) private readonly extensionChannelService: IExtensionChannelService, - @inject(IInsiderExtensionPrompt) private readonly insidersPrompt: IInsiderExtensionPrompt, - @inject(IApplicationEnvironment) private readonly appEnvironment: IApplicationEnvironment, - @inject(ICommandManager) private readonly cmdManager: ICommandManager, - @inject(IServiceContainer) private readonly serviceContainer: IServiceContainer, - @inject(IExtensionBuildInstaller) - @named(INSIDERS_INSTALLER) - private readonly insidersInstaller: IExtensionBuildInstaller, - @inject(IDisposableRegistry) public readonly disposables: IDisposable[] - ) {} - - public async activate() { - this.registerCommandsAndHandlers(); - await this.initChannel(); - } - - public registerCommandsAndHandlers(): void { - this.disposables.push( - this.extensionChannelService.onDidChannelChange(channel => { - return this.handleChannel(channel, true); - }) - ); - this.disposables.push( - this.cmdManager.registerCommand(Commands.SwitchOffInsidersChannel, () => - this.extensionChannelService.updateChannel('off') - ) - ); - this.disposables.push( - this.cmdManager.registerCommand(Commands.SwitchToInsidersDaily, () => - this.extensionChannelService.updateChannel('daily') - ) - ); - this.disposables.push( - this.cmdManager.registerCommand(Commands.SwitchToInsidersWeekly, () => - this.extensionChannelService.updateChannel('weekly') - ) - ); - } - - public async initChannel() { - const channel = this.extensionChannelService.getChannel(); - const isDefault = this.extensionChannelService.isChannelUsingDefaultConfiguration; - - const alreadyHandled = await this.handleEdgeCases(channel, isDefault); - if (!alreadyHandled) { - this.handleChannel(channel).ignoreErrors(); - } - } - - // Everything past here is the "channel handler" implementation. - - @traceDecorators.error('Handling channel failed') - public async handleChannel(installChannel: ExtensionChannels, didChannelChange: boolean = false): Promise { - const channelRule = this.serviceContainer.get(IExtensionChannelRule, installChannel); - const shouldInstall = await channelRule.shouldLookForInsidersBuild(didChannelChange); - if (!shouldInstall) { - return; - } - await this.insidersInstaller.install(); - await this.insidersPrompt.promptToReload(); - } - - /** - * Choose what to do in miscellaneous situations - * @returns `true` if install channel is handled in these miscellaneous cases, `false` if install channel needs further handling - */ - public async handleEdgeCases(installChannel: ExtensionChannels, isDefault: boolean): Promise { - // When running UI Tests we might want to disable these prompts. - if (process.env.UITEST_DISABLE_INSIDERS) { - return true; - } else if (await this.promptToInstallInsidersIfApplicable(isDefault)) { - return true; - } else if (await this.setInsidersChannelToOffIfApplicable(installChannel)) { - return true; - } else { - return false; - } - } - - /** - * Only when using VSC insiders and if they have not been notified before (usually the first session), notify to enroll into the insiders program - * @returns `true` if prompt is shown, `false` otherwise - */ - private async promptToInstallInsidersIfApplicable(isDefault: boolean): Promise { - if (this.appEnvironment.channel !== 'insiders') { - return false; - } - if (this.insidersPrompt.hasUserBeenNotified.value) { - return false; - } - if (!isDefault) { - return false; - } - - await this.insidersPrompt.promptToInstallInsiders(); - return true; - } - - /** - * When install channel is not in sync with what is installed, resolve discrepency by setting channel to "off" - * @returns `true` if channel is set to off, `false` otherwise - */ - private async setInsidersChannelToOffIfApplicable(installChannel: ExtensionChannels): Promise { - if (installChannel === 'off') { - return false; - } - if (this.appEnvironment.extensionChannel !== 'stable') { - return false; - } - - // Install channel is set to "weekly" or "daily" but stable version of extension is installed. Switch channel to "off" to use the installed version - await this.extensionChannelService.updateChannel('off'); - return true; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import '../extensions'; + +import { inject, injectable, named } from 'inversify'; +import { IExtensionSingleActivationService } from '../../../client/activation/types'; +import { IServiceContainer } from '../../ioc/types'; +import { IApplicationEnvironment, ICommandManager } from '../application/types'; +import { Commands } from '../constants'; +import { IExtensionBuildInstaller, INSIDERS_INSTALLER } from '../installer/types'; +import { traceDecorators } from '../logger'; +import { IDisposable, IDisposableRegistry } from '../types'; +import { ExtensionChannels, IExtensionChannelRule, IExtensionChannelService, IInsiderExtensionPrompt } from './types'; + +@injectable() +export class InsidersExtensionService implements IExtensionSingleActivationService { + constructor( + @inject(IExtensionChannelService) private readonly extensionChannelService: IExtensionChannelService, + @inject(IInsiderExtensionPrompt) private readonly insidersPrompt: IInsiderExtensionPrompt, + @inject(IApplicationEnvironment) private readonly appEnvironment: IApplicationEnvironment, + @inject(ICommandManager) private readonly cmdManager: ICommandManager, + @inject(IServiceContainer) private readonly serviceContainer: IServiceContainer, + @inject(IExtensionBuildInstaller) + @named(INSIDERS_INSTALLER) + private readonly insidersInstaller: IExtensionBuildInstaller, + @inject(IDisposableRegistry) public readonly disposables: IDisposable[] + ) {} + + public async activate() { + this.registerCommandsAndHandlers(); + await this.initChannel(); + } + + public registerCommandsAndHandlers(): void { + this.disposables.push( + this.extensionChannelService.onDidChannelChange((channel) => { + return this.handleChannel(channel, true); + }) + ); + this.disposables.push( + this.cmdManager.registerCommand(Commands.SwitchOffInsidersChannel, () => + this.extensionChannelService.updateChannel('off') + ) + ); + this.disposables.push( + this.cmdManager.registerCommand(Commands.SwitchToInsidersDaily, () => + this.extensionChannelService.updateChannel('daily') + ) + ); + this.disposables.push( + this.cmdManager.registerCommand(Commands.SwitchToInsidersWeekly, () => + this.extensionChannelService.updateChannel('weekly') + ) + ); + } + + public async initChannel() { + const channel = this.extensionChannelService.getChannel(); + const isDefault = this.extensionChannelService.isChannelUsingDefaultConfiguration; + + const alreadyHandled = await this.handleEdgeCases(channel, isDefault); + if (!alreadyHandled) { + this.handleChannel(channel).ignoreErrors(); + } + } + + // Everything past here is the "channel handler" implementation. + + @traceDecorators.error('Handling channel failed') + public async handleChannel(installChannel: ExtensionChannels, didChannelChange: boolean = false): Promise { + const channelRule = this.serviceContainer.get(IExtensionChannelRule, installChannel); + const shouldInstall = await channelRule.shouldLookForInsidersBuild(didChannelChange); + if (!shouldInstall) { + return; + } + await this.insidersInstaller.install(); + await this.insidersPrompt.promptToReload(); + } + + /** + * Choose what to do in miscellaneous situations + * @returns `true` if install channel is handled in these miscellaneous cases, `false` if install channel needs further handling + */ + public async handleEdgeCases(installChannel: ExtensionChannels, isDefault: boolean): Promise { + // When running UI Tests we might want to disable these prompts. + if (process.env.UITEST_DISABLE_INSIDERS) { + return true; + } else if (await this.promptToInstallInsidersIfApplicable(isDefault)) { + return true; + } else if (await this.setInsidersChannelToOffIfApplicable(installChannel)) { + return true; + } else { + return false; + } + } + + /** + * Only when using VSC insiders and if they have not been notified before (usually the first session), notify to enroll into the insiders program + * @returns `true` if prompt is shown, `false` otherwise + */ + private async promptToInstallInsidersIfApplicable(isDefault: boolean): Promise { + if (this.appEnvironment.channel !== 'insiders') { + return false; + } + if (this.insidersPrompt.hasUserBeenNotified.value) { + return false; + } + if (!isDefault) { + return false; + } + + await this.insidersPrompt.promptToInstallInsiders(); + return true; + } + + /** + * When install channel is not in sync with what is installed, resolve discrepency by setting channel to "off" + * @returns `true` if channel is set to off, `false` otherwise + */ + private async setInsidersChannelToOffIfApplicable(installChannel: ExtensionChannels): Promise { + if (installChannel === 'off') { + return false; + } + if (this.appEnvironment.extensionChannel !== 'stable') { + return false; + } + + // Install channel is set to "weekly" or "daily" but stable version of extension is installed. Switch channel to "off" to use the installed version + await this.extensionChannelService.updateChannel('off'); + return true; + } +} diff --git a/src/client/common/installer/channelManager.ts b/src/client/common/installer/channelManager.ts index e90722396344..b748f44555c6 100644 --- a/src/client/common/installer/channelManager.ts +++ b/src/client/common/installer/channelManager.ts @@ -34,7 +34,7 @@ export class InstallationChannelManager implements IInstallationChannelManager { } const placeHolder = `Select an option to install ${productName}`; - const options = channels.map(installer => { + const options = channels.map((installer) => { return { label: `Install using ${installer.displayName}`, description: '', diff --git a/src/client/common/installer/extensionBuildInstaller.ts b/src/client/common/installer/extensionBuildInstaller.ts index 80f8416d723b..d77534551f74 100644 --- a/src/client/common/installer/extensionBuildInstaller.ts +++ b/src/client/common/installer/extensionBuildInstaller.ts @@ -57,7 +57,7 @@ export class InsidersBuildInstaller implements IExtensionBuildInstaller { outputChannel: this.output, progressMessagePrefix: ExtensionChannels.downloadingInsidersMessage() }; - return this.fileDownloader.downloadFile(developmentBuildUri, downloadOptions).then(file => { + return this.fileDownloader.downloadFile(developmentBuildUri, downloadOptions).then((file) => { this.output.appendLine(ExtensionChannels.downloadCompletedOutputMessage()); return file; }); diff --git a/src/client/common/installer/moduleInstaller.ts b/src/client/common/installer/moduleInstaller.ts index 92e3e5b4e961..1248a2910b89 100644 --- a/src/client/common/installer/moduleInstaller.ts +++ b/src/client/common/installer/moduleInstaller.ts @@ -49,7 +49,7 @@ export abstract class ModuleInstaller implements IModuleInstaller { await terminalService.sendCommand(pythonPath, args, token); } else if (settings.globalModuleInstallation) { const fs = this.serviceContainer.get(IFileSystem); - if (await fs.isDirReadonly(path.dirname(pythonPath)).catch(_err => true)) { + if (await fs.isDirReadonly(path.dirname(pythonPath)).catch((_err) => true)) { this.elevatedInstall(pythonPath, args); } else { await terminalService.sendCommand(pythonPath, args, token); @@ -112,7 +112,7 @@ export abstract class ModuleInstaller implements IModuleInstaller { } protected abstract getExecutionInfo(moduleName: string, resource?: InterpreterUri): Promise; private async processInstallArgs(args: string[], resource?: InterpreterUri): Promise { - const indexOfPylint = args.findIndex(arg => arg.toUpperCase() === 'PYLINT'); + const indexOfPylint = args.findIndex((arg) => arg.toUpperCase() === 'PYLINT'); if (indexOfPylint === -1) { return args; } diff --git a/src/client/common/installer/pipInstaller.ts b/src/client/common/installer/pipInstaller.ts index 221baea4a44c..9b0afd3abbc2 100644 --- a/src/client/common/installer/pipInstaller.ts +++ b/src/client/common/installer/pipInstaller.ts @@ -47,7 +47,7 @@ export class PipInstaller extends ModuleInstaller { const pythonPath = isResource(info) ? undefined : info.path; return pythonExecutionFactory .create({ resource, pythonPath }) - .then(proc => proc.isModuleInstalled('pip')) + .then((proc) => proc.isModuleInstalled('pip')) .catch(() => false); } } diff --git a/src/client/common/installer/productInstaller.ts b/src/client/common/installer/productInstaller.ts index d0b673232b17..297027834a6a 100644 --- a/src/client/common/installer/productInstaller.ts +++ b/src/client/common/installer/productInstaller.ts @@ -91,9 +91,9 @@ export abstract class BaseInstaller { const moduleName = translateProductToModule(product, ModuleNamePurpose.install); await installer .installModule(moduleName, resource, cancel) - .catch(ex => traceError(`Error in installing the module '${moduleName}', ${ex}`)); + .catch((ex) => traceError(`Error in installing the module '${moduleName}', ${ex}`)); - return this.isInstalled(product, resource).then(isInstalled => + return this.isInstalled(product, resource).then((isInstalled) => isInstalled ? InstallerResponse.Installed : InstallerResponse.Ignore ); } @@ -164,7 +164,7 @@ export class CTagsInstaller extends BaseInstaller { .getTerminalService(resource); terminalService .sendCommand(CTagsInsllationScript, []) - .catch(ex => traceError(`Failed to install ctags. Script sent '${CTagsInsllationScript}', ${ex}`)); + .catch((ex) => traceError(`Failed to install ctags. Script sent '${CTagsInsllationScript}', ${ex}`)); } return InstallerResponse.Ignore; } @@ -191,10 +191,10 @@ export class FormatterInstaller extends BaseInstaller { // Hard-coded on purpose because the UI won't necessarily work having // another formatter. const formatters = [Product.autopep8, Product.black, Product.yapf]; - const formatterNames = formatters.map(formatter => ProductNames.get(formatter)!); + const formatterNames = formatters.map((formatter) => ProductNames.get(formatter)!); const productName = ProductNames.get(product)!; formatterNames.splice(formatterNames.indexOf(productName), 1); - const useOptions = formatterNames.map(name => `Use ${name}`); + const useOptions = formatterNames.map((name) => `Use ${name}`); const yesChoice = 'Yes'; const options = [...useOptions]; diff --git a/src/client/common/installer/types.ts b/src/client/common/installer/types.ts index a515652925e0..cacc711a77c2 100644 --- a/src/client/common/installer/types.ts +++ b/src/client/common/installer/types.ts @@ -1,56 +1,56 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { CancellationToken, Uri } from 'vscode'; -import { PythonInterpreter } from '../../interpreter/contracts'; -import { Product, ProductType, Resource } from '../types'; - -export type InterpreterUri = Resource | PythonInterpreter; - -export const IModuleInstaller = Symbol('IModuleInstaller'); -export interface IModuleInstaller { - readonly name: string; - readonly displayName: string; - readonly priority: number; - /** - * Installs a module - * If a cancellation token is provided, then a cancellable progress message is dispalyed. - * At this point, this method would resolve only after the module has been successfully installed. - * If cancellation token is not provided, its not guaranteed that module installation has completed. - * @param {string} name - * @param {InterpreterUri} [resource] - * @param {CancellationToken} [cancel] - * @returns {Promise} - * @memberof IModuleInstaller - */ - installModule(name: string, resource?: InterpreterUri, cancel?: CancellationToken): Promise; - isSupported(resource?: InterpreterUri): Promise; -} - -export const IPythonInstallation = Symbol('IPythonInstallation'); -export interface IPythonInstallation { - checkInstallation(): Promise; -} - -export const IInstallationChannelManager = Symbol('IInstallationChannelManager'); -export interface IInstallationChannelManager { - getInstallationChannel(product: Product, resource?: InterpreterUri): Promise; - getInstallationChannels(resource?: InterpreterUri): Promise; - showNoInstallersMessage(): void; -} -export const IProductService = Symbol('IProductService'); -export interface IProductService { - getProductType(product: Product): ProductType; -} -export const IProductPathService = Symbol('IProductPathService'); -export interface IProductPathService { - getExecutableNameFromSettings(product: Product, resource?: Uri): string; - isExecutableAModule(product: Product, resource?: Uri): Boolean; -} - -export const INSIDERS_INSTALLER = 'INSIDERS_INSTALLER'; -export const STABLE_INSTALLER = 'STABLE_INSTALLER'; -export const IExtensionBuildInstaller = Symbol('IExtensionBuildInstaller'); -export interface IExtensionBuildInstaller { - install(): Promise; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { CancellationToken, Uri } from 'vscode'; +import { PythonInterpreter } from '../../interpreter/contracts'; +import { Product, ProductType, Resource } from '../types'; + +export type InterpreterUri = Resource | PythonInterpreter; + +export const IModuleInstaller = Symbol('IModuleInstaller'); +export interface IModuleInstaller { + readonly name: string; + readonly displayName: string; + readonly priority: number; + /** + * Installs a module + * If a cancellation token is provided, then a cancellable progress message is dispalyed. + * At this point, this method would resolve only after the module has been successfully installed. + * If cancellation token is not provided, its not guaranteed that module installation has completed. + * @param {string} name + * @param {InterpreterUri} [resource] + * @param {CancellationToken} [cancel] + * @returns {Promise} + * @memberof IModuleInstaller + */ + installModule(name: string, resource?: InterpreterUri, cancel?: CancellationToken): Promise; + isSupported(resource?: InterpreterUri): Promise; +} + +export const IPythonInstallation = Symbol('IPythonInstallation'); +export interface IPythonInstallation { + checkInstallation(): Promise; +} + +export const IInstallationChannelManager = Symbol('IInstallationChannelManager'); +export interface IInstallationChannelManager { + getInstallationChannel(product: Product, resource?: InterpreterUri): Promise; + getInstallationChannels(resource?: InterpreterUri): Promise; + showNoInstallersMessage(): void; +} +export const IProductService = Symbol('IProductService'); +export interface IProductService { + getProductType(product: Product): ProductType; +} +export const IProductPathService = Symbol('IProductPathService'); +export interface IProductPathService { + getExecutableNameFromSettings(product: Product, resource?: Uri): string; + isExecutableAModule(product: Product, resource?: Uri): Boolean; +} + +export const INSIDERS_INSTALLER = 'INSIDERS_INSTALLER'; +export const STABLE_INSTALLER = 'STABLE_INSTALLER'; +export const IExtensionBuildInstaller = Symbol('IExtensionBuildInstaller'); +export interface IExtensionBuildInstaller { + install(): Promise; +} diff --git a/src/client/common/logger.ts b/src/client/common/logger.ts index 556fa5d71e21..a7c446564f8f 100644 --- a/src/client/common/logger.ts +++ b/src/client/common/logger.ts @@ -88,31 +88,31 @@ function initializeConsoleLogger() { (console as any)[logMethods.warn] = console.warn; // tslint:disable-next-line: no-function-expression - console.log = function() { + console.log = function () { const args = Array.prototype.slice.call(arguments); logToConsole('log', ...args); logToFile(LogLevel.Information, ...args); }; // tslint:disable-next-line: no-function-expression - console.info = function() { + console.info = function () { const args = Array.prototype.slice.call(arguments); logToConsole('info', ...args); logToFile(LogLevel.Information, ...args); }; // tslint:disable-next-line: no-function-expression - console.warn = function() { + console.warn = function () { const args = Array.prototype.slice.call(arguments); logToConsole('warn', ...args); logToFile(LogLevel.Warning, ...args); }; // tslint:disable-next-line: no-function-expression - console.error = function() { + console.error = function () { const args = Array.prototype.slice.call(arguments); logToConsole('error', ...args); logToFile(LogLevel.Error, ...args); }; // tslint:disable-next-line: no-function-expression - console.debug = function() { + console.debug = function () { const args = Array.prototype.slice.call(arguments); logToConsole('debug', ...args); logToFile(LogLevel.Information, ...args); @@ -272,10 +272,10 @@ export namespace traceDecorators { } function trace(message: string, options: LogOptions = LogOptions.None, logLevel?: LogLevel) { // tslint:disable-next-line:no-function-expression no-any - return function(_: Object, __: string, descriptor: TypedPropertyDescriptor) { + return function (_: Object, __: string, descriptor: TypedPropertyDescriptor) { const originalMethod = descriptor.value; // tslint:disable-next-line:no-function-expression no-any - descriptor.value = function(...args: any[]) { + descriptor.value = function (...args: any[]) { const className = _ && _.constructor ? _.constructor.name : ''; // tslint:disable-next-line:no-any function writeSuccess(elapsedTime: number, returnValue: any) { @@ -317,11 +317,11 @@ function trace(message: string, options: LogOptions = LogOptions.None, logLevel? if (result && typeof result.then === 'function' && typeof result.catch === 'function') { // tslint:disable-next-line:prefer-type-cast (result as Promise) - .then(data => { + .then((data) => { writeSuccess(timer.elapsedTime, data); return data; }) - .catch(ex => { + .catch((ex) => { writeError(timer.elapsedTime, ex); }); } else { diff --git a/src/client/common/markdown/restTextConverter.ts b/src/client/common/markdown/restTextConverter.ts index 1b160a5e714d..0881b37f3cfb 100644 --- a/src/client/common/markdown/restTextConverter.ts +++ b/src/client/common/markdown/restTextConverter.ts @@ -33,11 +33,7 @@ export class RestTextConverter { public escapeMarkdown(text: string): string { // Not complete escape list so it does not interfere // with subsequent code highlighting (see above). - return text - .replace(/\#/g, '\\#') - .replace(/\*/g, '\\*') - .replace(/\ _/g, ' \\_') - .replace(/^_/, '\\_'); + return text.replace(/\#/g, '\\#').replace(/\*/g, '\\*').replace(/\ _/g, ' \\_').replace(/^_/, '\\_'); } private transformLines(docstring: string): string { diff --git a/src/client/common/net/fileDownloader.ts b/src/client/common/net/fileDownloader.ts index 185cfd35484d..d21c7132459a 100644 --- a/src/client/common/net/fileDownloader.ts +++ b/src/client/common/net/fileDownloader.ts @@ -27,7 +27,7 @@ export class FileDownloader implements IFileDownloader { await this.downloadFileWithStatusBarProgress(uri, options.progressMessagePrefix, tempFile.filePath).then( noop, - ex => { + (ex) => { tempFile.dispose(); return Promise.reject(ex); } @@ -40,7 +40,7 @@ export class FileDownloader implements IFileDownloader { progressMessage: string, tmpFilePath: string ): Promise { - await this.appShell.withProgress({ location: ProgressLocation.Window }, async progress => { + await this.appShell.withProgress({ location: ProgressLocation.Window }, async (progress) => { const req = await this.httpClient.downloadFile(uri); const fileStream = this.fs.createWriteStream(tmpFilePath); return this.displayDownloadProgress(uri, progress, req, fileStream, progressMessage); @@ -54,7 +54,7 @@ export class FileDownloader implements IFileDownloader { progressMessagePrefix: string ): Promise { return new Promise((resolve, reject) => { - request.on('response', response => { + request.on('response', (response) => { if (response.statusCode !== 200) { reject( new Error(`Failed with status ${response.statusCode}, ${response.statusMessage}, Uri ${uri}`) diff --git a/src/client/common/net/socket/socketServer.ts b/src/client/common/net/socket/socketServer.ts index cd9e9d3299a3..ed0b37367f42 100644 --- a/src/client/common/net/socket/socketServer.ts +++ b/src/client/common/net/socket/socketServer.ts @@ -36,7 +36,7 @@ export class SocketServer extends EventEmitter implements ISocketServer { const port = typeof options.port === 'number' ? options.port! : 0; const host = typeof options.host === 'string' ? options.host! : 'localhost'; - this.socketServer!.on('error', ex => { + this.socketServer!.on('error', (ex) => { const msg = `Failed to start the socket server. (Error: ${ex.message})`; def.reject(msg); diff --git a/src/client/common/nuget/azureBlobStoreNugetRepository.ts b/src/client/common/nuget/azureBlobStoreNugetRepository.ts index 1c07f5cf6da1..ae19c65e81be 100644 --- a/src/client/common/nuget/azureBlobStoreNugetRepository.ts +++ b/src/client/common/nuget/azureBlobStoreNugetRepository.ts @@ -47,7 +47,7 @@ export class AzureBlobStoreNugetRepository implements INugetRepository { packageName ); const nugetService = this.serviceContainer.get(INugetService); - return results.map(item => { + return results.map((item) => { return { package: item.name, uri: `${azureCDNBlobStorageAccount}/${azureBlobStorageContainer}/${item.name}`, diff --git a/src/client/common/nuget/nugetRepository.ts b/src/client/common/nuget/nugetRepository.ts index 066cdbfaf6cc..08524d7c3068 100644 --- a/src/client/common/nuget/nugetRepository.ts +++ b/src/client/common/nuget/nugetRepository.ts @@ -1,34 +1,34 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { inject, injectable } from 'inversify'; -import { parse, SemVer } from 'semver'; -import { IHttpClient } from '../../common/types'; -import { IServiceContainer } from '../../ioc/types'; -import { INugetRepository, NugetPackage } from './types'; - -const nugetPackageBaseAddress = - 'https://dotnetmyget.blob.core.windows.net/artifacts/dotnet-core-svc/nuget/v3/flatcontainer'; - -@injectable() -export class NugetRepository implements INugetRepository { - constructor(@inject(IServiceContainer) private readonly serviceContainer: IServiceContainer) {} - public async getPackages(packageName: string): Promise { - const versions = await this.getVersions(nugetPackageBaseAddress, packageName); - return versions.map(version => { - const uri = this.getNugetPackageUri(nugetPackageBaseAddress, packageName, version); - return { version, uri, package: packageName }; - }); - } - public async getVersions(packageBaseAddress: string, packageName: string): Promise { - const uri = `${packageBaseAddress}/${packageName.toLowerCase().trim()}/index.json`; - const httpClient = this.serviceContainer.get(IHttpClient); - const result = await httpClient.getJSON<{ versions: string[] }>(uri); - return result.versions.map(v => parse(v, true) || new SemVer('0.0.0')); - } - public getNugetPackageUri(packageBaseAddress: string, packageName: string, version: SemVer): string { - return `${packageBaseAddress}/${packageName}/${version.raw}/${packageName}.${version.raw}.nupkg`; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { inject, injectable } from 'inversify'; +import { parse, SemVer } from 'semver'; +import { IHttpClient } from '../../common/types'; +import { IServiceContainer } from '../../ioc/types'; +import { INugetRepository, NugetPackage } from './types'; + +const nugetPackageBaseAddress = + 'https://dotnetmyget.blob.core.windows.net/artifacts/dotnet-core-svc/nuget/v3/flatcontainer'; + +@injectable() +export class NugetRepository implements INugetRepository { + constructor(@inject(IServiceContainer) private readonly serviceContainer: IServiceContainer) {} + public async getPackages(packageName: string): Promise { + const versions = await this.getVersions(nugetPackageBaseAddress, packageName); + return versions.map((version) => { + const uri = this.getNugetPackageUri(nugetPackageBaseAddress, packageName, version); + return { version, uri, package: packageName }; + }); + } + public async getVersions(packageBaseAddress: string, packageName: string): Promise { + const uri = `${packageBaseAddress}/${packageName.toLowerCase().trim()}/index.json`; + const httpClient = this.serviceContainer.get(IHttpClient); + const result = await httpClient.getJSON<{ versions: string[] }>(uri); + return result.versions.map((v) => parse(v, true) || new SemVer('0.0.0')); + } + public getNugetPackageUri(packageBaseAddress: string, packageName: string, version: SemVer): string { + return `${packageBaseAddress}/${packageName}/${version.raw}/${packageName}.${version.raw}.nupkg`; + } +} diff --git a/src/client/common/open.ts b/src/client/common/open.ts index 7e1986c4d96e..648cfa8029a9 100644 --- a/src/client/common/open.ts +++ b/src/client/common/open.ts @@ -65,10 +65,10 @@ export function open(opts: any): Promise { const cp = childProcess.spawn(cmd, args, cpOpts); if (opts.wait) { - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { cp.once('error', reject); - cp.once('close', function(code) { + cp.once('close', function (code) { if (code > 0) { reject(new Error(`Exited with code ${code}`)); return; diff --git a/src/client/common/platform/registry.ts b/src/client/common/platform/registry.ts index f612682c23c5..6657d8dd8670 100644 --- a/src/client/common/platform/registry.ts +++ b/src/client/common/platform/registry.ts @@ -32,7 +32,7 @@ export function getArchitectureDisplayName(arch?: Architecture) { async function getRegistryValue(options: Options, name: string = '') { // tslint:disable-next-line:no-require-imports const Registry = require('winreg') as typeof import('winreg'); - return new Promise(resolve => { + return new Promise((resolve) => { new Registry(options).get(name, (error, result) => { if (error || !result || typeof result.value !== 'string') { return resolve(undefined); @@ -46,12 +46,12 @@ async function getRegistryKeys(options: Options): Promise { // tslint:disable-next-line:no-require-imports const Registry = require('winreg') as typeof import('winreg'); // https://github.com/python/peps/blob/master/pep-0514.txt#L85 - return new Promise(resolve => { + return new Promise((resolve) => { new Registry(options).keys((error, result) => { if (error || !Array.isArray(result)) { return resolve([]); } - resolve(result.filter(item => typeof item.key === 'string').map(item => item.key)); + resolve(result.filter((item) => typeof item.key === 'string').map((item) => item.key)); }); }); } diff --git a/src/client/common/process/logger.ts b/src/client/common/process/logger.ts index abb2021c0dc9..a17896fdd467 100644 --- a/src/client/common/process/logger.ts +++ b/src/client/common/process/logger.ts @@ -37,7 +37,7 @@ export class ProcessLogger implements IProcessLogger { info.push(`${Logging.currentWorkingDirectory()} ${this.pathUtils.getDisplayName(options.cwd)}`); } - info.forEach(line => { + info.forEach((line) => { traceInfo(line); this.outputChannel.appendLine(line); }); diff --git a/src/client/common/process/proc.ts b/src/client/common/process/proc.ts index 89b5d4a8b05b..54cf2f53bf43 100644 --- a/src/client/common/process/proc.ts +++ b/src/client/common/process/proc.ts @@ -47,7 +47,7 @@ export class ProcessService extends EventEmitter implements IProcessService { } public dispose() { this.removeAllListeners(); - this.processesToKill.forEach(p => { + this.processesToKill.forEach((p) => { try { p.dispose(); } catch { @@ -63,7 +63,7 @@ export class ProcessService extends EventEmitter implements IProcessService { let procExited = false; const disposable: IDisposable = { // tslint:disable-next-line: no-function-expression - dispose: function() { + dispose: function () { if (proc && !proc.killed && !procExited) { ProcessService.kill(proc.pid); } @@ -74,7 +74,7 @@ export class ProcessService extends EventEmitter implements IProcessService { }; this.processesToKill.add(disposable); - const output = new Observable>(subscriber => { + const output = new Observable>((subscriber) => { const disposables: IDisposable[] = []; const on = (ee: NodeJS.EventEmitter, name: string, fn: Function) => { @@ -108,17 +108,17 @@ export class ProcessService extends EventEmitter implements IProcessService { proc.once('close', () => { procExited = true; subscriber.complete(); - disposables.forEach(d => d.dispose()); + disposables.forEach((d) => d.dispose()); }); proc.once('exit', () => { procExited = true; subscriber.complete(); - disposables.forEach(d => d.dispose()); + disposables.forEach((d) => d.dispose()); }); - proc.once('error', ex => { + proc.once('error', (ex) => { procExited = true; subscriber.error(ex); - disposables.forEach(d => d.dispose()); + disposables.forEach((d) => d.dispose()); }); }); @@ -178,11 +178,11 @@ export class ProcessService extends EventEmitter implements IProcessService { const stdout = this.decoder.decode(stdoutBuffers, encoding); deferred.resolve({ stdout, stderr }); } - disposables.forEach(d => d.dispose()); + disposables.forEach((d) => d.dispose()); }); - proc.once('error', ex => { + proc.once('error', (ex) => { deferred.reject(ex); - disposables.forEach(d => d.dispose()); + disposables.forEach((d) => d.dispose()); }); this.emit('exec', file, args, options); diff --git a/src/client/common/process/pythonDaemon.ts b/src/client/common/process/pythonDaemon.ts index 11117f104fbe..90c5f8187e8f 100644 --- a/src/client/common/process/pythonDaemon.ts +++ b/src/client/common/process/pythonDaemon.ts @@ -70,7 +70,7 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi } catch { noop(); } - this.disposables.forEach(item => item.dispose()); + this.disposables.forEach((item) => item.dispose()); } public async getInterpreterInformation(): Promise { try { @@ -223,7 +223,7 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi 'extraVariables' ]; // tslint:disable-next-line: no-any - return Object.keys(options).every(item => daemonSupportedSpawnOptions.indexOf(item as any) >= 0); + return Object.keys(options).every((item) => daemonSupportedSpawnOptions.indexOf(item as any) >= 0); } private sendRequestWithoutArgs(type: RequestType0): Thenable { return Promise.race([this.connection.sendRequest(type), this.connectionClosedDeferred.promise]); @@ -346,7 +346,7 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi let stdErr = ''; this.proc.stderr.on('data', (output: string | Buffer) => (stdErr += output.toString())); // Wire up stdout/stderr. - const subscription = this.outputObservale.subscribe(out => { + const subscription = this.outputObservale.subscribe((out) => { if (out.source === 'stderr' && options.throwOnStdErr) { subject.error(new StdErrError(out.out)); } else if (out.source === 'stderr' && options.mergeStdOutErr) { @@ -356,7 +356,7 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi } }); start() - .catch(ex => { + .catch((ex) => { const errorMsg = `Failed to run ${ 'fileName' in moduleOrFile ? moduleOrFile.fileName : moduleOrFile.moduleName } as observable with args ${args.join(' ')}`; @@ -391,17 +391,17 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi }; this.disposables.push(this.connection.onClose(() => logConnectionStatus('Daemon Connection Closed'))); this.disposables.push(this.connection.onDispose(() => logConnectionStatus('Daemon Connection disposed'))); - this.disposables.push(this.connection.onError(ex => logConnectionStatus('Daemon Connection errored', ex))); + this.disposables.push(this.connection.onError((ex) => logConnectionStatus('Daemon Connection errored', ex))); // this.proc.on('error', error => logConnectionStatus('Daemon Processed died with error', error)); - this.proc.on('exit', code => logConnectionStatus('Daemon Processed died with exit code', code)); + this.proc.on('exit', (code) => logConnectionStatus('Daemon Processed died with exit code', code)); // Wire up stdout/stderr. const OuputNotification = new NotificationType, void>('output'); - this.connection.onNotification(OuputNotification, output => this.outputObservale.next(output)); + this.connection.onNotification(OuputNotification, (output) => this.outputObservale.next(output)); const logNotification = new NotificationType< { level: 'WARN' | 'WARNING' | 'INFO' | 'DEBUG' | 'NOTSET'; msg: string }, void >('log'); - this.connection.onNotification(logNotification, output => { + this.connection.onNotification(logNotification, (output) => { const msg = `Python Daemon: ${output.msg}`; if (output.level === 'DEBUG' || output.level === 'NOTSET') { traceVerbose(msg); diff --git a/src/client/common/process/pythonDaemonPool.ts b/src/client/common/process/pythonDaemonPool.ts index a2b04e4a75b1..e11922d5f315 100644 --- a/src/client/common/process/pythonDaemonPool.ts +++ b/src/client/common/process/pythonDaemonPool.ts @@ -91,22 +91,22 @@ export class PythonDaemonExecutionServicePool implements IPythonDaemonExecutionS } public async getInterpreterInformation(): Promise { const msg = { args: ['GetPythonVersion'] }; - return this.wrapCall(daemon => daemon.getInterpreterInformation(), msg); + return this.wrapCall((daemon) => daemon.getInterpreterInformation(), msg); } public async getExecutablePath(): Promise { const msg = { args: ['getExecutablePath'] }; - return this.wrapCall(daemon => daemon.getExecutablePath(), msg); + return this.wrapCall((daemon) => daemon.getExecutablePath(), msg); } public getExecutionInfo(args: string[]): PythonExecutionInfo { return this.pythonExecutionService.getExecutionInfo(args); } public async isModuleInstalled(moduleName: string): Promise { const msg = { args: ['-m', moduleName] }; - return this.wrapCall(daemon => daemon.isModuleInstalled(moduleName), msg); + return this.wrapCall((daemon) => daemon.isModuleInstalled(moduleName), msg); } public async exec(args: string[], options: SpawnOptions): Promise> { const msg = { args, options }; - return this.wrapCall(daemon => daemon.exec(args, options), msg); + return this.wrapCall((daemon) => daemon.exec(args, options), msg); } public async execModule( moduleName: string, @@ -114,11 +114,11 @@ export class PythonDaemonExecutionServicePool implements IPythonDaemonExecutionS options: SpawnOptions ): Promise> { const msg = { args: ['-m', moduleName].concat(args), options }; - return this.wrapCall(daemon => daemon.execModule(moduleName, args, options), msg); + return this.wrapCall((daemon) => daemon.execModule(moduleName, args, options), msg); } public execObservable(args: string[], options: SpawnOptions): ObservableExecutionResult { const msg = { args, options }; - return this.wrapObservableCall(daemon => daemon.execObservable(args, options), msg); + return this.wrapObservableCall((daemon) => daemon.execObservable(args, options), msg); } public execModuleObservable( moduleName: string, @@ -126,7 +126,7 @@ export class PythonDaemonExecutionServicePool implements IPythonDaemonExecutionS options: SpawnOptions ): ObservableExecutionResult { const msg = { args: ['-m', moduleName].concat(args), options }; - return this.wrapObservableCall(daemon => daemon.execModuleObservable(moduleName, args, options), msg); + return this.wrapObservableCall((daemon) => daemon.execModuleObservable(moduleName, args, options), msg); } /** * Protected so we can override for testing purposes. @@ -163,7 +163,7 @@ export class PythonDaemonExecutionServicePool implements IPythonDaemonExecutionS data = typeof data === 'string' ? data : data.toString('utf8'); stdError += data; }); - daemonProc.proc.on('error', ex => (procEndEx = ex)); + daemonProc.proc.on('error', (ex) => (procEndEx = ex)); try { await this.testDaemon(connection); diff --git a/src/client/common/process/pythonExecutionFactory.ts b/src/client/common/process/pythonExecutionFactory.ts index 87dc5a240202..1c4c5f58fd63 100644 --- a/src/client/common/process/pythonExecutionFactory.ts +++ b/src/client/common/process/pythonExecutionFactory.ts @@ -111,7 +111,7 @@ export class PythonExecutionFactory implements IPythonExecutionFactory { promise = start(); this.daemonsPerPythonService.set(daemonPoolKey, promise); } - return promise.catch(ex => { + return promise.catch((ex) => { // Ok, we failed to create the daemon (or failed to start). // What ever the cause, we need to log this & give a standard IPythonExecutionService traceError('Failed to create the daemon service, defaulting to activated environment', ex); diff --git a/src/client/common/process/pythonProcess.ts b/src/client/common/process/pythonProcess.ts index 234aded986ed..285ffea1ee0d 100644 --- a/src/client/common/process/pythonProcess.ts +++ b/src/client/common/process/pythonProcess.ts @@ -50,7 +50,7 @@ export class PythonExecutionService implements IPythonExecutionService { } const { command, args } = this.getExecutionInfo(['-c', 'import sys;print(sys.executable)']); - return this.procService.exec(command, args, { throwOnStdErr: true }).then(output => output.stdout.trim()); + return this.procService.exec(command, args, { throwOnStdErr: true }).then((output) => output.stdout.trim()); } public async isModuleInstalled(moduleName: string): Promise { const { command, args } = this.getExecutionInfo(['-c', `import ${moduleName}`]); diff --git a/src/client/common/terminal/activator/index.ts b/src/client/common/terminal/activator/index.ts index 9046039df157..fb7cda998fe9 100644 --- a/src/client/common/terminal/activator/index.ts +++ b/src/client/common/terminal/activator/index.ts @@ -22,7 +22,7 @@ export class TerminalActivator implements ITerminalActivator { options?: TerminalActivationOptions ): Promise { const activated = await this.baseActivator.activateEnvironmentInTerminal(terminal, options); - this.handlers.forEach(handler => + this.handlers.forEach((handler) => handler .handleActivation(terminal, options?.resource, options?.preserveFocus === true, activated) .ignoreErrors() diff --git a/src/client/common/terminal/environmentActivationProviders/condaActivationProvider.ts b/src/client/common/terminal/environmentActivationProviders/condaActivationProvider.ts index 1e1832ddd12c..fa49afb2a29e 100644 --- a/src/client/common/terminal/environmentActivationProviders/condaActivationProvider.ts +++ b/src/client/common/terminal/environmentActivationProviders/condaActivationProvider.ts @@ -1,150 +1,150 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../extensions'; - -import { inject, injectable } from 'inversify'; -import * as path from 'path'; -import { Uri } from 'vscode'; - -import { ICondaService } from '../../../interpreter/contracts'; -import { IPlatformService } from '../../platform/types'; -import { IConfigurationService } from '../../types'; -import { ITerminalActivationCommandProvider, TerminalShellType } from '../types'; - -// Version number of conda that requires we call activate with 'conda activate' instead of just 'activate' -const CondaRequiredMajor = 4; -const CondaRequiredMinor = 4; -const CondaRequiredMinorForPowerShell = 6; - -/** - * Support conda env activation (in the terminal). - */ -@injectable() -export class CondaActivationCommandProvider implements ITerminalActivationCommandProvider { - constructor( - @inject(ICondaService) private readonly condaService: ICondaService, - @inject(IPlatformService) private platform: IPlatformService, - @inject(IConfigurationService) private configService: IConfigurationService - ) {} - - /** - * Is the given shell supported for activating a conda env? - */ - public isShellSupported(_targetShell: TerminalShellType): boolean { - return true; - } - - /** - * Return the command needed to activate the conda env. - */ - public getActivationCommands( - resource: Uri | undefined, - targetShell: TerminalShellType - ): Promise { - const pythonPath = this.configService.getSettings(resource).pythonPath; - return this.getActivationCommandsForInterpreter(pythonPath, targetShell); - } - - /** - * Return the command needed to activate the conda env. - * - */ - public async getActivationCommandsForInterpreter( - pythonPath: string, - targetShell: TerminalShellType - ): Promise { - const envInfo = await this.condaService.getCondaEnvironment(pythonPath); - if (!envInfo) { - return; - } - - const condaEnv = envInfo.name.length > 0 ? envInfo.name : envInfo.path; - - // Algorithm differs based on version - // Old version, just call activate directly. - // New version, call activate from the same path as our python path, then call it again to activate our environment. - // -- note that the 'default' conda location won't allow activate to work for the environment sometimes. - const versionInfo = await this.condaService.getCondaVersion(); - if (versionInfo && versionInfo.major >= CondaRequiredMajor) { - // Conda added support for powershell in 4.6. - if ( - versionInfo.minor >= CondaRequiredMinorForPowerShell && - (targetShell === TerminalShellType.powershell || targetShell === TerminalShellType.powershellCore) - ) { - return this.getPowershellCommands(condaEnv); - } - if (versionInfo.minor >= CondaRequiredMinor) { - // New version. - const interpreterPath = await this.condaService.getCondaFileFromInterpreter(pythonPath, envInfo.name); - if (interpreterPath) { - const activatePath = path.join(path.dirname(interpreterPath), 'activate').fileToCommandArgument(); - const firstActivate = this.platform.isWindows ? activatePath : `source ${activatePath}`; - return [firstActivate, `conda activate ${condaEnv.toCommandArgument()}`]; - } - } - } - - switch (targetShell) { - case TerminalShellType.powershell: - case TerminalShellType.powershellCore: - return this.getPowershellCommands(condaEnv); - - // tslint:disable-next-line:no-suspicious-comment - // TODO: Do we really special-case fish on Windows? - case TerminalShellType.fish: - return this.getFishCommands(condaEnv, await this.condaService.getCondaFile()); - - default: - if (this.platform.isWindows) { - return this.getWindowsCommands(condaEnv); - } else { - return this.getUnixCommands(condaEnv, await this.condaService.getCondaFile()); - } - } - } - - public async getWindowsActivateCommand(): Promise { - let activateCmd: string = 'activate'; - - const condaExePath = await this.condaService.getCondaFile(); - - if (condaExePath && path.basename(condaExePath) !== condaExePath) { - const condaScriptsPath: string = path.dirname(condaExePath); - // prefix the cmd with the found path, and ensure it's quoted properly - activateCmd = path.join(condaScriptsPath, activateCmd); - activateCmd = activateCmd.toCommandArgument(); - } - - return activateCmd; - } - - public async getWindowsCommands(condaEnv: string): Promise { - const activate = await this.getWindowsActivateCommand(); - return [`${activate} ${condaEnv.toCommandArgument()}`]; - } - /** - * The expectation is for the user to configure Powershell for Conda. - * Hence we just send the command `conda activate ...`. - * This configuration is documented on Conda. - * Extension will not attempt to work around issues by trying to setup shell for user. - * - * @param {string} envName - * @returns {(Promise)} - * @memberof CondaActivationCommandProvider - */ - public async getPowershellCommands(condaEnv: string): Promise { - return [`conda activate ${condaEnv.toCommandArgument()}`]; - } - - public async getFishCommands(condaEnv: string, condaFile: string): Promise { - // https://github.com/conda/conda/blob/be8c08c083f4d5e05b06bd2689d2cd0d410c2ffe/shell/etc/fish/conf.d/conda.fish#L18-L28 - return [`${condaFile.fileToCommandArgument()} activate ${condaEnv.toCommandArgument()}`]; - } - - public async getUnixCommands(condaEnv: string, condaFile: string): Promise { - const condaDir = path.dirname(condaFile); - const activateFile = path.join(condaDir, 'activate'); - return [`source ${activateFile.fileToCommandArgument()} ${condaEnv.toCommandArgument()}`]; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../extensions'; + +import { inject, injectable } from 'inversify'; +import * as path from 'path'; +import { Uri } from 'vscode'; + +import { ICondaService } from '../../../interpreter/contracts'; +import { IPlatformService } from '../../platform/types'; +import { IConfigurationService } from '../../types'; +import { ITerminalActivationCommandProvider, TerminalShellType } from '../types'; + +// Version number of conda that requires we call activate with 'conda activate' instead of just 'activate' +const CondaRequiredMajor = 4; +const CondaRequiredMinor = 4; +const CondaRequiredMinorForPowerShell = 6; + +/** + * Support conda env activation (in the terminal). + */ +@injectable() +export class CondaActivationCommandProvider implements ITerminalActivationCommandProvider { + constructor( + @inject(ICondaService) private readonly condaService: ICondaService, + @inject(IPlatformService) private platform: IPlatformService, + @inject(IConfigurationService) private configService: IConfigurationService + ) {} + + /** + * Is the given shell supported for activating a conda env? + */ + public isShellSupported(_targetShell: TerminalShellType): boolean { + return true; + } + + /** + * Return the command needed to activate the conda env. + */ + public getActivationCommands( + resource: Uri | undefined, + targetShell: TerminalShellType + ): Promise { + const pythonPath = this.configService.getSettings(resource).pythonPath; + return this.getActivationCommandsForInterpreter(pythonPath, targetShell); + } + + /** + * Return the command needed to activate the conda env. + * + */ + public async getActivationCommandsForInterpreter( + pythonPath: string, + targetShell: TerminalShellType + ): Promise { + const envInfo = await this.condaService.getCondaEnvironment(pythonPath); + if (!envInfo) { + return; + } + + const condaEnv = envInfo.name.length > 0 ? envInfo.name : envInfo.path; + + // Algorithm differs based on version + // Old version, just call activate directly. + // New version, call activate from the same path as our python path, then call it again to activate our environment. + // -- note that the 'default' conda location won't allow activate to work for the environment sometimes. + const versionInfo = await this.condaService.getCondaVersion(); + if (versionInfo && versionInfo.major >= CondaRequiredMajor) { + // Conda added support for powershell in 4.6. + if ( + versionInfo.minor >= CondaRequiredMinorForPowerShell && + (targetShell === TerminalShellType.powershell || targetShell === TerminalShellType.powershellCore) + ) { + return this.getPowershellCommands(condaEnv); + } + if (versionInfo.minor >= CondaRequiredMinor) { + // New version. + const interpreterPath = await this.condaService.getCondaFileFromInterpreter(pythonPath, envInfo.name); + if (interpreterPath) { + const activatePath = path.join(path.dirname(interpreterPath), 'activate').fileToCommandArgument(); + const firstActivate = this.platform.isWindows ? activatePath : `source ${activatePath}`; + return [firstActivate, `conda activate ${condaEnv.toCommandArgument()}`]; + } + } + } + + switch (targetShell) { + case TerminalShellType.powershell: + case TerminalShellType.powershellCore: + return this.getPowershellCommands(condaEnv); + + // tslint:disable-next-line:no-suspicious-comment + // TODO: Do we really special-case fish on Windows? + case TerminalShellType.fish: + return this.getFishCommands(condaEnv, await this.condaService.getCondaFile()); + + default: + if (this.platform.isWindows) { + return this.getWindowsCommands(condaEnv); + } else { + return this.getUnixCommands(condaEnv, await this.condaService.getCondaFile()); + } + } + } + + public async getWindowsActivateCommand(): Promise { + let activateCmd: string = 'activate'; + + const condaExePath = await this.condaService.getCondaFile(); + + if (condaExePath && path.basename(condaExePath) !== condaExePath) { + const condaScriptsPath: string = path.dirname(condaExePath); + // prefix the cmd with the found path, and ensure it's quoted properly + activateCmd = path.join(condaScriptsPath, activateCmd); + activateCmd = activateCmd.toCommandArgument(); + } + + return activateCmd; + } + + public async getWindowsCommands(condaEnv: string): Promise { + const activate = await this.getWindowsActivateCommand(); + return [`${activate} ${condaEnv.toCommandArgument()}`]; + } + /** + * The expectation is for the user to configure Powershell for Conda. + * Hence we just send the command `conda activate ...`. + * This configuration is documented on Conda. + * Extension will not attempt to work around issues by trying to setup shell for user. + * + * @param {string} envName + * @returns {(Promise)} + * @memberof CondaActivationCommandProvider + */ + public async getPowershellCommands(condaEnv: string): Promise { + return [`conda activate ${condaEnv.toCommandArgument()}`]; + } + + public async getFishCommands(condaEnv: string, condaFile: string): Promise { + // https://github.com/conda/conda/blob/be8c08c083f4d5e05b06bd2689d2cd0d410c2ffe/shell/etc/fish/conf.d/conda.fish#L18-L28 + return [`${condaFile.fileToCommandArgument()} activate ${condaEnv.toCommandArgument()}`]; + } + + public async getUnixCommands(condaEnv: string, condaFile: string): Promise { + const condaDir = path.dirname(condaFile); + const activateFile = path.join(condaDir, 'activate'); + return [`source ${activateFile.fileToCommandArgument()} ${condaEnv.toCommandArgument()}`]; + } +} diff --git a/src/client/common/terminal/helper.ts b/src/client/common/terminal/helper.ts index b56be21ed664..6f73678354da 100644 --- a/src/client/common/terminal/helper.ts +++ b/src/client/common/terminal/helper.ts @@ -145,7 +145,7 @@ export class TerminalHelper implements ITerminalHelper { } // Search from the list of providers. - const supportedProviders = providers.filter(provider => provider.isShellSupported(terminalShellType)); + const supportedProviders = providers.filter((provider) => provider.isShellSupported(terminalShellType)); for (const provider of supportedProviders) { const activationCommands = interpreter diff --git a/src/client/common/terminal/service.ts b/src/client/common/terminal/service.ts index 10acc06fc36c..ae46b86eda75 100644 --- a/src/client/common/terminal/service.ts +++ b/src/client/common/terminal/service.ts @@ -76,7 +76,7 @@ export class TerminalService implements ITerminalService, Disposable { }); // Sometimes the terminal takes some time to start up before it can start accepting input. - await new Promise(resolve => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); await this.terminalActivator.activateEnvironmentInTerminal(this.terminal!, { resource: this.options?.resource, diff --git a/src/client/common/terminal/syncTerminalService.ts b/src/client/common/terminal/syncTerminalService.ts index e24712bc9d84..149b631d463a 100644 --- a/src/client/common/terminal/syncTerminalService.ts +++ b/src/client/common/terminal/syncTerminalService.ts @@ -160,7 +160,7 @@ export class SynchronousTerminalService implements ITerminalService, Disposable } private createLockFile(): Promise { - return this.fs.createTemporaryFile('.log').then(l => { + return this.fs.createTemporaryFile('.log').then((l) => { this.disposables.push(l); return l; }); diff --git a/src/client/common/utils/async.ts b/src/client/common/utils/async.ts index ee490c01c38b..8ecd281a862d 100644 --- a/src/client/common/utils/async.ts +++ b/src/client/common/utils/async.ts @@ -4,7 +4,7 @@ 'use strict'; export async function sleep(timeout: number): Promise { - return new Promise(resolve => { + return new Promise((resolve) => { setTimeout(resolve, timeout); }); } @@ -14,13 +14,13 @@ export async function waitForPromise(promise: Promise, timeout: number): P return new Promise((resolve, reject) => { const timer = setTimeout(() => resolve(null), timeout); promise - .then(result => { + .then((result) => { // When the promise resolves, make sure to clear the timer or // the timer may stick around causing tests to wait clearTimeout(timer); resolve(result); }) - .catch(e => { + .catch((e) => { clearTimeout(timer); reject(e); }); diff --git a/src/client/common/utils/cacheUtils.ts b/src/client/common/utils/cacheUtils.ts index a9394f12e8ae..f6b74de2fb3f 100644 --- a/src/client/common/utils/cacheUtils.ts +++ b/src/client/common/utils/cacheUtils.ts @@ -74,7 +74,7 @@ export function getGlobalCacheStore() { } export function getCacheKeyFromFunctionArgs(keyPrefix: string, fnArgs: any[]): string { - const argsKey = fnArgs.map(arg => `${JSON.stringify(arg)}`).join('-Arg-Separator-'); + const argsKey = fnArgs.map((arg) => `${JSON.stringify(arg)}`).join('-Arg-Separator-'); return `KeyPrefix=${keyPrefix}-Args=${argsKey}`; } diff --git a/src/client/common/utils/decorators.ts b/src/client/common/utils/decorators.ts index 315d7f16149b..644819e1ae53 100644 --- a/src/client/common/utils/decorators.ts +++ b/src/client/common/utils/decorators.ts @@ -56,7 +56,7 @@ export function debounceAsync(wait?: number) { export function makeDebounceDecorator(wait?: number) { // tslint:disable-next-line:no-any no-function-expression - return function(_target: any, _propertyName: string, descriptor: TypedPropertyDescriptor) { + return function (_target: any, _propertyName: string, descriptor: TypedPropertyDescriptor) { // We could also make use of _debounce() options. For instance, // the following causes the original method to be called // immediately: @@ -71,7 +71,7 @@ export function makeDebounceDecorator(wait?: number) { const options = {}; const originalMethod = descriptor.value!; const debounced = _debounce( - function(this: any) { + function (this: any) { return originalMethod.apply(this, arguments as any); }, wait, @@ -83,7 +83,7 @@ export function makeDebounceDecorator(wait?: number) { export function makeDebounceAsyncDecorator(wait?: number) { // tslint:disable-next-line:no-any no-function-expression - return function(_target: any, _propertyName: string, descriptor: TypedPropertyDescriptor) { + return function (_target: any, _propertyName: string, descriptor: TypedPropertyDescriptor) { type StateInformation = { started: boolean; deferred: Deferred | undefined; @@ -93,7 +93,7 @@ export function makeDebounceAsyncDecorator(wait?: number) { const state: StateInformation = { started: false, deferred: undefined, timer: undefined }; // Lets defer execution using a setTimeout for the given time. - (descriptor as any).value = function(this: any) { + (descriptor as any).value = function (this: any) { const existingDeferred: Deferred | undefined = state.deferred; if (existingDeferred && state.started) { return existingDeferred.promise; @@ -111,11 +111,11 @@ export function makeDebounceAsyncDecorator(wait?: number) { state.started = true; originalMethod .apply(this) - .then(r => { + .then((r) => { state.started = false; deferred.resolve(r); }) - .catch(ex => { + .catch((ex) => { state.started = false; deferred.reject(ex); }); @@ -141,20 +141,20 @@ export function cacheResourceSpecificInterpreterData( expiryDurationMs: number, vscode: VSCodeType = require('vscode') ) { - return function( + return function ( _target: Object, _propertyName: string, descriptor: TypedPropertyDescriptor ) { const originalMethod = descriptor.value!; - descriptor.value = async function(...args: [Uri | undefined, ...any[]]) { + descriptor.value = async function (...args: [Uri | undefined, ...any[]]) { const cacheStore = new InMemoryInterpreterSpecificCache(key, expiryDurationMs, args, vscode); if (cacheStore.hasData) { traceVerbose(`Cached data exists ${key}, ${args[0] ? args[0].fsPath : ''}`); return Promise.resolve(cacheStore.data); } const promise = originalMethod.apply(this, args) as Promise; - promise.then(result => (cacheStore.data = result)).ignoreErrors(); + promise.then((result) => (cacheStore.data = result)).ignoreErrors(); return promise; }; }; @@ -163,7 +163,7 @@ export function cacheResourceSpecificInterpreterData( type PromiseFunctionWithAnyArgs = (...any: any) => Promise; const cacheStoreForMethods = getGlobalCacheStore(); export function cache(expiryDurationMs: number) { - return function( + return function ( target: Object, propertyName: string, descriptor: TypedPropertyDescriptor @@ -171,7 +171,7 @@ export function cache(expiryDurationMs: number) { const originalMethod = descriptor.value!; const className = 'constructor' in target && target.constructor.name ? target.constructor.name : ''; const keyPrefix = `Cache_Method_Output_${className}.${propertyName}`; - descriptor.value = async function(...args: any) { + descriptor.value = async function (...args: any) { if (isTestExecution()) { return originalMethod.apply(this, args) as Promise; } @@ -183,7 +183,9 @@ export function cache(expiryDurationMs: number) { } const promise = originalMethod.apply(this, args) as Promise; promise - .then(result => cacheStoreForMethods.set(key, { data: result, expiry: Date.now() + expiryDurationMs })) + .then((result) => + cacheStoreForMethods.set(key, { data: result, expiry: Date.now() + expiryDurationMs }) + ) .ignoreErrors(); return promise; }; @@ -199,18 +201,18 @@ export function cache(expiryDurationMs: number) { */ export function swallowExceptions(scopeName: string) { // tslint:disable-next-line:no-any no-function-expression - return function(_target: any, propertyName: string, descriptor: TypedPropertyDescriptor) { + return function (_target: any, propertyName: string, descriptor: TypedPropertyDescriptor) { const originalMethod = descriptor.value!; const errorMessage = `Python Extension (Error in ${scopeName}, method:${propertyName}):`; // tslint:disable-next-line:no-any no-function-expression - descriptor.value = function(...args: any[]) { + descriptor.value = function (...args: any[]) { try { // tslint:disable-next-line:no-invalid-this no-use-before-declare no-unsafe-any const result = originalMethod.apply(this, args); // If method being wrapped returns a promise then wait and swallow errors. if (result && typeof result.then === 'function' && typeof result.catch === 'function') { - return (result as Promise).catch(error => { + return (result as Promise).catch((error) => { if (isTestExecution()) { return; } @@ -231,10 +233,10 @@ export function swallowExceptions(scopeName: string) { type PromiseFunction = (...any: any[]) => Promise; export function displayProgress(title: string, location = ProgressLocation.Window) { - return function(_target: Object, _propertyName: string, descriptor: TypedPropertyDescriptor) { + return function (_target: Object, _propertyName: string, descriptor: TypedPropertyDescriptor) { const originalMethod = descriptor.value!; // tslint:disable-next-line:no-any no-function-expression - descriptor.value = async function(...args: any[]) { + descriptor.value = async function (...args: any[]) { const progressOptions: ProgressOptions = { location, title }; // tslint:disable-next-line:no-invalid-this const promise = originalMethod.apply(this, args); diff --git a/src/client/common/utils/enum.ts b/src/client/common/utils/enum.ts index d2ad3acf7d90..2d6a53fdb505 100644 --- a/src/client/common/utils/enum.ts +++ b/src/client/common/utils/enum.ts @@ -6,17 +6,17 @@ // tslint:disable:no-any export function getNamesAndValues(e: any): { name: string; value: T }[] { - return getNames(e).map(n => ({ name: n, value: e[n] })); + return getNames(e).map((n) => ({ name: n, value: e[n] })); } export function getNames(e: any) { - return getObjValues(e).filter(v => typeof v === 'string') as string[]; + return getObjValues(e).filter((v) => typeof v === 'string') as string[]; } export function getValues(e: any) { - return (getObjValues(e).filter(v => typeof v === 'number') as any) as T[]; + return (getObjValues(e).filter((v) => typeof v === 'number') as any) as T[]; } function getObjValues(e: any): (number | string)[] { - return Object.keys(e).map(k => e[k]); + return Object.keys(e).map((k) => e[k]); } diff --git a/src/client/common/utils/misc.ts b/src/client/common/utils/misc.ts index c0c15f050c09..fc61a123b4cf 100644 --- a/src/client/common/utils/misc.ts +++ b/src/client/common/utils/misc.ts @@ -1,63 +1,63 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { Uri } from 'vscode'; -import { InterpreterUri } from '../installer/types'; -import { IAsyncDisposable, IDisposable, Resource } from '../types'; - -// tslint:disable-next-line:no-empty -export function noop() {} - -export function using(disposable: T, func: (obj: T) => void) { - try { - func(disposable); - } finally { - disposable.dispose(); - } -} - -export async function usingAsync( - disposable: T, - func: (obj: T) => Promise -): Promise { - try { - return await func(disposable); - } finally { - await disposable.dispose(); - } -} - -/** - * Checking whether something is a Resource (Uri/undefined). - * Using `instanceof Uri` doesn't always work as the object is not an instance of Uri (at least not in tests). - * That's why VSC too has a helper method `URI.isUri` (though not public). - * - * @export - * @param {InterpreterUri} [resource] - * @returns {resource is Resource} - */ -export function isResource(resource?: InterpreterUri): resource is Resource { - if (!resource) { - return true; - } - const uri = resource as Uri; - return typeof uri.path === 'string' && typeof uri.scheme === 'string'; -} - -/** - * Checking whether something is a Uri. - * Using `instanceof Uri` doesn't always work as the object is not an instance of Uri (at least not in tests). - * That's why VSC too has a helper method `URI.isUri` (though not public). - * - * @export - * @param {InterpreterUri} [resource] - * @returns {resource is Uri} - */ -// tslint:disable-next-line: no-any -export function isUri(resource?: Uri | any): resource is Uri { - if (!resource) { - return false; - } - const uri = resource as Uri; - return typeof uri.path === 'string' && typeof uri.scheme === 'string'; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { Uri } from 'vscode'; +import { InterpreterUri } from '../installer/types'; +import { IAsyncDisposable, IDisposable, Resource } from '../types'; + +// tslint:disable-next-line:no-empty +export function noop() {} + +export function using(disposable: T, func: (obj: T) => void) { + try { + func(disposable); + } finally { + disposable.dispose(); + } +} + +export async function usingAsync( + disposable: T, + func: (obj: T) => Promise +): Promise { + try { + return await func(disposable); + } finally { + await disposable.dispose(); + } +} + +/** + * Checking whether something is a Resource (Uri/undefined). + * Using `instanceof Uri` doesn't always work as the object is not an instance of Uri (at least not in tests). + * That's why VSC too has a helper method `URI.isUri` (though not public). + * + * @export + * @param {InterpreterUri} [resource] + * @returns {resource is Resource} + */ +export function isResource(resource?: InterpreterUri): resource is Resource { + if (!resource) { + return true; + } + const uri = resource as Uri; + return typeof uri.path === 'string' && typeof uri.scheme === 'string'; +} + +/** + * Checking whether something is a Uri. + * Using `instanceof Uri` doesn't always work as the object is not an instance of Uri (at least not in tests). + * That's why VSC too has a helper method `URI.isUri` (though not public). + * + * @export + * @param {InterpreterUri} [resource] + * @returns {resource is Uri} + */ +// tslint:disable-next-line: no-any +export function isUri(resource?: Uri | any): resource is Uri { + if (!resource) { + return false; + } + const uri = resource as Uri; + return typeof uri.path === 'string' && typeof uri.scheme === 'string'; +} diff --git a/src/client/common/utils/multiStepInput.ts b/src/client/common/utils/multiStepInput.ts index c905300fbec5..424f1552dd1d 100644 --- a/src/client/common/utils/multiStepInput.ts +++ b/src/client/common/utils/multiStepInput.ts @@ -104,14 +104,14 @@ export class MultiStepInput implements IMultiStepInput { } input.buttons = [...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), ...(buttons || [])]; disposables.push( - input.onDidTriggerButton(item => { + input.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { reject(InputFlowAction.back); } else { resolve(item); } }), - input.onDidChangeSelection(selectedItems => resolve(selectedItems[0])), + input.onDidChangeSelection((selectedItems) => resolve(selectedItems[0])), input.onDidHide(() => { (async () => { reject( @@ -127,7 +127,7 @@ export class MultiStepInput implements IMultiStepInput { this.current.show(); }); } finally { - disposables.forEach(d => d.dispose()); + disposables.forEach((d) => d.dispose()); } } @@ -154,7 +154,7 @@ export class MultiStepInput implements IMultiStepInput { input.buttons = [...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), ...(buttons || [])]; let validating = validate(''); disposables.push( - input.onDidTriggerButton(item => { + input.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { reject(InputFlowAction.back); } else { @@ -171,7 +171,7 @@ export class MultiStepInput implements IMultiStepInput { input.enabled = true; input.busy = false; }), - input.onDidChangeValue(async text => { + input.onDidChangeValue(async (text) => { const current = validate(text); validating = current; const validationMessage = await current; @@ -194,7 +194,7 @@ export class MultiStepInput implements IMultiStepInput { this.current.show(); }); } finally { - disposables.forEach(d => d.dispose()); + disposables.forEach((d) => d.dispose()); } } diff --git a/src/client/common/utils/sysTypes.ts b/src/client/common/utils/sysTypes.ts index a67726a58f5f..ce0bce0af963 100644 --- a/src/client/common/utils/sysTypes.ts +++ b/src/client/common/utils/sysTypes.ts @@ -45,7 +45,7 @@ export function isString(str: any): str is string { * @returns whether the provided parameter is a JavaScript Array and each element in the array is a string. */ export function isStringArray(value: any): value is string[] { - return isArray(value) && (value).every(elem => isString(elem)); + return isArray(value) && (value).every((elem) => isString(elem)); } /** diff --git a/src/client/common/utils/version.ts b/src/client/common/utils/version.ts index a631efa52248..e48ac849f655 100644 --- a/src/client/common/utils/version.ts +++ b/src/client/common/utils/version.ts @@ -22,8 +22,8 @@ export function parsePythonVersion(version: string): Version | undefined { } const versionParts = (version || '') .split('.') - .map(item => item.trim()) - .filter(item => item.length > 0) + .map((item) => item.trim()) + .filter((item) => item.length > 0) .filter((_, index) => index < 4); if (versionParts.length > 0 && versionParts[versionParts.length - 1].indexOf('-') > 0) { diff --git a/src/client/common/variables/environment.ts b/src/client/common/variables/environment.ts index 887cd0993501..63f7896d5423 100644 --- a/src/client/common/variables/environment.ts +++ b/src/client/common/variables/environment.ts @@ -33,7 +33,7 @@ export class EnvironmentVariablesService implements IEnvironmentVariablesService return; } const settingsNotToMerge = ['PYTHONPATH', this.pathVariable]; - Object.keys(source).forEach(setting => { + Object.keys(source).forEach((setting) => { if (settingsNotToMerge.indexOf(setting) >= 0) { return; } @@ -64,8 +64,8 @@ export class EnvironmentVariablesService implements IEnvironmentVariablesService ...pathsToAppend: string[] ) { const valueToAppend = pathsToAppend - .filter(item => typeof item === 'string' && item.trim().length > 0) - .map(item => item.trim()) + .filter((item) => typeof item === 'string' && item.trim().length > 0) + .map((item) => item.trim()) .join(path.delimiter); if (valueToAppend.length === 0) { return vars; diff --git a/src/client/common/variables/environmentVariablesProvider.ts b/src/client/common/variables/environmentVariablesProvider.ts index 2ba64ed64798..a97963be0c2a 100644 --- a/src/client/common/variables/environmentVariablesProvider.ts +++ b/src/client/common/variables/environmentVariablesProvider.ts @@ -36,7 +36,7 @@ export class EnvironmentVariablesProvider implements IEnvironmentVariablesProvid public dispose() { this.changeEventEmitter.dispose(); - this.fileWatchers.forEach(watcher => { + this.fileWatchers.forEach((watcher) => { if (watcher) { watcher.dispose(); } @@ -67,7 +67,7 @@ export class EnvironmentVariablesProvider implements IEnvironmentVariablesProvid return this.envVarsService.parseFile(settings.envFile, this.process.env); } public configurationChanged(e: ConfigurationChangeEvent) { - this.trackedWorkspaceFolders.forEach(item => { + this.trackedWorkspaceFolders.forEach((item) => { const uri = item && item.length > 0 ? Uri.file(item) : undefined; if (e.affectsConfiguration('python.envFile', uri)) { this.onEnvironmentFileChanged(uri); diff --git a/src/client/common/variables/systemVariables.ts b/src/client/common/variables/systemVariables.ts index c0d615a13164..74b307dafb78 100644 --- a/src/client/common/variables/systemVariables.ts +++ b/src/client/common/variables/systemVariables.ts @@ -62,7 +62,7 @@ abstract class AbstractSystemVariables implements ISystemVariables { values: IStringDictionary | string[]> ): IStringDictionary | string[]> { const result: IStringDictionary | string[]> = Object.create(null); - Object.keys(values).forEach(key => { + Object.keys(values).forEach((key) => { const value = values[key]; // tslint:disable-next-line:no-any result[key] = this.resolve(value); @@ -74,7 +74,7 @@ abstract class AbstractSystemVariables implements ISystemVariables { // tslint:disable-next-line:no-any private __resolveAnyLiteral(values: any): any { const result: IStringDictionary | string[]> = Object.create(null); - Object.keys(values).forEach(key => { + Object.keys(values).forEach((key) => { const value = values[key]; // tslint:disable-next-line:no-any result[key] = this.resolveAny(value); @@ -83,13 +83,13 @@ abstract class AbstractSystemVariables implements ISystemVariables { } private __resolveArray(value: string[]): string[] { - return value.map(s => this.__resolveString(s)); + return value.map((s) => this.__resolveString(s)); } private __resolveAnyArray(value: T[]): T[]; // tslint:disable-next-line:no-any private __resolveAnyArray(value: any[]): any[] { - return value.map(s => this.resolveAny(s)); + return value.map((s) => this.resolveAny(s)); } } @@ -122,7 +122,7 @@ export class SystemVariables extends AbstractSystemVariables { ); } this._execPath = process.execPath; - Object.keys(process.env).forEach(key => { + Object.keys(process.env).forEach((key) => { ((this as any) as Record)[`env:${key}`] = ((this as any) as Record< string, string | undefined diff --git a/src/client/datascience/cellFactory.ts b/src/client/datascience/cellFactory.ts index 01f3b5b130b1..e127ac208191 100644 --- a/src/client/datascience/cellFactory.ts +++ b/src/client/datascience/cellFactory.ts @@ -151,7 +151,7 @@ export function generateCellsFromString(source: string, settings?: IDataScienceS // For each one, get its text and turn it into a cell return Array.prototype.concat( - ...starts.map(s => { + ...starts.map((s) => { return generateCells(settings, s.code, '', s.startLine, false, uuid()); }) ); @@ -194,7 +194,7 @@ export function generateCellsFromDocument(document: TextDocument, settings?: IDa // For each one, get its text and turn it into a cell return Array.prototype.concat( - ...ranges.map(cr => { + ...ranges.map((cr) => { const code = document.getText(cr.range); return generateCells(settings, code, '', cr.range.start.line, false, uuid()); }) diff --git a/src/client/datascience/cellMatcher.ts b/src/client/datascience/cellMatcher.ts index fbb937506f5f..53a89392fd97 100644 --- a/src/client/datascience/cellMatcher.ts +++ b/src/client/datascience/cellMatcher.ts @@ -1,88 +1,88 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../common/extensions'; - -import { IDataScienceSettings } from '../common/types'; -import { noop } from '../common/utils/misc'; -import { RegExpValues } from './constants'; - -export class CellMatcher { - private codeMatchRegEx: RegExp; - private markdownMatchRegEx: RegExp; - private codeExecRegEx: RegExp; - private markdownExecRegEx: RegExp; - private defaultCellMarker: string; - private defaultCellMarkerExec: RegExp; - - constructor(settings?: IDataScienceSettings) { - this.codeMatchRegEx = this.createRegExp( - settings ? settings.codeRegularExpression : undefined, - RegExpValues.PythonCellMarker - ); - this.markdownMatchRegEx = this.createRegExp( - settings ? settings.markdownRegularExpression : undefined, - RegExpValues.PythonMarkdownCellMarker - ); - this.codeExecRegEx = new RegExp(`${this.codeMatchRegEx.source}(.*)`); - this.markdownExecRegEx = new RegExp(`${this.markdownMatchRegEx.source}(.*)`); - this.defaultCellMarker = settings?.defaultCellMarker ? settings.defaultCellMarker : '# %%'; - this.defaultCellMarkerExec = this.createRegExp(`${this.defaultCellMarker}(.*)`, /# %%(.*)/); - } - - public isCell(code: string): boolean { - return this.isCode(code) || this.isMarkdown(code); - } - - public isMarkdown(code: string): boolean { - return this.markdownMatchRegEx.test(code); - } - - public isCode(code: string): boolean { - return this.codeMatchRegEx.test(code) || code.trim() === this.defaultCellMarker; - } - - public getCellType(code: string): string { - return this.isMarkdown(code) ? 'markdown' : 'code'; - } - - public stripFirstMarker(code: string): string { - const lines = code.splitLines({ trim: false, removeEmptyEntries: false }); - - // Only strip this off the first line. Otherwise we want the markers in the code. - if (lines.length > 0 && (this.isCode(lines[0]) || this.isMarkdown(lines[0]))) { - return lines.slice(1).join('\n'); - } - return code; - } - - public exec(code: string): string | undefined { - let result: RegExpExecArray | null = null; - if (this.defaultCellMarkerExec.test(code)) { - this.defaultCellMarkerExec.lastIndex = -1; - result = this.defaultCellMarkerExec.exec(code); - } else if (this.codeMatchRegEx.test(code)) { - this.codeExecRegEx.lastIndex = -1; - result = this.codeExecRegEx.exec(code); - } else if (this.markdownMatchRegEx.test(code)) { - this.markdownExecRegEx.lastIndex = -1; - result = this.markdownExecRegEx.exec(code); - } - if (result) { - return result.length > 1 ? result[result.length - 1].trim() : ''; - } - return undefined; - } - - private createRegExp(potential: string | undefined, backup: RegExp): RegExp { - try { - if (potential) { - return new RegExp(potential); - } - } catch { - noop(); - } - - return backup; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../common/extensions'; + +import { IDataScienceSettings } from '../common/types'; +import { noop } from '../common/utils/misc'; +import { RegExpValues } from './constants'; + +export class CellMatcher { + private codeMatchRegEx: RegExp; + private markdownMatchRegEx: RegExp; + private codeExecRegEx: RegExp; + private markdownExecRegEx: RegExp; + private defaultCellMarker: string; + private defaultCellMarkerExec: RegExp; + + constructor(settings?: IDataScienceSettings) { + this.codeMatchRegEx = this.createRegExp( + settings ? settings.codeRegularExpression : undefined, + RegExpValues.PythonCellMarker + ); + this.markdownMatchRegEx = this.createRegExp( + settings ? settings.markdownRegularExpression : undefined, + RegExpValues.PythonMarkdownCellMarker + ); + this.codeExecRegEx = new RegExp(`${this.codeMatchRegEx.source}(.*)`); + this.markdownExecRegEx = new RegExp(`${this.markdownMatchRegEx.source}(.*)`); + this.defaultCellMarker = settings?.defaultCellMarker ? settings.defaultCellMarker : '# %%'; + this.defaultCellMarkerExec = this.createRegExp(`${this.defaultCellMarker}(.*)`, /# %%(.*)/); + } + + public isCell(code: string): boolean { + return this.isCode(code) || this.isMarkdown(code); + } + + public isMarkdown(code: string): boolean { + return this.markdownMatchRegEx.test(code); + } + + public isCode(code: string): boolean { + return this.codeMatchRegEx.test(code) || code.trim() === this.defaultCellMarker; + } + + public getCellType(code: string): string { + return this.isMarkdown(code) ? 'markdown' : 'code'; + } + + public stripFirstMarker(code: string): string { + const lines = code.splitLines({ trim: false, removeEmptyEntries: false }); + + // Only strip this off the first line. Otherwise we want the markers in the code. + if (lines.length > 0 && (this.isCode(lines[0]) || this.isMarkdown(lines[0]))) { + return lines.slice(1).join('\n'); + } + return code; + } + + public exec(code: string): string | undefined { + let result: RegExpExecArray | null = null; + if (this.defaultCellMarkerExec.test(code)) { + this.defaultCellMarkerExec.lastIndex = -1; + result = this.defaultCellMarkerExec.exec(code); + } else if (this.codeMatchRegEx.test(code)) { + this.codeExecRegEx.lastIndex = -1; + result = this.codeExecRegEx.exec(code); + } else if (this.markdownMatchRegEx.test(code)) { + this.markdownExecRegEx.lastIndex = -1; + result = this.markdownExecRegEx.exec(code); + } + if (result) { + return result.length > 1 ? result[result.length - 1].trim() : ''; + } + return undefined; + } + + private createRegExp(potential: string | undefined, backup: RegExp): RegExp { + try { + if (potential) { + return new RegExp(potential); + } + } catch { + noop(); + } + + return backup; + } +} diff --git a/src/client/datascience/codeCssGenerator.ts b/src/client/datascience/codeCssGenerator.ts index c1b1a1a65b6e..8e2d615a3fd0 100644 --- a/src/client/datascience/codeCssGenerator.ts +++ b/src/client/datascience/codeCssGenerator.ts @@ -1,519 +1,519 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { JSONArray, JSONObject } from '@phosphor/coreutils'; -import { inject, injectable } from 'inversify'; -import { parse } from 'jsonc-parser'; -import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; -import * as path from 'path'; - -import { IWorkspaceService } from '../common/application/types'; -import { traceError, traceInfo, traceWarning } from '../common/logger'; -import { IFileSystem } from '../common/platform/types'; -import { IConfigurationService, Resource } from '../common/types'; -import { DefaultTheme } from './constants'; -import { ICodeCssGenerator, IThemeFinder } from './types'; - -// tslint:disable:no-any -const DarkTheme = 'dark'; -const LightTheme = 'light'; - -const MonacoColorRegEx = /^#?([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/; -const ThreeColorRegEx = /^#?([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f])$/; - -// These are based on the colors generated by 'Default Light+' and are only set when we -// are ignoring themes. -//tslint:disable:no-multiline-string object-literal-key-quotes -const DefaultCssVars: { [key: string]: string } = { - light: ` - :root { - --override-widget-background: #f3f3f3; - --override-foreground: #000000; - --override-background: #FFFFFF; - --override-selection-background: #add6ff; - --override-watermark-color: rgba(66, 66, 66, 0.75); - --override-tabs-background: #f3f3f3; - --override-progress-background: #0066bf; - --override-badge-background: #c4c4c4; - --override-lineHighlightBorder: #eeeeee; - --override-peek-background: #f2f8fc; - } -`, - dark: ` - :root { - --override-widget-background: #1e1e1e; - --override-foreground: #d4d4d4; - --override-background: #1e1e1e; - --override-selection-background: #264f78; - --override-watermark-color: rgba(231, 231, 231, 0.6); - --override-tabs-background: #252526; - --override-progress-background: #0066bf; - --override-badge-background: #4d4d4d; - --override-lineHighlightBorder: #282828; - --override-peek-background: #001f33; - } -` -}; - -// These colors below should match colors that come from either the Default Light+ theme or the Default Dark+ theme. -// They are used when we can't find a theme json file. -const DefaultColors: { [key: string]: string } = { - 'light.comment': '#008000', - 'light.constant.numeric': '#09885a', - 'light.string': '#a31515', - 'light.keyword.control': '#AF00DB', - 'light.keyword.operator': '#000000', - 'light.variable': '#001080', - 'light.entity.name.type': '#267f99', - 'light.support.function': '#795E26', - 'light.punctuation': '#000000', - 'dark.comment': '#6A9955', - 'dark.constant.numeric': '#b5cea8', - 'dark.string': '#ce9178', - 'dark.keyword.control': '#C586C0', - 'dark.keyword.operator': '#d4d4d4', - 'dark.variable': '#9CDCFE', - 'dark.entity.name.type': '#4EC9B0', - 'dark.support.function': '#DCDCAA', - 'dark.punctuation': '#1e1e1e' -}; - -interface IApplyThemeArgs { - tokenColors?: JSONArray | null; - baseColors?: JSONObject | null; - fontFamily: string; - fontSize: number; - isDark: boolean; - defaultStyle: string | undefined; -} - -// This class generates css using the current theme in order to colorize code. -// -// NOTE: This is all a big hack. It's relying on the theme json files to have a certain format -// in order for this to work. -// See this vscode issue for the real way we think this should happen: -// https://github.com/Microsoft/vscode/issues/32813 -@injectable() -export class CodeCssGenerator implements ICodeCssGenerator { - constructor( - @inject(IWorkspaceService) private workspaceService: IWorkspaceService, - @inject(IThemeFinder) private themeFinder: IThemeFinder, - @inject(IConfigurationService) private configService: IConfigurationService, - @inject(IFileSystem) private fs: IFileSystem - ) {} - - public generateThemeCss(resource: Resource, isDark: boolean, theme: string): Promise { - return this.applyThemeData(resource, isDark, theme, '', this.generateCss.bind(this)); - } - - public generateMonacoTheme(resource: Resource, isDark: boolean, theme: string): Promise { - return this.applyThemeData(resource, isDark, theme, {} as any, this.generateMonacoThemeObject.bind(this)); - } - - private async applyThemeData( - resource: Resource, - isDark: boolean, - theme: string, - defaultT: T, - applier: (args: IApplyThemeArgs) => T - ): Promise { - let result = defaultT; - try { - // First compute our current theme. - const ignoreTheme = this.configService.getSettings(resource).datascience.ignoreVscodeTheme ? true : false; - theme = ignoreTheme ? DefaultTheme : theme; - const editor = this.workspaceService.getConfiguration('editor', undefined); - const fontFamily = editor - ? editor.get('fontFamily', "Consolas, 'Courier New', monospace") - : "Consolas, 'Courier New', monospace"; - const fontSize = editor ? editor.get('fontSize', 14) : 14; - const isDarkUpdated = ignoreTheme ? false : isDark; - - // Then we have to find where the theme resources are loaded from - if (theme) { - traceInfo('Searching for token colors ...'); - const tokenColors = await this.findTokenColors(theme); - const baseColors = await this.findBaseColors(theme); - - // The tokens object then contains the necessary data to generate our css - if (tokenColors && fontFamily && fontSize) { - traceInfo('Using colors to generate CSS ...'); - result = applier({ - tokenColors, - baseColors, - fontFamily, - fontSize, - isDark: isDarkUpdated, - defaultStyle: ignoreTheme ? LightTheme : undefined - }); - } else if (tokenColors === null && fontFamily && fontSize) { - // No colors found. See if we can figure out what type of theme we have - const style = isDark ? DarkTheme : LightTheme; - result = applier({ fontFamily, fontSize, isDark: isDarkUpdated, defaultStyle: style }); - } - } - } catch (err) { - // On error don't fail, just log - traceError(err); - } - - return result; - } - - private getScopes(entry: any): JSONArray { - if (entry && entry.scope) { - return Array.isArray(entry.scope) ? (entry.scope as JSONArray) : entry.scope.toString().split(','); - } - return []; - } - - private matchTokenColor(tokenColors: JSONArray, scope: string): number { - return tokenColors.findIndex((entry: any) => { - const scopeArray = this.getScopes(entry); - if (scopeArray.find(v => v !== null && v !== undefined && v.toString().trim() === scope)) { - return true; - } - return false; - }); - } - - private getScopeStyle = ( - tokenColors: JSONArray | null | undefined, - scope: string, - secondary: string, - defaultStyle: string | undefined - ): { color: string; fontStyle: string } => { - // Search through the scopes on the json object - if (tokenColors) { - let match = this.matchTokenColor(tokenColors, scope); - if (match < 0 && secondary) { - match = this.matchTokenColor(tokenColors, secondary); - } - const found = match >= 0 ? (tokenColors[match] as any) : null; - if (found !== null) { - const settings = found.settings; - if (settings && settings !== null) { - const fontStyle = settings.fontStyle ? settings.fontStyle : 'normal'; - const foreground = settings.foreground ? settings.foreground : 'var(--vscode-editor-foreground)'; - - return { fontStyle, color: foreground }; - } - } - } - - // Default to editor foreground - return { color: this.getDefaultColor(defaultStyle, scope), fontStyle: 'normal' }; - }; - - private getDefaultColor(style: string | undefined, scope: string): string { - return style - ? DefaultColors[`${style}.${scope}`] - : 'var(--override-foreground, var(--vscode-editor-foreground))'; - } - - // tslint:disable-next-line:max-func-body-length - private generateCss(args: IApplyThemeArgs): string { - // There's a set of values that need to be found - const commentStyle = this.getScopeStyle(args.tokenColors, 'comment', 'comment', args.defaultStyle); - const numericStyle = this.getScopeStyle(args.tokenColors, 'constant.numeric', 'constant', args.defaultStyle); - const stringStyle = this.getScopeStyle(args.tokenColors, 'string', 'string', args.defaultStyle); - const variableStyle = this.getScopeStyle(args.tokenColors, 'variable', 'variable', args.defaultStyle); - const entityTypeStyle = this.getScopeStyle( - args.tokenColors, - 'entity.name.type', - 'entity.name.type', - args.defaultStyle - ); - - // Use these values to fill in our format string - return ` -:root { - --code-comment-color: ${commentStyle.color}; - --code-numeric-color: ${numericStyle.color}; - --code-string-color: ${stringStyle.color}; - --code-variable-color: ${variableStyle.color}; - --code-type-color: ${entityTypeStyle.color}; - --code-font-family: ${args.fontFamily}; - --code-font-size: ${args.fontSize}px; -} - -${args.defaultStyle ? DefaultCssVars[args.defaultStyle] : ''} -`; - } - - // Based on this data here: - // https://github.com/Microsoft/vscode/blob/master/src/vs/editor/standalone/common/themes.ts#L13 - // tslint:disable: max-func-body-length - private generateMonacoThemeObject(args: IApplyThemeArgs): monacoEditor.editor.IStandaloneThemeData { - const result: monacoEditor.editor.IStandaloneThemeData = { - base: args.isDark ? 'vs-dark' : 'vs', - inherit: false, - rules: [], - colors: {} - }; - // If we have token colors enumerate them and add them into the rules - if (args.tokenColors && args.tokenColors.length) { - const tokenSet = new Set(); - args.tokenColors.forEach((t: any) => { - const scopes = this.getScopes(t); - const settings = t && t.settings ? t.settings : undefined; - if (scopes && settings) { - scopes.forEach(s => { - const token = s ? s.toString() : ''; - if (!tokenSet.has(token)) { - tokenSet.add(token); - - if (settings.foreground) { - // Make sure matches the monaco requirements of having 6 values - if (!MonacoColorRegEx.test(settings.foreground)) { - const match = ThreeColorRegEx.exec(settings.foreground); - if (match && match.length > 3) { - settings.foreground = `#${match[1]}${match[1]}${match[2]}${match[2]}${match[3]}${match[3]}`; - } else { - settings.foreground = undefined; - } - } - } - - if (settings.foreground) { - result.rules.push({ - token, - foreground: settings.foreground, - background: settings.background, - fontStyle: settings.fontStyle - }); - } else { - result.rules.push({ - token, - background: settings.background, - fontStyle: settings.fontStyle - }); - } - - // Special case some items. punctuation.definition.comment doesn't seem to - // be listed anywhere. Add it manually when we find a 'comment' - // tslint:disable-next-line: possible-timing-attack - if (token === 'comment') { - result.rules.push({ - token: 'punctuation.definition.comment', - foreground: settings.foreground, - background: settings.background, - fontStyle: settings.fontStyle - }); - } - - // Same for string - // tslint:disable-next-line: possible-timing-attack - if (token === 'string') { - result.rules.push({ - token: 'punctuation.definition.string', - foreground: settings.foreground, - background: settings.background, - fontStyle: settings.fontStyle - }); - } - } - }); - } - }); - - result.rules = result.rules.sort( - (a: monacoEditor.editor.ITokenThemeRule, b: monacoEditor.editor.ITokenThemeRule) => { - return a.token.localeCompare(b.token); - } - ); - } else { - // Otherwise use our default values. - result.base = args.defaultStyle === DarkTheme ? 'vs-dark' : 'vs'; - result.inherit = true; - - if (args.defaultStyle) { - // Special case. We need rules for the comment beginning and the string beginning - result.rules.push({ - token: 'punctuation.definition.comment', - foreground: DefaultColors[`${args.defaultStyle}.comment`] - }); - result.rules.push({ - token: 'punctuation.definition.string', - foreground: DefaultColors[`${args.defaultStyle}.string`] - }); - } - } - // If we have base colors enumerate them and add them to the colors - if (args.baseColors) { - const keys = Object.keys(args.baseColors); - keys.forEach(k => { - const color = args.baseColors && args.baseColors[k] ? args.baseColors[k] : '#000000'; - result.colors[k] = color ? color.toString() : '#000000'; - }); - } // The else case here should end up inheriting. - return result; - } - - private mergeColors = (colors1: JSONArray, colors2: JSONArray): JSONArray => { - return [...colors1, ...colors2]; - }; - - private mergeBaseColors = (colors1: JSONObject, colors2: JSONObject): JSONObject => { - return { ...colors1, ...colors2 }; - }; - - private readTokenColors = async (themeFile: string): Promise => { - try { - const tokenContent = await this.fs.readFile(themeFile); - const theme = parse(tokenContent); - let tokenColors: JSONArray = []; - - if (typeof theme.tokenColors === 'string') { - const style = await this.fs.readData(theme.tokenColors); - tokenColors = JSON.parse(style.toString()); - } else { - tokenColors = theme.tokenColors as JSONArray; - } - - if (tokenColors && tokenColors.length > 0) { - // This theme may include others. If so we need to combine the two together - const include = theme ? theme.include : undefined; - if (include) { - const includePath = path.join(path.dirname(themeFile), include.toString()); - const includedColors = await this.readTokenColors(includePath); - return this.mergeColors(tokenColors, includedColors); - } - - // Theme is a root, don't need to include others - return tokenColors; - } - - // Might also have a 'settings' object that equates to token colors - const settings = theme.settings as JSONArray; - if (settings && settings.length > 0) { - return settings; - } - - return []; - } catch (e) { - traceError('Python Extension: Error reading custom theme', e); - return []; - } - }; - - private readBaseColors = async (themeFile: string): Promise => { - const tokenContent = await this.fs.readFile(themeFile); - const theme = parse(tokenContent); - const colors = theme.colors as JSONObject; - - // This theme may include others. If so we need to combine the two together - const include = theme ? theme.include : undefined; - if (include) { - const includePath = path.join(path.dirname(themeFile), include.toString()); - const includedColors = await this.readBaseColors(includePath); - return this.mergeBaseColors(colors, includedColors); - } - - // Theme is a root, don't need to include others - return colors; - }; - - private findTokenColors = async (theme: string): Promise => { - try { - traceInfo('Attempting search for colors ...'); - const themeRoot = await this.themeFinder.findThemeRootJson(theme); - - // Use the first result if we have one - if (themeRoot) { - traceInfo(`Loading colors from ${themeRoot} ...`); - - // This should be the path to the file. Load it as a json object - const contents = await this.fs.readFile(themeRoot); - const json = parse(contents); - - // There should be a theme colors section - const contributes = json.contributes as JSONObject; - - // If no contributes section, see if we have a tokenColors section. This means - // this is a direct token colors file - if (!contributes) { - const tokenColors = json.tokenColors as JSONObject; - if (tokenColors) { - return await this.readTokenColors(themeRoot); - } - } - - // This should have a themes section - const themes = contributes.themes as JSONArray; - - // One of these (it's an array), should have our matching theme entry - const index = themes.findIndex((e: any) => { - return e !== null && (e.id === theme || e.name === theme); - }); - - const found = index >= 0 ? (themes[index] as any) : null; - if (found !== null) { - // Then the path entry should contain a relative path to the json file with - // the tokens in it - const themeFile = path.join(path.dirname(themeRoot), found.path); - traceInfo(`Reading colors from ${themeFile}`); - return await this.readTokenColors(themeFile); - } - } else { - traceWarning(`Color theme ${theme} not found. Using default colors.`); - } - } catch (err) { - // Swallow any exceptions with searching or parsing - traceError(err); - } - - // Force the colors to the defaults - return null; - }; - - private findBaseColors = async (theme: string): Promise => { - try { - traceInfo('Attempting search for colors ...'); - const themeRoot = await this.themeFinder.findThemeRootJson(theme); - - // Use the first result if we have one - if (themeRoot) { - traceInfo(`Loading base colors from ${themeRoot} ...`); - - // This should be the path to the file. Load it as a json object - const contents = await this.fs.readFile(themeRoot); - const json = parse(contents); - - // There should be a theme colors section - const contributes = json.contributes as JSONObject; - - // If no contributes section, see if we have a tokenColors section. This means - // this is a direct token colors file - if (!contributes) { - return await this.readBaseColors(themeRoot); - } - - // This should have a themes section - const themes = contributes.themes as JSONArray; - - // One of these (it's an array), should have our matching theme entry - const index = themes.findIndex((e: any) => { - return e !== null && (e.id === theme || e.name === theme); - }); - - const found = index >= 0 ? (themes[index] as any) : null; - if (found !== null) { - // Then the path entry should contain a relative path to the json file with - // the tokens in it - const themeFile = path.join(path.dirname(themeRoot), found.path); - traceInfo(`Reading base colors from ${themeFile}`); - return await this.readBaseColors(themeFile); - } - } else { - traceWarning(`Color theme ${theme} not found. Using default colors.`); - } - } catch (err) { - // Swallow any exceptions with searching or parsing - traceError(err); - } - - // Force the colors to the defaults - return null; - }; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { JSONArray, JSONObject } from '@phosphor/coreutils'; +import { inject, injectable } from 'inversify'; +import { parse } from 'jsonc-parser'; +import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; +import * as path from 'path'; + +import { IWorkspaceService } from '../common/application/types'; +import { traceError, traceInfo, traceWarning } from '../common/logger'; +import { IFileSystem } from '../common/platform/types'; +import { IConfigurationService, Resource } from '../common/types'; +import { DefaultTheme } from './constants'; +import { ICodeCssGenerator, IThemeFinder } from './types'; + +// tslint:disable:no-any +const DarkTheme = 'dark'; +const LightTheme = 'light'; + +const MonacoColorRegEx = /^#?([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/; +const ThreeColorRegEx = /^#?([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f])$/; + +// These are based on the colors generated by 'Default Light+' and are only set when we +// are ignoring themes. +//tslint:disable:no-multiline-string object-literal-key-quotes +const DefaultCssVars: { [key: string]: string } = { + light: ` + :root { + --override-widget-background: #f3f3f3; + --override-foreground: #000000; + --override-background: #FFFFFF; + --override-selection-background: #add6ff; + --override-watermark-color: rgba(66, 66, 66, 0.75); + --override-tabs-background: #f3f3f3; + --override-progress-background: #0066bf; + --override-badge-background: #c4c4c4; + --override-lineHighlightBorder: #eeeeee; + --override-peek-background: #f2f8fc; + } +`, + dark: ` + :root { + --override-widget-background: #1e1e1e; + --override-foreground: #d4d4d4; + --override-background: #1e1e1e; + --override-selection-background: #264f78; + --override-watermark-color: rgba(231, 231, 231, 0.6); + --override-tabs-background: #252526; + --override-progress-background: #0066bf; + --override-badge-background: #4d4d4d; + --override-lineHighlightBorder: #282828; + --override-peek-background: #001f33; + } +` +}; + +// These colors below should match colors that come from either the Default Light+ theme or the Default Dark+ theme. +// They are used when we can't find a theme json file. +const DefaultColors: { [key: string]: string } = { + 'light.comment': '#008000', + 'light.constant.numeric': '#09885a', + 'light.string': '#a31515', + 'light.keyword.control': '#AF00DB', + 'light.keyword.operator': '#000000', + 'light.variable': '#001080', + 'light.entity.name.type': '#267f99', + 'light.support.function': '#795E26', + 'light.punctuation': '#000000', + 'dark.comment': '#6A9955', + 'dark.constant.numeric': '#b5cea8', + 'dark.string': '#ce9178', + 'dark.keyword.control': '#C586C0', + 'dark.keyword.operator': '#d4d4d4', + 'dark.variable': '#9CDCFE', + 'dark.entity.name.type': '#4EC9B0', + 'dark.support.function': '#DCDCAA', + 'dark.punctuation': '#1e1e1e' +}; + +interface IApplyThemeArgs { + tokenColors?: JSONArray | null; + baseColors?: JSONObject | null; + fontFamily: string; + fontSize: number; + isDark: boolean; + defaultStyle: string | undefined; +} + +// This class generates css using the current theme in order to colorize code. +// +// NOTE: This is all a big hack. It's relying on the theme json files to have a certain format +// in order for this to work. +// See this vscode issue for the real way we think this should happen: +// https://github.com/Microsoft/vscode/issues/32813 +@injectable() +export class CodeCssGenerator implements ICodeCssGenerator { + constructor( + @inject(IWorkspaceService) private workspaceService: IWorkspaceService, + @inject(IThemeFinder) private themeFinder: IThemeFinder, + @inject(IConfigurationService) private configService: IConfigurationService, + @inject(IFileSystem) private fs: IFileSystem + ) {} + + public generateThemeCss(resource: Resource, isDark: boolean, theme: string): Promise { + return this.applyThemeData(resource, isDark, theme, '', this.generateCss.bind(this)); + } + + public generateMonacoTheme(resource: Resource, isDark: boolean, theme: string): Promise { + return this.applyThemeData(resource, isDark, theme, {} as any, this.generateMonacoThemeObject.bind(this)); + } + + private async applyThemeData( + resource: Resource, + isDark: boolean, + theme: string, + defaultT: T, + applier: (args: IApplyThemeArgs) => T + ): Promise { + let result = defaultT; + try { + // First compute our current theme. + const ignoreTheme = this.configService.getSettings(resource).datascience.ignoreVscodeTheme ? true : false; + theme = ignoreTheme ? DefaultTheme : theme; + const editor = this.workspaceService.getConfiguration('editor', undefined); + const fontFamily = editor + ? editor.get('fontFamily', "Consolas, 'Courier New', monospace") + : "Consolas, 'Courier New', monospace"; + const fontSize = editor ? editor.get('fontSize', 14) : 14; + const isDarkUpdated = ignoreTheme ? false : isDark; + + // Then we have to find where the theme resources are loaded from + if (theme) { + traceInfo('Searching for token colors ...'); + const tokenColors = await this.findTokenColors(theme); + const baseColors = await this.findBaseColors(theme); + + // The tokens object then contains the necessary data to generate our css + if (tokenColors && fontFamily && fontSize) { + traceInfo('Using colors to generate CSS ...'); + result = applier({ + tokenColors, + baseColors, + fontFamily, + fontSize, + isDark: isDarkUpdated, + defaultStyle: ignoreTheme ? LightTheme : undefined + }); + } else if (tokenColors === null && fontFamily && fontSize) { + // No colors found. See if we can figure out what type of theme we have + const style = isDark ? DarkTheme : LightTheme; + result = applier({ fontFamily, fontSize, isDark: isDarkUpdated, defaultStyle: style }); + } + } + } catch (err) { + // On error don't fail, just log + traceError(err); + } + + return result; + } + + private getScopes(entry: any): JSONArray { + if (entry && entry.scope) { + return Array.isArray(entry.scope) ? (entry.scope as JSONArray) : entry.scope.toString().split(','); + } + return []; + } + + private matchTokenColor(tokenColors: JSONArray, scope: string): number { + return tokenColors.findIndex((entry: any) => { + const scopeArray = this.getScopes(entry); + if (scopeArray.find((v) => v !== null && v !== undefined && v.toString().trim() === scope)) { + return true; + } + return false; + }); + } + + private getScopeStyle = ( + tokenColors: JSONArray | null | undefined, + scope: string, + secondary: string, + defaultStyle: string | undefined + ): { color: string; fontStyle: string } => { + // Search through the scopes on the json object + if (tokenColors) { + let match = this.matchTokenColor(tokenColors, scope); + if (match < 0 && secondary) { + match = this.matchTokenColor(tokenColors, secondary); + } + const found = match >= 0 ? (tokenColors[match] as any) : null; + if (found !== null) { + const settings = found.settings; + if (settings && settings !== null) { + const fontStyle = settings.fontStyle ? settings.fontStyle : 'normal'; + const foreground = settings.foreground ? settings.foreground : 'var(--vscode-editor-foreground)'; + + return { fontStyle, color: foreground }; + } + } + } + + // Default to editor foreground + return { color: this.getDefaultColor(defaultStyle, scope), fontStyle: 'normal' }; + }; + + private getDefaultColor(style: string | undefined, scope: string): string { + return style + ? DefaultColors[`${style}.${scope}`] + : 'var(--override-foreground, var(--vscode-editor-foreground))'; + } + + // tslint:disable-next-line:max-func-body-length + private generateCss(args: IApplyThemeArgs): string { + // There's a set of values that need to be found + const commentStyle = this.getScopeStyle(args.tokenColors, 'comment', 'comment', args.defaultStyle); + const numericStyle = this.getScopeStyle(args.tokenColors, 'constant.numeric', 'constant', args.defaultStyle); + const stringStyle = this.getScopeStyle(args.tokenColors, 'string', 'string', args.defaultStyle); + const variableStyle = this.getScopeStyle(args.tokenColors, 'variable', 'variable', args.defaultStyle); + const entityTypeStyle = this.getScopeStyle( + args.tokenColors, + 'entity.name.type', + 'entity.name.type', + args.defaultStyle + ); + + // Use these values to fill in our format string + return ` +:root { + --code-comment-color: ${commentStyle.color}; + --code-numeric-color: ${numericStyle.color}; + --code-string-color: ${stringStyle.color}; + --code-variable-color: ${variableStyle.color}; + --code-type-color: ${entityTypeStyle.color}; + --code-font-family: ${args.fontFamily}; + --code-font-size: ${args.fontSize}px; +} + +${args.defaultStyle ? DefaultCssVars[args.defaultStyle] : ''} +`; + } + + // Based on this data here: + // https://github.com/Microsoft/vscode/blob/master/src/vs/editor/standalone/common/themes.ts#L13 + // tslint:disable: max-func-body-length + private generateMonacoThemeObject(args: IApplyThemeArgs): monacoEditor.editor.IStandaloneThemeData { + const result: monacoEditor.editor.IStandaloneThemeData = { + base: args.isDark ? 'vs-dark' : 'vs', + inherit: false, + rules: [], + colors: {} + }; + // If we have token colors enumerate them and add them into the rules + if (args.tokenColors && args.tokenColors.length) { + const tokenSet = new Set(); + args.tokenColors.forEach((t: any) => { + const scopes = this.getScopes(t); + const settings = t && t.settings ? t.settings : undefined; + if (scopes && settings) { + scopes.forEach((s) => { + const token = s ? s.toString() : ''; + if (!tokenSet.has(token)) { + tokenSet.add(token); + + if (settings.foreground) { + // Make sure matches the monaco requirements of having 6 values + if (!MonacoColorRegEx.test(settings.foreground)) { + const match = ThreeColorRegEx.exec(settings.foreground); + if (match && match.length > 3) { + settings.foreground = `#${match[1]}${match[1]}${match[2]}${match[2]}${match[3]}${match[3]}`; + } else { + settings.foreground = undefined; + } + } + } + + if (settings.foreground) { + result.rules.push({ + token, + foreground: settings.foreground, + background: settings.background, + fontStyle: settings.fontStyle + }); + } else { + result.rules.push({ + token, + background: settings.background, + fontStyle: settings.fontStyle + }); + } + + // Special case some items. punctuation.definition.comment doesn't seem to + // be listed anywhere. Add it manually when we find a 'comment' + // tslint:disable-next-line: possible-timing-attack + if (token === 'comment') { + result.rules.push({ + token: 'punctuation.definition.comment', + foreground: settings.foreground, + background: settings.background, + fontStyle: settings.fontStyle + }); + } + + // Same for string + // tslint:disable-next-line: possible-timing-attack + if (token === 'string') { + result.rules.push({ + token: 'punctuation.definition.string', + foreground: settings.foreground, + background: settings.background, + fontStyle: settings.fontStyle + }); + } + } + }); + } + }); + + result.rules = result.rules.sort( + (a: monacoEditor.editor.ITokenThemeRule, b: monacoEditor.editor.ITokenThemeRule) => { + return a.token.localeCompare(b.token); + } + ); + } else { + // Otherwise use our default values. + result.base = args.defaultStyle === DarkTheme ? 'vs-dark' : 'vs'; + result.inherit = true; + + if (args.defaultStyle) { + // Special case. We need rules for the comment beginning and the string beginning + result.rules.push({ + token: 'punctuation.definition.comment', + foreground: DefaultColors[`${args.defaultStyle}.comment`] + }); + result.rules.push({ + token: 'punctuation.definition.string', + foreground: DefaultColors[`${args.defaultStyle}.string`] + }); + } + } + // If we have base colors enumerate them and add them to the colors + if (args.baseColors) { + const keys = Object.keys(args.baseColors); + keys.forEach((k) => { + const color = args.baseColors && args.baseColors[k] ? args.baseColors[k] : '#000000'; + result.colors[k] = color ? color.toString() : '#000000'; + }); + } // The else case here should end up inheriting. + return result; + } + + private mergeColors = (colors1: JSONArray, colors2: JSONArray): JSONArray => { + return [...colors1, ...colors2]; + }; + + private mergeBaseColors = (colors1: JSONObject, colors2: JSONObject): JSONObject => { + return { ...colors1, ...colors2 }; + }; + + private readTokenColors = async (themeFile: string): Promise => { + try { + const tokenContent = await this.fs.readFile(themeFile); + const theme = parse(tokenContent); + let tokenColors: JSONArray = []; + + if (typeof theme.tokenColors === 'string') { + const style = await this.fs.readData(theme.tokenColors); + tokenColors = JSON.parse(style.toString()); + } else { + tokenColors = theme.tokenColors as JSONArray; + } + + if (tokenColors && tokenColors.length > 0) { + // This theme may include others. If so we need to combine the two together + const include = theme ? theme.include : undefined; + if (include) { + const includePath = path.join(path.dirname(themeFile), include.toString()); + const includedColors = await this.readTokenColors(includePath); + return this.mergeColors(tokenColors, includedColors); + } + + // Theme is a root, don't need to include others + return tokenColors; + } + + // Might also have a 'settings' object that equates to token colors + const settings = theme.settings as JSONArray; + if (settings && settings.length > 0) { + return settings; + } + + return []; + } catch (e) { + traceError('Python Extension: Error reading custom theme', e); + return []; + } + }; + + private readBaseColors = async (themeFile: string): Promise => { + const tokenContent = await this.fs.readFile(themeFile); + const theme = parse(tokenContent); + const colors = theme.colors as JSONObject; + + // This theme may include others. If so we need to combine the two together + const include = theme ? theme.include : undefined; + if (include) { + const includePath = path.join(path.dirname(themeFile), include.toString()); + const includedColors = await this.readBaseColors(includePath); + return this.mergeBaseColors(colors, includedColors); + } + + // Theme is a root, don't need to include others + return colors; + }; + + private findTokenColors = async (theme: string): Promise => { + try { + traceInfo('Attempting search for colors ...'); + const themeRoot = await this.themeFinder.findThemeRootJson(theme); + + // Use the first result if we have one + if (themeRoot) { + traceInfo(`Loading colors from ${themeRoot} ...`); + + // This should be the path to the file. Load it as a json object + const contents = await this.fs.readFile(themeRoot); + const json = parse(contents); + + // There should be a theme colors section + const contributes = json.contributes as JSONObject; + + // If no contributes section, see if we have a tokenColors section. This means + // this is a direct token colors file + if (!contributes) { + const tokenColors = json.tokenColors as JSONObject; + if (tokenColors) { + return await this.readTokenColors(themeRoot); + } + } + + // This should have a themes section + const themes = contributes.themes as JSONArray; + + // One of these (it's an array), should have our matching theme entry + const index = themes.findIndex((e: any) => { + return e !== null && (e.id === theme || e.name === theme); + }); + + const found = index >= 0 ? (themes[index] as any) : null; + if (found !== null) { + // Then the path entry should contain a relative path to the json file with + // the tokens in it + const themeFile = path.join(path.dirname(themeRoot), found.path); + traceInfo(`Reading colors from ${themeFile}`); + return await this.readTokenColors(themeFile); + } + } else { + traceWarning(`Color theme ${theme} not found. Using default colors.`); + } + } catch (err) { + // Swallow any exceptions with searching or parsing + traceError(err); + } + + // Force the colors to the defaults + return null; + }; + + private findBaseColors = async (theme: string): Promise => { + try { + traceInfo('Attempting search for colors ...'); + const themeRoot = await this.themeFinder.findThemeRootJson(theme); + + // Use the first result if we have one + if (themeRoot) { + traceInfo(`Loading base colors from ${themeRoot} ...`); + + // This should be the path to the file. Load it as a json object + const contents = await this.fs.readFile(themeRoot); + const json = parse(contents); + + // There should be a theme colors section + const contributes = json.contributes as JSONObject; + + // If no contributes section, see if we have a tokenColors section. This means + // this is a direct token colors file + if (!contributes) { + return await this.readBaseColors(themeRoot); + } + + // This should have a themes section + const themes = contributes.themes as JSONArray; + + // One of these (it's an array), should have our matching theme entry + const index = themes.findIndex((e: any) => { + return e !== null && (e.id === theme || e.name === theme); + }); + + const found = index >= 0 ? (themes[index] as any) : null; + if (found !== null) { + // Then the path entry should contain a relative path to the json file with + // the tokens in it + const themeFile = path.join(path.dirname(themeRoot), found.path); + traceInfo(`Reading base colors from ${themeFile}`); + return await this.readBaseColors(themeFile); + } + } else { + traceWarning(`Color theme ${theme} not found. Using default colors.`); + } + } catch (err) { + // Swallow any exceptions with searching or parsing + traceError(err); + } + + // Force the colors to the defaults + return null; + }; +} diff --git a/src/client/datascience/commands/commandLineSelector.ts b/src/client/datascience/commands/commandLineSelector.ts index 4ecc0f0170cd..0ea635d5c83d 100644 --- a/src/client/datascience/commands/commandLineSelector.ts +++ b/src/client/datascience/commands/commandLineSelector.ts @@ -26,6 +26,6 @@ export class JupyterCommandLineSelectorCommand implements IDisposable { ); } public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } } diff --git a/src/client/datascience/commands/commandRegistry.ts b/src/client/datascience/commands/commandRegistry.ts index 280a8d731d1c..a18a49b4e2b7 100644 --- a/src/client/datascience/commands/commandRegistry.ts +++ b/src/client/datascience/commands/commandRegistry.ts @@ -76,7 +76,7 @@ export class CommandRegistry implements IDisposable { } } public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } private registerCommand< E extends keyof ICommandNameArgumentTypeMapping, @@ -88,7 +88,7 @@ export class CommandRegistry implements IDisposable { } private getCodeWatcher(file: string): ICodeWatcher | undefined { - const possibleDocuments = this.documentManager.textDocuments.filter(d => d.fileName === file); + const possibleDocuments = this.documentManager.textDocuments.filter((d) => d.fileName === file); if (possibleDocuments && possibleDocuments.length === 1) { return this.dataScienceCodeLensProvider.getCodeWatcher(possibleDocuments[0]); } else if (possibleDocuments && possibleDocuments.length > 1) { diff --git a/src/client/datascience/commands/kernelSwitcher.ts b/src/client/datascience/commands/kernelSwitcher.ts index c4beb39a3331..0ee96e3c668e 100644 --- a/src/client/datascience/commands/kernelSwitcher.ts +++ b/src/client/datascience/commands/kernelSwitcher.ts @@ -27,7 +27,7 @@ export class KernelSwitcherCommand implements IDisposable { ); } public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } private async switchKernel(notebook?: INotebook): Promise { // If notebook isn't know, then user invoked this command from command palette or similar. diff --git a/src/client/datascience/commands/serverSelector.ts b/src/client/datascience/commands/serverSelector.ts index ca0a305d4fa1..ea2956f0d331 100644 --- a/src/client/datascience/commands/serverSelector.ts +++ b/src/client/datascience/commands/serverSelector.ts @@ -26,6 +26,6 @@ export class JupyterServerSelectorCommand implements IDisposable { ); } public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } } diff --git a/src/client/datascience/common.ts b/src/client/datascience/common.ts index 0a9372a7a9e1..e9aa53212c7a 100644 --- a/src/client/datascience/common.ts +++ b/src/client/datascience/common.ts @@ -1,25 +1,25 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { Memento } from 'vscode'; -import { noop } from '../common/utils/misc'; -import { Settings } from './constants'; - -export function getSavedUriList(globalState: Memento): { uri: string; time: number }[] { - const uriList = globalState.get<{ uri: string; time: number }[]>(Settings.JupyterServerUriList); - return uriList - ? uriList.sort((a, b) => { - return b.time - a.time; - }) - : []; -} -export function addToUriList(globalState: Memento, uri: string, time: number) { - const uriList = getSavedUriList(globalState); - - const editList = uriList.filter((f, i) => { - return f.uri !== uri && i < Settings.JupyterServerUriListMax - 1; - }); - editList.splice(0, 0, { uri, time }); - - globalState.update(Settings.JupyterServerUriList, editList).then(noop, noop); -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { Memento } from 'vscode'; +import { noop } from '../common/utils/misc'; +import { Settings } from './constants'; + +export function getSavedUriList(globalState: Memento): { uri: string; time: number }[] { + const uriList = globalState.get<{ uri: string; time: number }[]>(Settings.JupyterServerUriList); + return uriList + ? uriList.sort((a, b) => { + return b.time - a.time; + }) + : []; +} +export function addToUriList(globalState: Memento, uri: string, time: number) { + const uriList = getSavedUriList(globalState); + + const editList = uriList.filter((f, i) => { + return f.uri !== uri && i < Settings.JupyterServerUriListMax - 1; + }); + editList.splice(0, 0, { uri, time }); + + globalState.update(Settings.JupyterServerUriList, editList).then(noop, noop); +} diff --git a/src/client/datascience/context/activeEditorContext.ts b/src/client/datascience/context/activeEditorContext.ts index e0bc38fd0801..57b79eb8b79b 100644 --- a/src/client/datascience/context/activeEditorContext.ts +++ b/src/client/datascience/context/activeEditorContext.ts @@ -48,7 +48,7 @@ export class ActiveEditorContextService implements IExtensionSingleActivationSer ); } public dispose() { - this.disposables.forEach(item => item.dispose()); + this.disposables.forEach((item) => item.dispose()); } public async activate(): Promise { this.docManager.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, this.disposables); diff --git a/src/client/datascience/data-viewing/dataViewer.ts b/src/client/datascience/data-viewing/dataViewer.ts index 4efb066f031b..77350b95d575 100644 --- a/src/client/datascience/data-viewing/dataViewer.ts +++ b/src/client/datascience/data-viewing/dataViewer.ts @@ -1,189 +1,189 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../common/extensions'; - -import { inject, injectable } from 'inversify'; -import * as path from 'path'; -import { ViewColumn } from 'vscode'; - -import { IApplicationShell, IWebPanelProvider, IWorkspaceService } from '../../common/application/types'; -import { EXTENSION_ROOT_DIR } from '../../common/constants'; -import { WebHostNotebook } from '../../common/experimentGroups'; -import { traceError } from '../../common/logger'; -import { IConfigurationService, IDisposable, IExperimentsManager, Resource } from '../../common/types'; -import * as localize from '../../common/utils/localize'; -import { noop } from '../../common/utils/misc'; -import { StopWatch } from '../../common/utils/stopWatch'; -import { sendTelemetryEvent } from '../../telemetry'; -import { HelpLinks, Telemetry } from '../constants'; -import { JupyterDataRateLimitError } from '../jupyter/jupyterDataRateLimitError'; -import { ICodeCssGenerator, IDataViewer, IJupyterVariable, IJupyterVariables, INotebook, IThemeFinder } from '../types'; -import { WebViewHost } from '../webViewHost'; -import { DataViewerMessageListener } from './dataViewerMessageListener'; -import { DataViewerMessages, IDataViewerMapping, IGetRowsRequest } from './types'; - -const dataExplorereDir = path.join(EXTENSION_ROOT_DIR, 'out', 'datascience-ui', 'viewers'); -@injectable() -export class DataViewer extends WebViewHost implements IDataViewer, IDisposable { - private notebook: INotebook | undefined; - private variable: IJupyterVariable | undefined; - private rowsTimer: StopWatch | undefined; - private pendingRowsCount: number = 0; - - constructor( - @inject(IWebPanelProvider) provider: IWebPanelProvider, - @inject(IConfigurationService) configuration: IConfigurationService, - @inject(ICodeCssGenerator) cssGenerator: ICodeCssGenerator, - @inject(IThemeFinder) themeFinder: IThemeFinder, - @inject(IWorkspaceService) workspaceService: IWorkspaceService, - @inject(IJupyterVariables) private variableManager: IJupyterVariables, - @inject(IApplicationShell) private applicationShell: IApplicationShell, - @inject(IExperimentsManager) experimentsManager: IExperimentsManager - ) { - super( - configuration, - provider, - cssGenerator, - themeFinder, - workspaceService, - (c, v, d) => new DataViewerMessageListener(c, v, d), - dataExplorereDir, - [path.join(dataExplorereDir, 'commons.initial.bundle.js'), path.join(dataExplorereDir, 'dataExplorer.js')], - localize.DataScience.dataExplorerTitle(), - ViewColumn.One, - experimentsManager.inExperiment(WebHostNotebook.experiment) - ); - - // Load the web panel using our current directory as we don't expect to load any other files - super.loadWebPanel(process.cwd()).catch(traceError); - } - - public async showVariable(variable: IJupyterVariable, notebook: INotebook): Promise { - if (!this.isDisposed) { - // Save notebook this is tied to - this.notebook = notebook; - - // Fill in our variable's beginning data - this.variable = await this.prepVariable(variable, notebook); - - // Create our new title with the variable name - let newTitle = `${localize.DataScience.dataExplorerTitle()} - ${variable.name}`; - const TRIM_LENGTH = 40; - if (newTitle.length > TRIM_LENGTH) { - newTitle = `${newTitle.substr(0, TRIM_LENGTH)}...`; - } - - super.setTitle(newTitle); - - // Then show our web panel. Eventually we need to consume the data - await super.show(true); - - // Send a message with our data - this.postMessage(DataViewerMessages.InitializeData, this.variable).ignoreErrors(); - } - } - - protected getOwningResource(): Promise { - return Promise.resolve(undefined); - } - - //tslint:disable-next-line:no-any - protected onMessage(message: string, payload: any) { - switch (message) { - case DataViewerMessages.GetAllRowsRequest: - this.getAllRows().ignoreErrors(); - break; - - case DataViewerMessages.GetRowsRequest: - this.getRowChunk(payload as IGetRowsRequest).ignoreErrors(); - break; - - default: - break; - } - - super.onMessage(message, payload); - } - - private async prepVariable(variable: IJupyterVariable, notebook: INotebook): Promise { - this.rowsTimer = new StopWatch(); - const output = await this.variableManager.getDataFrameInfo(variable, notebook); - - // Log telemetry about number of rows - try { - sendTelemetryEvent(Telemetry.ShowDataViewer, 0, { - rows: output.rowCount ? output.rowCount : 0, - columns: output.columns ? output.columns.length : 0 - }); - - // Count number of rows to fetch so can send telemetry on how long it took. - this.pendingRowsCount = output.rowCount ? output.rowCount : 0; - } catch { - noop(); - } - - return output; - } - - private async getAllRows() { - return this.wrapRequest(async () => { - if (this.variable && this.variable.rowCount && this.notebook) { - const allRows = await this.variableManager.getDataFrameRows( - this.variable, - this.notebook, - 0, - this.variable.rowCount - ); - this.pendingRowsCount = 0; - return this.postMessage(DataViewerMessages.GetAllRowsResponse, allRows); - } - }); - } - - private getRowChunk(request: IGetRowsRequest) { - return this.wrapRequest(async () => { - if (this.variable && this.variable.rowCount && this.notebook) { - const rows = await this.variableManager.getDataFrameRows( - this.variable, - this.notebook, - request.start, - Math.min(request.end, this.variable.rowCount) - ); - return this.postMessage(DataViewerMessages.GetRowsResponse, { - rows, - start: request.start, - end: request.end - }); - } - }); - } - - private async wrapRequest(func: () => Promise) { - try { - return await func(); - } catch (e) { - if (e instanceof JupyterDataRateLimitError) { - traceError(e); - const actionTitle = localize.DataScience.pythonInteractiveHelpLink(); - this.applicationShell.showErrorMessage(e.toString(), actionTitle).then(v => { - // User clicked on the link, open it. - if (v === actionTitle) { - this.applicationShell.openUrl(HelpLinks.JupyterDataRateHelpLink); - } - }); - this.dispose(); - } - traceError(e); - this.applicationShell.showErrorMessage(e); - } finally { - this.sendElapsedTimeTelemetry(); - } - } - - private sendElapsedTimeTelemetry() { - if (this.rowsTimer && this.pendingRowsCount === 0) { - sendTelemetryEvent(Telemetry.ShowDataViewer, this.rowsTimer.elapsedTime); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../common/extensions'; + +import { inject, injectable } from 'inversify'; +import * as path from 'path'; +import { ViewColumn } from 'vscode'; + +import { IApplicationShell, IWebPanelProvider, IWorkspaceService } from '../../common/application/types'; +import { EXTENSION_ROOT_DIR } from '../../common/constants'; +import { WebHostNotebook } from '../../common/experimentGroups'; +import { traceError } from '../../common/logger'; +import { IConfigurationService, IDisposable, IExperimentsManager, Resource } from '../../common/types'; +import * as localize from '../../common/utils/localize'; +import { noop } from '../../common/utils/misc'; +import { StopWatch } from '../../common/utils/stopWatch'; +import { sendTelemetryEvent } from '../../telemetry'; +import { HelpLinks, Telemetry } from '../constants'; +import { JupyterDataRateLimitError } from '../jupyter/jupyterDataRateLimitError'; +import { ICodeCssGenerator, IDataViewer, IJupyterVariable, IJupyterVariables, INotebook, IThemeFinder } from '../types'; +import { WebViewHost } from '../webViewHost'; +import { DataViewerMessageListener } from './dataViewerMessageListener'; +import { DataViewerMessages, IDataViewerMapping, IGetRowsRequest } from './types'; + +const dataExplorereDir = path.join(EXTENSION_ROOT_DIR, 'out', 'datascience-ui', 'viewers'); +@injectable() +export class DataViewer extends WebViewHost implements IDataViewer, IDisposable { + private notebook: INotebook | undefined; + private variable: IJupyterVariable | undefined; + private rowsTimer: StopWatch | undefined; + private pendingRowsCount: number = 0; + + constructor( + @inject(IWebPanelProvider) provider: IWebPanelProvider, + @inject(IConfigurationService) configuration: IConfigurationService, + @inject(ICodeCssGenerator) cssGenerator: ICodeCssGenerator, + @inject(IThemeFinder) themeFinder: IThemeFinder, + @inject(IWorkspaceService) workspaceService: IWorkspaceService, + @inject(IJupyterVariables) private variableManager: IJupyterVariables, + @inject(IApplicationShell) private applicationShell: IApplicationShell, + @inject(IExperimentsManager) experimentsManager: IExperimentsManager + ) { + super( + configuration, + provider, + cssGenerator, + themeFinder, + workspaceService, + (c, v, d) => new DataViewerMessageListener(c, v, d), + dataExplorereDir, + [path.join(dataExplorereDir, 'commons.initial.bundle.js'), path.join(dataExplorereDir, 'dataExplorer.js')], + localize.DataScience.dataExplorerTitle(), + ViewColumn.One, + experimentsManager.inExperiment(WebHostNotebook.experiment) + ); + + // Load the web panel using our current directory as we don't expect to load any other files + super.loadWebPanel(process.cwd()).catch(traceError); + } + + public async showVariable(variable: IJupyterVariable, notebook: INotebook): Promise { + if (!this.isDisposed) { + // Save notebook this is tied to + this.notebook = notebook; + + // Fill in our variable's beginning data + this.variable = await this.prepVariable(variable, notebook); + + // Create our new title with the variable name + let newTitle = `${localize.DataScience.dataExplorerTitle()} - ${variable.name}`; + const TRIM_LENGTH = 40; + if (newTitle.length > TRIM_LENGTH) { + newTitle = `${newTitle.substr(0, TRIM_LENGTH)}...`; + } + + super.setTitle(newTitle); + + // Then show our web panel. Eventually we need to consume the data + await super.show(true); + + // Send a message with our data + this.postMessage(DataViewerMessages.InitializeData, this.variable).ignoreErrors(); + } + } + + protected getOwningResource(): Promise { + return Promise.resolve(undefined); + } + + //tslint:disable-next-line:no-any + protected onMessage(message: string, payload: any) { + switch (message) { + case DataViewerMessages.GetAllRowsRequest: + this.getAllRows().ignoreErrors(); + break; + + case DataViewerMessages.GetRowsRequest: + this.getRowChunk(payload as IGetRowsRequest).ignoreErrors(); + break; + + default: + break; + } + + super.onMessage(message, payload); + } + + private async prepVariable(variable: IJupyterVariable, notebook: INotebook): Promise { + this.rowsTimer = new StopWatch(); + const output = await this.variableManager.getDataFrameInfo(variable, notebook); + + // Log telemetry about number of rows + try { + sendTelemetryEvent(Telemetry.ShowDataViewer, 0, { + rows: output.rowCount ? output.rowCount : 0, + columns: output.columns ? output.columns.length : 0 + }); + + // Count number of rows to fetch so can send telemetry on how long it took. + this.pendingRowsCount = output.rowCount ? output.rowCount : 0; + } catch { + noop(); + } + + return output; + } + + private async getAllRows() { + return this.wrapRequest(async () => { + if (this.variable && this.variable.rowCount && this.notebook) { + const allRows = await this.variableManager.getDataFrameRows( + this.variable, + this.notebook, + 0, + this.variable.rowCount + ); + this.pendingRowsCount = 0; + return this.postMessage(DataViewerMessages.GetAllRowsResponse, allRows); + } + }); + } + + private getRowChunk(request: IGetRowsRequest) { + return this.wrapRequest(async () => { + if (this.variable && this.variable.rowCount && this.notebook) { + const rows = await this.variableManager.getDataFrameRows( + this.variable, + this.notebook, + request.start, + Math.min(request.end, this.variable.rowCount) + ); + return this.postMessage(DataViewerMessages.GetRowsResponse, { + rows, + start: request.start, + end: request.end + }); + } + }); + } + + private async wrapRequest(func: () => Promise) { + try { + return await func(); + } catch (e) { + if (e instanceof JupyterDataRateLimitError) { + traceError(e); + const actionTitle = localize.DataScience.pythonInteractiveHelpLink(); + this.applicationShell.showErrorMessage(e.toString(), actionTitle).then((v) => { + // User clicked on the link, open it. + if (v === actionTitle) { + this.applicationShell.openUrl(HelpLinks.JupyterDataRateHelpLink); + } + }); + this.dispose(); + } + traceError(e); + this.applicationShell.showErrorMessage(e); + } finally { + this.sendElapsedTimeTelemetry(); + } + } + + private sendElapsedTimeTelemetry() { + if (this.rowsTimer && this.pendingRowsCount === 0) { + sendTelemetryEvent(Telemetry.ShowDataViewer, this.rowsTimer.elapsedTime); + } + } +} diff --git a/src/client/datascience/data-viewing/dataViewerMessageListener.ts b/src/client/datascience/data-viewing/dataViewerMessageListener.ts index ca6a66d7c2b1..b8535472be9d 100644 --- a/src/client/datascience/data-viewing/dataViewerMessageListener.ts +++ b/src/client/datascience/data-viewing/dataViewerMessageListener.ts @@ -1,46 +1,46 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../common/extensions'; - -import { IWebPanel, IWebPanelMessageListener } from '../../common/application/types'; - -// tslint:disable:no-any - -// This class listens to messages that come from the local Data Explorer window -export class DataViewerMessageListener implements IWebPanelMessageListener { - private disposedCallback: () => void; - private callback: (message: string, payload: any) => void; - private viewChanged: (panel: IWebPanel) => void; - - constructor( - callback: (message: string, payload: any) => void, - viewChanged: (panel: IWebPanel) => void, - disposed: () => void - ) { - // Save our dispose callback so we remove our interactive window - this.disposedCallback = disposed; - - // Save our local callback so we can handle the non broadcast case(s) - this.callback = callback; - - // Save view changed so we can forward view change events. - this.viewChanged = viewChanged; - } - - public async dispose() { - this.disposedCallback(); - } - - public onMessage(message: string, payload: any) { - // Send to just our local callback. - this.callback(message, payload); - } - - public onChangeViewState(panel: IWebPanel) { - // Forward this onto our callback - if (this.viewChanged) { - this.viewChanged(panel); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../common/extensions'; + +import { IWebPanel, IWebPanelMessageListener } from '../../common/application/types'; + +// tslint:disable:no-any + +// This class listens to messages that come from the local Data Explorer window +export class DataViewerMessageListener implements IWebPanelMessageListener { + private disposedCallback: () => void; + private callback: (message: string, payload: any) => void; + private viewChanged: (panel: IWebPanel) => void; + + constructor( + callback: (message: string, payload: any) => void, + viewChanged: (panel: IWebPanel) => void, + disposed: () => void + ) { + // Save our dispose callback so we remove our interactive window + this.disposedCallback = disposed; + + // Save our local callback so we can handle the non broadcast case(s) + this.callback = callback; + + // Save view changed so we can forward view change events. + this.viewChanged = viewChanged; + } + + public async dispose() { + this.disposedCallback(); + } + + public onMessage(message: string, payload: any) { + // Send to just our local callback. + this.callback(message, payload); + } + + public onChangeViewState(panel: IWebPanel) { + // Forward this onto our callback + if (this.viewChanged) { + this.viewChanged(panel); + } + } +} diff --git a/src/client/datascience/data-viewing/dataViewerProvider.ts b/src/client/datascience/data-viewing/dataViewerProvider.ts index 65ba757e7302..7a41344b01fa 100644 --- a/src/client/datascience/data-viewing/dataViewerProvider.ts +++ b/src/client/datascience/data-viewing/dataViewerProvider.ts @@ -1,49 +1,49 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../common/extensions'; - -import { inject, injectable } from 'inversify'; - -import { IAsyncDisposable, IAsyncDisposableRegistry } from '../../common/types'; -import { IServiceContainer } from '../../ioc/types'; -import { IDataViewer, IDataViewerProvider, IJupyterVariable, INotebook } from '../types'; -import { DataViewerDependencyService } from './dataViewerDependencyService'; - -@injectable() -export class DataViewerProvider implements IDataViewerProvider, IAsyncDisposable { - private activeExplorers: IDataViewer[] = []; - constructor( - @inject(IServiceContainer) private serviceContainer: IServiceContainer, - @inject(IAsyncDisposableRegistry) asyncRegistry: IAsyncDisposableRegistry, - @inject(DataViewerDependencyService) private dependencyService: DataViewerDependencyService - ) { - asyncRegistry.push(this); - } - - public async dispose() { - await Promise.all(this.activeExplorers.map(d => d.dispose())); - } - - public async create(variable: IJupyterVariable, notebook: INotebook): Promise { - let result: IDataViewer | undefined; - - // Create the data explorer (this should show the window) - const dataExplorer = this.serviceContainer.get(IDataViewer); - try { - // Verify this is allowed. - await this.dependencyService.checkAndInstallMissingDependencies(notebook.getMatchingInterpreter()); - - // Then load the data. - this.activeExplorers.push(dataExplorer); - await dataExplorer.showVariable(variable, notebook); - result = dataExplorer; - } finally { - if (!result) { - // If throw any errors, close the window we opened. - dataExplorer.dispose(); - } - } - return result; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../common/extensions'; + +import { inject, injectable } from 'inversify'; + +import { IAsyncDisposable, IAsyncDisposableRegistry } from '../../common/types'; +import { IServiceContainer } from '../../ioc/types'; +import { IDataViewer, IDataViewerProvider, IJupyterVariable, INotebook } from '../types'; +import { DataViewerDependencyService } from './dataViewerDependencyService'; + +@injectable() +export class DataViewerProvider implements IDataViewerProvider, IAsyncDisposable { + private activeExplorers: IDataViewer[] = []; + constructor( + @inject(IServiceContainer) private serviceContainer: IServiceContainer, + @inject(IAsyncDisposableRegistry) asyncRegistry: IAsyncDisposableRegistry, + @inject(DataViewerDependencyService) private dependencyService: DataViewerDependencyService + ) { + asyncRegistry.push(this); + } + + public async dispose() { + await Promise.all(this.activeExplorers.map((d) => d.dispose())); + } + + public async create(variable: IJupyterVariable, notebook: INotebook): Promise { + let result: IDataViewer | undefined; + + // Create the data explorer (this should show the window) + const dataExplorer = this.serviceContainer.get(IDataViewer); + try { + // Verify this is allowed. + await this.dependencyService.checkAndInstallMissingDependencies(notebook.getMatchingInterpreter()); + + // Then load the data. + this.activeExplorers.push(dataExplorer); + await dataExplorer.showVariable(variable, notebook); + result = dataExplorer; + } finally { + if (!result) { + // If throw any errors, close the window we opened. + dataExplorer.dispose(); + } + } + return result; + } +} diff --git a/src/client/datascience/data-viewing/types.ts b/src/client/datascience/data-viewing/types.ts index 09751f4daa85..6e20d7e6ad4f 100644 --- a/src/client/datascience/data-viewing/types.ts +++ b/src/client/datascience/data-viewing/types.ts @@ -1,52 +1,52 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { JSONObject } from '@phosphor/coreutils'; - -import { SharedMessages } from '../messages'; -import { IJupyterVariable } from '../types'; - -export const CellFetchAllLimit = 100000; -export const CellFetchSizeFirst = 100000; -export const CellFetchSizeSubsequent = 1000000; -export const MaxStringCompare = 200; -export const ColumnWarningSize = 1000; // Anything over this takes too long to load - -export namespace DataViewerRowStates { - export const Fetching = 'fetching'; - export const Skipped = 'skipped'; -} - -export namespace DataViewerMessages { - export const Started = SharedMessages.Started; - export const UpdateSettings = SharedMessages.UpdateSettings; - export const InitializeData = 'init'; - export const GetAllRowsRequest = 'get_all_rows_request'; - export const GetAllRowsResponse = 'get_all_rows_response'; - export const GetRowsRequest = 'get_rows_request'; - export const GetRowsResponse = 'get_rows_response'; - export const CompletedData = 'complete'; -} - -export interface IGetRowsRequest { - start: number; - end: number; -} - -export interface IGetRowsResponse { - rows: JSONObject; - start: number; - end: number; -} - -// Map all messages to specific payloads -export type IDataViewerMapping = { - [DataViewerMessages.Started]: never | undefined; - [DataViewerMessages.UpdateSettings]: string; - [DataViewerMessages.InitializeData]: IJupyterVariable; - [DataViewerMessages.GetAllRowsRequest]: never | undefined; - [DataViewerMessages.GetAllRowsResponse]: JSONObject; - [DataViewerMessages.GetRowsRequest]: IGetRowsRequest; - [DataViewerMessages.GetRowsResponse]: IGetRowsResponse; - [DataViewerMessages.CompletedData]: never | undefined; -}; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { JSONObject } from '@phosphor/coreutils'; + +import { SharedMessages } from '../messages'; +import { IJupyterVariable } from '../types'; + +export const CellFetchAllLimit = 100000; +export const CellFetchSizeFirst = 100000; +export const CellFetchSizeSubsequent = 1000000; +export const MaxStringCompare = 200; +export const ColumnWarningSize = 1000; // Anything over this takes too long to load + +export namespace DataViewerRowStates { + export const Fetching = 'fetching'; + export const Skipped = 'skipped'; +} + +export namespace DataViewerMessages { + export const Started = SharedMessages.Started; + export const UpdateSettings = SharedMessages.UpdateSettings; + export const InitializeData = 'init'; + export const GetAllRowsRequest = 'get_all_rows_request'; + export const GetAllRowsResponse = 'get_all_rows_response'; + export const GetRowsRequest = 'get_rows_request'; + export const GetRowsResponse = 'get_rows_response'; + export const CompletedData = 'complete'; +} + +export interface IGetRowsRequest { + start: number; + end: number; +} + +export interface IGetRowsResponse { + rows: JSONObject; + start: number; + end: number; +} + +// Map all messages to specific payloads +export type IDataViewerMapping = { + [DataViewerMessages.Started]: never | undefined; + [DataViewerMessages.UpdateSettings]: string; + [DataViewerMessages.InitializeData]: IJupyterVariable; + [DataViewerMessages.GetAllRowsRequest]: never | undefined; + [DataViewerMessages.GetAllRowsResponse]: JSONObject; + [DataViewerMessages.GetRowsRequest]: IGetRowsRequest; + [DataViewerMessages.GetRowsResponse]: IGetRowsResponse; + [DataViewerMessages.CompletedData]: never | undefined; +}; diff --git a/src/client/datascience/editor-integration/cellhashLogger.ts b/src/client/datascience/editor-integration/cellhashLogger.ts index 262e8e0bb16f..4b21ba112413 100644 --- a/src/client/datascience/editor-integration/cellhashLogger.ts +++ b/src/client/datascience/editor-integration/cellhashLogger.ts @@ -22,7 +22,7 @@ export class CellHashLogger implements ICellHashLogger { if (!silent) { // Don't log empty cells const stripped = providerObj.extractExecutableLines(cell); - if (stripped.length > 0 && stripped.find(s => s.trim().length > 0)) { + if (stripped.length > 0 && stripped.find((s) => s.trim().length > 0)) { // When the user adds new code, we know the execution count is increasing providerObj.incExecutionCount(); diff --git a/src/client/datascience/editor-integration/cellhashprovider.ts b/src/client/datascience/editor-integration/cellhashprovider.ts index 6be1c1604e42..d75a66619a65 100644 --- a/src/client/datascience/editor-integration/cellhashprovider.ts +++ b/src/client/datascience/editor-integration/cellhashprovider.ts @@ -92,13 +92,13 @@ export class CellHashProvider implements ICellHashProvider, IInteractiveWindowLi public getHashes(): IFileHashes[] { return [...this.hashes.entries()] - .map(e => { + .map((e) => { return { file: e[0], - hashes: e[1].filter(h => !h.deleted) + hashes: e[1].filter((h) => !h.deleted) }; }) - .filter(e => e.hashes.length > 0); + .filter((e) => e.hashes.length > 0); } public async preExecute(cell: ICell, silent: boolean): Promise { @@ -106,7 +106,7 @@ export class CellHashProvider implements ICellHashProvider, IInteractiveWindowLi if (!silent) { // Don't log empty cells const stripped = this.extractExecutableLines(cell); - if (stripped.length > 0 && stripped.find(s => s.trim().length > 0)) { + if (stripped.length > 0 && stripped.find((s) => s.trim().length > 0)) { // When the user adds new code, we know the execution count is increasing this.executionCount += 1; @@ -140,7 +140,7 @@ export class CellHashProvider implements ICellHashProvider, IInteractiveWindowLi public async addCellHash(cell: ICell, expectedCount: number): Promise { // Find the text document that matches. We need more information than // the add code gives us - const doc = this.documentManager.textDocuments.find(d => this.fileSystem.arePathsSame(d.fileName, cell.file)); + const doc = this.documentManager.textDocuments.find((d) => this.fileSystem.arePathsSame(d.fileName, cell.file)); if (doc) { // Compute the code that will really be sent to jupyter const lines = splitMultilineString(cell.data.source); @@ -191,11 +191,7 @@ export class CellHashProvider implements ICellHashProvider, IInteractiveWindowLi const realCode = doc.getText(new Range(new Position(cell.line, 0), endLine.rangeIncludingLineBreak.end)); const hash: IRangedCellHash = { - hash: hashjs - .sha1() - .update(hashedCode) - .digest('hex') - .substr(0, 12), + hash: hashjs.sha1().update(hashedCode).digest('hex').substr(0, 12), line: line.lineNumber + 1, endLine: endLine.lineNumber + 1, executionCount: expectedCount, @@ -238,7 +234,7 @@ export class CellHashProvider implements ICellHashProvider, IInteractiveWindowLi // Tell listeners we have new hashes. if (this.listeners) { const hashes = this.getHashes(); - await Promise.all(this.listeners.map(l => l.hashesUpdated(hashes))); + await Promise.all(this.listeners.map((l) => l.hashesUpdated(hashes))); // Then fire our event this.updateEventEmitter.fire(); @@ -260,7 +256,7 @@ export class CellHashProvider implements ICellHashProvider, IInteractiveWindowLi if (perFile) { // Apply the content changes to the file's cells. const docText = e.document.getText(); - e.contentChanges.forEach(c => { + e.contentChanges.forEach((c) => { this.handleContentChange(docText, c, perFile); }); } @@ -274,7 +270,7 @@ export class CellHashProvider implements ICellHashProvider, IInteractiveWindowLi // Compute the inclusive offset that is changed by the cell. const endChangedOffset = c.rangeLength <= 0 ? c.rangeOffset : c.rangeOffset + c.rangeLength - 1; - hashes.forEach(h => { + hashes.forEach((h) => { // See how this existing cell compares to the change if (h.endOffset < c.rangeOffset) { // No change. This cell is entirely before the change diff --git a/src/client/datascience/editor-integration/codeLensFactory.ts b/src/client/datascience/editor-integration/codeLensFactory.ts index 29e6f95308eb..1e3bed9f0bcf 100644 --- a/src/client/datascience/editor-integration/codeLensFactory.ts +++ b/src/client/datascience/editor-integration/codeLensFactory.ts @@ -93,8 +93,8 @@ export class CodeLensFactory implements ICodeLensFactory, IInteractiveWindowList const codeLenses: CodeLens[] = []; let firstCell = true; - ranges.forEach(range => { - commands.forEach(c => { + ranges.forEach((range) => { + commands.forEach((c) => { const codeLens = this.createCodeLens(document, range, c, firstCell); if (codeLens) { codeLenses.push(codeLens); @@ -139,7 +139,7 @@ export class CodeLensFactory implements ICodeLensFactory, IInteractiveWindowList // Add our non-debug commands const commands = this.configService.getSettings(resource).datascience.codeLenses; if (commands) { - fullCommandList = commands.split(',').map(s => s.trim()); + fullCommandList = commands.split(',').map((s) => s.trim()); } else { fullCommandList = CodeLensCommands.DefaultDesignLenses; } @@ -147,7 +147,7 @@ export class CodeLensFactory implements ICodeLensFactory, IInteractiveWindowList // Add our debug commands const debugCommands = this.configService.getSettings(resource).datascience.debugCodeLenses; if (debugCommands) { - fullCommandList = fullCommandList.concat(debugCommands.split(',').map(s => s.trim())); + fullCommandList = fullCommandList.concat(debugCommands.split(',').map((s) => s.trim())); } else { fullCommandList = fullCommandList.concat(CodeLensCommands.DefaultDebuggingLenses); } @@ -283,10 +283,10 @@ export class CodeLensFactory implements ICodeLensFactory, IInteractiveWindowList } private addExecutionCount(codeLens: CodeLens[], document: TextDocument, range: Range, hashes: IFileHashes[]) { - const list = hashes.find(h => this.fileSystem.arePathsSame(h.file, document.fileName)); + const list = hashes.find((h) => this.fileSystem.arePathsSame(h.file, document.fileName)); if (list) { // Match just the start of the range. Should be - 2 (1 for 1 based numbers and 1 for skipping the comment at the top) - const rangeMatches = list.hashes.filter(h => h.line - 2 === range.start.line); + const rangeMatches = list.hashes.filter((h) => h.line - 2 === range.start.line); if (rangeMatches && rangeMatches.length) { const rangeMatch = rangeMatches[rangeMatches.length - 1]; if (this.cellExecutionCounts.has(rangeMatch.id)) { diff --git a/src/client/datascience/editor-integration/codelensprovider.ts b/src/client/datascience/editor-integration/codelensprovider.ts index 05089f8ffd71..1c0f931ff9a4 100644 --- a/src/client/datascience/editor-integration/codelensprovider.ts +++ b/src/client/datascience/editor-integration/codelensprovider.ts @@ -75,7 +75,7 @@ export class DataScienceCodeLensProvider implements IDataScienceCodeLensProvider } private onDidCloseTextDocument(e: vscode.TextDocument) { - const index = this.activeCodeWatchers.findIndex(item => item.getFileName() === e.fileName); + const index = this.activeCodeWatchers.findIndex((item) => item.getFileName() === e.fileName); if (index >= 0) { this.activeCodeWatchers.splice(index, 1); } @@ -116,13 +116,13 @@ export class DataScienceCodeLensProvider implements IDataScienceCodeLensProvider if (debugLocation && this.fileSystem.arePathsSame(debugLocation.fileName, document.uri.fsPath)) { // We are in the given debug file, so only return the code lens that contains the given line - const activeLenses = lenses.filter(lens => { + const activeLenses = lenses.filter((lens) => { // -1 for difference between file system one based and debugger zero based const pos = new vscode.Position(debugLocation.lineNumber - 1, debugLocation.column - 1); return lens.range.contains(pos); }); - return activeLenses.filter(lens => { + return activeLenses.filter((lens) => { if (lens.command) { return debugCellList.includes(lens.command.command); } @@ -130,7 +130,7 @@ export class DataScienceCodeLensProvider implements IDataScienceCodeLensProvider }); } } else { - return lenses.filter(lens => { + return lenses.filter((lens) => { if (lens.command) { return !debugCellList.includes(lens.command.command); } @@ -159,7 +159,7 @@ export class DataScienceCodeLensProvider implements IDataScienceCodeLensProvider } private matchWatcher(fileName: string, version: number, settings: IDataScienceSettings): ICodeWatcher | undefined { - const index = this.activeCodeWatchers.findIndex(item => item.getFileName() === fileName); + const index = this.activeCodeWatchers.findIndex((item) => item.getFileName() === fileName); if (index >= 0) { const item = this.activeCodeWatchers[index]; if (item.getVersion() === version) { @@ -176,7 +176,7 @@ export class DataScienceCodeLensProvider implements IDataScienceCodeLensProvider } // Create a new watcher for this file if we can find a matching document - const possibleDocuments = this.documentManager.textDocuments.filter(d => d.fileName === fileName); + const possibleDocuments = this.documentManager.textDocuments.filter((d) => d.fileName === fileName); if (possibleDocuments && possibleDocuments.length > 0) { return this.createNewCodeWatcher(possibleDocuments[0]); } diff --git a/src/client/datascience/editor-integration/codewatcher.ts b/src/client/datascience/editor-integration/codewatcher.ts index dc94a298d442..5c7846c3896f 100644 --- a/src/client/datascience/editor-integration/codewatcher.ts +++ b/src/client/datascience/editor-integration/codewatcher.ts @@ -95,7 +95,7 @@ export class CodeWatcher implements ICodeWatcher { @captureTelemetry(Telemetry.RunAllCells) public async runAllCells() { const runCellCommands = this.codeLenses.filter( - c => + (c) => c.command && c.command.command === Commands.RunCell && c.command.arguments && @@ -151,9 +151,9 @@ export class CodeWatcher implements ICodeWatcher { // Run all cells up to the cell containing this start line and character @captureTelemetry(Telemetry.RunAllCellsAbove) public async runAllCellsAbove(stopLine: number, stopCharacter: number) { - const runCellCommands = this.codeLenses.filter(c => c.command && c.command.command === Commands.RunCell); + const runCellCommands = this.codeLenses.filter((c) => c.command && c.command.command === Commands.RunCell); let leftCount = runCellCommands.findIndex( - c => c.range.start.line >= stopLine && c.range.start.character >= stopCharacter + (c) => c.range.start.line >= stopLine && c.range.start.character >= stopCharacter ); if (leftCount < 0) { leftCount = runCellCommands.length; @@ -189,9 +189,9 @@ export class CodeWatcher implements ICodeWatcher { @captureTelemetry(Telemetry.RunCellAndAllBelow) public async runCellAndAllBelow(startLine: number, startCharacter: number) { - const runCellCommands = this.codeLenses.filter(c => c.command && c.command.command === Commands.RunCell); + const runCellCommands = this.codeLenses.filter((c) => c.command && c.command.command === Commands.RunCell); const index = runCellCommands.findIndex( - c => c.range.start.line >= startLine && c.range.start.character >= startCharacter + (c) => c.range.start.line >= startLine && c.range.start.character >= startCharacter ); let leftCount = index > 0 ? runCellCommands.length - index : runCellCommands.length; @@ -308,7 +308,7 @@ export class CodeWatcher implements ICodeWatcher { const editor = this.documentManager.activeTextEditor; const cellDelineator = this.getDefaultCellMarker(editor?.document.uri); if (editor) { - editor.edit(editBuilder => { + editor.edit((editBuilder) => { editBuilder.insert(new Position(editor.document.lineCount, 0), `\n\n${cellDelineator}\n`); }); @@ -328,7 +328,7 @@ export class CodeWatcher implements ICodeWatcher { const cellDelineator = this.getDefaultCellMarker(editor.document.uri); if (editor) { - editor.edit(editBuilder => { + editor.edit((editBuilder) => { let lastCell = true; for (let i = editor.selection.end.line + 1; i < editor.document.lineCount; i += 1) { @@ -453,13 +453,13 @@ export class CodeWatcher implements ICodeWatcher { private getCurrentCellLens(pos: Position): CodeLens | undefined { return this.codeLenses.find( - l => l.range.contains(pos) && l.command !== undefined && l.command.command === Commands.RunCell + (l) => l.range.contains(pos) && l.command !== undefined && l.command.command === Commands.RunCell ); } private getNextCellLens(pos: Position): CodeLens | undefined { const currentIndex = this.codeLenses.findIndex( - l => l.range.contains(pos) && l.command !== undefined && l.command.command === Commands.RunCell + (l) => l.range.contains(pos) && l.command !== undefined && l.command.command === Commands.RunCell ); if (currentIndex >= 0) { return this.codeLenses.find( @@ -484,7 +484,7 @@ export class CodeWatcher implements ICodeWatcher { const newPosition = new Position(currentRange.end.line + 3, 0); // +3 to account for the added spaces and to position after the new mark if (editor) { - editor.edit(editBuilder => { + editor.edit((editBuilder) => { editBuilder.insert( new Position(currentRange.end.line + 1, 0), `\n\n${this.getDefaultCellMarker(editor.document.uri)}\n` diff --git a/src/client/datascience/editor-integration/decorator.ts b/src/client/datascience/editor-integration/decorator.ts index 339ad1b097f4..94da0cf9a67c 100644 --- a/src/client/datascience/editor-integration/decorator.ts +++ b/src/client/datascience/editor-integration/decorator.ts @@ -1,131 +1,131 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { inject, injectable } from 'inversify'; -import * as vscode from 'vscode'; - -import { IExtensionSingleActivationService } from '../../activation/types'; -import { IDocumentManager } from '../../common/application/types'; -import { PYTHON_LANGUAGE } from '../../common/constants'; -import { IConfigurationService, IDisposable, IDisposableRegistry } from '../../common/types'; -import { generateCellRangesFromDocument } from '../cellFactory'; - -@injectable() -export class Decorator implements IExtensionSingleActivationService, IDisposable { - private activeCellTop: vscode.TextEditorDecorationType | undefined; - private activeCellBottom: vscode.TextEditorDecorationType | undefined; - private cellSeparatorType: vscode.TextEditorDecorationType | undefined; - private timer: NodeJS.Timer | undefined | number; - - constructor( - @inject(IDocumentManager) private documentManager: IDocumentManager, - @inject(IDisposableRegistry) disposables: IDisposableRegistry, - @inject(IConfigurationService) private configuration: IConfigurationService - ) { - this.computeDecorations(); - disposables.push(this); - disposables.push(this.configuration.getSettings(undefined).onDidChange(this.settingsChanged, this)); - disposables.push(this.documentManager.onDidChangeActiveTextEditor(this.changedEditor, this)); - disposables.push(this.documentManager.onDidChangeTextEditorSelection(this.changedSelection, this)); - disposables.push(this.documentManager.onDidChangeTextDocument(this.changedDocument, this)); - this.settingsChanged(); - } - - public activate(): Promise { - // We don't need to do anything here as we already did all of our work in the - // constructor. - return Promise.resolve(); - } - - public dispose() { - if (this.timer) { - // tslint:disable-next-line: no-any - clearTimeout(this.timer as any); - } - } - - private settingsChanged() { - if (this.documentManager.activeTextEditor) { - this.triggerUpdate(this.documentManager.activeTextEditor); - } - } - - private changedEditor(editor: vscode.TextEditor | undefined) { - this.triggerUpdate(editor); - } - - private changedDocument(e: vscode.TextDocumentChangeEvent) { - if (this.documentManager.activeTextEditor && e.document === this.documentManager.activeTextEditor.document) { - this.triggerUpdate(this.documentManager.activeTextEditor); - } - } - - private changedSelection(e: vscode.TextEditorSelectionChangeEvent) { - if (e.textEditor && e.textEditor.selection.anchor) { - this.triggerUpdate(e.textEditor); - } - } - - private triggerUpdate(editor: vscode.TextEditor | undefined) { - if (this.timer) { - // tslint:disable-next-line: no-any - clearTimeout(this.timer as any); - } - this.timer = setTimeout(() => this.update(editor), 100); - } - - private computeDecorations() { - this.activeCellTop = this.documentManager.createTextEditorDecorationType({ - borderColor: new vscode.ThemeColor('peekView.border'), - borderWidth: '2px 0px 0px 0px', - borderStyle: 'solid', - isWholeLine: true - }); - this.activeCellBottom = this.documentManager.createTextEditorDecorationType({ - borderColor: new vscode.ThemeColor('peekView.border'), - borderWidth: '0px 0px 1px 0px', - borderStyle: 'solid', - isWholeLine: true - }); - this.cellSeparatorType = this.documentManager.createTextEditorDecorationType({ - borderColor: new vscode.ThemeColor('editor.lineHighlightBorder'), - borderWidth: '1px 0px 0px 0px', - borderStyle: 'solid', - isWholeLine: true - }); - } - - private update(editor: vscode.TextEditor | undefined) { - if ( - editor && - editor.document && - editor.document.languageId === PYTHON_LANGUAGE && - this.activeCellTop && - this.cellSeparatorType && - this.activeCellBottom - ) { - const settings = this.configuration.getSettings(editor.document.uri).datascience; - if (settings.decorateCells && settings.enabled) { - // Find all of the cells - const cells = generateCellRangesFromDocument(editor.document, settings); - - // Find the range for our active cell. - const currentRange = cells.map(c => c.range).filter(r => r.contains(editor.selection.anchor)); - const rangeTop = - currentRange.length > 0 ? [new vscode.Range(currentRange[0].start, currentRange[0].start)] : []; - const rangeBottom = - currentRange.length > 0 ? [new vscode.Range(currentRange[0].end, currentRange[0].end)] : []; - editor.setDecorations(this.activeCellTop, rangeTop); - editor.setDecorations(this.activeCellBottom, rangeBottom); - - // Find the start range for the rest - const startRanges = cells.map(c => new vscode.Range(c.range.start, c.range.start)); - editor.setDecorations(this.cellSeparatorType, startRanges); - } else { - editor.setDecorations(this.activeCellTop, []); - editor.setDecorations(this.activeCellBottom, []); - editor.setDecorations(this.cellSeparatorType, []); - } - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { inject, injectable } from 'inversify'; +import * as vscode from 'vscode'; + +import { IExtensionSingleActivationService } from '../../activation/types'; +import { IDocumentManager } from '../../common/application/types'; +import { PYTHON_LANGUAGE } from '../../common/constants'; +import { IConfigurationService, IDisposable, IDisposableRegistry } from '../../common/types'; +import { generateCellRangesFromDocument } from '../cellFactory'; + +@injectable() +export class Decorator implements IExtensionSingleActivationService, IDisposable { + private activeCellTop: vscode.TextEditorDecorationType | undefined; + private activeCellBottom: vscode.TextEditorDecorationType | undefined; + private cellSeparatorType: vscode.TextEditorDecorationType | undefined; + private timer: NodeJS.Timer | undefined | number; + + constructor( + @inject(IDocumentManager) private documentManager: IDocumentManager, + @inject(IDisposableRegistry) disposables: IDisposableRegistry, + @inject(IConfigurationService) private configuration: IConfigurationService + ) { + this.computeDecorations(); + disposables.push(this); + disposables.push(this.configuration.getSettings(undefined).onDidChange(this.settingsChanged, this)); + disposables.push(this.documentManager.onDidChangeActiveTextEditor(this.changedEditor, this)); + disposables.push(this.documentManager.onDidChangeTextEditorSelection(this.changedSelection, this)); + disposables.push(this.documentManager.onDidChangeTextDocument(this.changedDocument, this)); + this.settingsChanged(); + } + + public activate(): Promise { + // We don't need to do anything here as we already did all of our work in the + // constructor. + return Promise.resolve(); + } + + public dispose() { + if (this.timer) { + // tslint:disable-next-line: no-any + clearTimeout(this.timer as any); + } + } + + private settingsChanged() { + if (this.documentManager.activeTextEditor) { + this.triggerUpdate(this.documentManager.activeTextEditor); + } + } + + private changedEditor(editor: vscode.TextEditor | undefined) { + this.triggerUpdate(editor); + } + + private changedDocument(e: vscode.TextDocumentChangeEvent) { + if (this.documentManager.activeTextEditor && e.document === this.documentManager.activeTextEditor.document) { + this.triggerUpdate(this.documentManager.activeTextEditor); + } + } + + private changedSelection(e: vscode.TextEditorSelectionChangeEvent) { + if (e.textEditor && e.textEditor.selection.anchor) { + this.triggerUpdate(e.textEditor); + } + } + + private triggerUpdate(editor: vscode.TextEditor | undefined) { + if (this.timer) { + // tslint:disable-next-line: no-any + clearTimeout(this.timer as any); + } + this.timer = setTimeout(() => this.update(editor), 100); + } + + private computeDecorations() { + this.activeCellTop = this.documentManager.createTextEditorDecorationType({ + borderColor: new vscode.ThemeColor('peekView.border'), + borderWidth: '2px 0px 0px 0px', + borderStyle: 'solid', + isWholeLine: true + }); + this.activeCellBottom = this.documentManager.createTextEditorDecorationType({ + borderColor: new vscode.ThemeColor('peekView.border'), + borderWidth: '0px 0px 1px 0px', + borderStyle: 'solid', + isWholeLine: true + }); + this.cellSeparatorType = this.documentManager.createTextEditorDecorationType({ + borderColor: new vscode.ThemeColor('editor.lineHighlightBorder'), + borderWidth: '1px 0px 0px 0px', + borderStyle: 'solid', + isWholeLine: true + }); + } + + private update(editor: vscode.TextEditor | undefined) { + if ( + editor && + editor.document && + editor.document.languageId === PYTHON_LANGUAGE && + this.activeCellTop && + this.cellSeparatorType && + this.activeCellBottom + ) { + const settings = this.configuration.getSettings(editor.document.uri).datascience; + if (settings.decorateCells && settings.enabled) { + // Find all of the cells + const cells = generateCellRangesFromDocument(editor.document, settings); + + // Find the range for our active cell. + const currentRange = cells.map((c) => c.range).filter((r) => r.contains(editor.selection.anchor)); + const rangeTop = + currentRange.length > 0 ? [new vscode.Range(currentRange[0].start, currentRange[0].start)] : []; + const rangeBottom = + currentRange.length > 0 ? [new vscode.Range(currentRange[0].end, currentRange[0].end)] : []; + editor.setDecorations(this.activeCellTop, rangeTop); + editor.setDecorations(this.activeCellBottom, rangeBottom); + + // Find the start range for the rest + const startRanges = cells.map((c) => new vscode.Range(c.range.start, c.range.start)); + editor.setDecorations(this.cellSeparatorType, startRanges); + } else { + editor.setDecorations(this.activeCellTop, []); + editor.setDecorations(this.activeCellBottom, []); + editor.setDecorations(this.cellSeparatorType, []); + } + } + } +} diff --git a/src/client/datascience/errorHandler/errorHandler.ts b/src/client/datascience/errorHandler/errorHandler.ts index 6f5245e79782..f0fa2f29cd95 100644 --- a/src/client/datascience/errorHandler/errorHandler.ts +++ b/src/client/datascience/errorHandler/errorHandler.ts @@ -39,7 +39,7 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { const selectNewServer = localize.DataScience.selectNewServer(); this.applicationShell .showErrorMessage(localize.DataScience.nativeDependencyFail().format(err.toString()), selectNewServer) - .then(selection => { + .then((selection) => { if (selection === selectNewServer) { this.serverSelector.selectJupyterURI(false).ignoreErrors(); } diff --git a/src/client/datascience/gather/gather.ts b/src/client/datascience/gather/gather.ts index 27a76e0960fe..124fbd6f8ef1 100644 --- a/src/client/datascience/gather/gather.ts +++ b/src/client/datascience/gather/gather.ts @@ -43,7 +43,7 @@ export class GatherProvider implements IGatherProvider { this._executionSlicer = new ppa.ExecutionLogSlicer(this.dataflowAnalyzer); this.disposables.push( - this.configService.getSettings(undefined).onDidChange(e => this.updateEnableGather(e)) + this.configService.getSettings(undefined).onDidChange((e) => this.updateEnableGather(e)) ); } } catch (ex) { diff --git a/src/client/datascience/gather/gatherListener.ts b/src/client/datascience/gather/gatherListener.ts index d1567e1e97a4..3226ed6036fd 100644 --- a/src/client/datascience/gather/gatherListener.ts +++ b/src/client/datascience/gather/gatherListener.ts @@ -114,7 +114,7 @@ export class GatherListener implements IInteractiveWindowListener { } private doGather(payload: ICell): void { - this.gatherCodeInternal(payload).catch(err => { + this.gatherCodeInternal(payload).catch((err) => { this.applicationShell.showErrorMessage(err); }); } @@ -180,11 +180,11 @@ export class GatherListener implements IInteractiveWindowListener { private async showFile(slicedProgram: string, filename: string) { // Don't want to open the gathered code on top of the interactive window let viewColumn: ViewColumn | undefined; - const fileNameMatch = this.documentManager.visibleTextEditors.filter(textEditor => + const fileNameMatch = this.documentManager.visibleTextEditors.filter((textEditor) => this.fileSystem.arePathsSame(textEditor.document.fileName, filename) ); const definedVisibleEditors = this.documentManager.visibleTextEditors.filter( - textEditor => textEditor.viewColumn !== undefined + (textEditor) => textEditor.viewColumn !== undefined ); if (this.documentManager.visibleTextEditors.length > 0 && fileNameMatch.length > 0) { // Original file is visible @@ -205,7 +205,7 @@ export class GatherListener implements IInteractiveWindowListener { const editor = await this.documentManager.showTextDocument(doc, viewColumn); // Edit the document so that it is dirty (add a space at the end) - editor.edit(editBuilder => { + editor.edit((editBuilder) => { editBuilder.insert(new Position(editor.document.lineCount, 0), '\n'); }); } diff --git a/src/client/datascience/interactive-common/intellisense/conversion.ts b/src/client/datascience/interactive-common/intellisense/conversion.ts index 5a8c75cc519a..381efbd6e2a1 100644 --- a/src/client/datascience/interactive-common/intellisense/conversion.ts +++ b/src/client/datascience/interactive-common/intellisense/conversion.ts @@ -235,14 +235,14 @@ export function convertToMonacoCompletionList( if (result.hasOwnProperty('items')) { const list = result as vscodeLanguageClient.CompletionList; return { - suggestions: list.items.map(l => convertToMonacoCompletionItem(l, requiresKindConversion)), + suggestions: list.items.map((l) => convertToMonacoCompletionItem(l, requiresKindConversion)), incomplete: list.isIncomplete }; } else { // Must be one of the two array types since there's no items property. const array = result as vscodeLanguageClient.CompletionItem[]; return { - suggestions: array.map(l => convertToMonacoCompletionItem(l, requiresKindConversion)), + suggestions: array.map((l) => convertToMonacoCompletionItem(l, requiresKindConversion)), incomplete: false }; } @@ -285,7 +285,7 @@ function convertToMonacoMarkdown( ]; } else if (Array.isArray(strings)) { const array = strings as vscodeLanguageClient.MarkedString[]; - return array.map(a => convertToMonacoMarkdown(a)[0]); + return array.map((a) => convertToMonacoMarkdown(a)[0]); } return []; diff --git a/src/client/datascience/interactive-common/intellisense/intellisenseDocument.ts b/src/client/datascience/interactive-common/intellisense/intellisenseDocument.ts index d9fac20c0008..53ea071bc628 100644 --- a/src/client/datascience/interactive-common/intellisense/intellisenseDocument.ts +++ b/src/client/datascience/interactive-common/intellisense/intellisenseDocument.ts @@ -220,7 +220,7 @@ export class IntellisenseDocument implements TextDocument { // Normalize all of the cells, removing \r and separating each // with a newline - const normalized = cells.map(c => { + const normalized = cells.map((c) => { return { id: c.id, code: `${c.code.replace(/\r/g, '')}\n` @@ -231,7 +231,7 @@ export class IntellisenseDocument implements TextDocument { this._contents = normalized && normalized.length ? normalized - .map(c => c.code) + .map((c) => c.code) .reduce((p, c) => { return `${p}${c}`; }) @@ -239,7 +239,7 @@ export class IntellisenseDocument implements TextDocument { // Cell ranges are slightly more complicated let prev: number = 0; - this._cellRanges = normalized.map(c => { + this._cellRanges = normalized.map((c) => { const result = { id: c.id, start: prev, @@ -318,7 +318,7 @@ export class IntellisenseDocument implements TextDocument { const newCode = `${code.replace(/\r/g, '')}\n`; // Figure where this goes - const index = this._cellRanges.findIndex(r => r.id === id); + const index = this._cellRanges.findIndex((r) => r.id === id); if (index >= 0) { const start = this.positionAt(this._cellRanges[index].start); const end = this.positionAt(this._cellRanges[index].currentEnd); @@ -340,7 +340,7 @@ export class IntellisenseDocument implements TextDocument { const newCode = `${code.replace(/\r/g, '')}\n`; // Figure where this goes - const aboveIndex = this._cellRanges.findIndex(r => r.id === codeCellAboveOrIndex); + const aboveIndex = this._cellRanges.findIndex((r) => r.id === codeCellAboveOrIndex); const insertIndex = typeof codeCellAboveOrIndex === 'number' ? codeCellAboveOrIndex : aboveIndex + 1; // Compute where we start from. @@ -409,7 +409,7 @@ export class IntellisenseDocument implements TextDocument { const normalized = editorChanges[0].text.replace(/\r/g, ''); // Figure out which cell we're editing. - const cellIndex = this._cellRanges.findIndex(c => c.id === id); + const cellIndex = this._cellRanges.findIndex((c) => c.id === id); if (cellIndex >= 0 && (id === Identifiers.EditCellId || this.inEditMode)) { // This is an actual edit. // Line/column are within this cell. Use its offset to compute the real position @@ -438,7 +438,7 @@ export class IntellisenseDocument implements TextDocument { public remove(id: string): TextDocumentContentChangeEvent[] { let change: TextDocumentContentChangeEvent[] = []; - const index = this._cellRanges.findIndex(c => c.id === id); + const index = this._cellRanges.findIndex((c) => c.id === id); // Ignore unless in edit mode. For non edit mode, cells are still there. if (index >= 0 && this.inEditMode) { this._version += 1; @@ -478,8 +478,8 @@ export class IntellisenseDocument implements TextDocument { public swap(first: string, second: string): TextDocumentContentChangeEvent[] { let change: TextDocumentContentChangeEvent[] = []; - const firstIndex = this._cellRanges.findIndex(c => c.id === first); - const secondIndex = this._cellRanges.findIndex(c => c.id === second); + const firstIndex = this._cellRanges.findIndex((c) => c.id === first); + const secondIndex = this._cellRanges.findIndex((c) => c.id === second); if (firstIndex >= 0 && secondIndex >= 0 && firstIndex !== secondIndex && this.inEditMode) { this._version += 1; @@ -555,7 +555,7 @@ export class IntellisenseDocument implements TextDocument { public convertToDocumentPosition(id: string, line: number, ch: number): Position { // Monaco is 1 based, and we need to add in our cell offset. - const cellIndex = this._cellRanges.findIndex(c => c.id === id); + const cellIndex = this._cellRanges.findIndex((c) => c.id === id); if (cellIndex >= 0) { // Line/column are within this cell. Use its offset to compute the real position const editLine = this.positionAt(this._cellRanges[cellIndex].start); @@ -569,7 +569,7 @@ export class IntellisenseDocument implements TextDocument { } public getCellData(cellId: string) { - const range = this._cellRanges.find(cellRange => cellRange.id === cellId); + const range = this._cellRanges.find((cellRange) => cellRange.id === cellId); if (range) { return { offset: range.start, @@ -585,7 +585,7 @@ export class IntellisenseDocument implements TextDocument { public getEditCellOffset(cellId?: string) { // in native editor if (this.inEditMode && cellId) { - const cell = this._cellRanges.find(c => c.id === cellId); + const cell = this._cellRanges.find((c) => c.id === cellId); if (cell) { return cell.start; diff --git a/src/client/datascience/interactive-common/intellisense/intellisenseProvider.ts b/src/client/datascience/interactive-common/intellisense/intellisenseProvider.ts index 13db188a67a1..bb5189757f64 100644 --- a/src/client/datascience/interactive-common/intellisense/intellisenseProvider.ts +++ b/src/client/datascience/interactive-common/intellisense/intellisenseProvider.ts @@ -148,12 +148,12 @@ export class IntellisenseProvider implements IInteractiveWindowListener { } else { this.fileSystem .createTemporaryFile('.py') - .then(t => { + .then((t) => { this.temporaryFile = t; const dummyFilePath = this.temporaryFile.filePath; this.documentPromise!.resolve(new IntellisenseDocument(dummyFilePath)); }) - .catch(e => { + .catch((e) => { this.documentPromise!.reject(e); }); } @@ -397,7 +397,7 @@ export class IntellisenseProvider implements IInteractiveWindowListener { }; // Combine all of the results together. - this.postTimedResponse([getCompletions()], InteractiveWindowMessages.ProvideCompletionItemsResponse, c => { + this.postTimedResponse([getCompletions()], InteractiveWindowMessages.ProvideCompletionItemsResponse, (c) => { const list = this.combineCompletions(c); return { list, requestId: request.requestId }; }); @@ -412,7 +412,7 @@ export class IntellisenseProvider implements IInteractiveWindowListener { this.postTimedResponse( [this.resolveCompletionItem(request.position, request.item, request.cellId, cancelSource.token)], InteractiveWindowMessages.ResolveCompletionItemResponse, - c => { + (c) => { if (c && c[0]) { return { item: c[0], requestId: request.requestId }; } else { @@ -428,7 +428,7 @@ export class IntellisenseProvider implements IInteractiveWindowListener { this.postTimedResponse( [this.provideHover(request.position, request.cellId, cancelSource.token)], InteractiveWindowMessages.ProvideHoverResponse, - h => { + (h) => { if (h && h[0]) { return { hover: h[0]!, requestId: request.requestId }; } else { @@ -508,7 +508,7 @@ export class IntellisenseProvider implements IInteractiveWindowListener { const line = document.lineAt(pos); return line.isEmptyOrWhitespace ? jupyterResults.matches - : jupyterResults.matches.filter(match => !match.startsWith('%')); + : jupyterResults.matches.filter((match) => !match.startsWith('%')); } private postTimedResponse( @@ -518,13 +518,13 @@ export class IntellisenseProvider implements IInteractiveWindowListener { ) { // Time all of the promises to make sure they don't take too long. // Even if LS or Jupyter doesn't complete within e.g. 30s, then we should return an empty response (no point waiting that long). - const timed = promises.map(p => waitForPromise(p, Settings.MaxIntellisenseTimeout)); + const timed = promises.map((p) => waitForPromise(p, Settings.MaxIntellisenseTimeout)); // Wait for all of of the timings. const all = Promise.all(timed); - all.then(r => { + all.then((r) => { this.postResponse(message, formatResponse(r)); - }).catch(_e => { + }).catch((_e) => { this.postResponse(message, formatResponse([null])); }); } @@ -539,9 +539,9 @@ export class IntellisenseProvider implements IInteractiveWindowListener { string, monacoEditor.languages.CompletionItem >(); - list.forEach(c => { + list.forEach((c) => { if (c) { - c.suggestions.forEach(s => { + c.suggestions.forEach((s) => { if (!uniqueSuggestions.has(s.insertText)) { uniqueSuggestions.set(s.insertText, s); } @@ -561,7 +561,7 @@ export class IntellisenseProvider implements IInteractiveWindowListener { this.postTimedResponse( [this.provideSignatureHelp(request.position, request.context, request.cellId, cancelSource.token)], InteractiveWindowMessages.ProvideSignatureHelpResponse, - s => { + (s) => { if (s && s[0]) { return { signatureHelp: s[0]!, requestId: request.requestId }; } else { @@ -589,8 +589,8 @@ export class IntellisenseProvider implements IInteractiveWindowListener { private convertToDocCells(cells: ICell[]): { code: string; id: string }[] { return cells - .filter(c => c.data.cell_type === 'code') - .map(c => { + .filter((c) => c.data.cell_type === 'code') + .map((c) => { return { code: concatMultilineStringInput(c.data.source), id: c.id }; }); } @@ -682,8 +682,8 @@ export class IntellisenseProvider implements IInteractiveWindowListener { if (document) { const changes = document.loadAllCells( payload.cells - .filter(c => c.data.cell_type === 'code') - .map(cell => { + .filter((c) => c.data.cell_type === 'code') + .map((cell) => { return { code: concatMultilineStringInput(cell.data.source), id: cell.id diff --git a/src/client/datascience/interactive-common/interactiveBase.ts b/src/client/datascience/interactive-common/interactiveBase.ts index 93fa9f4d2686..f69715b0ba3d 100644 --- a/src/client/datascience/interactive-common/interactiveBase.ts +++ b/src/client/datascience/interactive-common/interactiveBase.ts @@ -174,13 +174,13 @@ export abstract class InteractiveBase extends WebViewHost this.reloadAfterShutdown()); // For each listener sign up for their post events - this.listeners.forEach(l => l.postMessage(e => this.postMessageInternal(e.message, e.payload))); + this.listeners.forEach((l) => l.postMessage((e) => this.postMessageInternal(e.message, e.payload))); // Tell each listener our identity. Can't do it here though as were in the constructor for the base class setTimeout(() => { this.getNotebookIdentity() - .then(uri => - this.listeners.forEach(l => + .then((uri) => + this.listeners.forEach((l) => l.onMessage(InteractiveWindowMessages.NotebookIdentity, { resource: uri.toString() }) ) ) @@ -316,7 +316,7 @@ export abstract class InteractiveBase extends WebViewHost (this._notebook ? this._notebook.setMatplotLibStyle(d) : Promise.resolve())) + .then((d) => (this._notebook ? this._notebook.setMatplotLibStyle(d) : Promise.resolve())) .ignoreErrors(); } break; @@ -328,7 +328,7 @@ export abstract class InteractiveBase extends WebViewHost l.dispose()); + this.listeners.forEach((l) => l.dispose()); this.updateContexts(undefined); } @@ -424,7 +424,7 @@ export abstract class InteractiveBase extends WebViewHost { + return this.copyCodeInternal(args.source).catch((err) => { this.applicationShell.showErrorMessage(err); }); } @@ -582,7 +582,7 @@ export abstract class InteractiveBase extends WebViewHost { + const disposable = this._notebook.onSessionStatusChanged((e) => { if (e === ServerStatus.Busy) { sendTelemetryEvent(Telemetry.StartExecuteNotebookCellPerceivedCold, stopWatch?.elapsedTime); disposable.dispose(); @@ -606,10 +606,10 @@ export abstract class InteractiveBase extends WebViewHost c.state === CellState.error) === undefined; + result = result && cells.find((c) => c.state === CellState.error) === undefined; } }, - error => { + (error) => { status.dispose(); if (!(error instanceof CancellationError)) { this.applicationShell.showErrorMessage(error.toString()); @@ -679,7 +679,7 @@ export abstract class InteractiveBase extends WebViewHost c.id !== cell.id); + this.unfinishedCells = this.unfinishedCells.filter((c) => c.id !== cell.id); break; default: @@ -810,7 +810,7 @@ export abstract class InteractiveBase extends WebViewHost l.onMessage(message, payload)); + this.listeners.forEach((l) => l.onMessage(message, payload)); } } @@ -918,12 +918,12 @@ export abstract class InteractiveBase extends WebViewHost { + this.unfinishedCells.forEach((c) => { c.state = CellState.error; this.postMessage(InteractiveWindowMessages.FinishCell, c).ignoreErrors(); }); this.unfinishedCells = []; - this.potentiallyUnfinishedStatus.forEach(s => s.dispose()); + this.potentiallyUnfinishedStatus.forEach((s) => s.dispose()); this.potentiallyUnfinishedStatus = []; } @@ -1144,7 +1144,7 @@ export abstract class InteractiveBase extends WebViewHost { + this.gotoCodeInternal(args.file, args.line).catch((err) => { this.applicationShell.showErrorMessage(err); }); } @@ -1156,7 +1156,7 @@ export abstract class InteractiveBase extends WebViewHost te.document.fileName === file); + editor = this.documentManager.visibleTextEditors.find((te) => te.document.fileName === file); if (editor) { editor.show(); } @@ -1174,7 +1174,7 @@ export abstract class InteractiveBase extends WebViewHost e.document.languageId === PYTHON_LANGUAGE || e.document.isUntitled + (e) => e.document.languageId === PYTHON_LANGUAGE || e.document.isUntitled ); if (pythonEditors.length > 0) { @@ -1196,7 +1196,7 @@ export abstract class InteractiveBase extends WebViewHost r.range.start.line <= line && r.range.end.line >= line); + const matchingRange = ranges.find((r) => r.range.start.line <= line && r.range.end.line >= line); // If in the middle, wrap the new code if (matchingRange && matchingRange.range.start.line < line && line < editor.document.lineCount - 1) { @@ -1209,7 +1209,7 @@ export abstract class InteractiveBase extends WebViewHost { + await editor.edit((editBuilder) => { editBuilder.insert(new Position(line, 0), newCode); }); editor.revealRange(new Range(revealLine, 0, revealLine + source.split('\n').length + 3, 0)); @@ -1334,10 +1334,10 @@ export abstract class InteractiveBase extends WebViewHost { + .then((s) => { this.postMessage(InteractiveWindowMessages.LoadTmLanguageResponse, s).ignoreErrors(); }) - .catch(_e => { + .catch((_e) => { this.postMessage(InteractiveWindowMessages.LoadTmLanguageResponse).ignoreErrors(); }); } diff --git a/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts b/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts index 866cc67c8a5e..7f1161adbb99 100644 --- a/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts +++ b/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts @@ -44,8 +44,8 @@ export class InteractiveWindowMessageListener implements IWebPanelMessageListene this.interactiveWindowMessages = this.getInteractiveWindowMessages(); // We need to register callbacks for all interactive window messages. - this.interactiveWindowMessages.forEach(m => { - this.postOffice.registerCallback(m, a => callback(m, a)).ignoreErrors(); + this.interactiveWindowMessages.forEach((m) => { + this.postOffice.registerCallback(m, (a) => callback(m, a)).ignoreErrors(); }); } @@ -72,7 +72,7 @@ export class InteractiveWindowMessageListener implements IWebPanelMessageListene } private getInteractiveWindowMessages(): string[] { - return Object.keys(InteractiveWindowMessages).map(k => (InteractiveWindowMessages as any)[k].toString()); + return Object.keys(InteractiveWindowMessages).map((k) => (InteractiveWindowMessages as any)[k].toString()); } private translateHostArgs(api: vsls.LiveShare | null, role: vsls.Role, args: any[]) { @@ -83,7 +83,7 @@ export class InteractiveWindowMessageListener implements IWebPanelMessageListene // See if the trueArg has a 'file' name or not if (trueArg) { const keys = Object.keys(trueArg); - keys.forEach(k => { + keys.forEach((k) => { if (k.includes('file')) { if (typeof trueArg[k] === 'string') { // Pull out the string. We need to convert it to a file or vsls uri based on our role diff --git a/src/client/datascience/interactive-common/linkProvider.ts b/src/client/datascience/interactive-common/linkProvider.ts index a6ca7818b869..a662af22eb1f 100644 --- a/src/client/datascience/interactive-common/linkProvider.ts +++ b/src/client/datascience/interactive-common/linkProvider.ts @@ -71,7 +71,7 @@ export class LinkProvider implements IInteractiveWindowListener { saveLabel: localize.DataScience.savePngTitle(), filters: filtersObject }) - .then(f => { + .then((f) => { if (f) { const buffer = new Buffer(payload.replace('data:image/png;base64', ''), 'base64'); this.fileSystem.writeFile(f.fsPath, buffer).ignoreErrors(); @@ -100,20 +100,20 @@ export class LinkProvider implements IInteractiveWindowListener { } // Show the matching editor if there is one - let editor = this.documentManager.visibleTextEditors.find(e => + let editor = this.documentManager.visibleTextEditors.find((e) => this.fileSystem.arePathsSame(e.document.fileName, uri.fsPath) ); if (editor) { this.documentManager .showTextDocument(editor.document, { selection, viewColumn: editor.viewColumn }) - .then(e => { + .then((e) => { e.revealRange(selection, TextEditorRevealType.InCenter); }); } else { // Not a visible editor, try opening otherwise this.commandManager.executeCommand('vscode.open', uri).then(() => { // See if that opened a text document - editor = this.documentManager.visibleTextEditors.find(e => + editor = this.documentManager.visibleTextEditors.find((e) => this.fileSystem.arePathsSame(e.document.fileName, uri.fsPath) ); if (editor) { diff --git a/src/client/datascience/interactive-common/notebookProvider.ts b/src/client/datascience/interactive-common/notebookProvider.ts index 54d2f954620b..13b896d3de39 100644 --- a/src/client/datascience/interactive-common/notebookProvider.ts +++ b/src/client/datascience/interactive-common/notebookProvider.ts @@ -99,7 +99,7 @@ export class NotebookProvider implements INotebookProvider { }; promise - .then(nb => { + .then((nb) => { // If the notebook is disposed, remove from cache. nb.onDisposed(removeFromCache); this._notebookCreated.fire({ identity: options.identity, notebook: nb }); @@ -182,7 +182,7 @@ export class NotebookProvider implements INotebookProvider { enableOption, closeOption ) - .then(value => { + .then((value) => { if (value === enableOption) { sendTelemetryEvent(Telemetry.SelfCertsMessageEnabled); this.configuration @@ -262,7 +262,7 @@ export class NotebookProvider implements INotebookProvider { private async onDidCloseNotebookEditor(editor: INotebookEditor) { // First find all notebooks associated with this editor (ipynb file). const editors = this.editorProvider.editors.filter( - e => this.fs.arePathsSame(e.file.fsPath, editor.file.fsPath) && e !== editor + (e) => this.fs.arePathsSame(e.file.fsPath, editor.file.fsPath) && e !== editor ); // If we have no editors for this file, then dispose the notebook. @@ -280,8 +280,8 @@ export class NotebookProvider implements INotebookProvider { return; } - Array.from(this.notebooks.values()).forEach(promise => { - promise.then(notebook => notebook.dispose()).catch(noop); + Array.from(this.notebooks.values()).forEach((promise) => { + promise.then((notebook) => notebook.dispose()).catch(noop); }); this.notebooks.clear(); diff --git a/src/client/datascience/interactive-ipynb/autoSaveService.ts b/src/client/datascience/interactive-ipynb/autoSaveService.ts index 8651df48aa60..4735bc20a183 100644 --- a/src/client/datascience/interactive-ipynb/autoSaveService.ts +++ b/src/client/datascience/interactive-ipynb/autoSaveService.ts @@ -69,7 +69,7 @@ export class AutoSaveService implements IInteractiveWindowListener { } } public dispose(): void | undefined { - this.disposables.filter(item => !!item).forEach(item => item.dispose()); + this.disposables.filter((item) => !!item).forEach((item) => item.dispose()); this.clearTimeout(); } private onNotebookModified(_: INotebookEditor) { @@ -89,7 +89,7 @@ export class AutoSaveService implements IInteractiveWindowListener { if (!uri) { return; } - return this.notebookEditorProvider.editors.find(item => + return this.notebookEditorProvider.editors.find((item) => this.fileSystem.arePathsSame(item.file.fsPath, uri.fsPath) ); } diff --git a/src/client/datascience/interactive-ipynb/nativeEditor.ts b/src/client/datascience/interactive-ipynb/nativeEditor.ts index 488c001fb815..cdfaf880fa79 100644 --- a/src/client/datascience/interactive-ipynb/nativeEditor.ts +++ b/src/client/datascience/interactive-ipynb/nativeEditor.ts @@ -250,7 +250,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { this.postMessage(InteractiveWindowMessages.NotebookDirty).ignoreErrors(); } }) - .catch(exc => traceError('Error loading cells: ', exc)); + .catch((exc) => traceError('Error loading cells: ', exc)); } break; case InteractiveWindowMessages.Sync: @@ -370,7 +370,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { // Activate the other side, and send as if came from a file this.editorProvider .show(this.file) - .then(_v => { + .then((_v) => { this.shareMessage(InteractiveWindowMessages.RemoteAddCode, { code: info.code, file: Identifiers.EmptyFileName, @@ -401,7 +401,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { const cellsExecuting = new Set(); try { for (let i = 0; i < info.cellIds.length && !tokenSource.token.isCancellationRequested; i += 1) { - const cell = this.model.cells.find(item => item.id === info.cellIds[i]); + const cell = this.model.cells.find((item) => item.id === info.cellIds[i]); if (!cell) { continue; } @@ -419,7 +419,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { this.executeCancelTokens.delete(tokenSource); // Make sure everything is marked as finished or error after the final finished - cellsExecuting.forEach(cell => this.finishCell(cell)); + cellsExecuting.forEach((cell) => this.finishCell(cell)); } } @@ -442,11 +442,11 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { protected sendCellsToWebView(cells: ICell[]) { // Filter out sysinfo messages. Don't want to show those - const filtered = cells.filter(c => c.data.cell_type !== 'messages'); + const filtered = cells.filter((c) => c.data.cell_type !== 'messages'); // Update these cells in our storage only when cells are finished - const modified = filtered.filter(c => c.state === CellState.finished || c.state === CellState.error); - const unmodified = this.model?.cells.filter(c => modified.find(m => m.id === c.id)); + const modified = filtered.filter((c) => c.state === CellState.finished || c.state === CellState.error); + const unmodified = this.model?.cells.filter((c) => modified.find((m) => m.id === c.id)); if (modified.length > 0 && unmodified && this.model) { this.model.update({ source: 'user', @@ -544,7 +544,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { } } private interruptExecution() { - this.executeCancelTokens.forEach(t => t.cancel()); + this.executeCancelTokens.forEach((t) => t.cancel()); } private finishCell(cell: ICell) { diff --git a/src/client/datascience/interactive-ipynb/nativeEditorProvider.ts b/src/client/datascience/interactive-ipynb/nativeEditorProvider.ts index 9552bcfff8bd..9b88c0f33a41 100644 --- a/src/client/datascience/interactive-ipynb/nativeEditorProvider.ts +++ b/src/client/datascience/interactive-ipynb/nativeEditorProvider.ts @@ -49,7 +49,7 @@ export class NativeEditorProvider return this._onDidOpenNotebookEditor.event; } public get activeEditor(): INotebookEditor | undefined { - return this.editors.find(e => e.visible && e.active); + return this.editors.find((e) => e.visible && e.active); } public get editingDelegate(): CustomEditorEditingDelegate { return this; @@ -90,7 +90,7 @@ export class NativeEditorProvider // on this though. const findFilesPromise = workspace.findFiles('**/*.ipynb'); if (findFilesPromise && findFilesPromise.then) { - findFilesPromise.then(r => (this.notebookCount += r.length)); + findFilesPromise.then((r) => (this.notebookCount += r.length)); } // Register for the custom editor service. @@ -101,30 +101,30 @@ export class NativeEditorProvider } public save(document: CustomDocument, cancellation: CancellationToken): Promise { - return this.loadStorage(document.uri).then(async s => { + return this.loadStorage(document.uri).then(async (s) => { if (s) { await s.save(cancellation); } }); } public saveAs(document: CustomDocument, targetResource: Uri): Promise { - return this.loadStorage(document.uri).then(async s => { + return this.loadStorage(document.uri).then(async (s) => { if (s) { await s.saveAs(targetResource); } }); } public applyEdits(document: CustomDocument, edits: readonly NotebookModelChange[]): Promise { - return this.loadModel(document.uri).then(s => { + return this.loadModel(document.uri).then((s) => { if (s) { - edits.forEach(e => s.update({ ...e, source: 'redo' })); + edits.forEach((e) => s.update({ ...e, source: 'redo' })); } }); } public undoEdits(document: CustomDocument, edits: readonly NotebookModelChange[]): Promise { - return this.loadModel(document.uri).then(s => { + return this.loadModel(document.uri).then((s) => { if (s) { - edits.forEach(e => s.update({ ...e, source: 'undo' })); + edits.forEach((e) => s.update({ ...e, source: 'undo' })); } }); } @@ -132,7 +132,7 @@ export class NativeEditorProvider noop(); } public async backup(document: CustomDocument, cancellation: CancellationToken): Promise { - return this.loadStorage(document.uri).then(async s => { + return this.loadStorage(document.uri).then(async (s) => { if (s) { await s.backup(cancellation); } @@ -244,7 +244,7 @@ export class NativeEditorProvider this.openedEditors.delete(editor); // If last editor, dispose of the storage const key = editor.file.toString(); - if (![...this.openedEditors].find(e => e.file.toString() === key)) { + if (![...this.openedEditors].find((e) => e.file.toString() === key)) { this.storageAndModels.delete(key); } this._onDidCloseNotebookEditor.fire(editor); @@ -292,7 +292,7 @@ export class NativeEditorProvider let modelPromise = this.storageAndModels.get(key); if (!modelPromise) { const storage = this.serviceContainer.get(INotebookStorage); - modelPromise = storage.load(file, contents).then(m => { + modelPromise = storage.load(file, contents).then((m) => { if (!this.models.has(m)) { this.models.add(m); this.disposables.push(m.changed(this.modelChanged.bind(this))); @@ -307,7 +307,7 @@ export class NativeEditorProvider private async getNextNewNotebookUri(): Promise { // See if we have any untitled storage already - const untitledStorage = [...this.storageAndModels.keys()].filter(k => Uri.parse(k).scheme === 'untitled'); + const untitledStorage = [...this.storageAndModels.keys()].filter((k) => Uri.parse(k).scheme === 'untitled'); // Just use the length (don't bother trying to fill in holes). We never remove storage objects from // our map, so we'll keep creating new untitled notebooks. diff --git a/src/client/datascience/interactive-ipynb/nativeEditorProviderOld.ts b/src/client/datascience/interactive-ipynb/nativeEditorProviderOld.ts index 06354a9858f9..1ed7109a3f10 100644 --- a/src/client/datascience/interactive-ipynb/nativeEditorProviderOld.ts +++ b/src/client/datascience/interactive-ipynb/nativeEditorProviderOld.ts @@ -22,7 +22,7 @@ import { NativeEditorProvider } from './nativeEditorProvider'; @injectable() export class NativeEditorProviderOld extends NativeEditorProvider { public get activeEditor(): INotebookEditor | undefined { - const active = [...this.activeEditors.entries()].find(e => e[1].active); + const active = [...this.activeEditors.entries()].find((e) => e[1].active); if (active) { return active[1]; } @@ -82,7 +82,7 @@ export class NativeEditorProviderOld extends NativeEditorProvider { // host, so postpone till after the ctor is finished. setTimeout(() => { if (this.documentManager.textDocuments && this.documentManager.textDocuments.forEach) { - this.documentManager.textDocuments.forEach(doc => this.openNotebookAndCloseEditor(doc, false)); + this.documentManager.textDocuments.forEach((doc) => this.openNotebookAndCloseEditor(doc, false)); } }, 0); } @@ -221,7 +221,7 @@ export class NativeEditorProviderOld extends NativeEditorProvider { // Possible we have a git diff view (with two editors git and file scheme), and we open the file view // on the side (different view column). const gitSchemeEditor = this.documentManager.visibleTextEditors.find( - editorUri => + (editorUri) => editorUri.document.uri.scheme === 'git' && this.fileSystem.arePathsSame(editorUri.document.uri.fsPath, editor.document.uri.fsPath) ); @@ -232,7 +232,7 @@ export class NativeEditorProviderOld extends NativeEditorProvider { // Look for other editors with the same file name that have a scheme of file/git and same viewcolumn. const fileSchemeEditor = this.documentManager.visibleTextEditors.find( - editorUri => + (editorUri) => (editorUri.document.uri.scheme === 'file' || editorUri.document.uri.scheme === 'git') && editorUri !== gitSchemeEditor && this.fileSystem.arePathsSame(editorUri.document.uri.fsPath, editor.document.uri.fsPath) && diff --git a/src/client/datascience/interactive-ipynb/nativeEditorStorage.ts b/src/client/datascience/interactive-ipynb/nativeEditorStorage.ts index 94f46631e42e..fbfadd195ac5 100644 --- a/src/client/datascience/interactive-ipynb/nativeEditorStorage.ts +++ b/src/client/datascience/interactive-ipynb/nativeEditorStorage.ts @@ -88,10 +88,10 @@ export class NativeEditorStorage implements INotebookModel, INotebookStorage { } public async applyEdits(edits: readonly NotebookModelChange[]): Promise { - edits.forEach(e => this.update({ ...e, source: 'redo' })); + edits.forEach((e) => this.update({ ...e, source: 'redo' })); } public async undoEdits(edits: readonly NotebookModelChange[]): Promise { - edits.forEach(e => this.update({ ...e, source: 'undo' })); + edits.forEach((e) => this.update({ ...e, source: 'undo' })); } public save(): Promise { return this.saveAs(this.file); @@ -311,7 +311,7 @@ export class NativeEditorStorage implements INotebookModel, INotebookStorage { const normalized = change.text.replace(/\r/g, ''); // Figure out which cell we're editing. - const index = this.cells.findIndex(c => c.id === id); + const index = this.cells.findIndex((c) => c.id === id); if (index >= 0) { // This is an actual edit. const contents = concatMultilineStringInput(this.cells[index].data.source); @@ -333,15 +333,15 @@ export class NativeEditorStorage implements INotebookModel, INotebookStorage { private editCell(changes: IEditorContentChange[], id: string): boolean { // Apply the changes to the visible cell list if (changes && changes.length) { - return changes.map(c => this.applyCellContentChange(c, id)).reduce((p, c) => p || c, false); + return changes.map((c) => this.applyCellContentChange(c, id)).reduce((p, c) => p || c, false); } return false; } private swapCells(firstCellId: string, secondCellId: string) { - const first = this.cells.findIndex(v => v.id === firstCellId); - const second = this.cells.findIndex(v => v.id === secondCellId); + const first = this.cells.findIndex((v) => v.id === firstCellId); + const second = this.cells.findIndex((v) => v.id === secondCellId); if (first >= 0 && second >= 0 && first !== second) { const temp = { ...this.cells[first] }; this._state.cells[first] = this.asCell(this.cells[second]); @@ -353,8 +353,8 @@ export class NativeEditorStorage implements INotebookModel, INotebookStorage { private modifyCells(cells: ICell[]): boolean { // Update these cells in our list - cells.forEach(c => { - const index = this.cells.findIndex(v => v.id === c.id); + cells.forEach((c) => { + const index = this.cells.findIndex((v) => v.id === c.id); this._state.cells[index] = this.asCell(c); }); return true; @@ -362,13 +362,13 @@ export class NativeEditorStorage implements INotebookModel, INotebookStorage { private changeCellType(cell: ICell): boolean { // Update the cell in our list. - const index = this.cells.findIndex(v => v.id === cell.id); + const index = this.cells.findIndex((v) => v.id === cell.id); this._state.cells[index] = this.asCell(cell); return true; } private removeCell(cell: ICell): boolean { - const index = this.cells.findIndex(c => c.id === cell.id); + const index = this.cells.findIndex((c) => c.id === cell.id); if (index >= 0) { this._state.cells.splice(index, 1); return true; @@ -377,7 +377,7 @@ export class NativeEditorStorage implements INotebookModel, INotebookStorage { } private clearOutputs(): boolean { - const newCells = this.cells.map(c => + const newCells = this.cells.map((c) => this.asCell({ ...c, data: { ...c.data, execution_count: null, outputs: [] } }) ); const result = !fastDeepEqual(newCells, this.cells); @@ -572,7 +572,7 @@ export class NativeEditorStorage implements INotebookModel, INotebookStorage { // Reuse our original json except for the cells. const json = { ...(this._state.notebookJson as nbformat.INotebookContent), - cells: cells.map(c => this.fixupCell(c.data)) + cells: cells.map((c) => this.fixupCell(c.data)) }; return JSON.stringify(json, null, this.indentAmount); } diff --git a/src/client/datascience/interactive-window/interactiveWindow.ts b/src/client/datascience/interactive-window/interactiveWindow.ts index ecef3b4586bb..4286ad90a0ed 100644 --- a/src/client/datascience/interactive-window/interactiveWindow.ts +++ b/src/client/datascience/interactive-window/interactiveWindow.ts @@ -1,416 +1,416 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { nbformat } from '@jupyterlab/coreutils'; -import { inject, injectable, multiInject, named } from 'inversify'; -import * as path from 'path'; -import { Event, EventEmitter, Memento, Uri, ViewColumn } from 'vscode'; -import { - IApplicationShell, - ICommandManager, - IDocumentManager, - ILiveShareApi, - IWebPanelProvider, - IWorkspaceService -} from '../../common/application/types'; -import { ContextKey } from '../../common/contextKey'; -import '../../common/extensions'; -import { traceError } from '../../common/logger'; -import { IFileSystem } from '../../common/platform/types'; -import { - GLOBAL_MEMENTO, - IConfigurationService, - IDisposableRegistry, - IExperimentsManager, - IMemento, - IPersistentStateFactory, - Resource -} from '../../common/types'; -import * as localize from '../../common/utils/localize'; -import { EXTENSION_ROOT_DIR } from '../../constants'; -import { PythonInterpreter } from '../../interpreter/contracts'; -import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; -import { EditorContexts, Identifiers, Telemetry } from '../constants'; -import { InteractiveBase } from '../interactive-common/interactiveBase'; -import { - InteractiveWindowMessages, - ISubmitNewCell, - NotebookModelChange, - SysInfoReason -} from '../interactive-common/interactiveWindowTypes'; -import { KernelSwitcher } from '../jupyter/kernels/kernelSwitcher'; -import { - ICell, - ICodeCssGenerator, - IDataScienceErrorHandler, - IDataViewerProvider, - IInteractiveWindow, - IInteractiveWindowInfo, - IInteractiveWindowListener, - IInteractiveWindowProvider, - IJupyterDebugger, - IJupyterExecution, - IJupyterKernelSpec, - IJupyterVariables, - INotebookExporter, - INotebookProvider, - IStatusProvider, - IThemeFinder, - WebViewViewChangeEventArgs -} from '../types'; - -const historyReactDir = path.join(EXTENSION_ROOT_DIR, 'out', 'datascience-ui', 'notebook'); - -@injectable() -export class InteractiveWindow extends InteractiveBase implements IInteractiveWindow { - public get onDidChangeViewState(): Event { - return this._onDidChangeViewState.event; - } - public get visible(): boolean { - return this.viewState.visible; - } - public get active(): boolean { - return this.viewState.active; - } - - public get closed(): Event { - return this.closedEvent.event; - } - private _onDidChangeViewState = new EventEmitter(); - private closedEvent: EventEmitter = new EventEmitter(); - private waitingForExportCells: boolean = false; - private trackedJupyterStart: boolean = false; - private lastFile: string | undefined; - - constructor( - @multiInject(IInteractiveWindowListener) listeners: IInteractiveWindowListener[], - @inject(ILiveShareApi) liveShare: ILiveShareApi, - @inject(IApplicationShell) applicationShell: IApplicationShell, - @inject(IDocumentManager) documentManager: IDocumentManager, - @inject(IStatusProvider) statusProvider: IStatusProvider, - @inject(IWebPanelProvider) provider: IWebPanelProvider, - @inject(IDisposableRegistry) disposables: IDisposableRegistry, - @inject(ICodeCssGenerator) cssGenerator: ICodeCssGenerator, - @inject(IThemeFinder) themeFinder: IThemeFinder, - @inject(IJupyterExecution) jupyterExecution: IJupyterExecution, - @inject(IFileSystem) fileSystem: IFileSystem, - @inject(IConfigurationService) configuration: IConfigurationService, - @inject(ICommandManager) commandManager: ICommandManager, - @inject(INotebookExporter) jupyterExporter: INotebookExporter, - @inject(IWorkspaceService) workspaceService: IWorkspaceService, - @inject(IInteractiveWindowProvider) private interactiveWindowProvider: IInteractiveWindowProvider, - @inject(IDataViewerProvider) dataExplorerProvider: IDataViewerProvider, - @inject(IJupyterVariables) jupyterVariables: IJupyterVariables, - @inject(IJupyterDebugger) jupyterDebugger: IJupyterDebugger, - @inject(IDataScienceErrorHandler) errorHandler: IDataScienceErrorHandler, - @inject(IPersistentStateFactory) private readonly stateFactory: IPersistentStateFactory, - @inject(IMemento) @named(GLOBAL_MEMENTO) globalStorage: Memento, - @inject(IExperimentsManager) experimentsManager: IExperimentsManager, - @inject(KernelSwitcher) switcher: KernelSwitcher, - @inject(INotebookProvider) notebookProvider: INotebookProvider - ) { - super( - listeners, - liveShare, - applicationShell, - documentManager, - provider, - disposables, - cssGenerator, - themeFinder, - statusProvider, - jupyterExecution, - fileSystem, - configuration, - jupyterExporter, - workspaceService, - dataExplorerProvider, - jupyterVariables, - jupyterDebugger, - errorHandler, - commandManager, - globalStorage, - historyReactDir, - [ - path.join(historyReactDir, 'require.js'), - path.join(historyReactDir, 'ipywidgets.js'), - path.join(historyReactDir, 'monaco.bundle.js'), - path.join(historyReactDir, 'commons.initial.bundle.js'), - path.join(historyReactDir, 'interactiveWindow.js') - ], - localize.DataScience.historyTitle(), - ViewColumn.Two, - experimentsManager, - switcher, - notebookProvider - ); - - // Send a telemetry event to indicate window is opening - sendTelemetryEvent(Telemetry.OpenedInteractiveWindow); - - // Start the server as soon as we open - this.ensureServerAndNotebook().ignoreErrors(); - } - - public dispose() { - const promise = super.dispose(); - if (this.closedEvent) { - this.closedEvent.fire(this); - } - return promise; - } - - public addMessage(message: string): Promise { - this.addMessageImpl(message); - return Promise.resolve(); - } - - public async show(): Promise { - // When showing we have to load the web panel. Make sure - // we use the last file sent through add code. - await this.loadWebPanel(this.lastFile ? path.dirname(this.lastFile) : process.cwd()); - - // Make sure we're loaded first. InteractiveWindow doesn't makes sense without an active server. - await this.ensureServerAndNotebook(); - - // Make sure we have at least the initial sys info - await this.addSysInfo(SysInfoReason.Start); - - // Then show our web panel. - return super.show(); - } - - public async addCode(code: string, file: string, line: number): Promise { - return this.addOrDebugCode(code, file, line, false); - } - - public exportCells() { - // First ask for all cells. Set state to indicate waiting for result - this.waitingForExportCells = true; - - // Telemetry will fire when the export function is called. - this.postMessage(InteractiveWindowMessages.GetAllCells).ignoreErrors(); - } - - // tslint:disable-next-line: no-any - public onMessage(message: string, payload: any) { - super.onMessage(message, payload); - - switch (message) { - case InteractiveWindowMessages.Export: - this.handleMessage(message, payload, this.export); - break; - - case InteractiveWindowMessages.ReturnAllCells: - this.handleMessage(message, payload, this.handleReturnAllCells); - break; - - case InteractiveWindowMessages.UpdateModel: - this.handleMessage(message, payload, this.handleModelChange); - break; - - default: - break; - } - } - - public async debugCode(code: string, file: string, line: number): Promise { - let saved = true; - // Make sure the file is saved before debugging - const doc = this.documentManager.textDocuments.find(d => this.fileSystem.arePathsSame(d.fileName, file)); - if (doc && doc.isUntitled) { - // Before we start, get the list of documents - const beforeSave = [...this.documentManager.textDocuments]; - - saved = await doc.save(); - - // If that worked, we have to open the new document. It should be - // the new entry in the list - if (saved) { - const diff = this.documentManager.textDocuments.filter(f => beforeSave.indexOf(f) === -1); - if (diff && diff.length > 0) { - file = diff[0].fileName; - - // Open the new document - await this.documentManager.openTextDocument(file); - } - } - } - - // Call the internal method if we were able to save - if (saved) { - return this.addOrDebugCode(code, file, line, true); - } - - return false; - } - - @captureTelemetry(Telemetry.ExpandAll) - public expandAllCells() { - this.postMessage(InteractiveWindowMessages.ExpandAll).ignoreErrors(); - } - - @captureTelemetry(Telemetry.CollapseAll) - public collapseAllCells() { - this.postMessage(InteractiveWindowMessages.CollapseAll).ignoreErrors(); - } - - @captureTelemetry(Telemetry.ScrolledToCell) - public scrollToCell(id: string): void { - this.postMessage(InteractiveWindowMessages.ScrollToCell, { id }).ignoreErrors(); - } - - public async getOwningResource(): Promise { - if (this.lastFile) { - return Uri.file(this.lastFile); - } - const root = this.workspaceService.rootPath; - if (root) { - return Uri.file(root); - } - return undefined; - } - protected async onViewStateChanged(args: WebViewViewChangeEventArgs) { - super.onViewStateChanged(args); - this._onDidChangeViewState.fire(); - } - - @captureTelemetry(Telemetry.SubmitCellThroughInput, undefined, false) - // tslint:disable-next-line:no-any - protected submitNewCell(info: ISubmitNewCell) { - // If there's any payload, it has the code and the id - if (info && info.code && info.id) { - // Send to ourselves. - this.submitCode(info.code, Identifiers.EmptyFileName, 0, info.id).ignoreErrors(); - - // Activate the other side, and send as if came from a file - this.interactiveWindowProvider - .getOrCreateActive() - .then(_v => { - this.shareMessage(InteractiveWindowMessages.RemoteAddCode, { - code: info.code, - file: Identifiers.EmptyFileName, - line: 0, - id: info.id, - originator: this.id, - debug: false - }); - }) - .ignoreErrors(); - } - } - - protected async getNotebookMetadata(): Promise { - return undefined; - } - - protected async updateNotebookOptions( - _kernelSpec: IJupyterKernelSpec, - _interpreter: PythonInterpreter | undefined - ): Promise { - // Do nothing as this data isn't stored in our options. - } - - protected async getNotebookIdentity(): Promise { - // Always the same identity (for now) - return Uri.parse(Identifiers.InteractiveWindowIdentity); - } - - protected updateContexts(info: IInteractiveWindowInfo | undefined) { - // This should be called by the python interactive window every - // time state changes. We use this opportunity to update our - // extension contexts - if (this.commandManager && this.commandManager.executeCommand) { - const interactiveContext = new ContextKey(EditorContexts.HaveInteractive, this.commandManager); - interactiveContext.set(!this.isDisposed).catch(); - const interactiveCellsContext = new ContextKey(EditorContexts.HaveInteractiveCells, this.commandManager); - const redoableContext = new ContextKey(EditorContexts.HaveRedoableCells, this.commandManager); - const hasCellSelectedContext = new ContextKey(EditorContexts.HaveCellSelected, this.commandManager); - if (info) { - interactiveCellsContext.set(info.cellCount > 0).catch(); - redoableContext.set(info.redoCount > 0).catch(); - hasCellSelectedContext.set(info.selectedCell ? true : false).catch(); - } else { - interactiveCellsContext.set(false).catch(); - redoableContext.set(false).catch(); - hasCellSelectedContext.set(false).catch(); - } - } - } - - protected async closeBecauseOfFailure(_exc: Error): Promise { - this.dispose(); - } - protected ensureServerAndNotebook(): Promise { - // Keep track of users who have used interactive window in a worksapce folder. - // To be used if/when changing workflows related to startup of jupyter. - if (!this.trackedJupyterStart) { - this.trackedJupyterStart = true; - const store = this.stateFactory.createGlobalPersistentState('INTERACTIVE_WINDOW_USED', false); - store.updateValue(true).ignoreErrors(); - } - return super.ensureServerAndNotebook(); - } - - private async addOrDebugCode(code: string, file: string, line: number, debug: boolean): Promise { - if (this.lastFile && !this.fileSystem.arePathsSame(file, this.lastFile)) { - sendTelemetryEvent(Telemetry.NewFileForInteractiveWindow); - } - // Save the last file we ran with. - this.lastFile = file; - - // Make sure our web panel opens. - await this.show(); - - // Tell the webpanel about the new directory. - this.updateCwd(path.dirname(file)); - - // Call the internal method. - return this.submitCode(code, file, line, undefined, undefined, debug); - } - - @captureTelemetry(Telemetry.ExportNotebook, undefined, false) - // tslint:disable-next-line: no-any no-empty - private async export(cells: ICell[]) { - // Should be an array of cells - if (cells && this.applicationShell) { - // Indicate busy - this.startProgress(); - try { - const filtersKey = localize.DataScience.exportDialogFilter(); - const filtersObject: Record = {}; - filtersObject[filtersKey] = ['ipynb']; - - // Bring up the open file dialog box - const uri = await this.applicationShell.showSaveDialog({ - saveLabel: localize.DataScience.exportDialogTitle(), - filters: filtersObject - }); - if (uri) { - await this.jupyterExporter.exportToFile(cells, uri.fsPath); - } - } finally { - this.stopProgress(); - } - } - } - - private handleModelChange(update: NotebookModelChange) { - // Send telemetry for delete and delete all. We don't send telemetry for the other updates yet - if (update.source === 'user') { - if (update.kind === 'remove_all') { - sendTelemetryEvent(Telemetry.DeleteAllCells); - } else if (update.kind === 'remove') { - sendTelemetryEvent(Telemetry.DeleteCell); - } - } - } - - // tslint:disable-next-line:no-any - private handleReturnAllCells(cells: ICell[]) { - // See what we're waiting for. - if (this.waitingForExportCells) { - this.export(cells).catch(ex => traceError('Error exporting:', ex)); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { nbformat } from '@jupyterlab/coreutils'; +import { inject, injectable, multiInject, named } from 'inversify'; +import * as path from 'path'; +import { Event, EventEmitter, Memento, Uri, ViewColumn } from 'vscode'; +import { + IApplicationShell, + ICommandManager, + IDocumentManager, + ILiveShareApi, + IWebPanelProvider, + IWorkspaceService +} from '../../common/application/types'; +import { ContextKey } from '../../common/contextKey'; +import '../../common/extensions'; +import { traceError } from '../../common/logger'; +import { IFileSystem } from '../../common/platform/types'; +import { + GLOBAL_MEMENTO, + IConfigurationService, + IDisposableRegistry, + IExperimentsManager, + IMemento, + IPersistentStateFactory, + Resource +} from '../../common/types'; +import * as localize from '../../common/utils/localize'; +import { EXTENSION_ROOT_DIR } from '../../constants'; +import { PythonInterpreter } from '../../interpreter/contracts'; +import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; +import { EditorContexts, Identifiers, Telemetry } from '../constants'; +import { InteractiveBase } from '../interactive-common/interactiveBase'; +import { + InteractiveWindowMessages, + ISubmitNewCell, + NotebookModelChange, + SysInfoReason +} from '../interactive-common/interactiveWindowTypes'; +import { KernelSwitcher } from '../jupyter/kernels/kernelSwitcher'; +import { + ICell, + ICodeCssGenerator, + IDataScienceErrorHandler, + IDataViewerProvider, + IInteractiveWindow, + IInteractiveWindowInfo, + IInteractiveWindowListener, + IInteractiveWindowProvider, + IJupyterDebugger, + IJupyterExecution, + IJupyterKernelSpec, + IJupyterVariables, + INotebookExporter, + INotebookProvider, + IStatusProvider, + IThemeFinder, + WebViewViewChangeEventArgs +} from '../types'; + +const historyReactDir = path.join(EXTENSION_ROOT_DIR, 'out', 'datascience-ui', 'notebook'); + +@injectable() +export class InteractiveWindow extends InteractiveBase implements IInteractiveWindow { + public get onDidChangeViewState(): Event { + return this._onDidChangeViewState.event; + } + public get visible(): boolean { + return this.viewState.visible; + } + public get active(): boolean { + return this.viewState.active; + } + + public get closed(): Event { + return this.closedEvent.event; + } + private _onDidChangeViewState = new EventEmitter(); + private closedEvent: EventEmitter = new EventEmitter(); + private waitingForExportCells: boolean = false; + private trackedJupyterStart: boolean = false; + private lastFile: string | undefined; + + constructor( + @multiInject(IInteractiveWindowListener) listeners: IInteractiveWindowListener[], + @inject(ILiveShareApi) liveShare: ILiveShareApi, + @inject(IApplicationShell) applicationShell: IApplicationShell, + @inject(IDocumentManager) documentManager: IDocumentManager, + @inject(IStatusProvider) statusProvider: IStatusProvider, + @inject(IWebPanelProvider) provider: IWebPanelProvider, + @inject(IDisposableRegistry) disposables: IDisposableRegistry, + @inject(ICodeCssGenerator) cssGenerator: ICodeCssGenerator, + @inject(IThemeFinder) themeFinder: IThemeFinder, + @inject(IJupyterExecution) jupyterExecution: IJupyterExecution, + @inject(IFileSystem) fileSystem: IFileSystem, + @inject(IConfigurationService) configuration: IConfigurationService, + @inject(ICommandManager) commandManager: ICommandManager, + @inject(INotebookExporter) jupyterExporter: INotebookExporter, + @inject(IWorkspaceService) workspaceService: IWorkspaceService, + @inject(IInteractiveWindowProvider) private interactiveWindowProvider: IInteractiveWindowProvider, + @inject(IDataViewerProvider) dataExplorerProvider: IDataViewerProvider, + @inject(IJupyterVariables) jupyterVariables: IJupyterVariables, + @inject(IJupyterDebugger) jupyterDebugger: IJupyterDebugger, + @inject(IDataScienceErrorHandler) errorHandler: IDataScienceErrorHandler, + @inject(IPersistentStateFactory) private readonly stateFactory: IPersistentStateFactory, + @inject(IMemento) @named(GLOBAL_MEMENTO) globalStorage: Memento, + @inject(IExperimentsManager) experimentsManager: IExperimentsManager, + @inject(KernelSwitcher) switcher: KernelSwitcher, + @inject(INotebookProvider) notebookProvider: INotebookProvider + ) { + super( + listeners, + liveShare, + applicationShell, + documentManager, + provider, + disposables, + cssGenerator, + themeFinder, + statusProvider, + jupyterExecution, + fileSystem, + configuration, + jupyterExporter, + workspaceService, + dataExplorerProvider, + jupyterVariables, + jupyterDebugger, + errorHandler, + commandManager, + globalStorage, + historyReactDir, + [ + path.join(historyReactDir, 'require.js'), + path.join(historyReactDir, 'ipywidgets.js'), + path.join(historyReactDir, 'monaco.bundle.js'), + path.join(historyReactDir, 'commons.initial.bundle.js'), + path.join(historyReactDir, 'interactiveWindow.js') + ], + localize.DataScience.historyTitle(), + ViewColumn.Two, + experimentsManager, + switcher, + notebookProvider + ); + + // Send a telemetry event to indicate window is opening + sendTelemetryEvent(Telemetry.OpenedInteractiveWindow); + + // Start the server as soon as we open + this.ensureServerAndNotebook().ignoreErrors(); + } + + public dispose() { + const promise = super.dispose(); + if (this.closedEvent) { + this.closedEvent.fire(this); + } + return promise; + } + + public addMessage(message: string): Promise { + this.addMessageImpl(message); + return Promise.resolve(); + } + + public async show(): Promise { + // When showing we have to load the web panel. Make sure + // we use the last file sent through add code. + await this.loadWebPanel(this.lastFile ? path.dirname(this.lastFile) : process.cwd()); + + // Make sure we're loaded first. InteractiveWindow doesn't makes sense without an active server. + await this.ensureServerAndNotebook(); + + // Make sure we have at least the initial sys info + await this.addSysInfo(SysInfoReason.Start); + + // Then show our web panel. + return super.show(); + } + + public async addCode(code: string, file: string, line: number): Promise { + return this.addOrDebugCode(code, file, line, false); + } + + public exportCells() { + // First ask for all cells. Set state to indicate waiting for result + this.waitingForExportCells = true; + + // Telemetry will fire when the export function is called. + this.postMessage(InteractiveWindowMessages.GetAllCells).ignoreErrors(); + } + + // tslint:disable-next-line: no-any + public onMessage(message: string, payload: any) { + super.onMessage(message, payload); + + switch (message) { + case InteractiveWindowMessages.Export: + this.handleMessage(message, payload, this.export); + break; + + case InteractiveWindowMessages.ReturnAllCells: + this.handleMessage(message, payload, this.handleReturnAllCells); + break; + + case InteractiveWindowMessages.UpdateModel: + this.handleMessage(message, payload, this.handleModelChange); + break; + + default: + break; + } + } + + public async debugCode(code: string, file: string, line: number): Promise { + let saved = true; + // Make sure the file is saved before debugging + const doc = this.documentManager.textDocuments.find((d) => this.fileSystem.arePathsSame(d.fileName, file)); + if (doc && doc.isUntitled) { + // Before we start, get the list of documents + const beforeSave = [...this.documentManager.textDocuments]; + + saved = await doc.save(); + + // If that worked, we have to open the new document. It should be + // the new entry in the list + if (saved) { + const diff = this.documentManager.textDocuments.filter((f) => beforeSave.indexOf(f) === -1); + if (diff && diff.length > 0) { + file = diff[0].fileName; + + // Open the new document + await this.documentManager.openTextDocument(file); + } + } + } + + // Call the internal method if we were able to save + if (saved) { + return this.addOrDebugCode(code, file, line, true); + } + + return false; + } + + @captureTelemetry(Telemetry.ExpandAll) + public expandAllCells() { + this.postMessage(InteractiveWindowMessages.ExpandAll).ignoreErrors(); + } + + @captureTelemetry(Telemetry.CollapseAll) + public collapseAllCells() { + this.postMessage(InteractiveWindowMessages.CollapseAll).ignoreErrors(); + } + + @captureTelemetry(Telemetry.ScrolledToCell) + public scrollToCell(id: string): void { + this.postMessage(InteractiveWindowMessages.ScrollToCell, { id }).ignoreErrors(); + } + + public async getOwningResource(): Promise { + if (this.lastFile) { + return Uri.file(this.lastFile); + } + const root = this.workspaceService.rootPath; + if (root) { + return Uri.file(root); + } + return undefined; + } + protected async onViewStateChanged(args: WebViewViewChangeEventArgs) { + super.onViewStateChanged(args); + this._onDidChangeViewState.fire(); + } + + @captureTelemetry(Telemetry.SubmitCellThroughInput, undefined, false) + // tslint:disable-next-line:no-any + protected submitNewCell(info: ISubmitNewCell) { + // If there's any payload, it has the code and the id + if (info && info.code && info.id) { + // Send to ourselves. + this.submitCode(info.code, Identifiers.EmptyFileName, 0, info.id).ignoreErrors(); + + // Activate the other side, and send as if came from a file + this.interactiveWindowProvider + .getOrCreateActive() + .then((_v) => { + this.shareMessage(InteractiveWindowMessages.RemoteAddCode, { + code: info.code, + file: Identifiers.EmptyFileName, + line: 0, + id: info.id, + originator: this.id, + debug: false + }); + }) + .ignoreErrors(); + } + } + + protected async getNotebookMetadata(): Promise { + return undefined; + } + + protected async updateNotebookOptions( + _kernelSpec: IJupyterKernelSpec, + _interpreter: PythonInterpreter | undefined + ): Promise { + // Do nothing as this data isn't stored in our options. + } + + protected async getNotebookIdentity(): Promise { + // Always the same identity (for now) + return Uri.parse(Identifiers.InteractiveWindowIdentity); + } + + protected updateContexts(info: IInteractiveWindowInfo | undefined) { + // This should be called by the python interactive window every + // time state changes. We use this opportunity to update our + // extension contexts + if (this.commandManager && this.commandManager.executeCommand) { + const interactiveContext = new ContextKey(EditorContexts.HaveInteractive, this.commandManager); + interactiveContext.set(!this.isDisposed).catch(); + const interactiveCellsContext = new ContextKey(EditorContexts.HaveInteractiveCells, this.commandManager); + const redoableContext = new ContextKey(EditorContexts.HaveRedoableCells, this.commandManager); + const hasCellSelectedContext = new ContextKey(EditorContexts.HaveCellSelected, this.commandManager); + if (info) { + interactiveCellsContext.set(info.cellCount > 0).catch(); + redoableContext.set(info.redoCount > 0).catch(); + hasCellSelectedContext.set(info.selectedCell ? true : false).catch(); + } else { + interactiveCellsContext.set(false).catch(); + redoableContext.set(false).catch(); + hasCellSelectedContext.set(false).catch(); + } + } + } + + protected async closeBecauseOfFailure(_exc: Error): Promise { + this.dispose(); + } + protected ensureServerAndNotebook(): Promise { + // Keep track of users who have used interactive window in a worksapce folder. + // To be used if/when changing workflows related to startup of jupyter. + if (!this.trackedJupyterStart) { + this.trackedJupyterStart = true; + const store = this.stateFactory.createGlobalPersistentState('INTERACTIVE_WINDOW_USED', false); + store.updateValue(true).ignoreErrors(); + } + return super.ensureServerAndNotebook(); + } + + private async addOrDebugCode(code: string, file: string, line: number, debug: boolean): Promise { + if (this.lastFile && !this.fileSystem.arePathsSame(file, this.lastFile)) { + sendTelemetryEvent(Telemetry.NewFileForInteractiveWindow); + } + // Save the last file we ran with. + this.lastFile = file; + + // Make sure our web panel opens. + await this.show(); + + // Tell the webpanel about the new directory. + this.updateCwd(path.dirname(file)); + + // Call the internal method. + return this.submitCode(code, file, line, undefined, undefined, debug); + } + + @captureTelemetry(Telemetry.ExportNotebook, undefined, false) + // tslint:disable-next-line: no-any no-empty + private async export(cells: ICell[]) { + // Should be an array of cells + if (cells && this.applicationShell) { + // Indicate busy + this.startProgress(); + try { + const filtersKey = localize.DataScience.exportDialogFilter(); + const filtersObject: Record = {}; + filtersObject[filtersKey] = ['ipynb']; + + // Bring up the open file dialog box + const uri = await this.applicationShell.showSaveDialog({ + saveLabel: localize.DataScience.exportDialogTitle(), + filters: filtersObject + }); + if (uri) { + await this.jupyterExporter.exportToFile(cells, uri.fsPath); + } + } finally { + this.stopProgress(); + } + } + } + + private handleModelChange(update: NotebookModelChange) { + // Send telemetry for delete and delete all. We don't send telemetry for the other updates yet + if (update.source === 'user') { + if (update.kind === 'remove_all') { + sendTelemetryEvent(Telemetry.DeleteAllCells); + } else if (update.kind === 'remove') { + sendTelemetryEvent(Telemetry.DeleteCell); + } + } + } + + // tslint:disable-next-line:no-any + private handleReturnAllCells(cells: ICell[]) { + // See what we're waiting for. + if (this.waitingForExportCells) { + this.export(cells).catch((ex) => traceError('Error exporting:', ex)); + } + } +} diff --git a/src/client/datascience/interactive-window/interactiveWindowCommandListener.ts b/src/client/datascience/interactive-window/interactiveWindowCommandListener.ts index c7616b11e3e4..bb62cefe5a66 100644 --- a/src/client/datascience/interactive-window/interactiveWindowCommandListener.ts +++ b/src/client/datascience/interactive-window/interactiveWindowCommandListener.ts @@ -1,500 +1,500 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../common/extensions'; - -import { inject, injectable } from 'inversify'; -import * as uuid from 'uuid/v4'; -import { Position, Range, TextDocument, Uri, ViewColumn } from 'vscode'; -import { CancellationToken, CancellationTokenSource } from 'vscode-jsonrpc'; - -import { IApplicationShell, ICommandManager, IDocumentManager } from '../../common/application/types'; -import { CancellationError } from '../../common/cancellation'; -import { PYTHON_LANGUAGE } from '../../common/constants'; -import { traceError, traceInfo } from '../../common/logger'; -import { IFileSystem } from '../../common/platform/types'; -import { IConfigurationService, IDisposableRegistry } from '../../common/types'; -import * as localize from '../../common/utils/localize'; -import { captureTelemetry } from '../../telemetry'; -import { CommandSource } from '../../testing/common/constants'; -import { generateCellRangesFromDocument, generateCellsFromDocument } from '../cellFactory'; -import { Commands, Identifiers, Telemetry } from '../constants'; -import { JupyterInstallError } from '../jupyter/jupyterInstallError'; -import { - IDataScienceCommandListener, - IDataScienceErrorHandler, - IInteractiveBase, - IInteractiveWindowProvider, - IJupyterExecution, - INotebookEditorProvider, - INotebookExporter, - INotebookImporter, - INotebookServer, - IStatusProvider -} from '../types'; - -@injectable() -export class InteractiveWindowCommandListener implements IDataScienceCommandListener { - constructor( - @inject(IDisposableRegistry) private disposableRegistry: IDisposableRegistry, - @inject(IInteractiveWindowProvider) private interactiveWindowProvider: IInteractiveWindowProvider, - @inject(INotebookExporter) private jupyterExporter: INotebookExporter, - @inject(IJupyterExecution) private jupyterExecution: IJupyterExecution, - @inject(IDocumentManager) private documentManager: IDocumentManager, - @inject(IApplicationShell) private applicationShell: IApplicationShell, - @inject(IFileSystem) private fileSystem: IFileSystem, - @inject(IConfigurationService) private configuration: IConfigurationService, - @inject(IStatusProvider) private statusProvider: IStatusProvider, - @inject(INotebookImporter) private jupyterImporter: INotebookImporter, - @inject(IDataScienceErrorHandler) private dataScienceErrorHandler: IDataScienceErrorHandler, - @inject(INotebookEditorProvider) protected ipynbProvider: INotebookEditorProvider - ) {} - - public register(commandManager: ICommandManager): void { - let disposable = commandManager.registerCommand(Commands.ShowHistoryPane, () => this.showInteractiveWindow()); - this.disposableRegistry.push(disposable); - disposable = commandManager.registerCommand( - Commands.ImportNotebook, - (file?: Uri, _cmdSource: CommandSource = CommandSource.commandPalette) => { - return this.listenForErrors(() => { - if (file && file.fsPath) { - return this.importNotebookOnFile(file.fsPath); - } else { - return this.importNotebook(); - } - }); - } - ); - this.disposableRegistry.push(disposable); - disposable = commandManager.registerCommand( - Commands.ImportNotebookFile, - (file?: Uri, _cmdSource: CommandSource = CommandSource.commandPalette) => { - return this.listenForErrors(() => { - if (file && file.fsPath) { - return this.importNotebookOnFile(file.fsPath); - } else { - return this.importNotebook(); - } - }); - } - ); - this.disposableRegistry.push(disposable); - disposable = commandManager.registerCommand( - Commands.ExportFileAsNotebook, - (file?: Uri, _cmdSource: CommandSource = CommandSource.commandPalette) => { - return this.listenForErrors(() => { - if (file && file.fsPath) { - return this.exportFile(file.fsPath); - } else { - const activeEditor = this.documentManager.activeTextEditor; - if (activeEditor && activeEditor.document.languageId === PYTHON_LANGUAGE) { - return this.exportFile(activeEditor.document.fileName); - } - } - - return Promise.resolve(); - }); - } - ); - this.disposableRegistry.push(disposable); - disposable = commandManager.registerCommand( - Commands.ExportFileAndOutputAsNotebook, - (file: Uri, _cmdSource: CommandSource = CommandSource.commandPalette) => { - return this.listenForErrors(() => { - if (file && file.fsPath) { - return this.exportFileAndOutput(file.fsPath); - } else { - const activeEditor = this.documentManager.activeTextEditor; - if (activeEditor && activeEditor.document.languageId === PYTHON_LANGUAGE) { - return this.exportFileAndOutput(activeEditor.document.fileName); - } - } - return Promise.resolve(); - }); - } - ); - this.disposableRegistry.push(disposable); - this.disposableRegistry.push(commandManager.registerCommand(Commands.UndoCells, () => this.undoCells())); - this.disposableRegistry.push(commandManager.registerCommand(Commands.RedoCells, () => this.redoCells())); - this.disposableRegistry.push( - commandManager.registerCommand(Commands.RemoveAllCells, () => this.removeAllCells()) - ); - this.disposableRegistry.push( - commandManager.registerCommand(Commands.InterruptKernel, () => this.interruptKernel()) - ); - this.disposableRegistry.push( - commandManager.registerCommand(Commands.RestartKernel, () => this.restartKernel()) - ); - this.disposableRegistry.push( - commandManager.registerCommand(Commands.ExpandAllCells, () => this.expandAllCells()) - ); - this.disposableRegistry.push( - commandManager.registerCommand(Commands.CollapseAllCells, () => this.collapseAllCells()) - ); - this.disposableRegistry.push( - commandManager.registerCommand(Commands.ExportOutputAsNotebook, () => this.exportCells()) - ); - this.disposableRegistry.push( - commandManager.registerCommand(Commands.ScrollToCell, (_file: string, id: string) => this.scrollToCell(id)) - ); - } - - // tslint:disable:no-any - private async listenForErrors(promise: () => Promise): Promise { - let result: any; - try { - result = await promise(); - return result; - } catch (err) { - if (!(err instanceof CancellationError)) { - if (err.message) { - traceError(err.message); - this.applicationShell.showErrorMessage(err.message); - } else { - traceError(err.toString()); - this.applicationShell.showErrorMessage(err.toString()); - } - } else { - traceInfo('Canceled'); - } - } - return result; - } - - private showInformationMessage(message: string, question?: string): Thenable { - if (question) { - return this.applicationShell.showInformationMessage(message, question); - } else { - return this.applicationShell.showInformationMessage(message); - } - } - - @captureTelemetry(Telemetry.ExportPythonFile, undefined, false) - private async exportFile(file: string): Promise { - if (file && file.length > 0) { - // If the current file is the active editor, then generate cells from the document. - const activeEditor = this.documentManager.activeTextEditor; - if (activeEditor && this.fileSystem.arePathsSame(activeEditor.document.fileName, file)) { - const cells = generateCellsFromDocument( - activeEditor.document, - this.configuration.getSettings(activeEditor.document.uri).datascience - ); - if (cells) { - const filtersKey = localize.DataScience.exportDialogFilter(); - const filtersObject: { [name: string]: string[] } = {}; - filtersObject[filtersKey] = ['ipynb']; - - // Bring up the save file dialog box - const uri = await this.applicationShell.showSaveDialog({ - saveLabel: localize.DataScience.exportDialogTitle(), - filters: filtersObject - }); - await this.waitForStatus( - async () => { - if (uri) { - let directoryChange; - const settings = this.configuration.getSettings(activeEditor.document.uri); - if (settings.datascience.changeDirOnImportExport) { - directoryChange = uri.fsPath; - } - - const notebook = await this.jupyterExporter.translateToNotebook(cells, directoryChange); - await this.fileSystem.writeFile(uri.fsPath, JSON.stringify(notebook)); - } - }, - localize.DataScience.exportingFormat(), - file - ); - // When all done, show a notice that it completed. - if (uri && uri.fsPath) { - const openQuestion1 = localize.DataScience.exportOpenQuestion1(); - const openQuestion2 = (await this.jupyterExecution.isSpawnSupported()) - ? localize.DataScience.exportOpenQuestion() - : undefined; - const questions = [openQuestion1, ...(openQuestion2 ? [openQuestion2] : [])]; - const selection = await this.applicationShell.showInformationMessage( - localize.DataScience.exportDialogComplete().format(uri.fsPath), - ...questions - ); - if (selection === openQuestion1) { - await this.ipynbProvider.open(uri); - } - if (selection === openQuestion2) { - // If the user wants to, open the notebook they just generated. - this.jupyterExecution.spawnNotebook(uri.fsPath).ignoreErrors(); - } - } - } - } - } - } - - @captureTelemetry(Telemetry.ExportPythonFileAndOutput, undefined, false) - private async exportFileAndOutput(file: string): Promise { - if (file && file.length > 0 && (await this.jupyterExecution.isNotebookSupported())) { - // If the current file is the active editor, then generate cells from the document. - const activeEditor = this.documentManager.activeTextEditor; - if ( - activeEditor && - activeEditor.document && - this.fileSystem.arePathsSame(activeEditor.document.fileName, file) - ) { - const ranges = generateCellRangesFromDocument(activeEditor.document); - if (ranges.length > 0) { - // Ask user for path - const output = await this.showExportDialog(); - - // If that worked, we need to start a jupyter server to get our output values. - // In the future we could potentially only update changed cells. - if (output) { - // Create a cancellation source so we can cancel starting the jupyter server if necessary - const cancelSource = new CancellationTokenSource(); - - // Then wait with status that lets the user cancel - await this.waitForStatus( - () => { - try { - return this.exportCellsWithOutput( - ranges, - activeEditor.document, - output, - cancelSource.token - ); - } catch (err) { - if (!(err instanceof CancellationError)) { - this.showInformationMessage( - localize.DataScience.exportDialogFailed().format(err) - ); - } - } - return Promise.resolve(); - }, - localize.DataScience.exportingFormat(), - file, - () => { - cancelSource.cancel(); - } - ); - - // When all done, show a notice that it completed. - const openQuestion1 = localize.DataScience.exportOpenQuestion1(); - const openQuestion2 = (await this.jupyterExecution.isSpawnSupported()) - ? localize.DataScience.exportOpenQuestion() - : undefined; - const questions = [openQuestion1, ...(openQuestion2 ? [openQuestion2] : [])]; - const selection = await this.applicationShell.showInformationMessage( - localize.DataScience.exportDialogComplete().format(output), - ...questions - ); - if (selection === openQuestion1) { - await this.ipynbProvider.open(Uri.file(output)); - } - if (selection === openQuestion2) { - // If the user wants to, open the notebook they just generated. - this.jupyterExecution.spawnNotebook(output).ignoreErrors(); - } - - return Uri.file(output); - } - } - } - } else { - await this.dataScienceErrorHandler.handleError( - new JupyterInstallError( - localize.DataScience.jupyterNotSupported().format(await this.jupyterExecution.getNotebookError()), - localize.DataScience.pythonInteractiveHelpLink() - ) - ); - } - } - - private async exportCellsWithOutput( - ranges: { range: Range; title: string }[], - document: TextDocument, - file: string, - cancelToken: CancellationToken - ): Promise { - let server: INotebookServer | undefined; - try { - const settings = this.configuration.getSettings(document.uri); - const useDefaultConfig: boolean | undefined = settings.datascience.useDefaultConfigForJupyter; - - // Try starting a server. Purpose should be unique so we - // create a brand new one. - server = await this.jupyterExecution.connectToNotebookServer( - { skipUsingDefaultConfig: !useDefaultConfig, purpose: uuid(), allowUI: () => false }, - cancelToken - ); - const notebook = server - ? await server.createNotebook(undefined, Uri.parse(Identifiers.InteractiveWindowIdentity)) - : undefined; - - // If that works, then execute all of the cells. - const cells = Array.prototype.concat( - ...(await Promise.all( - ranges.map(r => { - const code = document.getText(r.range); - return notebook - ? notebook.execute(code, document.fileName, r.range.start.line, uuid(), cancelToken) - : []; - }) - )) - ); - - // Then save them to the file - let directoryChange; - if (settings.datascience.changeDirOnImportExport) { - directoryChange = file; - } - - const notebookJson = await this.jupyterExporter.translateToNotebook(cells, directoryChange); - await this.fileSystem.writeFile(file, JSON.stringify(notebookJson)); - } finally { - if (server) { - await server.dispose(); - } - } - } - - private async showExportDialog(): Promise { - const filtersKey = localize.DataScience.exportDialogFilter(); - const filtersObject: { [name: string]: string[] } = {}; - filtersObject[filtersKey] = ['ipynb']; - - // Bring up the save file dialog box - const uri = await this.applicationShell.showSaveDialog({ - saveLabel: localize.DataScience.exportDialogTitle(), - filters: filtersObject - }); - - return uri ? uri.fsPath : undefined; - } - - private undoCells() { - const interactiveWindow = this.interactiveWindowProvider.getActive(); - if (interactiveWindow) { - interactiveWindow.undoCells(); - } - } - - private redoCells() { - const interactiveWindow = this.interactiveWindowProvider.getActive(); - if (interactiveWindow) { - interactiveWindow.redoCells(); - } - } - - private removeAllCells() { - const interactiveWindow = this.interactiveWindowProvider.getActive(); - if (interactiveWindow) { - interactiveWindow.removeAllCells(); - } - } - - private interruptKernel() { - const interactiveWindow = this.interactiveWindowProvider.getActive(); - if (interactiveWindow) { - interactiveWindow.interruptKernel().ignoreErrors(); - } - } - - private restartKernel() { - const interactiveWindow = this.interactiveWindowProvider.getActive(); - if (interactiveWindow) { - interactiveWindow.restartKernel().ignoreErrors(); - } - } - - private expandAllCells() { - const interactiveWindow = this.interactiveWindowProvider.getActive(); - if (interactiveWindow) { - interactiveWindow.expandAllCells(); - } - } - - private collapseAllCells() { - const interactiveWindow = this.interactiveWindowProvider.getActive(); - if (interactiveWindow) { - interactiveWindow.collapseAllCells(); - } - } - - private exportCells() { - const interactiveWindow = this.interactiveWindowProvider.getActive(); - if (interactiveWindow) { - interactiveWindow.exportCells(); - } - } - - @captureTelemetry(Telemetry.ShowHistoryPane, undefined, false) - private async showInteractiveWindow(): Promise { - const active = await this.interactiveWindowProvider.getOrCreateActive(); - return active.show(); - } - - private waitForStatus( - promise: () => Promise, - format: string, - file?: string, - canceled?: () => void, - interactiveWindow?: IInteractiveBase - ): Promise { - const message = file ? format.format(file) : format; - return this.statusProvider.waitWithStatus(promise, message, true, undefined, canceled, interactiveWindow); - } - - @captureTelemetry(Telemetry.ImportNotebook, { scope: 'command' }, false) - private async importNotebook(): Promise { - const filtersKey = localize.DataScience.importDialogFilter(); - const filtersObject: { [name: string]: string[] } = {}; - filtersObject[filtersKey] = ['ipynb']; - - const uris = await this.applicationShell.showOpenDialog({ - openLabel: localize.DataScience.importDialogTitle(), - filters: filtersObject - }); - - if (uris && uris.length > 0) { - // Don't call the other overload as we'll end up with double telemetry. - await this.waitForStatus( - async () => { - const contents = await this.jupyterImporter.importFromFile(uris[0].fsPath); - await this.viewDocument(contents); - }, - localize.DataScience.importingFormat(), - uris[0].fsPath - ); - } - } - - @captureTelemetry(Telemetry.ImportNotebook, { scope: 'file' }, false) - private async importNotebookOnFile(file: string): Promise { - if (file && file.length > 0) { - await this.waitForStatus( - async () => { - const contents = await this.jupyterImporter.importFromFile(file); - await this.viewDocument(contents); - }, - localize.DataScience.importingFormat(), - file - ); - } - } - - private viewDocument = async (contents: string): Promise => { - const doc = await this.documentManager.openTextDocument({ language: 'python', content: contents }); - const editor = await this.documentManager.showTextDocument(doc, ViewColumn.One); - - // Edit the document so that it is dirty (add a space at the end) - editor.edit(editBuilder => { - editBuilder.insert(new Position(editor.document.lineCount, 0), '\n'); - }); - }; - - private async scrollToCell(id: string): Promise { - if (id) { - const interactiveWindow = await this.interactiveWindowProvider.getOrCreateActive(); - interactiveWindow.scrollToCell(id); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../common/extensions'; + +import { inject, injectable } from 'inversify'; +import * as uuid from 'uuid/v4'; +import { Position, Range, TextDocument, Uri, ViewColumn } from 'vscode'; +import { CancellationToken, CancellationTokenSource } from 'vscode-jsonrpc'; + +import { IApplicationShell, ICommandManager, IDocumentManager } from '../../common/application/types'; +import { CancellationError } from '../../common/cancellation'; +import { PYTHON_LANGUAGE } from '../../common/constants'; +import { traceError, traceInfo } from '../../common/logger'; +import { IFileSystem } from '../../common/platform/types'; +import { IConfigurationService, IDisposableRegistry } from '../../common/types'; +import * as localize from '../../common/utils/localize'; +import { captureTelemetry } from '../../telemetry'; +import { CommandSource } from '../../testing/common/constants'; +import { generateCellRangesFromDocument, generateCellsFromDocument } from '../cellFactory'; +import { Commands, Identifiers, Telemetry } from '../constants'; +import { JupyterInstallError } from '../jupyter/jupyterInstallError'; +import { + IDataScienceCommandListener, + IDataScienceErrorHandler, + IInteractiveBase, + IInteractiveWindowProvider, + IJupyterExecution, + INotebookEditorProvider, + INotebookExporter, + INotebookImporter, + INotebookServer, + IStatusProvider +} from '../types'; + +@injectable() +export class InteractiveWindowCommandListener implements IDataScienceCommandListener { + constructor( + @inject(IDisposableRegistry) private disposableRegistry: IDisposableRegistry, + @inject(IInteractiveWindowProvider) private interactiveWindowProvider: IInteractiveWindowProvider, + @inject(INotebookExporter) private jupyterExporter: INotebookExporter, + @inject(IJupyterExecution) private jupyterExecution: IJupyterExecution, + @inject(IDocumentManager) private documentManager: IDocumentManager, + @inject(IApplicationShell) private applicationShell: IApplicationShell, + @inject(IFileSystem) private fileSystem: IFileSystem, + @inject(IConfigurationService) private configuration: IConfigurationService, + @inject(IStatusProvider) private statusProvider: IStatusProvider, + @inject(INotebookImporter) private jupyterImporter: INotebookImporter, + @inject(IDataScienceErrorHandler) private dataScienceErrorHandler: IDataScienceErrorHandler, + @inject(INotebookEditorProvider) protected ipynbProvider: INotebookEditorProvider + ) {} + + public register(commandManager: ICommandManager): void { + let disposable = commandManager.registerCommand(Commands.ShowHistoryPane, () => this.showInteractiveWindow()); + this.disposableRegistry.push(disposable); + disposable = commandManager.registerCommand( + Commands.ImportNotebook, + (file?: Uri, _cmdSource: CommandSource = CommandSource.commandPalette) => { + return this.listenForErrors(() => { + if (file && file.fsPath) { + return this.importNotebookOnFile(file.fsPath); + } else { + return this.importNotebook(); + } + }); + } + ); + this.disposableRegistry.push(disposable); + disposable = commandManager.registerCommand( + Commands.ImportNotebookFile, + (file?: Uri, _cmdSource: CommandSource = CommandSource.commandPalette) => { + return this.listenForErrors(() => { + if (file && file.fsPath) { + return this.importNotebookOnFile(file.fsPath); + } else { + return this.importNotebook(); + } + }); + } + ); + this.disposableRegistry.push(disposable); + disposable = commandManager.registerCommand( + Commands.ExportFileAsNotebook, + (file?: Uri, _cmdSource: CommandSource = CommandSource.commandPalette) => { + return this.listenForErrors(() => { + if (file && file.fsPath) { + return this.exportFile(file.fsPath); + } else { + const activeEditor = this.documentManager.activeTextEditor; + if (activeEditor && activeEditor.document.languageId === PYTHON_LANGUAGE) { + return this.exportFile(activeEditor.document.fileName); + } + } + + return Promise.resolve(); + }); + } + ); + this.disposableRegistry.push(disposable); + disposable = commandManager.registerCommand( + Commands.ExportFileAndOutputAsNotebook, + (file: Uri, _cmdSource: CommandSource = CommandSource.commandPalette) => { + return this.listenForErrors(() => { + if (file && file.fsPath) { + return this.exportFileAndOutput(file.fsPath); + } else { + const activeEditor = this.documentManager.activeTextEditor; + if (activeEditor && activeEditor.document.languageId === PYTHON_LANGUAGE) { + return this.exportFileAndOutput(activeEditor.document.fileName); + } + } + return Promise.resolve(); + }); + } + ); + this.disposableRegistry.push(disposable); + this.disposableRegistry.push(commandManager.registerCommand(Commands.UndoCells, () => this.undoCells())); + this.disposableRegistry.push(commandManager.registerCommand(Commands.RedoCells, () => this.redoCells())); + this.disposableRegistry.push( + commandManager.registerCommand(Commands.RemoveAllCells, () => this.removeAllCells()) + ); + this.disposableRegistry.push( + commandManager.registerCommand(Commands.InterruptKernel, () => this.interruptKernel()) + ); + this.disposableRegistry.push( + commandManager.registerCommand(Commands.RestartKernel, () => this.restartKernel()) + ); + this.disposableRegistry.push( + commandManager.registerCommand(Commands.ExpandAllCells, () => this.expandAllCells()) + ); + this.disposableRegistry.push( + commandManager.registerCommand(Commands.CollapseAllCells, () => this.collapseAllCells()) + ); + this.disposableRegistry.push( + commandManager.registerCommand(Commands.ExportOutputAsNotebook, () => this.exportCells()) + ); + this.disposableRegistry.push( + commandManager.registerCommand(Commands.ScrollToCell, (_file: string, id: string) => this.scrollToCell(id)) + ); + } + + // tslint:disable:no-any + private async listenForErrors(promise: () => Promise): Promise { + let result: any; + try { + result = await promise(); + return result; + } catch (err) { + if (!(err instanceof CancellationError)) { + if (err.message) { + traceError(err.message); + this.applicationShell.showErrorMessage(err.message); + } else { + traceError(err.toString()); + this.applicationShell.showErrorMessage(err.toString()); + } + } else { + traceInfo('Canceled'); + } + } + return result; + } + + private showInformationMessage(message: string, question?: string): Thenable { + if (question) { + return this.applicationShell.showInformationMessage(message, question); + } else { + return this.applicationShell.showInformationMessage(message); + } + } + + @captureTelemetry(Telemetry.ExportPythonFile, undefined, false) + private async exportFile(file: string): Promise { + if (file && file.length > 0) { + // If the current file is the active editor, then generate cells from the document. + const activeEditor = this.documentManager.activeTextEditor; + if (activeEditor && this.fileSystem.arePathsSame(activeEditor.document.fileName, file)) { + const cells = generateCellsFromDocument( + activeEditor.document, + this.configuration.getSettings(activeEditor.document.uri).datascience + ); + if (cells) { + const filtersKey = localize.DataScience.exportDialogFilter(); + const filtersObject: { [name: string]: string[] } = {}; + filtersObject[filtersKey] = ['ipynb']; + + // Bring up the save file dialog box + const uri = await this.applicationShell.showSaveDialog({ + saveLabel: localize.DataScience.exportDialogTitle(), + filters: filtersObject + }); + await this.waitForStatus( + async () => { + if (uri) { + let directoryChange; + const settings = this.configuration.getSettings(activeEditor.document.uri); + if (settings.datascience.changeDirOnImportExport) { + directoryChange = uri.fsPath; + } + + const notebook = await this.jupyterExporter.translateToNotebook(cells, directoryChange); + await this.fileSystem.writeFile(uri.fsPath, JSON.stringify(notebook)); + } + }, + localize.DataScience.exportingFormat(), + file + ); + // When all done, show a notice that it completed. + if (uri && uri.fsPath) { + const openQuestion1 = localize.DataScience.exportOpenQuestion1(); + const openQuestion2 = (await this.jupyterExecution.isSpawnSupported()) + ? localize.DataScience.exportOpenQuestion() + : undefined; + const questions = [openQuestion1, ...(openQuestion2 ? [openQuestion2] : [])]; + const selection = await this.applicationShell.showInformationMessage( + localize.DataScience.exportDialogComplete().format(uri.fsPath), + ...questions + ); + if (selection === openQuestion1) { + await this.ipynbProvider.open(uri); + } + if (selection === openQuestion2) { + // If the user wants to, open the notebook they just generated. + this.jupyterExecution.spawnNotebook(uri.fsPath).ignoreErrors(); + } + } + } + } + } + } + + @captureTelemetry(Telemetry.ExportPythonFileAndOutput, undefined, false) + private async exportFileAndOutput(file: string): Promise { + if (file && file.length > 0 && (await this.jupyterExecution.isNotebookSupported())) { + // If the current file is the active editor, then generate cells from the document. + const activeEditor = this.documentManager.activeTextEditor; + if ( + activeEditor && + activeEditor.document && + this.fileSystem.arePathsSame(activeEditor.document.fileName, file) + ) { + const ranges = generateCellRangesFromDocument(activeEditor.document); + if (ranges.length > 0) { + // Ask user for path + const output = await this.showExportDialog(); + + // If that worked, we need to start a jupyter server to get our output values. + // In the future we could potentially only update changed cells. + if (output) { + // Create a cancellation source so we can cancel starting the jupyter server if necessary + const cancelSource = new CancellationTokenSource(); + + // Then wait with status that lets the user cancel + await this.waitForStatus( + () => { + try { + return this.exportCellsWithOutput( + ranges, + activeEditor.document, + output, + cancelSource.token + ); + } catch (err) { + if (!(err instanceof CancellationError)) { + this.showInformationMessage( + localize.DataScience.exportDialogFailed().format(err) + ); + } + } + return Promise.resolve(); + }, + localize.DataScience.exportingFormat(), + file, + () => { + cancelSource.cancel(); + } + ); + + // When all done, show a notice that it completed. + const openQuestion1 = localize.DataScience.exportOpenQuestion1(); + const openQuestion2 = (await this.jupyterExecution.isSpawnSupported()) + ? localize.DataScience.exportOpenQuestion() + : undefined; + const questions = [openQuestion1, ...(openQuestion2 ? [openQuestion2] : [])]; + const selection = await this.applicationShell.showInformationMessage( + localize.DataScience.exportDialogComplete().format(output), + ...questions + ); + if (selection === openQuestion1) { + await this.ipynbProvider.open(Uri.file(output)); + } + if (selection === openQuestion2) { + // If the user wants to, open the notebook they just generated. + this.jupyterExecution.spawnNotebook(output).ignoreErrors(); + } + + return Uri.file(output); + } + } + } + } else { + await this.dataScienceErrorHandler.handleError( + new JupyterInstallError( + localize.DataScience.jupyterNotSupported().format(await this.jupyterExecution.getNotebookError()), + localize.DataScience.pythonInteractiveHelpLink() + ) + ); + } + } + + private async exportCellsWithOutput( + ranges: { range: Range; title: string }[], + document: TextDocument, + file: string, + cancelToken: CancellationToken + ): Promise { + let server: INotebookServer | undefined; + try { + const settings = this.configuration.getSettings(document.uri); + const useDefaultConfig: boolean | undefined = settings.datascience.useDefaultConfigForJupyter; + + // Try starting a server. Purpose should be unique so we + // create a brand new one. + server = await this.jupyterExecution.connectToNotebookServer( + { skipUsingDefaultConfig: !useDefaultConfig, purpose: uuid(), allowUI: () => false }, + cancelToken + ); + const notebook = server + ? await server.createNotebook(undefined, Uri.parse(Identifiers.InteractiveWindowIdentity)) + : undefined; + + // If that works, then execute all of the cells. + const cells = Array.prototype.concat( + ...(await Promise.all( + ranges.map((r) => { + const code = document.getText(r.range); + return notebook + ? notebook.execute(code, document.fileName, r.range.start.line, uuid(), cancelToken) + : []; + }) + )) + ); + + // Then save them to the file + let directoryChange; + if (settings.datascience.changeDirOnImportExport) { + directoryChange = file; + } + + const notebookJson = await this.jupyterExporter.translateToNotebook(cells, directoryChange); + await this.fileSystem.writeFile(file, JSON.stringify(notebookJson)); + } finally { + if (server) { + await server.dispose(); + } + } + } + + private async showExportDialog(): Promise { + const filtersKey = localize.DataScience.exportDialogFilter(); + const filtersObject: { [name: string]: string[] } = {}; + filtersObject[filtersKey] = ['ipynb']; + + // Bring up the save file dialog box + const uri = await this.applicationShell.showSaveDialog({ + saveLabel: localize.DataScience.exportDialogTitle(), + filters: filtersObject + }); + + return uri ? uri.fsPath : undefined; + } + + private undoCells() { + const interactiveWindow = this.interactiveWindowProvider.getActive(); + if (interactiveWindow) { + interactiveWindow.undoCells(); + } + } + + private redoCells() { + const interactiveWindow = this.interactiveWindowProvider.getActive(); + if (interactiveWindow) { + interactiveWindow.redoCells(); + } + } + + private removeAllCells() { + const interactiveWindow = this.interactiveWindowProvider.getActive(); + if (interactiveWindow) { + interactiveWindow.removeAllCells(); + } + } + + private interruptKernel() { + const interactiveWindow = this.interactiveWindowProvider.getActive(); + if (interactiveWindow) { + interactiveWindow.interruptKernel().ignoreErrors(); + } + } + + private restartKernel() { + const interactiveWindow = this.interactiveWindowProvider.getActive(); + if (interactiveWindow) { + interactiveWindow.restartKernel().ignoreErrors(); + } + } + + private expandAllCells() { + const interactiveWindow = this.interactiveWindowProvider.getActive(); + if (interactiveWindow) { + interactiveWindow.expandAllCells(); + } + } + + private collapseAllCells() { + const interactiveWindow = this.interactiveWindowProvider.getActive(); + if (interactiveWindow) { + interactiveWindow.collapseAllCells(); + } + } + + private exportCells() { + const interactiveWindow = this.interactiveWindowProvider.getActive(); + if (interactiveWindow) { + interactiveWindow.exportCells(); + } + } + + @captureTelemetry(Telemetry.ShowHistoryPane, undefined, false) + private async showInteractiveWindow(): Promise { + const active = await this.interactiveWindowProvider.getOrCreateActive(); + return active.show(); + } + + private waitForStatus( + promise: () => Promise, + format: string, + file?: string, + canceled?: () => void, + interactiveWindow?: IInteractiveBase + ): Promise { + const message = file ? format.format(file) : format; + return this.statusProvider.waitWithStatus(promise, message, true, undefined, canceled, interactiveWindow); + } + + @captureTelemetry(Telemetry.ImportNotebook, { scope: 'command' }, false) + private async importNotebook(): Promise { + const filtersKey = localize.DataScience.importDialogFilter(); + const filtersObject: { [name: string]: string[] } = {}; + filtersObject[filtersKey] = ['ipynb']; + + const uris = await this.applicationShell.showOpenDialog({ + openLabel: localize.DataScience.importDialogTitle(), + filters: filtersObject + }); + + if (uris && uris.length > 0) { + // Don't call the other overload as we'll end up with double telemetry. + await this.waitForStatus( + async () => { + const contents = await this.jupyterImporter.importFromFile(uris[0].fsPath); + await this.viewDocument(contents); + }, + localize.DataScience.importingFormat(), + uris[0].fsPath + ); + } + } + + @captureTelemetry(Telemetry.ImportNotebook, { scope: 'file' }, false) + private async importNotebookOnFile(file: string): Promise { + if (file && file.length > 0) { + await this.waitForStatus( + async () => { + const contents = await this.jupyterImporter.importFromFile(file); + await this.viewDocument(contents); + }, + localize.DataScience.importingFormat(), + file + ); + } + } + + private viewDocument = async (contents: string): Promise => { + const doc = await this.documentManager.openTextDocument({ language: 'python', content: contents }); + const editor = await this.documentManager.showTextDocument(doc, ViewColumn.One); + + // Edit the document so that it is dirty (add a space at the end) + editor.edit((editBuilder) => { + editBuilder.insert(new Position(editor.document.lineCount, 0), '\n'); + }); + }; + + private async scrollToCell(id: string): Promise { + if (id) { + const interactiveWindow = await this.interactiveWindowProvider.getOrCreateActive(); + interactiveWindow.scrollToCell(id); + } + } +} diff --git a/src/client/datascience/interactive-window/interactiveWindowProvider.ts b/src/client/datascience/interactive-window/interactiveWindowProvider.ts index 1cd39466079a..22e53e7675ea 100644 --- a/src/client/datascience/interactive-window/interactiveWindowProvider.ts +++ b/src/client/datascience/interactive-window/interactiveWindowProvider.ts @@ -45,7 +45,7 @@ export class InteractiveWindowProvider implements IInteractiveWindowProvider, IA this.postOffice = new PostOffice(LiveShare.InteractiveWindowProviderService, liveShare); // Listen for peer changes - this.postOffice.peerCountChanged(n => this.onPeerCountChanged(n)); + this.postOffice.peerCountChanged((n) => this.onPeerCountChanged(n)); // Listen for messages so we force a create on both sides. this.postOffice @@ -114,7 +114,7 @@ export class InteractiveWindowProvider implements IInteractiveWindowProvider, IA private onPeerCountChanged(newCount: number) { // If we're losing peers, resolve all syncs if (newCount < this.postOffice.peerCount) { - this.pendingSyncs.forEach(v => v.waitable.resolve()); + this.pendingSyncs.forEach((v) => v.waitable.resolve()); this.pendingSyncs.clear(); } } diff --git a/src/client/datascience/ipywidgets/ipyWidgetMessageDispatcher.ts b/src/client/datascience/ipywidgets/ipyWidgetMessageDispatcher.ts index 136c388b1acf..7cdd275f8959 100644 --- a/src/client/datascience/ipywidgets/ipyWidgetMessageDispatcher.ts +++ b/src/client/datascience/ipywidgets/ipyWidgetMessageDispatcher.ts @@ -98,7 +98,7 @@ export class IPyWidgetMessageDispatcher implements IIPyWidgetMessageDispatcher { ); const requestId = payload.requestId; future.done - .then(reply => { + .then((reply) => { this.raisePostMessage(IPyWidgetMessages.IPyWidgets_ShellSend_resolve, { requestId, msg: reply @@ -106,7 +106,7 @@ export class IPyWidgetMessageDispatcher implements IIPyWidgetMessageDispatcher { this.pendingShellMessages.delete(requestId); future.dispose(); }) - .catch(ex => { + .catch((ex) => { this.raisePostMessage(IPyWidgetMessages.IPyWidgets_ShellSend_reject, { requestId, msg: ex }); }); future.onIOPub = (msg: KernelMessage.IIOPubMessage) => { @@ -217,7 +217,7 @@ export class IPyWidgetMessageDispatcher implements IIPyWidgetMessageDispatcher { return; } // Ensure we re-register the comm targets. - Array.from(this.commTargetsRegistered.keys()).forEach(targetName => { + Array.from(this.commTargetsRegistered.keys()).forEach((targetName) => { this.commTargetsRegistered.delete(targetName); this.pendingTargetNames.add(targetName); }); diff --git a/src/client/datascience/ipywidgets/ipyWidgetMessageDispatcherFactory.ts b/src/client/datascience/ipywidgets/ipyWidgetMessageDispatcherFactory.ts index ee285301608d..4193b4362b5c 100644 --- a/src/client/datascience/ipywidgets/ipyWidgetMessageDispatcherFactory.ts +++ b/src/client/datascience/ipywidgets/ipyWidgetMessageDispatcherFactory.ts @@ -44,7 +44,7 @@ class IPyWidgetMessageDispatcherWithOldMessages implements IIPyWidgetMessageDisp private raisePostMessage(message: IPyWidgetMessage) { // Send all of the old messages the notebook may not have received. // Also send them in the same order. - this.oldMessages.forEach(oldMessage => { + this.oldMessages.forEach((oldMessage) => { this._postMessageEmitter.fire(oldMessage); }); this.oldMessages = []; @@ -84,10 +84,10 @@ export class IPyWidgetMessageDispatcherFactory implements IDisposable { @inject(IDisposableRegistry) disposables: IDisposableRegistry ) { disposables.push(this); - notebookProvider.onNotebookCreated(e => this.trackDisposingOfNotebook(e.notebook), this, this.disposables); + notebookProvider.onNotebookCreated((e) => this.trackDisposingOfNotebook(e.notebook), this, this.disposables); - notebookProvider.activeNotebooks.forEach(nbPromise => - nbPromise.then(notebook => this.trackDisposingOfNotebook(notebook)).ignoreErrors() + notebookProvider.activeNotebooks.forEach((nbPromise) => + nbPromise.then((notebook) => this.trackDisposingOfNotebook(notebook)).ignoreErrors() ); } diff --git a/src/client/datascience/ipywidgets/ipywidgetHandler.ts b/src/client/datascience/ipywidgets/ipywidgetHandler.ts index 26f5408af9a7..c8eb5f242da1 100644 --- a/src/client/datascience/ipywidgets/ipywidgetHandler.ts +++ b/src/client/datascience/ipywidgets/ipywidgetHandler.ts @@ -42,7 +42,7 @@ export class IPyWidgetHandler implements IInteractiveWindowListener { @inject(IPyWidgetMessageDispatcherFactory) private readonly factory: IPyWidgetMessageDispatcherFactory ) { disposables.push( - notebookProvider.onNotebookCreated(async e => { + notebookProvider.onNotebookCreated(async (e) => { if (e.identity.toString() === this.notebookIdentity?.toString()) { await this.initialize(); } @@ -57,7 +57,7 @@ export class IPyWidgetHandler implements IInteractiveWindowListener { // tslint:disable-next-line: no-any public onMessage(message: string, payload?: any): void { if (message === InteractiveWindowMessages.NotebookIdentity) { - this.saveIdentity(payload).catch(ex => traceError('Failed to initialize ipywidgetHandler', ex)); + this.saveIdentity(payload).catch((ex) => traceError('Failed to initialize ipywidgetHandler', ex)); } else if (this.ipywidgetMessages.includes(message)) { // Need a temp variable so SONAR doesn't flip out. const ipywidgetMulticaster = this.getIPyWidgetMulticaster(); @@ -80,7 +80,7 @@ export class IPyWidgetHandler implements IInteractiveWindowListener { const ipywidgetMulticaster = this.getIPyWidgetMulticaster(); this.disposables.push( - ipywidgetMulticaster!.postMessage(msg => { + ipywidgetMulticaster!.postMessage((msg) => { this.postEmitter.fire(msg); }) ); diff --git a/src/client/datascience/jupyter/interpreter/jupyterCommand.ts b/src/client/datascience/jupyter/interpreter/jupyterCommand.ts index 342da28034a4..6531a7b9abb1 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterCommand.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterCommand.ts @@ -1,344 +1,337 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { SpawnOptions } from 'child_process'; -import { inject, injectable } from 'inversify'; -import * as path from 'path'; -import { traceError } from '../../../common/logger'; -import { - ExecutionResult, - IProcessService, - IProcessServiceFactory, - IPythonExecutionFactory, - IPythonExecutionService, - ObservableExecutionResult -} from '../../../common/process/types'; -import { EXTENSION_ROOT_DIR } from '../../../constants'; -import { IEnvironmentActivationService } from '../../../interpreter/activation/types'; -import { IInterpreterService, PythonInterpreter } from '../../../interpreter/contracts'; -import { JupyterCommands, PythonDaemonModule } from '../../constants'; -import { IJupyterCommand, IJupyterCommandFactory } from '../../types'; - -// JupyterCommand objects represent some process that can be launched that should be guaranteed to work because it -// was found by testing it previously -class ProcessJupyterCommand implements IJupyterCommand { - private exe: string; - private requiredArgs: string[]; - private launcherPromise: Promise; - private interpreterPromise: Promise; - private activationHelper: IEnvironmentActivationService; - - constructor( - exe: string, - args: string[], - processServiceFactory: IProcessServiceFactory, - activationHelper: IEnvironmentActivationService, - interpreterService: IInterpreterService - ) { - this.exe = exe; - this.requiredArgs = args; - this.launcherPromise = processServiceFactory.create(); - this.activationHelper = activationHelper; - this.interpreterPromise = interpreterService.getInterpreterDetails(this.exe).catch(_e => undefined); - } - - public interpreter(): Promise { - return this.interpreterPromise; - } - - public async execObservable(args: string[], options: SpawnOptions): Promise> { - const newOptions = { ...options }; - newOptions.env = await this.fixupEnv(newOptions.env); - const launcher = await this.launcherPromise; - const newArgs = [...this.requiredArgs, ...args]; - return launcher.execObservable(this.exe, newArgs, newOptions); - } - - public async exec(args: string[], options: SpawnOptions): Promise> { - const newOptions = { ...options }; - newOptions.env = await this.fixupEnv(newOptions.env); - const launcher = await this.launcherPromise; - const newArgs = [...this.requiredArgs, ...args]; - return launcher.exec(this.exe, newArgs, newOptions); - } - - private fixupEnv(_env?: NodeJS.ProcessEnv): Promise { - if (this.activationHelper) { - return this.activationHelper.getActivatedEnvironmentVariables(undefined); - } - - return Promise.resolve(process.env); - } -} - -class InterpreterJupyterCommand implements IJupyterCommand { - protected interpreterPromise: Promise; - private pythonLauncher: Promise; - - constructor( - protected readonly moduleName: string, - protected args: string[], - protected readonly pythonExecutionFactory: IPythonExecutionFactory, - private readonly _interpreter: PythonInterpreter, - isActiveInterpreter: boolean - ) { - this.interpreterPromise = Promise.resolve(this._interpreter); - this.pythonLauncher = this.interpreterPromise.then(async interpreter => { - // Create a daemon only if the interpreter is the same as the current interpreter. - // We don't want too many daemons (we don't want one for each of the users interpreter on their machine). - if (isActiveInterpreter) { - const svc = await pythonExecutionFactory.createDaemon({ - daemonModule: PythonDaemonModule, - pythonPath: interpreter!.path - }); - - // If we're using this command to start notebook, then ensure the daemon can start a notebook inside it. - if ( - (moduleName.toLowerCase() === 'jupyter' && - args - .join(' ') - .toLowerCase() - .startsWith('-m jupyter notebook')) || - (moduleName.toLowerCase() === 'notebook' && - args - .join(' ') - .toLowerCase() - .startsWith('-m notebook')) - ) { - try { - const output = await svc.exec( - [ - path.join( - EXTENSION_ROOT_DIR, - 'pythonFiles', - 'vscode_datascience_helpers', - 'jupyter_nbInstalled.py' - ) - ], - {} - ); - if (output.stdout.toLowerCase().includes('available')) { - return svc; - } - } catch (ex) { - traceError('Checking whether notebook is importable failed', ex); - } - } - } - return pythonExecutionFactory.createActivatedEnvironment({ - interpreter: this._interpreter, - bypassCondaExecution: true - }); - }); - } - public interpreter(): Promise { - return this.interpreterPromise; - } - - public async execObservable(args: string[], options: SpawnOptions): Promise> { - const newOptions = { ...options, extraVariables: { PYTHONWARNINGS: 'ignore' } }; - const launcher = await this.pythonLauncher; - const newArgs = [...this.args, ...args]; - const moduleName = newArgs[1]; - newArgs.shift(); // Remove '-m' - newArgs.shift(); // Remove module name - return launcher.execModuleObservable(moduleName, newArgs, newOptions); - } - - public async exec(args: string[], options: SpawnOptions): Promise> { - const newOptions = { ...options, extraVariables: { PYTHONWARNINGS: 'ignore' } }; - const launcher = await this.pythonLauncher; - const newArgs = [...this.args, ...args]; - const moduleName = newArgs[1]; - newArgs.shift(); // Remove '-m' - newArgs.shift(); // Remove module name - return launcher.execModule(moduleName, newArgs, newOptions); - } -} - -/** - * This class is used to launch the notebook. - * I.e. anything to do with the command `python -m jupyter notebook` or `python -m notebook`. - * - * @class InterpreterJupyterNotebookCommand - * @implements {IJupyterCommand} - */ -export class InterpreterJupyterNotebookCommand extends InterpreterJupyterCommand { - constructor( - moduleName: string, - args: string[], - pythonExecutionFactory: IPythonExecutionFactory, - interpreter: PythonInterpreter, - isActiveInterpreter: boolean - ) { - super(moduleName, args, pythonExecutionFactory, interpreter, isActiveInterpreter); - } -} - -/** - * This class is used to handle kernelspecs. - * I.e. anything to do with the command `python -m jupyter kernelspec`. - * - * @class InterpreterJupyterKernelSpecCommand - * @implements {IJupyterCommand} - */ -// tslint:disable-next-line: max-classes-per-file -export class InterpreterJupyterKernelSpecCommand extends InterpreterJupyterCommand { - constructor( - moduleName: string, - args: string[], - pythonExecutionFactory: IPythonExecutionFactory, - interpreter: PythonInterpreter, - isActiveInterpreter: boolean - ) { - super(moduleName, args, pythonExecutionFactory, interpreter, isActiveInterpreter); - } - - /** - * Kernelspec subcommand requires special treatment. - * Its possible the sub command hasn't been registered (i.e. jupyter kernelspec command hasn't been installed). - * However its possible the kernlspec modules are available. - * So here's what we have: - * - python -m jupyter kernelspec --version (throws an error, as kernelspect sub command not installed) - * - `import jupyter_client.kernelspec` (works, hence kernelspec modules are available) - * - Problem is daemon will say that `kernelspec` is avaiable, as daemon can work with the `jupyter_client.kernelspec`. - * But rest of extension will assume kernelspec is available and `python -m jupyter kenerlspec --version` will fall over. - * Solution: - * - Run using daemon wrapper code if possible (we don't know whether daemon or python process will run kernel spec). - * - Now, its possible the python daemon process is busy in which case we fall back (in daemon wrapper) to using a python process to run the code. - * - However `python -m jupyter kernelspec` will fall over (as such a sub command hasn't been installed), hence calling daemon code will fail. - * - What we do in such an instance is run the python code `python xyz.py` to deal with kernels. - * If that works, great. - * If that fails, then we know that `kernelspec` sub command doesn't exist and `import jupyter_client.kernelspec` also doesn't work. - * In such a case re-throw the exception from the first execution (possibly the daemon wrapper). - * @param {string[]} args - * @param {SpawnOptions} options - * @returns {Promise>} - * @memberof InterpreterJupyterKernelSpecCommand - */ - public async exec(args: string[], options: SpawnOptions): Promise> { - let exception: Error | undefined; - let output: ExecutionResult = { stdout: '' }; - try { - output = await super.exec(args, options); - } catch (ex) { - exception = ex; - } - - if (!output.stderr && !exception) { - return output; - } - - const defaultAction = () => { - if (exception) { - traceError(`Exception attempting to enumerate kernelspecs: `, exception); - throw exception; - } - return output; - }; - - // We're only interested in `python -m jupyter kernelspec` - const interpreter = await this.interpreter(); - if ( - !interpreter || - this.moduleName.toLowerCase() !== 'jupyter' || - this.args.join(' ').toLowerCase() !== `-m jupyter ${JupyterCommands.KernelSpecCommand}`.toLowerCase() - ) { - return defaultAction(); - } - - // Otherwise try running a script instead. - try { - if (args.join(' ').toLowerCase() === 'list --json') { - // Try getting kernels using python script, if that fails (even if there's output in stderr) rethrow original exception. - output = await this.getKernelSpecList(interpreter, options); - return output; - } else if (args.join(' ').toLowerCase() === '--version') { - // Try getting kernelspec version using python script, if that fails (even if there's output in stderr) rethrow original exception. - output = await this.getKernelSpecVersion(interpreter, options); - return output; - } - } catch (innerEx) { - traceError('Failed to get a list of the kernelspec using python script', innerEx); - } - return defaultAction(); - } - - private async getKernelSpecList(interpreter: PythonInterpreter, options: SpawnOptions) { - // Try getting kernels using python script, if that fails (even if there's output in stderr) rethrow original exception. - const activatedEnv = await this.pythonExecutionFactory.createActivatedEnvironment({ - interpreter, - bypassCondaExecution: true - }); - return activatedEnv.exec( - [path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'vscode_datascience_helpers', 'getJupyterKernels.py')], - { ...options, throwOnStdErr: true } - ); - } - private async getKernelSpecVersion(interpreter: PythonInterpreter, options: SpawnOptions) { - // Try getting kernels using python script, if that fails (even if there's output in stderr) rethrow original exception. - const activatedEnv = await this.pythonExecutionFactory.createActivatedEnvironment({ - interpreter, - bypassCondaExecution: true - }); - return activatedEnv.exec( - [ - path.join( - EXTENSION_ROOT_DIR, - 'pythonFiles', - 'vscode_datascience_helpers', - 'getJupyterKernelspecVersion.py' - ) - ], - { ...options, throwOnStdErr: true } - ); - } -} - -// tslint:disable-next-line: max-classes-per-file -@injectable() -export class JupyterCommandFactory implements IJupyterCommandFactory { - constructor( - @inject(IPythonExecutionFactory) private readonly executionFactory: IPythonExecutionFactory, - @inject(IEnvironmentActivationService) private readonly activationHelper: IEnvironmentActivationService, - @inject(IProcessServiceFactory) private readonly processServiceFactory: IProcessServiceFactory, - @inject(IInterpreterService) private readonly interpreterService: IInterpreterService - ) {} - - public createInterpreterCommand( - command: JupyterCommands, - moduleName: string, - args: string[], - interpreter: PythonInterpreter, - isActiveInterpreter: boolean - ): IJupyterCommand { - if (command === JupyterCommands.NotebookCommand) { - return new InterpreterJupyterNotebookCommand( - moduleName, - args, - this.executionFactory, - interpreter, - isActiveInterpreter - ); - } else if (command === JupyterCommands.KernelSpecCommand) { - return new InterpreterJupyterKernelSpecCommand( - moduleName, - args, - this.executionFactory, - interpreter, - isActiveInterpreter - ); - } - return new InterpreterJupyterCommand(moduleName, args, this.executionFactory, interpreter, isActiveInterpreter); - } - - public createProcessCommand(exe: string, args: string[]): IJupyterCommand { - return new ProcessJupyterCommand( - exe, - args, - this.processServiceFactory, - this.activationHelper, - this.interpreterService - ); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { SpawnOptions } from 'child_process'; +import { inject, injectable } from 'inversify'; +import * as path from 'path'; +import { traceError } from '../../../common/logger'; +import { + ExecutionResult, + IProcessService, + IProcessServiceFactory, + IPythonExecutionFactory, + IPythonExecutionService, + ObservableExecutionResult +} from '../../../common/process/types'; +import { EXTENSION_ROOT_DIR } from '../../../constants'; +import { IEnvironmentActivationService } from '../../../interpreter/activation/types'; +import { IInterpreterService, PythonInterpreter } from '../../../interpreter/contracts'; +import { JupyterCommands, PythonDaemonModule } from '../../constants'; +import { IJupyterCommand, IJupyterCommandFactory } from '../../types'; + +// JupyterCommand objects represent some process that can be launched that should be guaranteed to work because it +// was found by testing it previously +class ProcessJupyterCommand implements IJupyterCommand { + private exe: string; + private requiredArgs: string[]; + private launcherPromise: Promise; + private interpreterPromise: Promise; + private activationHelper: IEnvironmentActivationService; + + constructor( + exe: string, + args: string[], + processServiceFactory: IProcessServiceFactory, + activationHelper: IEnvironmentActivationService, + interpreterService: IInterpreterService + ) { + this.exe = exe; + this.requiredArgs = args; + this.launcherPromise = processServiceFactory.create(); + this.activationHelper = activationHelper; + this.interpreterPromise = interpreterService.getInterpreterDetails(this.exe).catch((_e) => undefined); + } + + public interpreter(): Promise { + return this.interpreterPromise; + } + + public async execObservable(args: string[], options: SpawnOptions): Promise> { + const newOptions = { ...options }; + newOptions.env = await this.fixupEnv(newOptions.env); + const launcher = await this.launcherPromise; + const newArgs = [...this.requiredArgs, ...args]; + return launcher.execObservable(this.exe, newArgs, newOptions); + } + + public async exec(args: string[], options: SpawnOptions): Promise> { + const newOptions = { ...options }; + newOptions.env = await this.fixupEnv(newOptions.env); + const launcher = await this.launcherPromise; + const newArgs = [...this.requiredArgs, ...args]; + return launcher.exec(this.exe, newArgs, newOptions); + } + + private fixupEnv(_env?: NodeJS.ProcessEnv): Promise { + if (this.activationHelper) { + return this.activationHelper.getActivatedEnvironmentVariables(undefined); + } + + return Promise.resolve(process.env); + } +} + +class InterpreterJupyterCommand implements IJupyterCommand { + protected interpreterPromise: Promise; + private pythonLauncher: Promise; + + constructor( + protected readonly moduleName: string, + protected args: string[], + protected readonly pythonExecutionFactory: IPythonExecutionFactory, + private readonly _interpreter: PythonInterpreter, + isActiveInterpreter: boolean + ) { + this.interpreterPromise = Promise.resolve(this._interpreter); + this.pythonLauncher = this.interpreterPromise.then(async (interpreter) => { + // Create a daemon only if the interpreter is the same as the current interpreter. + // We don't want too many daemons (we don't want one for each of the users interpreter on their machine). + if (isActiveInterpreter) { + const svc = await pythonExecutionFactory.createDaemon({ + daemonModule: PythonDaemonModule, + pythonPath: interpreter!.path + }); + + // If we're using this command to start notebook, then ensure the daemon can start a notebook inside it. + if ( + (moduleName.toLowerCase() === 'jupyter' && + args.join(' ').toLowerCase().startsWith('-m jupyter notebook')) || + (moduleName.toLowerCase() === 'notebook' && args.join(' ').toLowerCase().startsWith('-m notebook')) + ) { + try { + const output = await svc.exec( + [ + path.join( + EXTENSION_ROOT_DIR, + 'pythonFiles', + 'vscode_datascience_helpers', + 'jupyter_nbInstalled.py' + ) + ], + {} + ); + if (output.stdout.toLowerCase().includes('available')) { + return svc; + } + } catch (ex) { + traceError('Checking whether notebook is importable failed', ex); + } + } + } + return pythonExecutionFactory.createActivatedEnvironment({ + interpreter: this._interpreter, + bypassCondaExecution: true + }); + }); + } + public interpreter(): Promise { + return this.interpreterPromise; + } + + public async execObservable(args: string[], options: SpawnOptions): Promise> { + const newOptions = { ...options, extraVariables: { PYTHONWARNINGS: 'ignore' } }; + const launcher = await this.pythonLauncher; + const newArgs = [...this.args, ...args]; + const moduleName = newArgs[1]; + newArgs.shift(); // Remove '-m' + newArgs.shift(); // Remove module name + return launcher.execModuleObservable(moduleName, newArgs, newOptions); + } + + public async exec(args: string[], options: SpawnOptions): Promise> { + const newOptions = { ...options, extraVariables: { PYTHONWARNINGS: 'ignore' } }; + const launcher = await this.pythonLauncher; + const newArgs = [...this.args, ...args]; + const moduleName = newArgs[1]; + newArgs.shift(); // Remove '-m' + newArgs.shift(); // Remove module name + return launcher.execModule(moduleName, newArgs, newOptions); + } +} + +/** + * This class is used to launch the notebook. + * I.e. anything to do with the command `python -m jupyter notebook` or `python -m notebook`. + * + * @class InterpreterJupyterNotebookCommand + * @implements {IJupyterCommand} + */ +export class InterpreterJupyterNotebookCommand extends InterpreterJupyterCommand { + constructor( + moduleName: string, + args: string[], + pythonExecutionFactory: IPythonExecutionFactory, + interpreter: PythonInterpreter, + isActiveInterpreter: boolean + ) { + super(moduleName, args, pythonExecutionFactory, interpreter, isActiveInterpreter); + } +} + +/** + * This class is used to handle kernelspecs. + * I.e. anything to do with the command `python -m jupyter kernelspec`. + * + * @class InterpreterJupyterKernelSpecCommand + * @implements {IJupyterCommand} + */ +// tslint:disable-next-line: max-classes-per-file +export class InterpreterJupyterKernelSpecCommand extends InterpreterJupyterCommand { + constructor( + moduleName: string, + args: string[], + pythonExecutionFactory: IPythonExecutionFactory, + interpreter: PythonInterpreter, + isActiveInterpreter: boolean + ) { + super(moduleName, args, pythonExecutionFactory, interpreter, isActiveInterpreter); + } + + /** + * Kernelspec subcommand requires special treatment. + * Its possible the sub command hasn't been registered (i.e. jupyter kernelspec command hasn't been installed). + * However its possible the kernlspec modules are available. + * So here's what we have: + * - python -m jupyter kernelspec --version (throws an error, as kernelspect sub command not installed) + * - `import jupyter_client.kernelspec` (works, hence kernelspec modules are available) + * - Problem is daemon will say that `kernelspec` is avaiable, as daemon can work with the `jupyter_client.kernelspec`. + * But rest of extension will assume kernelspec is available and `python -m jupyter kenerlspec --version` will fall over. + * Solution: + * - Run using daemon wrapper code if possible (we don't know whether daemon or python process will run kernel spec). + * - Now, its possible the python daemon process is busy in which case we fall back (in daemon wrapper) to using a python process to run the code. + * - However `python -m jupyter kernelspec` will fall over (as such a sub command hasn't been installed), hence calling daemon code will fail. + * - What we do in such an instance is run the python code `python xyz.py` to deal with kernels. + * If that works, great. + * If that fails, then we know that `kernelspec` sub command doesn't exist and `import jupyter_client.kernelspec` also doesn't work. + * In such a case re-throw the exception from the first execution (possibly the daemon wrapper). + * @param {string[]} args + * @param {SpawnOptions} options + * @returns {Promise>} + * @memberof InterpreterJupyterKernelSpecCommand + */ + public async exec(args: string[], options: SpawnOptions): Promise> { + let exception: Error | undefined; + let output: ExecutionResult = { stdout: '' }; + try { + output = await super.exec(args, options); + } catch (ex) { + exception = ex; + } + + if (!output.stderr && !exception) { + return output; + } + + const defaultAction = () => { + if (exception) { + traceError(`Exception attempting to enumerate kernelspecs: `, exception); + throw exception; + } + return output; + }; + + // We're only interested in `python -m jupyter kernelspec` + const interpreter = await this.interpreter(); + if ( + !interpreter || + this.moduleName.toLowerCase() !== 'jupyter' || + this.args.join(' ').toLowerCase() !== `-m jupyter ${JupyterCommands.KernelSpecCommand}`.toLowerCase() + ) { + return defaultAction(); + } + + // Otherwise try running a script instead. + try { + if (args.join(' ').toLowerCase() === 'list --json') { + // Try getting kernels using python script, if that fails (even if there's output in stderr) rethrow original exception. + output = await this.getKernelSpecList(interpreter, options); + return output; + } else if (args.join(' ').toLowerCase() === '--version') { + // Try getting kernelspec version using python script, if that fails (even if there's output in stderr) rethrow original exception. + output = await this.getKernelSpecVersion(interpreter, options); + return output; + } + } catch (innerEx) { + traceError('Failed to get a list of the kernelspec using python script', innerEx); + } + return defaultAction(); + } + + private async getKernelSpecList(interpreter: PythonInterpreter, options: SpawnOptions) { + // Try getting kernels using python script, if that fails (even if there's output in stderr) rethrow original exception. + const activatedEnv = await this.pythonExecutionFactory.createActivatedEnvironment({ + interpreter, + bypassCondaExecution: true + }); + return activatedEnv.exec( + [path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'vscode_datascience_helpers', 'getJupyterKernels.py')], + { ...options, throwOnStdErr: true } + ); + } + private async getKernelSpecVersion(interpreter: PythonInterpreter, options: SpawnOptions) { + // Try getting kernels using python script, if that fails (even if there's output in stderr) rethrow original exception. + const activatedEnv = await this.pythonExecutionFactory.createActivatedEnvironment({ + interpreter, + bypassCondaExecution: true + }); + return activatedEnv.exec( + [ + path.join( + EXTENSION_ROOT_DIR, + 'pythonFiles', + 'vscode_datascience_helpers', + 'getJupyterKernelspecVersion.py' + ) + ], + { ...options, throwOnStdErr: true } + ); + } +} + +// tslint:disable-next-line: max-classes-per-file +@injectable() +export class JupyterCommandFactory implements IJupyterCommandFactory { + constructor( + @inject(IPythonExecutionFactory) private readonly executionFactory: IPythonExecutionFactory, + @inject(IEnvironmentActivationService) private readonly activationHelper: IEnvironmentActivationService, + @inject(IProcessServiceFactory) private readonly processServiceFactory: IProcessServiceFactory, + @inject(IInterpreterService) private readonly interpreterService: IInterpreterService + ) {} + + public createInterpreterCommand( + command: JupyterCommands, + moduleName: string, + args: string[], + interpreter: PythonInterpreter, + isActiveInterpreter: boolean + ): IJupyterCommand { + if (command === JupyterCommands.NotebookCommand) { + return new InterpreterJupyterNotebookCommand( + moduleName, + args, + this.executionFactory, + interpreter, + isActiveInterpreter + ); + } else if (command === JupyterCommands.KernelSpecCommand) { + return new InterpreterJupyterKernelSpecCommand( + moduleName, + args, + this.executionFactory, + interpreter, + isActiveInterpreter + ); + } + return new InterpreterJupyterCommand(moduleName, args, this.executionFactory, interpreter, isActiveInterpreter); + } + + public createProcessCommand(exe: string, args: string[]): IJupyterCommand { + return new ProcessJupyterCommand( + exe, + args, + this.processServiceFactory, + this.activationHelper, + this.interpreterService + ); + } +} diff --git a/src/client/datascience/jupyter/interpreter/jupyterCommandFinder.ts b/src/client/datascience/jupyter/interpreter/jupyterCommandFinder.ts index 9f7c809aad02..9ec8eac4d6c2 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterCommandFinder.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterCommandFinder.ts @@ -83,7 +83,7 @@ export class JupyterCommandFinderImpl { this.processServicePromise = this.processServiceFactory.create(); disposableRegistry.push(this.interpreterService.onDidChangeInterpreter(async () => this.clearCache())); if (workspace) { - const disposable = workspace.onDidChangeConfiguration(async e => { + const disposable = workspace.onDidChangeConfiguration(async (e) => { if (e.affectsConfiguration('python.dataScience.searchForJupyter', undefined)) { // When config changes happen, recreate our commands. await this.clearCache(); @@ -193,7 +193,7 @@ export class JupyterCommandFinderImpl { private async lookForJupyterInDirectory(pathToCheck: string): Promise { try { const files = await this.fileSystem.getFiles(pathToCheck); - return files ? files.filter(s => RegExpValues.CheckJupyterRegEx.test(path.basename(s))) : []; + return files ? files.filter((s) => RegExpValues.CheckJupyterRegEx.test(path.basename(s))) : []; } catch (err) { traceWarning('Python Extension (fileSystem.getFiles):', err); } @@ -369,7 +369,9 @@ export class JupyterCommandFinderImpl { cancelAction: 'resolve', token: cancelToken }); - const promises = all.filter(i => i !== current).map(i => this.findInterpreterCommand(command, i, cancelToken)); + const promises = all + .filter((i) => i !== current) + .map((i) => this.findInterpreterCommand(command, i, cancelToken)); const foundList = await Promise.race([Promise.all(promises), cancelFind]); if (isCommandFinderCancelled(command, cancelToken)) { @@ -415,7 +417,7 @@ export class JupyterCommandFinderImpl { } } else { // Just pick the first one - found = foundList.find(f => f.status !== ModuleExistsStatus.NotFound) || found; + found = foundList.find((f) => f.status !== ModuleExistsStatus.NotFound) || found; } return found; @@ -591,11 +593,11 @@ export class JupyterCommandFinder extends JupyterCommandFinderImpl { // Otherwise wrap the result so we can check for a failure. this.findNotebookCommandPromise = createDeferred(); return this.findBestNotebookCommand(token) - .then(r => { + .then((r) => { this.findNotebookCommandPromise?.resolve(r); return r; }) - .catch(e => { + .catch((e) => { this.findNotebookCommandPromise?.reject(e); throw e; }); @@ -620,8 +622,8 @@ export class JupyterCommandFinder extends JupyterCommandFinderImpl { const cancellationTokenSource = new CancellationTokenSource(); const wrappedToken = wrapCancellationTokens(token, cancellationTokenSource.token); - const searchPromise = super.findBestCommand(command, wrappedToken).then(cmd => ({ cmd, source: 'search' })); - const cachePromise = this.getCachedNotebookInterpreter(wrappedToken).then(cmd => ({ cmd, source: 'cache' })); + const searchPromise = super.findBestCommand(command, wrappedToken).then((cmd) => ({ cmd, source: 'search' })); + const cachePromise = this.getCachedNotebookInterpreter(wrappedToken).then((cmd) => ({ cmd, source: 'cache' })); // Take which ever comes first. // Searching cache will certainly be faster, use that. @@ -661,7 +663,7 @@ export class JupyterCommandFinder extends JupyterCommandFinderImpl { // Cache for other VS Code sessions. // i.e. make it available when new VS Code instance is opened. - new Promise(async resolve => { + new Promise(async (resolve) => { try { const interpreter = await result.command!.interpreter(); if (!interpreter || (cancelToken && cancelToken.isCancellationRequested)) { diff --git a/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts b/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts index d7b0cb24beaf..94e883a67a69 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts @@ -36,7 +36,7 @@ export class JupyterCommandInterpreterDependencyService implements IJupyterInter const installers = await this.channels.getInstallationChannels(); if (installers) { // If Conda is available, always pick it as the user must have a Conda Environment - const installer = installers.find(ins => ins.name === 'Conda'); + const installer = installers.find((ins) => ins.name === 'Conda'); const product = ProductNames.get(Product.jupyter); const stopWatch = new StopWatch(); @@ -46,7 +46,7 @@ export class JupyterCommandInterpreterDependencyService implements IJupyterInter .then(() => { sendTelemetryEvent(Telemetry.UserInstalledJupyter, stopWatch.elapsedTime); }) - .catch(e => { + .catch((e) => { sendTelemetryEvent(Telemetry.JupyterInstallFailed, undefined, { product }); this.applicationShell.showErrorMessage(e.message, DataScience.pythonInteractiveHelpLink()); }); @@ -56,7 +56,7 @@ export class JupyterCommandInterpreterDependencyService implements IJupyterInter .then(() => { sendTelemetryEvent(Telemetry.UserInstalledJupyter, stopWatch.elapsedTime); }) - .catch(e => { + .catch((e) => { sendTelemetryEvent(Telemetry.JupyterInstallFailed, undefined, { product }); this.applicationShell.showErrorMessage(e.message, DataScience.pythonInteractiveHelpLink()); }); diff --git a/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterExecutionService.ts b/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterExecutionService.ts index cf9e70ec7a54..4eebcf73b1d2 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterExecutionService.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterExecutionService.ts @@ -128,7 +128,7 @@ export class JupyterCommandFinderInterpreterExecutionService implements IJupyter : [file, '--to', 'python', '--stdout']; return convert.command .exec(args, { throwOnStdErr: false, encoding: 'utf8', token }) - .then(output => output.stdout); + .then((output) => output.stdout); } public async openNotebook(notebookFile: string): Promise { // First we find a way to start a notebook server @@ -162,7 +162,7 @@ export class JupyterCommandFinderInterpreterExecutionService implements IJupyter encoding: 'utf8' }); - return parseKernelSpecs(output.stdout, this.fs, token).catch(parserError => { + return parseKernelSpecs(output.stdout, this.fs, token).catch((parserError) => { traceError('Failed to parse kernelspecs', parserError); // This is failing for some folks. In that case return nothing return []; diff --git a/src/client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService.ts b/src/client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService.ts index d89cd5416dc3..b70806d049e7 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService.ts @@ -60,9 +60,9 @@ function sortProductsInOrderForInstallation(products: Product[]) { export function getMessageForLibrariesNotInstalled(products: Product[], interpreterName?: string): string { // Even though kernelspec cannot be installed, display it so user knows what is missing. const names = products - .map(product => ProductNames.get(product)) - .filter(name => !!name) - .map(name => name as string); + .map((product) => ProductNames.get(product)) + .filter((name) => !!name) + .map((name) => name as string); switch (names.length) { case 0: @@ -160,7 +160,7 @@ export class JupyterInterpreterDependencyService { if (missingProducts.includes(Product.kernelspec) && !missingProducts.includes(Product.jupyter)) { missingProducts.push(Product.jupyter); } - const productsToInstall = missingProducts.filter(product => product !== Product.kernelspec); + const productsToInstall = missingProducts.filter((product) => product !== Product.kernelspec); // Install jupyter, then notebook, then others in that order. sortProductsInOrderForInstallation(productsToInstall); @@ -214,7 +214,7 @@ export class JupyterInterpreterDependencyService { * @memberof JupyterInterpreterConfigurationService */ public async areDependenciesInstalled(interpreter: PythonInterpreter, token?: CancellationToken): Promise { - return this.getDependenciesNotInstalled(interpreter, token).then(items => items.length === 0); + return this.getDependenciesNotInstalled(interpreter, token).then((items) => items.length === 0); } /** @@ -230,7 +230,7 @@ export class JupyterInterpreterDependencyService { if (this.nbconvertInstalledInInterpreter.has(interpreter.path)) { return true; } - const installed = this.installer.isInstalled(Product.nbconvert, interpreter).then(result => result === true); + const installed = this.installer.isInstalled(Product.nbconvert, interpreter).then((result) => result === true); if (installed) { this.nbconvertInstalledInInterpreter.add(interpreter.path); } @@ -259,10 +259,10 @@ export class JupyterInterpreterDependencyService { Promise.all([ this.installer .isInstalled(Product.jupyter, interpreter) - .then(installed => (installed ? noop() : notInstalled.push(Product.jupyter))), + .then((installed) => (installed ? noop() : notInstalled.push(Product.jupyter))), this.installer .isInstalled(Product.notebook, interpreter) - .then(installed => (installed ? noop() : notInstalled.push(Product.notebook))) + .then((installed) => (installed ? noop() : notInstalled.push(Product.notebook))) ]), createPromiseFromCancellation({ cancelAction: 'resolve', defaultValue: undefined, token }) ]); @@ -274,7 +274,7 @@ export class JupyterInterpreterDependencyService { return []; } // Perform this check only if jupyter & notebook modules are installed. - const products = await this.isKernelSpecAvailable(interpreter, token).then(installed => + const products = await this.isKernelSpecAvailable(interpreter, token).then((installed) => installed ? [] : [Product.kernelspec] ); if (products.length === 0) { @@ -303,7 +303,7 @@ export class JupyterInterpreterDependencyService { return command .exec(['--version'], { throwOnStdErr: true }) .then(() => true) - .catch(e => { + .catch((e) => { traceError(`Kernel spec not found: `, e); sendTelemetryEvent(Telemetry.KernelSpecNotFound); return false; diff --git a/src/client/datascience/jupyter/interpreter/jupyterInterpreterService.ts b/src/client/datascience/jupyter/interpreter/jupyterInterpreterService.ts index bff29827eb28..f4e8b9a83d0c 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterInterpreterService.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterInterpreterService.ts @@ -59,7 +59,7 @@ export class JupyterInterpreterService { // with a valid jupyter interpreter public async setInitialInterpreter(token?: CancellationToken): Promise { if (!this.getInitialInterpreterPromise) { - this.getInitialInterpreterPromise = this.getInitialInterpreterImpl(token).then(result => { + this.getInitialInterpreterPromise = this.getInitialInterpreterImpl(token).then((result) => { // Set ourselves as a valid interpreter if we found something if (result) { this.changeSelectedInterpreterProperty(result); diff --git a/src/client/datascience/jupyter/interpreter/jupyterInterpreterSubCommandExecutionService.ts b/src/client/datascience/jupyter/interpreter/jupyterInterpreterSubCommandExecutionService.ts index 350ddab38a5c..ddea8d2fbf62 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterInterpreterSubCommandExecutionService.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterInterpreterSubCommandExecutionService.ts @@ -157,7 +157,7 @@ export class JupyterInterpreterSubCommandExecutionService // stdout contains the generated python code. return daemon .execModule('jupyter', ['nbconvert'].concat(args), { throwOnStdErr: false, encoding: 'utf8', token }) - .then(output => output.stdout); + .then((output) => output.stdout); } public async openNotebook(notebookFile: string): Promise { const interpreter = await this.getSelectedInterpreterAndThrowIfNotAvailable(); @@ -190,8 +190,8 @@ export class JupyterInterpreterSubCommandExecutionService // Ask for our current list. const stdoutFromDaemonPromise = await daemon .execModule('jupyter', ['kernelspec', 'list', '--json'], spawnOptions) - .then(output => output.stdout) - .catch(daemonEx => { + .then((output) => output.stdout) + .catch((daemonEx) => { sendTelemetryEvent(Telemetry.KernelSpecNotFound); traceError('Failed to list kernels from daemon', daemonEx); return ''; @@ -209,8 +209,8 @@ export class JupyterInterpreterSubCommandExecutionService ], spawnOptions ) - .then(output => output.stdout) - .catch(fileEx => { + .then((output) => output.stdout) + .catch((fileEx) => { traceError('Failed to list kernels from getJupyterKernels.py', fileEx); return ''; }); @@ -220,7 +220,7 @@ export class JupyterInterpreterSubCommandExecutionService stdoutFromFileExecPromise ]); - return parseKernelSpecs(stdoutFromDaemon || stdoutFromFileExec, this.fs, token).catch(parserError => { + return parseKernelSpecs(stdoutFromDaemon || stdoutFromFileExec, this.fs, token).catch((parserError) => { traceError('Failed to parse kernelspecs', parserError); // This is failing for some folks. In that case return nothing return []; diff --git a/src/client/datascience/jupyter/jupyterCellOutputMimeTypeTracker.ts b/src/client/datascience/jupyter/jupyterCellOutputMimeTypeTracker.ts index 85d1157c43f8..d8be8d2add24 100644 --- a/src/client/datascience/jupyter/jupyterCellOutputMimeTypeTracker.ts +++ b/src/client/datascience/jupyter/jupyterCellOutputMimeTypeTracker.ts @@ -18,7 +18,7 @@ export class CellOutputMimeTypeTracker implements IExtensionSingleActivationServ private sentMimeTypes: Set = new Set(); constructor(@inject(INotebookEditorProvider) private notebookEditorProvider: INotebookEditorProvider) { - this.notebookEditorProvider.onDidOpenNotebookEditor(t => this.onOpenedOrClosedNotebook(t)); + this.notebookEditorProvider.onDidOpenNotebookEditor((t) => this.onOpenedOrClosedNotebook(t)); } public async preExecute(_cell: ICell, _silent: boolean): Promise { // Do nothing on pre execute @@ -30,7 +30,7 @@ export class CellOutputMimeTypeTracker implements IExtensionSingleActivationServ } public async activate(): Promise { // Act like all of our open documents just opened; our timeout will make sure this is delayed. - this.notebookEditorProvider.editors.forEach(e => this.onOpenedOrClosedNotebook(e)); + this.notebookEditorProvider.editors.forEach((e) => this.onOpenedOrClosedNotebook(e)); } private onOpenedOrClosedNotebook(e: INotebookEditor) { @@ -116,9 +116,7 @@ export class CellOutputMimeTypeTracker implements IExtensionSingleActivationServ this.sentMimeTypes.add(mimeType); // Hash the package name so that we will never accidentally see a // user's private package name. - const hashedName = sha256() - .update(mimeType) - .digest('hex'); + const hashedName = sha256().update(mimeType).digest('hex'); const lowerMimeType = mimeType.toLowerCase(); // The following gives us clues of the mimetype. diff --git a/src/client/datascience/jupyter/jupyterConnectError.ts b/src/client/datascience/jupyter/jupyterConnectError.ts index a024ebefd31f..9cd33eae3a3b 100644 --- a/src/client/datascience/jupyter/jupyterConnectError.ts +++ b/src/client/datascience/jupyter/jupyterConnectError.ts @@ -1,10 +1,10 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../common/extensions'; - -export class JupyterConnectError extends Error { - constructor(message: string, stderr?: string) { - super(message + (stderr ? `\n${stderr}` : '')); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../common/extensions'; + +export class JupyterConnectError extends Error { + constructor(message: string, stderr?: string) { + super(message + (stderr ? `\n${stderr}` : '')); + } +} diff --git a/src/client/datascience/jupyter/jupyterConnection.ts b/src/client/datascience/jupyter/jupyterConnection.ts index 254288202279..304840e9023f 100644 --- a/src/client/datascience/jupyter/jupyterConnection.ts +++ b/src/client/datascience/jupyter/jupyterConnection.ts @@ -1,249 +1,249 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../common/extensions'; - -import { ChildProcess } from 'child_process'; -import { CancellationToken, Disposable, Event, EventEmitter } from 'vscode'; -import { Cancellation, CancellationError } from '../../common/cancellation'; -import { traceInfo, traceWarning } from '../../common/logger'; -import { IFileSystem } from '../../common/platform/types'; -import { ObservableExecutionResult, Output } from '../../common/process/types'; -import { IConfigurationService, IDisposable } from '../../common/types'; -import { createDeferred, Deferred } from '../../common/utils/async'; -import * as localize from '../../common/utils/localize'; -import { IServiceContainer } from '../../ioc/types'; -import { RegExpValues } from '../constants'; -import { IConnection } from '../types'; -import { JupyterConnectError } from './jupyterConnectError'; - -// tslint:disable-next-line:no-require-imports no-var-requires no-any -const namedRegexp = require('named-js-regexp'); -const urlMatcher = namedRegexp(RegExpValues.UrlPatternRegEx); - -export type JupyterServerInfo = { - base_url: string; - notebook_dir: string; - hostname: string; - password: boolean; - pid: number; - port: number; - secure: boolean; - token: string; - url: string; -}; - -export class JupyterConnectionWaiter implements IDisposable { - private startPromise: Deferred; - private launchTimeout: NodeJS.Timer | number; - private configService: IConfigurationService; - private fileSystem: IFileSystem; - private stderr: string[] = []; - private connectionDisposed = false; - - constructor( - private readonly launchResult: ObservableExecutionResult, - private readonly notebookDir: string, - private readonly getServerInfo: (cancelToken?: CancellationToken) => Promise, - serviceContainer: IServiceContainer, - private readonly cancelToken?: CancellationToken - ) { - this.configService = serviceContainer.get(IConfigurationService); - this.fileSystem = serviceContainer.get(IFileSystem); - - // Cancel our start promise if a cancellation occurs - if (cancelToken) { - cancelToken.onCancellationRequested(() => this.startPromise.reject(new CancellationError())); - } - - // Setup our start promise - this.startPromise = createDeferred(); - - // We want to reject our Jupyter connection after a specific timeout - const settings = this.configService.getSettings(undefined); - const jupyterLaunchTimeout = settings.datascience.jupyterLaunchTimeout; - - this.launchTimeout = setTimeout(() => { - this.launchTimedOut(); - }, jupyterLaunchTimeout); - - // Listen for crashes - let exitCode = '0'; - if (launchResult.proc) { - launchResult.proc.on('exit', c => (exitCode = c ? c.toString() : '0')); - } - let stderr = ''; - // Listen on stderr for its connection information - launchResult.out.subscribe( - (output: Output) => { - if (output.source === 'stderr') { - stderr += output.out; - this.stderr.push(output.out); - this.extractConnectionInformation(stderr); - } else { - this.output(output.out); - } - }, - e => this.rejectStartPromise(e.message), - // If the process dies, we can't extract connection information. - () => this.rejectStartPromise(localize.DataScience.jupyterServerCrashed().format(exitCode)) - ); - } - public dispose() { - // tslint:disable-next-line: no-any - clearTimeout(this.launchTimeout as any); - } - - public waitForConnection(): Promise { - return this.startPromise.promise; - } - - private createConnection(baseUrl: string, token: string, hostName: string, processDisposable: Disposable) { - // tslint:disable-next-line: no-use-before-declare - return new JupyterConnection(baseUrl, token, hostName, processDisposable, this.launchResult.proc); - } - - // tslint:disable-next-line:no-any - private output = (data: any) => { - if (!this.connectionDisposed) { - traceInfo(data.toString('utf8')); - } - }; - - // From a list of jupyter server infos try to find the matching jupyter that we launched - // tslint:disable-next-line:no-any - private getJupyterURL(serverInfos: JupyterServerInfo[] | undefined, data: any) { - if (serverInfos && serverInfos.length > 0 && !this.startPromise.completed) { - const matchInfo = serverInfos.find(info => - this.fileSystem.arePathsSame(this.notebookDir, info.notebook_dir) - ); - if (matchInfo) { - const url = matchInfo.url; - const token = matchInfo.token; - const host = matchInfo.hostname; - this.resolveStartPromise(url, token, host); - } - } - // At this point we failed to get the server info or a matching server via the python code, so fall back to - // our URL parse - if (!this.startPromise.completed) { - this.getJupyterURLFromString(data); - } - } - - // tslint:disable-next-line:no-any - private getJupyterURLFromString(data: any) { - // tslint:disable-next-line:no-any - const urlMatch = urlMatcher.exec(data) as any; - const groups = urlMatch.groups() as RegExpValues.IUrlPatternGroupType; - if (urlMatch && !this.startPromise.completed && groups && (groups.LOCAL || groups.IP)) { - // Rebuild the URI from our group hits - const host = groups.LOCAL ? groups.LOCAL : groups.IP; - const uriString = `${groups.PREFIX}${host}${groups.REST}`; - - // URL is not being found for some reason. Pull it in forcefully - // tslint:disable-next-line:no-require-imports - const URL = require('url').URL; - let url: URL; - try { - url = new URL(uriString); - } catch (err) { - // Failed to parse the url either via server infos or the string - this.rejectStartPromise(localize.DataScience.jupyterLaunchNoURL()); - return; - } - - // Here we parsed the URL correctly - this.resolveStartPromise( - `${url.protocol}//${url.host}${url.pathname}`, - `${url.searchParams.get('token')}`, - url.hostname - ); - } - } - - // tslint:disable-next-line:no-any - private extractConnectionInformation = (data: any) => { - this.output(data); - - const httpMatch = RegExpValues.HttpPattern.exec(data); - - if (httpMatch && this.notebookDir && this.startPromise && !this.startPromise.completed && this.getServerInfo) { - // .then so that we can keep from pushing aync up to the subscribed observable function - this.getServerInfo(this.cancelToken) - .then(serverInfos => this.getJupyterURL(serverInfos, data)) - .catch(ex => traceWarning('Failed to get server info', ex)); - } - - // Sometimes jupyter will return a 403 error. Not sure why. We used - // to fail on this, but it looks like jupyter works with this error in place. - }; - - private launchTimedOut = () => { - if (!this.startPromise.completed) { - this.rejectStartPromise(localize.DataScience.jupyterLaunchTimedOut()); - } - }; - - private resolveStartPromise = (baseUrl: string, token: string, hostName: string) => { - // tslint:disable-next-line: no-any - clearTimeout(this.launchTimeout as any); - if (!this.startPromise.rejected) { - const connection = this.createConnection(baseUrl, token, hostName, this.launchResult); - const origDispose = connection.dispose.bind(connection); - connection.dispose = () => { - // Stop listening when we disconnect - this.connectionDisposed = true; - return origDispose(); - }; - this.startPromise.resolve(connection); - } - }; - - // tslint:disable-next-line:no-any - private rejectStartPromise = (message: string) => { - // tslint:disable-next-line: no-any - clearTimeout(this.launchTimeout as any); - if (!this.startPromise.resolved) { - this.startPromise.reject( - Cancellation.isCanceled(this.cancelToken) - ? new CancellationError() - : new JupyterConnectError(message, this.stderr.join('\n')) - ); - } - }; -} - -// Represents an active connection to a running jupyter notebook -class JupyterConnection implements IConnection { - public readonly localLaunch: boolean = true; - public localProcExitCode: number | undefined; - private eventEmitter: EventEmitter = new EventEmitter(); - constructor( - public readonly baseUrl: string, - public readonly token: string, - public readonly hostName: string, - private readonly disposable: Disposable, - childProc: ChildProcess | undefined - ) { - // If the local process exits, set our exit code and fire our event - if (childProc) { - childProc.on('exit', c => { - // Our code expects the exit code to be of type `number` or `undefined`. - const code = typeof c === 'number' ? c : undefined; - this.localProcExitCode = code; - this.eventEmitter.fire(code); - }); - } - } - - public get disconnected(): Event { - return this.eventEmitter.event; - } - - public dispose() { - if (this.disposable) { - this.disposable.dispose(); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../common/extensions'; + +import { ChildProcess } from 'child_process'; +import { CancellationToken, Disposable, Event, EventEmitter } from 'vscode'; +import { Cancellation, CancellationError } from '../../common/cancellation'; +import { traceInfo, traceWarning } from '../../common/logger'; +import { IFileSystem } from '../../common/platform/types'; +import { ObservableExecutionResult, Output } from '../../common/process/types'; +import { IConfigurationService, IDisposable } from '../../common/types'; +import { createDeferred, Deferred } from '../../common/utils/async'; +import * as localize from '../../common/utils/localize'; +import { IServiceContainer } from '../../ioc/types'; +import { RegExpValues } from '../constants'; +import { IConnection } from '../types'; +import { JupyterConnectError } from './jupyterConnectError'; + +// tslint:disable-next-line:no-require-imports no-var-requires no-any +const namedRegexp = require('named-js-regexp'); +const urlMatcher = namedRegexp(RegExpValues.UrlPatternRegEx); + +export type JupyterServerInfo = { + base_url: string; + notebook_dir: string; + hostname: string; + password: boolean; + pid: number; + port: number; + secure: boolean; + token: string; + url: string; +}; + +export class JupyterConnectionWaiter implements IDisposable { + private startPromise: Deferred; + private launchTimeout: NodeJS.Timer | number; + private configService: IConfigurationService; + private fileSystem: IFileSystem; + private stderr: string[] = []; + private connectionDisposed = false; + + constructor( + private readonly launchResult: ObservableExecutionResult, + private readonly notebookDir: string, + private readonly getServerInfo: (cancelToken?: CancellationToken) => Promise, + serviceContainer: IServiceContainer, + private readonly cancelToken?: CancellationToken + ) { + this.configService = serviceContainer.get(IConfigurationService); + this.fileSystem = serviceContainer.get(IFileSystem); + + // Cancel our start promise if a cancellation occurs + if (cancelToken) { + cancelToken.onCancellationRequested(() => this.startPromise.reject(new CancellationError())); + } + + // Setup our start promise + this.startPromise = createDeferred(); + + // We want to reject our Jupyter connection after a specific timeout + const settings = this.configService.getSettings(undefined); + const jupyterLaunchTimeout = settings.datascience.jupyterLaunchTimeout; + + this.launchTimeout = setTimeout(() => { + this.launchTimedOut(); + }, jupyterLaunchTimeout); + + // Listen for crashes + let exitCode = '0'; + if (launchResult.proc) { + launchResult.proc.on('exit', (c) => (exitCode = c ? c.toString() : '0')); + } + let stderr = ''; + // Listen on stderr for its connection information + launchResult.out.subscribe( + (output: Output) => { + if (output.source === 'stderr') { + stderr += output.out; + this.stderr.push(output.out); + this.extractConnectionInformation(stderr); + } else { + this.output(output.out); + } + }, + (e) => this.rejectStartPromise(e.message), + // If the process dies, we can't extract connection information. + () => this.rejectStartPromise(localize.DataScience.jupyterServerCrashed().format(exitCode)) + ); + } + public dispose() { + // tslint:disable-next-line: no-any + clearTimeout(this.launchTimeout as any); + } + + public waitForConnection(): Promise { + return this.startPromise.promise; + } + + private createConnection(baseUrl: string, token: string, hostName: string, processDisposable: Disposable) { + // tslint:disable-next-line: no-use-before-declare + return new JupyterConnection(baseUrl, token, hostName, processDisposable, this.launchResult.proc); + } + + // tslint:disable-next-line:no-any + private output = (data: any) => { + if (!this.connectionDisposed) { + traceInfo(data.toString('utf8')); + } + }; + + // From a list of jupyter server infos try to find the matching jupyter that we launched + // tslint:disable-next-line:no-any + private getJupyterURL(serverInfos: JupyterServerInfo[] | undefined, data: any) { + if (serverInfos && serverInfos.length > 0 && !this.startPromise.completed) { + const matchInfo = serverInfos.find((info) => + this.fileSystem.arePathsSame(this.notebookDir, info.notebook_dir) + ); + if (matchInfo) { + const url = matchInfo.url; + const token = matchInfo.token; + const host = matchInfo.hostname; + this.resolveStartPromise(url, token, host); + } + } + // At this point we failed to get the server info or a matching server via the python code, so fall back to + // our URL parse + if (!this.startPromise.completed) { + this.getJupyterURLFromString(data); + } + } + + // tslint:disable-next-line:no-any + private getJupyterURLFromString(data: any) { + // tslint:disable-next-line:no-any + const urlMatch = urlMatcher.exec(data) as any; + const groups = urlMatch.groups() as RegExpValues.IUrlPatternGroupType; + if (urlMatch && !this.startPromise.completed && groups && (groups.LOCAL || groups.IP)) { + // Rebuild the URI from our group hits + const host = groups.LOCAL ? groups.LOCAL : groups.IP; + const uriString = `${groups.PREFIX}${host}${groups.REST}`; + + // URL is not being found for some reason. Pull it in forcefully + // tslint:disable-next-line:no-require-imports + const URL = require('url').URL; + let url: URL; + try { + url = new URL(uriString); + } catch (err) { + // Failed to parse the url either via server infos or the string + this.rejectStartPromise(localize.DataScience.jupyterLaunchNoURL()); + return; + } + + // Here we parsed the URL correctly + this.resolveStartPromise( + `${url.protocol}//${url.host}${url.pathname}`, + `${url.searchParams.get('token')}`, + url.hostname + ); + } + } + + // tslint:disable-next-line:no-any + private extractConnectionInformation = (data: any) => { + this.output(data); + + const httpMatch = RegExpValues.HttpPattern.exec(data); + + if (httpMatch && this.notebookDir && this.startPromise && !this.startPromise.completed && this.getServerInfo) { + // .then so that we can keep from pushing aync up to the subscribed observable function + this.getServerInfo(this.cancelToken) + .then((serverInfos) => this.getJupyterURL(serverInfos, data)) + .catch((ex) => traceWarning('Failed to get server info', ex)); + } + + // Sometimes jupyter will return a 403 error. Not sure why. We used + // to fail on this, but it looks like jupyter works with this error in place. + }; + + private launchTimedOut = () => { + if (!this.startPromise.completed) { + this.rejectStartPromise(localize.DataScience.jupyterLaunchTimedOut()); + } + }; + + private resolveStartPromise = (baseUrl: string, token: string, hostName: string) => { + // tslint:disable-next-line: no-any + clearTimeout(this.launchTimeout as any); + if (!this.startPromise.rejected) { + const connection = this.createConnection(baseUrl, token, hostName, this.launchResult); + const origDispose = connection.dispose.bind(connection); + connection.dispose = () => { + // Stop listening when we disconnect + this.connectionDisposed = true; + return origDispose(); + }; + this.startPromise.resolve(connection); + } + }; + + // tslint:disable-next-line:no-any + private rejectStartPromise = (message: string) => { + // tslint:disable-next-line: no-any + clearTimeout(this.launchTimeout as any); + if (!this.startPromise.resolved) { + this.startPromise.reject( + Cancellation.isCanceled(this.cancelToken) + ? new CancellationError() + : new JupyterConnectError(message, this.stderr.join('\n')) + ); + } + }; +} + +// Represents an active connection to a running jupyter notebook +class JupyterConnection implements IConnection { + public readonly localLaunch: boolean = true; + public localProcExitCode: number | undefined; + private eventEmitter: EventEmitter = new EventEmitter(); + constructor( + public readonly baseUrl: string, + public readonly token: string, + public readonly hostName: string, + private readonly disposable: Disposable, + childProc: ChildProcess | undefined + ) { + // If the local process exits, set our exit code and fire our event + if (childProc) { + childProc.on('exit', (c) => { + // Our code expects the exit code to be of type `number` or `undefined`. + const code = typeof c === 'number' ? c : undefined; + this.localProcExitCode = code; + this.eventEmitter.fire(code); + }); + } + } + + public get disconnected(): Event { + return this.eventEmitter.event; + } + + public dispose() { + if (this.disposable) { + this.disposable.dispose(); + } + } +} diff --git a/src/client/datascience/jupyter/jupyterDebugger.ts b/src/client/datascience/jupyter/jupyterDebugger.ts index e6439a599cf7..1b3461984789 100644 --- a/src/client/datascience/jupyter/jupyterDebugger.ts +++ b/src/client/datascience/jupyter/jupyterDebugger.ts @@ -124,7 +124,7 @@ export class JupyterDebugger implements IJupyterDebugger, ICellHashListener { // Make sure that we have an active debugging session at this point if (this.debugService.activeDebugSession) { await Promise.all( - hashes.map(fileHash => { + hashes.map((fileHash) => { return this.debugService.activeDebugSession!.customRequest( 'setPydevdSourceMap', this.buildSourceMap(fileHash) @@ -272,7 +272,7 @@ export class JupyterDebugger implements IJupyterDebugger, ICellHashListener { private buildSourceMap(fileHash: IFileHashes): ISourceMapRequest { const sourceMapRequest: ISourceMapRequest = { source: { path: fileHash.file }, pydevdSourceMaps: [] }; - sourceMapRequest.pydevdSourceMaps = fileHash.hashes.map(cellHash => { + sourceMapRequest.pydevdSourceMaps = fileHash.hashes.map((cellHash) => { return { line: cellHash.line, endLine: cellHash.endLine, diff --git a/src/client/datascience/jupyter/jupyterExecution.ts b/src/client/datascience/jupyter/jupyterExecution.ts index 19504be244c2..554f6122c0df 100644 --- a/src/client/datascience/jupyter/jupyterExecution.ts +++ b/src/client/datascience/jupyter/jupyterExecution.ts @@ -1,402 +1,402 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import * as portfinder from 'portfinder'; -import * as uuid from 'uuid/v4'; -import { CancellationToken, CancellationTokenSource, Event, EventEmitter } from 'vscode'; - -import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../common/application/types'; -import { Cancellation } from '../../common/cancellation'; -import { traceError, traceInfo } from '../../common/logger'; -import { IConfigurationService, IDisposableRegistry, IOutputChannel } from '../../common/types'; -import { sleep } from '../../common/utils/async'; -import * as localize from '../../common/utils/localize'; -import { noop } from '../../common/utils/misc'; -import { StopWatch } from '../../common/utils/stopWatch'; -import { IInterpreterService, PythonInterpreter } from '../../interpreter/contracts'; -import { IServiceContainer } from '../../ioc/types'; -import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; -import { Commands, Telemetry } from '../constants'; -import { - IConnection, - IJupyterExecution, - IJupyterSessionManagerFactory, - IJupyterSubCommandExecutionService, - INotebookServer, - INotebookServerLaunchInfo, - INotebookServerOptions -} from '../types'; -import { JupyterSelfCertsError } from './jupyterSelfCertsError'; -import { JupyterSessionStartError } from './jupyterSession'; -import { createRemoteConnectionInfo } from './jupyterUtils'; -import { JupyterWaitForIdleError } from './jupyterWaitForIdleError'; -import { JupyterZMQBinariesNotFoundError } from './jupyterZMQBinariesNotFoundError'; -import { KernelSelector, KernelSpecInterpreter } from './kernels/kernelSelector'; -import { NotebookStarter } from './notebookStarter'; - -const LocalHosts = ['localhost', '127.0.0.1', '::1']; - -export class JupyterExecutionBase implements IJupyterExecution { - private usablePythonInterpreter: PythonInterpreter | undefined; - private eventEmitter: EventEmitter = new EventEmitter(); - private startedEmitter: EventEmitter = new EventEmitter(); - private disposed: boolean = false; - private readonly jupyterInterpreterService: IJupyterSubCommandExecutionService; - private zmqError: Error | undefined; - - constructor( - _liveShare: ILiveShareApi, - private readonly interpreterService: IInterpreterService, - private readonly disposableRegistry: IDisposableRegistry, - workspace: IWorkspaceService, - private readonly configuration: IConfigurationService, - private readonly kernelSelector: KernelSelector, - private readonly notebookStarter: NotebookStarter, - private readonly appShell: IApplicationShell, - private readonly jupyterOutputChannel: IOutputChannel, - private readonly serviceContainer: IServiceContainer - ) { - this.jupyterInterpreterService = serviceContainer.get( - IJupyterSubCommandExecutionService - ); - this.disposableRegistry.push(this.interpreterService.onDidChangeInterpreter(() => this.onSettingsChanged())); - this.disposableRegistry.push(this); - - if (workspace) { - const disposable = workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('python.dataScience', undefined)) { - // When config changes happen, recreate our commands. - this.onSettingsChanged(); - } - }); - this.disposableRegistry.push(disposable); - } - } - - public get sessionChanged(): Event { - return this.eventEmitter.event; - } - - public get serverStarted(): Event { - return this.startedEmitter.event; - } - - public dispose(): Promise { - this.disposed = true; - return Promise.resolve(); - } - - public async refreshCommands(): Promise { - await this.jupyterInterpreterService.refreshCommands(); - } - - public isNotebookSupported(cancelToken?: CancellationToken): Promise { - // See if we can find the command notebook - return this.jupyterInterpreterService.isNotebookSupported(cancelToken); - } - - public async getNotebookError(): Promise { - return this.jupyterInterpreterService.getReasonForJupyterNotebookNotBeingSupported(); - } - - public async getUsableJupyterPython(cancelToken?: CancellationToken): Promise { - // Only try to compute this once. - if (!this.usablePythonInterpreter && !this.disposed) { - this.usablePythonInterpreter = await Cancellation.race( - () => this.jupyterInterpreterService.getSelectedInterpreter(cancelToken), - cancelToken - ); - } - return this.usablePythonInterpreter; - } - - public isImportSupported(cancelToken?: CancellationToken): Promise { - // See if we can find the command nbconvert - return this.jupyterInterpreterService.isExportSupported(cancelToken); - } - - public isSpawnSupported(cancelToken?: CancellationToken): Promise { - // Supported if we can run a notebook - return this.isNotebookSupported(cancelToken); - } - - //tslint:disable:cyclomatic-complexity max-func-body-length - public connectToNotebookServer( - options?: INotebookServerOptions, - cancelToken?: CancellationToken - ): Promise { - // Return nothing if we cancel - // tslint:disable-next-line: max-func-body-length - return Cancellation.race(async () => { - let result: INotebookServer | undefined; - let connection: IConnection | undefined; - let kernelSpecInterpreter: KernelSpecInterpreter | undefined; - let kernelSpecInterpreterPromise: Promise = Promise.resolve({}); - traceInfo(`Connecting to ${options ? options.purpose : 'unknown type of'} server`); - const allowUI = !options || options.allowUI(); - const kernelSpecCancelSource = new CancellationTokenSource(); - if (cancelToken) { - cancelToken.onCancellationRequested(() => { - kernelSpecCancelSource.cancel(); - }); - } - const isLocalConnection = !options || !options.uri; - - if (isLocalConnection) { - // Get hold of the kernelspec and corresponding (matching) interpreter that'll be used as the spec. - // We can do this in parallel, while starting the server (faster). - traceInfo(`Getting kernel specs for ${options ? options.purpose : 'unknown type of'} server`); - kernelSpecInterpreterPromise = this.kernelSelector.getKernelForLocalConnection( - undefined, - undefined, - options?.metadata, - !allowUI, - kernelSpecCancelSource.token - ); - } - - // Try to connect to our jupyter process. Check our setting for the number of tries - let tryCount = 0; - const maxTries = this.configuration.getSettings(undefined).datascience.jupyterLaunchRetries; - const stopWatch = new StopWatch(); - while (tryCount < maxTries) { - try { - // Start or connect to the process - [connection, kernelSpecInterpreter] = await Promise.all([ - this.startOrConnect(options, cancelToken), - kernelSpecInterpreterPromise - ]); - - if (!connection.localLaunch && LocalHosts.includes(connection.hostName.toLowerCase())) { - sendTelemetryEvent(Telemetry.ConnectRemoteJupyterViaLocalHost); - } - // Create a server tha t we will then attempt to connect to. - result = this.serviceContainer.get(INotebookServer); - - // In a remote non quest situation, figure out a kernel spec too. - if (!kernelSpecInterpreter.kernelSpec && connection && !options?.skipSearchingForKernel) { - const sessionManagerFactory = this.serviceContainer.get( - IJupyterSessionManagerFactory - ); - const sessionManager = await sessionManagerFactory.create(connection); - kernelSpecInterpreter = await this.kernelSelector.getKernelForRemoteConnection( - undefined, - sessionManager, - options?.metadata, - cancelToken - ); - await sessionManager.dispose(); - } - - // If no kernel and not going to pick one, exit early - if (!Object.keys(kernelSpecInterpreter) && !allowUI) { - return undefined; - } - - // Populate the launch info that we are starting our server with - const launchInfo: INotebookServerLaunchInfo = { - connectionInfo: connection!, - interpreter: kernelSpecInterpreter.interpreter, - kernelSpec: kernelSpecInterpreter.kernelSpec, - workingDir: options ? options.workingDir : undefined, - uri: options ? options.uri : undefined, - purpose: options ? options.purpose : uuid() - }; - - // tslint:disable-next-line: no-constant-condition - while (true) { - try { - traceInfo( - `Connecting to process for ${options ? options.purpose : 'unknown type of'} server` - ); - await result.connect(launchInfo, cancelToken); - traceInfo( - `Connection complete for ${options ? options.purpose : 'unknown type of'} server` - ); - break; - } catch (ex) { - traceError('Failed to connect to server', ex); - if (ex instanceof JupyterSessionStartError && isLocalConnection && allowUI) { - // Keep retrying, until it works or user cancels. - // Sometimes if a bad kernel is selected, starting a session can fail. - // In such cases we need to let the user know about this and prompt them to select another kernel. - const message = localize.DataScience.sessionStartFailedWithKernel().format( - launchInfo.kernelSpec?.display_name || launchInfo.kernelSpec?.name || '', - Commands.ViewJupyterOutput - ); - const selectKernel = localize.DataScience.selectDifferentKernel(); - const cancel = localize.Common.cancel(); - const selection = await this.appShell.showErrorMessage(message, selectKernel, cancel); - if (selection === selectKernel) { - const sessionManagerFactory = this.serviceContainer.get< - IJupyterSessionManagerFactory - >(IJupyterSessionManagerFactory); - const sessionManager = await sessionManagerFactory.create(connection); - const kernelInterpreter = await this.kernelSelector.selectLocalKernel( - undefined, - new StopWatch(), - sessionManager, - cancelToken, - launchInfo.kernelSpec - ); - if (Object.keys(kernelInterpreter).length > 0) { - launchInfo.interpreter = kernelInterpreter.interpreter; - launchInfo.kernelSpec = - kernelInterpreter.kernelSpec || kernelInterpreter.kernelModel; - continue; - } - } - } - throw ex; - } - } - - sendTelemetryEvent( - isLocalConnection ? Telemetry.ConnectLocalJupyter : Telemetry.ConnectRemoteJupyter - ); - return result; - } catch (err) { - // Cleanup after ourselves. server may be running partially. - if (result) { - traceInfo(`Killing server because of error ${err}`); - await result.dispose(); - } - if (err instanceof JupyterWaitForIdleError && tryCount < maxTries) { - // Special case. This sometimes happens where jupyter doesn't ever connect. Cleanup after - // ourselves and propagate the failure outwards. - traceInfo('Retry because of wait for idle problem.'); - sendTelemetryEvent(Telemetry.SessionIdleTimeout); - - // Close existing connection. - connection?.dispose(); - tryCount += 1; - } else if (connection) { - kernelSpecCancelSource.cancel(); - - // Something else went wrong - if (!isLocalConnection) { - sendTelemetryEvent(Telemetry.ConnectRemoteFailedJupyter); - - // Check for the self signed certs error specifically - if (err.message.indexOf('reason: self signed certificate') >= 0) { - sendTelemetryEvent(Telemetry.ConnectRemoteSelfCertFailedJupyter); - throw new JupyterSelfCertsError(connection.baseUrl); - } else { - throw new Error( - localize.DataScience.jupyterNotebookRemoteConnectFailed().format( - connection.baseUrl, - err - ) - ); - } - } else { - sendTelemetryEvent(Telemetry.ConnectFailedJupyter); - throw new Error( - localize.DataScience.jupyterNotebookConnectFailed().format(connection.baseUrl, err) - ); - } - } else { - kernelSpecCancelSource.cancel(); - throw err; - } - } - } - - // If we're here, then starting jupyter timeout. - // Kill any existing connections. - connection?.dispose(); - sendTelemetryEvent(Telemetry.JupyterStartTimeout, stopWatch.elapsedTime, { - timeout: stopWatch.elapsedTime - }); - if (allowUI) { - this.appShell - .showErrorMessage(localize.DataScience.jupyterStartTimedout(), localize.Common.openOutputPanel()) - .then(selection => { - if (selection === localize.Common.openOutputPanel()) { - this.jupyterOutputChannel.show(); - } - }, noop); - } - }, cancelToken); - } - - public async spawnNotebook(file: string): Promise { - return this.jupyterInterpreterService.openNotebook(file); - } - - public async importNotebook(file: string, template: string | undefined): Promise { - return this.jupyterInterpreterService.exportNotebookToPython(file, template); - } - - public getServer(_options?: INotebookServerOptions): Promise { - // This is cached at the host or guest level - return Promise.resolve(undefined); - } - - private async verifyZMQ() { - if (this.zmqError) { - throw this.zmqError; - } - try { - const zmq = await import('zeromq'); - const sock = new zmq.Push(); - const port = await portfinder.getPortPromise(); - - await sock.bind(`tcp://127.0.0.1:${port}`); - sock.send('some work').ignoreErrors(); // This will never return unless there's a listener. Just used for testing the API is available - await sleep(50); - sock.close(); - traceInfo(`ZMQ connection to port ${port} verified.`); - } catch (e) { - traceError(`Exception while attempting zmq :`, e); - sendTelemetryEvent(Telemetry.ZMQNotSupported); - this.zmqError = new JupyterZMQBinariesNotFoundError(e.toString()); - throw this.zmqError; - } - } - private async startOrConnect( - options?: INotebookServerOptions, - cancelToken?: CancellationToken - ): Promise { - // If our uri is undefined or if it's set to local launch we need to launch a server locally - if (!options || !options.uri) { - // First verify we have ZMQ installed correctly (this might change when we don't 'launch' servers anymore) - await this.verifyZMQ(); - - // If that works, then attempt to start the server - traceInfo(`Launching ${options ? options.purpose : 'unknown type of'} server`); - const useDefaultConfig = !options || options.skipUsingDefaultConfig ? false : true; - const connection = await this.startNotebookServer( - useDefaultConfig, - this.configuration.getSettings(undefined).datascience.jupyterCommandLineArguments, - cancelToken - ); - if (connection) { - return connection; - } else { - // Throw a cancellation error if we were canceled. - Cancellation.throwIfCanceled(cancelToken); - - // Otherwise we can't connect - throw new Error(localize.DataScience.jupyterNotebookFailure().format('')); - } - } else { - // If we have a URI spec up a connection info for it - return createRemoteConnectionInfo(options.uri, this.configuration.getSettings(undefined).datascience); - } - } - - // tslint:disable-next-line: max-func-body-length - @captureTelemetry(Telemetry.StartJupyter) - private async startNotebookServer( - useDefaultConfig: boolean, - customCommandLine: string[], - cancelToken?: CancellationToken - ): Promise { - return this.notebookStarter.start(useDefaultConfig, customCommandLine, cancelToken); - } - private onSettingsChanged() { - // Clear our usableJupyterInterpreter so that we recompute our values - this.usablePythonInterpreter = undefined; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import * as portfinder from 'portfinder'; +import * as uuid from 'uuid/v4'; +import { CancellationToken, CancellationTokenSource, Event, EventEmitter } from 'vscode'; + +import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../common/application/types'; +import { Cancellation } from '../../common/cancellation'; +import { traceError, traceInfo } from '../../common/logger'; +import { IConfigurationService, IDisposableRegistry, IOutputChannel } from '../../common/types'; +import { sleep } from '../../common/utils/async'; +import * as localize from '../../common/utils/localize'; +import { noop } from '../../common/utils/misc'; +import { StopWatch } from '../../common/utils/stopWatch'; +import { IInterpreterService, PythonInterpreter } from '../../interpreter/contracts'; +import { IServiceContainer } from '../../ioc/types'; +import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; +import { Commands, Telemetry } from '../constants'; +import { + IConnection, + IJupyterExecution, + IJupyterSessionManagerFactory, + IJupyterSubCommandExecutionService, + INotebookServer, + INotebookServerLaunchInfo, + INotebookServerOptions +} from '../types'; +import { JupyterSelfCertsError } from './jupyterSelfCertsError'; +import { JupyterSessionStartError } from './jupyterSession'; +import { createRemoteConnectionInfo } from './jupyterUtils'; +import { JupyterWaitForIdleError } from './jupyterWaitForIdleError'; +import { JupyterZMQBinariesNotFoundError } from './jupyterZMQBinariesNotFoundError'; +import { KernelSelector, KernelSpecInterpreter } from './kernels/kernelSelector'; +import { NotebookStarter } from './notebookStarter'; + +const LocalHosts = ['localhost', '127.0.0.1', '::1']; + +export class JupyterExecutionBase implements IJupyterExecution { + private usablePythonInterpreter: PythonInterpreter | undefined; + private eventEmitter: EventEmitter = new EventEmitter(); + private startedEmitter: EventEmitter = new EventEmitter(); + private disposed: boolean = false; + private readonly jupyterInterpreterService: IJupyterSubCommandExecutionService; + private zmqError: Error | undefined; + + constructor( + _liveShare: ILiveShareApi, + private readonly interpreterService: IInterpreterService, + private readonly disposableRegistry: IDisposableRegistry, + workspace: IWorkspaceService, + private readonly configuration: IConfigurationService, + private readonly kernelSelector: KernelSelector, + private readonly notebookStarter: NotebookStarter, + private readonly appShell: IApplicationShell, + private readonly jupyterOutputChannel: IOutputChannel, + private readonly serviceContainer: IServiceContainer + ) { + this.jupyterInterpreterService = serviceContainer.get( + IJupyterSubCommandExecutionService + ); + this.disposableRegistry.push(this.interpreterService.onDidChangeInterpreter(() => this.onSettingsChanged())); + this.disposableRegistry.push(this); + + if (workspace) { + const disposable = workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('python.dataScience', undefined)) { + // When config changes happen, recreate our commands. + this.onSettingsChanged(); + } + }); + this.disposableRegistry.push(disposable); + } + } + + public get sessionChanged(): Event { + return this.eventEmitter.event; + } + + public get serverStarted(): Event { + return this.startedEmitter.event; + } + + public dispose(): Promise { + this.disposed = true; + return Promise.resolve(); + } + + public async refreshCommands(): Promise { + await this.jupyterInterpreterService.refreshCommands(); + } + + public isNotebookSupported(cancelToken?: CancellationToken): Promise { + // See if we can find the command notebook + return this.jupyterInterpreterService.isNotebookSupported(cancelToken); + } + + public async getNotebookError(): Promise { + return this.jupyterInterpreterService.getReasonForJupyterNotebookNotBeingSupported(); + } + + public async getUsableJupyterPython(cancelToken?: CancellationToken): Promise { + // Only try to compute this once. + if (!this.usablePythonInterpreter && !this.disposed) { + this.usablePythonInterpreter = await Cancellation.race( + () => this.jupyterInterpreterService.getSelectedInterpreter(cancelToken), + cancelToken + ); + } + return this.usablePythonInterpreter; + } + + public isImportSupported(cancelToken?: CancellationToken): Promise { + // See if we can find the command nbconvert + return this.jupyterInterpreterService.isExportSupported(cancelToken); + } + + public isSpawnSupported(cancelToken?: CancellationToken): Promise { + // Supported if we can run a notebook + return this.isNotebookSupported(cancelToken); + } + + //tslint:disable:cyclomatic-complexity max-func-body-length + public connectToNotebookServer( + options?: INotebookServerOptions, + cancelToken?: CancellationToken + ): Promise { + // Return nothing if we cancel + // tslint:disable-next-line: max-func-body-length + return Cancellation.race(async () => { + let result: INotebookServer | undefined; + let connection: IConnection | undefined; + let kernelSpecInterpreter: KernelSpecInterpreter | undefined; + let kernelSpecInterpreterPromise: Promise = Promise.resolve({}); + traceInfo(`Connecting to ${options ? options.purpose : 'unknown type of'} server`); + const allowUI = !options || options.allowUI(); + const kernelSpecCancelSource = new CancellationTokenSource(); + if (cancelToken) { + cancelToken.onCancellationRequested(() => { + kernelSpecCancelSource.cancel(); + }); + } + const isLocalConnection = !options || !options.uri; + + if (isLocalConnection) { + // Get hold of the kernelspec and corresponding (matching) interpreter that'll be used as the spec. + // We can do this in parallel, while starting the server (faster). + traceInfo(`Getting kernel specs for ${options ? options.purpose : 'unknown type of'} server`); + kernelSpecInterpreterPromise = this.kernelSelector.getKernelForLocalConnection( + undefined, + undefined, + options?.metadata, + !allowUI, + kernelSpecCancelSource.token + ); + } + + // Try to connect to our jupyter process. Check our setting for the number of tries + let tryCount = 0; + const maxTries = this.configuration.getSettings(undefined).datascience.jupyterLaunchRetries; + const stopWatch = new StopWatch(); + while (tryCount < maxTries) { + try { + // Start or connect to the process + [connection, kernelSpecInterpreter] = await Promise.all([ + this.startOrConnect(options, cancelToken), + kernelSpecInterpreterPromise + ]); + + if (!connection.localLaunch && LocalHosts.includes(connection.hostName.toLowerCase())) { + sendTelemetryEvent(Telemetry.ConnectRemoteJupyterViaLocalHost); + } + // Create a server tha t we will then attempt to connect to. + result = this.serviceContainer.get(INotebookServer); + + // In a remote non quest situation, figure out a kernel spec too. + if (!kernelSpecInterpreter.kernelSpec && connection && !options?.skipSearchingForKernel) { + const sessionManagerFactory = this.serviceContainer.get( + IJupyterSessionManagerFactory + ); + const sessionManager = await sessionManagerFactory.create(connection); + kernelSpecInterpreter = await this.kernelSelector.getKernelForRemoteConnection( + undefined, + sessionManager, + options?.metadata, + cancelToken + ); + await sessionManager.dispose(); + } + + // If no kernel and not going to pick one, exit early + if (!Object.keys(kernelSpecInterpreter) && !allowUI) { + return undefined; + } + + // Populate the launch info that we are starting our server with + const launchInfo: INotebookServerLaunchInfo = { + connectionInfo: connection!, + interpreter: kernelSpecInterpreter.interpreter, + kernelSpec: kernelSpecInterpreter.kernelSpec, + workingDir: options ? options.workingDir : undefined, + uri: options ? options.uri : undefined, + purpose: options ? options.purpose : uuid() + }; + + // tslint:disable-next-line: no-constant-condition + while (true) { + try { + traceInfo( + `Connecting to process for ${options ? options.purpose : 'unknown type of'} server` + ); + await result.connect(launchInfo, cancelToken); + traceInfo( + `Connection complete for ${options ? options.purpose : 'unknown type of'} server` + ); + break; + } catch (ex) { + traceError('Failed to connect to server', ex); + if (ex instanceof JupyterSessionStartError && isLocalConnection && allowUI) { + // Keep retrying, until it works or user cancels. + // Sometimes if a bad kernel is selected, starting a session can fail. + // In such cases we need to let the user know about this and prompt them to select another kernel. + const message = localize.DataScience.sessionStartFailedWithKernel().format( + launchInfo.kernelSpec?.display_name || launchInfo.kernelSpec?.name || '', + Commands.ViewJupyterOutput + ); + const selectKernel = localize.DataScience.selectDifferentKernel(); + const cancel = localize.Common.cancel(); + const selection = await this.appShell.showErrorMessage(message, selectKernel, cancel); + if (selection === selectKernel) { + const sessionManagerFactory = this.serviceContainer.get< + IJupyterSessionManagerFactory + >(IJupyterSessionManagerFactory); + const sessionManager = await sessionManagerFactory.create(connection); + const kernelInterpreter = await this.kernelSelector.selectLocalKernel( + undefined, + new StopWatch(), + sessionManager, + cancelToken, + launchInfo.kernelSpec + ); + if (Object.keys(kernelInterpreter).length > 0) { + launchInfo.interpreter = kernelInterpreter.interpreter; + launchInfo.kernelSpec = + kernelInterpreter.kernelSpec || kernelInterpreter.kernelModel; + continue; + } + } + } + throw ex; + } + } + + sendTelemetryEvent( + isLocalConnection ? Telemetry.ConnectLocalJupyter : Telemetry.ConnectRemoteJupyter + ); + return result; + } catch (err) { + // Cleanup after ourselves. server may be running partially. + if (result) { + traceInfo(`Killing server because of error ${err}`); + await result.dispose(); + } + if (err instanceof JupyterWaitForIdleError && tryCount < maxTries) { + // Special case. This sometimes happens where jupyter doesn't ever connect. Cleanup after + // ourselves and propagate the failure outwards. + traceInfo('Retry because of wait for idle problem.'); + sendTelemetryEvent(Telemetry.SessionIdleTimeout); + + // Close existing connection. + connection?.dispose(); + tryCount += 1; + } else if (connection) { + kernelSpecCancelSource.cancel(); + + // Something else went wrong + if (!isLocalConnection) { + sendTelemetryEvent(Telemetry.ConnectRemoteFailedJupyter); + + // Check for the self signed certs error specifically + if (err.message.indexOf('reason: self signed certificate') >= 0) { + sendTelemetryEvent(Telemetry.ConnectRemoteSelfCertFailedJupyter); + throw new JupyterSelfCertsError(connection.baseUrl); + } else { + throw new Error( + localize.DataScience.jupyterNotebookRemoteConnectFailed().format( + connection.baseUrl, + err + ) + ); + } + } else { + sendTelemetryEvent(Telemetry.ConnectFailedJupyter); + throw new Error( + localize.DataScience.jupyterNotebookConnectFailed().format(connection.baseUrl, err) + ); + } + } else { + kernelSpecCancelSource.cancel(); + throw err; + } + } + } + + // If we're here, then starting jupyter timeout. + // Kill any existing connections. + connection?.dispose(); + sendTelemetryEvent(Telemetry.JupyterStartTimeout, stopWatch.elapsedTime, { + timeout: stopWatch.elapsedTime + }); + if (allowUI) { + this.appShell + .showErrorMessage(localize.DataScience.jupyterStartTimedout(), localize.Common.openOutputPanel()) + .then((selection) => { + if (selection === localize.Common.openOutputPanel()) { + this.jupyterOutputChannel.show(); + } + }, noop); + } + }, cancelToken); + } + + public async spawnNotebook(file: string): Promise { + return this.jupyterInterpreterService.openNotebook(file); + } + + public async importNotebook(file: string, template: string | undefined): Promise { + return this.jupyterInterpreterService.exportNotebookToPython(file, template); + } + + public getServer(_options?: INotebookServerOptions): Promise { + // This is cached at the host or guest level + return Promise.resolve(undefined); + } + + private async verifyZMQ() { + if (this.zmqError) { + throw this.zmqError; + } + try { + const zmq = await import('zeromq'); + const sock = new zmq.Push(); + const port = await portfinder.getPortPromise(); + + await sock.bind(`tcp://127.0.0.1:${port}`); + sock.send('some work').ignoreErrors(); // This will never return unless there's a listener. Just used for testing the API is available + await sleep(50); + sock.close(); + traceInfo(`ZMQ connection to port ${port} verified.`); + } catch (e) { + traceError(`Exception while attempting zmq :`, e); + sendTelemetryEvent(Telemetry.ZMQNotSupported); + this.zmqError = new JupyterZMQBinariesNotFoundError(e.toString()); + throw this.zmqError; + } + } + private async startOrConnect( + options?: INotebookServerOptions, + cancelToken?: CancellationToken + ): Promise { + // If our uri is undefined or if it's set to local launch we need to launch a server locally + if (!options || !options.uri) { + // First verify we have ZMQ installed correctly (this might change when we don't 'launch' servers anymore) + await this.verifyZMQ(); + + // If that works, then attempt to start the server + traceInfo(`Launching ${options ? options.purpose : 'unknown type of'} server`); + const useDefaultConfig = !options || options.skipUsingDefaultConfig ? false : true; + const connection = await this.startNotebookServer( + useDefaultConfig, + this.configuration.getSettings(undefined).datascience.jupyterCommandLineArguments, + cancelToken + ); + if (connection) { + return connection; + } else { + // Throw a cancellation error if we were canceled. + Cancellation.throwIfCanceled(cancelToken); + + // Otherwise we can't connect + throw new Error(localize.DataScience.jupyterNotebookFailure().format('')); + } + } else { + // If we have a URI spec up a connection info for it + return createRemoteConnectionInfo(options.uri, this.configuration.getSettings(undefined).datascience); + } + } + + // tslint:disable-next-line: max-func-body-length + @captureTelemetry(Telemetry.StartJupyter) + private async startNotebookServer( + useDefaultConfig: boolean, + customCommandLine: string[], + cancelToken?: CancellationToken + ): Promise { + return this.notebookStarter.start(useDefaultConfig, customCommandLine, cancelToken); + } + private onSettingsChanged() { + // Clear our usableJupyterInterpreter so that we recompute our values + this.usablePythonInterpreter = undefined; + } +} diff --git a/src/client/datascience/jupyter/jupyterExecutionFactory.ts b/src/client/datascience/jupyter/jupyterExecutionFactory.ts index 5e1d59233677..58a45a9c00a1 100644 --- a/src/client/datascience/jupyter/jupyterExecutionFactory.ts +++ b/src/client/datascience/jupyter/jupyterExecutionFactory.ts @@ -1,158 +1,158 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { inject, injectable, named } from 'inversify'; -import { CancellationToken, Event, EventEmitter } from 'vscode'; - -import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../common/application/types'; -import { IFileSystem } from '../../common/platform/types'; -import { - IAsyncDisposable, - IAsyncDisposableRegistry, - IConfigurationService, - IDisposableRegistry, - IOutputChannel -} from '../../common/types'; -import { IInterpreterService, PythonInterpreter } from '../../interpreter/contracts'; -import { IServiceContainer } from '../../ioc/types'; -import { JUPYTER_OUTPUT_CHANNEL } from '../constants'; -import { IJupyterExecution, INotebookServer, INotebookServerOptions } from '../types'; -import { KernelSelector } from './kernels/kernelSelector'; -import { GuestJupyterExecution } from './liveshare/guestJupyterExecution'; -import { HostJupyterExecution } from './liveshare/hostJupyterExecution'; -import { IRoleBasedObject, RoleBasedFactory } from './liveshare/roleBasedFactory'; -import { NotebookStarter } from './notebookStarter'; - -interface IJupyterExecutionInterface extends IRoleBasedObject, IJupyterExecution {} - -// tslint:disable:callable-types -type JupyterExecutionClassType = { - new ( - liveShare: ILiveShareApi, - interpreterService: IInterpreterService, - disposableRegistry: IDisposableRegistry, - asyncRegistry: IAsyncDisposableRegistry, - fileSystem: IFileSystem, - workspace: IWorkspaceService, - configuration: IConfigurationService, - kernelSelector: KernelSelector, - notebookStarter: NotebookStarter, - appShell: IApplicationShell, - jupyterOutputChannel: IOutputChannel, - serviceContainer: IServiceContainer - ): IJupyterExecutionInterface; -}; -// tslint:enable:callable-types - -@injectable() -export class JupyterExecutionFactory implements IJupyterExecution, IAsyncDisposable { - private executionFactory: RoleBasedFactory; - private sessionChangedEventEmitter: EventEmitter = new EventEmitter(); - private serverStartedEventEmitter: EventEmitter = new EventEmitter< - INotebookServerOptions - >(); - - constructor( - @inject(ILiveShareApi) liveShare: ILiveShareApi, - @inject(IInterpreterService) interpreterService: IInterpreterService, - @inject(IDisposableRegistry) disposableRegistry: IDisposableRegistry, - @inject(IAsyncDisposableRegistry) asyncRegistry: IAsyncDisposableRegistry, - @inject(IFileSystem) fileSystem: IFileSystem, - @inject(IWorkspaceService) workspace: IWorkspaceService, - @inject(IConfigurationService) configuration: IConfigurationService, - @inject(KernelSelector) kernelSelector: KernelSelector, - @inject(NotebookStarter) notebookStarter: NotebookStarter, - @inject(IApplicationShell) appShell: IApplicationShell, - @inject(IOutputChannel) @named(JUPYTER_OUTPUT_CHANNEL) jupyterOutputChannel: IOutputChannel, - @inject(IServiceContainer) serviceContainer: IServiceContainer - ) { - asyncRegistry.push(this); - this.executionFactory = new RoleBasedFactory( - liveShare, - HostJupyterExecution, - GuestJupyterExecution, - liveShare, - interpreterService, - disposableRegistry, - asyncRegistry, - fileSystem, - workspace, - configuration, - kernelSelector, - notebookStarter, - appShell, - jupyterOutputChannel, - serviceContainer - ); - this.executionFactory.sessionChanged(() => this.onSessionChanged()); - } - - public get sessionChanged(): Event { - return this.sessionChangedEventEmitter.event; - } - - public get serverStarted(): Event { - return this.serverStartedEventEmitter.event; - } - - public async dispose(): Promise { - // Dispose of our execution object - const execution = await this.executionFactory.get(); - return execution.dispose(); - } - - public async refreshCommands(): Promise { - const execution = await this.executionFactory.get(); - return execution.refreshCommands(); - } - - public async isNotebookSupported(cancelToken?: CancellationToken): Promise { - const execution = await this.executionFactory.get(); - return execution.isNotebookSupported(cancelToken); - } - - public async getNotebookError(): Promise { - const execution = await this.executionFactory.get(); - return execution.getNotebookError(); - } - - public async isImportSupported(cancelToken?: CancellationToken): Promise { - const execution = await this.executionFactory.get(); - return execution.isImportSupported(cancelToken); - } - public async isSpawnSupported(cancelToken?: CancellationToken): Promise { - const execution = await this.executionFactory.get(); - return execution.isSpawnSupported(cancelToken); - } - public async connectToNotebookServer( - options?: INotebookServerOptions, - cancelToken?: CancellationToken - ): Promise { - const execution = await this.executionFactory.get(); - const server = await execution.connectToNotebookServer(options, cancelToken); - if (server) { - this.serverStartedEventEmitter.fire(options); - } - return server; - } - public async spawnNotebook(file: string): Promise { - const execution = await this.executionFactory.get(); - return execution.spawnNotebook(file); - } - public async importNotebook(file: string, template: string | undefined): Promise { - const execution = await this.executionFactory.get(); - return execution.importNotebook(file, template); - } - public async getUsableJupyterPython(cancelToken?: CancellationToken): Promise { - const execution = await this.executionFactory.get(); - return execution.getUsableJupyterPython(cancelToken); - } - public async getServer(options?: INotebookServerOptions): Promise { - const execution = await this.executionFactory.get(); - return execution.getServer(options); - } - - private onSessionChanged() { - this.sessionChangedEventEmitter.fire(); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { inject, injectable, named } from 'inversify'; +import { CancellationToken, Event, EventEmitter } from 'vscode'; + +import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../common/application/types'; +import { IFileSystem } from '../../common/platform/types'; +import { + IAsyncDisposable, + IAsyncDisposableRegistry, + IConfigurationService, + IDisposableRegistry, + IOutputChannel +} from '../../common/types'; +import { IInterpreterService, PythonInterpreter } from '../../interpreter/contracts'; +import { IServiceContainer } from '../../ioc/types'; +import { JUPYTER_OUTPUT_CHANNEL } from '../constants'; +import { IJupyterExecution, INotebookServer, INotebookServerOptions } from '../types'; +import { KernelSelector } from './kernels/kernelSelector'; +import { GuestJupyterExecution } from './liveshare/guestJupyterExecution'; +import { HostJupyterExecution } from './liveshare/hostJupyterExecution'; +import { IRoleBasedObject, RoleBasedFactory } from './liveshare/roleBasedFactory'; +import { NotebookStarter } from './notebookStarter'; + +interface IJupyterExecutionInterface extends IRoleBasedObject, IJupyterExecution {} + +// tslint:disable:callable-types +type JupyterExecutionClassType = { + new ( + liveShare: ILiveShareApi, + interpreterService: IInterpreterService, + disposableRegistry: IDisposableRegistry, + asyncRegistry: IAsyncDisposableRegistry, + fileSystem: IFileSystem, + workspace: IWorkspaceService, + configuration: IConfigurationService, + kernelSelector: KernelSelector, + notebookStarter: NotebookStarter, + appShell: IApplicationShell, + jupyterOutputChannel: IOutputChannel, + serviceContainer: IServiceContainer + ): IJupyterExecutionInterface; +}; +// tslint:enable:callable-types + +@injectable() +export class JupyterExecutionFactory implements IJupyterExecution, IAsyncDisposable { + private executionFactory: RoleBasedFactory; + private sessionChangedEventEmitter: EventEmitter = new EventEmitter(); + private serverStartedEventEmitter: EventEmitter = new EventEmitter< + INotebookServerOptions + >(); + + constructor( + @inject(ILiveShareApi) liveShare: ILiveShareApi, + @inject(IInterpreterService) interpreterService: IInterpreterService, + @inject(IDisposableRegistry) disposableRegistry: IDisposableRegistry, + @inject(IAsyncDisposableRegistry) asyncRegistry: IAsyncDisposableRegistry, + @inject(IFileSystem) fileSystem: IFileSystem, + @inject(IWorkspaceService) workspace: IWorkspaceService, + @inject(IConfigurationService) configuration: IConfigurationService, + @inject(KernelSelector) kernelSelector: KernelSelector, + @inject(NotebookStarter) notebookStarter: NotebookStarter, + @inject(IApplicationShell) appShell: IApplicationShell, + @inject(IOutputChannel) @named(JUPYTER_OUTPUT_CHANNEL) jupyterOutputChannel: IOutputChannel, + @inject(IServiceContainer) serviceContainer: IServiceContainer + ) { + asyncRegistry.push(this); + this.executionFactory = new RoleBasedFactory( + liveShare, + HostJupyterExecution, + GuestJupyterExecution, + liveShare, + interpreterService, + disposableRegistry, + asyncRegistry, + fileSystem, + workspace, + configuration, + kernelSelector, + notebookStarter, + appShell, + jupyterOutputChannel, + serviceContainer + ); + this.executionFactory.sessionChanged(() => this.onSessionChanged()); + } + + public get sessionChanged(): Event { + return this.sessionChangedEventEmitter.event; + } + + public get serverStarted(): Event { + return this.serverStartedEventEmitter.event; + } + + public async dispose(): Promise { + // Dispose of our execution object + const execution = await this.executionFactory.get(); + return execution.dispose(); + } + + public async refreshCommands(): Promise { + const execution = await this.executionFactory.get(); + return execution.refreshCommands(); + } + + public async isNotebookSupported(cancelToken?: CancellationToken): Promise { + const execution = await this.executionFactory.get(); + return execution.isNotebookSupported(cancelToken); + } + + public async getNotebookError(): Promise { + const execution = await this.executionFactory.get(); + return execution.getNotebookError(); + } + + public async isImportSupported(cancelToken?: CancellationToken): Promise { + const execution = await this.executionFactory.get(); + return execution.isImportSupported(cancelToken); + } + public async isSpawnSupported(cancelToken?: CancellationToken): Promise { + const execution = await this.executionFactory.get(); + return execution.isSpawnSupported(cancelToken); + } + public async connectToNotebookServer( + options?: INotebookServerOptions, + cancelToken?: CancellationToken + ): Promise { + const execution = await this.executionFactory.get(); + const server = await execution.connectToNotebookServer(options, cancelToken); + if (server) { + this.serverStartedEventEmitter.fire(options); + } + return server; + } + public async spawnNotebook(file: string): Promise { + const execution = await this.executionFactory.get(); + return execution.spawnNotebook(file); + } + public async importNotebook(file: string, template: string | undefined): Promise { + const execution = await this.executionFactory.get(); + return execution.importNotebook(file, template); + } + public async getUsableJupyterPython(cancelToken?: CancellationToken): Promise { + const execution = await this.executionFactory.get(); + return execution.getUsableJupyterPython(cancelToken); + } + public async getServer(options?: INotebookServerOptions): Promise { + const execution = await this.executionFactory.get(); + return execution.getServer(options); + } + + private onSessionChanged() { + this.sessionChangedEventEmitter.fire(); + } +} diff --git a/src/client/datascience/jupyter/jupyterExporter.ts b/src/client/datascience/jupyter/jupyterExporter.ts index 4675d08fc59d..12de29f5f542 100644 --- a/src/client/datascience/jupyter/jupyterExporter.ts +++ b/src/client/datascience/jupyter/jupyterExporter.ts @@ -1,262 +1,262 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { nbformat } from '@jupyterlab/coreutils'; -import { inject, injectable } from 'inversify'; -import * as os from 'os'; -import * as path from 'path'; -import * as uuid from 'uuid/v4'; - -import { Uri } from 'vscode'; -import { concatMultilineStringInput } from '../../../datascience-ui/common'; -import { createCodeCell } from '../../../datascience-ui/common/cellFactory'; -import { IApplicationShell, IWorkspaceService } from '../../common/application/types'; -import { traceError } from '../../common/logger'; -import { IFileSystem, IPlatformService } from '../../common/platform/types'; -import { IConfigurationService } from '../../common/types'; -import * as localize from '../../common/utils/localize'; -import { noop } from '../../common/utils/misc'; -import { CellMatcher } from '../cellMatcher'; -import { CodeSnippits, Identifiers } from '../constants'; -import { - CellState, - ICell, - IDataScienceErrorHandler, - IJupyterExecution, - INotebookEditorProvider, - INotebookExporter -} from '../types'; - -@injectable() -export class JupyterExporter implements INotebookExporter { - constructor( - @inject(IJupyterExecution) private jupyterExecution: IJupyterExecution, - @inject(IWorkspaceService) private workspaceService: IWorkspaceService, - @inject(IConfigurationService) private configService: IConfigurationService, - @inject(IFileSystem) private fileSystem: IFileSystem, - @inject(IPlatformService) private readonly platform: IPlatformService, - @inject(IApplicationShell) private readonly applicationShell: IApplicationShell, - @inject(INotebookEditorProvider) protected ipynbProvider: INotebookEditorProvider, - @inject(IDataScienceErrorHandler) protected errorHandler: IDataScienceErrorHandler - ) {} - - public dispose() { - noop(); - } - - public async exportToFile(cells: ICell[], file: string): Promise { - let directoryChange; - const settings = this.configService.getSettings(); - if (settings.datascience.changeDirOnImportExport) { - directoryChange = file; - } - - const notebook = await this.translateToNotebook(cells, directoryChange); - - try { - // tslint:disable-next-line: no-any - const contents = JSON.stringify(notebook); - await this.fileSystem.writeFile(file, contents, { encoding: 'utf8', flag: 'w' }); - const openQuestion1 = localize.DataScience.exportOpenQuestion1(); - const openQuestion2 = (await this.jupyterExecution.isSpawnSupported()) - ? localize.DataScience.exportOpenQuestion() - : undefined; - this.showInformationMessage( - localize.DataScience.exportDialogComplete().format(file), - openQuestion1, - openQuestion2 - ).then(async (str: string | undefined) => { - try { - if (str === openQuestion2 && openQuestion2) { - // If the user wants to, open the notebook they just generated. - await this.jupyterExecution.spawnNotebook(file); - } else if (str === openQuestion1) { - await this.ipynbProvider.open(Uri.file(file)); - } - } catch (e) { - await this.errorHandler.handleError(e); - } - }); - } catch (exc) { - traceError('Error in exporting notebook file'); - this.applicationShell.showInformationMessage(localize.DataScience.exportDialogFailed().format(exc)); - } - } - public async translateToNotebook( - cells: ICell[], - changeDirectory?: string - ): Promise { - // If requested, add in a change directory cell to fix relative paths - if (changeDirectory && this.configService.getSettings().datascience.changeDirOnImportExport) { - cells = await this.addDirectoryChangeCell(cells, changeDirectory); - } - - const pythonNumber = await this.extractPythonMainVersion(); - - // Use this to build our metadata object - const metadata: nbformat.INotebookMetadata = { - language_info: { - name: 'python', - codemirror_mode: { - name: 'ipython', - version: pythonNumber - } - }, - orig_nbformat: 2, - file_extension: '.py', - mimetype: 'text/x-python', - name: 'python', - npconvert_exporter: 'python', - pygments_lexer: `ipython${pythonNumber}`, - version: pythonNumber - }; - - // Create an object for matching cell definitions - const matcher = new CellMatcher(this.configService.getSettings().datascience); - - // Combine this into a JSON object - return { - cells: this.pruneCells(cells, matcher), - nbformat: 4, - nbformat_minor: 2, - metadata: metadata - }; - } - - private showInformationMessage( - message: string, - question1: string, - question2?: string - ): Thenable { - if (question2) { - return this.applicationShell.showInformationMessage(message, question1, question2); - } else { - return this.applicationShell.showInformationMessage(message, question1); - } - } - - // For exporting, put in a cell that will change the working directory back to the workspace directory so relative data paths will load correctly - private addDirectoryChangeCell = async (cells: ICell[], file: string): Promise => { - const changeDirectory = await this.calculateDirectoryChange(file, cells); - - if (changeDirectory) { - const exportChangeDirectory = CodeSnippits.ChangeDirectory.join(os.EOL).format( - localize.DataScience.exportChangeDirectoryComment(), - CodeSnippits.ChangeDirectoryCommentIdentifier, - changeDirectory - ); - - const cell: ICell = { - data: createCodeCell(exportChangeDirectory), - id: uuid(), - file: Identifiers.EmptyFileName, - line: 0, - state: CellState.finished - }; - - return [cell, ...cells]; - } else { - return cells; - } - }; - - // When we export we want to our change directory back to the first real file that we saw run from any workspace folder - private firstWorkspaceFolder = async (cells: ICell[]): Promise => { - for (const cell of cells) { - const filename = cell.file; - - // First check that this is an absolute file that exists (we add in temp files to run system cell) - if (path.isAbsolute(filename) && (await this.fileSystem.fileExists(filename))) { - // We've already check that workspace folders above - for (const folder of this.workspaceService.workspaceFolders!) { - if (filename.toLowerCase().startsWith(folder.uri.fsPath.toLowerCase())) { - return folder.uri.fsPath; - } - } - } - } - - return undefined; - }; - - private calculateDirectoryChange = async (notebookFile: string, cells: ICell[]): Promise => { - // Make sure we don't already have a cell with a ChangeDirectory comment in it. - let directoryChange: string | undefined; - const haveChangeAlready = cells.find(c => - concatMultilineStringInput(c.data.source).includes(CodeSnippits.ChangeDirectoryCommentIdentifier) - ); - if (!haveChangeAlready) { - const notebookFilePath = path.dirname(notebookFile); - // First see if we have a workspace open, this only works if we have a workspace root to be relative to - if (this.workspaceService.hasWorkspaceFolders) { - const workspacePath = await this.firstWorkspaceFolder(cells); - - // Make sure that we have everything that we need here - if ( - workspacePath && - path.isAbsolute(workspacePath) && - notebookFilePath && - path.isAbsolute(notebookFilePath) - ) { - directoryChange = path.relative(notebookFilePath, workspacePath); - } - } - } - - // If path.relative can't calculate a relative path, then it just returns the full second path - // so check here, we only want this if we were able to calculate a relative path, no network shares or drives - if (directoryChange && !path.isAbsolute(directoryChange)) { - // Escape windows path chars so they end up in the source escaped - if (this.platform.isWindows) { - directoryChange = directoryChange.replace('\\', '\\\\'); - } - - return directoryChange; - } else { - return undefined; - } - }; - - private pruneCells = (cells: ICell[], cellMatcher: CellMatcher): nbformat.IBaseCell[] => { - // First filter out sys info cells. Jupyter doesn't understand these - return ( - cells - .filter(c => c.data.cell_type !== 'messages') - // Then prune each cell down to just the cell data. - .map(c => this.pruneCell(c, cellMatcher)) - ); - }; - - private pruneCell = (cell: ICell, cellMatcher: CellMatcher): nbformat.IBaseCell => { - // Remove the #%% of the top of the source if there is any. We don't need - // this to end up in the exported ipynb file. - const copy = { ...cell.data }; - copy.source = this.pruneSource(cell.data.source, cellMatcher); - return copy; - }; - - private pruneSource = (source: nbformat.MultilineString, cellMatcher: CellMatcher): nbformat.MultilineString => { - // Remove the comments on the top if there. - if (Array.isArray(source) && source.length > 0) { - if (cellMatcher.isCell(source[0])) { - return source.slice(1); - } - } else { - const array = source - .toString() - .split('\n') - .map(s => `${s}\n`); - if (array.length > 0 && cellMatcher.isCell(array[0])) { - return array.slice(1); - } - } - - return source; - }; - - private extractPythonMainVersion = async (): Promise => { - // Use the active interpreter - const usableInterpreter = await this.jupyterExecution.getUsableJupyterPython(); - return usableInterpreter && usableInterpreter.version ? usableInterpreter.version.major : 3; - }; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { nbformat } from '@jupyterlab/coreutils'; +import { inject, injectable } from 'inversify'; +import * as os from 'os'; +import * as path from 'path'; +import * as uuid from 'uuid/v4'; + +import { Uri } from 'vscode'; +import { concatMultilineStringInput } from '../../../datascience-ui/common'; +import { createCodeCell } from '../../../datascience-ui/common/cellFactory'; +import { IApplicationShell, IWorkspaceService } from '../../common/application/types'; +import { traceError } from '../../common/logger'; +import { IFileSystem, IPlatformService } from '../../common/platform/types'; +import { IConfigurationService } from '../../common/types'; +import * as localize from '../../common/utils/localize'; +import { noop } from '../../common/utils/misc'; +import { CellMatcher } from '../cellMatcher'; +import { CodeSnippits, Identifiers } from '../constants'; +import { + CellState, + ICell, + IDataScienceErrorHandler, + IJupyterExecution, + INotebookEditorProvider, + INotebookExporter +} from '../types'; + +@injectable() +export class JupyterExporter implements INotebookExporter { + constructor( + @inject(IJupyterExecution) private jupyterExecution: IJupyterExecution, + @inject(IWorkspaceService) private workspaceService: IWorkspaceService, + @inject(IConfigurationService) private configService: IConfigurationService, + @inject(IFileSystem) private fileSystem: IFileSystem, + @inject(IPlatformService) private readonly platform: IPlatformService, + @inject(IApplicationShell) private readonly applicationShell: IApplicationShell, + @inject(INotebookEditorProvider) protected ipynbProvider: INotebookEditorProvider, + @inject(IDataScienceErrorHandler) protected errorHandler: IDataScienceErrorHandler + ) {} + + public dispose() { + noop(); + } + + public async exportToFile(cells: ICell[], file: string): Promise { + let directoryChange; + const settings = this.configService.getSettings(); + if (settings.datascience.changeDirOnImportExport) { + directoryChange = file; + } + + const notebook = await this.translateToNotebook(cells, directoryChange); + + try { + // tslint:disable-next-line: no-any + const contents = JSON.stringify(notebook); + await this.fileSystem.writeFile(file, contents, { encoding: 'utf8', flag: 'w' }); + const openQuestion1 = localize.DataScience.exportOpenQuestion1(); + const openQuestion2 = (await this.jupyterExecution.isSpawnSupported()) + ? localize.DataScience.exportOpenQuestion() + : undefined; + this.showInformationMessage( + localize.DataScience.exportDialogComplete().format(file), + openQuestion1, + openQuestion2 + ).then(async (str: string | undefined) => { + try { + if (str === openQuestion2 && openQuestion2) { + // If the user wants to, open the notebook they just generated. + await this.jupyterExecution.spawnNotebook(file); + } else if (str === openQuestion1) { + await this.ipynbProvider.open(Uri.file(file)); + } + } catch (e) { + await this.errorHandler.handleError(e); + } + }); + } catch (exc) { + traceError('Error in exporting notebook file'); + this.applicationShell.showInformationMessage(localize.DataScience.exportDialogFailed().format(exc)); + } + } + public async translateToNotebook( + cells: ICell[], + changeDirectory?: string + ): Promise { + // If requested, add in a change directory cell to fix relative paths + if (changeDirectory && this.configService.getSettings().datascience.changeDirOnImportExport) { + cells = await this.addDirectoryChangeCell(cells, changeDirectory); + } + + const pythonNumber = await this.extractPythonMainVersion(); + + // Use this to build our metadata object + const metadata: nbformat.INotebookMetadata = { + language_info: { + name: 'python', + codemirror_mode: { + name: 'ipython', + version: pythonNumber + } + }, + orig_nbformat: 2, + file_extension: '.py', + mimetype: 'text/x-python', + name: 'python', + npconvert_exporter: 'python', + pygments_lexer: `ipython${pythonNumber}`, + version: pythonNumber + }; + + // Create an object for matching cell definitions + const matcher = new CellMatcher(this.configService.getSettings().datascience); + + // Combine this into a JSON object + return { + cells: this.pruneCells(cells, matcher), + nbformat: 4, + nbformat_minor: 2, + metadata: metadata + }; + } + + private showInformationMessage( + message: string, + question1: string, + question2?: string + ): Thenable { + if (question2) { + return this.applicationShell.showInformationMessage(message, question1, question2); + } else { + return this.applicationShell.showInformationMessage(message, question1); + } + } + + // For exporting, put in a cell that will change the working directory back to the workspace directory so relative data paths will load correctly + private addDirectoryChangeCell = async (cells: ICell[], file: string): Promise => { + const changeDirectory = await this.calculateDirectoryChange(file, cells); + + if (changeDirectory) { + const exportChangeDirectory = CodeSnippits.ChangeDirectory.join(os.EOL).format( + localize.DataScience.exportChangeDirectoryComment(), + CodeSnippits.ChangeDirectoryCommentIdentifier, + changeDirectory + ); + + const cell: ICell = { + data: createCodeCell(exportChangeDirectory), + id: uuid(), + file: Identifiers.EmptyFileName, + line: 0, + state: CellState.finished + }; + + return [cell, ...cells]; + } else { + return cells; + } + }; + + // When we export we want to our change directory back to the first real file that we saw run from any workspace folder + private firstWorkspaceFolder = async (cells: ICell[]): Promise => { + for (const cell of cells) { + const filename = cell.file; + + // First check that this is an absolute file that exists (we add in temp files to run system cell) + if (path.isAbsolute(filename) && (await this.fileSystem.fileExists(filename))) { + // We've already check that workspace folders above + for (const folder of this.workspaceService.workspaceFolders!) { + if (filename.toLowerCase().startsWith(folder.uri.fsPath.toLowerCase())) { + return folder.uri.fsPath; + } + } + } + } + + return undefined; + }; + + private calculateDirectoryChange = async (notebookFile: string, cells: ICell[]): Promise => { + // Make sure we don't already have a cell with a ChangeDirectory comment in it. + let directoryChange: string | undefined; + const haveChangeAlready = cells.find((c) => + concatMultilineStringInput(c.data.source).includes(CodeSnippits.ChangeDirectoryCommentIdentifier) + ); + if (!haveChangeAlready) { + const notebookFilePath = path.dirname(notebookFile); + // First see if we have a workspace open, this only works if we have a workspace root to be relative to + if (this.workspaceService.hasWorkspaceFolders) { + const workspacePath = await this.firstWorkspaceFolder(cells); + + // Make sure that we have everything that we need here + if ( + workspacePath && + path.isAbsolute(workspacePath) && + notebookFilePath && + path.isAbsolute(notebookFilePath) + ) { + directoryChange = path.relative(notebookFilePath, workspacePath); + } + } + } + + // If path.relative can't calculate a relative path, then it just returns the full second path + // so check here, we only want this if we were able to calculate a relative path, no network shares or drives + if (directoryChange && !path.isAbsolute(directoryChange)) { + // Escape windows path chars so they end up in the source escaped + if (this.platform.isWindows) { + directoryChange = directoryChange.replace('\\', '\\\\'); + } + + return directoryChange; + } else { + return undefined; + } + }; + + private pruneCells = (cells: ICell[], cellMatcher: CellMatcher): nbformat.IBaseCell[] => { + // First filter out sys info cells. Jupyter doesn't understand these + return ( + cells + .filter((c) => c.data.cell_type !== 'messages') + // Then prune each cell down to just the cell data. + .map((c) => this.pruneCell(c, cellMatcher)) + ); + }; + + private pruneCell = (cell: ICell, cellMatcher: CellMatcher): nbformat.IBaseCell => { + // Remove the #%% of the top of the source if there is any. We don't need + // this to end up in the exported ipynb file. + const copy = { ...cell.data }; + copy.source = this.pruneSource(cell.data.source, cellMatcher); + return copy; + }; + + private pruneSource = (source: nbformat.MultilineString, cellMatcher: CellMatcher): nbformat.MultilineString => { + // Remove the comments on the top if there. + if (Array.isArray(source) && source.length > 0) { + if (cellMatcher.isCell(source[0])) { + return source.slice(1); + } + } else { + const array = source + .toString() + .split('\n') + .map((s) => `${s}\n`); + if (array.length > 0 && cellMatcher.isCell(array[0])) { + return array.slice(1); + } + } + + return source; + }; + + private extractPythonMainVersion = async (): Promise => { + // Use the active interpreter + const usableInterpreter = await this.jupyterExecution.getUsableJupyterPython(); + return usableInterpreter && usableInterpreter.version ? usableInterpreter.version.major : 3; + }; +} diff --git a/src/client/datascience/jupyter/jupyterInstallError.ts b/src/client/datascience/jupyter/jupyterInstallError.ts index c603b4dd5627..6af845382351 100644 --- a/src/client/datascience/jupyter/jupyterInstallError.ts +++ b/src/client/datascience/jupyter/jupyterInstallError.ts @@ -1,16 +1,16 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../common/extensions'; -import { HelpLinks } from '../constants'; - -export class JupyterInstallError extends Error { - public action: string; - public actionTitle: string; - - constructor(message: string, actionFormatString: string) { - super(message); - this.action = HelpLinks.PythonInteractiveHelpLink; - this.actionTitle = actionFormatString.format(HelpLinks.PythonInteractiveHelpLink); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../common/extensions'; +import { HelpLinks } from '../constants'; + +export class JupyterInstallError extends Error { + public action: string; + public actionTitle: string; + + constructor(message: string, actionFormatString: string) { + super(message); + this.action = HelpLinks.PythonInteractiveHelpLink; + this.actionTitle = actionFormatString.format(HelpLinks.PythonInteractiveHelpLink); + } +} diff --git a/src/client/datascience/jupyter/jupyterInterruptError.ts b/src/client/datascience/jupyter/jupyterInterruptError.ts index 8189045a6359..172899250d3d 100644 --- a/src/client/datascience/jupyter/jupyterInterruptError.ts +++ b/src/client/datascience/jupyter/jupyterInterruptError.ts @@ -1,9 +1,9 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -export class JupyterInterruptError extends Error { - constructor(message: string) { - super(message); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +export class JupyterInterruptError extends Error { + constructor(message: string) { + super(message); + } +} diff --git a/src/client/datascience/jupyter/jupyterNotebook.ts b/src/client/datascience/jupyter/jupyterNotebook.ts index d09dfa08acb9..eb29d23b3de9 100644 --- a/src/client/datascience/jupyter/jupyterNotebook.ts +++ b/src/client/datascience/jupyter/jupyterNotebook.ts @@ -309,7 +309,7 @@ export class JupyterNotebookBase implements INotebook { (cells: ICell[]) => { output = cells; }, - error => { + (error) => { deferred.reject(error); }, () => { @@ -340,14 +340,14 @@ export class JupyterNotebookBase implements INotebook { // Ask session for inspect result this.session .requestInspect({ code, cursor_pos: 0, detail_level: 0 }) - .then(r => { + .then((r) => { if (r && r.content.status === 'ok') { deferred.resolve(r.content.data); } else { deferred.resolve(undefined); } }) - .catch(ex => { + .catch((ex) => { deferred.reject(ex); }); } @@ -376,12 +376,12 @@ export class JupyterNotebookBase implements INotebook { // Create an observable and wrap the result so we can time it. const stopWatch = new StopWatch(); const result = this.executeObservableImpl(code, file, line, id, silent); - return new Observable(subscriber => { + return new Observable((subscriber) => { result.subscribe( - cells => { + (cells) => { subscriber.next(cells); }, - error => { + (error) => { subscriber.error(error); }, () => { @@ -486,7 +486,7 @@ export class JupyterNotebookBase implements INotebook { const restartHandlerToken = this.session.onSessionStatusChanged(restartHandler); // Start our interrupt. If it fails, indicate a restart - this.session.interrupt(timeoutMs).catch(exc => { + this.session.interrupt(timeoutMs).catch((exc) => { traceWarning(`Error during interrupt: ${exc}`); restarted.resolve([]); }); @@ -712,7 +712,7 @@ export class JupyterNotebookBase implements INotebook { private finishUncompletedCells() { const copyPending = [...this.pendingCellSubscriptions]; - copyPending.forEach(c => c.cancel()); + copyPending.forEach((c) => c.cancel()); this.pendingCellSubscriptions = []; } @@ -729,7 +729,7 @@ export class JupyterNotebookBase implements INotebook { (cells: ICell[]) => { output = cells; }, - error => { + (error) => { deferred.reject(error); }, () => { @@ -752,7 +752,7 @@ export class JupyterNotebookBase implements INotebook { if (cell.state === CellState.error || cell.state === CellState.finished) { const outputs = cell.data.outputs as nbformat.IOutput[]; if (outputs) { - outputs.forEach(o => { + outputs.forEach((o) => { if (o.output_type === 'stream') { const stream = o as nbformat.IStream; result = result.concat(formatStreamText(concatMultilineStringOutput(stream.text))); @@ -808,7 +808,7 @@ export class JupyterNotebookBase implements INotebook { traceError('No session during execute observable'); // Can't run because no session - return new Observable(subscriber => { + return new Observable((subscriber) => { subscriber.error(this.getDisposedError()); subscriber.complete(); }); @@ -842,13 +842,13 @@ export class JupyterNotebookBase implements INotebook { }; private combineObservables = (...args: Observable[]): Observable => { - return new Observable(subscriber => { + return new Observable((subscriber) => { // When all complete, we have our results const results: Record = {}; - args.forEach(o => { + args.forEach((o) => { o.subscribe( - c => { + (c) => { results[c.id] = c; // Convert to an array @@ -861,12 +861,12 @@ export class JupyterNotebookBase implements INotebook { subscriber.next(array); // Complete when everybody is finished - if (array.every(a => a.state === CellState.finished || a.state === CellState.error)) { + if (array.every((a) => a.state === CellState.finished || a.state === CellState.error)) { subscriber.complete(); } } }, - e => { + (e) => { subscriber.error(e); } ); @@ -876,7 +876,7 @@ export class JupyterNotebookBase implements INotebook { private executeMarkdownObservable = (cell: ICell): Observable => { // Markdown doesn't need any execution - return new Observable(subscriber => { + return new Observable((subscriber) => { subscriber.next(cell); subscriber.complete(); }); @@ -956,7 +956,7 @@ export class JupyterNotebookBase implements INotebook { // Tell all of the listeners about the event. They can cause this to not return until // they are done handling the event. // One such example is a comm_msg for ipywidgets. We have to wait for it to finish. - result = Promise.all([...this.ioPubListeners].map(l => l(msg, msg.header.msg_id))); + result = Promise.all([...this.ioPubListeners].map((l) => l(msg, msg.header.msg_id))); // Show our update if any new output. subscriber.next(this.sessionStartTime); @@ -988,7 +988,7 @@ export class JupyterNotebookBase implements INotebook { prompt: msg.content.prompt ? msg.content.prompt.toString() : '', password: hasPassword }) - .then(v => { + .then((v) => { this.session.sendInputReply(v || ''); }); } @@ -1039,7 +1039,7 @@ export class JupyterNotebookBase implements INotebook { let exitHandlerDisposable: Disposable | undefined; if (this.launchInfo && this.launchInfo.connectionInfo) { // If the server crashes, cancel the current observable - exitHandlerDisposable = this.launchInfo.connectionInfo.disconnected(c => { + exitHandlerDisposable = this.launchInfo.connectionInfo.disconnected((c) => { const str = c ? c.toString() : ''; // Only do an error if we're not disposed. If we're disposed we already shutdown. if (!this._disposed) { @@ -1072,7 +1072,7 @@ export class JupyterNotebookBase implements INotebook { // When the request finishes we are done request.done .then(() => subscriber.complete(this.sessionStartTime)) - .catch(e => { + .catch((e) => { // @jupyterlab/services throws a `Canceled` error when the kernel is interrupted. // Such an error must be ignored. if (e && e instanceof Error && e.message === 'Canceled') { @@ -1106,7 +1106,7 @@ export class JupyterNotebookBase implements INotebook { }; private executeCodeObservable(cell: ICell, silent?: boolean): Observable { - return new Observable(subscriber => { + return new Observable((subscriber) => { // Tell our listener. NOTE: have to do this asap so that markdown cells don't get // run before our cells. subscriber.next(cell); @@ -1116,7 +1116,7 @@ export class JupyterNotebookBase implements INotebook { // synchronously so it happens before interruptions. const cellSubscriber = new CellSubscriber(cell, subscriber, (self: CellSubscriber) => { // Subscriber completed, remove from subscriptions. - this.pendingCellSubscriptions = this.pendingCellSubscriptions.filter(p => p !== self); + this.pendingCellSubscriptions = this.pendingCellSubscriptions.filter((p) => p !== self); // Indicate success or failure this.logPostCode(cell, isSilent).ignoreErrors(); @@ -1134,11 +1134,11 @@ export class JupyterNotebookBase implements INotebook { } private async logPreCode(cell: ICell, silent: boolean): Promise { - await Promise.all(this.loggers.map(l => l.preExecute(cell, silent))); + await Promise.all(this.loggers.map((l) => l.preExecute(cell, silent))); } private async logPostCode(cell: ICell, silent: boolean): Promise { - await Promise.all(this.loggers.map(l => l.postExecute(cloneDeep(cell), silent))); + await Promise.all(this.loggers.map((l) => l.postExecute(cloneDeep(cell), silent))); } private addToCellData = ( @@ -1197,7 +1197,7 @@ export class JupyterNotebookBase implements INotebook { ) { const reply = msg.content as KernelMessage.IExecuteReply; if (reply.payload) { - reply.payload.forEach(o => { + reply.payload.forEach((o) => { if (o.data && o.data.hasOwnProperty('text/plain')) { // tslint:disable-next-line: no-any const str = (o.data as any)['text/plain'].toString(); @@ -1292,7 +1292,7 @@ export class JupyterNotebookBase implements INotebook { private handleUpdateDisplayData(msg: KernelMessage.IUpdateDisplayDataMsg, _clearState: RefBool, cell: ICell) { // Should already have a display data output in our cell. const data: nbformat.ICodeCell = cell.data as nbformat.ICodeCell; - const output = data.outputs.find(o => o.output_type === 'display_data'); + const output = data.outputs.find((o) => o.output_type === 'display_data'); if (output) { output.data = msg.content.data; output.metadata = msg.content.metadata; @@ -1345,7 +1345,7 @@ export class JupyterNotebookBase implements INotebook { // In the error scenario, we want to stop all other pending cells. if (this.configService.getSettings(this.resource).datascience.stopOnError) { - this.pendingCellSubscriptions.forEach(c => { + this.pendingCellSubscriptions.forEach((c) => { if (c.cell.id !== cell.id) { c.cancel(); } diff --git a/src/client/datascience/jupyter/jupyterPasswordConnect.ts b/src/client/datascience/jupyter/jupyterPasswordConnect.ts index ea35808affe9..63840525a068 100644 --- a/src/client/datascience/jupyter/jupyterPasswordConnect.ts +++ b/src/client/datascience/jupyter/jupyterPasswordConnect.ts @@ -177,7 +177,7 @@ export class JupyterPasswordConnect implements IJupyterPasswordConnect { const cookies: string | null = response.headers.get('set-cookie'); if (cookies) { - cookies.split(';').forEach(value => { + cookies.split(';').forEach((value) => { const cookieKey = value.substring(0, value.indexOf('=')); const cookieVal = value.substring(value.indexOf('=') + 1); cookieList.set(cookieKey, cookieVal); diff --git a/src/client/datascience/jupyter/jupyterServer.ts b/src/client/datascience/jupyter/jupyterServer.ts index 14fcd4814cf1..2c4ba72763d7 100644 --- a/src/client/datascience/jupyter/jupyterServer.ts +++ b/src/client/datascience/jupyter/jupyterServer.ts @@ -1,255 +1,255 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { nbformat } from '@jupyterlab/coreutils'; -import * as uuid from 'uuid/v4'; -import { Disposable, Uri } from 'vscode'; -import { CancellationToken } from 'vscode-jsonrpc'; -import { ILiveShareApi } from '../../common/application/types'; -import '../../common/extensions'; -import { traceError, traceInfo } from '../../common/logger'; -import { - IAsyncDisposableRegistry, - IConfigurationService, - IDisposableRegistry, - IOutputChannel, - Resource -} from '../../common/types'; -import { createDeferred, Deferred } from '../../common/utils/async'; -import * as localize from '../../common/utils/localize'; -import { noop } from '../../common/utils/misc'; -import { IServiceContainer } from '../../ioc/types'; -import { - IConnection, - IJupyterSession, - IJupyterSessionManager, - IJupyterSessionManagerFactory, - INotebook, - INotebookServer, - INotebookServerLaunchInfo -} from '../types'; - -// This code is based on the examples here: -// https://www.npmjs.com/package/@jupyterlab/services - -export class JupyterServerBase implements INotebookServer { - private launchInfo: INotebookServerLaunchInfo | undefined; - private _id = uuid(); - private connectPromise: Deferred = createDeferred(); - private connectionInfoDisconnectHandler: Disposable | undefined; - private serverExitCode: number | undefined; - private notebooks = new Map>(); - private sessionManager: IJupyterSessionManager | undefined; - private savedSession: IJupyterSession | undefined; - - constructor( - _liveShare: ILiveShareApi, - private asyncRegistry: IAsyncDisposableRegistry, - private disposableRegistry: IDisposableRegistry, - protected readonly configService: IConfigurationService, - private sessionManagerFactory: IJupyterSessionManagerFactory, - private serviceContainer: IServiceContainer, - private jupyterOutputChannel: IOutputChannel - ) { - this.asyncRegistry.push(this); - } - - public async connect(launchInfo: INotebookServerLaunchInfo, cancelToken?: CancellationToken): Promise { - traceInfo( - `Connecting server ${this.id} kernelSpec ${launchInfo.kernelSpec ? launchInfo.kernelSpec.name : 'unknown'}` - ); - - // Save our launch info - this.launchInfo = launchInfo; - - // Indicate connect started - this.connectPromise.resolve(launchInfo); - - // Listen to the process going down - if (this.launchInfo && this.launchInfo.connectionInfo) { - this.connectionInfoDisconnectHandler = this.launchInfo.connectionInfo.disconnected(c => { - try { - this.serverExitCode = c; - traceError(localize.DataScience.jupyterServerCrashed().format(c.toString())); - this.shutdown().ignoreErrors(); - } catch { - noop(); - } - }); - } - - // Indicate we have a new session on the output channel - this.logRemoteOutput(localize.DataScience.connectingToJupyterUri().format(launchInfo.connectionInfo.baseUrl)); - - // Create our session manager - this.sessionManager = await this.sessionManagerFactory.create(launchInfo.connectionInfo); - // Try creating a session just to ensure we're connected. Callers of this function check to make sure jupyter - // is running and connectable. - let session: IJupyterSession | undefined; - session = await this.sessionManager.startNew(launchInfo.kernelSpec, cancelToken); - const idleTimeout = this.configService.getSettings().datascience.jupyterLaunchTimeout; - // The wait for idle should throw if we can't connect. - await session.waitForIdle(idleTimeout); - // If that works, save this session for the next notebook to use - this.savedSession = session; - } - - public createNotebook( - resource: Resource, - identity: Uri, - notebookMetadata?: nbformat.INotebookMetadata, - cancelToken?: CancellationToken - ): Promise { - if (!this.sessionManager) { - throw new Error(localize.DataScience.sessionDisposed()); - } - // If we have a saved session send this into the notebook so we don't create a new one - const savedSession = this.savedSession; - this.savedSession = undefined; - - // Create a notebook and return it. - return this.createNotebookInstance( - resource, - identity, - this.sessionManager, - savedSession, - this.disposableRegistry, - this.configService, - this.serviceContainer, - notebookMetadata, - cancelToken - ).then(r => { - const baseUrl = this.launchInfo?.connectionInfo.baseUrl || ''; - this.logRemoteOutput(localize.DataScience.createdNewNotebook().format(baseUrl)); - return r; - }); - } - - public async shutdown(): Promise { - // Order should be - // 1) connectionInfoDisconnectHandler - listens to process close - // 2) sessions (owned by the notebooks) - // 3) session manager (owned by this object) - // 4) connInfo (owned by this object) - kills the jupyter process - - if (this.connectionInfoDisconnectHandler) { - this.connectionInfoDisconnectHandler.dispose(); - this.connectionInfoDisconnectHandler = undefined; - } - - // Destroy the kernel spec - await this.destroyKernelSpec(); - - // Remove the saved session if we haven't passed it onto a notebook - if (this.savedSession) { - await this.savedSession.dispose(); - this.savedSession = undefined; - } - - traceInfo(`Shutting down notebooks for ${this.id}`); - const notebooks = await Promise.all([...this.notebooks.values()]); - await Promise.all(notebooks.map(n => n?.dispose())); - traceInfo(`Shut down session manager`); - if (this.sessionManager) { - await this.sessionManager.dispose(); - this.sessionManager = undefined; - } - - // After shutting down notebooks and session manager, kill the main process. - if (this.launchInfo && this.launchInfo.connectionInfo) { - traceInfo('Shutdown server - dispose conn info'); - this.launchInfo.connectionInfo.dispose(); // This should kill the process that's running - this.launchInfo = undefined; - } - } - - public dispose(): Promise { - return this.shutdown(); - } - - public get id(): string { - return this._id; - } - - public waitForConnect(): Promise { - return this.connectPromise.promise; - } - - // Return a copy of the connection information that this server used to connect with - public getConnectionInfo(): IConnection | undefined { - if (!this.launchInfo) { - return undefined; - } - - // Return a copy with a no-op for dispose - return { - ...this.launchInfo.connectionInfo, - dispose: noop - }; - } - - public getDisposedError(): Error { - // We may have been disposed because of a crash. See if our connection info is indicating shutdown - if (this.serverExitCode) { - return new Error(localize.DataScience.jupyterServerCrashed().format(this.serverExitCode.toString())); - } - - // Default is just say session was disposed - return new Error(localize.DataScience.sessionDisposed()); - } - - public async getNotebook(identity: Uri): Promise { - return this.notebooks.get(identity.toString()); - } - - protected getNotebooks(): Promise[] { - return [...this.notebooks.values()]; - } - - protected setNotebook(identity: Uri, notebook: Promise) { - const removeNotebook = () => { - if (this.notebooks.get(identity.toString()) === notebook) { - this.notebooks.delete(identity.toString()); - } - }; - - notebook - .then(nb => { - const oldDispose = nb.dispose; - nb.dispose = () => { - this.notebooks.delete(identity.toString()); - return oldDispose(); - }; - }) - .catch(removeNotebook); - - // Save the notebook - this.notebooks.set(identity.toString(), notebook); - } - - protected createNotebookInstance( - _resource: Resource, - _identity: Uri, - _sessionManager: IJupyterSessionManager, - _savedSession: IJupyterSession | undefined, - _disposableRegistry: IDisposableRegistry, - _configService: IConfigurationService, - _serviceContainer: IServiceContainer, - _notebookMetadata?: nbformat.INotebookMetadata, - _cancelToken?: CancellationToken - ): Promise { - throw new Error('You forgot to override createNotebookInstance'); - } - - private async destroyKernelSpec() { - if (this.launchInfo) { - this.launchInfo.kernelSpec = undefined; - } - } - - private logRemoteOutput(output: string) { - if (this.launchInfo && !this.launchInfo.connectionInfo.localLaunch) { - this.jupyterOutputChannel.appendLine(output); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { nbformat } from '@jupyterlab/coreutils'; +import * as uuid from 'uuid/v4'; +import { Disposable, Uri } from 'vscode'; +import { CancellationToken } from 'vscode-jsonrpc'; +import { ILiveShareApi } from '../../common/application/types'; +import '../../common/extensions'; +import { traceError, traceInfo } from '../../common/logger'; +import { + IAsyncDisposableRegistry, + IConfigurationService, + IDisposableRegistry, + IOutputChannel, + Resource +} from '../../common/types'; +import { createDeferred, Deferred } from '../../common/utils/async'; +import * as localize from '../../common/utils/localize'; +import { noop } from '../../common/utils/misc'; +import { IServiceContainer } from '../../ioc/types'; +import { + IConnection, + IJupyterSession, + IJupyterSessionManager, + IJupyterSessionManagerFactory, + INotebook, + INotebookServer, + INotebookServerLaunchInfo +} from '../types'; + +// This code is based on the examples here: +// https://www.npmjs.com/package/@jupyterlab/services + +export class JupyterServerBase implements INotebookServer { + private launchInfo: INotebookServerLaunchInfo | undefined; + private _id = uuid(); + private connectPromise: Deferred = createDeferred(); + private connectionInfoDisconnectHandler: Disposable | undefined; + private serverExitCode: number | undefined; + private notebooks = new Map>(); + private sessionManager: IJupyterSessionManager | undefined; + private savedSession: IJupyterSession | undefined; + + constructor( + _liveShare: ILiveShareApi, + private asyncRegistry: IAsyncDisposableRegistry, + private disposableRegistry: IDisposableRegistry, + protected readonly configService: IConfigurationService, + private sessionManagerFactory: IJupyterSessionManagerFactory, + private serviceContainer: IServiceContainer, + private jupyterOutputChannel: IOutputChannel + ) { + this.asyncRegistry.push(this); + } + + public async connect(launchInfo: INotebookServerLaunchInfo, cancelToken?: CancellationToken): Promise { + traceInfo( + `Connecting server ${this.id} kernelSpec ${launchInfo.kernelSpec ? launchInfo.kernelSpec.name : 'unknown'}` + ); + + // Save our launch info + this.launchInfo = launchInfo; + + // Indicate connect started + this.connectPromise.resolve(launchInfo); + + // Listen to the process going down + if (this.launchInfo && this.launchInfo.connectionInfo) { + this.connectionInfoDisconnectHandler = this.launchInfo.connectionInfo.disconnected((c) => { + try { + this.serverExitCode = c; + traceError(localize.DataScience.jupyterServerCrashed().format(c.toString())); + this.shutdown().ignoreErrors(); + } catch { + noop(); + } + }); + } + + // Indicate we have a new session on the output channel + this.logRemoteOutput(localize.DataScience.connectingToJupyterUri().format(launchInfo.connectionInfo.baseUrl)); + + // Create our session manager + this.sessionManager = await this.sessionManagerFactory.create(launchInfo.connectionInfo); + // Try creating a session just to ensure we're connected. Callers of this function check to make sure jupyter + // is running and connectable. + let session: IJupyterSession | undefined; + session = await this.sessionManager.startNew(launchInfo.kernelSpec, cancelToken); + const idleTimeout = this.configService.getSettings().datascience.jupyterLaunchTimeout; + // The wait for idle should throw if we can't connect. + await session.waitForIdle(idleTimeout); + // If that works, save this session for the next notebook to use + this.savedSession = session; + } + + public createNotebook( + resource: Resource, + identity: Uri, + notebookMetadata?: nbformat.INotebookMetadata, + cancelToken?: CancellationToken + ): Promise { + if (!this.sessionManager) { + throw new Error(localize.DataScience.sessionDisposed()); + } + // If we have a saved session send this into the notebook so we don't create a new one + const savedSession = this.savedSession; + this.savedSession = undefined; + + // Create a notebook and return it. + return this.createNotebookInstance( + resource, + identity, + this.sessionManager, + savedSession, + this.disposableRegistry, + this.configService, + this.serviceContainer, + notebookMetadata, + cancelToken + ).then((r) => { + const baseUrl = this.launchInfo?.connectionInfo.baseUrl || ''; + this.logRemoteOutput(localize.DataScience.createdNewNotebook().format(baseUrl)); + return r; + }); + } + + public async shutdown(): Promise { + // Order should be + // 1) connectionInfoDisconnectHandler - listens to process close + // 2) sessions (owned by the notebooks) + // 3) session manager (owned by this object) + // 4) connInfo (owned by this object) - kills the jupyter process + + if (this.connectionInfoDisconnectHandler) { + this.connectionInfoDisconnectHandler.dispose(); + this.connectionInfoDisconnectHandler = undefined; + } + + // Destroy the kernel spec + await this.destroyKernelSpec(); + + // Remove the saved session if we haven't passed it onto a notebook + if (this.savedSession) { + await this.savedSession.dispose(); + this.savedSession = undefined; + } + + traceInfo(`Shutting down notebooks for ${this.id}`); + const notebooks = await Promise.all([...this.notebooks.values()]); + await Promise.all(notebooks.map((n) => n?.dispose())); + traceInfo(`Shut down session manager`); + if (this.sessionManager) { + await this.sessionManager.dispose(); + this.sessionManager = undefined; + } + + // After shutting down notebooks and session manager, kill the main process. + if (this.launchInfo && this.launchInfo.connectionInfo) { + traceInfo('Shutdown server - dispose conn info'); + this.launchInfo.connectionInfo.dispose(); // This should kill the process that's running + this.launchInfo = undefined; + } + } + + public dispose(): Promise { + return this.shutdown(); + } + + public get id(): string { + return this._id; + } + + public waitForConnect(): Promise { + return this.connectPromise.promise; + } + + // Return a copy of the connection information that this server used to connect with + public getConnectionInfo(): IConnection | undefined { + if (!this.launchInfo) { + return undefined; + } + + // Return a copy with a no-op for dispose + return { + ...this.launchInfo.connectionInfo, + dispose: noop + }; + } + + public getDisposedError(): Error { + // We may have been disposed because of a crash. See if our connection info is indicating shutdown + if (this.serverExitCode) { + return new Error(localize.DataScience.jupyterServerCrashed().format(this.serverExitCode.toString())); + } + + // Default is just say session was disposed + return new Error(localize.DataScience.sessionDisposed()); + } + + public async getNotebook(identity: Uri): Promise { + return this.notebooks.get(identity.toString()); + } + + protected getNotebooks(): Promise[] { + return [...this.notebooks.values()]; + } + + protected setNotebook(identity: Uri, notebook: Promise) { + const removeNotebook = () => { + if (this.notebooks.get(identity.toString()) === notebook) { + this.notebooks.delete(identity.toString()); + } + }; + + notebook + .then((nb) => { + const oldDispose = nb.dispose; + nb.dispose = () => { + this.notebooks.delete(identity.toString()); + return oldDispose(); + }; + }) + .catch(removeNotebook); + + // Save the notebook + this.notebooks.set(identity.toString(), notebook); + } + + protected createNotebookInstance( + _resource: Resource, + _identity: Uri, + _sessionManager: IJupyterSessionManager, + _savedSession: IJupyterSession | undefined, + _disposableRegistry: IDisposableRegistry, + _configService: IConfigurationService, + _serviceContainer: IServiceContainer, + _notebookMetadata?: nbformat.INotebookMetadata, + _cancelToken?: CancellationToken + ): Promise { + throw new Error('You forgot to override createNotebookInstance'); + } + + private async destroyKernelSpec() { + if (this.launchInfo) { + this.launchInfo.kernelSpec = undefined; + } + } + + private logRemoteOutput(output: string) { + if (this.launchInfo && !this.launchInfo.connectionInfo.localLaunch) { + this.jupyterOutputChannel.appendLine(output); + } + } +} diff --git a/src/client/datascience/jupyter/jupyterSession.ts b/src/client/datascience/jupyter/jupyterSession.ts index 353d83c1bcd7..6f6f7ab7948b 100644 --- a/src/client/datascience/jupyter/jupyterSession.ts +++ b/src/client/datascience/jupyter/jupyterSession.ts @@ -1,615 +1,615 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { - Contents, - ContentsManager, - Kernel, - KernelMessage, - ServerConnection, - Session, - SessionManager -} from '@jupyterlab/services'; -import { JSONObject } from '@phosphor/coreutils'; -import { Slot } from '@phosphor/signaling'; -import * as uuid from 'uuid/v4'; -import { Event, EventEmitter } from 'vscode'; -import { CancellationToken } from 'vscode-jsonrpc'; -import { ServerStatus } from '../../../datascience-ui/interactive-common/mainState'; -import { Cancellation } from '../../common/cancellation'; -import { isTestExecution } from '../../common/constants'; -import { traceError, traceInfo, traceWarning } from '../../common/logger'; -import { IOutputChannel } from '../../common/types'; -import { sleep, waitForPromise } from '../../common/utils/async'; -import * as localize from '../../common/utils/localize'; -import { noop } from '../../common/utils/misc'; -import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; -import { Telemetry } from '../constants'; -import { reportAction } from '../progress/decorator'; -import { ReportableAction } from '../progress/types'; -import { IConnection, IJupyterKernelSpec, IJupyterSession } from '../types'; -import { JupyterInvalidKernelError } from './jupyterInvalidKernelError'; -import { JupyterWaitForIdleError } from './jupyterWaitForIdleError'; -import { JupyterKernelPromiseFailedError } from './kernels/jupyterKernelPromiseFailedError'; -import { KernelSelector } from './kernels/kernelSelector'; -import { LiveKernelModel } from './kernels/types'; - -type ISession = Session.ISession & { - /** - * Whether this is a remote session that we attached to. - * - * @type {boolean} - */ - isRemoteSession?: boolean; -}; - -/** - * Exception raised when starting a Jupyter Session fails. - * - * @export - * @class JupyterSessionStartError - * @extends {Error} - */ -export class JupyterSessionStartError extends Error { - constructor(originalException: Error) { - super(originalException.message); - this.stack = originalException.stack; - sendTelemetryEvent(Telemetry.StartSessionFailedJupyter); - } -} - -export class JupyterSession implements IJupyterSession { - private session: ISession | undefined; - private restartSessionPromise: Promise | undefined; - private notebookFiles: Contents.IModel[] = []; - private onStatusChangedEvent: EventEmitter = new EventEmitter(); - private statusHandler: Slot; - private connected: boolean = false; - private _jupyterLab?: typeof import('@jupyterlab/services'); - constructor( - private connInfo: IConnection, - private serverSettings: ServerConnection.ISettings, - private kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, - private sessionManager: SessionManager, - private contentsManager: ContentsManager, - private readonly kernelSelector: KernelSelector, - private readonly outputChannel: IOutputChannel - ) { - this.statusHandler = this.onStatusChanged.bind(this); - } - private get jupyterLab(): undefined | typeof import('@jupyterlab/services') { - if (!this._jupyterLab) { - // tslint:disable-next-line:no-require-imports - this._jupyterLab = require('@jupyterlab/services') as typeof import('@jupyterlab/services'); // NOSONAR - } - return this._jupyterLab; - } - public dispose(): Promise { - return this.shutdown(); - } - - public async shutdown(): Promise { - // Destroy the notebook file if not local. Local is cleaned up when we destroy the kernel spec. - if (this.notebookFiles.length && this.contentsManager && this.connInfo && !this.connInfo.localLaunch) { - try { - // Make sure we have a session first and it returns something - await this.sessionManager.refreshRunning(); - await Promise.all(this.notebookFiles.map(f => this.contentsManager!.delete(f.path))); - this.notebookFiles = []; - } catch { - noop(); - } - } - if (this.session) { - try { - traceInfo('Shutdown session - current session'); - await this.shutdownSession(this.session, this.statusHandler); - traceInfo('Shutdown session - get restart session'); - if (this.restartSessionPromise) { - const restartSession = await this.restartSessionPromise; - traceInfo('Shutdown session - shutdown restart session'); - await this.shutdownSession(restartSession, undefined); - } - } catch { - noop(); - } - this.session = undefined; - this.restartSessionPromise = undefined; - } - if (this.onStatusChangedEvent) { - this.onStatusChangedEvent.dispose(); - } - traceInfo('Shutdown session -- complete'); - } - - public get onSessionStatusChanged(): Event { - if (!this.onStatusChangedEvent) { - this.onStatusChangedEvent = new EventEmitter(); - } - return this.onStatusChangedEvent.event; - } - - public get status(): ServerStatus { - return this.getServerStatus(); - } - - @reportAction(ReportableAction.JupyterSessionWaitForIdleSession) - public async waitForIdle(timeout: number): Promise { - // Wait for idle on this session - await this.waitForIdleOnSession(this.session, timeout); - } - - public async restart(_timeout: number): Promise { - if (this.session?.isRemoteSession) { - await this.session.kernel.restart(); - return; - } - - // Start the restart session now in case it wasn't started - if (!this.restartSessionPromise) { - this.startRestartSession(); - } - - // Just kill the current session and switch to the other - if (this.restartSessionPromise && this.session && this.sessionManager && this.contentsManager) { - traceInfo(`Restarting ${this.session.kernel.id}`); - - // Save old state for shutdown - const oldSession = this.session; - const oldStatusHandler = this.statusHandler; - - // Just switch to the other session. It should already be ready - this.session = await this.restartSessionPromise; - if (!this.session) { - throw new Error(localize.DataScience.sessionDisposed()); - } - this.kernelSelector.removeKernelFromIgnoreList(this.session.kernel); - traceInfo(`Got new session ${this.session.kernel.id}`); - - // Rewire our status changed event. - this.session.statusChanged.connect(this.statusHandler); - - // After switching, start another in case we restart again. - this.restartSessionPromise = this.createRestartSession( - oldSession.serverSettings, - this.kernelSpec, - this.contentsManager - ); - traceInfo('Started new restart session'); - if (oldStatusHandler) { - oldSession.statusChanged.disconnect(oldStatusHandler); - } - this.shutdownSession(oldSession, undefined).ignoreErrors(); - } else { - throw new Error(localize.DataScience.sessionDisposed()); - } - } - - public async interrupt(timeout: number): Promise { - if (this.session && this.session.kernel) { - // Listen for session status changes - this.session.statusChanged.connect(this.statusHandler); - - await this.waitForKernelPromise( - this.session.kernel.interrupt(), - timeout, - localize.DataScience.interruptingKernelFailed() - ); - } - } - - public requestExecute( - content: KernelMessage.IExecuteRequestMsg['content'], - disposeOnDone?: boolean, - metadata?: JSONObject - ): Kernel.IShellFuture | undefined { - const result = - this.session && this.session.kernel - ? this.session.kernel.requestExecute(content, disposeOnDone, metadata) - : undefined; - // It has been observed that starting the restart session slows down first time to execute a cell. - // Solution is to start the restart session after the first execution of user code. - if (!content.silent && result && !isTestExecution()) { - result.done.finally(() => this.startRestartSession()).ignoreErrors(); - } - return result; - } - - public requestInspect( - content: KernelMessage.IInspectRequestMsg['content'] - ): Promise { - return this.session && this.session.kernel - ? this.session.kernel.requestInspect(content) - : Promise.resolve(undefined); - } - - public requestComplete( - content: KernelMessage.ICompleteRequestMsg['content'] - ): Promise { - return this.session && this.session.kernel - ? this.session.kernel.requestComplete(content) - : Promise.resolve(undefined); - } - - public sendInputReply(content: string) { - if (this.session && this.session.kernel) { - // tslint:disable-next-line: no-any - this.session.kernel.sendInputReply({ value: content, status: 'ok' }); - } - } - - public async connect(cancelToken?: CancellationToken): Promise { - if (!this.connInfo) { - throw new Error(localize.DataScience.sessionDisposed()); - } - - // Start a new session - this.session = await this.createSession( - this.serverSettings, - this.kernelSpec, - this.contentsManager, - cancelToken - ); - - // Listen for session status changes - this.session.statusChanged.connect(this.statusHandler); - - // Made it this far, we're connected now - this.connected = true; - } - - public get isConnected(): boolean { - return this.connected; - } - - public async changeKernel(kernel: IJupyterKernelSpec | LiveKernelModel, timeoutMS: number): Promise { - let newSession: ISession | undefined; - - // If we are already using this kernel in an active session just return back - if (this.kernelSpec?.name === kernel.name && this.session) { - return; - } - - try { - // Don't immediately assume this kernel is valid. Try creating a session with it first. - if (kernel.id && this.session && 'session' in kernel) { - // Remote case. - newSession = this.sessionManager.connectTo(kernel.session); - newSession.isRemoteSession = true; - } else { - newSession = await this.createSession(this.serverSettings, kernel, this.contentsManager); - } - - // Make sure it is idle before we return - await this.waitForIdleOnSession(newSession, timeoutMS); - } catch (exc) { - // Throw a new exception indicating we cannot change. - throw new JupyterInvalidKernelError(kernel); - } - - // This is just like doing a restart, kill the old session (and the old restart session), and start new ones - if (this.session) { - this.shutdownSession(this.session, this.statusHandler).ignoreErrors(); - this.restartSessionPromise?.then(r => this.shutdownSession(r, undefined)).ignoreErrors(); - } - - // Update our kernel spec - this.kernelSpec = kernel; - - // Save the new session - this.session = newSession; - - // Listen for session status changes - this.session.statusChanged.connect(this.statusHandler); - - // Start the restart session promise too. - this.restartSessionPromise = this.createRestartSession(this.serverSettings, kernel, this.contentsManager); - } - - public registerCommTarget( - targetName: string, - callback: (comm: Kernel.IComm, msg: KernelMessage.ICommOpenMsg) => void | PromiseLike - ) { - if (this.session && this.session.kernel) { - this.session.kernel.registerCommTarget(targetName, callback); - } else { - throw new Error(localize.DataScience.sessionDisposed()); - } - } - - public sendCommMessage( - buffers: (ArrayBuffer | ArrayBufferView)[], - content: { comm_id: string; data: JSONObject; target_name: string | undefined }, - // tslint:disable-next-line: no-any - metadata: any, - // tslint:disable-next-line: no-any - msgId: any - ): Kernel.IShellFuture< - KernelMessage.IShellMessage<'comm_msg'>, - KernelMessage.IShellMessage - > { - if (this.session && this.session.kernel && this.jupyterLab) { - const shellMessage = this.jupyterLab.KernelMessage.createMessage>({ - // tslint:disable-next-line: no-any - msgType: 'comm_msg', - channel: 'shell', - buffers, - content, - metadata, - msgId, - session: this.session.kernel.clientId, - username: this.session.kernel.username - }); - - return this.session.kernel.sendShellMessage(shellMessage, false, true); - } else { - throw new Error(localize.DataScience.sessionDisposed()); - } - } - - public requestCommInfo( - content: KernelMessage.ICommInfoRequestMsg['content'] - ): Promise { - if (this.session?.kernel) { - return this.session.kernel.requestCommInfo(content); - } else { - throw new Error(localize.DataScience.sessionDisposed()); - } - } - public registerMessageHook( - msgId: string, - hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike - ): void { - if (this.session?.kernel) { - return this.session.kernel.registerMessageHook(msgId, hook); - } else { - throw new Error(localize.DataScience.sessionDisposed()); - } - } - public removeMessageHook( - msgId: string, - hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike - ): void { - if (this.session?.kernel) { - return this.session.kernel.removeMessageHook(msgId, hook); - } else { - throw new Error(localize.DataScience.sessionDisposed()); - } - } - - private getServerStatus(): ServerStatus { - if (this.session) { - switch (this.session.kernel.status) { - case 'busy': - return ServerStatus.Busy; - case 'dead': - return ServerStatus.Dead; - case 'idle': - case 'connected': - return ServerStatus.Idle; - case 'restarting': - case 'autorestarting': - case 'reconnecting': - return ServerStatus.Restarting; - case 'starting': - return ServerStatus.Starting; - default: - return ServerStatus.NotStarted; - } - } - - return ServerStatus.NotStarted; - } - - private startRestartSession() { - if (!this.restartSessionPromise && this.session && this.contentsManager) { - this.restartSessionPromise = this.createRestartSession( - this.session.serverSettings, - this.kernelSpec, - this.contentsManager - ); - } - } - - @captureTelemetry(Telemetry.WaitForIdleJupyter, undefined, true) - private async waitForIdleOnSession(session: ISession | undefined, timeout: number): Promise { - if (session && session.kernel) { - traceInfo(`Waiting for idle on (kernel): ${session.kernel.id} -> ${session.kernel.status}`); - // tslint:disable-next-line: no-any - const statusHandler = (resolve: () => void, reject: (exc: any) => void, e: Kernel.Status | undefined) => { - if (e === 'idle') { - resolve(); - } else if (e === 'dead') { - traceError('Kernel died while waiting for idle'); - // If we throw an exception, make sure to shutdown the session as it's not usable anymore - this.shutdownSession(session, this.statusHandler).ignoreErrors(); - reject( - new JupyterInvalidKernelError({ - ...session.kernel, - lastActivityTime: new Date(), - numberOfConnections: 0, - session: session.model - }) - ); - } - }; - - const kernelStatusChangedPromise = new Promise((resolve, reject) => - session.statusChanged.connect((_, e) => statusHandler(resolve, reject, e)) - ); - const statusChangedPromise = new Promise((resolve, reject) => - session.kernelChanged.connect((_, e) => statusHandler(resolve, reject, e.newValue?.status)) - ); - const checkStatusPromise = new Promise(async resolve => { - // This function seems to cause CI builds to timeout randomly on - // different tests. Waiting for status to go idle doesn't seem to work and - // in the past, waiting on the ready promise doesn't work either. Check status with a maximum of 5 seconds - const startTime = Date.now(); - while ( - session && - session.kernel && - session.kernel.status !== 'idle' && - Date.now() - startTime < timeout - ) { - await sleep(100); - } - resolve(); - }); - await Promise.race([kernelStatusChangedPromise, statusChangedPromise, checkStatusPromise]); - traceInfo(`Finished waiting for idle on (kernel): ${session.kernel.id} -> ${session.kernel.status}`); - - // If we didn't make it out in ten seconds, indicate an error - if (session.kernel && session.kernel.status === 'idle') { - return; - } - - // If we throw an exception, make sure to shutdown the session as it's not usable anymore - this.shutdownSession(session, this.statusHandler).ignoreErrors(); - throw new JupyterWaitForIdleError(localize.DataScience.jupyterLaunchTimedOut()); - } - } - - private async createRestartSession( - serverSettings: ServerConnection.ISettings, - kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, - contentsManager: ContentsManager, - cancelToken?: CancellationToken - ): Promise { - let result: ISession | undefined; - let tryCount = 0; - // tslint:disable-next-line: no-any - let exception: any; - while (tryCount < 3) { - try { - result = await this.createSession(serverSettings, kernelSpec, contentsManager, cancelToken); - await this.waitForIdleOnSession(result, 30000); - this.kernelSelector.addKernelToIgnoreList(result.kernel); - return result; - } catch (exc) { - traceInfo(`Error waiting for restart session: ${exc}`); - tryCount += 1; - if (result) { - this.shutdownSession(result, undefined).ignoreErrors(); - } - result = undefined; - exception = exc; - } - } - throw exception; - } - - private async createSession( - serverSettings: ServerConnection.ISettings, - kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, - contentsManager: ContentsManager, - cancelToken?: CancellationToken - ): Promise { - // Create a temporary notebook for this session. - this.notebookFiles.push(await contentsManager.newUntitled({ type: 'notebook' })); - - // Create our session options using this temporary notebook and our connection info - const options: Session.IOptions = { - path: this.notebookFiles[this.notebookFiles.length - 1].path, - kernelName: kernelSpec ? kernelSpec.name : '', - name: uuid(), // This is crucial to distinguish this session from any other. - serverSettings: serverSettings - }; - - return Cancellation.race( - () => - this.sessionManager!.startNew(options) - .then(s => { - this.logRemoteOutput( - localize.DataScience.createdNewKernel().format(this.connInfo.baseUrl, s?.kernel?.id) - ); - return s; - }) - .catch(ex => Promise.reject(new JupyterSessionStartError(ex))), - cancelToken - ); - } - - private logRemoteOutput(output: string) { - if (this.connInfo && !this.connInfo.localLaunch) { - this.outputChannel.appendLine(output); - } - } - - private async waitForKernelPromise( - kernelPromise: Promise, - timeout: number, - errorMessage: string - ): Promise { - // Wait for this kernel promise to happen - try { - return await waitForPromise(kernelPromise, timeout); - } catch (e) { - if (!e) { - // We timed out. Throw a specific exception - throw new JupyterKernelPromiseFailedError(errorMessage); - } - throw e; - } - } - - private onStatusChanged(_s: Session.ISession) { - if (this.onStatusChangedEvent) { - this.onStatusChangedEvent.fire(this.getServerStatus()); - } - } - - private async shutdownSession( - session: ISession | undefined, - statusHandler: Slot | undefined - ): Promise { - if (session && session.kernel) { - const kernelId = session.kernel.id; - traceInfo(`shutdownSession ${kernelId} - start`); - try { - if (statusHandler) { - session.statusChanged.disconnect(statusHandler); - } - // Do not shutdown remote sessions. - if (session.isRemoteSession) { - session.dispose(); - return; - } - try { - // When running under a test, mark all futures as done so we - // don't hit this problem: - // https://github.com/jupyterlab/jupyterlab/issues/4252 - // tslint:disable:no-any - if (isTestExecution()) { - const defaultKernel = session.kernel as any; - if (defaultKernel && defaultKernel._futures) { - const futures = defaultKernel._futures as Map; - if (futures) { - futures.forEach(f => { - if (f._status !== undefined) { - f._status |= 4; - } - }); - } - } - if (defaultKernel && defaultKernel._reconnectLimit) { - defaultKernel._reconnectLimit = 0; - } - await waitForPromise(session.shutdown(), 1000); - } else { - // Shutdown may fail if the process has been killed - await waitForPromise(session.shutdown(), 1000); - } - } catch { - noop(); - } - if (session && !session.isDisposed) { - session.dispose(); - } - } catch (e) { - // Ignore, just trace. - traceWarning(e); - } - traceInfo(`shutdownSession ${kernelId} - shutdown complete`); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { + Contents, + ContentsManager, + Kernel, + KernelMessage, + ServerConnection, + Session, + SessionManager +} from '@jupyterlab/services'; +import { JSONObject } from '@phosphor/coreutils'; +import { Slot } from '@phosphor/signaling'; +import * as uuid from 'uuid/v4'; +import { Event, EventEmitter } from 'vscode'; +import { CancellationToken } from 'vscode-jsonrpc'; +import { ServerStatus } from '../../../datascience-ui/interactive-common/mainState'; +import { Cancellation } from '../../common/cancellation'; +import { isTestExecution } from '../../common/constants'; +import { traceError, traceInfo, traceWarning } from '../../common/logger'; +import { IOutputChannel } from '../../common/types'; +import { sleep, waitForPromise } from '../../common/utils/async'; +import * as localize from '../../common/utils/localize'; +import { noop } from '../../common/utils/misc'; +import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; +import { Telemetry } from '../constants'; +import { reportAction } from '../progress/decorator'; +import { ReportableAction } from '../progress/types'; +import { IConnection, IJupyterKernelSpec, IJupyterSession } from '../types'; +import { JupyterInvalidKernelError } from './jupyterInvalidKernelError'; +import { JupyterWaitForIdleError } from './jupyterWaitForIdleError'; +import { JupyterKernelPromiseFailedError } from './kernels/jupyterKernelPromiseFailedError'; +import { KernelSelector } from './kernels/kernelSelector'; +import { LiveKernelModel } from './kernels/types'; + +type ISession = Session.ISession & { + /** + * Whether this is a remote session that we attached to. + * + * @type {boolean} + */ + isRemoteSession?: boolean; +}; + +/** + * Exception raised when starting a Jupyter Session fails. + * + * @export + * @class JupyterSessionStartError + * @extends {Error} + */ +export class JupyterSessionStartError extends Error { + constructor(originalException: Error) { + super(originalException.message); + this.stack = originalException.stack; + sendTelemetryEvent(Telemetry.StartSessionFailedJupyter); + } +} + +export class JupyterSession implements IJupyterSession { + private session: ISession | undefined; + private restartSessionPromise: Promise | undefined; + private notebookFiles: Contents.IModel[] = []; + private onStatusChangedEvent: EventEmitter = new EventEmitter(); + private statusHandler: Slot; + private connected: boolean = false; + private _jupyterLab?: typeof import('@jupyterlab/services'); + constructor( + private connInfo: IConnection, + private serverSettings: ServerConnection.ISettings, + private kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, + private sessionManager: SessionManager, + private contentsManager: ContentsManager, + private readonly kernelSelector: KernelSelector, + private readonly outputChannel: IOutputChannel + ) { + this.statusHandler = this.onStatusChanged.bind(this); + } + private get jupyterLab(): undefined | typeof import('@jupyterlab/services') { + if (!this._jupyterLab) { + // tslint:disable-next-line:no-require-imports + this._jupyterLab = require('@jupyterlab/services') as typeof import('@jupyterlab/services'); // NOSONAR + } + return this._jupyterLab; + } + public dispose(): Promise { + return this.shutdown(); + } + + public async shutdown(): Promise { + // Destroy the notebook file if not local. Local is cleaned up when we destroy the kernel spec. + if (this.notebookFiles.length && this.contentsManager && this.connInfo && !this.connInfo.localLaunch) { + try { + // Make sure we have a session first and it returns something + await this.sessionManager.refreshRunning(); + await Promise.all(this.notebookFiles.map((f) => this.contentsManager!.delete(f.path))); + this.notebookFiles = []; + } catch { + noop(); + } + } + if (this.session) { + try { + traceInfo('Shutdown session - current session'); + await this.shutdownSession(this.session, this.statusHandler); + traceInfo('Shutdown session - get restart session'); + if (this.restartSessionPromise) { + const restartSession = await this.restartSessionPromise; + traceInfo('Shutdown session - shutdown restart session'); + await this.shutdownSession(restartSession, undefined); + } + } catch { + noop(); + } + this.session = undefined; + this.restartSessionPromise = undefined; + } + if (this.onStatusChangedEvent) { + this.onStatusChangedEvent.dispose(); + } + traceInfo('Shutdown session -- complete'); + } + + public get onSessionStatusChanged(): Event { + if (!this.onStatusChangedEvent) { + this.onStatusChangedEvent = new EventEmitter(); + } + return this.onStatusChangedEvent.event; + } + + public get status(): ServerStatus { + return this.getServerStatus(); + } + + @reportAction(ReportableAction.JupyterSessionWaitForIdleSession) + public async waitForIdle(timeout: number): Promise { + // Wait for idle on this session + await this.waitForIdleOnSession(this.session, timeout); + } + + public async restart(_timeout: number): Promise { + if (this.session?.isRemoteSession) { + await this.session.kernel.restart(); + return; + } + + // Start the restart session now in case it wasn't started + if (!this.restartSessionPromise) { + this.startRestartSession(); + } + + // Just kill the current session and switch to the other + if (this.restartSessionPromise && this.session && this.sessionManager && this.contentsManager) { + traceInfo(`Restarting ${this.session.kernel.id}`); + + // Save old state for shutdown + const oldSession = this.session; + const oldStatusHandler = this.statusHandler; + + // Just switch to the other session. It should already be ready + this.session = await this.restartSessionPromise; + if (!this.session) { + throw new Error(localize.DataScience.sessionDisposed()); + } + this.kernelSelector.removeKernelFromIgnoreList(this.session.kernel); + traceInfo(`Got new session ${this.session.kernel.id}`); + + // Rewire our status changed event. + this.session.statusChanged.connect(this.statusHandler); + + // After switching, start another in case we restart again. + this.restartSessionPromise = this.createRestartSession( + oldSession.serverSettings, + this.kernelSpec, + this.contentsManager + ); + traceInfo('Started new restart session'); + if (oldStatusHandler) { + oldSession.statusChanged.disconnect(oldStatusHandler); + } + this.shutdownSession(oldSession, undefined).ignoreErrors(); + } else { + throw new Error(localize.DataScience.sessionDisposed()); + } + } + + public async interrupt(timeout: number): Promise { + if (this.session && this.session.kernel) { + // Listen for session status changes + this.session.statusChanged.connect(this.statusHandler); + + await this.waitForKernelPromise( + this.session.kernel.interrupt(), + timeout, + localize.DataScience.interruptingKernelFailed() + ); + } + } + + public requestExecute( + content: KernelMessage.IExecuteRequestMsg['content'], + disposeOnDone?: boolean, + metadata?: JSONObject + ): Kernel.IShellFuture | undefined { + const result = + this.session && this.session.kernel + ? this.session.kernel.requestExecute(content, disposeOnDone, metadata) + : undefined; + // It has been observed that starting the restart session slows down first time to execute a cell. + // Solution is to start the restart session after the first execution of user code. + if (!content.silent && result && !isTestExecution()) { + result.done.finally(() => this.startRestartSession()).ignoreErrors(); + } + return result; + } + + public requestInspect( + content: KernelMessage.IInspectRequestMsg['content'] + ): Promise { + return this.session && this.session.kernel + ? this.session.kernel.requestInspect(content) + : Promise.resolve(undefined); + } + + public requestComplete( + content: KernelMessage.ICompleteRequestMsg['content'] + ): Promise { + return this.session && this.session.kernel + ? this.session.kernel.requestComplete(content) + : Promise.resolve(undefined); + } + + public sendInputReply(content: string) { + if (this.session && this.session.kernel) { + // tslint:disable-next-line: no-any + this.session.kernel.sendInputReply({ value: content, status: 'ok' }); + } + } + + public async connect(cancelToken?: CancellationToken): Promise { + if (!this.connInfo) { + throw new Error(localize.DataScience.sessionDisposed()); + } + + // Start a new session + this.session = await this.createSession( + this.serverSettings, + this.kernelSpec, + this.contentsManager, + cancelToken + ); + + // Listen for session status changes + this.session.statusChanged.connect(this.statusHandler); + + // Made it this far, we're connected now + this.connected = true; + } + + public get isConnected(): boolean { + return this.connected; + } + + public async changeKernel(kernel: IJupyterKernelSpec | LiveKernelModel, timeoutMS: number): Promise { + let newSession: ISession | undefined; + + // If we are already using this kernel in an active session just return back + if (this.kernelSpec?.name === kernel.name && this.session) { + return; + } + + try { + // Don't immediately assume this kernel is valid. Try creating a session with it first. + if (kernel.id && this.session && 'session' in kernel) { + // Remote case. + newSession = this.sessionManager.connectTo(kernel.session); + newSession.isRemoteSession = true; + } else { + newSession = await this.createSession(this.serverSettings, kernel, this.contentsManager); + } + + // Make sure it is idle before we return + await this.waitForIdleOnSession(newSession, timeoutMS); + } catch (exc) { + // Throw a new exception indicating we cannot change. + throw new JupyterInvalidKernelError(kernel); + } + + // This is just like doing a restart, kill the old session (and the old restart session), and start new ones + if (this.session) { + this.shutdownSession(this.session, this.statusHandler).ignoreErrors(); + this.restartSessionPromise?.then((r) => this.shutdownSession(r, undefined)).ignoreErrors(); + } + + // Update our kernel spec + this.kernelSpec = kernel; + + // Save the new session + this.session = newSession; + + // Listen for session status changes + this.session.statusChanged.connect(this.statusHandler); + + // Start the restart session promise too. + this.restartSessionPromise = this.createRestartSession(this.serverSettings, kernel, this.contentsManager); + } + + public registerCommTarget( + targetName: string, + callback: (comm: Kernel.IComm, msg: KernelMessage.ICommOpenMsg) => void | PromiseLike + ) { + if (this.session && this.session.kernel) { + this.session.kernel.registerCommTarget(targetName, callback); + } else { + throw new Error(localize.DataScience.sessionDisposed()); + } + } + + public sendCommMessage( + buffers: (ArrayBuffer | ArrayBufferView)[], + content: { comm_id: string; data: JSONObject; target_name: string | undefined }, + // tslint:disable-next-line: no-any + metadata: any, + // tslint:disable-next-line: no-any + msgId: any + ): Kernel.IShellFuture< + KernelMessage.IShellMessage<'comm_msg'>, + KernelMessage.IShellMessage + > { + if (this.session && this.session.kernel && this.jupyterLab) { + const shellMessage = this.jupyterLab.KernelMessage.createMessage>({ + // tslint:disable-next-line: no-any + msgType: 'comm_msg', + channel: 'shell', + buffers, + content, + metadata, + msgId, + session: this.session.kernel.clientId, + username: this.session.kernel.username + }); + + return this.session.kernel.sendShellMessage(shellMessage, false, true); + } else { + throw new Error(localize.DataScience.sessionDisposed()); + } + } + + public requestCommInfo( + content: KernelMessage.ICommInfoRequestMsg['content'] + ): Promise { + if (this.session?.kernel) { + return this.session.kernel.requestCommInfo(content); + } else { + throw new Error(localize.DataScience.sessionDisposed()); + } + } + public registerMessageHook( + msgId: string, + hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike + ): void { + if (this.session?.kernel) { + return this.session.kernel.registerMessageHook(msgId, hook); + } else { + throw new Error(localize.DataScience.sessionDisposed()); + } + } + public removeMessageHook( + msgId: string, + hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike + ): void { + if (this.session?.kernel) { + return this.session.kernel.removeMessageHook(msgId, hook); + } else { + throw new Error(localize.DataScience.sessionDisposed()); + } + } + + private getServerStatus(): ServerStatus { + if (this.session) { + switch (this.session.kernel.status) { + case 'busy': + return ServerStatus.Busy; + case 'dead': + return ServerStatus.Dead; + case 'idle': + case 'connected': + return ServerStatus.Idle; + case 'restarting': + case 'autorestarting': + case 'reconnecting': + return ServerStatus.Restarting; + case 'starting': + return ServerStatus.Starting; + default: + return ServerStatus.NotStarted; + } + } + + return ServerStatus.NotStarted; + } + + private startRestartSession() { + if (!this.restartSessionPromise && this.session && this.contentsManager) { + this.restartSessionPromise = this.createRestartSession( + this.session.serverSettings, + this.kernelSpec, + this.contentsManager + ); + } + } + + @captureTelemetry(Telemetry.WaitForIdleJupyter, undefined, true) + private async waitForIdleOnSession(session: ISession | undefined, timeout: number): Promise { + if (session && session.kernel) { + traceInfo(`Waiting for idle on (kernel): ${session.kernel.id} -> ${session.kernel.status}`); + // tslint:disable-next-line: no-any + const statusHandler = (resolve: () => void, reject: (exc: any) => void, e: Kernel.Status | undefined) => { + if (e === 'idle') { + resolve(); + } else if (e === 'dead') { + traceError('Kernel died while waiting for idle'); + // If we throw an exception, make sure to shutdown the session as it's not usable anymore + this.shutdownSession(session, this.statusHandler).ignoreErrors(); + reject( + new JupyterInvalidKernelError({ + ...session.kernel, + lastActivityTime: new Date(), + numberOfConnections: 0, + session: session.model + }) + ); + } + }; + + const kernelStatusChangedPromise = new Promise((resolve, reject) => + session.statusChanged.connect((_, e) => statusHandler(resolve, reject, e)) + ); + const statusChangedPromise = new Promise((resolve, reject) => + session.kernelChanged.connect((_, e) => statusHandler(resolve, reject, e.newValue?.status)) + ); + const checkStatusPromise = new Promise(async (resolve) => { + // This function seems to cause CI builds to timeout randomly on + // different tests. Waiting for status to go idle doesn't seem to work and + // in the past, waiting on the ready promise doesn't work either. Check status with a maximum of 5 seconds + const startTime = Date.now(); + while ( + session && + session.kernel && + session.kernel.status !== 'idle' && + Date.now() - startTime < timeout + ) { + await sleep(100); + } + resolve(); + }); + await Promise.race([kernelStatusChangedPromise, statusChangedPromise, checkStatusPromise]); + traceInfo(`Finished waiting for idle on (kernel): ${session.kernel.id} -> ${session.kernel.status}`); + + // If we didn't make it out in ten seconds, indicate an error + if (session.kernel && session.kernel.status === 'idle') { + return; + } + + // If we throw an exception, make sure to shutdown the session as it's not usable anymore + this.shutdownSession(session, this.statusHandler).ignoreErrors(); + throw new JupyterWaitForIdleError(localize.DataScience.jupyterLaunchTimedOut()); + } + } + + private async createRestartSession( + serverSettings: ServerConnection.ISettings, + kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, + contentsManager: ContentsManager, + cancelToken?: CancellationToken + ): Promise { + let result: ISession | undefined; + let tryCount = 0; + // tslint:disable-next-line: no-any + let exception: any; + while (tryCount < 3) { + try { + result = await this.createSession(serverSettings, kernelSpec, contentsManager, cancelToken); + await this.waitForIdleOnSession(result, 30000); + this.kernelSelector.addKernelToIgnoreList(result.kernel); + return result; + } catch (exc) { + traceInfo(`Error waiting for restart session: ${exc}`); + tryCount += 1; + if (result) { + this.shutdownSession(result, undefined).ignoreErrors(); + } + result = undefined; + exception = exc; + } + } + throw exception; + } + + private async createSession( + serverSettings: ServerConnection.ISettings, + kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, + contentsManager: ContentsManager, + cancelToken?: CancellationToken + ): Promise { + // Create a temporary notebook for this session. + this.notebookFiles.push(await contentsManager.newUntitled({ type: 'notebook' })); + + // Create our session options using this temporary notebook and our connection info + const options: Session.IOptions = { + path: this.notebookFiles[this.notebookFiles.length - 1].path, + kernelName: kernelSpec ? kernelSpec.name : '', + name: uuid(), // This is crucial to distinguish this session from any other. + serverSettings: serverSettings + }; + + return Cancellation.race( + () => + this.sessionManager!.startNew(options) + .then((s) => { + this.logRemoteOutput( + localize.DataScience.createdNewKernel().format(this.connInfo.baseUrl, s?.kernel?.id) + ); + return s; + }) + .catch((ex) => Promise.reject(new JupyterSessionStartError(ex))), + cancelToken + ); + } + + private logRemoteOutput(output: string) { + if (this.connInfo && !this.connInfo.localLaunch) { + this.outputChannel.appendLine(output); + } + } + + private async waitForKernelPromise( + kernelPromise: Promise, + timeout: number, + errorMessage: string + ): Promise { + // Wait for this kernel promise to happen + try { + return await waitForPromise(kernelPromise, timeout); + } catch (e) { + if (!e) { + // We timed out. Throw a specific exception + throw new JupyterKernelPromiseFailedError(errorMessage); + } + throw e; + } + } + + private onStatusChanged(_s: Session.ISession) { + if (this.onStatusChangedEvent) { + this.onStatusChangedEvent.fire(this.getServerStatus()); + } + } + + private async shutdownSession( + session: ISession | undefined, + statusHandler: Slot | undefined + ): Promise { + if (session && session.kernel) { + const kernelId = session.kernel.id; + traceInfo(`shutdownSession ${kernelId} - start`); + try { + if (statusHandler) { + session.statusChanged.disconnect(statusHandler); + } + // Do not shutdown remote sessions. + if (session.isRemoteSession) { + session.dispose(); + return; + } + try { + // When running under a test, mark all futures as done so we + // don't hit this problem: + // https://github.com/jupyterlab/jupyterlab/issues/4252 + // tslint:disable:no-any + if (isTestExecution()) { + const defaultKernel = session.kernel as any; + if (defaultKernel && defaultKernel._futures) { + const futures = defaultKernel._futures as Map; + if (futures) { + futures.forEach((f) => { + if (f._status !== undefined) { + f._status |= 4; + } + }); + } + } + if (defaultKernel && defaultKernel._reconnectLimit) { + defaultKernel._reconnectLimit = 0; + } + await waitForPromise(session.shutdown(), 1000); + } else { + // Shutdown may fail if the process has been killed + await waitForPromise(session.shutdown(), 1000); + } + } catch { + noop(); + } + if (session && !session.isDisposed) { + session.dispose(); + } + } catch (e) { + // Ignore, just trace. + traceWarning(e); + } + traceInfo(`shutdownSession ${kernelId} - shutdown complete`); + } + } +} diff --git a/src/client/datascience/jupyter/jupyterSessionManager.ts b/src/client/datascience/jupyter/jupyterSessionManager.ts index 2a99967ea94c..6ce736593199 100644 --- a/src/client/datascience/jupyter/jupyterSessionManager.ts +++ b/src/client/datascience/jupyter/jupyterSessionManager.ts @@ -1,252 +1,252 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { ContentsManager, Kernel, ServerConnection, Session, SessionManager } from '@jupyterlab/services'; -import { Agent as HttpsAgent } from 'https'; -import { CancellationToken } from 'vscode-jsonrpc'; - -import { traceInfo } from '../../common/logger'; -import { IConfigurationService, IOutputChannel } from '../../common/types'; -import * as localize from '../../common/utils/localize'; -import { noop } from '../../common/utils/misc'; -import { - IConnection, - IJupyterKernel, - IJupyterKernelSpec, - IJupyterPasswordConnect, - IJupyterPasswordConnectInfo, - IJupyterSession, - IJupyterSessionManager -} from '../types'; -import { JupyterSession } from './jupyterSession'; -import { createJupyterWebSocket } from './jupyterWebSocket'; -import { JupyterKernelSpec } from './kernels/jupyterKernelSpec'; -import { KernelSelector } from './kernels/kernelSelector'; -import { LiveKernelModel } from './kernels/types'; - -export class JupyterSessionManager implements IJupyterSessionManager { - private sessionManager: SessionManager | undefined; - private contentsManager: ContentsManager | undefined; - private connInfo: IConnection | undefined; - private serverSettings: ServerConnection.ISettings | undefined; - - constructor( - private jupyterPasswordConnect: IJupyterPasswordConnect, - private config: IConfigurationService, - private failOnPassword: boolean | undefined, - private kernelSelector: KernelSelector, - private outputChannel: IOutputChannel - ) {} - - public async dispose() { - if (this.contentsManager) { - traceInfo('SessionManager - dispose contents manager'); - this.contentsManager.dispose(); - this.contentsManager = undefined; - } - if (this.sessionManager && !this.sessionManager.isDisposed) { - traceInfo('ShutdownSessionAndConnection - dispose session manager'); - // Make sure it finishes startup. - await this.sessionManager.ready; - - // tslint:disable-next-line: no-any - const sessionManager = this.sessionManager as any; - this.sessionManager.dispose(); // Note, shutting down all will kill all kernels on the same connection. We don't want that. - this.sessionManager = undefined; - - // The session manager can actually be stuck in the context of a timer. Clear out the specs inside of - // it so the memory for the session is minimized. Otherwise functional tests can run out of memory - if (sessionManager._specs) { - sessionManager._specs = {}; - } - if (sessionManager._sessions && sessionManager._sessions.clear) { - sessionManager._sessions.clear(); - } - if (sessionManager._pollModels) { - this.clearPoll(sessionManager._pollModels); - } - if (sessionManager._pollSpecs) { - this.clearPoll(sessionManager._pollSpecs); - } - } - } - - public getConnInfo(): IConnection { - return this.connInfo!; - } - - public async initialize(connInfo: IConnection): Promise { - this.connInfo = connInfo; - this.serverSettings = await this.getServerConnectSettings(connInfo); - this.sessionManager = new SessionManager({ serverSettings: this.serverSettings }); - this.contentsManager = new ContentsManager({ serverSettings: this.serverSettings }); - } - - public async getRunningSessions(): Promise { - if (!this.sessionManager) { - return []; - } - // Not refreshing will result in `running` returning an empty iterator. - await this.sessionManager.refreshRunning(); - - const sessions: Session.IModel[] = []; - const iterator = this.sessionManager.running(); - let session = iterator.next(); - - while (session) { - sessions.push(session); - session = iterator.next(); - } - - return sessions; - } - - public async getRunningKernels(): Promise { - const models = await Kernel.listRunning(this.serverSettings); - // Remove duplicates. - const dup = new Set(); - return models - .map(m => { - return { - id: m.id, - name: m.name, - lastActivityTime: m.last_activity ? new Date(Date.parse(m.last_activity.toString())) : new Date(), - numberOfConnections: m.connections ? parseInt(m.connections.toString(), 10) : 0 - }; - }) - .filter(item => { - if (dup.has(item.id)) { - return false; - } - dup.add(item.id); - return true; - }); - } - - public async startNew( - kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, - cancelToken?: CancellationToken - ): Promise { - if (!this.connInfo || !this.sessionManager || !this.contentsManager || !this.serverSettings) { - throw new Error(localize.DataScience.sessionDisposed()); - } - // Create a new session and attempt to connect to it - const session = new JupyterSession( - this.connInfo, - this.serverSettings, - kernelSpec, - this.sessionManager, - this.contentsManager, - this.kernelSelector, - this.outputChannel - ); - try { - await session.connect(cancelToken); - } finally { - if (!session.isConnected) { - await session.dispose(); - } - } - return session; - } - - public async getKernelSpecs(): Promise { - if (!this.connInfo || !this.sessionManager || !this.contentsManager) { - throw new Error(localize.DataScience.sessionDisposed()); - } - try { - // Ask the session manager to refresh its list of kernel specs. - await this.sessionManager.refreshSpecs(); - - // Enumerate all of the kernel specs, turning each into a JupyterKernelSpec - const kernelspecs = - this.sessionManager.specs && this.sessionManager.specs.kernelspecs - ? this.sessionManager.specs.kernelspecs - : {}; - const keys = Object.keys(kernelspecs); - return keys.map(k => { - const spec = kernelspecs[k]; - return new JupyterKernelSpec(spec) as IJupyterKernelSpec; - }); - } catch { - // For some reason this is failing. Just return nothing - return []; - } - } - - // tslint:disable-next-line: no-any - private clearPoll(poll: { _timeout: any }) { - try { - clearTimeout(poll._timeout); - } catch { - noop(); - } - } - - private getSessionCookieString(pwSettings: IJupyterPasswordConnectInfo): string { - return `_xsrf=${pwSettings.xsrfCookie}; ${pwSettings.sessionCookieName}=${pwSettings.sessionCookieValue}`; - } - - private async getServerConnectSettings(connInfo: IConnection): Promise { - let serverSettings: Partial = { - baseUrl: connInfo.baseUrl, - appUrl: '', - // A web socket is required to allow token authentication - wsUrl: connInfo.baseUrl.replace('http', 'ws') - }; - - // Agent is allowed to be set on this object, but ts doesn't like it on RequestInit, so any - // tslint:disable-next-line:no-any - let requestInit: any = { cache: 'no-store', credentials: 'same-origin' }; - let cookieString; - let allowUnauthorized; - - // If no token is specified prompt for a password - if (connInfo.token === '' || connInfo.token === 'null') { - if (this.failOnPassword) { - throw new Error('Password request not allowed.'); - } - serverSettings = { ...serverSettings, token: '' }; - const pwSettings = await this.jupyterPasswordConnect.getPasswordConnectionInfo( - connInfo.baseUrl, - connInfo.allowUnauthorized ? true : false - ); - if (pwSettings && !pwSettings.emptyPassword) { - cookieString = this.getSessionCookieString(pwSettings); - const requestHeaders = { Cookie: cookieString, 'X-XSRFToken': pwSettings.xsrfCookie }; - requestInit = { ...requestInit, headers: requestHeaders }; - } else if (pwSettings && pwSettings.emptyPassword) { - serverSettings = { ...serverSettings, token: connInfo.token }; - } else { - // Failed to get password info, notify the user - throw new Error(localize.DataScience.passwordFailure()); - } - } else { - serverSettings = { ...serverSettings, token: connInfo.token }; - } - - // If this is an https connection and we want to allow unauthorized connections set that option on our agent - // we don't need to save the agent as the previous behaviour is just to create a temporary default agent when not specified - if (connInfo.baseUrl.startsWith('https') && connInfo.allowUnauthorized) { - const requestAgent = new HttpsAgent({ rejectUnauthorized: false }); - requestInit = { ...requestInit, agent: requestAgent }; - allowUnauthorized = true; - } - - // This replaces the WebSocket constructor in jupyter lab services with our own implementation - // See _createSocket here: - // https://github.com/jupyterlab/jupyterlab/blob/cfc8ebda95e882b4ed2eefd54863bb8cdb0ab763/packages/services/src/kernel/default.ts - serverSettings = { - ...serverSettings, - init: requestInit, - WebSocket: createJupyterWebSocket( - this.config.getSettings().datascience.verboseLogging, - cookieString, - allowUnauthorized - // tslint:disable-next-line:no-any - ) as any - }; - - return ServerConnection.makeSettings(serverSettings); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { ContentsManager, Kernel, ServerConnection, Session, SessionManager } from '@jupyterlab/services'; +import { Agent as HttpsAgent } from 'https'; +import { CancellationToken } from 'vscode-jsonrpc'; + +import { traceInfo } from '../../common/logger'; +import { IConfigurationService, IOutputChannel } from '../../common/types'; +import * as localize from '../../common/utils/localize'; +import { noop } from '../../common/utils/misc'; +import { + IConnection, + IJupyterKernel, + IJupyterKernelSpec, + IJupyterPasswordConnect, + IJupyterPasswordConnectInfo, + IJupyterSession, + IJupyterSessionManager +} from '../types'; +import { JupyterSession } from './jupyterSession'; +import { createJupyterWebSocket } from './jupyterWebSocket'; +import { JupyterKernelSpec } from './kernels/jupyterKernelSpec'; +import { KernelSelector } from './kernels/kernelSelector'; +import { LiveKernelModel } from './kernels/types'; + +export class JupyterSessionManager implements IJupyterSessionManager { + private sessionManager: SessionManager | undefined; + private contentsManager: ContentsManager | undefined; + private connInfo: IConnection | undefined; + private serverSettings: ServerConnection.ISettings | undefined; + + constructor( + private jupyterPasswordConnect: IJupyterPasswordConnect, + private config: IConfigurationService, + private failOnPassword: boolean | undefined, + private kernelSelector: KernelSelector, + private outputChannel: IOutputChannel + ) {} + + public async dispose() { + if (this.contentsManager) { + traceInfo('SessionManager - dispose contents manager'); + this.contentsManager.dispose(); + this.contentsManager = undefined; + } + if (this.sessionManager && !this.sessionManager.isDisposed) { + traceInfo('ShutdownSessionAndConnection - dispose session manager'); + // Make sure it finishes startup. + await this.sessionManager.ready; + + // tslint:disable-next-line: no-any + const sessionManager = this.sessionManager as any; + this.sessionManager.dispose(); // Note, shutting down all will kill all kernels on the same connection. We don't want that. + this.sessionManager = undefined; + + // The session manager can actually be stuck in the context of a timer. Clear out the specs inside of + // it so the memory for the session is minimized. Otherwise functional tests can run out of memory + if (sessionManager._specs) { + sessionManager._specs = {}; + } + if (sessionManager._sessions && sessionManager._sessions.clear) { + sessionManager._sessions.clear(); + } + if (sessionManager._pollModels) { + this.clearPoll(sessionManager._pollModels); + } + if (sessionManager._pollSpecs) { + this.clearPoll(sessionManager._pollSpecs); + } + } + } + + public getConnInfo(): IConnection { + return this.connInfo!; + } + + public async initialize(connInfo: IConnection): Promise { + this.connInfo = connInfo; + this.serverSettings = await this.getServerConnectSettings(connInfo); + this.sessionManager = new SessionManager({ serverSettings: this.serverSettings }); + this.contentsManager = new ContentsManager({ serverSettings: this.serverSettings }); + } + + public async getRunningSessions(): Promise { + if (!this.sessionManager) { + return []; + } + // Not refreshing will result in `running` returning an empty iterator. + await this.sessionManager.refreshRunning(); + + const sessions: Session.IModel[] = []; + const iterator = this.sessionManager.running(); + let session = iterator.next(); + + while (session) { + sessions.push(session); + session = iterator.next(); + } + + return sessions; + } + + public async getRunningKernels(): Promise { + const models = await Kernel.listRunning(this.serverSettings); + // Remove duplicates. + const dup = new Set(); + return models + .map((m) => { + return { + id: m.id, + name: m.name, + lastActivityTime: m.last_activity ? new Date(Date.parse(m.last_activity.toString())) : new Date(), + numberOfConnections: m.connections ? parseInt(m.connections.toString(), 10) : 0 + }; + }) + .filter((item) => { + if (dup.has(item.id)) { + return false; + } + dup.add(item.id); + return true; + }); + } + + public async startNew( + kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, + cancelToken?: CancellationToken + ): Promise { + if (!this.connInfo || !this.sessionManager || !this.contentsManager || !this.serverSettings) { + throw new Error(localize.DataScience.sessionDisposed()); + } + // Create a new session and attempt to connect to it + const session = new JupyterSession( + this.connInfo, + this.serverSettings, + kernelSpec, + this.sessionManager, + this.contentsManager, + this.kernelSelector, + this.outputChannel + ); + try { + await session.connect(cancelToken); + } finally { + if (!session.isConnected) { + await session.dispose(); + } + } + return session; + } + + public async getKernelSpecs(): Promise { + if (!this.connInfo || !this.sessionManager || !this.contentsManager) { + throw new Error(localize.DataScience.sessionDisposed()); + } + try { + // Ask the session manager to refresh its list of kernel specs. + await this.sessionManager.refreshSpecs(); + + // Enumerate all of the kernel specs, turning each into a JupyterKernelSpec + const kernelspecs = + this.sessionManager.specs && this.sessionManager.specs.kernelspecs + ? this.sessionManager.specs.kernelspecs + : {}; + const keys = Object.keys(kernelspecs); + return keys.map((k) => { + const spec = kernelspecs[k]; + return new JupyterKernelSpec(spec) as IJupyterKernelSpec; + }); + } catch { + // For some reason this is failing. Just return nothing + return []; + } + } + + // tslint:disable-next-line: no-any + private clearPoll(poll: { _timeout: any }) { + try { + clearTimeout(poll._timeout); + } catch { + noop(); + } + } + + private getSessionCookieString(pwSettings: IJupyterPasswordConnectInfo): string { + return `_xsrf=${pwSettings.xsrfCookie}; ${pwSettings.sessionCookieName}=${pwSettings.sessionCookieValue}`; + } + + private async getServerConnectSettings(connInfo: IConnection): Promise { + let serverSettings: Partial = { + baseUrl: connInfo.baseUrl, + appUrl: '', + // A web socket is required to allow token authentication + wsUrl: connInfo.baseUrl.replace('http', 'ws') + }; + + // Agent is allowed to be set on this object, but ts doesn't like it on RequestInit, so any + // tslint:disable-next-line:no-any + let requestInit: any = { cache: 'no-store', credentials: 'same-origin' }; + let cookieString; + let allowUnauthorized; + + // If no token is specified prompt for a password + if (connInfo.token === '' || connInfo.token === 'null') { + if (this.failOnPassword) { + throw new Error('Password request not allowed.'); + } + serverSettings = { ...serverSettings, token: '' }; + const pwSettings = await this.jupyterPasswordConnect.getPasswordConnectionInfo( + connInfo.baseUrl, + connInfo.allowUnauthorized ? true : false + ); + if (pwSettings && !pwSettings.emptyPassword) { + cookieString = this.getSessionCookieString(pwSettings); + const requestHeaders = { Cookie: cookieString, 'X-XSRFToken': pwSettings.xsrfCookie }; + requestInit = { ...requestInit, headers: requestHeaders }; + } else if (pwSettings && pwSettings.emptyPassword) { + serverSettings = { ...serverSettings, token: connInfo.token }; + } else { + // Failed to get password info, notify the user + throw new Error(localize.DataScience.passwordFailure()); + } + } else { + serverSettings = { ...serverSettings, token: connInfo.token }; + } + + // If this is an https connection and we want to allow unauthorized connections set that option on our agent + // we don't need to save the agent as the previous behaviour is just to create a temporary default agent when not specified + if (connInfo.baseUrl.startsWith('https') && connInfo.allowUnauthorized) { + const requestAgent = new HttpsAgent({ rejectUnauthorized: false }); + requestInit = { ...requestInit, agent: requestAgent }; + allowUnauthorized = true; + } + + // This replaces the WebSocket constructor in jupyter lab services with our own implementation + // See _createSocket here: + // https://github.com/jupyterlab/jupyterlab/blob/cfc8ebda95e882b4ed2eefd54863bb8cdb0ab763/packages/services/src/kernel/default.ts + serverSettings = { + ...serverSettings, + init: requestInit, + WebSocket: createJupyterWebSocket( + this.config.getSettings().datascience.verboseLogging, + cookieString, + allowUnauthorized + // tslint:disable-next-line:no-any + ) as any + }; + + return ServerConnection.makeSettings(serverSettings); + } +} diff --git a/src/client/datascience/jupyter/jupyterUtils.ts b/src/client/datascience/jupyter/jupyterUtils.ts index d0bdd8e2c855..0a54621e123d 100644 --- a/src/client/datascience/jupyter/jupyterUtils.ts +++ b/src/client/datascience/jupyter/jupyterUtils.ts @@ -49,7 +49,7 @@ export function createRemoteConnectionInfo(uri: string, settings: IDataScienceSe hostName: url.hostname, localLaunch: false, localProcExitCode: undefined, - disconnected: _l => { + disconnected: (_l) => { return { dispose: noop }; }, dispose: noop diff --git a/src/client/datascience/jupyter/jupyterVariables.ts b/src/client/datascience/jupyter/jupyterVariables.ts index 0cba8b381613..c2a2df52b445 100644 --- a/src/client/datascience/jupyter/jupyterVariables.ts +++ b/src/client/datascience/jupyter/jupyterVariables.ts @@ -138,7 +138,7 @@ export class JupyterVariables implements IJupyterVariables { const regexPattern = extraReplacements.length === 0 ? '_VSCode_JupyterTestValue' - : ['_VSCode_JupyterTestValue', ...extraReplacements.map(v => v.key)].join('|'); + : ['_VSCode_JupyterTestValue', ...extraReplacements.map((v) => v.key)].join('|'); const replaceRegex = new RegExp(regexPattern, 'g'); // Replace the test value with our current value. Replace start and end as well @@ -146,7 +146,7 @@ export class JupyterVariables implements IJupyterVariables { if (match === '_VSCode_JupyterTestValue') { return variableString; } else { - const index = extraReplacements.findIndex(v => v.key === match); + const index = extraReplacements.findIndex((v) => v.key === match); if (index >= 0) { return extraReplacements[index].value; } @@ -234,7 +234,7 @@ export class JupyterVariables implements IJupyterVariables { if (!result) { let query = this.configService .getSettings(notebook.resource) - .datascience.variableQueries.find(v => v.language === language); + .datascience.variableQueries.find((v) => v.language === language); if (!query) { query = Settings.DefaultVariableQuery; } @@ -277,7 +277,7 @@ export class JupyterVariables implements IJupyterVariables { // Refetch the list of names from the notebook. They might have changed. list = { currentExecutionCount: request.executionCount, - variables: (await this.getVariableNamesFromKernel(notebook)).map(n => { + variables: (await this.getVariableNamesFromKernel(notebook)).map((n) => { return { name: n, value: undefined, diff --git a/src/client/datascience/jupyter/jupyterWaitForIdleError.ts b/src/client/datascience/jupyter/jupyterWaitForIdleError.ts index 0f6626843918..b02bab07b807 100644 --- a/src/client/datascience/jupyter/jupyterWaitForIdleError.ts +++ b/src/client/datascience/jupyter/jupyterWaitForIdleError.ts @@ -1,9 +1,9 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -export class JupyterWaitForIdleError extends Error { - constructor(message: string) { - super(message); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +export class JupyterWaitForIdleError extends Error { + constructor(message: string) { + super(message); + } +} diff --git a/src/client/datascience/jupyter/kernels/jupyterKernelSpec.ts b/src/client/datascience/jupyter/kernels/jupyterKernelSpec.ts index bfe4540c8d86..792541347135 100644 --- a/src/client/datascience/jupyter/kernels/jupyterKernelSpec.ts +++ b/src/client/datascience/jupyter/kernels/jupyterKernelSpec.ts @@ -49,7 +49,7 @@ export async function parseKernelSpecs(stdout: string, fs: IFileSystem, token?: const specs = await Promise.race([ Promise.all( - Object.keys(kernelSpecs).map(async kernelName => { + Object.keys(kernelSpecs).map(async (kernelName) => { const specFile = path.join(kernelSpecs[kernelName].resource_dir, 'kernel.json'); const spec = kernelSpecs[kernelName].spec; // Add the missing name property. @@ -67,5 +67,5 @@ export async function parseKernelSpecs(stdout: string, fs: IFileSystem, token?: ), createPromiseFromCancellation({ cancelAction: 'resolve', defaultValue: [], token }) ]); - return specs.filter(item => !!item).map(item => item as JupyterKernelSpec); + return specs.filter((item) => !!item).map((item) => item as JupyterKernelSpec); } diff --git a/src/client/datascience/jupyter/kernels/kernelSelections.ts b/src/client/datascience/jupyter/kernels/kernelSelections.ts index fc4f49dbb9ea..a56c748ce84d 100644 --- a/src/client/datascience/jupyter/kernels/kernelSelections.ts +++ b/src/client/datascience/jupyter/kernels/kernelSelections.ts @@ -73,10 +73,10 @@ export class ActiveJupyterSessionKernelSelectionListProvider implements IKernelS this.sessionManager.getRunningSessions(), this.sessionManager.getKernelSpecs() ]); - const items = activeSessions.map(item => { + const items = activeSessions.map((item) => { const matchingSpec: Partial = - kernelSpecs.find(spec => spec.name === item.kernel.name) || {}; - const activeKernel = activeKernels.find(active => active.id === item.kernel.id) || {}; + kernelSpecs.find((spec) => spec.name === item.kernel.name) || {}; + const activeKernel = activeKernels.find((active) => active.id === item.kernel.id) || {}; // tslint:disable-next-line: no-object-literal-type-assertion return { ...item.kernel, @@ -86,10 +86,10 @@ export class ActiveJupyterSessionKernelSelectionListProvider implements IKernelS } as LiveKernelModel; }); return items - .filter(item => item.display_name || item.name) - .filter(item => 'lastActivityTime' in item && 'numberOfConnections' in item) - .filter(item => (item.language || '').toLowerCase() === PYTHON_LANGUAGE.toLowerCase()) - .map(item => getQuickPickItemForActiveKernel(item, this.pathUtils)); + .filter((item) => item.display_name || item.name) + .filter((item) => 'lastActivityTime' in item && 'numberOfConnections' in item) + .filter((item) => (item.language || '').toLowerCase() === PYTHON_LANGUAGE.toLowerCase()) + .map((item) => getQuickPickItemForActiveKernel(item, this.pathUtils)); } } @@ -112,8 +112,8 @@ export class InstalledJupyterKernelSelectionListProvider implements IKernelSelec ): Promise { const items = await this.kernelService.getKernelSpecs(this.sessionManager, cancelToken); return items - .filter(item => (item.language || '').toLowerCase() === PYTHON_LANGUAGE.toLowerCase()) - .map(item => getQuickPickItemForKernelSpec(item, this.pathUtils)); + .filter((item) => (item.language || '').toLowerCase() === PYTHON_LANGUAGE.toLowerCase()) + .map((item) => getQuickPickItemForKernelSpec(item, this.pathUtils)); } } @@ -132,7 +132,7 @@ export class InterpreterKernelSelectionListProvider implements IKernelSelectionL _cancelToken?: CancellationToken | undefined ): Promise { const items = await this.interpreterSelector.getSuggestions(resource); - return items.map(item => { + return items.map((item) => { return { ...item, // We don't want descriptions. @@ -190,7 +190,7 @@ export class KernelSelectionProvider { return [...liveKernels!, ...installedKernels!]; }; - const liveItems = getSelections().then(items => (this.localSuggestionsCache = items)); + const liveItems = getSelections().then((items) => (this.localSuggestionsCache = items)); // If we have someting in cache, return that, while fetching in the background. const cachedItems = this.remoteSuggestionsCache.length > 0 ? Promise.resolve(this.remoteSuggestionsCache) : liveItems; @@ -223,11 +223,11 @@ export class KernelSelectionProvider { let [installedKernels, interpreters] = await Promise.all([installedKernelsPromise, interpretersPromise]); interpreters = interpreters - .filter(item => { + .filter((item) => { // If the interpreter is registered as a kernel then don't inlcude it. if ( installedKernels.find( - installedKernel => + (installedKernel) => installedKernel.selection.kernelSpec?.display_name === item.selection.interpreter?.displayName && (this.fileSystem.arePathsSame( @@ -244,7 +244,7 @@ export class KernelSelectionProvider { } return true; }) - .map(item => { + .map((item) => { // We don't want descriptions. return { ...item, description: '' }; }); @@ -256,7 +256,7 @@ export class KernelSelectionProvider { return unifiedList; }; - const liveItems = getSelections().then(items => (this.localSuggestionsCache = items)); + const liveItems = getSelections().then((items) => (this.localSuggestionsCache = items)); // If we have someting in cache, return that, while fetching in the background. const cachedItems = this.localSuggestionsCache.length > 0 ? Promise.resolve(this.localSuggestionsCache) : liveItems; diff --git a/src/client/datascience/jupyter/kernels/kernelSelector.ts b/src/client/datascience/jupyter/kernels/kernelSelector.ts index 28de866ff7e6..9bff05b81885 100644 --- a/src/client/datascience/jupyter/kernels/kernelSelector.ts +++ b/src/client/datascience/jupyter/kernels/kernelSelector.ts @@ -101,7 +101,7 @@ export class KernelSelector { session, cancelToken ); - suggestions = suggestions.filter(item => !this.kernelIdsToHide.has(item.selection.kernelModel?.id || '')); + suggestions = suggestions.filter((item) => !this.kernelIdsToHide.has(item.selection.kernelModel?.id || '')); return this.selectKernel( resource, stopWatch, @@ -132,7 +132,7 @@ export class KernelSelector { session, cancelToken ); - suggestions = suggestions.filter(item => !this.kernelIdsToHide.has(item.selection.kernelModel?.id || '')); + suggestions = suggestions.filter((item) => !this.kernelIdsToHide.has(item.selection.kernelModel?.id || '')); return this.selectKernel( resource, stopWatch, diff --git a/src/client/datascience/jupyter/kernels/kernelService.ts b/src/client/datascience/jupyter/kernels/kernelService.ts index c6923e94da7e..3dd0d054d9cb 100644 --- a/src/client/datascience/jupyter/kernels/kernelService.ts +++ b/src/client/datascience/jupyter/kernels/kernelService.ts @@ -100,7 +100,7 @@ export class KernelService { ): Promise { const specs = await this.getKernelSpecs(sessionManager, cancelToken); if (isInterpreter(option)) { - return specs.find(item => { + return specs.find((item) => { if (item.language.toLowerCase() !== PYTHON_LANGUAGE.toLowerCase()) { return false; } @@ -111,7 +111,7 @@ export class KernelService { }); } else { return specs.find( - item => + (item) => item.language === PYTHON_LANGUAGE && item.display_name === option.display_name && item.name === option.name @@ -185,7 +185,7 @@ export class KernelService { // Find an interpreter that matches the const allInterpreters = await allInterpretersPromise; - const found = allInterpreters.find(item => item.version?.major === majorVersion); + const found = allInterpreters.find((item) => item.version?.major === majorVersion); // If we cannot find a matching one, then use the current interpreter. if (found) { @@ -209,7 +209,7 @@ export class KernelService { return; } - const found = allInterpreters.find(item => item.displayName === kernelSpec.display_name); + const found = allInterpreters.find((item) => item.displayName === kernelSpec.display_name); if (found) { traceVerbose( @@ -338,7 +338,7 @@ export class KernelService { } if (!kernel) { // Possible user doesn't have kernelspec installed. - kernel = await this.getKernelSpecFromStdOut(output.stdout).catch(ex => { + kernel = await this.getKernelSpecFromStdOut(output.stdout).catch((ex) => { traceError('Failed to get kernelspec from stdout', ex); return undefined; }); @@ -395,7 +395,7 @@ export class KernelService { .getActivatedEnvironmentVariables(undefined, interpreter, true) .catch(noop) // tslint:disable-next-line: no-any - .then(env => (env || {}) as any); + .then((env) => (env || {}) as any); if (Cancellation.isCanceled(cancelToken)) { return; } @@ -446,10 +446,10 @@ export class KernelService { return []; } const specs: IJupyterKernelSpec[] = await enumerator; - const result = specs.filter(item => !!item); + const result = specs.filter((item) => !!item); // Send telemetry on this enumeration. - const anyPython = result.find(k => k.language === 'python') !== undefined; + const anyPython = result.find((k) => k.language === 'python') !== undefined; sendTelemetryEvent(Telemetry.KernelEnumeration, undefined, { count: result.length, isPython: anyPython, diff --git a/src/client/datascience/jupyter/liveshare/guestJupyterExecution.ts b/src/client/datascience/jupyter/liveshare/guestJupyterExecution.ts index e0ec30c58ff5..91402f78af07 100644 --- a/src/client/datascience/jupyter/liveshare/guestJupyterExecution.ts +++ b/src/client/datascience/jupyter/liveshare/guestJupyterExecution.ts @@ -1,158 +1,158 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { injectable } from 'inversify'; -import * as uuid from 'uuid/v4'; -import { CancellationToken } from 'vscode'; - -import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../../common/application/types'; -import { IFileSystem } from '../../../common/platform/types'; -import { - IAsyncDisposableRegistry, - IConfigurationService, - IDisposableRegistry, - IOutputChannel -} from '../../../common/types'; -import * as localize from '../../../common/utils/localize'; -import { IInterpreterService, PythonInterpreter } from '../../../interpreter/contracts'; -import { IServiceContainer } from '../../../ioc/types'; -import { LiveShare, LiveShareCommands } from '../../constants'; -import { IConnection, INotebookServer, INotebookServerOptions } from '../../types'; -import { JupyterConnectError } from '../jupyterConnectError'; -import { JupyterExecutionBase } from '../jupyterExecution'; -import { KernelSelector } from '../kernels/kernelSelector'; -import { NotebookStarter } from '../notebookStarter'; -import { LiveShareParticipantGuest } from './liveShareParticipantMixin'; -import { ServerCache } from './serverCache'; - -// This class is really just a wrapper around a jupyter execution that also provides a shared live share service -@injectable() -export class GuestJupyterExecution extends LiveShareParticipantGuest( - JupyterExecutionBase, - LiveShare.JupyterExecutionService -) { - private serverCache: ServerCache; - - constructor( - liveShare: ILiveShareApi, - interpreterService: IInterpreterService, - disposableRegistry: IDisposableRegistry, - asyncRegistry: IAsyncDisposableRegistry, - fileSystem: IFileSystem, - workspace: IWorkspaceService, - configuration: IConfigurationService, - kernelSelector: KernelSelector, - notebookStarter: NotebookStarter, - appShell: IApplicationShell, - jupyterOutputChannel: IOutputChannel, - serviceContainer: IServiceContainer - ) { - super( - liveShare, - interpreterService, - disposableRegistry, - workspace, - configuration, - kernelSelector, - notebookStarter, - appShell, - jupyterOutputChannel, - serviceContainer - ); - asyncRegistry.push(this); - this.serverCache = new ServerCache(configuration, workspace, fileSystem); - } - - public async dispose(): Promise { - await super.dispose(); - - // Dispose of all of our cached servers - await this.serverCache.dispose(); - } - - public async isNotebookSupported(cancelToken?: CancellationToken): Promise { - return this.checkSupported(LiveShareCommands.isNotebookSupported, cancelToken); - } - public isImportSupported(cancelToken?: CancellationToken): Promise { - return this.checkSupported(LiveShareCommands.isImportSupported, cancelToken); - } - public isSpawnSupported(_cancelToken?: CancellationToken): Promise { - return Promise.resolve(false); - } - - public async guestConnectToNotebookServer( - options?: INotebookServerOptions, - cancelToken?: CancellationToken - ): Promise { - const service = await this.waitForService(); - if (service) { - const purpose = options ? options.purpose : uuid(); - const connection: IConnection = await service.request( - LiveShareCommands.connectToNotebookServer, - [options], - cancelToken - ); - - // If that works, then treat this as a remote server and connect to it - if (connection && connection.baseUrl) { - const newUri = `${connection.baseUrl}?token=${connection.token}`; - return super.connectToNotebookServer( - { - uri: newUri, - skipUsingDefaultConfig: options && options.skipUsingDefaultConfig, - workingDir: options ? options.workingDir : undefined, - purpose, - allowUI: () => false, - skipSearchingForKernel: true - }, - cancelToken - ); - } - } - } - - public async connectToNotebookServer( - options?: INotebookServerOptions, - cancelToken?: CancellationToken - ): Promise { - const result = await this.serverCache.getOrCreate( - this.guestConnectToNotebookServer.bind(this), - options, - cancelToken - ); - - if (!result) { - throw new JupyterConnectError(localize.DataScience.liveShareConnectFailure()); - } - - return result; - } - - public spawnNotebook(_file: string): Promise { - // Not supported in liveshare - throw new Error(localize.DataScience.liveShareCannotSpawnNotebooks()); - } - - public async getUsableJupyterPython(cancelToken?: CancellationToken): Promise { - const service = await this.waitForService(); - if (service) { - return service.request(LiveShareCommands.getUsableJupyterPython, [], cancelToken); - } - } - - public async getServer(options?: INotebookServerOptions): Promise { - return this.serverCache.get(options); - } - - private async checkSupported(command: string, cancelToken?: CancellationToken): Promise { - const service = await this.waitForService(); - - // Make a remote call on the proxy - if (service) { - const result = await service.request(command, [], cancelToken); - return result as boolean; - } - - return false; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { injectable } from 'inversify'; +import * as uuid from 'uuid/v4'; +import { CancellationToken } from 'vscode'; + +import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../../common/application/types'; +import { IFileSystem } from '../../../common/platform/types'; +import { + IAsyncDisposableRegistry, + IConfigurationService, + IDisposableRegistry, + IOutputChannel +} from '../../../common/types'; +import * as localize from '../../../common/utils/localize'; +import { IInterpreterService, PythonInterpreter } from '../../../interpreter/contracts'; +import { IServiceContainer } from '../../../ioc/types'; +import { LiveShare, LiveShareCommands } from '../../constants'; +import { IConnection, INotebookServer, INotebookServerOptions } from '../../types'; +import { JupyterConnectError } from '../jupyterConnectError'; +import { JupyterExecutionBase } from '../jupyterExecution'; +import { KernelSelector } from '../kernels/kernelSelector'; +import { NotebookStarter } from '../notebookStarter'; +import { LiveShareParticipantGuest } from './liveShareParticipantMixin'; +import { ServerCache } from './serverCache'; + +// This class is really just a wrapper around a jupyter execution that also provides a shared live share service +@injectable() +export class GuestJupyterExecution extends LiveShareParticipantGuest( + JupyterExecutionBase, + LiveShare.JupyterExecutionService +) { + private serverCache: ServerCache; + + constructor( + liveShare: ILiveShareApi, + interpreterService: IInterpreterService, + disposableRegistry: IDisposableRegistry, + asyncRegistry: IAsyncDisposableRegistry, + fileSystem: IFileSystem, + workspace: IWorkspaceService, + configuration: IConfigurationService, + kernelSelector: KernelSelector, + notebookStarter: NotebookStarter, + appShell: IApplicationShell, + jupyterOutputChannel: IOutputChannel, + serviceContainer: IServiceContainer + ) { + super( + liveShare, + interpreterService, + disposableRegistry, + workspace, + configuration, + kernelSelector, + notebookStarter, + appShell, + jupyterOutputChannel, + serviceContainer + ); + asyncRegistry.push(this); + this.serverCache = new ServerCache(configuration, workspace, fileSystem); + } + + public async dispose(): Promise { + await super.dispose(); + + // Dispose of all of our cached servers + await this.serverCache.dispose(); + } + + public async isNotebookSupported(cancelToken?: CancellationToken): Promise { + return this.checkSupported(LiveShareCommands.isNotebookSupported, cancelToken); + } + public isImportSupported(cancelToken?: CancellationToken): Promise { + return this.checkSupported(LiveShareCommands.isImportSupported, cancelToken); + } + public isSpawnSupported(_cancelToken?: CancellationToken): Promise { + return Promise.resolve(false); + } + + public async guestConnectToNotebookServer( + options?: INotebookServerOptions, + cancelToken?: CancellationToken + ): Promise { + const service = await this.waitForService(); + if (service) { + const purpose = options ? options.purpose : uuid(); + const connection: IConnection = await service.request( + LiveShareCommands.connectToNotebookServer, + [options], + cancelToken + ); + + // If that works, then treat this as a remote server and connect to it + if (connection && connection.baseUrl) { + const newUri = `${connection.baseUrl}?token=${connection.token}`; + return super.connectToNotebookServer( + { + uri: newUri, + skipUsingDefaultConfig: options && options.skipUsingDefaultConfig, + workingDir: options ? options.workingDir : undefined, + purpose, + allowUI: () => false, + skipSearchingForKernel: true + }, + cancelToken + ); + } + } + } + + public async connectToNotebookServer( + options?: INotebookServerOptions, + cancelToken?: CancellationToken + ): Promise { + const result = await this.serverCache.getOrCreate( + this.guestConnectToNotebookServer.bind(this), + options, + cancelToken + ); + + if (!result) { + throw new JupyterConnectError(localize.DataScience.liveShareConnectFailure()); + } + + return result; + } + + public spawnNotebook(_file: string): Promise { + // Not supported in liveshare + throw new Error(localize.DataScience.liveShareCannotSpawnNotebooks()); + } + + public async getUsableJupyterPython(cancelToken?: CancellationToken): Promise { + const service = await this.waitForService(); + if (service) { + return service.request(LiveShareCommands.getUsableJupyterPython, [], cancelToken); + } + } + + public async getServer(options?: INotebookServerOptions): Promise { + return this.serverCache.get(options); + } + + private async checkSupported(command: string, cancelToken?: CancellationToken): Promise { + const service = await this.waitForService(); + + // Make a remote call on the proxy + if (service) { + const result = await service.request(command, [], cancelToken); + return result as boolean; + } + + return false; + } +} diff --git a/src/client/datascience/jupyter/liveshare/guestJupyterNotebook.ts b/src/client/datascience/jupyter/liveshare/guestJupyterNotebook.ts index eb7b3ec079d8..4c01c87ec089 100644 --- a/src/client/datascience/jupyter/liveshare/guestJupyterNotebook.ts +++ b/src/client/datascience/jupyter/liveshare/guestJupyterNotebook.ts @@ -124,7 +124,7 @@ export class GuestJupyterNotebook (cells: ICell[]) => { output = cells; }, - error => { + (error) => { deferred.reject(error); }, () => { @@ -159,7 +159,7 @@ export class GuestJupyterNotebook public executeObservable(code: string, file: string, line: number, id: string): Observable { // Mimic this to the other side and then wait for a response this.waitForService() - .then(s => { + .then((s) => { if (s) { s.notify(LiveShareCommands.executeObservable, { code, file, line, id }); } diff --git a/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts b/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts index b237e7048827..8cdfd3d706a0 100644 --- a/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts +++ b/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts @@ -1,159 +1,159 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import * as uuid from 'uuid/v4'; -import { Uri } from 'vscode'; -import { CancellationToken } from 'vscode-jsonrpc'; -import * as vsls from 'vsls/vscode'; -import { ILiveShareApi, IWorkspaceService } from '../../../common/application/types'; -import { IAsyncDisposableRegistry, IConfigurationService, IDisposableRegistry, Resource } from '../../../common/types'; -import { createDeferred, Deferred } from '../../../common/utils/async'; -import * as localize from '../../../common/utils/localize'; -import { IServiceContainer } from '../../../ioc/types'; -import { LiveShare, LiveShareCommands } from '../../constants'; -import { - IConnection, - IDataScience, - IJupyterSessionManagerFactory, - INotebook, - INotebookServer, - INotebookServerLaunchInfo -} from '../../types'; -import { GuestJupyterNotebook } from './guestJupyterNotebook'; -import { LiveShareParticipantDefault, LiveShareParticipantGuest } from './liveShareParticipantMixin'; -import { ILiveShareParticipant } from './types'; - -export class GuestJupyterServer - extends LiveShareParticipantGuest(LiveShareParticipantDefault, LiveShare.JupyterServerSharedService) - implements INotebookServer, ILiveShareParticipant { - private launchInfo: INotebookServerLaunchInfo | undefined; - private connectPromise: Deferred = createDeferred(); - private _id = uuid(); - private notebooks = new Map>(); - - constructor( - private liveShare: ILiveShareApi, - private dataScience: IDataScience, - _asyncRegistry: IAsyncDisposableRegistry, - private disposableRegistry: IDisposableRegistry, - private configService: IConfigurationService, - _sessionManager: IJupyterSessionManagerFactory, - _workspaceService: IWorkspaceService, - _serviceContainer: IServiceContainer - ) { - super(liveShare); - } - - public get id(): string { - return this._id; - } - - public async connect(launchInfo: INotebookServerLaunchInfo, _cancelToken?: CancellationToken): Promise { - this.launchInfo = launchInfo; - this.connectPromise.resolve(launchInfo); - return Promise.resolve(); - } - - public async createNotebook(resource: Resource, identity: Uri): Promise { - // Remember we can have multiple native editors opened against the same ipynb file. - if (this.notebooks.get(identity.toString())) { - return this.notebooks.get(identity.toString())!; - } - - const deferred = createDeferred(); - this.notebooks.set(identity.toString(), deferred.promise); - // Tell the host side to generate a notebook for this uri - const service = await this.waitForService(); - if (service) { - const resourceString = resource ? resource.toString() : undefined; - const identityString = identity.toString(); - await service.request(LiveShareCommands.createNotebook, [resourceString, identityString]); - } - - // Return a new notebook to listen to - const result = new GuestJupyterNotebook( - this.liveShare, - this.disposableRegistry, - this.configService, - resource, - identity, - this, - this.dataScience.activationStartTime - ); - deferred.resolve(result); - const oldDispose = result.dispose.bind(result); - result.dispose = () => { - this.notebooks.delete(identity.toString()); - return oldDispose(); - }; - - return result; - } - - public async onSessionChange(api: vsls.LiveShare | null): Promise { - await super.onSessionChange(api); - - this.notebooks.forEach(async notebook => { - const guestNotebook = (await notebook) as GuestJupyterNotebook; - if (guestNotebook) { - await guestNotebook.onSessionChange(api); - } - }); - } - - public async getNotebook(resource: Uri): Promise { - return this.notebooks.get(resource.toString()); - } - - public async shutdown(): Promise { - // Send this across to the other side. Otherwise the host server will remain running (like during an export) - const service = await this.waitForService(); - if (service) { - await service.request(LiveShareCommands.disposeServer, []); - } - } - - public dispose(): Promise { - return this.shutdown(); - } - - // Return a copy of the connection information that this server used to connect with - public getConnectionInfo(): IConnection | undefined { - if (this.launchInfo) { - return this.launchInfo.connectionInfo; - } - - return undefined; - } - - public waitForConnect(): Promise { - return this.connectPromise.promise; - } - - public async waitForServiceName(): Promise { - // First wait for connect to occur - const launchInfo = await this.waitForConnect(); - - // Use our base name plus our purpose. This means one unique server per purpose - if (!launchInfo) { - return LiveShare.JupyterServerSharedService; - } - // tslint:disable-next-line:no-suspicious-comment - // TODO: Should there be some separator in the name? - return `${LiveShare.JupyterServerSharedService}${launchInfo.purpose}`; - } - - public async onAttach(api: vsls.LiveShare | null): Promise { - await super.onAttach(api); - - if (api) { - const service = await this.waitForService(); - - // Wait for sync up - const synced = service ? await service.request(LiveShareCommands.syncRequest, []) : undefined; - if (!synced && api.session && api.session.role !== vsls.Role.None) { - throw new Error(localize.DataScience.liveShareSyncFailure()); - } - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import * as uuid from 'uuid/v4'; +import { Uri } from 'vscode'; +import { CancellationToken } from 'vscode-jsonrpc'; +import * as vsls from 'vsls/vscode'; +import { ILiveShareApi, IWorkspaceService } from '../../../common/application/types'; +import { IAsyncDisposableRegistry, IConfigurationService, IDisposableRegistry, Resource } from '../../../common/types'; +import { createDeferred, Deferred } from '../../../common/utils/async'; +import * as localize from '../../../common/utils/localize'; +import { IServiceContainer } from '../../../ioc/types'; +import { LiveShare, LiveShareCommands } from '../../constants'; +import { + IConnection, + IDataScience, + IJupyterSessionManagerFactory, + INotebook, + INotebookServer, + INotebookServerLaunchInfo +} from '../../types'; +import { GuestJupyterNotebook } from './guestJupyterNotebook'; +import { LiveShareParticipantDefault, LiveShareParticipantGuest } from './liveShareParticipantMixin'; +import { ILiveShareParticipant } from './types'; + +export class GuestJupyterServer + extends LiveShareParticipantGuest(LiveShareParticipantDefault, LiveShare.JupyterServerSharedService) + implements INotebookServer, ILiveShareParticipant { + private launchInfo: INotebookServerLaunchInfo | undefined; + private connectPromise: Deferred = createDeferred(); + private _id = uuid(); + private notebooks = new Map>(); + + constructor( + private liveShare: ILiveShareApi, + private dataScience: IDataScience, + _asyncRegistry: IAsyncDisposableRegistry, + private disposableRegistry: IDisposableRegistry, + private configService: IConfigurationService, + _sessionManager: IJupyterSessionManagerFactory, + _workspaceService: IWorkspaceService, + _serviceContainer: IServiceContainer + ) { + super(liveShare); + } + + public get id(): string { + return this._id; + } + + public async connect(launchInfo: INotebookServerLaunchInfo, _cancelToken?: CancellationToken): Promise { + this.launchInfo = launchInfo; + this.connectPromise.resolve(launchInfo); + return Promise.resolve(); + } + + public async createNotebook(resource: Resource, identity: Uri): Promise { + // Remember we can have multiple native editors opened against the same ipynb file. + if (this.notebooks.get(identity.toString())) { + return this.notebooks.get(identity.toString())!; + } + + const deferred = createDeferred(); + this.notebooks.set(identity.toString(), deferred.promise); + // Tell the host side to generate a notebook for this uri + const service = await this.waitForService(); + if (service) { + const resourceString = resource ? resource.toString() : undefined; + const identityString = identity.toString(); + await service.request(LiveShareCommands.createNotebook, [resourceString, identityString]); + } + + // Return a new notebook to listen to + const result = new GuestJupyterNotebook( + this.liveShare, + this.disposableRegistry, + this.configService, + resource, + identity, + this, + this.dataScience.activationStartTime + ); + deferred.resolve(result); + const oldDispose = result.dispose.bind(result); + result.dispose = () => { + this.notebooks.delete(identity.toString()); + return oldDispose(); + }; + + return result; + } + + public async onSessionChange(api: vsls.LiveShare | null): Promise { + await super.onSessionChange(api); + + this.notebooks.forEach(async (notebook) => { + const guestNotebook = (await notebook) as GuestJupyterNotebook; + if (guestNotebook) { + await guestNotebook.onSessionChange(api); + } + }); + } + + public async getNotebook(resource: Uri): Promise { + return this.notebooks.get(resource.toString()); + } + + public async shutdown(): Promise { + // Send this across to the other side. Otherwise the host server will remain running (like during an export) + const service = await this.waitForService(); + if (service) { + await service.request(LiveShareCommands.disposeServer, []); + } + } + + public dispose(): Promise { + return this.shutdown(); + } + + // Return a copy of the connection information that this server used to connect with + public getConnectionInfo(): IConnection | undefined { + if (this.launchInfo) { + return this.launchInfo.connectionInfo; + } + + return undefined; + } + + public waitForConnect(): Promise { + return this.connectPromise.promise; + } + + public async waitForServiceName(): Promise { + // First wait for connect to occur + const launchInfo = await this.waitForConnect(); + + // Use our base name plus our purpose. This means one unique server per purpose + if (!launchInfo) { + return LiveShare.JupyterServerSharedService; + } + // tslint:disable-next-line:no-suspicious-comment + // TODO: Should there be some separator in the name? + return `${LiveShare.JupyterServerSharedService}${launchInfo.purpose}`; + } + + public async onAttach(api: vsls.LiveShare | null): Promise { + await super.onAttach(api); + + if (api) { + const service = await this.waitForService(); + + // Wait for sync up + const synced = service ? await service.request(LiveShareCommands.syncRequest, []) : undefined; + if (!synced && api.session && api.session.role !== vsls.Role.None) { + throw new Error(localize.DataScience.liveShareSyncFailure()); + } + } + } +} diff --git a/src/client/datascience/jupyter/liveshare/guestJupyterSessionManager.ts b/src/client/datascience/jupyter/liveshare/guestJupyterSessionManager.ts index 8e407260921e..296e0d559614 100644 --- a/src/client/datascience/jupyter/liveshare/guestJupyterSessionManager.ts +++ b/src/client/datascience/jupyter/liveshare/guestJupyterSessionManager.ts @@ -1,49 +1,49 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { CancellationToken } from 'vscode-jsonrpc'; - -import { Session } from '@jupyterlab/services'; -import { noop } from '../../../common/utils/misc'; -import { IConnection, IJupyterKernel, IJupyterKernelSpec, IJupyterSession, IJupyterSessionManager } from '../../types'; -import { LiveKernelModel } from '../kernels/types'; - -export class GuestJupyterSessionManager implements IJupyterSessionManager { - private connInfo: IConnection | undefined; - - public constructor(private realSessionManager: IJupyterSessionManager) { - noop(); - } - - public startNew( - kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, - cancelToken?: CancellationToken - ): Promise { - return this.realSessionManager.startNew(kernelSpec, cancelToken); - } - - public async getKernelSpecs(): Promise { - // Don't return any kernel specs in guest mode. They're only needed for the host side - return Promise.resolve([]); - } - - public getRunningKernels(): Promise { - return Promise.resolve([]); - } - - public getRunningSessions(): Promise { - return Promise.resolve([]); - } - - public async dispose(): Promise { - noop(); - } - - public async initialize(_connInfo: IConnection): Promise { - this.connInfo = _connInfo; - } - - public getConnInfo(): IConnection { - return this.connInfo!; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { CancellationToken } from 'vscode-jsonrpc'; + +import { Session } from '@jupyterlab/services'; +import { noop } from '../../../common/utils/misc'; +import { IConnection, IJupyterKernel, IJupyterKernelSpec, IJupyterSession, IJupyterSessionManager } from '../../types'; +import { LiveKernelModel } from '../kernels/types'; + +export class GuestJupyterSessionManager implements IJupyterSessionManager { + private connInfo: IConnection | undefined; + + public constructor(private realSessionManager: IJupyterSessionManager) { + noop(); + } + + public startNew( + kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, + cancelToken?: CancellationToken + ): Promise { + return this.realSessionManager.startNew(kernelSpec, cancelToken); + } + + public async getKernelSpecs(): Promise { + // Don't return any kernel specs in guest mode. They're only needed for the host side + return Promise.resolve([]); + } + + public getRunningKernels(): Promise { + return Promise.resolve([]); + } + + public getRunningSessions(): Promise { + return Promise.resolve([]); + } + + public async dispose(): Promise { + noop(); + } + + public async initialize(_connInfo: IConnection): Promise { + this.connInfo = _connInfo; + } + + public getConnInfo(): IConnection { + return this.connInfo!; + } +} diff --git a/src/client/datascience/jupyter/liveshare/hostJupyterExecution.ts b/src/client/datascience/jupyter/liveshare/hostJupyterExecution.ts index 6a07f5092888..d55c95599eaf 100644 --- a/src/client/datascience/jupyter/liveshare/hostJupyterExecution.ts +++ b/src/client/datascience/jupyter/liveshare/hostJupyterExecution.ts @@ -1,168 +1,168 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../../common/extensions'; - -import { CancellationToken } from 'vscode'; -import * as vsls from 'vsls/vscode'; - -import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../../common/application/types'; -import { IFileSystem } from '../../../common/platform/types'; -import { - IAsyncDisposableRegistry, - IConfigurationService, - IDisposableRegistry, - IOutputChannel -} from '../../../common/types'; -import { noop } from '../../../common/utils/misc'; -import { IInterpreterService } from '../../../interpreter/contracts'; -import { IServiceContainer } from '../../../ioc/types'; -import { LiveShare, LiveShareCommands } from '../../constants'; -import { IConnection, IJupyterExecution, INotebookServer, INotebookServerOptions } from '../../types'; -import { JupyterExecutionBase } from '../jupyterExecution'; -import { KernelSelector } from '../kernels/kernelSelector'; -import { NotebookStarter } from '../notebookStarter'; -import { LiveShareParticipantHost } from './liveShareParticipantMixin'; -import { IRoleBasedObject } from './roleBasedFactory'; -import { ServerCache } from './serverCache'; - -// tslint:disable:no-any - -// This class is really just a wrapper around a jupyter execution that also provides a shared live share service -export class HostJupyterExecution - extends LiveShareParticipantHost(JupyterExecutionBase, LiveShare.JupyterExecutionService) - implements IRoleBasedObject, IJupyterExecution { - private serverCache: ServerCache; - constructor( - liveShare: ILiveShareApi, - interpreterService: IInterpreterService, - disposableRegistry: IDisposableRegistry, - asyncRegistry: IAsyncDisposableRegistry, - fileSys: IFileSystem, - workspace: IWorkspaceService, - configService: IConfigurationService, - kernelSelector: KernelSelector, - notebookStarter: NotebookStarter, - appShell: IApplicationShell, - jupyterOutputChannel: IOutputChannel, - serviceContainer: IServiceContainer - ) { - super( - liveShare, - interpreterService, - disposableRegistry, - workspace, - configService, - kernelSelector, - notebookStarter, - appShell, - jupyterOutputChannel, - serviceContainer - ); - this.serverCache = new ServerCache(configService, workspace, fileSys); - asyncRegistry.push(this); - } - - public async dispose(): Promise { - await super.dispose(); - const api = await this.api; - await this.onDetach(api); - - // Cleanup on dispose. We are going away permanently - if (this.serverCache) { - await this.serverCache.dispose(); - } - } - - public async hostConnectToNotebookServer( - options?: INotebookServerOptions, - cancelToken?: CancellationToken - ): Promise { - return super.connectToNotebookServer(await this.serverCache.generateDefaultOptions(options), cancelToken); - } - - public async connectToNotebookServer( - options?: INotebookServerOptions, - cancelToken?: CancellationToken - ): Promise { - return this.serverCache.getOrCreate(this.hostConnectToNotebookServer.bind(this), options, cancelToken); - } - - public async onAttach(api: vsls.LiveShare | null): Promise { - await super.onAttach(api); - - if (api) { - const service = await this.waitForService(); - - // Register handlers for all of the supported remote calls - if (service) { - service.onRequest(LiveShareCommands.isNotebookSupported, this.onRemoteIsNotebookSupported); - service.onRequest(LiveShareCommands.isImportSupported, this.onRemoteIsImportSupported); - service.onRequest(LiveShareCommands.connectToNotebookServer, this.onRemoteConnectToNotebookServer); - service.onRequest(LiveShareCommands.getUsableJupyterPython, this.onRemoteGetUsableJupyterPython); - } - } - } - - public async onDetach(api: vsls.LiveShare | null): Promise { - await super.onDetach(api); - - // clear our cached servers if our role is no longer host or none - const newRole = - api === null || (api.session && api.session.role !== vsls.Role.Guest) ? vsls.Role.Host : vsls.Role.Guest; - if (newRole !== vsls.Role.Host) { - await this.serverCache.dispose(); - } - } - - public getServer(options?: INotebookServerOptions): Promise { - // See if we have this server or not. - return this.serverCache.get(options); - } - - private onRemoteIsNotebookSupported = (_args: any[], cancellation: CancellationToken): Promise => { - // Just call local - return this.isNotebookSupported(cancellation); - }; - - private onRemoteIsImportSupported = (_args: any[], cancellation: CancellationToken): Promise => { - // Just call local - return this.isImportSupported(cancellation); - }; - - private onRemoteConnectToNotebookServer = async ( - args: any[], - cancellation: CancellationToken - ): Promise => { - // Connect to the local server. THe local server should have started the port forwarding already - const localServer = await this.connectToNotebookServer( - args[0] as INotebookServerOptions | undefined, - cancellation - ); - - // Extract the URI and token for the other side - if (localServer) { - // The other side should be using 'localhost' for anything it's port forwarding. That should just remap - // on the guest side. However we need to eliminate the dispose method. Methods are not serializable - const connectionInfo = localServer.getConnectionInfo(); - if (connectionInfo) { - return { - baseUrl: connectionInfo.baseUrl, - token: connectionInfo.token, - hostName: connectionInfo.hostName, - localLaunch: false, - localProcExitCode: undefined, - disconnected: _l => { - return { dispose: noop }; - }, - dispose: noop - }; - } - } - }; - - private onRemoteGetUsableJupyterPython = (_args: any[], cancellation: CancellationToken): Promise => { - // Just call local - return this.getUsableJupyterPython(cancellation); - }; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../../common/extensions'; + +import { CancellationToken } from 'vscode'; +import * as vsls from 'vsls/vscode'; + +import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../../common/application/types'; +import { IFileSystem } from '../../../common/platform/types'; +import { + IAsyncDisposableRegistry, + IConfigurationService, + IDisposableRegistry, + IOutputChannel +} from '../../../common/types'; +import { noop } from '../../../common/utils/misc'; +import { IInterpreterService } from '../../../interpreter/contracts'; +import { IServiceContainer } from '../../../ioc/types'; +import { LiveShare, LiveShareCommands } from '../../constants'; +import { IConnection, IJupyterExecution, INotebookServer, INotebookServerOptions } from '../../types'; +import { JupyterExecutionBase } from '../jupyterExecution'; +import { KernelSelector } from '../kernels/kernelSelector'; +import { NotebookStarter } from '../notebookStarter'; +import { LiveShareParticipantHost } from './liveShareParticipantMixin'; +import { IRoleBasedObject } from './roleBasedFactory'; +import { ServerCache } from './serverCache'; + +// tslint:disable:no-any + +// This class is really just a wrapper around a jupyter execution that also provides a shared live share service +export class HostJupyterExecution + extends LiveShareParticipantHost(JupyterExecutionBase, LiveShare.JupyterExecutionService) + implements IRoleBasedObject, IJupyterExecution { + private serverCache: ServerCache; + constructor( + liveShare: ILiveShareApi, + interpreterService: IInterpreterService, + disposableRegistry: IDisposableRegistry, + asyncRegistry: IAsyncDisposableRegistry, + fileSys: IFileSystem, + workspace: IWorkspaceService, + configService: IConfigurationService, + kernelSelector: KernelSelector, + notebookStarter: NotebookStarter, + appShell: IApplicationShell, + jupyterOutputChannel: IOutputChannel, + serviceContainer: IServiceContainer + ) { + super( + liveShare, + interpreterService, + disposableRegistry, + workspace, + configService, + kernelSelector, + notebookStarter, + appShell, + jupyterOutputChannel, + serviceContainer + ); + this.serverCache = new ServerCache(configService, workspace, fileSys); + asyncRegistry.push(this); + } + + public async dispose(): Promise { + await super.dispose(); + const api = await this.api; + await this.onDetach(api); + + // Cleanup on dispose. We are going away permanently + if (this.serverCache) { + await this.serverCache.dispose(); + } + } + + public async hostConnectToNotebookServer( + options?: INotebookServerOptions, + cancelToken?: CancellationToken + ): Promise { + return super.connectToNotebookServer(await this.serverCache.generateDefaultOptions(options), cancelToken); + } + + public async connectToNotebookServer( + options?: INotebookServerOptions, + cancelToken?: CancellationToken + ): Promise { + return this.serverCache.getOrCreate(this.hostConnectToNotebookServer.bind(this), options, cancelToken); + } + + public async onAttach(api: vsls.LiveShare | null): Promise { + await super.onAttach(api); + + if (api) { + const service = await this.waitForService(); + + // Register handlers for all of the supported remote calls + if (service) { + service.onRequest(LiveShareCommands.isNotebookSupported, this.onRemoteIsNotebookSupported); + service.onRequest(LiveShareCommands.isImportSupported, this.onRemoteIsImportSupported); + service.onRequest(LiveShareCommands.connectToNotebookServer, this.onRemoteConnectToNotebookServer); + service.onRequest(LiveShareCommands.getUsableJupyterPython, this.onRemoteGetUsableJupyterPython); + } + } + } + + public async onDetach(api: vsls.LiveShare | null): Promise { + await super.onDetach(api); + + // clear our cached servers if our role is no longer host or none + const newRole = + api === null || (api.session && api.session.role !== vsls.Role.Guest) ? vsls.Role.Host : vsls.Role.Guest; + if (newRole !== vsls.Role.Host) { + await this.serverCache.dispose(); + } + } + + public getServer(options?: INotebookServerOptions): Promise { + // See if we have this server or not. + return this.serverCache.get(options); + } + + private onRemoteIsNotebookSupported = (_args: any[], cancellation: CancellationToken): Promise => { + // Just call local + return this.isNotebookSupported(cancellation); + }; + + private onRemoteIsImportSupported = (_args: any[], cancellation: CancellationToken): Promise => { + // Just call local + return this.isImportSupported(cancellation); + }; + + private onRemoteConnectToNotebookServer = async ( + args: any[], + cancellation: CancellationToken + ): Promise => { + // Connect to the local server. THe local server should have started the port forwarding already + const localServer = await this.connectToNotebookServer( + args[0] as INotebookServerOptions | undefined, + cancellation + ); + + // Extract the URI and token for the other side + if (localServer) { + // The other side should be using 'localhost' for anything it's port forwarding. That should just remap + // on the guest side. However we need to eliminate the dispose method. Methods are not serializable + const connectionInfo = localServer.getConnectionInfo(); + if (connectionInfo) { + return { + baseUrl: connectionInfo.baseUrl, + token: connectionInfo.token, + hostName: connectionInfo.hostName, + localLaunch: false, + localProcExitCode: undefined, + disconnected: (_l) => { + return { dispose: noop }; + }, + dispose: noop + }; + } + } + }; + + private onRemoteGetUsableJupyterPython = (_args: any[], cancellation: CancellationToken): Promise => { + // Just call local + return this.getUsableJupyterPython(cancellation); + }; +} diff --git a/src/client/datascience/jupyter/liveshare/hostJupyterNotebook.ts b/src/client/datascience/jupyter/liveshare/hostJupyterNotebook.ts index 80bb2d4bb809..1a7c6f429e4f 100644 --- a/src/client/datascience/jupyter/liveshare/hostJupyterNotebook.ts +++ b/src/client/datascience/jupyter/liveshare/hostJupyterNotebook.ts @@ -138,8 +138,8 @@ export class HostJupyterNotebook // Keep track of the number of guests that need to do a catchup request this.catchupPendingCount += - ev.added.filter(e => e.role === vsls.Role.Guest).length - - ev.removed.filter(e => e.role === vsls.Role.Guest).length; + ev.added.filter((e) => e.role === vsls.Role.Guest).length - + ev.removed.filter((e) => e.role === vsls.Role.Guest).length; } public clear(id: string): void { @@ -203,7 +203,7 @@ export class HostJupyterNotebook (cells: ICell[]) => { output = cells; }, - error => { + (error) => { deferred.reject(error); }, () => { @@ -316,12 +316,12 @@ export class HostJupyterNotebook id: string, responseQueues: ResponseQueue[] ): Observable { - return new Observable(subscriber => { + return new Observable((subscriber) => { let pos = 0; // Listen to all of the events on the observable passed in. observable.subscribe( - cells => { + (cells) => { // Forward to the next listener subscriber.next(cells); @@ -334,7 +334,7 @@ export class HostJupyterNotebook this.postException(e, responseQueues); } }, - e => { + (e) => { subscriber.error(e); this.postException(e, responseQueues); }, @@ -377,7 +377,7 @@ export class HostJupyterNotebook this.postResult( ServerResponseType.Exception, { type: ServerResponseType.Exception, time: Date.now(), message: exc.toString() }, - r => r, + (r) => r, responseQueues ); } @@ -394,7 +394,7 @@ export class HostJupyterNotebook // Make a deep copy before we send. Don't want local copies being modified const deepCopy = cloneDeep(typedResult); this.waitForService() - .then(s => { + .then((s) => { if (s) { s.notify(LiveShareCommands.serverResponse, guestTranslator(deepCopy)); } @@ -402,7 +402,7 @@ export class HostJupyterNotebook .ignoreErrors(); // Need to also save in memory for those guests that are in the middle of starting up - responseQueues.forEach(r => r.push(deepCopy)); + responseQueues.forEach((r) => r.push(deepCopy)); } catch (exc) { traceError(exc); } diff --git a/src/client/datascience/jupyter/liveshare/hostJupyterServer.ts b/src/client/datascience/jupyter/liveshare/hostJupyterServer.ts index 9fc2f50a9544..bac0e63170f5 100644 --- a/src/client/datascience/jupyter/liveshare/hostJupyterServer.ts +++ b/src/client/datascience/jupyter/liveshare/hostJupyterServer.ts @@ -1,339 +1,339 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../../common/extensions'; - -import * as os from 'os'; -import * as vscode from 'vscode'; -import { CancellationToken } from 'vscode-jsonrpc'; -import * as vsls from 'vsls/vscode'; - -import { nbformat } from '@jupyterlab/coreutils'; -import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../../common/application/types'; -import { isTestExecution } from '../../../common/constants'; -import { traceInfo } from '../../../common/logger'; -import { IFileSystem } from '../../../common/platform/types'; -import { - IAsyncDisposableRegistry, - IConfigurationService, - IDisposableRegistry, - IOutputChannel, - Resource -} from '../../../common/types'; -import { createDeferred } from '../../../common/utils/async'; -import * as localize from '../../../common/utils/localize'; -import { IInterpreterService } from '../../../interpreter/contracts'; -import { IServiceContainer } from '../../../ioc/types'; -import { Identifiers, LiveShare, LiveShareCommands, RegExpValues } from '../../constants'; -import { - IDataScience, - IJupyterSession, - IJupyterSessionManager, - IJupyterSessionManagerFactory, - INotebook, - INotebookExecutionLogger, - INotebookServer, - INotebookServerLaunchInfo -} from '../../types'; -import { JupyterServerBase } from '../jupyterServer'; -import { KernelSelector } from '../kernels/kernelSelector'; -import { HostJupyterNotebook } from './hostJupyterNotebook'; -import { LiveShareParticipantHost } from './liveShareParticipantMixin'; -import { IRoleBasedObject } from './roleBasedFactory'; - -// tslint:disable-next-line: no-require-imports -// tslint:disable:no-any - -export class HostJupyterServer extends LiveShareParticipantHost(JupyterServerBase, LiveShare.JupyterServerSharedService) - implements IRoleBasedObject, INotebookServer { - private disposed = false; - private portToForward = 0; - private sharedPort: vscode.Disposable | undefined; - constructor( - private liveShare: ILiveShareApi, - _dataScience: IDataScience, - asyncRegistry: IAsyncDisposableRegistry, - disposableRegistry: IDisposableRegistry, - configService: IConfigurationService, - sessionManager: IJupyterSessionManagerFactory, - private workspaceService: IWorkspaceService, - serviceContainer: IServiceContainer, - private appService: IApplicationShell, - private fs: IFileSystem, - private readonly kernelSelector: KernelSelector, - private readonly interpreterService: IInterpreterService, - outputChannel: IOutputChannel - ) { - super( - liveShare, - asyncRegistry, - disposableRegistry, - configService, - sessionManager, - serviceContainer, - outputChannel - ); - } - - public async dispose(): Promise { - if (!this.disposed) { - this.disposed = true; - await super.dispose(); - const api = await this.api; - return this.onDetach(api); - } - } - - public async connect(launchInfo: INotebookServerLaunchInfo, cancelToken?: CancellationToken): Promise { - if (launchInfo.connectionInfo && launchInfo.connectionInfo.localLaunch) { - const portMatch = RegExpValues.ExtractPortRegex.exec(launchInfo.connectionInfo.baseUrl); - if (portMatch && portMatch.length > 1) { - const port = parseInt(portMatch[1], 10); - await this.attemptToForwardPort(this.finishedApi, port); - } - } - return super.connect(launchInfo, cancelToken); - } - - public async onAttach(api: vsls.LiveShare | null): Promise { - await super.onAttach(api); - - if (api && !this.disposed) { - const service = await this.waitForService(); - - // Attach event handlers to different requests - if (service) { - // Requests return arrays - service.onRequest(LiveShareCommands.syncRequest, (_args: any[], _cancellation: CancellationToken) => - this.onSync() - ); - service.onRequest(LiveShareCommands.disposeServer, (_args: any[], _cancellation: CancellationToken) => - this.dispose() - ); - service.onRequest( - LiveShareCommands.createNotebook, - async (args: any[], cancellation: CancellationToken) => { - const resource = this.parseUri(args[0]); - const identity = this.parseUri(args[1]); - // Don't return the notebook. We don't want it to be serialized. We just want its live share server to be started. - const notebook = (await this.createNotebook( - resource, - identity!, - undefined, - cancellation - )) as HostJupyterNotebook; - await notebook.onAttach(api); - } - ); - - // See if we need to forward the port - await this.attemptToForwardPort(api, this.portToForward); - } - } - } - - public async onSessionChange(api: vsls.LiveShare | null): Promise { - await super.onSessionChange(api); - - this.getNotebooks().forEach(async notebook => { - const hostNotebook = (await notebook) as HostJupyterNotebook; - if (hostNotebook) { - await hostNotebook.onSessionChange(api); - } - }); - } - - public async onDetach(api: vsls.LiveShare | null): Promise { - await super.onDetach(api); - - // Make sure to unshare our port - if (api && this.sharedPort) { - this.sharedPort.dispose(); - this.sharedPort = undefined; - } - } - - public async waitForServiceName(): Promise { - // First wait for connect to occur - const launchInfo = await this.waitForConnect(); - - // Use our base name plus our purpose. This means one unique server per purpose - if (!launchInfo) { - return LiveShare.JupyterServerSharedService; - } - // tslint:disable-next-line:no-suspicious-comment - // TODO: Should there be some separator in the name? - return `${LiveShare.JupyterServerSharedService}${launchInfo.purpose}`; - } - - protected async createNotebookInstance( - resource: Resource, - identity: vscode.Uri, - sessionManager: IJupyterSessionManager, - possibleSession: IJupyterSession | undefined, - disposableRegistry: IDisposableRegistry, - configService: IConfigurationService, - serviceContainer: IServiceContainer, - notebookMetadata?: nbformat.INotebookMetadata, - cancelToken?: CancellationToken - ): Promise { - // See if already exists. - const existing = await this.getNotebook(identity); - if (existing) { - // Dispose the possible session as we don't need it - if (possibleSession) { - await possibleSession.dispose(); - } - - // Then we can return the existing notebook. - return existing; - } - - // Compute launch information from the resource and the notebook metadata - const notebookPromise = createDeferred(); - // Save the notebook - this.setNotebook(identity, notebookPromise.promise); - - const getExistingSession = async () => { - const { info, changedKernel } = await this.computeLaunchInfo( - resource, - sessionManager, - notebookMetadata, - cancelToken - ); - - // If we switched kernels, try switching the possible session - if (changedKernel && possibleSession && info.kernelSpec) { - await possibleSession.changeKernel( - info.kernelSpec, - this.configService.getSettings(resource).datascience.jupyterLaunchTimeout - ); - } - - // Start a session (or use the existing one) - const session = possibleSession || (await sessionManager.startNew(info.kernelSpec, cancelToken)); - traceInfo(`Started session ${this.id}`); - return { info, session }; - }; - - try { - const { info, session } = await getExistingSession(); - - if (session) { - // Create our notebook - const notebook = new HostJupyterNotebook( - this.liveShare, - session, - configService, - disposableRegistry, - this, - info, - serviceContainer.getAll(INotebookExecutionLogger), - resource, - identity, - this.getDisposedError.bind(this), - this.workspaceService, - this.appService, - this.fs - ); - - // Wait for it to be ready - traceInfo(`Waiting for idle (session) ${this.id}`); - const idleTimeout = configService.getSettings().datascience.jupyterLaunchTimeout; - await notebook.waitForIdle(idleTimeout); - - // Run initial setup - await notebook.initialize(cancelToken); - - traceInfo(`Finished connecting ${this.id}`); - - notebookPromise.resolve(notebook); - } else { - notebookPromise.reject(this.getDisposedError()); - } - } catch (ex) { - // If there's an error, then reject the promise that is returned. - // This original promise must be rejected as it is cached (check `setNotebook`). - notebookPromise.reject(ex); - } - - return notebookPromise.promise; - } - - private async computeLaunchInfo( - resource: Resource, - sessionManager: IJupyterSessionManager, - notebookMetadata?: nbformat.INotebookMetadata, - cancelToken?: CancellationToken - ): Promise<{ info: INotebookServerLaunchInfo; changedKernel: boolean }> { - // First we need our launch information so we can start a new session (that's what our notebook is really) - let launchInfo = await this.waitForConnect(); - if (!launchInfo) { - throw this.getDisposedError(); - } - // Create a copy of launch info, cuz we're modifying it here. - // This launch info contains the server connection info (that could be shared across other nbs). - // However the kernel info is different. The kernel info is stored as a property of this, hence create a separate instance for each nb. - launchInfo = { - ...launchInfo - }; - - // Determine the interpreter for our resource. If different, we need a different kernel. - const resourceInterpreter = await this.interpreterService.getActiveInterpreter(resource); - - // Find a kernel that can be used. - // Do this only if kernel information has been provided in the metadata, or the resource's interpreter is different. - let changedKernel = false; - if (notebookMetadata?.kernelspec || resourceInterpreter?.displayName !== launchInfo.interpreter?.displayName) { - const kernelInfo = await (launchInfo.connectionInfo.localLaunch - ? this.kernelSelector.getKernelForLocalConnection( - resource, - sessionManager, - notebookMetadata, - isTestExecution(), - cancelToken - ) - : this.kernelSelector.getKernelForRemoteConnection( - resource, - sessionManager, - notebookMetadata, - cancelToken - )); - - const kernelInfoToUse = kernelInfo?.kernelSpec || kernelInfo?.kernelModel; - if (kernelInfoToUse) { - launchInfo.kernelSpec = kernelInfoToUse; - launchInfo.interpreter = resourceInterpreter; - changedKernel = true; - } - } - - return { info: launchInfo, changedKernel }; - } - - private parseUri(uri: string | undefined): Resource { - const parsed = uri ? vscode.Uri.parse(uri) : undefined; - return parsed && - parsed.scheme && - parsed.scheme !== Identifiers.InteractiveWindowIdentityScheme && - parsed.scheme === 'vsls' - ? this.finishedApi!.convertSharedUriToLocal(parsed) - : parsed; - } - - private async attemptToForwardPort(api: vsls.LiveShare | null | undefined, port: number): Promise { - if (port !== 0 && api && api.session && api.session.role === vsls.Role.Host) { - this.portToForward = 0; - this.sharedPort = await api.shareServer({ - port, - displayName: localize.DataScience.liveShareHostFormat().format(os.hostname()) - }); - } else { - this.portToForward = port; - } - } - - private onSync(): Promise { - return Promise.resolve(true); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../../common/extensions'; + +import * as os from 'os'; +import * as vscode from 'vscode'; +import { CancellationToken } from 'vscode-jsonrpc'; +import * as vsls from 'vsls/vscode'; + +import { nbformat } from '@jupyterlab/coreutils'; +import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../../common/application/types'; +import { isTestExecution } from '../../../common/constants'; +import { traceInfo } from '../../../common/logger'; +import { IFileSystem } from '../../../common/platform/types'; +import { + IAsyncDisposableRegistry, + IConfigurationService, + IDisposableRegistry, + IOutputChannel, + Resource +} from '../../../common/types'; +import { createDeferred } from '../../../common/utils/async'; +import * as localize from '../../../common/utils/localize'; +import { IInterpreterService } from '../../../interpreter/contracts'; +import { IServiceContainer } from '../../../ioc/types'; +import { Identifiers, LiveShare, LiveShareCommands, RegExpValues } from '../../constants'; +import { + IDataScience, + IJupyterSession, + IJupyterSessionManager, + IJupyterSessionManagerFactory, + INotebook, + INotebookExecutionLogger, + INotebookServer, + INotebookServerLaunchInfo +} from '../../types'; +import { JupyterServerBase } from '../jupyterServer'; +import { KernelSelector } from '../kernels/kernelSelector'; +import { HostJupyterNotebook } from './hostJupyterNotebook'; +import { LiveShareParticipantHost } from './liveShareParticipantMixin'; +import { IRoleBasedObject } from './roleBasedFactory'; + +// tslint:disable-next-line: no-require-imports +// tslint:disable:no-any + +export class HostJupyterServer extends LiveShareParticipantHost(JupyterServerBase, LiveShare.JupyterServerSharedService) + implements IRoleBasedObject, INotebookServer { + private disposed = false; + private portToForward = 0; + private sharedPort: vscode.Disposable | undefined; + constructor( + private liveShare: ILiveShareApi, + _dataScience: IDataScience, + asyncRegistry: IAsyncDisposableRegistry, + disposableRegistry: IDisposableRegistry, + configService: IConfigurationService, + sessionManager: IJupyterSessionManagerFactory, + private workspaceService: IWorkspaceService, + serviceContainer: IServiceContainer, + private appService: IApplicationShell, + private fs: IFileSystem, + private readonly kernelSelector: KernelSelector, + private readonly interpreterService: IInterpreterService, + outputChannel: IOutputChannel + ) { + super( + liveShare, + asyncRegistry, + disposableRegistry, + configService, + sessionManager, + serviceContainer, + outputChannel + ); + } + + public async dispose(): Promise { + if (!this.disposed) { + this.disposed = true; + await super.dispose(); + const api = await this.api; + return this.onDetach(api); + } + } + + public async connect(launchInfo: INotebookServerLaunchInfo, cancelToken?: CancellationToken): Promise { + if (launchInfo.connectionInfo && launchInfo.connectionInfo.localLaunch) { + const portMatch = RegExpValues.ExtractPortRegex.exec(launchInfo.connectionInfo.baseUrl); + if (portMatch && portMatch.length > 1) { + const port = parseInt(portMatch[1], 10); + await this.attemptToForwardPort(this.finishedApi, port); + } + } + return super.connect(launchInfo, cancelToken); + } + + public async onAttach(api: vsls.LiveShare | null): Promise { + await super.onAttach(api); + + if (api && !this.disposed) { + const service = await this.waitForService(); + + // Attach event handlers to different requests + if (service) { + // Requests return arrays + service.onRequest(LiveShareCommands.syncRequest, (_args: any[], _cancellation: CancellationToken) => + this.onSync() + ); + service.onRequest(LiveShareCommands.disposeServer, (_args: any[], _cancellation: CancellationToken) => + this.dispose() + ); + service.onRequest( + LiveShareCommands.createNotebook, + async (args: any[], cancellation: CancellationToken) => { + const resource = this.parseUri(args[0]); + const identity = this.parseUri(args[1]); + // Don't return the notebook. We don't want it to be serialized. We just want its live share server to be started. + const notebook = (await this.createNotebook( + resource, + identity!, + undefined, + cancellation + )) as HostJupyterNotebook; + await notebook.onAttach(api); + } + ); + + // See if we need to forward the port + await this.attemptToForwardPort(api, this.portToForward); + } + } + } + + public async onSessionChange(api: vsls.LiveShare | null): Promise { + await super.onSessionChange(api); + + this.getNotebooks().forEach(async (notebook) => { + const hostNotebook = (await notebook) as HostJupyterNotebook; + if (hostNotebook) { + await hostNotebook.onSessionChange(api); + } + }); + } + + public async onDetach(api: vsls.LiveShare | null): Promise { + await super.onDetach(api); + + // Make sure to unshare our port + if (api && this.sharedPort) { + this.sharedPort.dispose(); + this.sharedPort = undefined; + } + } + + public async waitForServiceName(): Promise { + // First wait for connect to occur + const launchInfo = await this.waitForConnect(); + + // Use our base name plus our purpose. This means one unique server per purpose + if (!launchInfo) { + return LiveShare.JupyterServerSharedService; + } + // tslint:disable-next-line:no-suspicious-comment + // TODO: Should there be some separator in the name? + return `${LiveShare.JupyterServerSharedService}${launchInfo.purpose}`; + } + + protected async createNotebookInstance( + resource: Resource, + identity: vscode.Uri, + sessionManager: IJupyterSessionManager, + possibleSession: IJupyterSession | undefined, + disposableRegistry: IDisposableRegistry, + configService: IConfigurationService, + serviceContainer: IServiceContainer, + notebookMetadata?: nbformat.INotebookMetadata, + cancelToken?: CancellationToken + ): Promise { + // See if already exists. + const existing = await this.getNotebook(identity); + if (existing) { + // Dispose the possible session as we don't need it + if (possibleSession) { + await possibleSession.dispose(); + } + + // Then we can return the existing notebook. + return existing; + } + + // Compute launch information from the resource and the notebook metadata + const notebookPromise = createDeferred(); + // Save the notebook + this.setNotebook(identity, notebookPromise.promise); + + const getExistingSession = async () => { + const { info, changedKernel } = await this.computeLaunchInfo( + resource, + sessionManager, + notebookMetadata, + cancelToken + ); + + // If we switched kernels, try switching the possible session + if (changedKernel && possibleSession && info.kernelSpec) { + await possibleSession.changeKernel( + info.kernelSpec, + this.configService.getSettings(resource).datascience.jupyterLaunchTimeout + ); + } + + // Start a session (or use the existing one) + const session = possibleSession || (await sessionManager.startNew(info.kernelSpec, cancelToken)); + traceInfo(`Started session ${this.id}`); + return { info, session }; + }; + + try { + const { info, session } = await getExistingSession(); + + if (session) { + // Create our notebook + const notebook = new HostJupyterNotebook( + this.liveShare, + session, + configService, + disposableRegistry, + this, + info, + serviceContainer.getAll(INotebookExecutionLogger), + resource, + identity, + this.getDisposedError.bind(this), + this.workspaceService, + this.appService, + this.fs + ); + + // Wait for it to be ready + traceInfo(`Waiting for idle (session) ${this.id}`); + const idleTimeout = configService.getSettings().datascience.jupyterLaunchTimeout; + await notebook.waitForIdle(idleTimeout); + + // Run initial setup + await notebook.initialize(cancelToken); + + traceInfo(`Finished connecting ${this.id}`); + + notebookPromise.resolve(notebook); + } else { + notebookPromise.reject(this.getDisposedError()); + } + } catch (ex) { + // If there's an error, then reject the promise that is returned. + // This original promise must be rejected as it is cached (check `setNotebook`). + notebookPromise.reject(ex); + } + + return notebookPromise.promise; + } + + private async computeLaunchInfo( + resource: Resource, + sessionManager: IJupyterSessionManager, + notebookMetadata?: nbformat.INotebookMetadata, + cancelToken?: CancellationToken + ): Promise<{ info: INotebookServerLaunchInfo; changedKernel: boolean }> { + // First we need our launch information so we can start a new session (that's what our notebook is really) + let launchInfo = await this.waitForConnect(); + if (!launchInfo) { + throw this.getDisposedError(); + } + // Create a copy of launch info, cuz we're modifying it here. + // This launch info contains the server connection info (that could be shared across other nbs). + // However the kernel info is different. The kernel info is stored as a property of this, hence create a separate instance for each nb. + launchInfo = { + ...launchInfo + }; + + // Determine the interpreter for our resource. If different, we need a different kernel. + const resourceInterpreter = await this.interpreterService.getActiveInterpreter(resource); + + // Find a kernel that can be used. + // Do this only if kernel information has been provided in the metadata, or the resource's interpreter is different. + let changedKernel = false; + if (notebookMetadata?.kernelspec || resourceInterpreter?.displayName !== launchInfo.interpreter?.displayName) { + const kernelInfo = await (launchInfo.connectionInfo.localLaunch + ? this.kernelSelector.getKernelForLocalConnection( + resource, + sessionManager, + notebookMetadata, + isTestExecution(), + cancelToken + ) + : this.kernelSelector.getKernelForRemoteConnection( + resource, + sessionManager, + notebookMetadata, + cancelToken + )); + + const kernelInfoToUse = kernelInfo?.kernelSpec || kernelInfo?.kernelModel; + if (kernelInfoToUse) { + launchInfo.kernelSpec = kernelInfoToUse; + launchInfo.interpreter = resourceInterpreter; + changedKernel = true; + } + } + + return { info: launchInfo, changedKernel }; + } + + private parseUri(uri: string | undefined): Resource { + const parsed = uri ? vscode.Uri.parse(uri) : undefined; + return parsed && + parsed.scheme && + parsed.scheme !== Identifiers.InteractiveWindowIdentityScheme && + parsed.scheme === 'vsls' + ? this.finishedApi!.convertSharedUriToLocal(parsed) + : parsed; + } + + private async attemptToForwardPort(api: vsls.LiveShare | null | undefined, port: number): Promise { + if (port !== 0 && api && api.session && api.session.role === vsls.Role.Host) { + this.portToForward = 0; + this.sharedPort = await api.shareServer({ + port, + displayName: localize.DataScience.liveShareHostFormat().format(os.hostname()) + }); + } else { + this.portToForward = port; + } + } + + private onSync(): Promise { + return Promise.resolve(true); + } +} diff --git a/src/client/datascience/jupyter/liveshare/liveShareParticipantMixin.ts b/src/client/datascience/jupyter/liveshare/liveShareParticipantMixin.ts index 7c34d8e32164..84a1ac362b32 100644 --- a/src/client/datascience/jupyter/liveshare/liveShareParticipantMixin.ts +++ b/src/client/datascience/jupyter/liveshare/liveShareParticipantMixin.ts @@ -1,159 +1,159 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import * as vsls from 'vsls/vscode'; - -import { ILiveShareApi } from '../../../common/application/types'; -import '../../../common/extensions'; -import { IAsyncDisposable } from '../../../common/types'; -import { noop } from '../../../common/utils/misc'; -import { ClassType } from '../../../ioc/types'; -import { ILiveShareParticipant } from './types'; -import { waitForGuestService, waitForHostService } from './utils'; - -// tslint:disable:no-any - -export class LiveShareParticipantDefault implements IAsyncDisposable { - constructor(..._rest: any[]) { - noop(); - } - - public async dispose(): Promise { - noop(); - } -} - -export function LiveShareParticipantGuest>(SuperClass: T, serviceName: string) { - return LiveShareParticipantMixin( - SuperClass, - vsls.Role.Guest, - serviceName, - waitForGuestService - ); -} - -export function LiveShareParticipantHost>(SuperClass: T, serviceName: string) { - return LiveShareParticipantMixin( - SuperClass, - vsls.Role.Host, - serviceName, - waitForHostService - ); -} - -/** - * This is called a mixin class in TypeScript. - * Allows us to have different base classes but inherit behavior (workaround for not allowing multiple inheritance). - * Essentially it sticks a temp class in between the base class and the class you're writing. - * Something like this: - * - * class Base { - * doStuff() { - * - * } - * } - * - * function Mixin = (SuperClass) { - * return class extends SuperClass { - * doExtraStuff() { - * super.doStuff(); - * } - * } - * } - * - * function SubClass extends Mixin(Base) { - * doBar() : { - * super.doExtraStuff(); - * } - * } - * - */ -function LiveShareParticipantMixin, S>( - SuperClass: T, - expectedRole: vsls.Role, - serviceName: string, - serviceWaiter: (api: vsls.LiveShare, name: string) => Promise -) { - return class extends SuperClass implements ILiveShareParticipant { - protected finishedApi: vsls.LiveShare | null | undefined; - protected api: Promise; - private actualRole = vsls.Role.None; - private wantedRole = expectedRole; - private servicePromise: Promise | undefined; - private serviceFullName: string | undefined; - - constructor(...rest: any[]) { - super(...rest); - // First argument should be our live share api - if (rest.length > 0) { - const liveShare = rest[0] as ILiveShareApi; - this.api = liveShare.getApi(); - this.api - .then(a => { - this.finishedApi = a; - this.onSessionChange(a).ignoreErrors(); - }) - .ignoreErrors(); - } else { - this.api = Promise.resolve(null); - } - } - - public get role() { - return this.actualRole; - } - - public async onPeerChange(_ev: vsls.PeersChangeEvent): Promise { - noop(); - } - - public async onAttach(_api: vsls.LiveShare | null): Promise { - noop(); - } - - public waitForServiceName(): Promise { - // Default is just to return the server name - return Promise.resolve(serviceName); - } - - public onDetach(api: vsls.LiveShare | null): Promise { - if (api && this.serviceFullName && api.session && api.session.role === vsls.Role.Host) { - return api.unshareService(this.serviceFullName); - } - return Promise.resolve(); - } - - public async onSessionChange(api: vsls.LiveShare | null): Promise { - this.servicePromise = undefined; - const newRole = api !== null && api.session ? api.session.role : vsls.Role.None; - if (newRole !== this.actualRole) { - this.actualRole = newRole; - if (newRole === this.wantedRole) { - this.onAttach(api).ignoreErrors(); - } else { - this.onDetach(api).ignoreErrors(); - } - } - } - - public async waitForService(): Promise { - if (this.servicePromise) { - return this.servicePromise; - } - const api = await this.api; - if (!api || api.session.role !== this.wantedRole) { - this.servicePromise = Promise.resolve(undefined); - } else { - this.serviceFullName = this.sanitizeServiceName(await this.waitForServiceName()); - this.servicePromise = serviceWaiter(api, this.serviceFullName); - } - - return this.servicePromise; - } - - // Liveshare doesn't support '.' in service names - private sanitizeServiceName(baseServiceName: string): string { - return baseServiceName.replace('.', ''); - } - }; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import * as vsls from 'vsls/vscode'; + +import { ILiveShareApi } from '../../../common/application/types'; +import '../../../common/extensions'; +import { IAsyncDisposable } from '../../../common/types'; +import { noop } from '../../../common/utils/misc'; +import { ClassType } from '../../../ioc/types'; +import { ILiveShareParticipant } from './types'; +import { waitForGuestService, waitForHostService } from './utils'; + +// tslint:disable:no-any + +export class LiveShareParticipantDefault implements IAsyncDisposable { + constructor(..._rest: any[]) { + noop(); + } + + public async dispose(): Promise { + noop(); + } +} + +export function LiveShareParticipantGuest>(SuperClass: T, serviceName: string) { + return LiveShareParticipantMixin( + SuperClass, + vsls.Role.Guest, + serviceName, + waitForGuestService + ); +} + +export function LiveShareParticipantHost>(SuperClass: T, serviceName: string) { + return LiveShareParticipantMixin( + SuperClass, + vsls.Role.Host, + serviceName, + waitForHostService + ); +} + +/** + * This is called a mixin class in TypeScript. + * Allows us to have different base classes but inherit behavior (workaround for not allowing multiple inheritance). + * Essentially it sticks a temp class in between the base class and the class you're writing. + * Something like this: + * + * class Base { + * doStuff() { + * + * } + * } + * + * function Mixin = (SuperClass) { + * return class extends SuperClass { + * doExtraStuff() { + * super.doStuff(); + * } + * } + * } + * + * function SubClass extends Mixin(Base) { + * doBar() : { + * super.doExtraStuff(); + * } + * } + * + */ +function LiveShareParticipantMixin, S>( + SuperClass: T, + expectedRole: vsls.Role, + serviceName: string, + serviceWaiter: (api: vsls.LiveShare, name: string) => Promise +) { + return class extends SuperClass implements ILiveShareParticipant { + protected finishedApi: vsls.LiveShare | null | undefined; + protected api: Promise; + private actualRole = vsls.Role.None; + private wantedRole = expectedRole; + private servicePromise: Promise | undefined; + private serviceFullName: string | undefined; + + constructor(...rest: any[]) { + super(...rest); + // First argument should be our live share api + if (rest.length > 0) { + const liveShare = rest[0] as ILiveShareApi; + this.api = liveShare.getApi(); + this.api + .then((a) => { + this.finishedApi = a; + this.onSessionChange(a).ignoreErrors(); + }) + .ignoreErrors(); + } else { + this.api = Promise.resolve(null); + } + } + + public get role() { + return this.actualRole; + } + + public async onPeerChange(_ev: vsls.PeersChangeEvent): Promise { + noop(); + } + + public async onAttach(_api: vsls.LiveShare | null): Promise { + noop(); + } + + public waitForServiceName(): Promise { + // Default is just to return the server name + return Promise.resolve(serviceName); + } + + public onDetach(api: vsls.LiveShare | null): Promise { + if (api && this.serviceFullName && api.session && api.session.role === vsls.Role.Host) { + return api.unshareService(this.serviceFullName); + } + return Promise.resolve(); + } + + public async onSessionChange(api: vsls.LiveShare | null): Promise { + this.servicePromise = undefined; + const newRole = api !== null && api.session ? api.session.role : vsls.Role.None; + if (newRole !== this.actualRole) { + this.actualRole = newRole; + if (newRole === this.wantedRole) { + this.onAttach(api).ignoreErrors(); + } else { + this.onDetach(api).ignoreErrors(); + } + } + } + + public async waitForService(): Promise { + if (this.servicePromise) { + return this.servicePromise; + } + const api = await this.api; + if (!api || api.session.role !== this.wantedRole) { + this.servicePromise = Promise.resolve(undefined); + } else { + this.serviceFullName = this.sanitizeServiceName(await this.waitForServiceName()); + this.servicePromise = serviceWaiter(api, this.serviceFullName); + } + + return this.servicePromise; + } + + // Liveshare doesn't support '.' in service names + private sanitizeServiceName(baseServiceName: string): string { + return baseServiceName.replace('.', ''); + } + }; +} diff --git a/src/client/datascience/jupyter/liveshare/responseQueue.ts b/src/client/datascience/jupyter/liveshare/responseQueue.ts index 3d62244e4c12..86de298eaad4 100644 --- a/src/client/datascience/jupyter/liveshare/responseQueue.ts +++ b/src/client/datascience/jupyter/liveshare/responseQueue.ts @@ -1,90 +1,90 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { Observable } from 'rxjs/Observable'; -import { Subscriber } from 'rxjs/Subscriber'; -import * as vsls from 'vsls/vscode'; - -import { createDeferred, Deferred } from '../../../common/utils/async'; -import { LiveShareCommands } from '../../constants'; -import { ICell } from '../../types'; -import { IExecuteObservableResponse, IServerResponse } from './types'; - -export class ResponseQueue { - private responseQueue: IServerResponse[] = []; - private waitingQueue: { deferred: Deferred; predicate(r: IServerResponse): boolean }[] = []; - - public waitForObservable(code: string, id: string): Observable { - // Create a wrapper observable around the actual server - return new Observable(subscriber => { - // Wait for the observable responses to come in - this.waitForResponses(subscriber, code, id).catch(e => { - subscriber.error(e); - subscriber.complete(); - }); - }); - } - - public push(response: IServerResponse) { - this.responseQueue.push(response); - this.dispatchResponse(response); - } - - public send(service: vsls.SharedService, translator: (r: IServerResponse) => IServerResponse) { - this.responseQueue.forEach(r => service.notify(LiveShareCommands.serverResponse, translator(r))); - } - - public clear() { - this.responseQueue = []; - } - - private async waitForResponses(subscriber: Subscriber, code: string, id: string): Promise { - let pos = 0; - let cells: ICell[] | undefined = []; - while (cells !== undefined) { - // Find all matches in order - const response = await this.waitForSpecificResponse(r => { - return r.pos === pos && id === r.id && code === r.code; - }); - if (response.cells) { - subscriber.next(response.cells); - pos += 1; - } - cells = response.cells; - } - subscriber.complete(); - - // Clear responses after we respond to the subscriber. - this.responseQueue = this.responseQueue.filter(r => { - const er = r as IExecuteObservableResponse; - return er.id !== id; - }); - } - - private waitForSpecificResponse(predicate: (response: T) => boolean): Promise { - // See if we have any responses right now with this type - const index = this.responseQueue.findIndex(r => predicate(r as T)); - if (index >= 0) { - // Pull off the match - const match = this.responseQueue[index]; - - // Return this single item - return Promise.resolve(match as T); - } else { - // We have to wait for a new input to happen - const waitable = { deferred: createDeferred(), predicate }; - this.waitingQueue.push(waitable); - return waitable.deferred.promise; - } - } - - private dispatchResponse(response: IServerResponse) { - // Look through all of our responses that are queued up and see if they make a - // waiting promise resolve - const matchIndex = this.waitingQueue.findIndex(w => w.predicate(response)); - if (matchIndex >= 0) { - this.waitingQueue[matchIndex].deferred.resolve(response); - this.waitingQueue.splice(matchIndex, 1); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { Observable } from 'rxjs/Observable'; +import { Subscriber } from 'rxjs/Subscriber'; +import * as vsls from 'vsls/vscode'; + +import { createDeferred, Deferred } from '../../../common/utils/async'; +import { LiveShareCommands } from '../../constants'; +import { ICell } from '../../types'; +import { IExecuteObservableResponse, IServerResponse } from './types'; + +export class ResponseQueue { + private responseQueue: IServerResponse[] = []; + private waitingQueue: { deferred: Deferred; predicate(r: IServerResponse): boolean }[] = []; + + public waitForObservable(code: string, id: string): Observable { + // Create a wrapper observable around the actual server + return new Observable((subscriber) => { + // Wait for the observable responses to come in + this.waitForResponses(subscriber, code, id).catch((e) => { + subscriber.error(e); + subscriber.complete(); + }); + }); + } + + public push(response: IServerResponse) { + this.responseQueue.push(response); + this.dispatchResponse(response); + } + + public send(service: vsls.SharedService, translator: (r: IServerResponse) => IServerResponse) { + this.responseQueue.forEach((r) => service.notify(LiveShareCommands.serverResponse, translator(r))); + } + + public clear() { + this.responseQueue = []; + } + + private async waitForResponses(subscriber: Subscriber, code: string, id: string): Promise { + let pos = 0; + let cells: ICell[] | undefined = []; + while (cells !== undefined) { + // Find all matches in order + const response = await this.waitForSpecificResponse((r) => { + return r.pos === pos && id === r.id && code === r.code; + }); + if (response.cells) { + subscriber.next(response.cells); + pos += 1; + } + cells = response.cells; + } + subscriber.complete(); + + // Clear responses after we respond to the subscriber. + this.responseQueue = this.responseQueue.filter((r) => { + const er = r as IExecuteObservableResponse; + return er.id !== id; + }); + } + + private waitForSpecificResponse(predicate: (response: T) => boolean): Promise { + // See if we have any responses right now with this type + const index = this.responseQueue.findIndex((r) => predicate(r as T)); + if (index >= 0) { + // Pull off the match + const match = this.responseQueue[index]; + + // Return this single item + return Promise.resolve(match as T); + } else { + // We have to wait for a new input to happen + const waitable = { deferred: createDeferred(), predicate }; + this.waitingQueue.push(waitable); + return waitable.deferred.promise; + } + } + + private dispatchResponse(response: IServerResponse) { + // Look through all of our responses that are queued up and see if they make a + // waiting promise resolve + const matchIndex = this.waitingQueue.findIndex((w) => w.predicate(response)); + if (matchIndex >= 0) { + this.waitingQueue[matchIndex].deferred.resolve(response); + this.waitingQueue.splice(matchIndex, 1); + } + } +} diff --git a/src/client/datascience/jupyter/liveshare/roleBasedFactory.ts b/src/client/datascience/jupyter/liveshare/roleBasedFactory.ts index 3ad4b3b20cdc..3d6454c57d17 100644 --- a/src/client/datascience/jupyter/liveshare/roleBasedFactory.ts +++ b/src/client/datascience/jupyter/liveshare/roleBasedFactory.ts @@ -1,112 +1,112 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import * as vscode from 'vscode'; -import * as vsls from 'vsls/vscode'; - -import { ILiveShareApi } from '../../../common/application/types'; -import { IAsyncDisposable } from '../../../common/types'; -import { ClassType } from '../../../ioc/types'; -import { ILiveShareHasRole, ILiveShareParticipant } from './types'; - -export interface IRoleBasedObject extends IAsyncDisposable, ILiveShareParticipant {} - -// tslint:disable:no-any -export class RoleBasedFactory> implements ILiveShareHasRole { - private ctorArgs: ConstructorParameters[]; - private firstTime: boolean = true; - private createPromise: Promise | undefined; - private sessionChangedEmitter = new vscode.EventEmitter(); - private _role: vsls.Role = vsls.Role.None; - - constructor( - private liveShare: ILiveShareApi, - private hostCtor: CtorType, - private guestCtor: CtorType, - ...args: ConstructorParameters - ) { - this.ctorArgs = args; - this.createPromise = this.createBasedOnRole(); // We need to start creation immediately or one side may call before we init. - } - - public get sessionChanged(): vscode.Event { - return this.sessionChangedEmitter.event; - } - - public get role(): vsls.Role { - return this._role; - } - - public get(): Promise { - // Make sure only one create happens at a time - if (this.createPromise) { - return this.createPromise; - } - this.createPromise = this.createBasedOnRole(); - return this.createPromise; - } - - private async createBasedOnRole(): Promise { - // Figure out our role to compute the object to create. Default is host. This - // allows for the host object to keep existing if we suddenly start a new session. - // For a guest, starting a new session resets the entire workspace. - const api = await this.liveShare.getApi(); - let ctor: CtorType = this.hostCtor; - let role: vsls.Role = vsls.Role.Host; - - if (api) { - // Create based on role. - if (api.session && api.session.role === vsls.Role.Host) { - ctor = this.hostCtor; - } else if (api.session && api.session.role === vsls.Role.Guest) { - ctor = this.guestCtor; - role = vsls.Role.Guest; - } - } - this._role = role; - - // Create our object - const obj = new ctor(...this.ctorArgs); - - // Rewrite the object's dispose so we can get rid of our own state. - let objDisposed = false; - const oldDispose = obj.dispose.bind(obj); - obj.dispose = () => { - objDisposed = true; - this.createPromise = undefined; - return oldDispose(); - }; - - // If the session changes, tell the listener - if (api && this.firstTime) { - this.firstTime = false; - api.onDidChangeSession(_a => { - // Dispose the object if the role changes - const newRole = - api !== null && api.session && api.session.role === vsls.Role.Guest - ? vsls.Role.Guest - : vsls.Role.Host; - if (newRole !== role) { - obj.dispose().ignoreErrors(); - } - - // Update the object with respect to the api - if (!objDisposed) { - obj.onSessionChange(api).ignoreErrors(); - } - - // Fire our event indicating old data is no longer valid. - if (newRole !== role) { - this.sessionChangedEmitter.fire(); - } - }); - api.onDidChangePeers(e => { - if (!objDisposed) { - obj.onPeerChange(e).ignoreErrors(); - } - }); - } - - return obj; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import * as vscode from 'vscode'; +import * as vsls from 'vsls/vscode'; + +import { ILiveShareApi } from '../../../common/application/types'; +import { IAsyncDisposable } from '../../../common/types'; +import { ClassType } from '../../../ioc/types'; +import { ILiveShareHasRole, ILiveShareParticipant } from './types'; + +export interface IRoleBasedObject extends IAsyncDisposable, ILiveShareParticipant {} + +// tslint:disable:no-any +export class RoleBasedFactory> implements ILiveShareHasRole { + private ctorArgs: ConstructorParameters[]; + private firstTime: boolean = true; + private createPromise: Promise | undefined; + private sessionChangedEmitter = new vscode.EventEmitter(); + private _role: vsls.Role = vsls.Role.None; + + constructor( + private liveShare: ILiveShareApi, + private hostCtor: CtorType, + private guestCtor: CtorType, + ...args: ConstructorParameters + ) { + this.ctorArgs = args; + this.createPromise = this.createBasedOnRole(); // We need to start creation immediately or one side may call before we init. + } + + public get sessionChanged(): vscode.Event { + return this.sessionChangedEmitter.event; + } + + public get role(): vsls.Role { + return this._role; + } + + public get(): Promise { + // Make sure only one create happens at a time + if (this.createPromise) { + return this.createPromise; + } + this.createPromise = this.createBasedOnRole(); + return this.createPromise; + } + + private async createBasedOnRole(): Promise { + // Figure out our role to compute the object to create. Default is host. This + // allows for the host object to keep existing if we suddenly start a new session. + // For a guest, starting a new session resets the entire workspace. + const api = await this.liveShare.getApi(); + let ctor: CtorType = this.hostCtor; + let role: vsls.Role = vsls.Role.Host; + + if (api) { + // Create based on role. + if (api.session && api.session.role === vsls.Role.Host) { + ctor = this.hostCtor; + } else if (api.session && api.session.role === vsls.Role.Guest) { + ctor = this.guestCtor; + role = vsls.Role.Guest; + } + } + this._role = role; + + // Create our object + const obj = new ctor(...this.ctorArgs); + + // Rewrite the object's dispose so we can get rid of our own state. + let objDisposed = false; + const oldDispose = obj.dispose.bind(obj); + obj.dispose = () => { + objDisposed = true; + this.createPromise = undefined; + return oldDispose(); + }; + + // If the session changes, tell the listener + if (api && this.firstTime) { + this.firstTime = false; + api.onDidChangeSession((_a) => { + // Dispose the object if the role changes + const newRole = + api !== null && api.session && api.session.role === vsls.Role.Guest + ? vsls.Role.Guest + : vsls.Role.Host; + if (newRole !== role) { + obj.dispose().ignoreErrors(); + } + + // Update the object with respect to the api + if (!objDisposed) { + obj.onSessionChange(api).ignoreErrors(); + } + + // Fire our event indicating old data is no longer valid. + if (newRole !== role) { + this.sessionChangedEmitter.fire(); + } + }); + api.onDidChangePeers((e) => { + if (!objDisposed) { + obj.onPeerChange(e).ignoreErrors(); + } + }); + } + + return obj; + } +} diff --git a/src/client/datascience/jupyter/liveshare/serverCache.ts b/src/client/datascience/jupyter/liveshare/serverCache.ts index e68ad38aee95..6ef3fe5a1a97 100644 --- a/src/client/datascience/jupyter/liveshare/serverCache.ts +++ b/src/client/datascience/jupyter/liveshare/serverCache.ts @@ -1,166 +1,166 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../../../common/extensions'; - -import * as path from 'path'; -import * as uuid from 'uuid/v4'; -import { CancellationToken, CancellationTokenSource } from 'vscode'; - -import { IWorkspaceService } from '../../../common/application/types'; -import { IFileSystem } from '../../../common/platform/types'; -import { IAsyncDisposable, IConfigurationService } from '../../../common/types'; -import { INotebookServer, INotebookServerOptions } from '../../types'; - -interface IServerData { - options: INotebookServerOptions; - promise: Promise; - cancelSource: CancellationTokenSource; - resolved: boolean; -} - -export class ServerCache implements IAsyncDisposable { - private cache: Map = new Map(); - private emptyKey = uuid(); - - constructor( - private configService: IConfigurationService, - private workspace: IWorkspaceService, - private fileSystem: IFileSystem - ) {} - - public async getOrCreate( - createFunction: ( - options?: INotebookServerOptions, - cancelToken?: CancellationToken - ) => Promise, - options?: INotebookServerOptions, - cancelToken?: CancellationToken - ): Promise { - const cancelSource = new CancellationTokenSource(); - if (cancelToken) { - cancelToken.onCancellationRequested(() => cancelSource.cancel()); - } - const fixedOptions = await this.generateDefaultOptions(options); - const key = this.generateKey(fixedOptions); - let data: IServerData | undefined; - - // Check to see if we already have a promise for this key - data = this.cache.get(key); - - if (!data) { - // Didn't find one, so start up our promise and cache it - data = { - promise: createFunction(options, cancelSource.token), - options: fixedOptions, - cancelSource, - resolved: false - }; - this.cache.set(key, data); - } - - return data.promise - .then((server: INotebookServer | undefined) => { - if (!server) { - this.cache.delete(key); - return undefined; - } - - // Change the dispose on it so we - // can detach from the server when it goes away. - const oldDispose = server.dispose.bind(server); - server.dispose = () => { - this.cache.delete(key); - return oldDispose(); - }; - - // We've resolved the promise at this point - if (data) { - data.resolved = true; - } - - return server; - }) - .catch(e => { - this.cache.delete(key); - throw e; - }); - } - - public async get(options?: INotebookServerOptions): Promise { - const fixedOptions = await this.generateDefaultOptions(options); - const key = this.generateKey(fixedOptions); - if (this.cache.has(key)) { - return this.cache.get(key)?.promise; - } - } - - public async dispose(): Promise { - await Promise.all( - [...this.cache.values()].map(async d => { - const server = await d.promise; - await server?.dispose(); - }) - ); - this.cache.clear(); - } - - public async generateDefaultOptions(options?: INotebookServerOptions): Promise { - return { - uri: options ? options.uri : undefined, - skipUsingDefaultConfig: options ? options.skipUsingDefaultConfig : false, // Default for this is false - usingDarkTheme: options ? options.usingDarkTheme : undefined, - purpose: options ? options.purpose : uuid(), - workingDir: options && options.workingDir ? options.workingDir : await this.calculateWorkingDirectory(), - metadata: options?.metadata, - allowUI: options?.allowUI ? options.allowUI : () => false - }; - } - - private generateKey(options?: INotebookServerOptions): string { - if (!options) { - return this.emptyKey; - } else { - // combine all the values together to make a unique key - const uri = options.uri ? options.uri : ''; - const useFlag = options.skipUsingDefaultConfig ? 'true' : 'false'; - return `${options.purpose}${uri}${useFlag}${options.workingDir}`; - } - } - - private async calculateWorkingDirectory(): Promise { - let workingDir: string | undefined; - // For a local launch calculate the working directory that we should switch into - const settings = this.configService.getSettings(undefined); - const fileRoot = settings.datascience.notebookFileRoot; - - // If we don't have a workspace open the notebookFileRoot seems to often have a random location in it (we use ${workspaceRoot} as default) - // so only do this setting if we actually have a valid workspace open - if (fileRoot && this.workspace.hasWorkspaceFolders) { - const workspaceFolderPath = this.workspace.workspaceFolders![0].uri.fsPath; - if (path.isAbsolute(fileRoot)) { - if (await this.fileSystem.directoryExists(fileRoot)) { - // User setting is absolute and exists, use it - workingDir = fileRoot; - } else { - // User setting is absolute and doesn't exist, use workspace - workingDir = workspaceFolderPath; - } - } else if (!fileRoot.includes('${')) { - // fileRoot is a relative path, combine it with the workspace folder - const combinedPath = path.join(workspaceFolderPath, fileRoot); - if (await this.fileSystem.directoryExists(combinedPath)) { - // combined path exists, use it - workingDir = combinedPath; - } else { - // Combined path doesn't exist, use workspace - workingDir = workspaceFolderPath; - } - } else { - // fileRoot is a variable that hasn't been expanded - workingDir = fileRoot; - } - } - return workingDir; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../../../common/extensions'; + +import * as path from 'path'; +import * as uuid from 'uuid/v4'; +import { CancellationToken, CancellationTokenSource } from 'vscode'; + +import { IWorkspaceService } from '../../../common/application/types'; +import { IFileSystem } from '../../../common/platform/types'; +import { IAsyncDisposable, IConfigurationService } from '../../../common/types'; +import { INotebookServer, INotebookServerOptions } from '../../types'; + +interface IServerData { + options: INotebookServerOptions; + promise: Promise; + cancelSource: CancellationTokenSource; + resolved: boolean; +} + +export class ServerCache implements IAsyncDisposable { + private cache: Map = new Map(); + private emptyKey = uuid(); + + constructor( + private configService: IConfigurationService, + private workspace: IWorkspaceService, + private fileSystem: IFileSystem + ) {} + + public async getOrCreate( + createFunction: ( + options?: INotebookServerOptions, + cancelToken?: CancellationToken + ) => Promise, + options?: INotebookServerOptions, + cancelToken?: CancellationToken + ): Promise { + const cancelSource = new CancellationTokenSource(); + if (cancelToken) { + cancelToken.onCancellationRequested(() => cancelSource.cancel()); + } + const fixedOptions = await this.generateDefaultOptions(options); + const key = this.generateKey(fixedOptions); + let data: IServerData | undefined; + + // Check to see if we already have a promise for this key + data = this.cache.get(key); + + if (!data) { + // Didn't find one, so start up our promise and cache it + data = { + promise: createFunction(options, cancelSource.token), + options: fixedOptions, + cancelSource, + resolved: false + }; + this.cache.set(key, data); + } + + return data.promise + .then((server: INotebookServer | undefined) => { + if (!server) { + this.cache.delete(key); + return undefined; + } + + // Change the dispose on it so we + // can detach from the server when it goes away. + const oldDispose = server.dispose.bind(server); + server.dispose = () => { + this.cache.delete(key); + return oldDispose(); + }; + + // We've resolved the promise at this point + if (data) { + data.resolved = true; + } + + return server; + }) + .catch((e) => { + this.cache.delete(key); + throw e; + }); + } + + public async get(options?: INotebookServerOptions): Promise { + const fixedOptions = await this.generateDefaultOptions(options); + const key = this.generateKey(fixedOptions); + if (this.cache.has(key)) { + return this.cache.get(key)?.promise; + } + } + + public async dispose(): Promise { + await Promise.all( + [...this.cache.values()].map(async (d) => { + const server = await d.promise; + await server?.dispose(); + }) + ); + this.cache.clear(); + } + + public async generateDefaultOptions(options?: INotebookServerOptions): Promise { + return { + uri: options ? options.uri : undefined, + skipUsingDefaultConfig: options ? options.skipUsingDefaultConfig : false, // Default for this is false + usingDarkTheme: options ? options.usingDarkTheme : undefined, + purpose: options ? options.purpose : uuid(), + workingDir: options && options.workingDir ? options.workingDir : await this.calculateWorkingDirectory(), + metadata: options?.metadata, + allowUI: options?.allowUI ? options.allowUI : () => false + }; + } + + private generateKey(options?: INotebookServerOptions): string { + if (!options) { + return this.emptyKey; + } else { + // combine all the values together to make a unique key + const uri = options.uri ? options.uri : ''; + const useFlag = options.skipUsingDefaultConfig ? 'true' : 'false'; + return `${options.purpose}${uri}${useFlag}${options.workingDir}`; + } + } + + private async calculateWorkingDirectory(): Promise { + let workingDir: string | undefined; + // For a local launch calculate the working directory that we should switch into + const settings = this.configService.getSettings(undefined); + const fileRoot = settings.datascience.notebookFileRoot; + + // If we don't have a workspace open the notebookFileRoot seems to often have a random location in it (we use ${workspaceRoot} as default) + // so only do this setting if we actually have a valid workspace open + if (fileRoot && this.workspace.hasWorkspaceFolders) { + const workspaceFolderPath = this.workspace.workspaceFolders![0].uri.fsPath; + if (path.isAbsolute(fileRoot)) { + if (await this.fileSystem.directoryExists(fileRoot)) { + // User setting is absolute and exists, use it + workingDir = fileRoot; + } else { + // User setting is absolute and doesn't exist, use workspace + workingDir = workspaceFolderPath; + } + } else if (!fileRoot.includes('${')) { + // fileRoot is a relative path, combine it with the workspace folder + const combinedPath = path.join(workspaceFolderPath, fileRoot); + if (await this.fileSystem.directoryExists(combinedPath)) { + // combined path exists, use it + workingDir = combinedPath; + } else { + // Combined path doesn't exist, use workspace + workingDir = workspaceFolderPath; + } + } else { + // fileRoot is a variable that hasn't been expanded + workingDir = fileRoot; + } + } + return workingDir; + } +} diff --git a/src/client/datascience/jupyter/liveshare/types.ts b/src/client/datascience/jupyter/liveshare/types.ts index fccabb8f119d..5e662b135171 100644 --- a/src/client/datascience/jupyter/liveshare/types.ts +++ b/src/client/datascience/jupyter/liveshare/types.ts @@ -1,52 +1,52 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import * as vsls from 'vsls/vscode'; - -import { IAsyncDisposable } from '../../../common/types'; -import { ICell } from '../../types'; - -// tslint:disable:max-classes-per-file - -export enum ServerResponseType { - ExecuteObservable, - Exception -} - -export interface IServerResponse { - type: ServerResponseType; - time: number; -} - -export interface IExecuteObservableResponse extends IServerResponse { - pos: number; - code: string; - id: string; // Unique id so guest side can tell what observable it belongs with - cells: ICell[] | undefined; -} - -export interface IExceptionResponse extends IServerResponse { - message: string; -} - -// Map all responses to their properties -export interface IResponseMapping { - [ServerResponseType.ExecuteObservable]: IExecuteObservableResponse; - [ServerResponseType.Exception]: IExceptionResponse; -} - -export interface ICatchupRequest { - since: number; -} - -export interface ILiveShareHasRole { - readonly role: vsls.Role; -} - -export interface ILiveShareParticipant extends IAsyncDisposable, ILiveShareHasRole { - onSessionChange(api: vsls.LiveShare | null): Promise; - onAttach(api: vsls.LiveShare | null): Promise; - onDetach(api: vsls.LiveShare | null): Promise; - onPeerChange(ev: vsls.PeersChangeEvent): Promise; - waitForServiceName(): Promise; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import * as vsls from 'vsls/vscode'; + +import { IAsyncDisposable } from '../../../common/types'; +import { ICell } from '../../types'; + +// tslint:disable:max-classes-per-file + +export enum ServerResponseType { + ExecuteObservable, + Exception +} + +export interface IServerResponse { + type: ServerResponseType; + time: number; +} + +export interface IExecuteObservableResponse extends IServerResponse { + pos: number; + code: string; + id: string; // Unique id so guest side can tell what observable it belongs with + cells: ICell[] | undefined; +} + +export interface IExceptionResponse extends IServerResponse { + message: string; +} + +// Map all responses to their properties +export interface IResponseMapping { + [ServerResponseType.ExecuteObservable]: IExecuteObservableResponse; + [ServerResponseType.Exception]: IExceptionResponse; +} + +export interface ICatchupRequest { + since: number; +} + +export interface ILiveShareHasRole { + readonly role: vsls.Role; +} + +export interface ILiveShareParticipant extends IAsyncDisposable, ILiveShareHasRole { + onSessionChange(api: vsls.LiveShare | null): Promise; + onAttach(api: vsls.LiveShare | null): Promise; + onDetach(api: vsls.LiveShare | null): Promise; + onPeerChange(ev: vsls.PeersChangeEvent): Promise; + waitForServiceName(): Promise; +} diff --git a/src/client/datascience/jupyter/liveshare/utils.ts b/src/client/datascience/jupyter/liveshare/utils.ts index 0eed6d7b3ace..9c6f39c10a31 100644 --- a/src/client/datascience/jupyter/liveshare/utils.ts +++ b/src/client/datascience/jupyter/liveshare/utils.ts @@ -1,45 +1,45 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { Disposable, Event } from 'vscode'; -import * as vsls from 'vsls/vscode'; - -import { createDeferred } from '../../../common/utils/async'; - -export async function waitForHostService(api: vsls.LiveShare, name: string): Promise { - const service = await api.shareService(name); - if (service && !service.isServiceAvailable) { - return waitForAvailability(service); - } - return service; -} - -export async function waitForGuestService(api: vsls.LiveShare, name: string): Promise { - const service = await api.getSharedService(name); - if (service && !service.isServiceAvailable) { - return waitForAvailability(service); - } - return service; -} - -interface IChangeWatchable { - readonly onDidChangeIsServiceAvailable: Event; -} - -async function waitForAvailability(service: T): Promise { - const deferred = createDeferred(); - let disposable: Disposable | undefined; - try { - disposable = service.onDidChangeIsServiceAvailable(e => { - if (e) { - deferred.resolve(service); - } - }); - await deferred.promise; - } finally { - if (disposable) { - disposable.dispose(); - } - } - return service; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { Disposable, Event } from 'vscode'; +import * as vsls from 'vsls/vscode'; + +import { createDeferred } from '../../../common/utils/async'; + +export async function waitForHostService(api: vsls.LiveShare, name: string): Promise { + const service = await api.shareService(name); + if (service && !service.isServiceAvailable) { + return waitForAvailability(service); + } + return service; +} + +export async function waitForGuestService(api: vsls.LiveShare, name: string): Promise { + const service = await api.getSharedService(name); + if (service && !service.isServiceAvailable) { + return waitForAvailability(service); + } + return service; +} + +interface IChangeWatchable { + readonly onDidChangeIsServiceAvailable: Event; +} + +async function waitForAvailability(service: T): Promise { + const deferred = createDeferred(); + let disposable: Disposable | undefined; + try { + disposable = service.onDidChangeIsServiceAvailable((e) => { + if (e) { + deferred.resolve(service); + } + }); + await deferred.promise; + } finally { + if (disposable) { + disposable.dispose(); + } + } + return service; +} diff --git a/src/client/datascience/jupyter/notebookStarter.ts b/src/client/datascience/jupyter/notebookStarter.ts index 77f7c4e757ea..b723101f0354 100644 --- a/src/client/datascience/jupyter/notebookStarter.ts +++ b/src/client/datascience/jupyter/notebookStarter.ts @@ -68,7 +68,7 @@ export class NotebookStarter implements Disposable { try { // Generate a temp dir with a unique GUID, both to match up our started server and to easily clean up after const tempDirPromise = this.generateTempDir(); - tempDirPromise.then(dir => this.disposables.push(dir)).ignoreErrors(); + tempDirPromise.then((dir) => this.disposables.push(dir)).ignoreErrors(); // Before starting the notebook process, make sure we generate a kernel spec const args = await this.generateArguments(useDefaultConfig, customCommandLine, tempDirPromise); @@ -92,7 +92,7 @@ export class NotebookStarter implements Disposable { // Watch for premature exits if (launchResult.proc) { launchResult.proc.on('exit', (c: number | null) => (exitCode = c)); - launchResult.out.subscribe(out => this.jupyterOutputChannel.append(out.out)); + launchResult.out.subscribe((out) => this.jupyterOutputChannel.append(out.out)); } // Make sure this process gets cleaned up. We might be canceled before the connection finishes. @@ -253,7 +253,7 @@ export class NotebookStarter implements Disposable { args.push('127.0.0.1'); // Now see if we need --allow-root. - return new Promise(resolve => { + return new Promise((resolve) => { cp.exec('id', { encoding: 'utf-8' }, (_, stdout: string | Buffer) => { if (stdout && stdout.toString().includes('(root)')) { args.push('--allow-root'); diff --git a/src/client/datascience/jupyter/serverSelector.ts b/src/client/datascience/jupyter/serverSelector.ts index 4478c0c41aef..e11700a862e2 100644 --- a/src/client/datascience/jupyter/serverSelector.ts +++ b/src/client/datascience/jupyter/serverSelector.ts @@ -154,7 +154,7 @@ export class JupyterServerSelector { // Get our list of recent server connections and display that as well const savedURIList = getSavedUriList(this.globalState); - savedURIList.forEach(uriItem => { + savedURIList.forEach((uriItem) => { if (uriItem.uri) { const uriDate = new Date(uriItem.time); items.push({ diff --git a/src/client/datascience/liveshare/liveshare.ts b/src/client/datascience/liveshare/liveshare.ts index 1303b6251277..c7ecb30ec1d8 100644 --- a/src/client/datascience/liveshare/liveshare.ts +++ b/src/client/datascience/liveshare/liveshare.ts @@ -1,62 +1,62 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { inject, injectable } from 'inversify'; -import * as vsls from 'vsls/vscode'; - -import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../common/application/types'; -import { IConfigurationService, IDisposableRegistry } from '../../common/types'; -import { LiveShareProxy } from './liveshareProxy'; - -// tslint:disable:no-any unified-signatures - -@injectable() -export class LiveShareApi implements ILiveShareApi { - private supported: boolean = false; - private apiPromise: Promise | undefined; - private disposed: boolean = false; - - constructor( - @inject(IDisposableRegistry) disposableRegistry: IDisposableRegistry, - @inject(IWorkspaceService) workspace: IWorkspaceService, - @inject(IConfigurationService) private configService: IConfigurationService, - @inject(IApplicationShell) private appShell: IApplicationShell - ) { - const disposable = workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('python.dataScience', undefined)) { - // When config changes happen, recreate our commands. - this.onSettingsChanged(); - } - }); - disposableRegistry.push(disposable); - disposableRegistry.push(this); - this.onSettingsChanged(); - } - - public dispose(): void { - this.disposed = true; - } - - public getApi(): Promise { - if (this.disposed) { - return Promise.resolve(null); - } - return this.apiPromise!; - } - - private onSettingsChanged() { - const supported = this.configService.getSettings().datascience.allowLiveShare; - if (supported !== this.supported) { - this.supported = supported ? true : false; - const liveShareTimeout = this.configService.getSettings().datascience.liveShareConnectionTimeout; - this.apiPromise = supported - ? vsls - .getApi() - .then(a => (a ? new LiveShareProxy(this.appShell, liveShareTimeout, a) : a)) - .catch(_e => null) - : Promise.resolve(null); - } else if (!this.apiPromise) { - this.apiPromise = Promise.resolve(null); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { inject, injectable } from 'inversify'; +import * as vsls from 'vsls/vscode'; + +import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../common/application/types'; +import { IConfigurationService, IDisposableRegistry } from '../../common/types'; +import { LiveShareProxy } from './liveshareProxy'; + +// tslint:disable:no-any unified-signatures + +@injectable() +export class LiveShareApi implements ILiveShareApi { + private supported: boolean = false; + private apiPromise: Promise | undefined; + private disposed: boolean = false; + + constructor( + @inject(IDisposableRegistry) disposableRegistry: IDisposableRegistry, + @inject(IWorkspaceService) workspace: IWorkspaceService, + @inject(IConfigurationService) private configService: IConfigurationService, + @inject(IApplicationShell) private appShell: IApplicationShell + ) { + const disposable = workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('python.dataScience', undefined)) { + // When config changes happen, recreate our commands. + this.onSettingsChanged(); + } + }); + disposableRegistry.push(disposable); + disposableRegistry.push(this); + this.onSettingsChanged(); + } + + public dispose(): void { + this.disposed = true; + } + + public getApi(): Promise { + if (this.disposed) { + return Promise.resolve(null); + } + return this.apiPromise!; + } + + private onSettingsChanged() { + const supported = this.configService.getSettings().datascience.allowLiveShare; + if (supported !== this.supported) { + this.supported = supported ? true : false; + const liveShareTimeout = this.configService.getSettings().datascience.liveShareConnectionTimeout; + this.apiPromise = supported + ? vsls + .getApi() + .then((a) => (a ? new LiveShareProxy(this.appShell, liveShareTimeout, a) : a)) + .catch((_e) => null) + : Promise.resolve(null); + } else if (!this.apiPromise) { + this.apiPromise = Promise.resolve(null); + } + } +} diff --git a/src/client/datascience/liveshare/liveshareProxy.ts b/src/client/datascience/liveshare/liveshareProxy.ts index c774a51c971f..4a4b2f10b5d3 100644 --- a/src/client/datascience/liveshare/liveshareProxy.ts +++ b/src/client/datascience/liveshare/liveshareProxy.ts @@ -1,182 +1,182 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { Disposable, Event, TreeDataProvider, Uri } from 'vscode'; -import * as vsls from 'vsls/vscode'; - -import { IApplicationShell } from '../../common/application/types'; -import { createDeferred, Deferred } from '../../common/utils/async'; -import * as localize from '../../common/utils/localize'; -import { LiveShare, LiveShareCommands } from '../constants'; -import { ServiceProxy } from './serviceProxy'; - -// tslint:disable:no-any unified-signatures -export class LiveShareProxy implements vsls.LiveShare { - private currentRole: vsls.Role = vsls.Role.None; - private guestChecker: vsls.SharedService | vsls.SharedServiceProxy | null = null; - private pendingGuestCheckCount = 0; - private peerCheckPromise: Deferred | undefined; - constructor( - private applicationShell: IApplicationShell, - private peerTimeout: number | undefined, - private realApi: vsls.LiveShare - ) { - this.realApi.onDidChangePeers(this.onPeersChanged, this); - this.realApi.onDidChangeSession(this.onSessionChanged, this); - this.onSessionChanged({ session: this.realApi.session }).ignoreErrors(); - } - public get session(): vsls.Session { - return this.realApi.session; - } - public get onDidChangeSession(): Event { - return this.realApi.onDidChangeSession; - } - public get peers(): vsls.Peer[] { - return this.realApi.peers; - } - public get onDidChangePeers(): Event { - return this.realApi.onDidChangePeers; - } - public share(options?: vsls.ShareOptions | undefined): Promise { - return this.realApi.share(options); - } - public join(link: Uri, options?: vsls.JoinOptions | undefined): Promise { - return this.realApi.join(link, options); - } - public end(): Promise { - return this.realApi.end(); - } - public async shareService(name: string): Promise { - // Create the real shared service. - const realService = await this.realApi.shareService(name); - - // Create a proxy for the shared service. This allows us to wait for the next request/response - // on the shared service to cause a failure when the guest doesn't have the python extension installed. - if (realService) { - return new ServiceProxy( - realService, - () => this.peersAreOkay(), - () => this.forceShutdown() - ); - } - - return realService; - } - public unshareService(name: string): Promise { - return this.realApi.unshareService(name); - } - public getSharedService(name: string): Promise { - return this.realApi.getSharedService(name); - } - public convertLocalUriToShared(localUri: Uri): Uri { - return this.realApi.convertLocalUriToShared(localUri); - } - public convertSharedUriToLocal(sharedUri: Uri): Uri { - return this.realApi.convertSharedUriToLocal(sharedUri); - } - public registerCommand(command: string, isEnabled?: (() => boolean) | undefined, thisArg?: any): Disposable | null { - return this.realApi.registerCommand(command, isEnabled, thisArg); - } - public registerTreeDataProvider(viewId: vsls.View, treeDataProvider: TreeDataProvider): Disposable | null { - return this.realApi.registerTreeDataProvider(viewId, treeDataProvider); - } - public registerContactServiceProvider( - name: string, - contactServiceProvider: vsls.ContactServiceProvider - ): Disposable | null { - return this.realApi.registerContactServiceProvider(name, contactServiceProvider); - } - public shareServer(server: vsls.Server): Promise { - return this.realApi.shareServer(server); - } - public getContacts(emails: string[]): Promise { - return this.realApi.getContacts(emails); - } - - private async onSessionChanged(ev: vsls.SessionChangeEvent): Promise { - const newRole = ev.session ? ev.session.role : vsls.Role.None; - if (this.currentRole !== newRole) { - // Setup our guest checker service. - if (this.currentRole === vsls.Role.Host) { - await this.realApi.unshareService(LiveShare.GuestCheckerService); - } - this.currentRole = newRole; - - // If host, we need to listen for responses - if (this.currentRole === vsls.Role.Host) { - this.guestChecker = await this.realApi.shareService(LiveShare.GuestCheckerService); - if (this.guestChecker) { - this.guestChecker.onNotify(LiveShareCommands.guestCheck, (_args: object) => this.onGuestResponse()); - } - - // If guest, we need to list for requests. - } else if (this.currentRole === vsls.Role.Guest) { - this.guestChecker = await this.realApi.getSharedService(LiveShare.GuestCheckerService); - if (this.guestChecker) { - this.guestChecker.onNotify(LiveShareCommands.guestCheck, (_args: object) => this.onHostRequest()); - } - } - } - } - - private onPeersChanged(_ev: vsls.PeersChangeEvent) { - if (this.currentRole === vsls.Role.Host && this.guestChecker) { - // Update our pending count. This means we need to ask again if positive. - this.pendingGuestCheckCount = this.realApi.peers.length; - this.peerCheckPromise = undefined; - } - } - - private peersAreOkay(): Promise { - // If already asking, just use that promise - if (this.peerCheckPromise) { - return this.peerCheckPromise.promise; - } - - // Shortcut if we don't need to ask. - if (!this.guestChecker || this.currentRole !== vsls.Role.Host || this.pendingGuestCheckCount <= 0) { - return Promise.resolve(true); - } - - // We need to ask each guest then. - this.peerCheckPromise = createDeferred(); - this.guestChecker.notify(LiveShareCommands.guestCheck, {}); - - // Wait for a second and then check - setTimeout(this.validatePendingGuests.bind(this), this.peerTimeout ? this.peerTimeout : 1000); - return this.peerCheckPromise.promise; - } - - private validatePendingGuests() { - if (this.peerCheckPromise && !this.peerCheckPromise.resolved) { - this.peerCheckPromise.resolve(this.pendingGuestCheckCount <= 0); - } - } - - private onGuestResponse() { - // Guest has responded to a guest check. Update our pending count - this.pendingGuestCheckCount -= 1; - if (this.pendingGuestCheckCount <= 0 && this.peerCheckPromise) { - this.peerCheckPromise.resolve(true); - } - } - - private onHostRequest() { - // Host is asking us to respond - if (this.guestChecker && this.currentRole === vsls.Role.Guest) { - this.guestChecker.notify(LiveShareCommands.guestCheck, {}); - } - } - - private forceShutdown() { - // One or more guests doesn't have the python extension installed. Force our live share session to disconnect - this.realApi - .end() - .then(() => { - this.pendingGuestCheckCount = 0; - this.peerCheckPromise = undefined; - this.applicationShell.showErrorMessage(localize.DataScience.liveShareInvalid()); - }) - .ignoreErrors(); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { Disposable, Event, TreeDataProvider, Uri } from 'vscode'; +import * as vsls from 'vsls/vscode'; + +import { IApplicationShell } from '../../common/application/types'; +import { createDeferred, Deferred } from '../../common/utils/async'; +import * as localize from '../../common/utils/localize'; +import { LiveShare, LiveShareCommands } from '../constants'; +import { ServiceProxy } from './serviceProxy'; + +// tslint:disable:no-any unified-signatures +export class LiveShareProxy implements vsls.LiveShare { + private currentRole: vsls.Role = vsls.Role.None; + private guestChecker: vsls.SharedService | vsls.SharedServiceProxy | null = null; + private pendingGuestCheckCount = 0; + private peerCheckPromise: Deferred | undefined; + constructor( + private applicationShell: IApplicationShell, + private peerTimeout: number | undefined, + private realApi: vsls.LiveShare + ) { + this.realApi.onDidChangePeers(this.onPeersChanged, this); + this.realApi.onDidChangeSession(this.onSessionChanged, this); + this.onSessionChanged({ session: this.realApi.session }).ignoreErrors(); + } + public get session(): vsls.Session { + return this.realApi.session; + } + public get onDidChangeSession(): Event { + return this.realApi.onDidChangeSession; + } + public get peers(): vsls.Peer[] { + return this.realApi.peers; + } + public get onDidChangePeers(): Event { + return this.realApi.onDidChangePeers; + } + public share(options?: vsls.ShareOptions | undefined): Promise { + return this.realApi.share(options); + } + public join(link: Uri, options?: vsls.JoinOptions | undefined): Promise { + return this.realApi.join(link, options); + } + public end(): Promise { + return this.realApi.end(); + } + public async shareService(name: string): Promise { + // Create the real shared service. + const realService = await this.realApi.shareService(name); + + // Create a proxy for the shared service. This allows us to wait for the next request/response + // on the shared service to cause a failure when the guest doesn't have the python extension installed. + if (realService) { + return new ServiceProxy( + realService, + () => this.peersAreOkay(), + () => this.forceShutdown() + ); + } + + return realService; + } + public unshareService(name: string): Promise { + return this.realApi.unshareService(name); + } + public getSharedService(name: string): Promise { + return this.realApi.getSharedService(name); + } + public convertLocalUriToShared(localUri: Uri): Uri { + return this.realApi.convertLocalUriToShared(localUri); + } + public convertSharedUriToLocal(sharedUri: Uri): Uri { + return this.realApi.convertSharedUriToLocal(sharedUri); + } + public registerCommand(command: string, isEnabled?: (() => boolean) | undefined, thisArg?: any): Disposable | null { + return this.realApi.registerCommand(command, isEnabled, thisArg); + } + public registerTreeDataProvider(viewId: vsls.View, treeDataProvider: TreeDataProvider): Disposable | null { + return this.realApi.registerTreeDataProvider(viewId, treeDataProvider); + } + public registerContactServiceProvider( + name: string, + contactServiceProvider: vsls.ContactServiceProvider + ): Disposable | null { + return this.realApi.registerContactServiceProvider(name, contactServiceProvider); + } + public shareServer(server: vsls.Server): Promise { + return this.realApi.shareServer(server); + } + public getContacts(emails: string[]): Promise { + return this.realApi.getContacts(emails); + } + + private async onSessionChanged(ev: vsls.SessionChangeEvent): Promise { + const newRole = ev.session ? ev.session.role : vsls.Role.None; + if (this.currentRole !== newRole) { + // Setup our guest checker service. + if (this.currentRole === vsls.Role.Host) { + await this.realApi.unshareService(LiveShare.GuestCheckerService); + } + this.currentRole = newRole; + + // If host, we need to listen for responses + if (this.currentRole === vsls.Role.Host) { + this.guestChecker = await this.realApi.shareService(LiveShare.GuestCheckerService); + if (this.guestChecker) { + this.guestChecker.onNotify(LiveShareCommands.guestCheck, (_args: object) => this.onGuestResponse()); + } + + // If guest, we need to list for requests. + } else if (this.currentRole === vsls.Role.Guest) { + this.guestChecker = await this.realApi.getSharedService(LiveShare.GuestCheckerService); + if (this.guestChecker) { + this.guestChecker.onNotify(LiveShareCommands.guestCheck, (_args: object) => this.onHostRequest()); + } + } + } + } + + private onPeersChanged(_ev: vsls.PeersChangeEvent) { + if (this.currentRole === vsls.Role.Host && this.guestChecker) { + // Update our pending count. This means we need to ask again if positive. + this.pendingGuestCheckCount = this.realApi.peers.length; + this.peerCheckPromise = undefined; + } + } + + private peersAreOkay(): Promise { + // If already asking, just use that promise + if (this.peerCheckPromise) { + return this.peerCheckPromise.promise; + } + + // Shortcut if we don't need to ask. + if (!this.guestChecker || this.currentRole !== vsls.Role.Host || this.pendingGuestCheckCount <= 0) { + return Promise.resolve(true); + } + + // We need to ask each guest then. + this.peerCheckPromise = createDeferred(); + this.guestChecker.notify(LiveShareCommands.guestCheck, {}); + + // Wait for a second and then check + setTimeout(this.validatePendingGuests.bind(this), this.peerTimeout ? this.peerTimeout : 1000); + return this.peerCheckPromise.promise; + } + + private validatePendingGuests() { + if (this.peerCheckPromise && !this.peerCheckPromise.resolved) { + this.peerCheckPromise.resolve(this.pendingGuestCheckCount <= 0); + } + } + + private onGuestResponse() { + // Guest has responded to a guest check. Update our pending count + this.pendingGuestCheckCount -= 1; + if (this.pendingGuestCheckCount <= 0 && this.peerCheckPromise) { + this.peerCheckPromise.resolve(true); + } + } + + private onHostRequest() { + // Host is asking us to respond + if (this.guestChecker && this.currentRole === vsls.Role.Guest) { + this.guestChecker.notify(LiveShareCommands.guestCheck, {}); + } + } + + private forceShutdown() { + // One or more guests doesn't have the python extension installed. Force our live share session to disconnect + this.realApi + .end() + .then(() => { + this.pendingGuestCheckCount = 0; + this.peerCheckPromise = undefined; + this.applicationShell.showErrorMessage(localize.DataScience.liveShareInvalid()); + }) + .ignoreErrors(); + } +} diff --git a/src/client/datascience/liveshare/postOffice.ts b/src/client/datascience/liveshare/postOffice.ts index cb28aa234b0c..08f98b8deb74 100644 --- a/src/client/datascience/liveshare/postOffice.ts +++ b/src/client/datascience/liveshare/postOffice.ts @@ -1,280 +1,282 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { JSONArray } from '@phosphor/coreutils'; -import * as vscode from 'vscode'; -import * as vsls from 'vsls/vscode'; - -import { ILiveShareApi } from '../../common/application/types'; -import { IAsyncDisposable } from '../../common/types'; -import { createDeferred, Deferred } from '../../common/utils/async'; -import { LiveShare } from '../constants'; - -// tslint:disable:no-any - -interface IMessageArgs { - args: string; -} - -// This class is used to register two communication between a host and all of its guests -export class PostOffice implements IAsyncDisposable { - private name: string; - private startedPromise: Deferred | undefined; - private hostServer: vsls.SharedService | null = null; - private guestServer: vsls.SharedServiceProxy | null = null; - private currentRole: vsls.Role = vsls.Role.None; - private currentPeerCount: number = 0; - private peerCountChangedEmitter: vscode.EventEmitter = new vscode.EventEmitter(); - private commandMap: { [key: string]: { thisArg: any; callback(...args: any[]): void } } = {}; - - constructor( - name: string, - private liveShareApi: ILiveShareApi, - private hostArgsTranslator?: (api: vsls.LiveShare | null, command: string, role: vsls.Role, args: any[]) => void - ) { - this.name = name; - - // Note to self, could the callbacks be keeping things alive that we don't want to be alive? - } - - public get peerCount() { - return this.currentPeerCount; - } - - public get peerCountChanged(): vscode.Event { - return this.peerCountChangedEmitter.event; - } - - public get role() { - return this.currentRole; - } - - public async dispose() { - this.peerCountChangedEmitter.fire(); - this.peerCountChangedEmitter.dispose(); - if (this.hostServer) { - const s = await this.getApi(); - if (s !== null) { - await s.unshareService(this.name); - } - this.hostServer = null; - } - this.guestServer = null; - } - - public async postCommand(command: string, ...args: any[]): Promise { - // Make sure startup finished - const api = await this.getApi(); - let skipDefault = false; - - if (api && api.session) { - switch (this.currentRole) { - case vsls.Role.Guest: - // Ask host to broadcast - if (this.guestServer) { - this.guestServer.notify( - LiveShare.LiveShareBroadcastRequest, - this.createBroadcastArgs(command, ...args) - ); - } - skipDefault = true; - break; - case vsls.Role.Host: - // Notify everybody and call our local callback (by falling through) - if (this.hostServer) { - this.hostServer.notify( - this.escapeCommandName(command), - this.translateArgs(api, command, ...args) - ); - } - break; - default: - break; - } - } - - if (!skipDefault) { - // Default when not connected is to just call the registered callback - this.callCallback(command, ...args); - } - } - - public async registerCallback(command: string, callback: (...args: any[]) => void, thisArg?: any): Promise { - const api = await this.getApi(); - - // For a guest, make sure to register the notification - if (api && api.session && api.session.role === vsls.Role.Guest && this.guestServer) { - this.guestServer.onNotify(this.escapeCommandName(command), a => - this.onGuestNotify(command, a as IMessageArgs) - ); - } - - // Always stick in the command map so that if we switch roles, we reregister - this.commandMap[command] = { callback, thisArg }; - } - - private createBroadcastArgs(command: string, ...args: any[]): IMessageArgs { - return { args: JSON.stringify([command, ...args]) }; - } - - private translateArgs(api: vsls.LiveShare, command: string, ...args: any[]): IMessageArgs { - // Make sure to eliminate all .toJSON functions on our arguments. Otherwise they're stringified incorrectly - for (let a = 0; a <= args.length; a += 1) { - // Eliminate this on only object types (https://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript) - if (args[a] === Object(args[a])) { - args[a].toJSON = undefined; - } - } - - // Copy our args so we don't affect callers. - const copyArgs = JSON.parse(JSON.stringify(args)); - - // Some file path args need to have their values translated to guest - // uri format for use on a guest. Try to find any file arguments - const callback = this.commandMap.hasOwnProperty(command) ? this.commandMap[command].callback : undefined; - if (callback) { - // Give the passed in args translator a chance to attempt a translation - if (this.hostArgsTranslator) { - this.hostArgsTranslator(api, command, vsls.Role.Host, copyArgs); - } - } - - // Then wrap them all up in a string. - return { args: JSON.stringify(copyArgs) }; - } - - private escapeCommandName(command: string): string { - // Replace . with $ instead. - return command.replace(/\./g, '$'); - } - - private unescapeCommandName(command: string): string { - // Turn $ back into . - return command.replace(/\$/g, '.'); - } - - private onGuestNotify = (command: string, m: IMessageArgs) => { - const unescaped = this.unescapeCommandName(command); - const args = JSON.parse(m.args) as JSONArray; - this.callCallback(unescaped, ...args); - }; - - private callCallback(command: string, ...args: any[]) { - const callback = this.getCallback(command); - if (callback) { - callback(...args); - } - } - - private getCallback(command: string): ((...args: any[]) => void) | undefined { - let callback = this.commandMap.hasOwnProperty(command) ? this.commandMap[command].callback : undefined; - if (callback) { - // Bind the this arg if necessary - const thisArg = this.commandMap[command].thisArg; - if (thisArg) { - callback = callback.bind(thisArg); - } - } - - return callback; - } - - private getApi(): Promise { - if (!this.startedPromise) { - this.startedPromise = createDeferred(); - this.startCommandServer() - .then(v => this.startedPromise!.resolve(v)) - .catch(e => this.startedPromise!.reject(e)); - } - - return this.startedPromise.promise; - } - - private async startCommandServer(): Promise { - const api = await this.liveShareApi.getApi(); - if (api !== null) { - api.onDidChangeSession(() => this.onChangeSession(api).ignoreErrors()); - api.onDidChangePeers(() => this.onChangePeers(api).ignoreErrors()); - await this.onChangeSession(api); - await this.onChangePeers(api); - } - return api; - } - - private async onChangeSession(api: vsls.LiveShare): Promise { - // Startup or shutdown our connection to the other side - if (api.session) { - if (this.currentRole !== api.session.role) { - // We're changing our role. - if (this.hostServer) { - await api.unshareService(this.name); - this.hostServer = null; - } - if (this.guestServer) { - this.guestServer = null; - } - } - - // Startup our proxy or server - this.currentRole = api.session.role; - if (api.session.role === vsls.Role.Host) { - this.hostServer = await api.shareService(this.name); - - // When we start the host, listen for the broadcast message - if (this.hostServer !== null) { - this.hostServer.onNotify(LiveShare.LiveShareBroadcastRequest, a => - this.onBroadcastRequest(api, a as IMessageArgs) - ); - } - } else if (api.session.role === vsls.Role.Guest) { - this.guestServer = await api.getSharedService(this.name); - - // When we switch to guest mode, we may have to reregister all of our commands. - this.registerGuestCommands(api); - } - } - } - - private async onChangePeers(api: vsls.LiveShare): Promise { - let newPeerCount = 0; - if (api.session) { - newPeerCount = api.peers.length; - } - if (newPeerCount !== this.currentPeerCount) { - this.peerCountChangedEmitter.fire(newPeerCount); - this.currentPeerCount = newPeerCount; - } - } - - private onBroadcastRequest = (api: vsls.LiveShare, a: IMessageArgs) => { - // This means we need to rebroadcast a request. We should also handle this request ourselves (as this means - // a guest is trying to tell everybody about a command) - if (a.args.length > 0) { - const jsonArray = JSON.parse(a.args) as JSONArray; - if (jsonArray !== null && jsonArray.length >= 2) { - const firstArg = jsonArray[0]!; // More stupid hygiene problems. - const command = firstArg !== null ? firstArg.toString() : ''; - - // Args need to be translated from guest to host - const rest = jsonArray.slice(1); - if (this.hostArgsTranslator) { - this.hostArgsTranslator(api, command, vsls.Role.Guest, rest); - } - - this.postCommand(command, ...rest).ignoreErrors(); - } - } - }; - - private registerGuestCommands(api: vsls.LiveShare) { - if (api && api.session && api.session.role === vsls.Role.Guest && this.guestServer !== null) { - const keys = Object.keys(this.commandMap); - keys.forEach(k => { - if (this.guestServer !== null) { - // Hygiene is too dumb to recognize the if above - this.guestServer.onNotify(this.escapeCommandName(k), a => this.onGuestNotify(k, a as IMessageArgs)); - } - }); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { JSONArray } from '@phosphor/coreutils'; +import * as vscode from 'vscode'; +import * as vsls from 'vsls/vscode'; + +import { ILiveShareApi } from '../../common/application/types'; +import { IAsyncDisposable } from '../../common/types'; +import { createDeferred, Deferred } from '../../common/utils/async'; +import { LiveShare } from '../constants'; + +// tslint:disable:no-any + +interface IMessageArgs { + args: string; +} + +// This class is used to register two communication between a host and all of its guests +export class PostOffice implements IAsyncDisposable { + private name: string; + private startedPromise: Deferred | undefined; + private hostServer: vsls.SharedService | null = null; + private guestServer: vsls.SharedServiceProxy | null = null; + private currentRole: vsls.Role = vsls.Role.None; + private currentPeerCount: number = 0; + private peerCountChangedEmitter: vscode.EventEmitter = new vscode.EventEmitter(); + private commandMap: { [key: string]: { thisArg: any; callback(...args: any[]): void } } = {}; + + constructor( + name: string, + private liveShareApi: ILiveShareApi, + private hostArgsTranslator?: (api: vsls.LiveShare | null, command: string, role: vsls.Role, args: any[]) => void + ) { + this.name = name; + + // Note to self, could the callbacks be keeping things alive that we don't want to be alive? + } + + public get peerCount() { + return this.currentPeerCount; + } + + public get peerCountChanged(): vscode.Event { + return this.peerCountChangedEmitter.event; + } + + public get role() { + return this.currentRole; + } + + public async dispose() { + this.peerCountChangedEmitter.fire(); + this.peerCountChangedEmitter.dispose(); + if (this.hostServer) { + const s = await this.getApi(); + if (s !== null) { + await s.unshareService(this.name); + } + this.hostServer = null; + } + this.guestServer = null; + } + + public async postCommand(command: string, ...args: any[]): Promise { + // Make sure startup finished + const api = await this.getApi(); + let skipDefault = false; + + if (api && api.session) { + switch (this.currentRole) { + case vsls.Role.Guest: + // Ask host to broadcast + if (this.guestServer) { + this.guestServer.notify( + LiveShare.LiveShareBroadcastRequest, + this.createBroadcastArgs(command, ...args) + ); + } + skipDefault = true; + break; + case vsls.Role.Host: + // Notify everybody and call our local callback (by falling through) + if (this.hostServer) { + this.hostServer.notify( + this.escapeCommandName(command), + this.translateArgs(api, command, ...args) + ); + } + break; + default: + break; + } + } + + if (!skipDefault) { + // Default when not connected is to just call the registered callback + this.callCallback(command, ...args); + } + } + + public async registerCallback(command: string, callback: (...args: any[]) => void, thisArg?: any): Promise { + const api = await this.getApi(); + + // For a guest, make sure to register the notification + if (api && api.session && api.session.role === vsls.Role.Guest && this.guestServer) { + this.guestServer.onNotify(this.escapeCommandName(command), (a) => + this.onGuestNotify(command, a as IMessageArgs) + ); + } + + // Always stick in the command map so that if we switch roles, we reregister + this.commandMap[command] = { callback, thisArg }; + } + + private createBroadcastArgs(command: string, ...args: any[]): IMessageArgs { + return { args: JSON.stringify([command, ...args]) }; + } + + private translateArgs(api: vsls.LiveShare, command: string, ...args: any[]): IMessageArgs { + // Make sure to eliminate all .toJSON functions on our arguments. Otherwise they're stringified incorrectly + for (let a = 0; a <= args.length; a += 1) { + // Eliminate this on only object types (https://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript) + if (args[a] === Object(args[a])) { + args[a].toJSON = undefined; + } + } + + // Copy our args so we don't affect callers. + const copyArgs = JSON.parse(JSON.stringify(args)); + + // Some file path args need to have their values translated to guest + // uri format for use on a guest. Try to find any file arguments + const callback = this.commandMap.hasOwnProperty(command) ? this.commandMap[command].callback : undefined; + if (callback) { + // Give the passed in args translator a chance to attempt a translation + if (this.hostArgsTranslator) { + this.hostArgsTranslator(api, command, vsls.Role.Host, copyArgs); + } + } + + // Then wrap them all up in a string. + return { args: JSON.stringify(copyArgs) }; + } + + private escapeCommandName(command: string): string { + // Replace . with $ instead. + return command.replace(/\./g, '$'); + } + + private unescapeCommandName(command: string): string { + // Turn $ back into . + return command.replace(/\$/g, '.'); + } + + private onGuestNotify = (command: string, m: IMessageArgs) => { + const unescaped = this.unescapeCommandName(command); + const args = JSON.parse(m.args) as JSONArray; + this.callCallback(unescaped, ...args); + }; + + private callCallback(command: string, ...args: any[]) { + const callback = this.getCallback(command); + if (callback) { + callback(...args); + } + } + + private getCallback(command: string): ((...args: any[]) => void) | undefined { + let callback = this.commandMap.hasOwnProperty(command) ? this.commandMap[command].callback : undefined; + if (callback) { + // Bind the this arg if necessary + const thisArg = this.commandMap[command].thisArg; + if (thisArg) { + callback = callback.bind(thisArg); + } + } + + return callback; + } + + private getApi(): Promise { + if (!this.startedPromise) { + this.startedPromise = createDeferred(); + this.startCommandServer() + .then((v) => this.startedPromise!.resolve(v)) + .catch((e) => this.startedPromise!.reject(e)); + } + + return this.startedPromise.promise; + } + + private async startCommandServer(): Promise { + const api = await this.liveShareApi.getApi(); + if (api !== null) { + api.onDidChangeSession(() => this.onChangeSession(api).ignoreErrors()); + api.onDidChangePeers(() => this.onChangePeers(api).ignoreErrors()); + await this.onChangeSession(api); + await this.onChangePeers(api); + } + return api; + } + + private async onChangeSession(api: vsls.LiveShare): Promise { + // Startup or shutdown our connection to the other side + if (api.session) { + if (this.currentRole !== api.session.role) { + // We're changing our role. + if (this.hostServer) { + await api.unshareService(this.name); + this.hostServer = null; + } + if (this.guestServer) { + this.guestServer = null; + } + } + + // Startup our proxy or server + this.currentRole = api.session.role; + if (api.session.role === vsls.Role.Host) { + this.hostServer = await api.shareService(this.name); + + // When we start the host, listen for the broadcast message + if (this.hostServer !== null) { + this.hostServer.onNotify(LiveShare.LiveShareBroadcastRequest, (a) => + this.onBroadcastRequest(api, a as IMessageArgs) + ); + } + } else if (api.session.role === vsls.Role.Guest) { + this.guestServer = await api.getSharedService(this.name); + + // When we switch to guest mode, we may have to reregister all of our commands. + this.registerGuestCommands(api); + } + } + } + + private async onChangePeers(api: vsls.LiveShare): Promise { + let newPeerCount = 0; + if (api.session) { + newPeerCount = api.peers.length; + } + if (newPeerCount !== this.currentPeerCount) { + this.peerCountChangedEmitter.fire(newPeerCount); + this.currentPeerCount = newPeerCount; + } + } + + private onBroadcastRequest = (api: vsls.LiveShare, a: IMessageArgs) => { + // This means we need to rebroadcast a request. We should also handle this request ourselves (as this means + // a guest is trying to tell everybody about a command) + if (a.args.length > 0) { + const jsonArray = JSON.parse(a.args) as JSONArray; + if (jsonArray !== null && jsonArray.length >= 2) { + const firstArg = jsonArray[0]!; // More stupid hygiene problems. + const command = firstArg !== null ? firstArg.toString() : ''; + + // Args need to be translated from guest to host + const rest = jsonArray.slice(1); + if (this.hostArgsTranslator) { + this.hostArgsTranslator(api, command, vsls.Role.Guest, rest); + } + + this.postCommand(command, ...rest).ignoreErrors(); + } + } + }; + + private registerGuestCommands(api: vsls.LiveShare) { + if (api && api.session && api.session.role === vsls.Role.Guest && this.guestServer !== null) { + const keys = Object.keys(this.commandMap); + keys.forEach((k) => { + if (this.guestServer !== null) { + // Hygiene is too dumb to recognize the if above + this.guestServer.onNotify(this.escapeCommandName(k), (a) => + this.onGuestNotify(k, a as IMessageArgs) + ); + } + }); + } + } +} diff --git a/src/client/datascience/liveshare/serviceProxy.ts b/src/client/datascience/liveshare/serviceProxy.ts index d599a8b20d0b..31d94cb26433 100644 --- a/src/client/datascience/liveshare/serviceProxy.ts +++ b/src/client/datascience/liveshare/serviceProxy.ts @@ -1,34 +1,34 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { Event } from 'vscode'; -import * as vsls from 'vsls/vscode'; - -// tslint:disable:no-any unified-signatures -export class ServiceProxy implements vsls.SharedService { - constructor( - private realService: vsls.SharedService, - private guestsResponding: () => Promise, - private forceShutdown: () => void - ) {} - public get isServiceAvailable(): boolean { - return this.realService.isServiceAvailable; - } - public get onDidChangeIsServiceAvailable(): Event { - return this.realService.onDidChangeIsServiceAvailable; - } - - public onRequest(name: string, handler: vsls.RequestHandler): void { - return this.realService.onRequest(name, handler); - } - public onNotify(name: string, handler: vsls.NotifyHandler): void { - return this.realService.onNotify(name, handler); - } - public async notify(name: string, args: object): Promise { - if (await this.guestsResponding()) { - return this.realService.notify(name, args); - } else { - this.forceShutdown(); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { Event } from 'vscode'; +import * as vsls from 'vsls/vscode'; + +// tslint:disable:no-any unified-signatures +export class ServiceProxy implements vsls.SharedService { + constructor( + private realService: vsls.SharedService, + private guestsResponding: () => Promise, + private forceShutdown: () => void + ) {} + public get isServiceAvailable(): boolean { + return this.realService.isServiceAvailable; + } + public get onDidChangeIsServiceAvailable(): Event { + return this.realService.onDidChangeIsServiceAvailable; + } + + public onRequest(name: string, handler: vsls.RequestHandler): void { + return this.realService.onRequest(name, handler); + } + public onNotify(name: string, handler: vsls.NotifyHandler): void { + return this.realService.onNotify(name, handler); + } + public async notify(name: string, args: object): Promise { + if (await this.guestsResponding()) { + return this.realService.notify(name, args); + } else { + this.forceShutdown(); + } + } +} diff --git a/src/client/datascience/progress/decorator.ts b/src/client/datascience/progress/decorator.ts index 643ba19a214b..5f0215d08736 100644 --- a/src/client/datascience/progress/decorator.ts +++ b/src/client/datascience/progress/decorator.ts @@ -19,7 +19,7 @@ export function disposeRegisteredReporters() { function report(progress: Progress) { try { - _reporters.forEach(item => item.report(progress)); + _reporters.forEach((item) => item.report(progress)); } catch (ex) { traceError('Failed to report progress', ex); } @@ -34,10 +34,10 @@ function report(progress: Progress) { * @returns */ export function reportAction(action: ReportableAction) { - return function(_target: Object, _propertyName: string, descriptor: TypedPropertyDescriptor) { + return function (_target: Object, _propertyName: string, descriptor: TypedPropertyDescriptor) { const originalMethod = descriptor.value!; // tslint:disable-next-line:no-any no-function-expression - descriptor.value = async function(...args: any[]) { + descriptor.value = async function (...args: any[]) { report({ action, phase: 'started' }); // tslint:disable-next-line:no-invalid-this return originalMethod.apply(this, args).finally(() => { diff --git a/src/client/datascience/progress/progressReporter.ts b/src/client/datascience/progress/progressReporter.ts index 1714d724d87e..45098c9a0ff8 100644 --- a/src/client/datascience/progress/progressReporter.ts +++ b/src/client/datascience/progress/progressReporter.ts @@ -100,7 +100,7 @@ export class ProgressReporter implements IProgressReporter { } const message = getUserMessageForAction(this.currentAction); if (message) { - this.progressReporters.forEach(item => item.report({ message })); + this.progressReporters.forEach((item) => item.report({ message })); } } } diff --git a/src/client/datascience/serviceRegistry.ts b/src/client/datascience/serviceRegistry.ts index 8402c87c84ee..a7285abab47a 100644 --- a/src/client/datascience/serviceRegistry.ts +++ b/src/client/datascience/serviceRegistry.ts @@ -1,218 +1,218 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { IExtensionSingleActivationService } from '../activation/types'; -import { IApplicationEnvironment, IWorkspaceService } from '../common/application/types'; -import { UseCustomEditorApi } from '../common/constants'; -import { IServiceManager } from '../ioc/types'; -import { Activation } from './activation'; -import { CodeCssGenerator } from './codeCssGenerator'; -import { JupyterCommandLineSelectorCommand } from './commands/commandLineSelector'; -import { CommandRegistry } from './commands/commandRegistry'; -import { KernelSwitcherCommand } from './commands/kernelSwitcher'; -import { JupyterServerSelectorCommand } from './commands/serverSelector'; -import { ActiveEditorContextService } from './context/activeEditorContext'; -import { DataViewer } from './data-viewing/dataViewer'; -import { DataViewerDependencyService } from './data-viewing/dataViewerDependencyService'; -import { DataViewerProvider } from './data-viewing/dataViewerProvider'; -import { DataScience } from './datascience'; -import { DataScienceSurveyBannerLogger } from './dataScienceSurveyBanner'; -import { DebugLocationTrackerFactory } from './debugLocationTrackerFactory'; -import { CellHashLogger } from './editor-integration/cellhashLogger'; -import { CellHashProvider } from './editor-integration/cellhashprovider'; -import { CodeLensFactory } from './editor-integration/codeLensFactory'; -import { DataScienceCodeLensProvider } from './editor-integration/codelensprovider'; -import { CodeWatcher } from './editor-integration/codewatcher'; -import { Decorator } from './editor-integration/decorator'; -import { DataScienceErrorHandler } from './errorHandler/errorHandler'; -import { GatherListener } from './gather/gatherListener'; -import { GatherLogger } from './gather/gatherLogger'; -import { DebugListener } from './interactive-common/debugListener'; -import { IntellisenseProvider } from './interactive-common/intellisense/intellisenseProvider'; -import { LinkProvider } from './interactive-common/linkProvider'; -import { NotebookProvider } from './interactive-common/notebookProvider'; -import { ShowPlotListener } from './interactive-common/showPlotListener'; -import { AutoSaveService } from './interactive-ipynb/autoSaveService'; -import { NativeEditor } from './interactive-ipynb/nativeEditor'; -import { NativeEditorCommandListener } from './interactive-ipynb/nativeEditorCommandListener'; -import { NativeEditorOldWebView } from './interactive-ipynb/nativeEditorOldWebView'; -import { NativeEditorProvider } from './interactive-ipynb/nativeEditorProvider'; -import { NativeEditorProviderOld } from './interactive-ipynb/nativeEditorProviderOld'; -import { NativeEditorStorage } from './interactive-ipynb/nativeEditorStorage'; -import { NativeEditorSynchronizer } from './interactive-ipynb/nativeEditorSynchronizer'; -import { InteractiveWindow } from './interactive-window/interactiveWindow'; -import { InteractiveWindowCommandListener } from './interactive-window/interactiveWindowCommandListener'; -import { InteractiveWindowProvider } from './interactive-window/interactiveWindowProvider'; -import { IPyWidgetHandler } from './ipywidgets/ipywidgetHandler'; -import { IPyWidgetMessageDispatcherFactory } from './ipywidgets/ipyWidgetMessageDispatcherFactory'; -import { JupyterCommandLineSelector } from './jupyter/commandLineSelector'; -import { JupyterCommandFactory } from './jupyter/interpreter/jupyterCommand'; -import { JupyterCommandFinder } from './jupyter/interpreter/jupyterCommandFinder'; -import { JupyterCommandInterpreterDependencyService } from './jupyter/interpreter/jupyterCommandInterpreterDependencyService'; -import { JupyterCommandFinderInterpreterExecutionService } from './jupyter/interpreter/jupyterCommandInterpreterExecutionService'; -import { JupyterInterpreterDependencyService } from './jupyter/interpreter/jupyterInterpreterDependencyService'; -import { JupyterInterpreterOldCacheStateStore } from './jupyter/interpreter/jupyterInterpreterOldCacheStateStore'; -import { JupyterInterpreterSelectionCommand } from './jupyter/interpreter/jupyterInterpreterSelectionCommand'; -import { JupyterInterpreterSelector } from './jupyter/interpreter/jupyterInterpreterSelector'; -import { JupyterInterpreterService } from './jupyter/interpreter/jupyterInterpreterService'; -import { JupyterInterpreterStateStore } from './jupyter/interpreter/jupyterInterpreterStateStore'; -import { JupyterInterpreterSubCommandExecutionService } from './jupyter/interpreter/jupyterInterpreterSubCommandExecutionService'; -import { CellOutputMimeTypeTracker } from './jupyter/jupyterCellOutputMimeTypeTracker'; -import { JupyterDebugger } from './jupyter/jupyterDebugger'; -import { JupyterExecutionFactory } from './jupyter/jupyterExecutionFactory'; -import { JupyterExporter } from './jupyter/jupyterExporter'; -import { JupyterImporter } from './jupyter/jupyterImporter'; -import { JupyterPasswordConnect } from './jupyter/jupyterPasswordConnect'; -import { JupyterServerWrapper } from './jupyter/jupyterServerWrapper'; -import { JupyterSessionManagerFactory } from './jupyter/jupyterSessionManagerFactory'; -import { JupyterVariables } from './jupyter/jupyterVariables'; -import { KernelSelectionProvider } from './jupyter/kernels/kernelSelections'; -import { KernelSelector } from './jupyter/kernels/kernelSelector'; -import { KernelService } from './jupyter/kernels/kernelService'; -import { KernelSwitcher } from './jupyter/kernels/kernelSwitcher'; -import { NotebookStarter } from './jupyter/notebookStarter'; -import { ServerPreload } from './jupyter/serverPreload'; -import { JupyterServerSelector } from './jupyter/serverSelector'; -import { PlotViewer } from './plotting/plotViewer'; -import { PlotViewerProvider } from './plotting/plotViewerProvider'; -import { PreWarmActivatedJupyterEnvironmentVariables } from './preWarmVariables'; -import { ProgressReporter } from './progress/progressReporter'; -import { StatusProvider } from './statusProvider'; -import { ThemeFinder } from './themeFinder'; -import { - ICellHashListener, - ICellHashLogger, - ICellHashProvider, - ICodeCssGenerator, - ICodeLensFactory, - ICodeWatcher, - IDataScience, - IDataScienceCodeLensProvider, - IDataScienceCommandListener, - IDataScienceErrorHandler, - IDataViewer, - IDataViewerProvider, - IDebugLocationTracker, - IGatherLogger, - IGatherProvider, - IInteractiveWindow, - IInteractiveWindowListener, - IInteractiveWindowProvider, - IJupyterCommandFactory, - IJupyterDebugger, - IJupyterExecution, - IJupyterInterpreterDependencyManager, - IJupyterPasswordConnect, - IJupyterSessionManagerFactory, - IJupyterSubCommandExecutionService, - IJupyterVariables, - INotebookEditor, - INotebookEditorProvider, - INotebookExecutionLogger, - INotebookExporter, - INotebookImporter, - INotebookProvider, - INotebookServer, - INotebookStorage, - IPlotViewer, - IPlotViewerProvider, - IStatusProvider, - IThemeFinder -} from './types'; - -// README: Did you make sure "dataScienceIocContainer.ts" has also been updated appropriately? - -// tslint:disable-next-line: max-func-body-length -export function registerTypes(serviceManager: IServiceManager) { - const useCustomEditorApi = serviceManager.get(IApplicationEnvironment).packageJson.enableProposedApi; - serviceManager.addSingletonInstance(UseCustomEditorApi, useCustomEditorApi); - - serviceManager.add(ICellHashLogger, CellHashLogger, undefined, [INotebookExecutionLogger]); - serviceManager.add(ICellHashProvider, CellHashProvider); - serviceManager.add(ICodeWatcher, CodeWatcher); - serviceManager.addSingleton(IDataScienceErrorHandler, DataScienceErrorHandler); - serviceManager.add(IDataViewer, DataViewer); - serviceManager.add(IInteractiveWindow, InteractiveWindow); - serviceManager.add(IInteractiveWindowListener, AutoSaveService); - serviceManager.add(IInteractiveWindowListener, DebugListener); - serviceManager.add(IInteractiveWindowListener, GatherListener); - serviceManager.add(IInteractiveWindowListener, IntellisenseProvider); - serviceManager.add(IInteractiveWindowListener, LinkProvider); - serviceManager.add(IInteractiveWindowListener, ShowPlotListener); - serviceManager.add(IInteractiveWindowListener, IPyWidgetHandler); - serviceManager.add(IJupyterCommandFactory, JupyterCommandFactory); - serviceManager.add(INotebookEditor, useCustomEditorApi ? NativeEditor : NativeEditorOldWebView); - serviceManager.add(INotebookExporter, JupyterExporter); - serviceManager.add(INotebookImporter, JupyterImporter); - serviceManager.add(INotebookServer, JupyterServerWrapper); - serviceManager.add(INotebookStorage, NativeEditorStorage); - serviceManager.add(IPlotViewer, PlotViewer); - serviceManager.addSingleton(ActiveEditorContextService, ActiveEditorContextService); - serviceManager.addSingleton(CellOutputMimeTypeTracker, CellOutputMimeTypeTracker, undefined, [IExtensionSingleActivationService, INotebookExecutionLogger]); - serviceManager.addSingleton(CommandRegistry, CommandRegistry); - serviceManager.addSingleton(DataViewerDependencyService, DataViewerDependencyService); - serviceManager.addSingleton(ICodeCssGenerator, CodeCssGenerator); - serviceManager.addSingleton(ICodeLensFactory, CodeLensFactory, undefined, [IInteractiveWindowListener]); - serviceManager.addSingleton(IDataScience, DataScience); - serviceManager.addSingleton(IDataScienceCodeLensProvider, DataScienceCodeLensProvider); - serviceManager.addSingleton(IDataScienceCommandListener, InteractiveWindowCommandListener); - serviceManager.addSingleton(IDataScienceCommandListener, NativeEditorCommandListener); - serviceManager.addSingleton(IDataViewerProvider, DataViewerProvider); - serviceManager.addSingleton(IDebugLocationTracker, DebugLocationTrackerFactory); - serviceManager.addSingleton(IExtensionSingleActivationService, Activation); - serviceManager.addSingleton(IExtensionSingleActivationService, Decorator); - serviceManager.addSingleton(IExtensionSingleActivationService, JupyterInterpreterSelectionCommand); - serviceManager.addSingleton(IExtensionSingleActivationService, PreWarmActivatedJupyterEnvironmentVariables); - serviceManager.addSingleton(IExtensionSingleActivationService, ServerPreload); - serviceManager.addSingleton(IInteractiveWindowListener, DataScienceSurveyBannerLogger); - serviceManager.addSingleton(IInteractiveWindowProvider, InteractiveWindowProvider); - serviceManager.addSingleton(IJupyterDebugger, JupyterDebugger, undefined, [ICellHashListener]); - serviceManager.addSingleton(IJupyterExecution, JupyterExecutionFactory); - serviceManager.addSingleton(IJupyterPasswordConnect, JupyterPasswordConnect); - serviceManager.addSingleton(IJupyterSessionManagerFactory, JupyterSessionManagerFactory); - serviceManager.addSingleton(IJupyterVariables, JupyterVariables); - serviceManager.addSingleton(INotebookEditorProvider, useCustomEditorApi ? NativeEditorProvider : NativeEditorProviderOld); - serviceManager.addSingleton(IPlotViewerProvider, PlotViewerProvider); - serviceManager.addSingleton(IStatusProvider, StatusProvider); - serviceManager.addSingleton(IThemeFinder, ThemeFinder); - serviceManager.addSingleton(JupyterCommandFinder, JupyterCommandFinder); - serviceManager.addSingleton(JupyterCommandLineSelector, JupyterCommandLineSelector); - serviceManager.addSingleton(JupyterCommandLineSelectorCommand, JupyterCommandLineSelectorCommand); - serviceManager.addSingleton(JupyterInterpreterDependencyService, JupyterInterpreterDependencyService); - serviceManager.addSingleton(JupyterInterpreterOldCacheStateStore, JupyterInterpreterOldCacheStateStore); - serviceManager.addSingleton(JupyterInterpreterSelector, JupyterInterpreterSelector); - serviceManager.addSingleton(JupyterInterpreterService, JupyterInterpreterService); - serviceManager.addSingleton(JupyterInterpreterStateStore, JupyterInterpreterStateStore); - serviceManager.addSingleton(JupyterServerSelector, JupyterServerSelector); - serviceManager.addSingleton(JupyterServerSelectorCommand, JupyterServerSelectorCommand); - serviceManager.addSingleton(KernelSelectionProvider, KernelSelectionProvider); - serviceManager.addSingleton(KernelSelector, KernelSelector); - serviceManager.addSingleton(KernelService, KernelService); - serviceManager.addSingleton(KernelSwitcher, KernelSwitcher); - serviceManager.addSingleton(KernelSwitcherCommand, KernelSwitcherCommand); - serviceManager.addSingleton(NotebookStarter, NotebookStarter); - serviceManager.addSingleton(ProgressReporter, ProgressReporter); - serviceManager.addSingleton(NativeEditorSynchronizer, NativeEditorSynchronizer); - serviceManager.addSingleton(INotebookProvider, NotebookProvider); - serviceManager.addSingleton(IPyWidgetMessageDispatcherFactory, IPyWidgetMessageDispatcherFactory); - - // Temporary code, to allow users to revert to the old behavior. - const cfg = serviceManager.get(IWorkspaceService).getConfiguration('python.dataScience', undefined); - if (cfg.get('useOldJupyterServer', false)) { - serviceManager.addSingleton(IJupyterInterpreterDependencyManager, JupyterCommandInterpreterDependencyService); - serviceManager.addSingleton(IJupyterSubCommandExecutionService, JupyterCommandFinderInterpreterExecutionService); - } else { - serviceManager.addSingleton(IJupyterInterpreterDependencyManager, JupyterInterpreterSubCommandExecutionService); - serviceManager.addSingleton(IJupyterSubCommandExecutionService, JupyterInterpreterSubCommandExecutionService); - } - - registerGatherTypes(serviceManager); -} - -export function registerGatherTypes(serviceManager: IServiceManager) { - // tslint:disable-next-line: no-require-imports - const gather = require('./gather/gather'); - - serviceManager.add(IGatherProvider, gather.GatherProvider); - serviceManager.add(IGatherLogger, GatherLogger, undefined, [INotebookExecutionLogger]); -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { IExtensionSingleActivationService } from '../activation/types'; +import { IApplicationEnvironment, IWorkspaceService } from '../common/application/types'; +import { UseCustomEditorApi } from '../common/constants'; +import { IServiceManager } from '../ioc/types'; +import { Activation } from './activation'; +import { CodeCssGenerator } from './codeCssGenerator'; +import { JupyterCommandLineSelectorCommand } from './commands/commandLineSelector'; +import { CommandRegistry } from './commands/commandRegistry'; +import { KernelSwitcherCommand } from './commands/kernelSwitcher'; +import { JupyterServerSelectorCommand } from './commands/serverSelector'; +import { ActiveEditorContextService } from './context/activeEditorContext'; +import { DataViewer } from './data-viewing/dataViewer'; +import { DataViewerDependencyService } from './data-viewing/dataViewerDependencyService'; +import { DataViewerProvider } from './data-viewing/dataViewerProvider'; +import { DataScience } from './datascience'; +import { DataScienceSurveyBannerLogger } from './dataScienceSurveyBanner'; +import { DebugLocationTrackerFactory } from './debugLocationTrackerFactory'; +import { CellHashLogger } from './editor-integration/cellhashLogger'; +import { CellHashProvider } from './editor-integration/cellhashprovider'; +import { CodeLensFactory } from './editor-integration/codeLensFactory'; +import { DataScienceCodeLensProvider } from './editor-integration/codelensprovider'; +import { CodeWatcher } from './editor-integration/codewatcher'; +import { Decorator } from './editor-integration/decorator'; +import { DataScienceErrorHandler } from './errorHandler/errorHandler'; +import { GatherListener } from './gather/gatherListener'; +import { GatherLogger } from './gather/gatherLogger'; +import { DebugListener } from './interactive-common/debugListener'; +import { IntellisenseProvider } from './interactive-common/intellisense/intellisenseProvider'; +import { LinkProvider } from './interactive-common/linkProvider'; +import { NotebookProvider } from './interactive-common/notebookProvider'; +import { ShowPlotListener } from './interactive-common/showPlotListener'; +import { AutoSaveService } from './interactive-ipynb/autoSaveService'; +import { NativeEditor } from './interactive-ipynb/nativeEditor'; +import { NativeEditorCommandListener } from './interactive-ipynb/nativeEditorCommandListener'; +import { NativeEditorOldWebView } from './interactive-ipynb/nativeEditorOldWebView'; +import { NativeEditorProvider } from './interactive-ipynb/nativeEditorProvider'; +import { NativeEditorProviderOld } from './interactive-ipynb/nativeEditorProviderOld'; +import { NativeEditorStorage } from './interactive-ipynb/nativeEditorStorage'; +import { NativeEditorSynchronizer } from './interactive-ipynb/nativeEditorSynchronizer'; +import { InteractiveWindow } from './interactive-window/interactiveWindow'; +import { InteractiveWindowCommandListener } from './interactive-window/interactiveWindowCommandListener'; +import { InteractiveWindowProvider } from './interactive-window/interactiveWindowProvider'; +import { IPyWidgetHandler } from './ipywidgets/ipywidgetHandler'; +import { IPyWidgetMessageDispatcherFactory } from './ipywidgets/ipyWidgetMessageDispatcherFactory'; +import { JupyterCommandLineSelector } from './jupyter/commandLineSelector'; +import { JupyterCommandFactory } from './jupyter/interpreter/jupyterCommand'; +import { JupyterCommandFinder } from './jupyter/interpreter/jupyterCommandFinder'; +import { JupyterCommandInterpreterDependencyService } from './jupyter/interpreter/jupyterCommandInterpreterDependencyService'; +import { JupyterCommandFinderInterpreterExecutionService } from './jupyter/interpreter/jupyterCommandInterpreterExecutionService'; +import { JupyterInterpreterDependencyService } from './jupyter/interpreter/jupyterInterpreterDependencyService'; +import { JupyterInterpreterOldCacheStateStore } from './jupyter/interpreter/jupyterInterpreterOldCacheStateStore'; +import { JupyterInterpreterSelectionCommand } from './jupyter/interpreter/jupyterInterpreterSelectionCommand'; +import { JupyterInterpreterSelector } from './jupyter/interpreter/jupyterInterpreterSelector'; +import { JupyterInterpreterService } from './jupyter/interpreter/jupyterInterpreterService'; +import { JupyterInterpreterStateStore } from './jupyter/interpreter/jupyterInterpreterStateStore'; +import { JupyterInterpreterSubCommandExecutionService } from './jupyter/interpreter/jupyterInterpreterSubCommandExecutionService'; +import { CellOutputMimeTypeTracker } from './jupyter/jupyterCellOutputMimeTypeTracker'; +import { JupyterDebugger } from './jupyter/jupyterDebugger'; +import { JupyterExecutionFactory } from './jupyter/jupyterExecutionFactory'; +import { JupyterExporter } from './jupyter/jupyterExporter'; +import { JupyterImporter } from './jupyter/jupyterImporter'; +import { JupyterPasswordConnect } from './jupyter/jupyterPasswordConnect'; +import { JupyterServerWrapper } from './jupyter/jupyterServerWrapper'; +import { JupyterSessionManagerFactory } from './jupyter/jupyterSessionManagerFactory'; +import { JupyterVariables } from './jupyter/jupyterVariables'; +import { KernelSelectionProvider } from './jupyter/kernels/kernelSelections'; +import { KernelSelector } from './jupyter/kernels/kernelSelector'; +import { KernelService } from './jupyter/kernels/kernelService'; +import { KernelSwitcher } from './jupyter/kernels/kernelSwitcher'; +import { NotebookStarter } from './jupyter/notebookStarter'; +import { ServerPreload } from './jupyter/serverPreload'; +import { JupyterServerSelector } from './jupyter/serverSelector'; +import { PlotViewer } from './plotting/plotViewer'; +import { PlotViewerProvider } from './plotting/plotViewerProvider'; +import { PreWarmActivatedJupyterEnvironmentVariables } from './preWarmVariables'; +import { ProgressReporter } from './progress/progressReporter'; +import { StatusProvider } from './statusProvider'; +import { ThemeFinder } from './themeFinder'; +import { + ICellHashListener, + ICellHashLogger, + ICellHashProvider, + ICodeCssGenerator, + ICodeLensFactory, + ICodeWatcher, + IDataScience, + IDataScienceCodeLensProvider, + IDataScienceCommandListener, + IDataScienceErrorHandler, + IDataViewer, + IDataViewerProvider, + IDebugLocationTracker, + IGatherLogger, + IGatherProvider, + IInteractiveWindow, + IInteractiveWindowListener, + IInteractiveWindowProvider, + IJupyterCommandFactory, + IJupyterDebugger, + IJupyterExecution, + IJupyterInterpreterDependencyManager, + IJupyterPasswordConnect, + IJupyterSessionManagerFactory, + IJupyterSubCommandExecutionService, + IJupyterVariables, + INotebookEditor, + INotebookEditorProvider, + INotebookExecutionLogger, + INotebookExporter, + INotebookImporter, + INotebookProvider, + INotebookServer, + INotebookStorage, + IPlotViewer, + IPlotViewerProvider, + IStatusProvider, + IThemeFinder +} from './types'; + +// README: Did you make sure "dataScienceIocContainer.ts" has also been updated appropriately? + +// tslint:disable-next-line: max-func-body-length +export function registerTypes(serviceManager: IServiceManager) { + const useCustomEditorApi = serviceManager.get(IApplicationEnvironment).packageJson.enableProposedApi; + serviceManager.addSingletonInstance(UseCustomEditorApi, useCustomEditorApi); + + serviceManager.add(ICellHashLogger, CellHashLogger, undefined, [INotebookExecutionLogger]); + serviceManager.add(ICellHashProvider, CellHashProvider); + serviceManager.add(ICodeWatcher, CodeWatcher); + serviceManager.addSingleton(IDataScienceErrorHandler, DataScienceErrorHandler); + serviceManager.add(IDataViewer, DataViewer); + serviceManager.add(IInteractiveWindow, InteractiveWindow); + serviceManager.add(IInteractiveWindowListener, AutoSaveService); + serviceManager.add(IInteractiveWindowListener, DebugListener); + serviceManager.add(IInteractiveWindowListener, GatherListener); + serviceManager.add(IInteractiveWindowListener, IntellisenseProvider); + serviceManager.add(IInteractiveWindowListener, LinkProvider); + serviceManager.add(IInteractiveWindowListener, ShowPlotListener); + serviceManager.add(IInteractiveWindowListener, IPyWidgetHandler); + serviceManager.add(IJupyterCommandFactory, JupyterCommandFactory); + serviceManager.add(INotebookEditor, useCustomEditorApi ? NativeEditor : NativeEditorOldWebView); + serviceManager.add(INotebookExporter, JupyterExporter); + serviceManager.add(INotebookImporter, JupyterImporter); + serviceManager.add(INotebookServer, JupyterServerWrapper); + serviceManager.add(INotebookStorage, NativeEditorStorage); + serviceManager.add(IPlotViewer, PlotViewer); + serviceManager.addSingleton(ActiveEditorContextService, ActiveEditorContextService); + serviceManager.addSingleton(CellOutputMimeTypeTracker, CellOutputMimeTypeTracker, undefined, [IExtensionSingleActivationService, INotebookExecutionLogger]); + serviceManager.addSingleton(CommandRegistry, CommandRegistry); + serviceManager.addSingleton(DataViewerDependencyService, DataViewerDependencyService); + serviceManager.addSingleton(ICodeCssGenerator, CodeCssGenerator); + serviceManager.addSingleton(ICodeLensFactory, CodeLensFactory, undefined, [IInteractiveWindowListener]); + serviceManager.addSingleton(IDataScience, DataScience); + serviceManager.addSingleton(IDataScienceCodeLensProvider, DataScienceCodeLensProvider); + serviceManager.addSingleton(IDataScienceCommandListener, InteractiveWindowCommandListener); + serviceManager.addSingleton(IDataScienceCommandListener, NativeEditorCommandListener); + serviceManager.addSingleton(IDataViewerProvider, DataViewerProvider); + serviceManager.addSingleton(IDebugLocationTracker, DebugLocationTrackerFactory); + serviceManager.addSingleton(IExtensionSingleActivationService, Activation); + serviceManager.addSingleton(IExtensionSingleActivationService, Decorator); + serviceManager.addSingleton(IExtensionSingleActivationService, JupyterInterpreterSelectionCommand); + serviceManager.addSingleton(IExtensionSingleActivationService, PreWarmActivatedJupyterEnvironmentVariables); + serviceManager.addSingleton(IExtensionSingleActivationService, ServerPreload); + serviceManager.addSingleton(IInteractiveWindowListener, DataScienceSurveyBannerLogger); + serviceManager.addSingleton(IInteractiveWindowProvider, InteractiveWindowProvider); + serviceManager.addSingleton(IJupyterDebugger, JupyterDebugger, undefined, [ICellHashListener]); + serviceManager.addSingleton(IJupyterExecution, JupyterExecutionFactory); + serviceManager.addSingleton(IJupyterPasswordConnect, JupyterPasswordConnect); + serviceManager.addSingleton(IJupyterSessionManagerFactory, JupyterSessionManagerFactory); + serviceManager.addSingleton(IJupyterVariables, JupyterVariables); + serviceManager.addSingleton(INotebookEditorProvider, useCustomEditorApi ? NativeEditorProvider : NativeEditorProviderOld); + serviceManager.addSingleton(IPlotViewerProvider, PlotViewerProvider); + serviceManager.addSingleton(IStatusProvider, StatusProvider); + serviceManager.addSingleton(IThemeFinder, ThemeFinder); + serviceManager.addSingleton(JupyterCommandFinder, JupyterCommandFinder); + serviceManager.addSingleton(JupyterCommandLineSelector, JupyterCommandLineSelector); + serviceManager.addSingleton(JupyterCommandLineSelectorCommand, JupyterCommandLineSelectorCommand); + serviceManager.addSingleton(JupyterInterpreterDependencyService, JupyterInterpreterDependencyService); + serviceManager.addSingleton(JupyterInterpreterOldCacheStateStore, JupyterInterpreterOldCacheStateStore); + serviceManager.addSingleton(JupyterInterpreterSelector, JupyterInterpreterSelector); + serviceManager.addSingleton(JupyterInterpreterService, JupyterInterpreterService); + serviceManager.addSingleton(JupyterInterpreterStateStore, JupyterInterpreterStateStore); + serviceManager.addSingleton(JupyterServerSelector, JupyterServerSelector); + serviceManager.addSingleton(JupyterServerSelectorCommand, JupyterServerSelectorCommand); + serviceManager.addSingleton(KernelSelectionProvider, KernelSelectionProvider); + serviceManager.addSingleton(KernelSelector, KernelSelector); + serviceManager.addSingleton(KernelService, KernelService); + serviceManager.addSingleton(KernelSwitcher, KernelSwitcher); + serviceManager.addSingleton(KernelSwitcherCommand, KernelSwitcherCommand); + serviceManager.addSingleton(NotebookStarter, NotebookStarter); + serviceManager.addSingleton(ProgressReporter, ProgressReporter); + serviceManager.addSingleton(NativeEditorSynchronizer, NativeEditorSynchronizer); + serviceManager.addSingleton(INotebookProvider, NotebookProvider); + serviceManager.addSingleton(IPyWidgetMessageDispatcherFactory, IPyWidgetMessageDispatcherFactory); + + // Temporary code, to allow users to revert to the old behavior. + const cfg = serviceManager.get(IWorkspaceService).getConfiguration('python.dataScience', undefined); + if (cfg.get('useOldJupyterServer', false)) { + serviceManager.addSingleton(IJupyterInterpreterDependencyManager, JupyterCommandInterpreterDependencyService); + serviceManager.addSingleton(IJupyterSubCommandExecutionService, JupyterCommandFinderInterpreterExecutionService); + } else { + serviceManager.addSingleton(IJupyterInterpreterDependencyManager, JupyterInterpreterSubCommandExecutionService); + serviceManager.addSingleton(IJupyterSubCommandExecutionService, JupyterInterpreterSubCommandExecutionService); + } + + registerGatherTypes(serviceManager); +} + +export function registerGatherTypes(serviceManager: IServiceManager) { + // tslint:disable-next-line: no-require-imports + const gather = require('./gather/gather'); + + serviceManager.add(IGatherProvider, gather.GatherProvider); + serviceManager.add(IGatherLogger, GatherLogger, undefined, [INotebookExecutionLogger]); +} diff --git a/src/client/datascience/statusProvider.ts b/src/client/datascience/statusProvider.ts index e8ab0b78ee14..c82ad4bad9dc 100644 --- a/src/client/datascience/statusProvider.ts +++ b/src/client/datascience/statusProvider.ts @@ -1,128 +1,128 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { inject, injectable } from 'inversify'; -import { Disposable, ProgressLocation, ProgressOptions } from 'vscode'; - -import { IApplicationShell } from '../common/application/types'; -import { createDeferred, Deferred } from '../common/utils/async'; -import { IInteractiveBase, IStatusProvider } from './types'; - -class StatusItem implements Disposable { - private deferred: Deferred; - private disposed: boolean = false; - private timeout: NodeJS.Timer | number | undefined; - private disposeCallback: () => void; - - constructor(_title: string, disposeCallback: () => void, timeout?: number) { - this.deferred = createDeferred(); - this.disposeCallback = disposeCallback; - - // A timeout is possible too. Auto dispose if that's the case - if (timeout) { - this.timeout = setTimeout(this.dispose, timeout); - } - } - - public dispose = () => { - if (!this.disposed) { - this.disposed = true; - if (this.timeout) { - // tslint:disable-next-line: no-any - clearTimeout(this.timeout as any); - this.timeout = undefined; - } - this.disposeCallback(); - if (!this.deferred.completed) { - this.deferred.resolve(); - } - } - }; - - public promise = (): Promise => { - return this.deferred.promise; - }; - - public reject = () => { - this.deferred.reject(); - this.dispose(); - }; -} - -@injectable() -export class StatusProvider implements IStatusProvider { - private statusCount: number = 0; - - constructor(@inject(IApplicationShell) private applicationShell: IApplicationShell) {} - - public set( - message: string, - showInWebView: boolean, - timeout?: number, - cancel?: () => void, - panel?: IInteractiveBase - ): Disposable { - // Start our progress - this.incrementCount(showInWebView, panel); - - // Create a StatusItem that will return our promise - const statusItem = new StatusItem(message, () => this.decrementCount(panel), timeout); - - const progressOptions: ProgressOptions = { - location: cancel ? ProgressLocation.Notification : ProgressLocation.Window, - title: message, - cancellable: cancel !== undefined - }; - - // Set our application shell status with a busy icon - this.applicationShell.withProgress(progressOptions, (_p, c) => { - if (c && cancel) { - c.onCancellationRequested(() => { - cancel(); - statusItem.reject(); - }); - } - return statusItem.promise(); - }); - - return statusItem; - } - - public async waitWithStatus( - promise: () => Promise, - message: string, - showInWebView: boolean, - timeout?: number, - cancel?: () => void, - panel?: IInteractiveBase - ): Promise { - // Create a status item and wait for our promise to either finish or reject - const status = this.set(message, showInWebView, timeout, cancel, panel); - let result: T; - try { - result = await promise(); - } finally { - status.dispose(); - } - return result; - } - - private incrementCount = (showInWebView: boolean, panel?: IInteractiveBase) => { - if (this.statusCount === 0) { - if (panel && showInWebView) { - panel.startProgress(); - } - } - this.statusCount += 1; - }; - - private decrementCount = (panel?: IInteractiveBase) => { - const updatedCount = this.statusCount - 1; - if (updatedCount === 0) { - if (panel) { - panel.stopProgress(); - } - } - this.statusCount = Math.max(updatedCount, 0); - }; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { inject, injectable } from 'inversify'; +import { Disposable, ProgressLocation, ProgressOptions } from 'vscode'; + +import { IApplicationShell } from '../common/application/types'; +import { createDeferred, Deferred } from '../common/utils/async'; +import { IInteractiveBase, IStatusProvider } from './types'; + +class StatusItem implements Disposable { + private deferred: Deferred; + private disposed: boolean = false; + private timeout: NodeJS.Timer | number | undefined; + private disposeCallback: () => void; + + constructor(_title: string, disposeCallback: () => void, timeout?: number) { + this.deferred = createDeferred(); + this.disposeCallback = disposeCallback; + + // A timeout is possible too. Auto dispose if that's the case + if (timeout) { + this.timeout = setTimeout(this.dispose, timeout); + } + } + + public dispose = () => { + if (!this.disposed) { + this.disposed = true; + if (this.timeout) { + // tslint:disable-next-line: no-any + clearTimeout(this.timeout as any); + this.timeout = undefined; + } + this.disposeCallback(); + if (!this.deferred.completed) { + this.deferred.resolve(); + } + } + }; + + public promise = (): Promise => { + return this.deferred.promise; + }; + + public reject = () => { + this.deferred.reject(); + this.dispose(); + }; +} + +@injectable() +export class StatusProvider implements IStatusProvider { + private statusCount: number = 0; + + constructor(@inject(IApplicationShell) private applicationShell: IApplicationShell) {} + + public set( + message: string, + showInWebView: boolean, + timeout?: number, + cancel?: () => void, + panel?: IInteractiveBase + ): Disposable { + // Start our progress + this.incrementCount(showInWebView, panel); + + // Create a StatusItem that will return our promise + const statusItem = new StatusItem(message, () => this.decrementCount(panel), timeout); + + const progressOptions: ProgressOptions = { + location: cancel ? ProgressLocation.Notification : ProgressLocation.Window, + title: message, + cancellable: cancel !== undefined + }; + + // Set our application shell status with a busy icon + this.applicationShell.withProgress(progressOptions, (_p, c) => { + if (c && cancel) { + c.onCancellationRequested(() => { + cancel(); + statusItem.reject(); + }); + } + return statusItem.promise(); + }); + + return statusItem; + } + + public async waitWithStatus( + promise: () => Promise, + message: string, + showInWebView: boolean, + timeout?: number, + cancel?: () => void, + panel?: IInteractiveBase + ): Promise { + // Create a status item and wait for our promise to either finish or reject + const status = this.set(message, showInWebView, timeout, cancel, panel); + let result: T; + try { + result = await promise(); + } finally { + status.dispose(); + } + return result; + } + + private incrementCount = (showInWebView: boolean, panel?: IInteractiveBase) => { + if (this.statusCount === 0) { + if (panel && showInWebView) { + panel.startProgress(); + } + } + this.statusCount += 1; + }; + + private decrementCount = (panel?: IInteractiveBase) => { + const updatedCount = this.statusCount - 1; + if (updatedCount === 0) { + if (panel) { + panel.stopProgress(); + } + } + this.statusCount = Math.max(updatedCount, 0); + }; +} diff --git a/src/client/datascience/themeFinder.ts b/src/client/datascience/themeFinder.ts index a4bf57b9f79c..b0cc018634f3 100644 --- a/src/client/datascience/themeFinder.ts +++ b/src/client/datascience/themeFinder.ts @@ -1,223 +1,223 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { inject, injectable } from 'inversify'; -import * as path from 'path'; - -import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../common/constants'; -import { traceError } from '../common/logger'; -import { IFileSystem } from '../common/platform/types'; -import { ICurrentProcess, IExtensions } from '../common/types'; -import { IThemeFinder } from './types'; - -// tslint:disable:no-any - -interface IThemeData { - rootFile: string; - isDark: boolean; -} - -@injectable() -export class ThemeFinder implements IThemeFinder { - private themeCache: { [key: string]: IThemeData | undefined } = {}; - private languageCache: { [key: string]: string | undefined } = {}; - - constructor( - @inject(IExtensions) private extensions: IExtensions, - @inject(ICurrentProcess) private currentProcess: ICurrentProcess, - @inject(IFileSystem) private fs: IFileSystem - ) {} - - public async findThemeRootJson(themeName: string): Promise { - // find our data - const themeData = await this.findThemeData(themeName); - - // Use that data if it worked - if (themeData) { - return themeData.rootFile; - } - } - - public async findTmLanguage(language: string): Promise { - // See if already found it or not - if (!this.themeCache.hasOwnProperty(language)) { - try { - this.languageCache[language] = await this.findMatchingLanguage(language); - } catch (exc) { - traceError(exc); - } - } - return this.languageCache[language]; - } - - public async isThemeDark(themeName: string): Promise { - // find our data - const themeData = await this.findThemeData(themeName); - - // Use that data if it worked - if (themeData) { - return themeData.isDark; - } - } - - private async findThemeData(themeName: string): Promise { - // See if already found it or not - if (!this.themeCache.hasOwnProperty(themeName)) { - try { - this.themeCache[themeName] = await this.findMatchingTheme(themeName); - } catch (exc) { - traceError(exc); - } - } - return this.themeCache[themeName]; - } - - private async findMatchingLanguage(language: string): Promise { - const currentExe = this.currentProcess.execPath; - let currentPath = path.dirname(currentExe); - - // Should be somewhere under currentPath/resources/app/extensions inside of a json file - let extensionsPath = path.join(currentPath, 'resources', 'app', 'extensions'); - if (!(await this.fs.directoryExists(extensionsPath))) { - // Might be on mac or linux. try a different path - currentPath = path.resolve(currentPath, '../../../..'); - extensionsPath = path.join(currentPath, 'resources', 'app', 'extensions'); - } - - // Search through all of the files in this folder - let results = await this.findMatchingLanguages(language, extensionsPath); - - // If that didn't work, see if it's our MagicPython predefined tmLanguage - if (!results && language === PYTHON_LANGUAGE) { - results = await this.fs.readFile(path.join(EXTENSION_ROOT_DIR, 'resources', 'MagicPython.tmLanguage.json')); - } - - return results; - } - - private async findMatchingLanguages(language: string, rootPath: string): Promise { - // Environment variable to mimic missing json problem - if (process.env.VSC_PYTHON_MIMIC_REMOTE) { - return undefined; - } - - // Search through all package.json files in the directory and below, looking - // for the themeName in them. - const foundPackages = await this.fs.search('**/package.json', rootPath); - if (foundPackages.length > 0) { - // For each one, open it up and look for the theme name. - for (const f of foundPackages) { - const fpath = path.join(rootPath, f); - const data = await this.findMatchingLanguageFromJson(fpath, language); - if (data) { - return data; - } - } - } - } - - private async findMatchingTheme(themeName: string): Promise { - // Environment variable to mimic missing json problem - if (process.env.VSC_PYTHON_MIMIC_REMOTE) { - return undefined; - } - - // Look through all extensions to find the theme. This will search - // the default extensions folder and our installed extensions. - const extensions = this.extensions.all; - for (const e of extensions) { - const result = await this.findMatchingThemeFromJson(path.join(e.extensionPath, 'package.json'), themeName); - if (result) { - return result; - } - } - - // If didn't find in the extensions folder, then try searching manually. This shouldn't happen, but - // this is our backup plan in case vscode changes stuff. - const currentExe = this.currentProcess.execPath; - let currentPath = path.dirname(currentExe); - - // Should be somewhere under currentPath/resources/app/extensions inside of a json file - let extensionsPath = path.join(currentPath, 'resources', 'app', 'extensions'); - if (!(await this.fs.directoryExists(extensionsPath))) { - // Might be on mac or linux. try a different path - currentPath = path.resolve(currentPath, '../../../..'); - extensionsPath = path.join(currentPath, 'resources', 'app', 'extensions'); - } - const other = await this.findMatchingThemes(extensionsPath, themeName); - if (other) { - return other; - } - } - - private async findMatchingThemes(rootPath: string, themeName: string): Promise { - // Search through all package.json files in the directory and below, looking - // for the themeName in them. - const foundPackages = await this.fs.search('**/package.json', rootPath); - if (foundPackages.length > 0) { - // For each one, open it up and look for the theme name. - for (const f of foundPackages) { - const fpath = path.join(rootPath, f); - const data = await this.findMatchingThemeFromJson(fpath, themeName); - if (data) { - return data; - } - } - } - } - - private async findMatchingLanguageFromJson(packageJson: string, language: string): Promise { - // Read the contents of the json file - const text = await this.fs.readFile(packageJson); - const json = JSON.parse(text); - - // Should have a name entry and a contributes entry - if (json.hasOwnProperty('name') && json.hasOwnProperty('contributes')) { - // See if contributes has a grammars - const contributes = json.contributes; - if (contributes.hasOwnProperty('grammars')) { - const grammars = contributes.grammars as any[]; - // Go through each theme, seeing if the label matches our theme name - for (const t of grammars) { - if (t.hasOwnProperty('language') && t.language === language) { - // Path is relative to the package.json file. - const rootFile = t.hasOwnProperty('path') - ? path.join(path.dirname(packageJson), t.path.toString()) - : ''; - return this.fs.readFile(rootFile); - } - } - } - } - } - - private async findMatchingThemeFromJson(packageJson: string, themeName: string): Promise { - // Read the contents of the json file - const text = await this.fs.readFile(packageJson); - const json = JSON.parse(text); - - // Should have a name entry and a contributes entry - if (json.hasOwnProperty('name') && json.hasOwnProperty('contributes')) { - // See if contributes has a theme - const contributes = json.contributes; - if (contributes.hasOwnProperty('themes')) { - const themes = contributes.themes as any[]; - // Go through each theme, seeing if the label matches our theme name - for (const t of themes) { - if ( - (t.hasOwnProperty('label') && t.label === themeName) || - (t.hasOwnProperty('id') && t.id === themeName) - ) { - const isDark = t.hasOwnProperty('uiTheme') && t.uiTheme === 'vs-dark'; - // Path is relative to the package.json file. - const rootFile = t.hasOwnProperty('path') - ? path.join(path.dirname(packageJson), t.path.toString()) - : ''; - - return { isDark, rootFile }; - } - } - } - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { inject, injectable } from 'inversify'; +import * as path from 'path'; + +import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../common/constants'; +import { traceError } from '../common/logger'; +import { IFileSystem } from '../common/platform/types'; +import { ICurrentProcess, IExtensions } from '../common/types'; +import { IThemeFinder } from './types'; + +// tslint:disable:no-any + +interface IThemeData { + rootFile: string; + isDark: boolean; +} + +@injectable() +export class ThemeFinder implements IThemeFinder { + private themeCache: { [key: string]: IThemeData | undefined } = {}; + private languageCache: { [key: string]: string | undefined } = {}; + + constructor( + @inject(IExtensions) private extensions: IExtensions, + @inject(ICurrentProcess) private currentProcess: ICurrentProcess, + @inject(IFileSystem) private fs: IFileSystem + ) {} + + public async findThemeRootJson(themeName: string): Promise { + // find our data + const themeData = await this.findThemeData(themeName); + + // Use that data if it worked + if (themeData) { + return themeData.rootFile; + } + } + + public async findTmLanguage(language: string): Promise { + // See if already found it or not + if (!this.themeCache.hasOwnProperty(language)) { + try { + this.languageCache[language] = await this.findMatchingLanguage(language); + } catch (exc) { + traceError(exc); + } + } + return this.languageCache[language]; + } + + public async isThemeDark(themeName: string): Promise { + // find our data + const themeData = await this.findThemeData(themeName); + + // Use that data if it worked + if (themeData) { + return themeData.isDark; + } + } + + private async findThemeData(themeName: string): Promise { + // See if already found it or not + if (!this.themeCache.hasOwnProperty(themeName)) { + try { + this.themeCache[themeName] = await this.findMatchingTheme(themeName); + } catch (exc) { + traceError(exc); + } + } + return this.themeCache[themeName]; + } + + private async findMatchingLanguage(language: string): Promise { + const currentExe = this.currentProcess.execPath; + let currentPath = path.dirname(currentExe); + + // Should be somewhere under currentPath/resources/app/extensions inside of a json file + let extensionsPath = path.join(currentPath, 'resources', 'app', 'extensions'); + if (!(await this.fs.directoryExists(extensionsPath))) { + // Might be on mac or linux. try a different path + currentPath = path.resolve(currentPath, '../../../..'); + extensionsPath = path.join(currentPath, 'resources', 'app', 'extensions'); + } + + // Search through all of the files in this folder + let results = await this.findMatchingLanguages(language, extensionsPath); + + // If that didn't work, see if it's our MagicPython predefined tmLanguage + if (!results && language === PYTHON_LANGUAGE) { + results = await this.fs.readFile(path.join(EXTENSION_ROOT_DIR, 'resources', 'MagicPython.tmLanguage.json')); + } + + return results; + } + + private async findMatchingLanguages(language: string, rootPath: string): Promise { + // Environment variable to mimic missing json problem + if (process.env.VSC_PYTHON_MIMIC_REMOTE) { + return undefined; + } + + // Search through all package.json files in the directory and below, looking + // for the themeName in them. + const foundPackages = await this.fs.search('**/package.json', rootPath); + if (foundPackages.length > 0) { + // For each one, open it up and look for the theme name. + for (const f of foundPackages) { + const fpath = path.join(rootPath, f); + const data = await this.findMatchingLanguageFromJson(fpath, language); + if (data) { + return data; + } + } + } + } + + private async findMatchingTheme(themeName: string): Promise { + // Environment variable to mimic missing json problem + if (process.env.VSC_PYTHON_MIMIC_REMOTE) { + return undefined; + } + + // Look through all extensions to find the theme. This will search + // the default extensions folder and our installed extensions. + const extensions = this.extensions.all; + for (const e of extensions) { + const result = await this.findMatchingThemeFromJson(path.join(e.extensionPath, 'package.json'), themeName); + if (result) { + return result; + } + } + + // If didn't find in the extensions folder, then try searching manually. This shouldn't happen, but + // this is our backup plan in case vscode changes stuff. + const currentExe = this.currentProcess.execPath; + let currentPath = path.dirname(currentExe); + + // Should be somewhere under currentPath/resources/app/extensions inside of a json file + let extensionsPath = path.join(currentPath, 'resources', 'app', 'extensions'); + if (!(await this.fs.directoryExists(extensionsPath))) { + // Might be on mac or linux. try a different path + currentPath = path.resolve(currentPath, '../../../..'); + extensionsPath = path.join(currentPath, 'resources', 'app', 'extensions'); + } + const other = await this.findMatchingThemes(extensionsPath, themeName); + if (other) { + return other; + } + } + + private async findMatchingThemes(rootPath: string, themeName: string): Promise { + // Search through all package.json files in the directory and below, looking + // for the themeName in them. + const foundPackages = await this.fs.search('**/package.json', rootPath); + if (foundPackages.length > 0) { + // For each one, open it up and look for the theme name. + for (const f of foundPackages) { + const fpath = path.join(rootPath, f); + const data = await this.findMatchingThemeFromJson(fpath, themeName); + if (data) { + return data; + } + } + } + } + + private async findMatchingLanguageFromJson(packageJson: string, language: string): Promise { + // Read the contents of the json file + const text = await this.fs.readFile(packageJson); + const json = JSON.parse(text); + + // Should have a name entry and a contributes entry + if (json.hasOwnProperty('name') && json.hasOwnProperty('contributes')) { + // See if contributes has a grammars + const contributes = json.contributes; + if (contributes.hasOwnProperty('grammars')) { + const grammars = contributes.grammars as any[]; + // Go through each theme, seeing if the label matches our theme name + for (const t of grammars) { + if (t.hasOwnProperty('language') && t.language === language) { + // Path is relative to the package.json file. + const rootFile = t.hasOwnProperty('path') + ? path.join(path.dirname(packageJson), t.path.toString()) + : ''; + return this.fs.readFile(rootFile); + } + } + } + } + } + + private async findMatchingThemeFromJson(packageJson: string, themeName: string): Promise { + // Read the contents of the json file + const text = await this.fs.readFile(packageJson); + const json = JSON.parse(text); + + // Should have a name entry and a contributes entry + if (json.hasOwnProperty('name') && json.hasOwnProperty('contributes')) { + // See if contributes has a theme + const contributes = json.contributes; + if (contributes.hasOwnProperty('themes')) { + const themes = contributes.themes as any[]; + // Go through each theme, seeing if the label matches our theme name + for (const t of themes) { + if ( + (t.hasOwnProperty('label') && t.label === themeName) || + (t.hasOwnProperty('id') && t.id === themeName) + ) { + const isDark = t.hasOwnProperty('uiTheme') && t.uiTheme === 'vs-dark'; + // Path is relative to the package.json file. + const rootFile = t.hasOwnProperty('path') + ? path.join(path.dirname(packageJson), t.path.toString()) + : ''; + + return { isDark, rootFile }; + } + } + } + } + } +} diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index b02e3b5b1fd4..5c8fce9123e0 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -1,932 +1,932 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { nbformat } from '@jupyterlab/coreutils'; -import { Session } from '@jupyterlab/services'; -import { Kernel, KernelMessage } from '@jupyterlab/services/lib/kernel'; -import { JSONObject } from '@phosphor/coreutils'; -import { Observable } from 'rxjs/Observable'; -import { - CancellationToken, - CodeLens, - CodeLensProvider, - DebugSession, - Disposable, - Event, - Range, - TextDocument, - TextEditor, - Uri, - WebviewPanel -} from 'vscode'; -import { ServerStatus } from '../../datascience-ui/interactive-common/mainState'; -import { ICommandManager } from '../common/application/types'; -import { ExecutionResult, ObservableExecutionResult, SpawnOptions } from '../common/process/types'; -import { IAsyncDisposable, IDataScienceSettings, IDisposable, Resource } from '../common/types'; -import { StopWatch } from '../common/utils/stopWatch'; -import { PythonInterpreter } from '../interpreter/contracts'; -import { JupyterCommands } from './constants'; -import { NotebookModelChange } from './interactive-common/interactiveWindowTypes'; -import { JupyterServerInfo } from './jupyter/jupyterConnection'; -import { JupyterInstallError } from './jupyter/jupyterInstallError'; -import { JupyterKernelSpec } from './jupyter/kernels/jupyterKernelSpec'; -import { LiveKernelModel } from './jupyter/kernels/types'; - -// tslint:disable-next-line:no-any -export type PromiseFunction = (...any: any[]) => Promise; - -// Main interface -export const IDataScience = Symbol('IDataScience'); -export interface IDataScience extends Disposable { - activationStartTime: number; - activate(): Promise; -} - -export const IDataScienceCommandListener = Symbol('IDataScienceCommandListener'); -export interface IDataScienceCommandListener { - register(commandManager: ICommandManager): void; -} - -// Connection information for talking to a jupyter notebook process -export interface IConnection extends Disposable { - readonly baseUrl: string; - readonly token: string; - readonly hostName: string; - readonly localLaunch: boolean; - localProcExitCode: number | undefined; - disconnected: Event; - allowUnauthorized?: boolean; -} - -export enum InterruptResult { - Success = 0, - TimedOut = 1, - Restarted = 2 -} - -// Information used to launch a notebook server -export interface INotebookServerLaunchInfo { - connectionInfo: IConnection; - /** - * The python interpreter associated with the kernel. - * - * @type {(PythonInterpreter | undefined)} - * @memberof INotebookServerLaunchInfo - */ - interpreter: PythonInterpreter | undefined; - uri: string | undefined; // Different from the connectionInfo as this is the setting used, not the result - kernelSpec: IJupyterKernelSpec | undefined | LiveKernelModel; - workingDir: string | undefined; - purpose: string | undefined; // Purpose this server is for -} - -export interface INotebookCompletion { - matches: ReadonlyArray; - cursor: { - start: number; - end: number; - }; - metadata: {}; -} - -// Talks to a jupyter ipython kernel to retrieve data for cells -export const INotebookServer = Symbol('INotebookServer'); -export interface INotebookServer extends IAsyncDisposable { - readonly id: string; - createNotebook( - resource: Resource, - identity: Uri, - notebookMetadata?: nbformat.INotebookMetadata, - cancelToken?: CancellationToken - ): Promise; - getNotebook(identity: Uri): Promise; - connect(launchInfo: INotebookServerLaunchInfo, cancelToken?: CancellationToken): Promise; - getConnectionInfo(): IConnection | undefined; - waitForConnect(): Promise; - shutdown(): Promise; -} - -export interface INotebook extends IAsyncDisposable { - readonly resource: Resource; - readonly identity: Uri; - readonly server: INotebookServer; - readonly status: ServerStatus; - onSessionStatusChanged: Event; - onDisposed: Event; - onKernelChanged: Event; - onKernelRestarted: Event; - clear(id: string): void; - executeObservable(code: string, file: string, line: number, id: string, silent: boolean): Observable; - execute( - code: string, - file: string, - line: number, - id: string, - cancelToken?: CancellationToken, - silent?: boolean - ): Promise; - inspect(code: string, cancelToken?: CancellationToken): Promise; - getCompletion( - cellCode: string, - offsetInCode: number, - cancelToken?: CancellationToken - ): Promise; - restartKernel(timeoutInMs: number): Promise; - waitForIdle(timeoutInMs: number): Promise; - interruptKernel(timeoutInMs: number): Promise; - setLaunchingFile(file: string): Promise; - getSysInfo(): Promise; - setMatplotLibStyle(useDark: boolean): Promise; - getMatchingInterpreter(): PythonInterpreter | undefined; - getKernelSpec(): IJupyterKernelSpec | LiveKernelModel | undefined; - setKernelSpec(spec: IJupyterKernelSpec | LiveKernelModel, timeoutMS: number): Promise; - setInterpreter(interpeter: PythonInterpreter): void; - getLoggers(): INotebookExecutionLogger[]; - registerIOPubListener(listener: (msg: KernelMessage.IIOPubMessage, requestId: string) => Promise): void; - registerCommTarget( - targetName: string, - callback: (comm: Kernel.IComm, msg: KernelMessage.ICommOpenMsg) => void | PromiseLike - ): void; - sendCommMessage( - buffers: (ArrayBuffer | ArrayBufferView)[], - content: { comm_id: string; data: JSONObject; target_name: string | undefined }, - // tslint:disable-next-line: no-any - metadata: any, - // tslint:disable-next-line: no-any - msgId: any - ): Kernel.IShellFuture< - KernelMessage.IShellMessage<'comm_msg'>, - KernelMessage.IShellMessage - >; - requestCommInfo(content: KernelMessage.ICommInfoRequestMsg['content']): Promise; - registerMessageHook( - msgId: string, - hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike - ): void; - removeMessageHook(msgId: string, hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike): void; -} - -export interface INotebookServerOptions { - uri?: string; - usingDarkTheme?: boolean; - skipUsingDefaultConfig?: boolean; - workingDir?: string; - purpose: string; - metadata?: nbformat.INotebookMetadata; - disableUI?: boolean; - skipSearchingForKernel?: boolean; - allowUI(): boolean; -} - -export const INotebookExecutionLogger = Symbol('INotebookExecutionLogger'); -export interface INotebookExecutionLogger { - preExecute(cell: ICell, silent: boolean): Promise; - postExecute(cell: ICell, silent: boolean): Promise; -} - -export const IGatherProvider = Symbol('IGatherProvider'); -export interface IGatherProvider { - enabled: boolean; - logExecution(vscCell: ICell): void; - gatherCode(vscCell: ICell): string; - resetLog(): void; -} - -export const IGatherLogger = Symbol('IGatherLogger'); -export interface IGatherLogger extends INotebookExecutionLogger { - getGatherProvider(): IGatherProvider; -} - -export const IJupyterExecution = Symbol('IJupyterExecution'); -export interface IJupyterExecution extends IAsyncDisposable { - sessionChanged: Event; - serverStarted: Event; - isNotebookSupported(cancelToken?: CancellationToken): Promise; - isImportSupported(cancelToken?: CancellationToken): Promise; - isSpawnSupported(cancelToken?: CancellationToken): Promise; - connectToNotebookServer( - options?: INotebookServerOptions, - cancelToken?: CancellationToken - ): Promise; - spawnNotebook(file: string): Promise; - importNotebook(file: string, template: string | undefined): Promise; - getUsableJupyterPython(cancelToken?: CancellationToken): Promise; - getServer(options?: INotebookServerOptions): Promise; - getNotebookError(): Promise; - refreshCommands(): Promise; -} - -export const IJupyterDebugger = Symbol('IJupyterDebugger'); -export interface IJupyterDebugger { - startDebugging(notebook: INotebook): Promise; - stopDebugging(notebook: INotebook): Promise; - onRestart(notebook: INotebook): void; -} - -export interface IJupyterPasswordConnectInfo { - emptyPassword: boolean; - xsrfCookie: string; - sessionCookieName: string; - sessionCookieValue: string; -} - -export const IJupyterPasswordConnect = Symbol('IJupyterPasswordConnect'); -export interface IJupyterPasswordConnect { - getPasswordConnectionInfo( - url: string, - allowUnauthorized: boolean - ): Promise; -} - -export const IJupyterSession = Symbol('IJupyterSession'); -export interface IJupyterSession extends IAsyncDisposable { - onSessionStatusChanged: Event; - readonly status: ServerStatus; - restart(timeout: number): Promise; - interrupt(timeout: number): Promise; - waitForIdle(timeout: number): Promise; - requestExecute( - content: KernelMessage.IExecuteRequestMsg['content'], - disposeOnDone?: boolean, - metadata?: JSONObject - ): Kernel.IShellFuture | undefined; - requestComplete( - content: KernelMessage.ICompleteRequestMsg['content'] - ): Promise; - requestInspect( - content: KernelMessage.IInspectRequestMsg['content'] - ): Promise; - sendInputReply(content: string): void; - changeKernel(kernel: IJupyterKernelSpec | LiveKernelModel, timeoutMS: number): Promise; - registerCommTarget( - targetName: string, - callback: (comm: Kernel.IComm, msg: KernelMessage.ICommOpenMsg) => void | PromiseLike - ): void; - sendCommMessage( - buffers: (ArrayBuffer | ArrayBufferView)[], - content: { comm_id: string; data: JSONObject; target_name: string | undefined }, - // tslint:disable-next-line: no-any - metadata: any, - // tslint:disable-next-line: no-any - msgId: any - ): Kernel.IShellFuture< - KernelMessage.IShellMessage<'comm_msg'>, - KernelMessage.IShellMessage - >; - requestCommInfo(content: KernelMessage.ICommInfoRequestMsg['content']): Promise; - registerMessageHook( - msgId: string, - hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike - ): void; - removeMessageHook(msgId: string, hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike): void; -} - -export const IJupyterSessionManagerFactory = Symbol('IJupyterSessionManagerFactory'); -export interface IJupyterSessionManagerFactory { - create(connInfo: IConnection, failOnPassword?: boolean): Promise; -} - -export interface IJupyterSessionManager extends IAsyncDisposable { - startNew( - kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, - cancelToken?: CancellationToken - ): Promise; - getKernelSpecs(): Promise; - getConnInfo(): IConnection; - getRunningKernels(): Promise; - getRunningSessions(): Promise; -} - -export interface IJupyterKernel { - /** - * Id of an existing (active) Kernel from an active session. - * - * @type {string} - * @memberof IJupyterKernel - */ - id?: string; - name: string; - lastActivityTime: Date; - numberOfConnections: number; -} - -export interface IJupyterKernelSpec { - /** - * Id of an existing (active) Kernel from an active session. - * - * @type {string} - * @memberof IJupyterKernel - */ - id?: string; - name: string; - language: string; - path: string; - /** - * Kernel display name. - * - * @type {string} - * @memberof IJupyterKernelSpec - */ - readonly display_name: string; - /** - * A dictionary of additional attributes about this kernel; used by clients to aid in kernel selection. - * Optionally storing the interpreter information in the metadata (helping extension search for kernels that match an interpereter). - */ - // tslint:disable-next-line: no-any - readonly metadata?: Record & { interpreter?: Partial }; - readonly argv: string[]; -} - -export const INotebookImporter = Symbol('INotebookImporter'); -export interface INotebookImporter extends Disposable { - importFromFile(contentsFile: string, originalFile?: string): Promise; // originalFile is the base file if file is a temp file / location - importCellsFromFile(file: string): Promise; - importCells(json: string): Promise; -} - -export const INotebookExporter = Symbol('INotebookExporter'); -export interface INotebookExporter extends Disposable { - translateToNotebook(cells: ICell[], directoryChange?: string): Promise; - exportToFile(cells: ICell[], file: string): Promise; -} - -export const IInteractiveWindowProvider = Symbol('IInteractiveWindowProvider'); -export interface IInteractiveWindowProvider { - readonly onDidChangeActiveInteractiveWindow: Event; - onExecutedCode: Event; - getActive(): IInteractiveWindow | undefined; - getOrCreateActive(): Promise; -} - -export const IDataScienceErrorHandler = Symbol('IDataScienceErrorHandler'); -export interface IDataScienceErrorHandler { - handleError(err: Error): Promise; -} - -export interface IInteractiveBase extends Disposable { - onExecutedCode: Event; - notebook?: INotebook; - show(): Promise; - startProgress(): void; - stopProgress(): void; - undoCells(): void; - redoCells(): void; - removeAllCells(): void; - interruptKernel(): Promise; - restartKernel(): Promise; -} - -export const IInteractiveWindow = Symbol('IInteractiveWindow'); -export interface IInteractiveWindow extends IInteractiveBase { - readonly onDidChangeViewState: Event; - readonly visible: boolean; - readonly active: boolean; - closed: Event; - addCode( - code: string, - file: string, - line: number, - editor?: TextEditor, - runningStopWatch?: StopWatch - ): Promise; - addMessage(message: string): Promise; - debugCode( - code: string, - file: string, - line: number, - editor?: TextEditor, - runningStopWatch?: StopWatch - ): Promise; - expandAllCells(): void; - collapseAllCells(): void; - exportCells(): void; - scrollToCell(id: string): void; -} - -// For native editing, the provider acts like the IDocumentManager for normal docs -export const INotebookEditorProvider = Symbol('INotebookEditorProvider'); -export interface INotebookEditorProvider { - readonly activeEditor: INotebookEditor | undefined; - readonly editors: INotebookEditor[]; - readonly onDidOpenNotebookEditor: Event; - readonly onDidChangeActiveNotebookEditor: Event; - readonly onDidCloseNotebookEditor: Event; - open(file: Uri): Promise; - show(file: Uri): Promise; - createNew(contents?: string): Promise; -} - -// For native editing, the INotebookEditor acts like a TextEditor and a TextDocument together -export const INotebookEditor = Symbol('INotebookEditor'); -export interface INotebookEditor extends IInteractiveBase { - readonly onDidChangeViewState: Event; - readonly closed: Event; - readonly executed: Event; - readonly modified: Event; - readonly saved: Event; - /** - * Is this notebook representing an untitled file which has never been saved yet. - */ - readonly isUntitled: boolean; - /** - * `true` if there are unpersisted changes. - */ - readonly isDirty: boolean; - readonly file: Uri; - readonly visible: boolean; - readonly active: boolean; - readonly model: INotebookModel | undefined; - load(storage: INotebookModel, webViewPanel?: WebviewPanel): Promise; - runAllCells(): void; - runSelectedCell(): void; - addCellBelow(): void; -} - -export const IInteractiveWindowListener = Symbol('IInteractiveWindowListener'); - -/** - * Listens to history messages to provide extra functionality - */ -export interface IInteractiveWindowListener extends IDisposable { - /** - * Fires this event when posting a response message - */ - // tslint:disable-next-line: no-any - postMessage: Event<{ message: string; payload: any }>; - /** - * Handles messages that the interactive window receives - * @param message message type - * @param payload message payload - */ - // tslint:disable-next-line: no-any - onMessage(message: string, payload?: any): void; -} - -// Wraps the vscode API in order to send messages back and forth from a webview -export const IPostOffice = Symbol('IPostOffice'); -export interface IPostOffice { - // tslint:disable-next-line:no-any - post(message: string, params: any[] | undefined): void; - // tslint:disable-next-line:no-any - listen(message: string, listener: (args: any[] | undefined) => void): void; -} - -// Wraps the vscode CodeLensProvider base class -export const IDataScienceCodeLensProvider = Symbol('IDataScienceCodeLensProvider'); -export interface IDataScienceCodeLensProvider extends CodeLensProvider { - getCodeWatcher(document: TextDocument): ICodeWatcher | undefined; -} - -// Wraps the Code Watcher API -export const ICodeWatcher = Symbol('ICodeWatcher'); -export interface ICodeWatcher { - codeLensUpdated: Event; - setDocument(document: TextDocument): void; - getFileName(): string; - getVersion(): number; - getCodeLenses(): CodeLens[]; - getCachedSettings(): IDataScienceSettings | undefined; - runAllCells(): Promise; - runCell(range: Range): Promise; - debugCell(range: Range): Promise; - runCurrentCell(): Promise; - runCurrentCellAndAdvance(): Promise; - runSelectionOrLine(activeEditor: TextEditor | undefined): Promise; - runToLine(targetLine: number): Promise; - runFromLine(targetLine: number): Promise; - runAllCellsAbove(stopLine: number, stopCharacter: number): Promise; - runCellAndAllBelow(startLine: number, startCharacter: number): Promise; - runFileInteractive(): Promise; - debugFileInteractive(): Promise; - addEmptyCellToBottom(): Promise; - runCurrentCellAndAddBelow(): Promise; - debugCurrentCell(): Promise; -} - -export const ICodeLensFactory = Symbol('ICodeLensFactory'); -export interface ICodeLensFactory { - updateRequired: Event; - createCodeLenses(document: TextDocument): CodeLens[]; -} - -export enum CellState { - editing = -1, - init = 0, - executing = 1, - finished = 2, - error = 3 -} - -// Basic structure for a cell from a notebook -export interface ICell { - id: string; // This value isn't unique. File and line are needed too. - file: string; - line: number; - state: CellState; - data: nbformat.ICodeCell | nbformat.IRawCell | nbformat.IMarkdownCell | IMessageCell; - extraLines?: number[]; -} - -export interface IInteractiveWindowInfo { - cellCount: number; - undoCount: number; - redoCount: number; - selectedCell: string | undefined; -} - -export interface IMessageCell extends nbformat.IBaseCell { - cell_type: 'messages'; - messages: string[]; -} - -export const ICodeCssGenerator = Symbol('ICodeCssGenerator'); -export interface ICodeCssGenerator { - generateThemeCss(resource: Resource, isDark: boolean, theme: string): Promise; - generateMonacoTheme(resource: Resource, isDark: boolean, theme: string): Promise; -} - -export const IThemeFinder = Symbol('IThemeFinder'); -export interface IThemeFinder { - findThemeRootJson(themeName: string): Promise; - findTmLanguage(language: string): Promise; - isThemeDark(themeName: string): Promise; -} - -export const IStatusProvider = Symbol('IStatusProvider'); -export interface IStatusProvider { - // call this function to set the new status on the active - // interactive window. Dispose of the returned object when done. - set( - message: string, - showInWebView: boolean, - timeout?: number, - canceled?: () => void, - interactivePanel?: IInteractiveBase - ): Disposable; - - // call this function to wait for a promise while displaying status - waitWithStatus( - promise: () => Promise, - message: string, - showInWebView: boolean, - timeout?: number, - canceled?: () => void, - interactivePanel?: IInteractiveBase - ): Promise; -} - -export interface IJupyterCommand { - interpreter(): Promise; - execObservable(args: string[], options: SpawnOptions): Promise>; - exec(args: string[], options: SpawnOptions): Promise>; -} - -export const IJupyterCommandFactory = Symbol('IJupyterCommandFactory'); -export interface IJupyterCommandFactory { - createInterpreterCommand( - command: JupyterCommands, - moduleName: string, - args: string[], - interpreter: PythonInterpreter, - isActiveInterpreter: boolean - ): IJupyterCommand; - createProcessCommand(exe: string, args: string[]): IJupyterCommand; -} - -// Config settings we pass to our react code -export type FileSettings = { - autoSaveDelay: number; - autoSave: 'afterDelay' | 'off' | 'onFocusChange' | 'onWindowChange'; -}; - -export interface IDataScienceExtraSettings extends IDataScienceSettings { - extraSettings: { - editor: { - cursor: string; - cursorBlink: string; - fontLigatures: boolean; - autoClosingBrackets: string; - autoClosingQuotes: string; - autoSurround: string; - autoIndent: boolean; - scrollBeyondLastLine: boolean; - horizontalScrollbarSize: number; - verticalScrollbarSize: number; - fontSize: number; - fontFamily: string; - }; - theme: string; - }; - intellisenseOptions: { - quickSuggestions: { - other: boolean; - comments: boolean; - strings: boolean; - }; - acceptSuggestionOnEnter: boolean | 'on' | 'smart' | 'off'; - quickSuggestionsDelay: number; - suggestOnTriggerCharacters: boolean; - tabCompletion: boolean | 'on' | 'off' | 'onlySnippets'; - suggestLocalityBonus: boolean; - suggestSelection: 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix'; - wordBasedSuggestions: boolean; - parameterHintsEnabled: boolean; - }; -} - -// Get variables from the currently running active Jupyter server -// Note: This definition is used implicitly by getJupyterVariableValue.py file -// Changes here may need to be reflected there as well -export interface IJupyterVariable { - name: string; - value: string | undefined; - executionCount?: number; - supportsDataExplorer: boolean; - type: string; - size: number; - shape: string; - count: number; - truncated: boolean; - columns?: { key: string; type: string }[]; - rowCount?: number; - indexColumn?: string; -} - -export const IJupyterVariables = Symbol('IJupyterVariables'); -export interface IJupyterVariables { - getVariables(notebook: INotebook, request: IJupyterVariablesRequest): Promise; - getDataFrameInfo(targetVariable: IJupyterVariable, notebook: INotebook): Promise; - getDataFrameRows( - targetVariable: IJupyterVariable, - notebook: INotebook, - start: number, - end: number - ): Promise; -} - -// Request for variables -export interface IJupyterVariablesRequest { - executionCount: number; - sortColumn: string; - sortAscending: boolean; - startIndex: number; - pageSize: number; -} - -// Response to a request -export interface IJupyterVariablesResponse { - executionCount: number; - totalCount: number; - pageStartIndex: number; - pageResponse: IJupyterVariable[]; -} - -export const IDataViewerProvider = Symbol('IDataViewerProvider'); -export interface IDataViewerProvider { - create(variable: IJupyterVariable, notebook: INotebook): Promise; -} -export const IDataViewer = Symbol('IDataViewer'); - -export interface IDataViewer extends IDisposable { - showVariable(variable: IJupyterVariable, notebook: INotebook): Promise; -} - -export const IPlotViewerProvider = Symbol('IPlotViewerProvider'); -export interface IPlotViewerProvider { - showPlot(imageHtml: string): Promise; -} -export const IPlotViewer = Symbol('IPlotViewer'); - -export interface IPlotViewer extends IDisposable { - closed: Event; - removed: Event; - addPlot(imageHtml: string): Promise; - show(): Promise; -} - -export interface ISourceMapMapping { - line: number; - endLine: number; - runtimeSource: { path: string }; - runtimeLine: number; -} - -export interface ISourceMapRequest { - source: { path: string }; - pydevdSourceMaps: ISourceMapMapping[]; -} - -export interface ICellHash { - line: number; // 1 based - endLine: number; // 1 based and inclusive - runtimeLine: number; // Line in the jupyter source to start at - hash: string; - executionCount: number; - id: string; // Cell id as sent to jupyter -} - -export interface IFileHashes { - file: string; - hashes: ICellHash[]; -} - -export const ICellHashListener = Symbol('ICellHashListener'); -export interface ICellHashListener { - hashesUpdated(hashes: IFileHashes[]): Promise; -} - -export const ICellHashProvider = Symbol('ICellHashProvider'); -export interface ICellHashProvider { - updated: Event; - getHashes(): IFileHashes[]; - getExecutionCount(): number; - incExecutionCount(): void; -} - -export const ICellHashLogger = Symbol('ICellHashLogger'); -export interface ICellHashLogger extends INotebookExecutionLogger { - getCellHashProvider(): ICellHashProvider; -} - -export interface IDebugLocation { - fileName: string; - lineNumber: number; - column: number; -} - -export const IDebugLocationTracker = Symbol('IDebugLocationTracker'); -export interface IDebugLocationTracker { - updated: Event; - getLocation(debugSession: DebugSession): IDebugLocation | undefined; -} - -export const IJupyterSubCommandExecutionService = Symbol('IJupyterSubCommandExecutionService'); -/** - * Responsible for execution of jupyter subcommands such as `notebook`, `nbconvert`, etc. - * The executed code is as follows `python -m jupyter `. - * - * @export - * @interface IJupyterSubCommandExecutionService - */ -export interface IJupyterSubCommandExecutionService { - /** - * Checks whether notebook is supported. - * - * @param {CancellationToken} [cancelToken] - * @returns {Promise} - * @memberof IJupyterSubCommandExecutionService - */ - isNotebookSupported(cancelToken?: CancellationToken): Promise; - /** - * Checks whether exporting of ipynb is supported. - * - * @param {CancellationToken} [cancelToken] - * @returns {Promise} - * @memberof IJupyterSubCommandExecutionService - */ - isExportSupported(cancelToken?: CancellationToken): Promise; - /** - * Error message indicating why jupyter notebook isn't supported. - * - * @returns {Promise} - * @memberof IJupyterSubCommandExecutionService - */ - getReasonForJupyterNotebookNotBeingSupported(): Promise; - /** - * Used to refresh the command finder. - * - * @returns {Promise} - * @memberof IJupyterSubCommandExecutionService - */ - refreshCommands(): Promise; - /** - * Gets the interpreter to be used for starting of jupyter server. - * - * @param {CancellationToken} [token] - * @returns {(Promise)} - * @memberof IJupyterInterpreterService - */ - getSelectedInterpreter(token?: CancellationToken): Promise; - /** - * Starts the jupyter notebook server - * - * @param {string[]} notebookArgs - * @param {SpawnOptions} options - * @returns {Promise>} - * @memberof IJupyterSubCommandExecutionService - */ - startNotebook(notebookArgs: string[], options: SpawnOptions): Promise>; - /** - * Gets a list of all locally running jupyter notebook servers. - * - * @param {CancellationToken} [token] - * @returns {(Promise)} - * @memberof IJupyterSubCommandExecutionService - */ - getRunningJupyterServers(token?: CancellationToken): Promise; - /** - * Exports a given notebook into a python file. - * - * @param {string} file - * @param {string} [template] - * @param {CancellationToken} [token] - * @returns {Promise} - * @memberof IJupyterSubCommandExecutionService - */ - exportNotebookToPython(file: string, template?: string, token?: CancellationToken): Promise; - /** - * Opens an ipynb file in a new instance of a jupyter notebook server. - * - * @param {string} notebookFile - * @returns {Promise} - * @memberof IJupyterSubCommandExecutionService - */ - openNotebook(notebookFile: string): Promise; - /** - * Gets the kernelspecs. - * - * @param {CancellationToken} [token] - * @returns {Promise} - * @memberof IJupyterSubCommandExecutionService - */ - getKernelSpecs(token?: CancellationToken): Promise; -} - -export const IJupyterInterpreterDependencyManager = Symbol('IJupyterInterpreterDependencyManager'); -export interface IJupyterInterpreterDependencyManager { - /** - * Installs the dependencies required to launch jupyter. - * - * @param {JupyterInstallError} [err] - * @returns {Promise} - * @memberof IJupyterInterpreterDependencyManager - */ - installMissingDependencies(err?: JupyterInstallError): Promise; -} - -export interface INotebookModel { - readonly file: Uri; - readonly isDirty: boolean; - readonly isUntitled: boolean; - readonly changed: Event; - readonly cells: ICell[]; - getJson(): Promise>; - getContent(cells?: ICell[]): Promise; - update(change: NotebookModelChange): void; -} - -export const INotebookStorage = Symbol('INotebookStorage'); - -export interface INotebookStorage { - readonly onDidEdit: Event; - save(cancellation: CancellationToken): Thenable; - saveAs(targetResource: Uri): Thenable; - applyEdits(edits: readonly NotebookModelChange[]): Thenable; - undoEdits(edits: readonly NotebookModelChange[]): Thenable; - backup(cancellation: CancellationToken): Thenable; - load(file: Uri, contents?: string): Promise; -} -type WebViewViewState = { - readonly visible: boolean; - readonly active: boolean; -}; -export type WebViewViewChangeEventArgs = { current: WebViewViewState; previous: WebViewViewState }; - -export const INotebookProvider = Symbol('INotebookProvider'); - -export type GetServerOptions = { - getOnly?: boolean; - disableUI?: boolean; - localOnly?: boolean; -}; - -/** - * Options for getting a notebook - */ -export type GetNotebookOptions = { - identity: Uri; - getOnly?: boolean; - disableUI?: boolean; - metadata?: nbformat.INotebookMetadata; -}; - -export interface INotebookProvider { - /** - * Fired when a notebook has been created for a given Uri/Identity - */ - onNotebookCreated: Event<{ identity: Uri; notebook: INotebook }>; - - /** - * List of all notebooks (active and ones that are being constructed). - */ - activeNotebooks: Promise[]; - /** - * Gets or creates a notebook, and manages the lifetime of notebooks. - */ - getOrCreateNotebook(options: GetNotebookOptions): Promise; - - /** - * Gets the server used for starting notebooks - */ - getOrCreateServer(options: GetServerOptions): Promise; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { nbformat } from '@jupyterlab/coreutils'; +import { Session } from '@jupyterlab/services'; +import { Kernel, KernelMessage } from '@jupyterlab/services/lib/kernel'; +import { JSONObject } from '@phosphor/coreutils'; +import { Observable } from 'rxjs/Observable'; +import { + CancellationToken, + CodeLens, + CodeLensProvider, + DebugSession, + Disposable, + Event, + Range, + TextDocument, + TextEditor, + Uri, + WebviewPanel +} from 'vscode'; +import { ServerStatus } from '../../datascience-ui/interactive-common/mainState'; +import { ICommandManager } from '../common/application/types'; +import { ExecutionResult, ObservableExecutionResult, SpawnOptions } from '../common/process/types'; +import { IAsyncDisposable, IDataScienceSettings, IDisposable, Resource } from '../common/types'; +import { StopWatch } from '../common/utils/stopWatch'; +import { PythonInterpreter } from '../interpreter/contracts'; +import { JupyterCommands } from './constants'; +import { NotebookModelChange } from './interactive-common/interactiveWindowTypes'; +import { JupyterServerInfo } from './jupyter/jupyterConnection'; +import { JupyterInstallError } from './jupyter/jupyterInstallError'; +import { JupyterKernelSpec } from './jupyter/kernels/jupyterKernelSpec'; +import { LiveKernelModel } from './jupyter/kernels/types'; + +// tslint:disable-next-line:no-any +export type PromiseFunction = (...any: any[]) => Promise; + +// Main interface +export const IDataScience = Symbol('IDataScience'); +export interface IDataScience extends Disposable { + activationStartTime: number; + activate(): Promise; +} + +export const IDataScienceCommandListener = Symbol('IDataScienceCommandListener'); +export interface IDataScienceCommandListener { + register(commandManager: ICommandManager): void; +} + +// Connection information for talking to a jupyter notebook process +export interface IConnection extends Disposable { + readonly baseUrl: string; + readonly token: string; + readonly hostName: string; + readonly localLaunch: boolean; + localProcExitCode: number | undefined; + disconnected: Event; + allowUnauthorized?: boolean; +} + +export enum InterruptResult { + Success = 0, + TimedOut = 1, + Restarted = 2 +} + +// Information used to launch a notebook server +export interface INotebookServerLaunchInfo { + connectionInfo: IConnection; + /** + * The python interpreter associated with the kernel. + * + * @type {(PythonInterpreter | undefined)} + * @memberof INotebookServerLaunchInfo + */ + interpreter: PythonInterpreter | undefined; + uri: string | undefined; // Different from the connectionInfo as this is the setting used, not the result + kernelSpec: IJupyterKernelSpec | undefined | LiveKernelModel; + workingDir: string | undefined; + purpose: string | undefined; // Purpose this server is for +} + +export interface INotebookCompletion { + matches: ReadonlyArray; + cursor: { + start: number; + end: number; + }; + metadata: {}; +} + +// Talks to a jupyter ipython kernel to retrieve data for cells +export const INotebookServer = Symbol('INotebookServer'); +export interface INotebookServer extends IAsyncDisposable { + readonly id: string; + createNotebook( + resource: Resource, + identity: Uri, + notebookMetadata?: nbformat.INotebookMetadata, + cancelToken?: CancellationToken + ): Promise; + getNotebook(identity: Uri): Promise; + connect(launchInfo: INotebookServerLaunchInfo, cancelToken?: CancellationToken): Promise; + getConnectionInfo(): IConnection | undefined; + waitForConnect(): Promise; + shutdown(): Promise; +} + +export interface INotebook extends IAsyncDisposable { + readonly resource: Resource; + readonly identity: Uri; + readonly server: INotebookServer; + readonly status: ServerStatus; + onSessionStatusChanged: Event; + onDisposed: Event; + onKernelChanged: Event; + onKernelRestarted: Event; + clear(id: string): void; + executeObservable(code: string, file: string, line: number, id: string, silent: boolean): Observable; + execute( + code: string, + file: string, + line: number, + id: string, + cancelToken?: CancellationToken, + silent?: boolean + ): Promise; + inspect(code: string, cancelToken?: CancellationToken): Promise; + getCompletion( + cellCode: string, + offsetInCode: number, + cancelToken?: CancellationToken + ): Promise; + restartKernel(timeoutInMs: number): Promise; + waitForIdle(timeoutInMs: number): Promise; + interruptKernel(timeoutInMs: number): Promise; + setLaunchingFile(file: string): Promise; + getSysInfo(): Promise; + setMatplotLibStyle(useDark: boolean): Promise; + getMatchingInterpreter(): PythonInterpreter | undefined; + getKernelSpec(): IJupyterKernelSpec | LiveKernelModel | undefined; + setKernelSpec(spec: IJupyterKernelSpec | LiveKernelModel, timeoutMS: number): Promise; + setInterpreter(interpeter: PythonInterpreter): void; + getLoggers(): INotebookExecutionLogger[]; + registerIOPubListener(listener: (msg: KernelMessage.IIOPubMessage, requestId: string) => Promise): void; + registerCommTarget( + targetName: string, + callback: (comm: Kernel.IComm, msg: KernelMessage.ICommOpenMsg) => void | PromiseLike + ): void; + sendCommMessage( + buffers: (ArrayBuffer | ArrayBufferView)[], + content: { comm_id: string; data: JSONObject; target_name: string | undefined }, + // tslint:disable-next-line: no-any + metadata: any, + // tslint:disable-next-line: no-any + msgId: any + ): Kernel.IShellFuture< + KernelMessage.IShellMessage<'comm_msg'>, + KernelMessage.IShellMessage + >; + requestCommInfo(content: KernelMessage.ICommInfoRequestMsg['content']): Promise; + registerMessageHook( + msgId: string, + hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike + ): void; + removeMessageHook(msgId: string, hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike): void; +} + +export interface INotebookServerOptions { + uri?: string; + usingDarkTheme?: boolean; + skipUsingDefaultConfig?: boolean; + workingDir?: string; + purpose: string; + metadata?: nbformat.INotebookMetadata; + disableUI?: boolean; + skipSearchingForKernel?: boolean; + allowUI(): boolean; +} + +export const INotebookExecutionLogger = Symbol('INotebookExecutionLogger'); +export interface INotebookExecutionLogger { + preExecute(cell: ICell, silent: boolean): Promise; + postExecute(cell: ICell, silent: boolean): Promise; +} + +export const IGatherProvider = Symbol('IGatherProvider'); +export interface IGatherProvider { + enabled: boolean; + logExecution(vscCell: ICell): void; + gatherCode(vscCell: ICell): string; + resetLog(): void; +} + +export const IGatherLogger = Symbol('IGatherLogger'); +export interface IGatherLogger extends INotebookExecutionLogger { + getGatherProvider(): IGatherProvider; +} + +export const IJupyterExecution = Symbol('IJupyterExecution'); +export interface IJupyterExecution extends IAsyncDisposable { + sessionChanged: Event; + serverStarted: Event; + isNotebookSupported(cancelToken?: CancellationToken): Promise; + isImportSupported(cancelToken?: CancellationToken): Promise; + isSpawnSupported(cancelToken?: CancellationToken): Promise; + connectToNotebookServer( + options?: INotebookServerOptions, + cancelToken?: CancellationToken + ): Promise; + spawnNotebook(file: string): Promise; + importNotebook(file: string, template: string | undefined): Promise; + getUsableJupyterPython(cancelToken?: CancellationToken): Promise; + getServer(options?: INotebookServerOptions): Promise; + getNotebookError(): Promise; + refreshCommands(): Promise; +} + +export const IJupyterDebugger = Symbol('IJupyterDebugger'); +export interface IJupyterDebugger { + startDebugging(notebook: INotebook): Promise; + stopDebugging(notebook: INotebook): Promise; + onRestart(notebook: INotebook): void; +} + +export interface IJupyterPasswordConnectInfo { + emptyPassword: boolean; + xsrfCookie: string; + sessionCookieName: string; + sessionCookieValue: string; +} + +export const IJupyterPasswordConnect = Symbol('IJupyterPasswordConnect'); +export interface IJupyterPasswordConnect { + getPasswordConnectionInfo( + url: string, + allowUnauthorized: boolean + ): Promise; +} + +export const IJupyterSession = Symbol('IJupyterSession'); +export interface IJupyterSession extends IAsyncDisposable { + onSessionStatusChanged: Event; + readonly status: ServerStatus; + restart(timeout: number): Promise; + interrupt(timeout: number): Promise; + waitForIdle(timeout: number): Promise; + requestExecute( + content: KernelMessage.IExecuteRequestMsg['content'], + disposeOnDone?: boolean, + metadata?: JSONObject + ): Kernel.IShellFuture | undefined; + requestComplete( + content: KernelMessage.ICompleteRequestMsg['content'] + ): Promise; + requestInspect( + content: KernelMessage.IInspectRequestMsg['content'] + ): Promise; + sendInputReply(content: string): void; + changeKernel(kernel: IJupyterKernelSpec | LiveKernelModel, timeoutMS: number): Promise; + registerCommTarget( + targetName: string, + callback: (comm: Kernel.IComm, msg: KernelMessage.ICommOpenMsg) => void | PromiseLike + ): void; + sendCommMessage( + buffers: (ArrayBuffer | ArrayBufferView)[], + content: { comm_id: string; data: JSONObject; target_name: string | undefined }, + // tslint:disable-next-line: no-any + metadata: any, + // tslint:disable-next-line: no-any + msgId: any + ): Kernel.IShellFuture< + KernelMessage.IShellMessage<'comm_msg'>, + KernelMessage.IShellMessage + >; + requestCommInfo(content: KernelMessage.ICommInfoRequestMsg['content']): Promise; + registerMessageHook( + msgId: string, + hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike + ): void; + removeMessageHook(msgId: string, hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike): void; +} + +export const IJupyterSessionManagerFactory = Symbol('IJupyterSessionManagerFactory'); +export interface IJupyterSessionManagerFactory { + create(connInfo: IConnection, failOnPassword?: boolean): Promise; +} + +export interface IJupyterSessionManager extends IAsyncDisposable { + startNew( + kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined, + cancelToken?: CancellationToken + ): Promise; + getKernelSpecs(): Promise; + getConnInfo(): IConnection; + getRunningKernels(): Promise; + getRunningSessions(): Promise; +} + +export interface IJupyterKernel { + /** + * Id of an existing (active) Kernel from an active session. + * + * @type {string} + * @memberof IJupyterKernel + */ + id?: string; + name: string; + lastActivityTime: Date; + numberOfConnections: number; +} + +export interface IJupyterKernelSpec { + /** + * Id of an existing (active) Kernel from an active session. + * + * @type {string} + * @memberof IJupyterKernel + */ + id?: string; + name: string; + language: string; + path: string; + /** + * Kernel display name. + * + * @type {string} + * @memberof IJupyterKernelSpec + */ + readonly display_name: string; + /** + * A dictionary of additional attributes about this kernel; used by clients to aid in kernel selection. + * Optionally storing the interpreter information in the metadata (helping extension search for kernels that match an interpereter). + */ + // tslint:disable-next-line: no-any + readonly metadata?: Record & { interpreter?: Partial }; + readonly argv: string[]; +} + +export const INotebookImporter = Symbol('INotebookImporter'); +export interface INotebookImporter extends Disposable { + importFromFile(contentsFile: string, originalFile?: string): Promise; // originalFile is the base file if file is a temp file / location + importCellsFromFile(file: string): Promise; + importCells(json: string): Promise; +} + +export const INotebookExporter = Symbol('INotebookExporter'); +export interface INotebookExporter extends Disposable { + translateToNotebook(cells: ICell[], directoryChange?: string): Promise; + exportToFile(cells: ICell[], file: string): Promise; +} + +export const IInteractiveWindowProvider = Symbol('IInteractiveWindowProvider'); +export interface IInteractiveWindowProvider { + readonly onDidChangeActiveInteractiveWindow: Event; + onExecutedCode: Event; + getActive(): IInteractiveWindow | undefined; + getOrCreateActive(): Promise; +} + +export const IDataScienceErrorHandler = Symbol('IDataScienceErrorHandler'); +export interface IDataScienceErrorHandler { + handleError(err: Error): Promise; +} + +export interface IInteractiveBase extends Disposable { + onExecutedCode: Event; + notebook?: INotebook; + show(): Promise; + startProgress(): void; + stopProgress(): void; + undoCells(): void; + redoCells(): void; + removeAllCells(): void; + interruptKernel(): Promise; + restartKernel(): Promise; +} + +export const IInteractiveWindow = Symbol('IInteractiveWindow'); +export interface IInteractiveWindow extends IInteractiveBase { + readonly onDidChangeViewState: Event; + readonly visible: boolean; + readonly active: boolean; + closed: Event; + addCode( + code: string, + file: string, + line: number, + editor?: TextEditor, + runningStopWatch?: StopWatch + ): Promise; + addMessage(message: string): Promise; + debugCode( + code: string, + file: string, + line: number, + editor?: TextEditor, + runningStopWatch?: StopWatch + ): Promise; + expandAllCells(): void; + collapseAllCells(): void; + exportCells(): void; + scrollToCell(id: string): void; +} + +// For native editing, the provider acts like the IDocumentManager for normal docs +export const INotebookEditorProvider = Symbol('INotebookEditorProvider'); +export interface INotebookEditorProvider { + readonly activeEditor: INotebookEditor | undefined; + readonly editors: INotebookEditor[]; + readonly onDidOpenNotebookEditor: Event; + readonly onDidChangeActiveNotebookEditor: Event; + readonly onDidCloseNotebookEditor: Event; + open(file: Uri): Promise; + show(file: Uri): Promise; + createNew(contents?: string): Promise; +} + +// For native editing, the INotebookEditor acts like a TextEditor and a TextDocument together +export const INotebookEditor = Symbol('INotebookEditor'); +export interface INotebookEditor extends IInteractiveBase { + readonly onDidChangeViewState: Event; + readonly closed: Event; + readonly executed: Event; + readonly modified: Event; + readonly saved: Event; + /** + * Is this notebook representing an untitled file which has never been saved yet. + */ + readonly isUntitled: boolean; + /** + * `true` if there are unpersisted changes. + */ + readonly isDirty: boolean; + readonly file: Uri; + readonly visible: boolean; + readonly active: boolean; + readonly model: INotebookModel | undefined; + load(storage: INotebookModel, webViewPanel?: WebviewPanel): Promise; + runAllCells(): void; + runSelectedCell(): void; + addCellBelow(): void; +} + +export const IInteractiveWindowListener = Symbol('IInteractiveWindowListener'); + +/** + * Listens to history messages to provide extra functionality + */ +export interface IInteractiveWindowListener extends IDisposable { + /** + * Fires this event when posting a response message + */ + // tslint:disable-next-line: no-any + postMessage: Event<{ message: string; payload: any }>; + /** + * Handles messages that the interactive window receives + * @param message message type + * @param payload message payload + */ + // tslint:disable-next-line: no-any + onMessage(message: string, payload?: any): void; +} + +// Wraps the vscode API in order to send messages back and forth from a webview +export const IPostOffice = Symbol('IPostOffice'); +export interface IPostOffice { + // tslint:disable-next-line:no-any + post(message: string, params: any[] | undefined): void; + // tslint:disable-next-line:no-any + listen(message: string, listener: (args: any[] | undefined) => void): void; +} + +// Wraps the vscode CodeLensProvider base class +export const IDataScienceCodeLensProvider = Symbol('IDataScienceCodeLensProvider'); +export interface IDataScienceCodeLensProvider extends CodeLensProvider { + getCodeWatcher(document: TextDocument): ICodeWatcher | undefined; +} + +// Wraps the Code Watcher API +export const ICodeWatcher = Symbol('ICodeWatcher'); +export interface ICodeWatcher { + codeLensUpdated: Event; + setDocument(document: TextDocument): void; + getFileName(): string; + getVersion(): number; + getCodeLenses(): CodeLens[]; + getCachedSettings(): IDataScienceSettings | undefined; + runAllCells(): Promise; + runCell(range: Range): Promise; + debugCell(range: Range): Promise; + runCurrentCell(): Promise; + runCurrentCellAndAdvance(): Promise; + runSelectionOrLine(activeEditor: TextEditor | undefined): Promise; + runToLine(targetLine: number): Promise; + runFromLine(targetLine: number): Promise; + runAllCellsAbove(stopLine: number, stopCharacter: number): Promise; + runCellAndAllBelow(startLine: number, startCharacter: number): Promise; + runFileInteractive(): Promise; + debugFileInteractive(): Promise; + addEmptyCellToBottom(): Promise; + runCurrentCellAndAddBelow(): Promise; + debugCurrentCell(): Promise; +} + +export const ICodeLensFactory = Symbol('ICodeLensFactory'); +export interface ICodeLensFactory { + updateRequired: Event; + createCodeLenses(document: TextDocument): CodeLens[]; +} + +export enum CellState { + editing = -1, + init = 0, + executing = 1, + finished = 2, + error = 3 +} + +// Basic structure for a cell from a notebook +export interface ICell { + id: string; // This value isn't unique. File and line are needed too. + file: string; + line: number; + state: CellState; + data: nbformat.ICodeCell | nbformat.IRawCell | nbformat.IMarkdownCell | IMessageCell; + extraLines?: number[]; +} + +export interface IInteractiveWindowInfo { + cellCount: number; + undoCount: number; + redoCount: number; + selectedCell: string | undefined; +} + +export interface IMessageCell extends nbformat.IBaseCell { + cell_type: 'messages'; + messages: string[]; +} + +export const ICodeCssGenerator = Symbol('ICodeCssGenerator'); +export interface ICodeCssGenerator { + generateThemeCss(resource: Resource, isDark: boolean, theme: string): Promise; + generateMonacoTheme(resource: Resource, isDark: boolean, theme: string): Promise; +} + +export const IThemeFinder = Symbol('IThemeFinder'); +export interface IThemeFinder { + findThemeRootJson(themeName: string): Promise; + findTmLanguage(language: string): Promise; + isThemeDark(themeName: string): Promise; +} + +export const IStatusProvider = Symbol('IStatusProvider'); +export interface IStatusProvider { + // call this function to set the new status on the active + // interactive window. Dispose of the returned object when done. + set( + message: string, + showInWebView: boolean, + timeout?: number, + canceled?: () => void, + interactivePanel?: IInteractiveBase + ): Disposable; + + // call this function to wait for a promise while displaying status + waitWithStatus( + promise: () => Promise, + message: string, + showInWebView: boolean, + timeout?: number, + canceled?: () => void, + interactivePanel?: IInteractiveBase + ): Promise; +} + +export interface IJupyterCommand { + interpreter(): Promise; + execObservable(args: string[], options: SpawnOptions): Promise>; + exec(args: string[], options: SpawnOptions): Promise>; +} + +export const IJupyterCommandFactory = Symbol('IJupyterCommandFactory'); +export interface IJupyterCommandFactory { + createInterpreterCommand( + command: JupyterCommands, + moduleName: string, + args: string[], + interpreter: PythonInterpreter, + isActiveInterpreter: boolean + ): IJupyterCommand; + createProcessCommand(exe: string, args: string[]): IJupyterCommand; +} + +// Config settings we pass to our react code +export type FileSettings = { + autoSaveDelay: number; + autoSave: 'afterDelay' | 'off' | 'onFocusChange' | 'onWindowChange'; +}; + +export interface IDataScienceExtraSettings extends IDataScienceSettings { + extraSettings: { + editor: { + cursor: string; + cursorBlink: string; + fontLigatures: boolean; + autoClosingBrackets: string; + autoClosingQuotes: string; + autoSurround: string; + autoIndent: boolean; + scrollBeyondLastLine: boolean; + horizontalScrollbarSize: number; + verticalScrollbarSize: number; + fontSize: number; + fontFamily: string; + }; + theme: string; + }; + intellisenseOptions: { + quickSuggestions: { + other: boolean; + comments: boolean; + strings: boolean; + }; + acceptSuggestionOnEnter: boolean | 'on' | 'smart' | 'off'; + quickSuggestionsDelay: number; + suggestOnTriggerCharacters: boolean; + tabCompletion: boolean | 'on' | 'off' | 'onlySnippets'; + suggestLocalityBonus: boolean; + suggestSelection: 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix'; + wordBasedSuggestions: boolean; + parameterHintsEnabled: boolean; + }; +} + +// Get variables from the currently running active Jupyter server +// Note: This definition is used implicitly by getJupyterVariableValue.py file +// Changes here may need to be reflected there as well +export interface IJupyterVariable { + name: string; + value: string | undefined; + executionCount?: number; + supportsDataExplorer: boolean; + type: string; + size: number; + shape: string; + count: number; + truncated: boolean; + columns?: { key: string; type: string }[]; + rowCount?: number; + indexColumn?: string; +} + +export const IJupyterVariables = Symbol('IJupyterVariables'); +export interface IJupyterVariables { + getVariables(notebook: INotebook, request: IJupyterVariablesRequest): Promise; + getDataFrameInfo(targetVariable: IJupyterVariable, notebook: INotebook): Promise; + getDataFrameRows( + targetVariable: IJupyterVariable, + notebook: INotebook, + start: number, + end: number + ): Promise; +} + +// Request for variables +export interface IJupyterVariablesRequest { + executionCount: number; + sortColumn: string; + sortAscending: boolean; + startIndex: number; + pageSize: number; +} + +// Response to a request +export interface IJupyterVariablesResponse { + executionCount: number; + totalCount: number; + pageStartIndex: number; + pageResponse: IJupyterVariable[]; +} + +export const IDataViewerProvider = Symbol('IDataViewerProvider'); +export interface IDataViewerProvider { + create(variable: IJupyterVariable, notebook: INotebook): Promise; +} +export const IDataViewer = Symbol('IDataViewer'); + +export interface IDataViewer extends IDisposable { + showVariable(variable: IJupyterVariable, notebook: INotebook): Promise; +} + +export const IPlotViewerProvider = Symbol('IPlotViewerProvider'); +export interface IPlotViewerProvider { + showPlot(imageHtml: string): Promise; +} +export const IPlotViewer = Symbol('IPlotViewer'); + +export interface IPlotViewer extends IDisposable { + closed: Event; + removed: Event; + addPlot(imageHtml: string): Promise; + show(): Promise; +} + +export interface ISourceMapMapping { + line: number; + endLine: number; + runtimeSource: { path: string }; + runtimeLine: number; +} + +export interface ISourceMapRequest { + source: { path: string }; + pydevdSourceMaps: ISourceMapMapping[]; +} + +export interface ICellHash { + line: number; // 1 based + endLine: number; // 1 based and inclusive + runtimeLine: number; // Line in the jupyter source to start at + hash: string; + executionCount: number; + id: string; // Cell id as sent to jupyter +} + +export interface IFileHashes { + file: string; + hashes: ICellHash[]; +} + +export const ICellHashListener = Symbol('ICellHashListener'); +export interface ICellHashListener { + hashesUpdated(hashes: IFileHashes[]): Promise; +} + +export const ICellHashProvider = Symbol('ICellHashProvider'); +export interface ICellHashProvider { + updated: Event; + getHashes(): IFileHashes[]; + getExecutionCount(): number; + incExecutionCount(): void; +} + +export const ICellHashLogger = Symbol('ICellHashLogger'); +export interface ICellHashLogger extends INotebookExecutionLogger { + getCellHashProvider(): ICellHashProvider; +} + +export interface IDebugLocation { + fileName: string; + lineNumber: number; + column: number; +} + +export const IDebugLocationTracker = Symbol('IDebugLocationTracker'); +export interface IDebugLocationTracker { + updated: Event; + getLocation(debugSession: DebugSession): IDebugLocation | undefined; +} + +export const IJupyterSubCommandExecutionService = Symbol('IJupyterSubCommandExecutionService'); +/** + * Responsible for execution of jupyter subcommands such as `notebook`, `nbconvert`, etc. + * The executed code is as follows `python -m jupyter `. + * + * @export + * @interface IJupyterSubCommandExecutionService + */ +export interface IJupyterSubCommandExecutionService { + /** + * Checks whether notebook is supported. + * + * @param {CancellationToken} [cancelToken] + * @returns {Promise} + * @memberof IJupyterSubCommandExecutionService + */ + isNotebookSupported(cancelToken?: CancellationToken): Promise; + /** + * Checks whether exporting of ipynb is supported. + * + * @param {CancellationToken} [cancelToken] + * @returns {Promise} + * @memberof IJupyterSubCommandExecutionService + */ + isExportSupported(cancelToken?: CancellationToken): Promise; + /** + * Error message indicating why jupyter notebook isn't supported. + * + * @returns {Promise} + * @memberof IJupyterSubCommandExecutionService + */ + getReasonForJupyterNotebookNotBeingSupported(): Promise; + /** + * Used to refresh the command finder. + * + * @returns {Promise} + * @memberof IJupyterSubCommandExecutionService + */ + refreshCommands(): Promise; + /** + * Gets the interpreter to be used for starting of jupyter server. + * + * @param {CancellationToken} [token] + * @returns {(Promise)} + * @memberof IJupyterInterpreterService + */ + getSelectedInterpreter(token?: CancellationToken): Promise; + /** + * Starts the jupyter notebook server + * + * @param {string[]} notebookArgs + * @param {SpawnOptions} options + * @returns {Promise>} + * @memberof IJupyterSubCommandExecutionService + */ + startNotebook(notebookArgs: string[], options: SpawnOptions): Promise>; + /** + * Gets a list of all locally running jupyter notebook servers. + * + * @param {CancellationToken} [token] + * @returns {(Promise)} + * @memberof IJupyterSubCommandExecutionService + */ + getRunningJupyterServers(token?: CancellationToken): Promise; + /** + * Exports a given notebook into a python file. + * + * @param {string} file + * @param {string} [template] + * @param {CancellationToken} [token] + * @returns {Promise} + * @memberof IJupyterSubCommandExecutionService + */ + exportNotebookToPython(file: string, template?: string, token?: CancellationToken): Promise; + /** + * Opens an ipynb file in a new instance of a jupyter notebook server. + * + * @param {string} notebookFile + * @returns {Promise} + * @memberof IJupyterSubCommandExecutionService + */ + openNotebook(notebookFile: string): Promise; + /** + * Gets the kernelspecs. + * + * @param {CancellationToken} [token] + * @returns {Promise} + * @memberof IJupyterSubCommandExecutionService + */ + getKernelSpecs(token?: CancellationToken): Promise; +} + +export const IJupyterInterpreterDependencyManager = Symbol('IJupyterInterpreterDependencyManager'); +export interface IJupyterInterpreterDependencyManager { + /** + * Installs the dependencies required to launch jupyter. + * + * @param {JupyterInstallError} [err] + * @returns {Promise} + * @memberof IJupyterInterpreterDependencyManager + */ + installMissingDependencies(err?: JupyterInstallError): Promise; +} + +export interface INotebookModel { + readonly file: Uri; + readonly isDirty: boolean; + readonly isUntitled: boolean; + readonly changed: Event; + readonly cells: ICell[]; + getJson(): Promise>; + getContent(cells?: ICell[]): Promise; + update(change: NotebookModelChange): void; +} + +export const INotebookStorage = Symbol('INotebookStorage'); + +export interface INotebookStorage { + readonly onDidEdit: Event; + save(cancellation: CancellationToken): Thenable; + saveAs(targetResource: Uri): Thenable; + applyEdits(edits: readonly NotebookModelChange[]): Thenable; + undoEdits(edits: readonly NotebookModelChange[]): Thenable; + backup(cancellation: CancellationToken): Thenable; + load(file: Uri, contents?: string): Promise; +} +type WebViewViewState = { + readonly visible: boolean; + readonly active: boolean; +}; +export type WebViewViewChangeEventArgs = { current: WebViewViewState; previous: WebViewViewState }; + +export const INotebookProvider = Symbol('INotebookProvider'); + +export type GetServerOptions = { + getOnly?: boolean; + disableUI?: boolean; + localOnly?: boolean; +}; + +/** + * Options for getting a notebook + */ +export type GetNotebookOptions = { + identity: Uri; + getOnly?: boolean; + disableUI?: boolean; + metadata?: nbformat.INotebookMetadata; +}; + +export interface INotebookProvider { + /** + * Fired when a notebook has been created for a given Uri/Identity + */ + onNotebookCreated: Event<{ identity: Uri; notebook: INotebook }>; + + /** + * List of all notebooks (active and ones that are being constructed). + */ + activeNotebooks: Promise[]; + /** + * Gets or creates a notebook, and manages the lifetime of notebooks. + */ + getOrCreateNotebook(options: GetNotebookOptions): Promise; + + /** + * Gets the server used for starting notebooks + */ + getOrCreateServer(options: GetServerOptions): Promise; +} diff --git a/src/client/datascience/webViewHost.ts b/src/client/datascience/webViewHost.ts index 11cded66e630..3139475f58da 100644 --- a/src/client/datascience/webViewHost.ts +++ b/src/client/datascience/webViewHost.ts @@ -1,379 +1,379 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import '../common/extensions'; - -import { injectable, unmanaged } from 'inversify'; -import { ConfigurationChangeEvent, ViewColumn, WebviewPanel, WorkspaceConfiguration } from 'vscode'; - -import { IWebPanel, IWebPanelMessageListener, IWebPanelProvider, IWorkspaceService } from '../common/application/types'; -import { isTestExecution } from '../common/constants'; -import { traceInfo, traceWarning } from '../common/logger'; -import { IConfigurationService, IDisposable, Resource } from '../common/types'; -import { createDeferred, Deferred } from '../common/utils/async'; -import * as localize from '../common/utils/localize'; -import { noop } from '../common/utils/misc'; -import { StopWatch } from '../common/utils/stopWatch'; -import { captureTelemetry, sendTelemetryEvent } from '../telemetry'; -import { DefaultTheme, Telemetry } from './constants'; -import { CssMessages, IGetCssRequest, IGetMonacoThemeRequest, SharedMessages } from './messages'; -import { ICodeCssGenerator, IDataScienceExtraSettings, IThemeFinder, WebViewViewChangeEventArgs } from './types'; - -@injectable() // For some reason this is necessary to get the class hierarchy to work. -export abstract class WebViewHost implements IDisposable { - protected get isDisposed(): boolean { - return this.disposed; - } - protected viewState: { visible: boolean; active: boolean } = { visible: false, active: false }; - private disposed: boolean = false; - private webPanel: IWebPanel | undefined; - private webPanelInit: Deferred | undefined = createDeferred(); - private messageListener: IWebPanelMessageListener; - private themeChangeHandler: IDisposable | undefined; - private settingsChangeHandler: IDisposable | undefined; - private themeIsDarkPromise: Deferred | undefined = createDeferred(); - private startupStopwatch = new StopWatch(); - - constructor( - @unmanaged() protected configService: IConfigurationService, - @unmanaged() private provider: IWebPanelProvider, - @unmanaged() private cssGenerator: ICodeCssGenerator, - @unmanaged() protected themeFinder: IThemeFinder, - @unmanaged() protected workspaceService: IWorkspaceService, - @unmanaged() - messageListenerCtor: ( - callback: (message: string, payload: {}) => void, - viewChanged: (panel: IWebPanel) => void, - disposed: () => void - ) => IWebPanelMessageListener, - @unmanaged() private rootPath: string, - @unmanaged() private scripts: string[], - @unmanaged() private title: string, - @unmanaged() private viewColumn: ViewColumn, - @unmanaged() private readonly useWebViewServer: boolean - ) { - // Create our message listener for our web panel. - this.messageListener = messageListenerCtor( - this.onMessage.bind(this), - this.webPanelViewStateChanged.bind(this), - this.dispose.bind(this) - ); - - // Listen for settings changes from vscode. - this.themeChangeHandler = this.workspaceService.onDidChangeConfiguration(this.onPossibleSettingsChange, this); - - // Listen for settings changes - this.settingsChangeHandler = this.configService - .getSettings(undefined) - .onDidChange(this.onDataScienceSettingsChanged.bind(this)); - } - - public async show(preserveFocus: boolean): Promise { - if (!this.isDisposed) { - // Then show our web panel. - if (this.webPanel) { - await this.webPanel.show(preserveFocus); - } - } - } - - public updateCwd(cwd: string): void { - if (this.webPanel) { - this.webPanel.updateCwd(cwd); - } - } - - public dispose() { - if (!this.isDisposed) { - this.disposed = true; - if (this.webPanel) { - this.webPanel.close(); - this.webPanel = undefined; - } - if (this.themeChangeHandler) { - this.themeChangeHandler.dispose(); - this.themeChangeHandler = undefined; - } - if (this.settingsChangeHandler) { - this.settingsChangeHandler.dispose(); - this.settingsChangeHandler = undefined; - } - this.webPanelInit = undefined; - this.themeIsDarkPromise = undefined; - } - } - - public setTitle(newTitle: string) { - if (!this.isDisposed && this.webPanel) { - this.webPanel.setTitle(newTitle); - } - } - - public setTheme(isDark: boolean) { - if (this.themeIsDarkPromise && !this.themeIsDarkPromise.resolved) { - this.themeIsDarkPromise.resolve(isDark); - } else { - this.themeIsDarkPromise = createDeferred(); - this.themeIsDarkPromise.resolve(isDark); - } - } - - protected abstract getOwningResource(): Promise; - - //tslint:disable-next-line:no-any - protected onMessage(message: string, payload: any) { - switch (message) { - case SharedMessages.Started: - this.webPanelRendered(); - break; - - case CssMessages.GetCssRequest: - this.handleCssRequest(payload as IGetCssRequest).ignoreErrors(); - break; - - case CssMessages.GetMonacoThemeRequest: - this.handleMonacoThemeRequest(payload as IGetMonacoThemeRequest).ignoreErrors(); - break; - - default: - break; - } - } - - protected postMessage(type: T, payload?: M[T]): Promise { - // Then send it the message - return this.postMessageInternal(type.toString(), payload); - } - - protected shareMessage(type: T, payload?: M[T]) { - // Send our remote message. - this.messageListener.onMessage(type.toString(), payload); - } - - protected onViewStateChanged(_args: WebViewViewChangeEventArgs) { - noop(); - } - - // tslint:disable-next-line:no-any - protected async postMessageInternal(type: string, payload?: any): Promise { - if (this.webPanelInit) { - // Make sure the webpanel is up before we send it anything. - await this.webPanelInit.promise; - - // Then send it the message - this.webPanel?.postMessage({ type: type.toString(), payload: payload }); - } - } - - protected async generateDataScienceExtraSettings(): Promise { - const resource = await this.getOwningResource(); - const editor = this.workspaceService.getConfiguration('editor'); - const workbench = this.workspaceService.getConfiguration('workbench'); - const theme = !workbench ? DefaultTheme : workbench.get('colorTheme', DefaultTheme); - return { - ...this.configService.getSettings(resource).datascience, - extraSettings: { - editor: { - cursor: this.getValue(editor, 'cursorStyle', 'line'), - cursorBlink: this.getValue(editor, 'cursorBlinking', 'blink'), - autoClosingBrackets: this.getValue(editor, 'autoClosingBrackets', 'languageDefined'), - autoClosingQuotes: this.getValue(editor, 'autoClosingQuotes', 'languageDefined'), - autoSurround: this.getValue(editor, 'autoSurround', 'languageDefined'), - autoIndent: this.getValue(editor, 'autoIndent', false), - fontLigatures: this.getValue(editor, 'fontLigatures', false), - scrollBeyondLastLine: this.getValue(editor, 'scrollBeyondLastLine', true), - // VS Code puts a value for this, but it's 10 (the explorer bar size) not 14 the editor size for vert - verticalScrollbarSize: this.getValue(editor, 'scrollbar.verticalScrollbarSize', 14), - horizontalScrollbarSize: this.getValue(editor, 'scrollbar.horizontalScrollbarSize', 10), - fontSize: this.getValue(editor, 'fontSize', 14), - fontFamily: this.getValue(editor, 'fontFamily', "Consolas, 'Courier New', monospace") - }, - theme: theme - }, - intellisenseOptions: { - quickSuggestions: { - other: this.getValue(editor, 'quickSuggestions.other', true), - comments: this.getValue(editor, 'quickSuggestions.comments', false), - strings: this.getValue(editor, 'quickSuggestions.strings', false) - }, - acceptSuggestionOnEnter: this.getValue(editor, 'acceptSuggestionOnEnter', 'on'), - quickSuggestionsDelay: this.getValue(editor, 'quickSuggestionsDelay', 10), - suggestOnTriggerCharacters: this.getValue(editor, 'suggestOnTriggerCharacters', true), - tabCompletion: this.getValue(editor, 'tabCompletion', 'on'), - suggestLocalityBonus: this.getValue(editor, 'suggest.localityBonus', true), - suggestSelection: this.getValue(editor, 'suggestSelection', 'recentlyUsed'), - wordBasedSuggestions: this.getValue(editor, 'wordBasedSuggestions', true), - parameterHintsEnabled: this.getValue(editor, 'parameterHints.enabled', true) - } - }; - } - - protected isDark(): Promise { - return this.themeIsDarkPromise ? this.themeIsDarkPromise.promise : Promise.resolve(false); - } - - protected async loadWebPanel(cwd: string, webViewPanel?: WebviewPanel) { - // Make not disposed anymore - this.disposed = false; - - // Setup our init promise for the web panel. We use this to make sure we're in sync with our - // react control. - this.webPanelInit = this.webPanelInit ? this.webPanelInit : createDeferred(); - - // Setup a promise that will wait until the webview passes back - // a message telling us what them is in use - this.themeIsDarkPromise = this.themeIsDarkPromise ? this.themeIsDarkPromise : createDeferred(); - - // Load our actual web panel - - traceInfo(`Loading web panel. Panel is ${this.webPanel ? 'set' : 'notset'}`); - - // Create our web panel (it's the UI that shows up for the history) - if (this.webPanel === undefined) { - const resource = await this.getOwningResource(); - - // Get our settings to pass along to the react control - const settings = await this.generateDataScienceExtraSettings(); - const insiders = this.configService.getSettings(resource).insidersChannel; - - traceInfo('Loading web view...'); - - let startHttpServer = false; - - if (typeof settings.useWebViewServer === 'boolean') { - // Always allow user to turn this feature on/off via settings. - startHttpServer = settings.useWebViewServer === true; - } else { - // Determine if we should start an HTTP server or not based on if in the insider's channel or - // if it's forced on. - startHttpServer = this.useWebViewServer || insiders !== 'off'; - } - - traceWarning(`startHttpServer=${startHttpServer}, will not be used. Temporarily turned off`); - - // Use this script to create our web view panel. It should contain all of the necessary - // script to communicate with this class. - this.webPanel = await this.provider.create({ - viewColumn: this.viewColumn, - listener: this.messageListener, - title: this.title, - rootPath: this.rootPath, - scripts: this.scripts, - settings, - startHttpServer: false, - cwd, - webViewPanel - }); - - traceInfo('Web view created.'); - } - - // Send the first settings message - this.onDataScienceSettingsChanged().ignoreErrors(); - - // Send the loc strings (skip during testing as it takes up a lot of memory) - this.sendLocStrings().ignoreErrors(); - } - - protected async sendLocStrings() { - const locStrings = isTestExecution() ? '{}' : localize.getCollectionJSON(); - this.postMessageInternal(SharedMessages.LocInit, locStrings).ignoreErrors(); - } - - // Post a message to our webpanel and update our new datascience settings - protected onDataScienceSettingsChanged = async () => { - // Stringify our settings to send over to the panel - const dsSettings = JSON.stringify(await this.generateDataScienceExtraSettings()); - this.postMessageInternal(SharedMessages.UpdateSettings, dsSettings).ignoreErrors(); - }; - - private getValue(workspaceConfig: WorkspaceConfiguration, section: string, defaultValue: T): T { - if (workspaceConfig) { - return workspaceConfig.get(section, defaultValue); - } - return defaultValue; - } - - private webPanelViewStateChanged = (webPanel: IWebPanel) => { - const visible = webPanel.isVisible(); - const active = webPanel.isActive(); - const current = { visible, active }; - const previous = { visible: this.viewState.visible, active: this.viewState.active }; - this.viewState.visible = visible; - this.viewState.active = active; - this.onViewStateChanged({ current, previous }); - }; - - @captureTelemetry(Telemetry.WebviewStyleUpdate) - private async handleCssRequest(request: IGetCssRequest): Promise { - const settings = await this.generateDataScienceExtraSettings(); - const requestIsDark = settings.ignoreVscodeTheme ? false : request.isDark; - this.setTheme(requestIsDark); - const isDark = settings.ignoreVscodeTheme - ? false - : await this.themeFinder.isThemeDark(settings.extraSettings.theme); - const resource = await this.getOwningResource(); - const css = await this.cssGenerator.generateThemeCss(resource, requestIsDark, settings.extraSettings.theme); - return this.postMessageInternal(CssMessages.GetCssResponse, { - css, - theme: settings.extraSettings.theme, - knownDark: isDark - }); - } - - @captureTelemetry(Telemetry.WebviewMonacoStyleUpdate) - private async handleMonacoThemeRequest(request: IGetMonacoThemeRequest): Promise { - const settings = await this.generateDataScienceExtraSettings(); - const isDark = settings.ignoreVscodeTheme ? false : request.isDark; - this.setTheme(isDark); - const resource = await this.getOwningResource(); - const monacoTheme = await this.cssGenerator.generateMonacoTheme(resource, isDark, settings.extraSettings.theme); - return this.postMessageInternal(CssMessages.GetMonacoThemeResponse, { theme: monacoTheme }); - } - - // tslint:disable-next-line:no-any - private webPanelRendered() { - if (this.webPanelInit && !this.webPanelInit.resolved) { - // Send telemetry for startup - sendTelemetryEvent(Telemetry.WebviewStartup, this.startupStopwatch.elapsedTime, { type: this.title }); - - // Resolve our started promise. This means the webpanel is ready to go. - this.webPanelInit.resolve(); - - traceInfo('Web view react rendered'); - } - - // On started, resend our init data. - this.sendLocStrings().ignoreErrors(); - this.onDataScienceSettingsChanged().ignoreErrors(); - } - - // Post a message to our webpanel and update our new datascience settings - private onPossibleSettingsChange = async (event: ConfigurationChangeEvent) => { - if ( - event.affectsConfiguration('workbench.colorTheme') || - event.affectsConfiguration('editor.fontSize') || - event.affectsConfiguration('editor.fontFamily') || - event.affectsConfiguration('editor.cursorStyle') || - event.affectsConfiguration('editor.cursorBlinking') || - event.affectsConfiguration('editor.autoClosingBrackets') || - event.affectsConfiguration('editor.autoClosingQuotes') || - event.affectsConfiguration('editor.autoSurround') || - event.affectsConfiguration('editor.autoIndent') || - event.affectsConfiguration('editor.scrollBeyondLastLine') || - event.affectsConfiguration('editor.fontLigatures') || - event.affectsConfiguration('editor.scrollbar.verticalScrollbarSize') || - event.affectsConfiguration('editor.scrollbar.horizontalScrollbarSize') || - event.affectsConfiguration('files.autoSave') || - event.affectsConfiguration('files.autoSaveDelay') || - event.affectsConfiguration('python.dataScience.enableGather') - ) { - // See if the theme changed - const newSettings = await this.generateDataScienceExtraSettings(); - if (newSettings) { - const dsSettings = JSON.stringify(newSettings); - this.postMessageInternal(SharedMessages.UpdateSettings, dsSettings).ignoreErrors(); - } - } - }; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import '../common/extensions'; + +import { injectable, unmanaged } from 'inversify'; +import { ConfigurationChangeEvent, ViewColumn, WebviewPanel, WorkspaceConfiguration } from 'vscode'; + +import { IWebPanel, IWebPanelMessageListener, IWebPanelProvider, IWorkspaceService } from '../common/application/types'; +import { isTestExecution } from '../common/constants'; +import { traceInfo, traceWarning } from '../common/logger'; +import { IConfigurationService, IDisposable, Resource } from '../common/types'; +import { createDeferred, Deferred } from '../common/utils/async'; +import * as localize from '../common/utils/localize'; +import { noop } from '../common/utils/misc'; +import { StopWatch } from '../common/utils/stopWatch'; +import { captureTelemetry, sendTelemetryEvent } from '../telemetry'; +import { DefaultTheme, Telemetry } from './constants'; +import { CssMessages, IGetCssRequest, IGetMonacoThemeRequest, SharedMessages } from './messages'; +import { ICodeCssGenerator, IDataScienceExtraSettings, IThemeFinder, WebViewViewChangeEventArgs } from './types'; + +@injectable() // For some reason this is necessary to get the class hierarchy to work. +export abstract class WebViewHost implements IDisposable { + protected get isDisposed(): boolean { + return this.disposed; + } + protected viewState: { visible: boolean; active: boolean } = { visible: false, active: false }; + private disposed: boolean = false; + private webPanel: IWebPanel | undefined; + private webPanelInit: Deferred | undefined = createDeferred(); + private messageListener: IWebPanelMessageListener; + private themeChangeHandler: IDisposable | undefined; + private settingsChangeHandler: IDisposable | undefined; + private themeIsDarkPromise: Deferred | undefined = createDeferred(); + private startupStopwatch = new StopWatch(); + + constructor( + @unmanaged() protected configService: IConfigurationService, + @unmanaged() private provider: IWebPanelProvider, + @unmanaged() private cssGenerator: ICodeCssGenerator, + @unmanaged() protected themeFinder: IThemeFinder, + @unmanaged() protected workspaceService: IWorkspaceService, + @unmanaged() + messageListenerCtor: ( + callback: (message: string, payload: {}) => void, + viewChanged: (panel: IWebPanel) => void, + disposed: () => void + ) => IWebPanelMessageListener, + @unmanaged() private rootPath: string, + @unmanaged() private scripts: string[], + @unmanaged() private title: string, + @unmanaged() private viewColumn: ViewColumn, + @unmanaged() private readonly useWebViewServer: boolean + ) { + // Create our message listener for our web panel. + this.messageListener = messageListenerCtor( + this.onMessage.bind(this), + this.webPanelViewStateChanged.bind(this), + this.dispose.bind(this) + ); + + // Listen for settings changes from vscode. + this.themeChangeHandler = this.workspaceService.onDidChangeConfiguration(this.onPossibleSettingsChange, this); + + // Listen for settings changes + this.settingsChangeHandler = this.configService + .getSettings(undefined) + .onDidChange(this.onDataScienceSettingsChanged.bind(this)); + } + + public async show(preserveFocus: boolean): Promise { + if (!this.isDisposed) { + // Then show our web panel. + if (this.webPanel) { + await this.webPanel.show(preserveFocus); + } + } + } + + public updateCwd(cwd: string): void { + if (this.webPanel) { + this.webPanel.updateCwd(cwd); + } + } + + public dispose() { + if (!this.isDisposed) { + this.disposed = true; + if (this.webPanel) { + this.webPanel.close(); + this.webPanel = undefined; + } + if (this.themeChangeHandler) { + this.themeChangeHandler.dispose(); + this.themeChangeHandler = undefined; + } + if (this.settingsChangeHandler) { + this.settingsChangeHandler.dispose(); + this.settingsChangeHandler = undefined; + } + this.webPanelInit = undefined; + this.themeIsDarkPromise = undefined; + } + } + + public setTitle(newTitle: string) { + if (!this.isDisposed && this.webPanel) { + this.webPanel.setTitle(newTitle); + } + } + + public setTheme(isDark: boolean) { + if (this.themeIsDarkPromise && !this.themeIsDarkPromise.resolved) { + this.themeIsDarkPromise.resolve(isDark); + } else { + this.themeIsDarkPromise = createDeferred(); + this.themeIsDarkPromise.resolve(isDark); + } + } + + protected abstract getOwningResource(): Promise; + + //tslint:disable-next-line:no-any + protected onMessage(message: string, payload: any) { + switch (message) { + case SharedMessages.Started: + this.webPanelRendered(); + break; + + case CssMessages.GetCssRequest: + this.handleCssRequest(payload as IGetCssRequest).ignoreErrors(); + break; + + case CssMessages.GetMonacoThemeRequest: + this.handleMonacoThemeRequest(payload as IGetMonacoThemeRequest).ignoreErrors(); + break; + + default: + break; + } + } + + protected postMessage(type: T, payload?: M[T]): Promise { + // Then send it the message + return this.postMessageInternal(type.toString(), payload); + } + + protected shareMessage(type: T, payload?: M[T]) { + // Send our remote message. + this.messageListener.onMessage(type.toString(), payload); + } + + protected onViewStateChanged(_args: WebViewViewChangeEventArgs) { + noop(); + } + + // tslint:disable-next-line:no-any + protected async postMessageInternal(type: string, payload?: any): Promise { + if (this.webPanelInit) { + // Make sure the webpanel is up before we send it anything. + await this.webPanelInit.promise; + + // Then send it the message + this.webPanel?.postMessage({ type: type.toString(), payload: payload }); + } + } + + protected async generateDataScienceExtraSettings(): Promise { + const resource = await this.getOwningResource(); + const editor = this.workspaceService.getConfiguration('editor'); + const workbench = this.workspaceService.getConfiguration('workbench'); + const theme = !workbench ? DefaultTheme : workbench.get('colorTheme', DefaultTheme); + return { + ...this.configService.getSettings(resource).datascience, + extraSettings: { + editor: { + cursor: this.getValue(editor, 'cursorStyle', 'line'), + cursorBlink: this.getValue(editor, 'cursorBlinking', 'blink'), + autoClosingBrackets: this.getValue(editor, 'autoClosingBrackets', 'languageDefined'), + autoClosingQuotes: this.getValue(editor, 'autoClosingQuotes', 'languageDefined'), + autoSurround: this.getValue(editor, 'autoSurround', 'languageDefined'), + autoIndent: this.getValue(editor, 'autoIndent', false), + fontLigatures: this.getValue(editor, 'fontLigatures', false), + scrollBeyondLastLine: this.getValue(editor, 'scrollBeyondLastLine', true), + // VS Code puts a value for this, but it's 10 (the explorer bar size) not 14 the editor size for vert + verticalScrollbarSize: this.getValue(editor, 'scrollbar.verticalScrollbarSize', 14), + horizontalScrollbarSize: this.getValue(editor, 'scrollbar.horizontalScrollbarSize', 10), + fontSize: this.getValue(editor, 'fontSize', 14), + fontFamily: this.getValue(editor, 'fontFamily', "Consolas, 'Courier New', monospace") + }, + theme: theme + }, + intellisenseOptions: { + quickSuggestions: { + other: this.getValue(editor, 'quickSuggestions.other', true), + comments: this.getValue(editor, 'quickSuggestions.comments', false), + strings: this.getValue(editor, 'quickSuggestions.strings', false) + }, + acceptSuggestionOnEnter: this.getValue(editor, 'acceptSuggestionOnEnter', 'on'), + quickSuggestionsDelay: this.getValue(editor, 'quickSuggestionsDelay', 10), + suggestOnTriggerCharacters: this.getValue(editor, 'suggestOnTriggerCharacters', true), + tabCompletion: this.getValue(editor, 'tabCompletion', 'on'), + suggestLocalityBonus: this.getValue(editor, 'suggest.localityBonus', true), + suggestSelection: this.getValue(editor, 'suggestSelection', 'recentlyUsed'), + wordBasedSuggestions: this.getValue(editor, 'wordBasedSuggestions', true), + parameterHintsEnabled: this.getValue(editor, 'parameterHints.enabled', true) + } + }; + } + + protected isDark(): Promise { + return this.themeIsDarkPromise ? this.themeIsDarkPromise.promise : Promise.resolve(false); + } + + protected async loadWebPanel(cwd: string, webViewPanel?: WebviewPanel) { + // Make not disposed anymore + this.disposed = false; + + // Setup our init promise for the web panel. We use this to make sure we're in sync with our + // react control. + this.webPanelInit = this.webPanelInit ? this.webPanelInit : createDeferred(); + + // Setup a promise that will wait until the webview passes back + // a message telling us what them is in use + this.themeIsDarkPromise = this.themeIsDarkPromise ? this.themeIsDarkPromise : createDeferred(); + + // Load our actual web panel + + traceInfo(`Loading web panel. Panel is ${this.webPanel ? 'set' : 'notset'}`); + + // Create our web panel (it's the UI that shows up for the history) + if (this.webPanel === undefined) { + const resource = await this.getOwningResource(); + + // Get our settings to pass along to the react control + const settings = await this.generateDataScienceExtraSettings(); + const insiders = this.configService.getSettings(resource).insidersChannel; + + traceInfo('Loading web view...'); + + let startHttpServer = false; + + if (typeof settings.useWebViewServer === 'boolean') { + // Always allow user to turn this feature on/off via settings. + startHttpServer = settings.useWebViewServer === true; + } else { + // Determine if we should start an HTTP server or not based on if in the insider's channel or + // if it's forced on. + startHttpServer = this.useWebViewServer || insiders !== 'off'; + } + + traceWarning(`startHttpServer=${startHttpServer}, will not be used. Temporarily turned off`); + + // Use this script to create our web view panel. It should contain all of the necessary + // script to communicate with this class. + this.webPanel = await this.provider.create({ + viewColumn: this.viewColumn, + listener: this.messageListener, + title: this.title, + rootPath: this.rootPath, + scripts: this.scripts, + settings, + startHttpServer: false, + cwd, + webViewPanel + }); + + traceInfo('Web view created.'); + } + + // Send the first settings message + this.onDataScienceSettingsChanged().ignoreErrors(); + + // Send the loc strings (skip during testing as it takes up a lot of memory) + this.sendLocStrings().ignoreErrors(); + } + + protected async sendLocStrings() { + const locStrings = isTestExecution() ? '{}' : localize.getCollectionJSON(); + this.postMessageInternal(SharedMessages.LocInit, locStrings).ignoreErrors(); + } + + // Post a message to our webpanel and update our new datascience settings + protected onDataScienceSettingsChanged = async () => { + // Stringify our settings to send over to the panel + const dsSettings = JSON.stringify(await this.generateDataScienceExtraSettings()); + this.postMessageInternal(SharedMessages.UpdateSettings, dsSettings).ignoreErrors(); + }; + + private getValue(workspaceConfig: WorkspaceConfiguration, section: string, defaultValue: T): T { + if (workspaceConfig) { + return workspaceConfig.get(section, defaultValue); + } + return defaultValue; + } + + private webPanelViewStateChanged = (webPanel: IWebPanel) => { + const visible = webPanel.isVisible(); + const active = webPanel.isActive(); + const current = { visible, active }; + const previous = { visible: this.viewState.visible, active: this.viewState.active }; + this.viewState.visible = visible; + this.viewState.active = active; + this.onViewStateChanged({ current, previous }); + }; + + @captureTelemetry(Telemetry.WebviewStyleUpdate) + private async handleCssRequest(request: IGetCssRequest): Promise { + const settings = await this.generateDataScienceExtraSettings(); + const requestIsDark = settings.ignoreVscodeTheme ? false : request.isDark; + this.setTheme(requestIsDark); + const isDark = settings.ignoreVscodeTheme + ? false + : await this.themeFinder.isThemeDark(settings.extraSettings.theme); + const resource = await this.getOwningResource(); + const css = await this.cssGenerator.generateThemeCss(resource, requestIsDark, settings.extraSettings.theme); + return this.postMessageInternal(CssMessages.GetCssResponse, { + css, + theme: settings.extraSettings.theme, + knownDark: isDark + }); + } + + @captureTelemetry(Telemetry.WebviewMonacoStyleUpdate) + private async handleMonacoThemeRequest(request: IGetMonacoThemeRequest): Promise { + const settings = await this.generateDataScienceExtraSettings(); + const isDark = settings.ignoreVscodeTheme ? false : request.isDark; + this.setTheme(isDark); + const resource = await this.getOwningResource(); + const monacoTheme = await this.cssGenerator.generateMonacoTheme(resource, isDark, settings.extraSettings.theme); + return this.postMessageInternal(CssMessages.GetMonacoThemeResponse, { theme: monacoTheme }); + } + + // tslint:disable-next-line:no-any + private webPanelRendered() { + if (this.webPanelInit && !this.webPanelInit.resolved) { + // Send telemetry for startup + sendTelemetryEvent(Telemetry.WebviewStartup, this.startupStopwatch.elapsedTime, { type: this.title }); + + // Resolve our started promise. This means the webpanel is ready to go. + this.webPanelInit.resolve(); + + traceInfo('Web view react rendered'); + } + + // On started, resend our init data. + this.sendLocStrings().ignoreErrors(); + this.onDataScienceSettingsChanged().ignoreErrors(); + } + + // Post a message to our webpanel and update our new datascience settings + private onPossibleSettingsChange = async (event: ConfigurationChangeEvent) => { + if ( + event.affectsConfiguration('workbench.colorTheme') || + event.affectsConfiguration('editor.fontSize') || + event.affectsConfiguration('editor.fontFamily') || + event.affectsConfiguration('editor.cursorStyle') || + event.affectsConfiguration('editor.cursorBlinking') || + event.affectsConfiguration('editor.autoClosingBrackets') || + event.affectsConfiguration('editor.autoClosingQuotes') || + event.affectsConfiguration('editor.autoSurround') || + event.affectsConfiguration('editor.autoIndent') || + event.affectsConfiguration('editor.scrollBeyondLastLine') || + event.affectsConfiguration('editor.fontLigatures') || + event.affectsConfiguration('editor.scrollbar.verticalScrollbarSize') || + event.affectsConfiguration('editor.scrollbar.horizontalScrollbarSize') || + event.affectsConfiguration('files.autoSave') || + event.affectsConfiguration('files.autoSaveDelay') || + event.affectsConfiguration('python.dataScience.enableGather') + ) { + // See if the theme changed + const newSettings = await this.generateDataScienceExtraSettings(); + if (newSettings) { + const dsSettings = JSON.stringify(newSettings); + this.postMessageInternal(SharedMessages.UpdateSettings, dsSettings).ignoreErrors(); + } + } + }; +} diff --git a/src/client/debugger/debugAdapter/Common/debugStreamProvider.ts b/src/client/debugger/debugAdapter/Common/debugStreamProvider.ts index cd743a0d2b68..5182b4ace49e 100644 --- a/src/client/debugger/debugAdapter/Common/debugStreamProvider.ts +++ b/src/client/debugger/debugAdapter/Common/debugStreamProvider.ts @@ -30,14 +30,14 @@ export class DebugStreamProvider implements IDebugStreamProvider { if (debugPort > 0) { // This section is what allows VS Code extension developers to attach to the current debugger. // Used in scenarios where extension developers would like to debug the debugger. - debugSocket = new Promise(resolve => { + debugSocket = new Promise((resolve) => { // start as a server, and print to console in VS Code debugger for extension developer. // Do not print this out when running unit tests. if (!isTestExecution()) { // tslint:disable-next-line: no-console console.error(`waiting for debug protocol on port ${debugPort}`); } - this.server = createServer(socket => { + this.server = createServer((socket) => { if (!isTestExecution()) { // tslint:disable-next-line: no-console console.error('>> accepted connection from client'); @@ -58,7 +58,7 @@ export class DebugStreamProvider implements IDebugStreamProvider { let debugPort = 0; const args = currentProcess.argv.slice(2); - args.forEach(val => { + args.forEach((val) => { const portMatch = /^--server=(\d{4,5})$/.exec(val); if (portMatch) { debugPort = parseInt(portMatch[1], 10); diff --git a/src/client/debugger/debugAdapter/Common/protocolLogger.ts b/src/client/debugger/debugAdapter/Common/protocolLogger.ts index 4293470e3ac6..1afc11b437e2 100644 --- a/src/client/debugger/debugAdapter/Common/protocolLogger.ts +++ b/src/client/debugger/debugAdapter/Common/protocolLogger.ts @@ -42,7 +42,7 @@ export class ProtocolLogger implements IProtocolLogger { }; private logMessages(messages: string[]) { if (this.logger) { - messages.forEach(message => this.logger!.verbose(`${message}`)); + messages.forEach((message) => this.logger!.verbose(`${message}`)); } else { this.messagesToLog.push(...messages); } diff --git a/src/client/debugger/debugAdapter/DebugClients/LocalDebugClient.ts b/src/client/debugger/debugAdapter/DebugClients/LocalDebugClient.ts index ab177a45452c..dd8b772e27c3 100644 --- a/src/client/debugger/debugAdapter/DebugClients/LocalDebugClient.ts +++ b/src/client/debugger/debugAdapter/DebugClients/LocalDebugClient.ts @@ -85,7 +85,7 @@ export class LocalDebugClient extends DebugClient { case 'externalTerminal': case 'integratedTerminal': { const isSudo = - Array.isArray(this.args.debugOptions) && this.args.debugOptions.some(opt => opt === 'Sudo'); + Array.isArray(this.args.debugOptions) && this.args.debugOptions.some((opt) => opt === 'Sudo'); this.launchExternalTerminal(isSudo, processCwd, pythonPath, args, envVars) .then(resolve) .catch(reject); @@ -99,14 +99,14 @@ export class LocalDebugClient extends DebugClient { // Only once connected do we know that the application has successfully launched. this.debugServer!.DebugClientConnected.then(resolve) // tslint:disable-next-line: no-console - .catch(ex => console.error('Python Extension: debugServer.DebugClientConnected', ex)); + .catch((ex) => console.error('Python Extension: debugServer.DebugClientConnected', ex)); } } }); } // tslint:disable-next-line:member-ordering protected handleProcessOutput(proc: ChildProcess, failedToLaunch: (error: Error | string | Buffer) => void) { - proc.on('error', error => { + proc.on('error', (error) => { // If debug server has started, then don't display errors. // The debug adapter will get this info from the debugger (e.g. ptvsd lib). const status = this.debugServerStatus; @@ -121,7 +121,7 @@ export class LocalDebugClient extends DebugClient { }); proc.stderr.setEncoding('utf8'); proc.stderr.on('data', noop); - proc.stdout.on('data', _ => { + proc.stdout.on('data', (_) => { // This is necessary so we read the stdout of the python process, // Else it just keep building up (related to issue #203 and #52). // tslint:disable-next-line:prefer-const no-unused-variable @@ -161,7 +161,7 @@ export class LocalDebugClient extends DebugClient { args: [command].concat(commandArgs), env }; - this.debugSession.runInTerminalRequest(termArgs, 5000, response => { + this.debugSession.runInTerminalRequest(termArgs, 5000, (response) => { if (response.success) { resolve(); } else { @@ -170,11 +170,11 @@ export class LocalDebugClient extends DebugClient { }); } else { open({ wait: false, app: [pythonPath].concat(args), cwd, env, sudo: sudo }).then( - proc => { + (proc) => { this.pyProc = proc; resolve(); }, - error => { + (error) => { if (this.debugServerStatus === DebugServerStatus.Running) { return; } diff --git a/src/client/debugger/debugAdapter/DebugServers/LocalDebugServerV2.ts b/src/client/debugger/debugAdapter/DebugServers/LocalDebugServerV2.ts index f51f43ffe936..a17d771b5b86 100644 --- a/src/client/debugger/debugAdapter/DebugServers/LocalDebugServerV2.ts +++ b/src/client/debugger/debugAdapter/DebugServers/LocalDebugServerV2.ts @@ -42,13 +42,13 @@ export class LocalDebugServerV2 extends BaseDebugServer { const socketServer = (this.socketServer = this.serviceContainer.get(ISocketServer)); const port = await socketServer.Start({ port: this.args.port, host }); socketServer.client - .then(socket => { + .then((socket) => { // This is required to prevent the launcher from aborting if the PTVSD process spits out any errors in stderr stream. this.isRunning = true; this.debugClientConnected.resolve(true); this.clientSocket.resolve(socket); }) - .catch(ex => { + .catch((ex) => { this.debugClientConnected.reject(ex); this.clientSocket.reject(ex); }); diff --git a/src/client/debugger/debugAdapter/DebugServers/RemoteDebugServerv2.ts b/src/client/debugger/debugAdapter/DebugServers/RemoteDebugServerv2.ts index 765e83aa8fed..c09ecdae1cee 100644 --- a/src/client/debugger/debugAdapter/DebugServers/RemoteDebugServerv2.ts +++ b/src/client/debugger/debugAdapter/DebugServers/RemoteDebugServerv2.ts @@ -33,7 +33,7 @@ export class RemoteDebugServerV2 extends BaseDebugServer { try { let connected = false; const socket = new Socket(); - socket.on('error', ex => { + socket.on('error', (ex) => { if (connected) { return; } diff --git a/src/client/debugger/debugAdapter/main.ts b/src/client/debugger/debugAdapter/main.ts index 3285272808a7..c417ecce0d03 100644 --- a/src/client/debugger/debugAdapter/main.ts +++ b/src/client/debugger/debugAdapter/main.ts @@ -111,7 +111,7 @@ export class PythonDebugger extends DebugSession { this.debugServer = launcher.CreateDebugServer(this.serviceContainer); this.debugServer!.Start() .then(() => this.emit('debugger_attached')) - .catch(ex => { + .catch((ex) => { logger.error('Attach failed'); logger.error(`${ex}, ${ex.name}, ${ex.message}, ${ex.stack}`); const message = this.getUserFriendlyAttachErrorMessage(ex) || 'Attach Failed'; @@ -142,7 +142,7 @@ export class PythonDebugger extends DebugSession { this.launchPTVSD(args) .then(() => this.waitForPTVSDToConnect(args)) .then(() => this.emit('debugger_launched')) - .catch(ex => { + .catch((ex) => { const message = this.getUserFriendlyLaunchErrorMessage(args, ex) || 'Debug Error'; this.sendErrorResponse( response, @@ -291,7 +291,7 @@ class DebugManager implements Disposable { public dispose() { try { const disposables = this.serviceContainer.get(IDisposableRegistry); - disposables.forEach(d => { + disposables.forEach((d) => { try { d.dispose(); } catch { @@ -393,7 +393,7 @@ class DebugManager implements Disposable { logger.verbose('disposing'); await sleep(100); // Dispose last, we don't want to dispose the protocol loggers too early. - this.disposables.forEach(disposable => disposable.dispose()); + this.disposables.forEach((disposable) => disposable.dispose()); } }; private sendMessage( @@ -468,7 +468,7 @@ class DebugManager implements Disposable { // Do not pipe. When restarting the debugger, the socket gets closed, // In which case, VSC will see this and shutdown the debugger completely. - ((this.inputStream as any) as NodeJS.ReadStream).on('data', data => { + ((this.inputStream as any) as NodeJS.ReadStream).on('data', (data) => { this.socket.write(data); }); this.socket.on('data', (data: string | Buffer) => { @@ -563,6 +563,6 @@ process.on('uncaughtException', (err: Error) => { setTimeout(() => process.exit(-1), 100); }); -startDebugger().catch(_ex => { +startDebugger().catch((_ex) => { // Not necessary except for debugging and to kill linter warning about unhandled promises. }); diff --git a/src/client/debugger/extension/adapter/outdatedDebuggerPrompt.ts b/src/client/debugger/extension/adapter/outdatedDebuggerPrompt.ts index 8106d19f76fc..f149e0958dbd 100644 --- a/src/client/debugger/extension/adapter/outdatedDebuggerPrompt.ts +++ b/src/client/debugger/extension/adapter/outdatedDebuggerPrompt.ts @@ -24,7 +24,7 @@ class OutdatedDebuggerPrompt implements DebugAdapterTracker { const prompts = [Common.moreInfo()]; this.appShell .showInformationMessage(OutdatedDebugger.outdatedDebuggerMessage(), ...prompts) - .then(selection => { + .then((selection) => { if (selection === prompts[0]) { this.browserService.launch('https://aka.ms/migrateToDebugpy'); } diff --git a/src/client/debugger/extension/attachQuickPick/picker.ts b/src/client/debugger/extension/attachQuickPick/picker.ts index bf0c400af17e..db1d4a8a8a74 100644 --- a/src/client/debugger/extension/attachQuickPick/picker.ts +++ b/src/client/debugger/extension/attachQuickPick/picker.ts @@ -54,7 +54,7 @@ export class AttachPicker implements IAttachPicker { const selectedId = quickPick.selectedItems[0].id; - disposables.forEach(item => item.dispose()); + disposables.forEach((item) => item.dispose()); quickPick.dispose(); resolve(selectedId); @@ -65,7 +65,7 @@ export class AttachPicker implements IAttachPicker { quickPick.onDidHide( () => { - disposables.forEach(item => item.dispose()); + disposables.forEach((item) => item.dispose()); quickPick.dispose(); reject(new Error(AttachProcess.noProcessSelected())); diff --git a/src/client/debugger/extension/attachQuickPick/provider.ts b/src/client/debugger/extension/attachQuickPick/provider.ts index 6d59c83dd515..fbeb3eee34ac 100644 --- a/src/client/debugger/extension/attachQuickPick/provider.ts +++ b/src/client/debugger/extension/attachQuickPick/provider.ts @@ -19,7 +19,7 @@ export class AttachProcessProvider implements IAttachProcessProvider { ) {} public getAttachItems(): Promise { - return this._getInternalProcessEntries().then(processEntries => { + return this._getInternalProcessEntries().then((processEntries) => { processEntries.sort( ( { processName: aprocessName, commandLine: aCommandLine }, diff --git a/src/client/debugger/extension/banner.ts b/src/client/debugger/extension/banner.ts index 0f9bac3331ed..f2cea5c64fd3 100644 --- a/src/client/debugger/extension/banner.ts +++ b/src/client/debugger/extension/banner.ts @@ -163,9 +163,9 @@ export class DebuggerBanner implements IDebuggerBanner { private addCallback() { const debuggerService = this.serviceContainer.get(IDebugService); - const disposable = debuggerService.onDidTerminateDebugSession(async e => { + const disposable = debuggerService.onDidTerminateDebugSession(async (e) => { if (e.type === DebuggerTypeName) { - await this.onDidTerminateDebugSession().catch(ex => traceError('Error in debugger Banner', ex)); + await this.onDidTerminateDebugSession().catch((ex) => traceError('Error in debugger Banner', ex)); } }); this.serviceContainer.get(IDisposableRegistry).push(disposable); diff --git a/src/client/debugger/extension/configuration/providers/djangoLaunch.ts b/src/client/debugger/extension/configuration/providers/djangoLaunch.ts index 4d559910aefe..54e5a0a72f44 100644 --- a/src/client/debugger/extension/configuration/providers/djangoLaunch.ts +++ b/src/client/debugger/extension/configuration/providers/djangoLaunch.ts @@ -45,7 +45,7 @@ export class DjangoLaunchDebugConfigurationProvider implements IDebugConfigurati title: DebugConfigStrings.django.enterManagePyPath.title(), value: defaultProgram, prompt: DebugConfigStrings.django.enterManagePyPath.prompt(), - validate: value => this.validateManagePy(state.folder, defaultProgram, value) + validate: (value) => this.validateManagePy(state.folder, defaultProgram, value) }); if (selectedProgram) { manuallyEnteredAValue = true; @@ -73,12 +73,7 @@ export class DjangoLaunchDebugConfigurationProvider implements IDebugConfigurati if (selected !== defaultValue && !(await this.fs.fileExists(resolvedPath))) { return error; } - if ( - !resolvedPath - .trim() - .toLowerCase() - .endsWith('.py') - ) { + if (!resolvedPath.trim().toLowerCase().endsWith('.py')) { return error; } return; diff --git a/src/client/debugger/extension/configuration/providers/flaskLaunch.ts b/src/client/debugger/extension/configuration/providers/flaskLaunch.ts index 7add44f0eb5e..45e5ee992ce3 100644 --- a/src/client/debugger/extension/configuration/providers/flaskLaunch.ts +++ b/src/client/debugger/extension/configuration/providers/flaskLaunch.ts @@ -43,7 +43,7 @@ export class FlaskLaunchDebugConfigurationProvider implements IDebugConfiguratio title: DebugConfigStrings.flask.enterAppPathOrNamePath.title(), value: 'app.py', prompt: DebugConfigStrings.flask.enterAppPathOrNamePath.prompt(), - validate: value => + validate: (value) => Promise.resolve( value && value.trim().length > 0 ? undefined diff --git a/src/client/debugger/extension/configuration/providers/moduleLaunch.ts b/src/client/debugger/extension/configuration/providers/moduleLaunch.ts index 5925ea001cbb..e2d6db959d7d 100644 --- a/src/client/debugger/extension/configuration/providers/moduleLaunch.ts +++ b/src/client/debugger/extension/configuration/providers/moduleLaunch.ts @@ -26,7 +26,7 @@ export class ModuleLaunchDebugConfigurationProvider implements IDebugConfigurati title: DebugConfigStrings.module.enterModule.title(), value: config.module || DebugConfigStrings.module.enterModule.default(), prompt: DebugConfigStrings.module.enterModule.prompt(), - validate: value => + validate: (value) => Promise.resolve( value && value.trim().length > 0 ? undefined : DebugConfigStrings.module.enterModule.invalid() ) diff --git a/src/client/debugger/extension/configuration/providers/pyramidLaunch.ts b/src/client/debugger/extension/configuration/providers/pyramidLaunch.ts index 352c87c7e5a3..9aab8b77c583 100644 --- a/src/client/debugger/extension/configuration/providers/pyramidLaunch.ts +++ b/src/client/debugger/extension/configuration/providers/pyramidLaunch.ts @@ -48,7 +48,7 @@ export class PyramidLaunchDebugConfigurationProvider implements IDebugConfigurat title: DebugConfigStrings.pyramid.enterDevelopmentIniPath.title(), value: defaultIni, prompt: DebugConfigStrings.pyramid.enterDevelopmentIniPath.prompt(), - validate: value => this.validateIniPath(state ? state.folder : undefined, defaultIni, value) + validate: (value) => this.validateIniPath(state ? state.folder : undefined, defaultIni, value) }); if (selectedIniPath) { manuallyEnteredAValue = true; @@ -79,12 +79,7 @@ export class PyramidLaunchDebugConfigurationProvider implements IDebugConfigurat if (selected !== defaultValue && !(await this.fs.fileExists(resolvedPath))) { return error; } - if ( - !resolvedPath - .trim() - .toLowerCase() - .endsWith('.ini') - ) { + if (!resolvedPath.trim().toLowerCase().endsWith('.ini')) { return error; } } diff --git a/src/client/debugger/extension/configuration/providers/remoteAttach.ts b/src/client/debugger/extension/configuration/providers/remoteAttach.ts index 15fe166a7dd6..b777f68b18f1 100644 --- a/src/client/debugger/extension/configuration/providers/remoteAttach.ts +++ b/src/client/debugger/extension/configuration/providers/remoteAttach.ts @@ -42,7 +42,7 @@ export class RemoteAttachDebugConfigurationProvider implements IDebugConfigurati totalSteps: 2, value: config.host || defaultHost, prompt: DebugConfigStrings.attach.enterRemoteHost.prompt(), - validate: value => + validate: (value) => Promise.resolve( value && value.trim().length > 0 ? undefined : DebugConfigStrings.attach.enterRemoteHost.invalid() ) @@ -56,7 +56,7 @@ export class RemoteAttachDebugConfigurationProvider implements IDebugConfigurati manuallyEnteredAValue: config.host !== defaultHost }); Object.assign(state.config, config); - return _ => this.configurePort(input, state.config); + return (_) => this.configurePort(input, state.config); } protected async configurePort( input: MultiStepInput, @@ -68,7 +68,7 @@ export class RemoteAttachDebugConfigurationProvider implements IDebugConfigurati totalSteps: 2, value: (config.port || defaultPort).toString(), prompt: DebugConfigStrings.attach.enterRemotePort.prompt(), - validate: value => + validate: (value) => Promise.resolve( value && /^\d+$/.test(value.trim()) ? undefined diff --git a/src/client/debugger/extension/configuration/resolvers/launchConfigExperiment.ts b/src/client/debugger/extension/configuration/resolvers/launchConfigExperiment.ts index 2f2384a96677..d6416950adf0 100644 --- a/src/client/debugger/extension/configuration/resolvers/launchConfigExperiment.ts +++ b/src/client/debugger/extension/configuration/resolvers/launchConfigExperiment.ts @@ -35,7 +35,7 @@ export class LaunchDebugConfigurationExperiment implements ILaunchDebugConfigura } let argsModified: boolean = false; - const args = debugConfiguration.args.filter(arg => arg !== '--noreload' && arg !== '--no-reload'); + const args = debugConfiguration.args.filter((arg) => arg !== '--noreload' && arg !== '--no-reload'); if (args.length !== debugConfiguration.args.length) { argsModified = true; debugConfiguration.args = args; diff --git a/src/client/debugger/extension/hooks/childProcessAttachService.ts b/src/client/debugger/extension/hooks/childProcessAttachService.ts index 8804d43caf1f..2876a893850a 100644 --- a/src/client/debugger/extension/hooks/childProcessAttachService.ts +++ b/src/client/debugger/extension/hooks/childProcessAttachService.ts @@ -79,7 +79,7 @@ export class ChildProcessAttachService implements IChildProcessAttachService { if (!this.workspaceService.hasWorkspaceFolders || !workspaceFolder) { return; } - return this.workspaceService.workspaceFolders!.find(ws => ws.uri.fsPath === workspaceFolder); + return this.workspaceService.workspaceFolders!.find((ws) => ws.uri.fsPath === workspaceFolder); } private getAttachConfiguration(data: ChildProcessLaunchData): AttachRequestArguments & DebugConfiguration { const args = data.rootStartRequest.arguments; diff --git a/src/client/debugger/extension/hooks/eventHandlerDispatcher.ts b/src/client/debugger/extension/hooks/eventHandlerDispatcher.ts index 75b967ba3d03..3aac7df6663f 100644 --- a/src/client/debugger/extension/hooks/eventHandlerDispatcher.ts +++ b/src/client/debugger/extension/hooks/eventHandlerDispatcher.ts @@ -16,15 +16,15 @@ export class DebugSessionEventDispatcher { ) {} public registerEventHandlers() { this.disposables.push( - this.debugService.onDidReceiveDebugSessionCustomEvent(e => { - this.eventHandlers.forEach(handler => + this.debugService.onDidReceiveDebugSessionCustomEvent((e) => { + this.eventHandlers.forEach((handler) => handler.handleCustomEvent ? handler.handleCustomEvent(e).ignoreErrors() : undefined ); }) ); this.disposables.push( - this.debugService.onDidTerminateDebugSession(e => { - this.eventHandlers.forEach(handler => + this.debugService.onDidTerminateDebugSession((e) => { + this.eventHandlers.forEach((handler) => handler.handleTerminateEvent ? handler.handleTerminateEvent(e).ignoreErrors() : undefined ); }) diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index 4097a08127de..b2e799fbde36 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -164,7 +164,7 @@ async function activateLegacy( const workspaceService = serviceContainer.get(IWorkspaceService); interpreterManager .refresh(workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined) - .catch(ex => traceError('Python Extension: interpreterManager.refresh', ex)); + .catch((ex) => traceError('Python Extension: interpreterManager.refresh', ex)); // Activate data science features const dataScience = serviceManager.get(IDataScience); @@ -196,7 +196,7 @@ async function activateLegacy( }) ); - serviceContainer.getAll(IDebugConfigurationService).forEach(debugConfigProvider => { + serviceContainer.getAll(IDebugConfigurationService).forEach((debugConfigProvider) => { context.subscriptions.push(debug.registerDebugConfigurationProvider(DebuggerTypeName, debugConfigProvider)); }); diff --git a/src/client/formatters/baseFormatter.ts b/src/client/formatters/baseFormatter.ts index 5f5b44f68910..6bbd4c85eb5d 100644 --- a/src/client/formatters/baseFormatter.ts +++ b/src/client/formatters/baseFormatter.ts @@ -73,14 +73,14 @@ export abstract class BaseFormatter { ); const promise = pythonToolsExecutionService .exec(executionInfo, { cwd, throwOnStdErr: false, token }, document.uri) - .then(output => output.stdout) - .then(data => { + .then((output) => output.stdout) + .then((data) => { if (this.checkCancellation(document.fileName, tempFile, token)) { return [] as vscode.TextEdit[]; } return getTextEditsFromPatch(document.getText(), data); }) - .catch(error => { + .catch((error) => { if (this.checkCancellation(document.fileName, tempFile, token)) { return [] as vscode.TextEdit[]; } @@ -88,7 +88,7 @@ export abstract class BaseFormatter { this.handleError(this.Id, error, document.uri).catch(() => {}); return [] as vscode.TextEdit[]; }) - .then(edits => { + .then((edits) => { this.deleteTempFile(document.fileName, tempFile).ignoreErrors(); return edits; }); @@ -110,7 +110,7 @@ export abstract class BaseFormatter { customError += `\nYou could either install the '${this.Id}' formatter, turn it off or use another formatter.`; installer .promptToInstall(this.product, resource) - .catch(ex => traceError('Python Extension: promptToInstall', ex)); + .catch((ex) => traceError('Python Extension: promptToInstall', ex)); } } diff --git a/src/client/interpreter/activation/service.ts b/src/client/interpreter/activation/service.ts index 247b87e9ebbb..9bac975eb3dc 100644 --- a/src/client/interpreter/activation/service.ts +++ b/src/client/interpreter/activation/service.ts @@ -115,7 +115,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi } public dispose(): void { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } @traceDecorators.verbose('getActivatedEnvironmentVariables', LogOptions.Arguments) @captureTelemetry(EventName.PYTHON_INTERPRETER_ACTIVATION_ENVIRONMENT_VARIABLES, { failed: false }, true) @@ -135,7 +135,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi // Cache only if successful, else keep trying & failing if necessary. const cache = new InMemoryCache(cacheDuration, ''); - return this.getActivatedEnvironmentVariablesImpl(resource, interpreter, allowExceptions).then(vars => { + return this.getActivatedEnvironmentVariablesImpl(resource, interpreter, allowExceptions).then((vars) => { cache.data = vars; this.activatedEnvVariablesCache.set(cacheKey, cache); return vars; @@ -162,10 +162,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi if (!activationCommands || !Array.isArray(activationCommands) || activationCommands.length === 0) { return; } - isPossiblyCondaEnv = activationCommands - .join(' ') - .toLowerCase() - .includes('conda'); + isPossiblyCondaEnv = activationCommands.join(' ').toLowerCase().includes('conda'); // Run the activate command collect the environment from it. const activationCommand = this.fixActivationCommands(activationCommands).join(' && '); const processService = await this.processServiceFactory.create(resource); @@ -211,7 +208,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi // Special case. Conda for some versions will state a file is in use. If // that's the case, wait and try again. This happens especially on AzDo const excString = exc.toString(); - if (condaRetryMessages.find(m => excString.includes(m)) && tryCount < 10) { + if (condaRetryMessages.find((m) => excString.includes(m)) && tryCount < 10) { traceInfo(`Conda is busy, attempting to retry ...`); result = undefined; tryCount += 1; @@ -246,7 +243,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi protected fixActivationCommands(commands: string[]): string[] { // Replace 'source ' with '. ' as that works in shell exec - return commands.map(cmd => cmd.replace(/^source\s+/, '. ')); + return commands.map((cmd) => cmd.replace(/^source\s+/, '. ')); } @traceDecorators.error('Failed to parse Environment variables') @traceDecorators.verbose('parseEnvironmentOutput', LogOptions.None) diff --git a/src/client/interpreter/activation/wrapperEnvironmentActivationService.ts b/src/client/interpreter/activation/wrapperEnvironmentActivationService.ts index 9e2a8fff8271..b8fc7317caeb 100644 --- a/src/client/interpreter/activation/wrapperEnvironmentActivationService.ts +++ b/src/client/interpreter/activation/wrapperEnvironmentActivationService.ts @@ -118,8 +118,8 @@ export class WrapperEnvironmentActivationService implements IEnvironmentActivati // What ever result we get back, store that in file (cache it for other VSC sessions). promise - .then(env => this.writeDataToCacheFile(cacheKey, { env })) - .catch(ex => traceError('Failed to write Env Vars to disc', ex)); + .then((env) => this.writeDataToCacheFile(cacheKey, { env })) + .catch((ex) => traceError('Failed to write Env Vars to disc', ex)); } return this.cachePerResourceAndInterpreter.get(cacheKey)!; @@ -136,7 +136,7 @@ export class WrapperEnvironmentActivationService implements IEnvironmentActivati } return this.fs .readFile(cacheFile) - .then(data => JSON.parse(data) as EnvVariablesInCachedFile) + .then((data) => JSON.parse(data) as EnvVariablesInCachedFile) .catch(() => undefined); } /** @@ -179,11 +179,11 @@ export class WrapperEnvironmentActivationService implements IEnvironmentActivati return this.terminalActivation .getActivatedEnvironmentVariables(resource, interpreter, allowExceptions) - .then(vars => { + .then((vars) => { // If no variables in terminal, then revert to old approach. return vars || fallback; }) - .catch(ex => { + .catch((ex) => { // Swallow exceptions when using terminal env and revert to using old approach. traceError('Failed to get variables using Terminal Service', ex); return fallback; @@ -201,7 +201,7 @@ export class WrapperEnvironmentActivationService implements IEnvironmentActivati // Get the custom environment variables as a string (if any errors, ignore and use empty string). const customEnvVariables = await this.envVarsProvider .getCustomEnvironmentVariables(resource) - .then(item => (item ? JSON.stringify(item) : '')) + .then((item) => (item ? JSON.stringify(item) : '')) .catch(() => ''); return this.crypto.createHash( diff --git a/src/client/interpreter/autoSelection/index.ts b/src/client/interpreter/autoSelection/index.ts index c30e1cc7d029..0696901fd9cc 100644 --- a/src/client/interpreter/autoSelection/index.ts +++ b/src/client/interpreter/autoSelection/index.ts @@ -102,7 +102,7 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio await this.clearWorkspaceStoreIfInvalid(resource); await this.userDefinedInterpreter.autoSelectInterpreter(resource, this); this.didAutoSelectedInterpreterEmitter.fire(); - Promise.all(this.rules.map(item => item.autoSelectInterpreter(resource))).ignoreErrors(); + Promise.all(this.rules.map((item) => item.autoSelectInterpreter(resource))).ignoreErrors(); deferred.resolve(); } return this.autoSelectedWorkspacePromises.get(key)!.promise; diff --git a/src/client/interpreter/autoSelection/rules/cached.ts b/src/client/interpreter/autoSelection/rules/cached.ts index 5e2de40345b4..134a53203051 100644 --- a/src/client/interpreter/autoSelection/rules/cached.ts +++ b/src/client/interpreter/autoSelection/rules/cached.ts @@ -36,9 +36,9 @@ export class CachedInterpretersAutoSelectionRule extends BaseRuleService { manager?: IInterpreterAutoSelectionService ): Promise { const cachedInterpreters = this.rules - .map(item => item.getPreviouslyAutoSelectedInterpreter(resource)) - .filter(item => !!item) - .map(item => item!); + .map((item) => item.getPreviouslyAutoSelectedInterpreter(resource)) + .filter((item) => !!item) + .map((item) => item!); const bestInterpreter = this.helper.getBestInterpreter(cachedInterpreters); traceVerbose( `Selected Interpreter from ${this.ruleName}, ${ diff --git a/src/client/interpreter/autoSelection/rules/system.ts b/src/client/interpreter/autoSelection/rules/system.ts index 8c151525620f..9feb4925b9a4 100644 --- a/src/client/interpreter/autoSelection/rules/system.ts +++ b/src/client/interpreter/autoSelection/rules/system.ts @@ -28,7 +28,7 @@ export class SystemWideInterpretersAutoSelectionRule extends BaseRuleService { const interpreters = await this.interpreterService.getInterpreters(resource); // Exclude non-local interpreters. const filteredInterpreters = interpreters.filter( - int => + (int) => int.type !== InterpreterType.VirtualEnv && int.type !== InterpreterType.Venv && int.type !== InterpreterType.Pipenv diff --git a/src/client/interpreter/autoSelection/rules/workspaceEnv.ts b/src/client/interpreter/autoSelection/rules/workspaceEnv.ts index 88d4aa573e43..b98aabe26ae0 100644 --- a/src/client/interpreter/autoSelection/rules/workspaceEnv.ts +++ b/src/client/interpreter/autoSelection/rules/workspaceEnv.ts @@ -102,7 +102,7 @@ export class WorkspaceVirtualEnvInterpretersAutoSelectionRule extends BaseRuleSe ? workspaceFolder.uri.fsPath.toUpperCase() : workspaceFolder.uri.fsPath; - return interpreters.filter(interpreter => { + return interpreters.filter((interpreter) => { const fsPath = Uri.file(interpreter.path).fsPath; const fsPathToCompare = this.platform.osType === OSType.Windows ? fsPath.toUpperCase() : fsPath; return fsPathToCompare.startsWith(workspacePath); diff --git a/src/client/interpreter/configuration/interpreterSelector.ts b/src/client/interpreter/configuration/interpreterSelector.ts index a6a8cacad00e..93a859a94483 100644 --- a/src/client/interpreter/configuration/interpreterSelector.ts +++ b/src/client/interpreter/configuration/interpreterSelector.ts @@ -34,7 +34,7 @@ export class InterpreterSelector implements IInterpreterSelector { @inject(ICommandManager) private readonly commandManager: ICommandManager ) {} public dispose() { - this.disposables.forEach(disposable => disposable.dispose()); + this.disposables.forEach((disposable) => disposable.dispose()); } public initialize() { @@ -49,7 +49,7 @@ export class InterpreterSelector implements IInterpreterSelector { public async getSuggestions(resource: Resource) { const interpreters = await this.interpreterManager.getInterpreters(resource); interpreters.sort(this.interpreterComparer.compare.bind(this.interpreterComparer)); - return Promise.all(interpreters.map(item => this.suggestionToQuickPickItem(item, resource))); + return Promise.all(interpreters.map((item) => this.suggestionToQuickPickItem(item, resource))); } protected async suggestionToQuickPickItem( suggestion: PythonInterpreter, diff --git a/src/client/interpreter/configuration/pythonPathUpdaterService.ts b/src/client/interpreter/configuration/pythonPathUpdaterService.ts index 699a5509fdb4..d750fb3782fe 100644 --- a/src/client/interpreter/configuration/pythonPathUpdaterService.ts +++ b/src/client/interpreter/configuration/pythonPathUpdaterService.ts @@ -42,7 +42,7 @@ export class PythonPathUpdaterService implements IPythonPathUpdaterServiceManage traceError(reason); } // do not wait for this to complete - this.sendTelemetry(stopWatch.elapsedTime, failed, trigger, pythonPath).catch(ex => + this.sendTelemetry(stopWatch.elapsedTime, failed, trigger, pythonPath).catch((ex) => traceError('Python Extension: sendTelemetry', ex) ); } @@ -63,7 +63,7 @@ export class PythonPathUpdaterService implements IPythonPathUpdaterServiceManage .catch(() => undefined); const pipVersionPromise = this.interpreterVersionService .getPipVersion(pythonPath) - .then(value => (value.length === 0 ? undefined : value)) + .then((value) => (value.length === 0 ? undefined : value)) .catch(() => ''); const [info, pipVersion] = await Promise.all([infoPromise, pipVersionPromise]); if (info && info.version) { diff --git a/src/client/interpreter/display/shebangCodeLensProvider.ts b/src/client/interpreter/display/shebangCodeLensProvider.ts index 0193eea9af91..c4e74db09b98 100644 --- a/src/client/interpreter/display/shebangCodeLensProvider.ts +++ b/src/client/interpreter/display/shebangCodeLensProvider.ts @@ -42,15 +42,15 @@ export class ShebangCodeLensProvider implements IShebangCodeLensProvider { // In case we have pythonPath as '/usr/bin/env python'. const parts = pythonPath .split(' ') - .map(part => part.trim()) - .filter(part => part.length > 0); + .map((part) => part.trim()) + .filter((part) => part.length > 0); cmdFile = parts.shift()!; args = parts.concat(args); } const processService = await this.processServiceFactory.create(resource); return processService .exec(cmdFile, args) - .then(output => output.stdout.trim()) + .then((output) => output.stdout.trim()) .catch(() => ''); } private async createShebangCodeLens(document: TextDocument) { diff --git a/src/client/interpreter/helpers.ts b/src/client/interpreter/helpers.ts index 69278c6aa839..0cfb575265e9 100644 --- a/src/client/interpreter/helpers.ts +++ b/src/client/interpreter/helpers.ts @@ -19,8 +19,8 @@ export function getFirstNonEmptyLineFromMultilineString(stdout: string) { } const lines = stdout .split(/\r?\n/g) - .map(line => line.trim()) - .filter(line => line.length > 0); + .map((line) => line.trim()) + .filter((line) => line.length > 0); return lines.length > 0 ? lines[0] : ''; } @@ -60,8 +60,8 @@ export class InterpreterHelper implements IInterpreterHelper { public async getInterpreterInformation(pythonPath: string): Promise> { const fileHash = await this.hashProviderFactory .create({ pythonPath }) - .then(provider => provider.getInterpreterHash(pythonPath)) - .catch(ex => { + .then((provider) => provider.getInterpreterHash(pythonPath)) + .catch((ex) => { traceError(`Failed to create File hash for interpreter ${pythonPath}`, ex); return ''; }); diff --git a/src/client/interpreter/interpreterService.ts b/src/client/interpreter/interpreterService.ts index 3380f127b789..c8b961c3b7e7 100644 --- a/src/client/interpreter/interpreterService.ts +++ b/src/client/interpreter/interpreterService.ts @@ -69,12 +69,12 @@ export class InterpreterService implements Disposable, IInterpreterService { const disposables = this.serviceContainer.get(IDisposableRegistry); const documentManager = this.serviceContainer.get(IDocumentManager); disposables.push( - documentManager.onDidChangeActiveTextEditor(e => (e ? this.refresh(e.document.uri) : undefined)) + documentManager.onDidChangeActiveTextEditor((e) => (e ? this.refresh(e.document.uri) : undefined)) ); const workspaceService = this.serviceContainer.get(IWorkspaceService); const pySettings = this.configService.getSettings(); this.pythonPathSetting = pySettings.pythonPath; - const disposable = workspaceService.onDidChangeConfiguration(e => { + const disposable = workspaceService.onDidChangeConfiguration((e) => { if (e.affectsConfiguration('python.pythonPath', undefined)) { this.onConfigChanged(); } @@ -87,8 +87,8 @@ export class InterpreterService implements Disposable, IInterpreterService { const interpreters = await this.locator.getInterpreters(resource); await Promise.all( interpreters - .filter(item => !item.displayName) - .map(async item => { + .filter((item) => !item.displayName) + .map(async (item) => { item.displayName = await this.getDisplayName(item, resource); // Keep information up to date with latest details. if (!item.cachedEntry) { @@ -155,7 +155,7 @@ export class InterpreterService implements Disposable, IInterpreterService { // This is the preferred approach, hence the delay in option 1. const option2 = (async () => { const interpreters = await this.getInterpreters(resource); - const found = interpreters.find(i => fs.arePathsSame(i.path, pythonPath)); + const found = interpreters.find((i) => fs.arePathsSame(i.path, pythonPath)); if (found) { // Cache the interpreter info, only if we get the data from interpretr list. // tslint:disable-next-line:no-any @@ -232,7 +232,7 @@ export class InterpreterService implements Disposable, IInterpreterService { protected async getInterepreterFileHash(pythonPath: string): Promise { return this.hashProviderFactory .create({ pythonPath }) - .then(provider => provider.getInterpreterHash(pythonPath)); + .then((provider) => provider.getInterpreterHash(pythonPath)); } protected async updateCachedInterpreterInformation(info: PythonInterpreter, resource: Resource): Promise { const key = JSON.stringify(info); @@ -288,7 +288,7 @@ export class InterpreterService implements Disposable, IInterpreterService { this.pythonPathSetting = pySettings.pythonPath; this.didChangeInterpreterEmitter.fire(); const interpreterDisplay = this.serviceContainer.get(IInterpreterDisplay); - interpreterDisplay.refresh().catch(ex => traceError('Python Extension: display.refresh', ex)); + interpreterDisplay.refresh().catch((ex) => traceError('Python Extension: display.refresh', ex)); } }; private async collectInterpreterDetails(pythonPath: string, resource: Uri | undefined) { diff --git a/src/client/interpreter/interpreterVersion.ts b/src/client/interpreter/interpreterVersion.ts index 93fde7efa808..b68fcfb78988 100644 --- a/src/client/interpreter/interpreterVersion.ts +++ b/src/client/interpreter/interpreterVersion.ts @@ -12,8 +12,8 @@ export class InterpreterVersionService implements IInterpreterVersionService { const processService = await this.processServiceFactory.create(); return processService .exec(pythonPath, ['--version'], { mergeStdOutErr: true }) - .then(output => output.stdout.splitLines()[0]) - .then(version => (version.length === 0 ? defaultValue : version)) + .then((output) => output.stdout.splitLines()[0]) + .then((version) => (version.length === 0 ? defaultValue : version)) .catch(() => defaultValue); } public async getPipVersion(pythonPath: string): Promise { diff --git a/src/client/interpreter/locators/helpers.ts b/src/client/interpreter/locators/helpers.ts index 62b996b53de9..cad2a24d2035 100644 --- a/src/client/interpreter/locators/helpers.ts +++ b/src/client/interpreter/locators/helpers.ts @@ -15,7 +15,7 @@ export async function lookForInterpretersInDirectory(pathToCheck: string, fs: IF const subDirs = await fs.listdir(pathToCheck); return subDirs .map(([filename, _ft]) => filename) - .filter(fileName => CheckPythonInterpreterRegEx.test(path.basename(fileName))); + .filter((fileName) => CheckPythonInterpreterRegEx.test(path.basename(fileName))); } catch (err) { traceError('Python Extension (lookForInterpretersInDirectory.fs.listdir):', err); return [] as string[]; @@ -30,16 +30,16 @@ export class InterpreterLocatorHelper implements IInterpreterLocatorHelper { ) {} public async mergeInterpreters(interpreters: PythonInterpreter[]): Promise { const items = interpreters - .map(item => { + .map((item) => { return { ...item }; }) - .map(item => { + .map((item) => { item.path = path.normalize(item.path); return item; }) .reduce((accumulator, current) => { const currentVersion = current && current.version ? current.version.raw : undefined; - const existingItem = accumulator.find(item => { + const existingItem = accumulator.find((item) => { // If same version and same base path, then ignore. // Could be Python 3.6 with path = python.exe, and Python 3.6 and path = python3.exe. if ( @@ -81,7 +81,7 @@ export class InterpreterLocatorHelper implements IInterpreterLocatorHelper { }, []); // This stuff needs to be fast. await Promise.all( - items.map(async item => { + items.map(async (item) => { const info = await this.pipEnvServiceHelper.getPipEnvInfo(item.path); if (info) { item.type = InterpreterType.Pipenv; diff --git a/src/client/interpreter/locators/index.ts b/src/client/interpreter/locators/index.ts index 0a1dcc92e37a..01eb171a96aa 100644 --- a/src/client/interpreter/locators/index.ts +++ b/src/client/interpreter/locators/index.ts @@ -63,7 +63,7 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi * Called by VS Code to indicate it is done with the resource. */ public dispose() { - this.disposables.forEach(disposable => disposable.dispose()); + this.disposables.forEach((disposable) => disposable.dispose()); } /** @@ -75,10 +75,10 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi @traceDecorators.verbose('Get Interpreters') public async getInterpreters(resource?: Uri): Promise { const locators = this.getLocators(); - const promises = locators.map(async provider => provider.getInterpreters(resource)); - locators.forEach(locator => { + const promises = locators.map(async (provider) => provider.getInterpreters(resource)); + locators.forEach((locator) => { locator.hasInterpreters - .then(found => { + .then((found) => { if (found) { this._hasInterpreters.resolve(true); } @@ -88,9 +88,9 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi const listOfInterpreters = await Promise.all(promises); const items = flatten(listOfInterpreters) - .filter(item => !!item) - .map(item => item!) - .filter(item => !this.interpreterFilter.isHiddenInterpreter(item)); + .filter((item) => !!item) + .map((item) => item!) + .filter((item) => !this.interpreterFilter.isHiddenInterpreter(item)); this._hasInterpreters.resolve(items.length > 0); return this.interpreterLocatorHelper.mergeInterpreters(items); } @@ -116,7 +116,7 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi [CURRENT_PATH_SERVICE, undefined] ]; return keys - .filter(item => item[1] === undefined || item[1] === this.platform.osType) - .map(item => this.serviceContainer.get(IInterpreterLocatorService, item[0])); + .filter((item) => item[1] === undefined || item[1] === this.platform.osType) + .map((item) => this.serviceContainer.get(IInterpreterLocatorService, item[0])); } } diff --git a/src/client/interpreter/locators/progressService.ts b/src/client/interpreter/locators/progressService.ts index 72d0c98ed00b..c6a4533d34e3 100644 --- a/src/client/interpreter/locators/progressService.ts +++ b/src/client/interpreter/locators/progressService.ts @@ -32,7 +32,7 @@ export class InterpreterLocatorProgressService implements IInterpreterLocatorPro return this.refreshed.event; } public register(): void { - this.locators.forEach(locator => { + this.locators.forEach((locator) => { locator.onLocating(this.handleProgress, this, this.disposables); }); } @@ -57,14 +57,14 @@ export class InterpreterLocatorProgressService implements IInterpreterLocatorPro if (this.areAllItemsComplete()) { return this.notifyCompleted(); } - Promise.all(this.deferreds.map(item => item.promise)) + Promise.all(this.deferreds.map((item) => item.promise)) .catch(noop) .then(() => this.checkProgress()) .ignoreErrors(); } @traceDecorators.verbose('Checking whether locactors have completed locating') private areAllItemsComplete() { - this.deferreds = this.deferreds.filter(item => !item.completed); + this.deferreds = this.deferreds.filter((item) => !item.completed); return this.deferreds.length === 0; } } diff --git a/src/client/interpreter/locators/services/KnownPathsService.ts b/src/client/interpreter/locators/services/KnownPathsService.ts index 74ae3bcf1228..83cde725c3b8 100644 --- a/src/client/interpreter/locators/services/KnownPathsService.ts +++ b/src/client/interpreter/locators/services/KnownPathsService.ts @@ -49,12 +49,16 @@ export class KnownPathsService extends CacheableLocatorService { * Return the located interpreters. */ private suggestionsFromKnownPaths() { - const promises = this.knownSearchPaths.getSearchPaths().map(dir => this.getInterpretersInDirectory(dir)); + const promises = this.knownSearchPaths.getSearchPaths().map((dir) => this.getInterpretersInDirectory(dir)); return Promise.all(promises) - .then(listOfInterpreters => flatten(listOfInterpreters)) - .then(interpreters => interpreters.filter(item => item.length > 0)) - .then(interpreters => Promise.all(interpreters.map(interpreter => this.getInterpreterDetails(interpreter)))) - .then(interpreters => interpreters.filter(interpreter => !!interpreter).map(interpreter => interpreter!)); + .then((listOfInterpreters) => flatten(listOfInterpreters)) + .then((interpreters) => interpreters.filter((item) => item.length > 0)) + .then((interpreters) => + Promise.all(interpreters.map((interpreter) => this.getInterpreterDetails(interpreter))) + ) + .then((interpreters) => + interpreters.filter((interpreter) => !!interpreter).map((interpreter) => interpreter!) + ); } /** @@ -80,7 +84,7 @@ export class KnownPathsService extends CacheableLocatorService { const fs = this.serviceContainer.get(IFileSystem); return fs .directoryExists(dir) - .then(exists => (exists ? lookForInterpretersInDirectory(dir, fs) : Promise.resolve([]))); + .then((exists) => (exists ? lookForInterpretersInDirectory(dir, fs) : Promise.resolve([]))); } } @@ -96,11 +100,11 @@ export class KnownSearchPathsForInterpreters implements IKnownSearchPathsForInte const pathUtils = this.serviceContainer.get(IPathUtils); const searchPaths = currentProcess.env[platformService.pathVariableName]!.split(pathUtils.delimiter) - .map(p => p.trim()) - .filter(p => p.length > 0); + .map((p) => p.trim()) + .filter((p) => p.length > 0); if (!platformService.isWindows) { - ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/sbin'].forEach(p => { + ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/sbin'].forEach((p) => { searchPaths.push(p); searchPaths.push(path.join(pathUtils.home, p)); }); diff --git a/src/client/interpreter/locators/services/baseVirtualEnvService.ts b/src/client/interpreter/locators/services/baseVirtualEnvService.ts index 00502c37b820..950b28e5dbc9 100644 --- a/src/client/interpreter/locators/services/baseVirtualEnvService.ts +++ b/src/client/interpreter/locators/services/baseVirtualEnvService.ts @@ -41,21 +41,23 @@ export class BaseVirtualEnvService extends CacheableLocatorService { private async suggestionsFromKnownVenvs(resource?: Uri) { const searchPaths = await this.searchPathsProvider.getSearchPaths(resource); return Promise.all( - searchPaths.map(dir => this.lookForInterpretersInVenvs(dir, resource)) - ).then(listOfInterpreters => flatten(listOfInterpreters)); + searchPaths.map((dir) => this.lookForInterpretersInVenvs(dir, resource)) + ).then((listOfInterpreters) => flatten(listOfInterpreters)); } private async lookForInterpretersInVenvs(pathToCheck: string, resource?: Uri) { return this.fileSystem .getSubDirectories(pathToCheck) - .then(subDirs => Promise.all(this.getProspectiveDirectoriesForLookup(subDirs))) - .then(dirs => dirs.filter(dir => dir.length > 0)) - .then(dirs => Promise.all(dirs.map(d => lookForInterpretersInDirectory(d, this.fileSystem)))) - .then(pathsWithInterpreters => flatten(pathsWithInterpreters)) - .then(interpreters => - Promise.all(interpreters.map(interpreter => this.getVirtualEnvDetails(interpreter, resource))) + .then((subDirs) => Promise.all(this.getProspectiveDirectoriesForLookup(subDirs))) + .then((dirs) => dirs.filter((dir) => dir.length > 0)) + .then((dirs) => Promise.all(dirs.map((d) => lookForInterpretersInDirectory(d, this.fileSystem)))) + .then((pathsWithInterpreters) => flatten(pathsWithInterpreters)) + .then((interpreters) => + Promise.all(interpreters.map((interpreter) => this.getVirtualEnvDetails(interpreter, resource))) ) - .then(interpreters => interpreters.filter(interpreter => !!interpreter).map(interpreter => interpreter!)) - .catch(err => { + .then((interpreters) => + interpreters.filter((interpreter) => !!interpreter).map((interpreter) => interpreter!) + ) + .catch((err) => { traceError('Python Extension (lookForInterpretersInVenvs):', err); // Ignore exceptions. return [] as PythonInterpreter[]; @@ -64,17 +66,17 @@ export class BaseVirtualEnvService extends CacheableLocatorService { private getProspectiveDirectoriesForLookup(subDirs: string[]) { const platform = this.serviceContainer.get(IPlatformService); const dirToLookFor = platform.virtualEnvBinName; - return subDirs.map(subDir => + return subDirs.map((subDir) => this.fileSystem .getSubDirectories(subDir) - .then(dirs => { - const scriptOrBinDirs = dirs.filter(dir => { + .then((dirs) => { + const scriptOrBinDirs = dirs.filter((dir) => { const folderName = path.basename(dir); return this.fileSystem.arePathsSame(folderName, dirToLookFor); }); return scriptOrBinDirs.length === 1 ? scriptOrBinDirs[0] : ''; }) - .catch(err => { + .catch((err) => { traceError('Python Extension (getProspectiveDirectoriesForLookup):', err); // Ignore exceptions. return ''; diff --git a/src/client/interpreter/locators/services/cacheableLocatorService.ts b/src/client/interpreter/locators/services/cacheableLocatorService.ts index d30625dc81f2..3071371d5a76 100644 --- a/src/client/interpreter/locators/services/cacheableLocatorService.ts +++ b/src/client/interpreter/locators/services/cacheableLocatorService.ts @@ -93,7 +93,7 @@ export abstract class CacheableLocatorService implements IInterpreterLocatorServ const stopWatch = new StopWatch(); this.getInterpretersImplementation(resource) - .then(async items => { + .then(async (items) => { await this.cacheInterpreters(items, resource); traceVerbose( `Interpreters returned by ${this.name} are of count ${Array.isArray(items) ? items.length : 0}` @@ -105,7 +105,7 @@ export abstract class CacheableLocatorService implements IInterpreterLocatorServ }); deferred!.resolve(items); }) - .catch(ex => { + .catch((ex) => { sendTelemetryEvent( EventName.PYTHON_INTERPRETER_DISCOVERY, stopWatch.elapsedTime, @@ -118,8 +118,8 @@ export abstract class CacheableLocatorService implements IInterpreterLocatorServ this.locating.fire(deferred.promise); } deferred.promise - .then(items => this._hasInterpreters.resolve(items.length > 0)) - .catch(_ => this._hasInterpreters.resolve(false)); + .then((items) => this._hasInterpreters.resolve(items.length > 0)) + .catch((_) => this._hasInterpreters.resolve(false)); if (deferred.completed) { return deferred.promise; @@ -135,7 +135,7 @@ export abstract class CacheableLocatorService implements IInterpreterLocatorServ this.handlersAddedToResource.add(cacheKey); const watchers = await this.getInterpreterWatchers(resource); const disposableRegisry = this.serviceContainer.get(IDisposableRegistry); - watchers.forEach(watcher => { + watchers.forEach((watcher) => { watcher.onDidCreate( () => { traceVerbose(`Interpreter Watcher change handler for ${this.cacheKeyPrefix}`); @@ -166,7 +166,7 @@ export abstract class CacheableLocatorService implements IInterpreterLocatorServ if (!Array.isArray(persistence.value)) { return; } - return persistence.value.map(item => { + return persistence.value.map((item) => { return { ...item, cachedEntry: true diff --git a/src/client/interpreter/locators/services/condaEnvFileService.ts b/src/client/interpreter/locators/services/condaEnvFileService.ts index e8f134c00961..010090da5b1e 100644 --- a/src/client/interpreter/locators/services/condaEnvFileService.ts +++ b/src/client/interpreter/locators/services/condaEnvFileService.ts @@ -58,7 +58,7 @@ export class CondaEnvFileService extends CacheableLocatorService { } return this.fileSystem .fileExists(this.condaService.condaEnvironmentsFile!) - .then(exists => + .then((exists) => exists ? this.getEnvironmentsFromFile(this.condaService.condaEnvironmentsFile!) : Promise.resolve([]) ); } @@ -71,19 +71,21 @@ export class CondaEnvFileService extends CacheableLocatorService { const fileContents = await this.fileSystem.readFile(envFile); const environmentPaths = fileContents .split(/\r?\n/g) - .map(environmentPath => environmentPath.trim()) - .filter(environmentPath => environmentPath.length > 0); + .map((environmentPath) => environmentPath.trim()) + .filter((environmentPath) => environmentPath.length > 0); const interpreters = ( - await Promise.all(environmentPaths.map(environmentPath => this.getInterpreterDetails(environmentPath))) + await Promise.all( + environmentPaths.map((environmentPath) => this.getInterpreterDetails(environmentPath)) + ) ) - .filter(item => !!item) - .map(item => item!); + .filter((item) => !!item) + .map((item) => item!); const environments = await this.condaService.getCondaEnvironments(true); if (Array.isArray(environments) && environments.length > 0) { - interpreters.forEach(interpreter => { - const environment = environments.find(item => + interpreters.forEach((interpreter) => { + const environment = environments.find((item) => this.fileSystem.arePathsSame(item.path, interpreter!.envPath!) ); if (environment) { diff --git a/src/client/interpreter/locators/services/condaEnvService.ts b/src/client/interpreter/locators/services/condaEnvService.ts index 2fb53e5342b9..479a095d9ed4 100644 --- a/src/client/interpreter/locators/services/condaEnvService.ts +++ b/src/client/interpreter/locators/services/condaEnvService.ts @@ -54,8 +54,8 @@ export class CondaEnvService extends CacheableLocatorService { this._hasInterpreters.resolve(interpreters.length > 0); const environments = await this.condaService.getCondaEnvironments(true); if (Array.isArray(environments) && environments.length > 0) { - interpreters.forEach(interpreter => { - const environment = environments.find(item => + interpreters.forEach((interpreter) => { + const environment = environments.find((item) => this.fileSystem.arePathsSame(item.path, interpreter!.envPath!) ); if (environment) { @@ -93,7 +93,7 @@ export async function parseCondaInfo( envs.push(info.default_prefix); } - const promises = envs.map(async envPath => { + const promises = envs.map(async (envPath) => { const pythonPath = condaService.getInterpreterPath(envPath); if (!(await fileSystem.fileExists(pythonPath))) { @@ -115,8 +115,10 @@ export async function parseCondaInfo( return ( Promise.all(promises) - .then(interpreters => interpreters.filter(interpreter => interpreter !== null && interpreter !== undefined)) + .then((interpreters) => + interpreters.filter((interpreter) => interpreter !== null && interpreter !== undefined) + ) // tslint:disable-next-line:no-non-null-assertion - .then(interpreters => interpreters.map(interpreter => interpreter!)) + .then((interpreters) => interpreters.map((interpreter) => interpreter!)) ); } diff --git a/src/client/interpreter/locators/services/condaHelper.ts b/src/client/interpreter/locators/services/condaHelper.ts index 5df512427976..d2fccba4f1bc 100644 --- a/src/client/interpreter/locators/services/condaHelper.ts +++ b/src/client/interpreter/locators/services/condaHelper.ts @@ -58,13 +58,13 @@ export class CondaHelper { */ public parseCondaEnvironmentNames(condaEnvironmentList: string): { name: string; path: string }[] | undefined { const environments = condaEnvironmentList.splitLines({ trim: false }); - const baseEnvironmentLine = environments.filter(line => line.indexOf('*') > 0); + const baseEnvironmentLine = environments.filter((line) => line.indexOf('*') > 0); if (baseEnvironmentLine.length === 0) { return; } const pathStartIndex = baseEnvironmentLine[0].indexOf(baseEnvironmentLine[0].split('*')[1].trim()); const envs: { name: string; path: string }[] = []; - environments.forEach(line => { + environments.forEach((line) => { if (line.length <= pathStartIndex) { return; } @@ -86,6 +86,6 @@ export class CondaHelper { */ private isIdentifiableAsAnaconda(value: string) { const valueToSearch = value.toLowerCase(); - return AnacondaIdentifiers.some(item => valueToSearch.indexOf(item.toLowerCase()) !== -1); + return AnacondaIdentifiers.some((item) => valueToSearch.indexOf(item.toLowerCase()) !== -1); } } diff --git a/src/client/interpreter/locators/services/condaService.ts b/src/client/interpreter/locators/services/condaService.ts index 7d9e2e881117..dc607acf078d 100644 --- a/src/client/interpreter/locators/services/condaService.ts +++ b/src/client/interpreter/locators/services/condaService.ts @@ -109,7 +109,7 @@ export class CondaService implements ICondaService { return this.isAvailable; } return this.getCondaVersion() - .then(version => (this.isAvailable = version !== undefined)) + .then((version) => (this.isAvailable = version !== undefined)) .catch(() => (this.isAvailable = false)); } @@ -129,8 +129,8 @@ export class CondaService implements ICondaService { versionString = info.conda_version; } else { const stdOut = await this.getCondaFile() - .then(condaFile => processService.exec(condaFile, ['--version'], {})) - .then(result => result.stdout.trim()) + .then((condaFile) => processService.exec(condaFile, ['--version'], {})) + .then((result) => result.stdout.trim()) .catch(() => undefined); versionString = stdOut && stdOut.startsWith('conda ') ? stdOut.substring('conda '.length).trim() : stdOut; @@ -154,7 +154,7 @@ export class CondaService implements ICondaService { const processService = await this.processServiceFactory.create(); return processService .exec('conda', ['--version']) - .then(output => output.stdout.length > 0) + .then((output) => output.stdout.length > 0) .catch(() => false); } @@ -167,7 +167,7 @@ export class CondaService implements ICondaService { try { const condaFile = await this.getCondaFile(); const processService = await this.processServiceFactory.create(); - const condaInfo = await processService.exec(condaFile, ['info', '--json']).then(output => output.stdout); + const condaInfo = await processService.exec(condaFile, ['info', '--json']).then((output) => output.stdout); return JSON.parse(condaInfo) as CondaInfo; } catch (ex) { @@ -209,12 +209,12 @@ export class CondaService implements ICondaService { // From the list of conda environments find this dir. let matchingEnvs = Array.isArray(environments) - ? environments.filter(item => this.fileSystem.arePathsSame(item.path, interpreterPathToMatch)) + ? environments.filter((item) => this.fileSystem.arePathsSame(item.path, interpreterPathToMatch)) : []; if (matchingEnvs.length === 0) { environments = await this.getCondaEnvironments(true); matchingEnvs = Array.isArray(environments) - ? environments.filter(item => this.fileSystem.arePathsSame(item.path, interpreterPathToMatch)) + ? environments.filter((item) => this.fileSystem.arePathsSame(item.path, interpreterPathToMatch)) : []; } @@ -243,7 +243,7 @@ export class CondaService implements ICondaService { try { const condaFile = await this.getCondaFile(); const processService = await this.processServiceFactory.create(); - let envInfo = await processService.exec(condaFile, ['env', 'list']).then(output => output.stdout); + let envInfo = await processService.exec(condaFile, ['env', 'list']).then((output) => output.stdout); traceVerbose(`Conda Env List ${envInfo}}`); if (!envInfo) { traceVerbose('Conda env list failure, attempting path additions.'); @@ -258,7 +258,7 @@ export class CondaService implements ICondaService { traceVerbose(`Attempting new path for conda env list: ${newEnv.PATH}`); envInfo = await processService .exec(condaFile, ['env', 'list'], { env: newEnv }) - .then(output => output.stdout); + .then((output) => output.stdout); } const environments = this.condaHelper.parseCondaEnvironmentNames(envInfo); await globalPersistence.updateValue({ data: environments }); @@ -357,9 +357,9 @@ export class CondaService implements ICondaService { } private async onDidChangeConfiguration(event: ConfigurationChangeEvent) { const workspacesUris: (Uri | undefined)[] = this.workspaceService.hasWorkspaceFolders - ? this.workspaceService.workspaceFolders!.map(workspace => workspace.uri) + ? this.workspaceService.workspaceFolders!.map((workspace) => workspace.uri) : [undefined]; - if (workspacesUris.findIndex(uri => event.affectsConfiguration('python.condaPath', uri)) === -1) { + if (workspacesUris.findIndex((uri) => event.affectsConfiguration('python.condaPath', uri)) === -1) { return; } this.condaFile = undefined; @@ -403,14 +403,14 @@ export class CondaService implements ICondaService { */ private async getCondaFileFromKnownLocations(): Promise { const globPattern = this.platform.isWindows ? CondaLocationsGlobWin : CondaLocationsGlob; - const condaFiles = await this.fileSystem.search(globPattern).catch(failReason => { + const condaFiles = await this.fileSystem.search(globPattern).catch((failReason) => { traceWarning( 'Default conda location search failed.', `Searching for default install locations for conda results in error: ${failReason}` ); return []; }); - const validCondaFiles = condaFiles.filter(condaPath => condaPath.length > 0); + const validCondaFiles = condaFiles.filter((condaPath) => condaPath.length > 0); return validCondaFiles.length === 0 ? 'conda' : validCondaFiles[0]; } } diff --git a/src/client/interpreter/locators/services/currentPathService.ts b/src/client/interpreter/locators/services/currentPathService.ts index 8608ae8d44b8..e644df7c6949 100644 --- a/src/client/interpreter/locators/services/currentPathService.ts +++ b/src/client/interpreter/locators/services/currentPathService.ts @@ -57,15 +57,15 @@ export class CurrentPathService extends CacheableLocatorService { .getSettings(resource); const pathsToCheck = [...this.pythonCommandProvider.getCommands(), { command: configSettings.pythonPath }]; - const pythonPaths = Promise.all(pathsToCheck.map(item => this.getInterpreter(item))); + const pythonPaths = Promise.all(pathsToCheck.map((item) => this.getInterpreter(item))); return ( pythonPaths - .then(interpreters => interpreters.filter(item => item.length > 0)) + .then((interpreters) => interpreters.filter((item) => item.length > 0)) // tslint:disable-next-line:promise-function-async - .then(interpreters => - Promise.all(interpreters.map(interpreter => this.getInterpreterDetails(interpreter))) + .then((interpreters) => + Promise.all(interpreters.map((interpreter) => this.getInterpreterDetails(interpreter))) ) - .then(interpreters => interpreters.filter(item => !!item).map(item => item!)) + .then((interpreters) => interpreters.filter((item) => !!item).map((item) => item!)) ); } @@ -73,7 +73,7 @@ export class CurrentPathService extends CacheableLocatorService { * Return the information about the identified interpreter binary. */ private async getInterpreterDetails(pythonPath: string): Promise { - return this.helper.getInterpreterInformation(pythonPath).then(details => { + return this.helper.getInterpreterInformation(pythonPath).then((details) => { if (!details) { return; } @@ -95,8 +95,8 @@ export class CurrentPathService extends CacheableLocatorService { const args = Array.isArray(options.args) ? options.args : []; return processService .exec(options.command, args.concat(['-c', 'import sys;print(sys.executable)']), {}) - .then(output => output.stdout.trim()) - .then(async value => { + .then((output) => output.stdout.trim()) + .then(async (value) => { if (value.length > 0 && (await this.fs.fileExists(value))) { return value; } @@ -107,7 +107,7 @@ export class CurrentPathService extends CacheableLocatorService { ); return ''; }) - .catch(_ex => { + .catch((_ex) => { traceInfo( `Detection of Python Interpreter for Command ${options.command} and args ${args.join( ' ' @@ -126,7 +126,7 @@ export class CurrentPathService extends CacheableLocatorService { export class PythonInPathCommandProvider implements IPythonInPathCommandProvider { constructor(@inject(IPlatformService) private readonly platform: IPlatformService) {} public getCommands(): { command: string; args?: string[] }[] { - const paths = ['python3.7', 'python3.6', 'python3', 'python2', 'python'].map(item => { + const paths = ['python3.7', 'python3.6', 'python3', 'python2', 'python'].map((item) => { return { command: item }; }); if (this.platform.osType !== OSType.Windows) { @@ -135,7 +135,7 @@ export class PythonInPathCommandProvider implements IPythonInPathCommandProvider const versions = ['3.7', '3.6', '3', '2']; return paths.concat( - versions.map(version => { + versions.map((version) => { return { command: 'py', args: [`-${version}`] }; }) ); diff --git a/src/client/interpreter/locators/services/globalVirtualEnvService.ts b/src/client/interpreter/locators/services/globalVirtualEnvService.ts index adf96538d792..1073ad97ae70 100644 --- a/src/client/interpreter/locators/services/globalVirtualEnvService.ts +++ b/src/client/interpreter/locators/services/globalVirtualEnvService.ts @@ -49,7 +49,7 @@ export class GlobalVirtualEnvironmentsSearchPathProvider implements IVirtualEnvi '.virtualenvs', ...this.config.getSettings(resource).venvFolders ]; - const folders = [...new Set(venvFolders.map(item => path.join(homedir, item)))]; + const folders = [...new Set(venvFolders.map((item) => path.join(homedir, item)))]; // Add support for the WORKON_HOME environment variable used by pipenv and virtualenvwrapper. const workonHomePath = this.currentProcess.env.WORKON_HOME; diff --git a/src/client/interpreter/locators/services/pipEnvService.ts b/src/client/interpreter/locators/services/pipEnvService.ts index b9a6c7c38526..4de14e4de07c 100644 --- a/src/client/interpreter/locators/services/pipEnvService.ts +++ b/src/client/interpreter/locators/services/pipEnvService.ts @@ -56,7 +56,7 @@ export class PipEnvService extends CacheableLocatorService implements IPipEnvSer } return this.getInterpreterFromPipenv(pipenvCwd) - .then(item => (item ? [item] : [])) + .then((item) => (item ? [item] : [])) .catch(() => []); } diff --git a/src/client/interpreter/locators/services/pipEnvServiceHelper.ts b/src/client/interpreter/locators/services/pipEnvServiceHelper.ts index bf3f71363149..a8f2e09f00ab 100644 --- a/src/client/interpreter/locators/services/pipEnvServiceHelper.ts +++ b/src/client/interpreter/locators/services/pipEnvServiceHelper.ts @@ -26,12 +26,12 @@ export class PipEnvServiceHelper implements IPipEnvServiceHelper { } public async getPipEnvInfo(pythonPath: string): Promise<{ workspaceFolder: Uri; envName: string } | undefined> { await this.initializeStateStore(); - const info = this.state.value.find(item => this.fs.arePathsSame(item.pythonPath, pythonPath)); + const info = this.state.value.find((item) => this.fs.arePathsSame(item.pythonPath, pythonPath)); return info ? { workspaceFolder: Uri.file(info.workspaceFolder), envName: info.envName } : undefined; } public async trackWorkspaceFolder(pythonPath: string, workspaceFolder: Uri): Promise { await this.initializeStateStore(); - const values = [...this.state.value].filter(item => !this.fs.arePathsSame(item.pythonPath, pythonPath)); + const values = [...this.state.value].filter((item) => !this.fs.arePathsSame(item.pythonPath, pythonPath)); const envName = path.basename(workspaceFolder.fsPath); values.push({ pythonPath, workspaceFolder: workspaceFolder.fsPath, envName }); await this.state.updateValue(values); @@ -41,9 +41,9 @@ export class PipEnvServiceHelper implements IPipEnvServiceHelper { return; } const list = await Promise.all( - this.state.value.map(async item => ((await this.fs.fileExists(item.pythonPath)) ? item : undefined)) + this.state.value.map(async (item) => ((await this.fs.fileExists(item.pythonPath)) ? item : undefined)) ); - const filteredList = list.filter(item => !!item) as PipEnvInformation[]; + const filteredList = list.filter((item) => !!item) as PipEnvInformation[]; await this.state.updateValue(filteredList); this.initialized = true; } diff --git a/src/client/interpreter/locators/services/windowsRegistryService.ts b/src/client/interpreter/locators/services/windowsRegistryService.ts index a9b91bd0b3f3..512d08a35a47 100644 --- a/src/client/interpreter/locators/services/windowsRegistryService.ts +++ b/src/client/interpreter/locators/services/windowsRegistryService.ts @@ -65,19 +65,19 @@ export class WindowsRegistryService extends CacheableLocatorService { const companies = await Promise.all(promises); const companyInterpreters = await Promise.all( flatten(companies) - .filter(item => item !== undefined && item !== null) - .map(company => { + .filter((item) => item !== undefined && item !== null) + .map((company) => { return this.getInterpretersForCompany(company.companyKey, company.hive, company.arch); }) ); return ( flatten(companyInterpreters) - .filter(item => item !== undefined && item !== null) + .filter((item) => item !== undefined && item !== null) // tslint:disable-next-line:no-non-null-assertion - .map(item => item!) + .map((item) => item!) .reduce((prev, current) => { - if (prev.findIndex(item => item.path.toUpperCase() === current.path.toUpperCase()) === -1) { + if (prev.findIndex((item) => item.path.toUpperCase() === current.path.toUpperCase()) === -1) { prev.push(current); } return prev; @@ -85,12 +85,12 @@ export class WindowsRegistryService extends CacheableLocatorService { ); } private async getCompanies(hive: RegistryHive, arch?: Architecture): Promise { - return this.registry.getKeys('\\Software\\Python', hive, arch).then(companyKeys => + return this.registry.getKeys('\\Software\\Python', hive, arch).then((companyKeys) => companyKeys .filter( - companyKey => CompaniesToIgnore.indexOf(this.pathUtils.basename(companyKey).toUpperCase()) === -1 + (companyKey) => CompaniesToIgnore.indexOf(this.pathUtils.basename(companyKey).toUpperCase()) === -1 ) - .map(companyKey => { + .map((companyKey) => { return { companyKey, hive, arch }; }) ); @@ -98,7 +98,7 @@ export class WindowsRegistryService extends CacheableLocatorService { private async getInterpretersForCompany(companyKey: string, hive: RegistryHive, arch?: Architecture) { const tagKeys = await this.registry.getKeys(companyKey, hive, arch); return Promise.all( - tagKeys.map(tagKey => this.getInreterpreterDetailsForCompany(tagKey, companyKey, hive, arch)) + tagKeys.map((tagKey) => this.getInreterpreterDetailsForCompany(tagKey, companyKey, hive, arch)) ); } private getInreterpreterDetailsForCompany( @@ -120,7 +120,7 @@ export class WindowsRegistryService extends CacheableLocatorService { }; return this.registry .getValue(key, hive, arch) - .then(installPath => { + .then((installPath) => { // Install path is mandatory. if (!installPath) { return Promise.resolve(null); @@ -182,15 +182,15 @@ export class WindowsRegistryService extends CacheableLocatorService { : InterpreterType.Unknown } as PythonInterpreter; }) - .then(interpreter => + .then((interpreter) => interpreter ? this.fs .fileExists(interpreter.path) .catch(() => false) - .then(exists => (exists ? interpreter : null)) + .then((exists) => (exists ? interpreter : null)) : null ) - .catch(error => { + .catch((error) => { traceError( `Failed to retrieve interpreter details for company ${companyKey},tag: ${tagKey}, hive: ${hive}, arch: ${arch}`, error diff --git a/src/client/interpreter/locators/services/workspaceVirtualEnvWatcherService.ts b/src/client/interpreter/locators/services/workspaceVirtualEnvWatcherService.ts index cb1359851273..bd7e1ab53ea0 100644 --- a/src/client/interpreter/locators/services/workspaceVirtualEnvWatcherService.ts +++ b/src/client/interpreter/locators/services/workspaceVirtualEnvWatcherService.ts @@ -53,7 +53,7 @@ export class WorkspaceVirtualEnvWatcherService implements IInterpreterWatcher, D traceVerbose(`Create file systemwatcher with pattern ${pattern}`); const fsWatcher = this.workspaceService.createFileSystemWatcher(globPatern); - fsWatcher.onDidCreate(e => this.createHandler(e), this, this.disposableRegistry); + fsWatcher.onDidCreate((e) => this.createHandler(e), this, this.disposableRegistry); this.disposableRegistry.push(fsWatcher); this.fsWatchers.push(fsWatcher); @@ -90,7 +90,7 @@ export class WorkspaceVirtualEnvWatcherService implements IInterpreterWatcher, D } private clearTimers() { // tslint:disable-next-line: no-any - this.timers.forEach(item => clearTimeout(item.timer as any)); + this.timers.forEach((item) => clearTimeout(item.timer as any)); this.timers.clear(); } private async isValidExecutable(pythonPath: string): Promise { diff --git a/src/client/interpreter/virtualEnvs/index.ts b/src/client/interpreter/virtualEnvs/index.ts index 621ac0de6fd8..fb3f7940ef7b 100644 --- a/src/client/interpreter/virtualEnvs/index.ts +++ b/src/client/interpreter/virtualEnvs/index.ts @@ -66,7 +66,7 @@ export class VirtualEnvironmentManager implements IVirtualEnvironmentManager { } public async isVenvEnvironment(pythonPath: string) { const dir = path.dirname(pythonPath); - const pyEnvCfgFiles = PYENVFILES.map(file => path.join(dir, file)); + const pyEnvCfgFiles = PYENVFILES.map((file) => path.join(dir, file)); for (const file of pyEnvCfgFiles) { if (await this.fs.fileExists(file)) { return true; @@ -115,8 +115,8 @@ export class VirtualEnvironmentManager implements IVirtualEnvironmentManager { public async isVirtualEnvironment(pythonPath: string) { const provider = this.getTerminalActivationProviderForVirtualEnvs(); const shells = getNamesAndValues(TerminalShellType) - .filter(shell => provider.isShellSupported(shell.value)) - .map(shell => shell.value); + .filter((shell) => provider.isShellSupported(shell.value)) + .map((shell) => shell.value); for (const shell of shells) { const cmds = await provider.getActivationCommandsForInterpreter!(pythonPath, shell); diff --git a/src/client/ioc/serviceManager.ts b/src/client/ioc/serviceManager.ts index 0c1bb8eb9452..f72800cf29e8 100644 --- a/src/client/ioc/serviceManager.ts +++ b/src/client/ioc/serviceManager.ts @@ -17,16 +17,13 @@ export class ServiceManager implements IServiceManager { bindings?: symbol[] ): void { if (name) { - this.container - .bind(serviceIdentifier) - .to(constructor) - .whenTargetNamed(name); + this.container.bind(serviceIdentifier).to(constructor).whenTargetNamed(name); } else { this.container.bind(serviceIdentifier).to(constructor); } if (bindings) { - bindings.forEach(binding => { + bindings.forEach((binding) => { this.addBinding(serviceIdentifier, binding); }); } @@ -50,20 +47,13 @@ export class ServiceManager implements IServiceManager { bindings?: symbol[] ): void { if (name) { - this.container - .bind(serviceIdentifier) - .to(constructor) - .inSingletonScope() - .whenTargetNamed(name); + this.container.bind(serviceIdentifier).to(constructor).inSingletonScope().whenTargetNamed(name); } else { - this.container - .bind(serviceIdentifier) - .to(constructor) - .inSingletonScope(); + this.container.bind(serviceIdentifier).to(constructor).inSingletonScope(); } if (bindings) { - bindings.forEach(binding => { + bindings.forEach((binding) => { this.addBinding(serviceIdentifier, binding); }); } @@ -75,10 +65,7 @@ export class ServiceManager implements IServiceManager { name?: string | number | symbol | undefined ): void { if (name) { - this.container - .bind(serviceIdentifier) - .toConstantValue(instance) - .whenTargetNamed(name); + this.container.bind(serviceIdentifier).toConstantValue(instance).whenTargetNamed(name); } else { this.container.bind(serviceIdentifier).toConstantValue(instance); } @@ -98,10 +85,7 @@ export class ServiceManager implements IServiceManager { name?: string | number | symbol ): void { if (name) { - this.container - .rebind(serviceIdentifier) - .to(constructor) - .whenTargetNamed(name); + this.container.rebind(serviceIdentifier).to(constructor).whenTargetNamed(name); } else { this.container.rebind(serviceIdentifier).to(constructor); } @@ -113,10 +97,7 @@ export class ServiceManager implements IServiceManager { name?: string | number | symbol ): void { if (name) { - this.container - .rebind(serviceIdentifier) - .toConstantValue(instance) - .whenTargetNamed(name); + this.container.rebind(serviceIdentifier).toConstantValue(instance).whenTargetNamed(name); } else { this.container.rebind(serviceIdentifier).toConstantValue(instance); } diff --git a/src/client/language/tokenizer.ts b/src/client/language/tokenizer.ts index 7f1de57cf3d6..f51ceaa350c7 100644 --- a/src/client/language/tokenizer.ts +++ b/src/client/language/tokenizer.ts @@ -416,10 +416,7 @@ export class Tokenizer implements ITokenizer { } if (this.cs.lookAhead(2) === Char.SingleQuote || this.cs.lookAhead(2) === Char.DoubleQuote) { - const prefix = this.cs - .getText() - .substr(this.cs.position, 2) - .toLowerCase(); + const prefix = this.cs.getText().substr(this.cs.position, 2).toLowerCase(); switch (prefix) { case 'rf': case 'ur': diff --git a/src/client/languageServices/jediProxyFactory.ts b/src/client/languageServices/jediProxyFactory.ts index 450d95195961..a03a456011fe 100644 --- a/src/client/languageServices/jediProxyFactory.ts +++ b/src/client/languageServices/jediProxyFactory.ts @@ -18,7 +18,7 @@ export class JediFactory implements Disposable { } public dispose() { - this.disposables.forEach(disposable => disposable.dispose()); + this.disposables.forEach((disposable) => disposable.dispose()); this.disposables = []; } public getJediProxyHandler(resource?: Uri): JediProxyHandler { diff --git a/src/client/linters/bandit.ts b/src/client/linters/bandit.ts index 066245c67b07..137d95d48275 100644 --- a/src/client/linters/bandit.ts +++ b/src/client/linters/bandit.ts @@ -29,7 +29,7 @@ export class Bandit extends BaseLinter { cancellation ); - messages.forEach(msg => { + messages.forEach((msg) => { msg.severity = severityMapping[msg.type]; }); return messages; diff --git a/src/client/linters/baseLinter.ts b/src/client/linters/baseLinter.ts index c08e5a601712..3799e31660b3 100644 --- a/src/client/linters/baseLinter.ts +++ b/src/client/linters/baseLinter.ts @@ -176,7 +176,7 @@ export abstract class BaseLinter implements ILinter { } else { this.errorHandler .handleError(error, resource, execInfo) - .catch(ex => traceError('Error in errorHandler.handleError', ex)) + .catch((ex) => traceError('Error in errorHandler.handleError', ex)) .ignoreErrors(); } } diff --git a/src/client/linters/errorHandlers/notInstalled.ts b/src/client/linters/errorHandlers/notInstalled.ts index eae05c8dc4d0..16871e7ee71f 100644 --- a/src/client/linters/errorHandlers/notInstalled.ts +++ b/src/client/linters/errorHandlers/notInstalled.ts @@ -21,7 +21,7 @@ export class NotInstalledErrorHandler extends BaseErrorHandler { this.installer .promptToInstall(this.product, resource) - .catch(ex => traceError('NotInstalledErrorHandler.promptToInstall', ex)); + .catch((ex) => traceError('NotInstalledErrorHandler.promptToInstall', ex)); const linterManager = this.serviceContainer.get(ILinterManager); const info = linterManager.getLinterInfo(execInfo.product!); diff --git a/src/client/linters/flake8.ts b/src/client/linters/flake8.ts index d91c63a13f5d..5d2cd0d4e6c1 100644 --- a/src/client/linters/flake8.ts +++ b/src/client/linters/flake8.ts @@ -18,7 +18,7 @@ export class Flake8 extends BaseLinter { document, cancellation ); - messages.forEach(msg => { + messages.forEach((msg) => { msg.severity = this.parseMessagesSeverity(msg.type, this.pythonSettings.linting.flake8CategorySeverity); // flake8 uses 0th line for some file-wide problems // but diagnostics expects positive line numbers. diff --git a/src/client/linters/linterCommands.ts b/src/client/linters/linterCommands.ts index 2503cc35bdbb..3f7f11dcc206 100644 --- a/src/client/linters/linterCommands.ts +++ b/src/client/linters/linterCommands.ts @@ -29,12 +29,12 @@ export class LinterCommands implements IDisposable { commandManager.registerCommand(Commands.Run_Linter, this.runLinting.bind(this)); } public dispose() { - this.disposables.forEach(disposable => disposable.dispose()); + this.disposables.forEach((disposable) => disposable.dispose()); } public async setLinterAsync(): Promise { const linters = this.linterManager.getAllLinterInfos(); - const suggestions = linters.map(x => x.id).sort(); + const suggestions = linters.map((x) => x.id).sort(); const linterList = ['Disable Linting', ...suggestions]; const activeLinters = await this.linterManager.getActiveLinters(true, this.settingsUri); @@ -63,7 +63,7 @@ export class LinterCommands implements IDisposable { await this.linterManager.enableLintingAsync(false); sendTelemetryEvent(EventName.SELECT_LINTER, undefined, { enabled: false }); } else { - const index = linters.findIndex(x => x.id === selection); + const index = linters.findIndex((x) => x.id === selection); if (activeLinters.length > 1) { const response = await this.appShell.showWarningMessage( Linters.replaceWithSelectedLinter().format(selection), diff --git a/src/client/linters/linterManager.ts b/src/client/linters/linterManager.ts index d6f52fa227a6..bfe9db05e692 100644 --- a/src/client/linters/linterManager.ts +++ b/src/client/linters/linterManager.ts @@ -80,14 +80,14 @@ export class LinterManager implements ILinterManager { if (!silent) { await this.enableUnconfiguredLinters(resource); } - return this.linters.filter(x => x.isEnabled(resource)); + return this.linters.filter((x) => x.isEnabled(resource)); } public async setActiveLintersAsync(products: Product[], resource?: Uri): Promise { // ensure we only allow valid linters to be set, otherwise leave things alone. // filter out any invalid products: - const validProducts = products.filter(product => { - const foundIndex = this.linters.findIndex(validLinter => validLinter.product === product); + const validProducts = products.filter((product) => { + const foundIndex = this.linters.findIndex((validLinter) => validLinter.product === product); return foundIndex !== -1; }); @@ -98,7 +98,7 @@ export class LinterManager implements ILinterManager { await x.enableAsync(false, resource); } if (products.length > 0) { - const toActivate = this.linters.filter(x => products.findIndex(p => x.product === p) >= 0); + const toActivate = this.linters.filter((x) => products.findIndex((p) => x.product === p) >= 0); for (const x of toActivate) { await x.enableAsync(true, resource); } @@ -154,7 +154,7 @@ export class LinterManager implements ILinterManager { this.checkedForInstalledLinters.add(workspaceKey); // only check & ask the user if they'd like to enable pylint - const pylintInfo = this.linters.find(linter => linter.id === 'pylint'); + const pylintInfo = this.linters.find((linter) => linter.id === 'pylint'); const activator = this.serviceContainer.get(IAvailableLinterActivator); await activator.promptIfLinterAvailable(pylintInfo!, resource); } diff --git a/src/client/linters/lintingEngine.ts b/src/client/linters/lintingEngine.ts index 60bbb7f83ff5..920080f24012 100644 --- a/src/client/linters/lintingEngine.ts +++ b/src/client/linters/lintingEngine.ts @@ -59,7 +59,7 @@ export class LintingEngine implements ILintingEngine { public async lintOpenPythonFiles(): Promise { this.diagnosticCollection.clear(); - const promises = this.documents.textDocuments.map(async document => this.lintDocument(document, 'auto')); + const promises = this.documents.textDocuments.map(async (document) => this.lintDocument(document, 'auto')); await Promise.all(promises); return this.diagnosticCollection; } @@ -142,7 +142,7 @@ export class LintingEngine implements ILintingEngine { } private isDocumentOpen(uri: vscode.Uri): boolean { - return this.documents.textDocuments.some(document => document.uri.fsPath === uri.fsPath); + return this.documents.textDocuments.some((document) => document.uri.fsPath === uri.fsPath); } private createDiagnostics(message: ILintMessage, _document: vscode.TextDocument): vscode.Diagnostic { @@ -176,8 +176,10 @@ export class LintingEngine implements ILintingEngine { const settings = this.configurationService.getSettings(document.uri); // { dot: true } is important so dirs like `.venv` will be matched by globs - const ignoreMinmatches = settings.linting.ignorePatterns.map(pattern => new Minimatch(pattern, { dot: true })); - if (ignoreMinmatches.some(matcher => matcher.match(document.fileName) || matcher.match(relativeFileName))) { + const ignoreMinmatches = settings.linting.ignorePatterns.map( + (pattern) => new Minimatch(pattern, { dot: true }) + ); + if (ignoreMinmatches.some((matcher) => matcher.match(document.fileName) || matcher.match(relativeFileName))) { return false; } if (document.uri.scheme !== 'file' || !document.uri.fsPath) { diff --git a/src/client/linters/mypy.ts b/src/client/linters/mypy.ts index 7c8559881a42..eff5c71be37a 100644 --- a/src/client/linters/mypy.ts +++ b/src/client/linters/mypy.ts @@ -14,7 +14,7 @@ export class MyPy extends BaseLinter { protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { const messages = await this.run([document.uri.fsPath], document, cancellation, REGEX); - messages.forEach(msg => { + messages.forEach((msg) => { msg.severity = this.parseMessagesSeverity(msg.type, this.pythonSettings.linting.mypyCategorySeverity); msg.code = msg.type; }); diff --git a/src/client/linters/prospector.ts b/src/client/linters/prospector.ts index 912b025eed9c..2341b49e155b 100644 --- a/src/client/linters/prospector.ts +++ b/src/client/linters/prospector.ts @@ -46,7 +46,7 @@ export class Prospector extends BaseLinter { } return parsedData.messages .filter((_value, index) => index <= this.pythonSettings.linting.maxNumberOfProblems) - .map(msg => { + .map((msg) => { const lineNumber = msg.location.line === null || isNaN(msg.location.line) ? 1 : msg.location.line; return { diff --git a/src/client/linters/pycodestyle.ts b/src/client/linters/pycodestyle.ts index 457613bbe6a5..2d425ab29b18 100644 --- a/src/client/linters/pycodestyle.ts +++ b/src/client/linters/pycodestyle.ts @@ -18,7 +18,7 @@ export class Pycodestyle extends BaseLinter { document, cancellation ); - messages.forEach(msg => { + messages.forEach((msg) => { msg.severity = this.parseMessagesSeverity( msg.type, this.pythonSettings.linting.pycodestyleCategorySeverity diff --git a/src/client/linters/pydocstyle.ts b/src/client/linters/pydocstyle.ts index f9c64ec120e1..718e8b359b21 100644 --- a/src/client/linters/pydocstyle.ts +++ b/src/client/linters/pydocstyle.ts @@ -16,7 +16,7 @@ export class PyDocStyle extends BaseLinter { protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { const messages = await this.run([document.uri.fsPath], document, cancellation); // All messages in pep8 are treated as warnings for now. - messages.forEach(msg => { + messages.forEach((msg) => { msg.severity = LintMessageSeverity.Warning; }); @@ -31,7 +31,7 @@ export class PyDocStyle extends BaseLinter { // So we have two lines per message, hence we need to take lines in pairs. const maxLines = this.pythonSettings.linting.maxNumberOfProblems * 2; // First line is almost always empty. - const oldOutputLines = outputLines.filter(line => line.length > 0); + const oldOutputLines = outputLines.filter((line) => line.length > 0); outputLines = []; for (let counter = 0; counter < oldOutputLines.length / 2; counter += 1) { outputLines.push(oldOutputLines[2 * counter] + oldOutputLines[2 * counter + 1]); @@ -40,7 +40,7 @@ export class PyDocStyle extends BaseLinter { return ( outputLines .filter((value, index) => index < maxLines && value.indexOf(':') >= 0) - .map(line => { + .map((line) => { // Windows will have a : after the drive letter (e.g. c:\). if (IS_WINDOWS) { return line.substring(line.indexOf(`${baseFileName}:`) + baseFileName.length + 1).trim(); @@ -49,7 +49,7 @@ export class PyDocStyle extends BaseLinter { }) // Iterate through the lines (skipping the messages). // So, just iterate the response in pairs. - .map(line => { + .map((line) => { try { if (line.trim().length === 0) { return; @@ -77,8 +77,8 @@ export class PyDocStyle extends BaseLinter { return; } }) - .filter(item => item !== undefined) - .map(item => item!) + .filter((item) => item !== undefined) + .map((item) => item!) ); } } diff --git a/src/client/linters/pylama.ts b/src/client/linters/pylama.ts index adc1a83547e8..c1159b2d9544 100644 --- a/src/client/linters/pylama.ts +++ b/src/client/linters/pylama.ts @@ -17,7 +17,7 @@ export class PyLama extends BaseLinter { protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { const messages = await this.run(['--format=parsable', document.uri.fsPath], document, cancellation, REGEX); // All messages in pylama are treated as warnings for now. - messages.forEach(msg => { + messages.forEach((msg) => { msg.severity = LintMessageSeverity.Warning; }); diff --git a/src/client/linters/pylint.ts b/src/client/linters/pylint.ts index 5ba33a9a69b3..cf3474427dd7 100644 --- a/src/client/linters/pylint.ts +++ b/src/client/linters/pylint.ts @@ -80,7 +80,7 @@ export class Pylint extends BaseLinter { uri.fsPath ]; const messages = await this.run(minArgs.concat(args), document, cancellation, REGEX); - messages.forEach(msg => { + messages.forEach((msg) => { msg.severity = this.parseMessagesSeverity(msg.type, settings.linting.pylintCategorySeverity); }); diff --git a/src/client/providers/codeActionProvider/launchJsonCodeActionProvider.ts b/src/client/providers/codeActionProvider/launchJsonCodeActionProvider.ts index e3cb8f90ec60..805614794bd7 100644 --- a/src/client/providers/codeActionProvider/launchJsonCodeActionProvider.ts +++ b/src/client/providers/codeActionProvider/launchJsonCodeActionProvider.ts @@ -18,8 +18,8 @@ import { export class LaunchJsonCodeActionProvider implements CodeActionProvider { public provideCodeActions(document: TextDocument, _: Range, context: CodeActionContext): CodeAction[] { return context.diagnostics - .filter(diagnostic => diagnostic.message === 'Incorrect type. Expected "string".') - .map(diagnostic => this.createFix(document, diagnostic)); + .filter((diagnostic) => diagnostic.message === 'Incorrect type. Expected "string".') + .map((diagnostic) => this.createFix(document, diagnostic)); } private createFix(document: TextDocument, diagnostic: Diagnostic): CodeAction { diff --git a/src/client/providers/completionSource.ts b/src/client/providers/completionSource.ts index 405adb4a6268..e135ba18be7b 100644 --- a/src/client/providers/completionSource.ts +++ b/src/client/providers/completionSource.ts @@ -103,7 +103,7 @@ export class CompletionSource { resource: vscode.Uri ): vscode.CompletionItem[] { return data && data.items.length > 0 - ? data.items.map(item => this.toVsCodeCompletion(documentPosition, item, resource)) + ? data.items.map((item) => this.toVsCodeCompletion(documentPosition, item, resource)) : []; } diff --git a/src/client/providers/definitionProvider.ts b/src/client/providers/definitionProvider.ts index 0bdbb54d9609..9b3d8f1a7ce6 100644 --- a/src/client/providers/definitionProvider.ts +++ b/src/client/providers/definitionProvider.ts @@ -10,7 +10,7 @@ export class PythonDefinitionProvider implements vscode.DefinitionProvider { public constructor(private jediFactory: JediFactory) {} private static parseData(data: proxy.IDefinitionResult, possibleWord: string): vscode.Definition | undefined { if (data && Array.isArray(data.definitions) && data.definitions.length > 0) { - const definitions = data.definitions.filter(d => d.text === possibleWord); + const definitions = data.definitions.filter((d) => d.text === possibleWord); const definition = definitions.length > 0 ? definitions[0] : data.definitions[data.definitions.length - 1]; const definitionResource = vscode.Uri.file(definition.fileName); const range = new vscode.Range( diff --git a/src/client/providers/formatProvider.ts b/src/client/providers/formatProvider.ts index ae15fc03d74b..cd4d8f3956c0 100644 --- a/src/client/providers/formatProvider.ts +++ b/src/client/providers/formatProvider.ts @@ -42,7 +42,7 @@ export class PythonFormattingEditProvider this.config = serviceContainer.get(IConfigurationService); const interpreterService = serviceContainer.get(IInterpreterService); this.disposables.push( - this.documentManager.onDidSaveTextDocument(async document => this.onSaveDocument(document)) + this.documentManager.onDidSaveTextDocument(async (document) => this.onSaveDocument(document)) ); this.disposables.push( interpreterService.onDidChangeInterpreter(async () => { @@ -54,7 +54,7 @@ export class PythonFormattingEditProvider } public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } public provideDocumentFormattingEdits( diff --git a/src/client/providers/hoverProvider.ts b/src/client/providers/hoverProvider.ts index f789fad61bcd..13a7ed7e205f 100644 --- a/src/client/providers/hoverProvider.ts +++ b/src/client/providers/hoverProvider.ts @@ -21,7 +21,7 @@ export class PythonHoverProvider implements vscode.HoverProvider { ): Promise { const itemInfos = await this.itemInfoSource.getItemInfoFromDocument(document, position, token); if (itemInfos) { - return new vscode.Hover(itemInfos.map(item => item.tooltip)); + return new vscode.Hover(itemInfos.map((item) => item.tooltip)); } } } diff --git a/src/client/providers/itemInfoSource.ts b/src/client/providers/itemInfoSource.ts index 560cdc068bc3..b7a38a65a884 100644 --- a/src/client/providers/itemInfoSource.ts +++ b/src/client/providers/itemInfoSource.ts @@ -118,7 +118,7 @@ export class ItemInfoSource implements IItemInfoSource { private getItemInfoFromHoverResult(data: proxy.IHoverResult, currentWord: string): LanguageItemInfo[] { const infos: LanguageItemInfo[] = []; - data.items.forEach(item => { + data.items.forEach((item) => { const signature = this.getSignature(item, currentWord); let tooltip = new vscode.MarkdownString(); if (item.docstring) { @@ -127,7 +127,7 @@ export class ItemInfoSource implements IItemInfoSource { // If the docstring starts with the signature, then remove those lines from the docstring. if (lines.length > 0 && item.signature.indexOf(lines[0]) === 0) { lines.shift(); - const endIndex = lines.findIndex(line => item.signature.endsWith(line)); + const endIndex = lines.findIndex((line) => item.signature.endsWith(line)); if (endIndex >= 0) { lines = lines.filter((_line, index) => index > endIndex); } diff --git a/src/client/providers/jediProxy.ts b/src/client/providers/jediProxy.ts index 01fe1b3a8221..10f32d2b4b04 100644 --- a/src/client/providers/jediProxy.ts +++ b/src/client/providers/jediProxy.ts @@ -234,7 +234,7 @@ export class JediProxy implements Disposable { // keep track of the directory so we can re-spawn the process. private initialize(): Promise { - return this.spawnProcess(path.join(this.extensionRootDir, 'pythonFiles')).catch(ex => { + return this.spawnProcess(path.join(this.extensionRootDir, 'pythonFiles')).catch((ex) => { if (this.languageServerStarted) { this.languageServerStarted.reject(ex); } @@ -344,7 +344,7 @@ export class JediProxy implements Disposable { private clearPendingRequests() { this.commandQueue = []; - this.commands.forEach(item => { + this.commands.forEach((item) => { if (item.deferred !== undefined) { item.deferred.resolve(); } @@ -387,10 +387,10 @@ export class JediProxy implements Disposable { const result = pythonProcess.execObservable(args, { cwd }); this.proc = result.proc; this.languageServerStarted.resolve(); - this.proc!.on('end', end => { + this.proc!.on('end', (end) => { traceError('spawnProcess.end', `End - ${end}`); }); - this.proc!.on('error', error => { + this.proc!.on('error', (error) => { this.handleError('error', `${error}`); this.spawnRetryAttempts += 1; if ( @@ -399,7 +399,7 @@ export class JediProxy implements Disposable { error.message && error.message.indexOf('This socket has been ended by the other party') >= 0 ) { - this.spawnProcess(cwd).catch(ex => { + this.spawnProcess(cwd).catch((ex) => { if (this.languageServerStarted) { this.languageServerStarted.reject(ex); } @@ -408,7 +408,7 @@ export class JediProxy implements Disposable { } }); result.out.subscribe( - output => { + (output) => { if (output.source === 'stderr') { this.handleError('stderr', output.out); } else { @@ -419,7 +419,7 @@ export class JediProxy implements Disposable { // tslint:disable-next-line:no-any let responses: any[]; try { - responses = dataStr.splitLines().map(resp => JSON.parse(resp)); + responses = dataStr.splitLines().map((resp) => JSON.parse(resp)); this.previousData = ''; } catch (ex) { // Possible we've only received part of the data, hence don't clear previousData. @@ -434,7 +434,7 @@ export class JediProxy implements Disposable { return; } - responses.forEach(response => { + responses.forEach((response) => { if (!response) { return; } @@ -473,7 +473,7 @@ export class JediProxy implements Disposable { }); } }, - error => this.handleError('subscription.error', `${error}`) + (error) => this.handleError('subscription.error', `${error}`) ); } private getCommandHandler( @@ -499,7 +499,7 @@ export class JediProxy implements Disposable { private onCompletion(command: IExecutionCommand, response: object): void { let results = JediProxy.getProperty(response, 'results'); results = Array.isArray(results) ? results : []; - results.forEach(item => { + results.forEach((item) => { // tslint:disable-next-line:no-any const originalType = (item.type); item.type = getMappedVSCodeType(originalType); @@ -521,7 +521,7 @@ export class JediProxy implements Disposable { definitions: [] }; if (defs.length > 0) { - defResult.definitions = defs.map(def => { + defResult.definitions = defs.map((def) => { const originalType = def.type as string; return { fileName: def.fileName, @@ -547,7 +547,7 @@ export class JediProxy implements Disposable { const defs = JediProxy.getProperty(response, 'results'); const defResult: IHoverResult = { requestId: command.id, - items: defs.map(def => { + items: defs.map((def) => { return { kind: getMappedVSCodeSymbol(def.type), description: def.description, @@ -568,7 +568,7 @@ export class JediProxy implements Disposable { requestId: command.id, definitions: [] }; - defResults.definitions = defs.map(def => { + defResults.definitions = defs.map((def) => { const originalType = def.type as string; return { fileName: def.fileName, @@ -594,7 +594,7 @@ export class JediProxy implements Disposable { defs = Array.isArray(defs) ? defs : []; const refResult: IReferenceResult = { requestId: command.id, - references: defs.map(item => { + references: defs.map((item) => { return { columnIndex: item.column, fileName: item.fileName, @@ -620,7 +620,7 @@ export class JediProxy implements Disposable { private checkQueueLength(): void { if (this.commandQueue.length > 10) { const items = this.commandQueue.splice(0, this.commandQueue.length - 10); - items.forEach(id => { + items.forEach((id) => { if (this.commands.has(id)) { const cmd1 = this.commands.get(id); try { @@ -679,7 +679,7 @@ export class JediProxy implements Disposable { this.getPathFromPythonCommand(['-c', 'import sys;print(sys.prefix)']).catch(() => ''), // exeucutable path. this.getPathFromPythonCommand(['-c', 'import sys;print(sys.executable)']) - .then(execPath => path.dirname(execPath)) + .then((execPath) => path.dirname(execPath)) .catch(() => ''), // Python specific site packages. // On windows we also need the libs path (second item will return c:\xxx\lib\site-packages). @@ -688,7 +688,7 @@ export class JediProxy implements Disposable { '-c', 'from distutils.sysconfig import get_python_lib; print(get_python_lib())' ]) - .then(libPath => { + .then((libPath) => { // On windows we also need the libs path (second item will return c:\xxx\lib\site-packages). // This is returned by "from distutils.sysconfig import get_python_lib; print(get_python_lib())". return IS_WINDOWS && libPath.length > 0 ? path.join(libPath, '..') : libPath; @@ -701,18 +701,18 @@ export class JediProxy implements Disposable { try { const pythonPaths = await this.getEnvironmentVariablesProvider() .getEnvironmentVariables(Uri.file(this.workspacePath)) - .then(customEnvironmentVars => + .then((customEnvironmentVars) => customEnvironmentVars ? JediProxy.getProperty(customEnvironmentVars, 'PYTHONPATH') : '' ) - .then(pythonPath => + .then((pythonPath) => typeof pythonPath === 'string' && pythonPath.trim().length > 0 ? pythonPath.trim() : '' ) - .then(pythonPath => pythonPath.split(path.delimiter).filter(item => item.trim().length > 0)); + .then((pythonPath) => pythonPath.split(path.delimiter).filter((item) => item.trim().length > 0)); const resolvedPaths = pythonPaths - .filter(pythonPath => !path.isAbsolute(pythonPath)) - .map(pythonPath => path.resolve(this.workspacePath, pythonPath)); + .filter((pythonPath) => !path.isAbsolute(pythonPath)) + .map((pythonPath) => path.resolve(this.workspacePath, pythonPath)); const filePaths = await Promise.all(filePathPromises); - return filePaths.concat(...pythonPaths, ...resolvedPaths).filter(p => p.length > 0); + return filePaths.concat(...pythonPaths, ...resolvedPaths).filter((p) => p.length > 0); } catch (ex) { traceError('Python Extension: jediProxy.filePaths', ex); return []; @@ -732,7 +732,7 @@ export class JediProxy implements Disposable { private getConfig() { // Add support for paths relative to workspace. const extraPaths = this.pythonSettings.autoComplete - ? this.pythonSettings.autoComplete.extraPaths.map(extraPath => { + ? this.pythonSettings.autoComplete.extraPaths.map((extraPath) => { if (path.isAbsolute(extraPath)) { return extraPath; } @@ -750,7 +750,7 @@ export class JediProxy implements Disposable { const distinctExtraPaths = extraPaths .concat(this.additionalAutoCompletePaths) - .filter(value => value.length > 0) + .filter((value) => value.length > 0) .filter((value, index, self) => self.indexOf(value) === index); return { @@ -902,7 +902,7 @@ export class JediProxyHandler implements Disposable { this.commandCancellationTokenSources.set(cmd.command, cancellation); executionCmd.token = cancellation.token; - return this.jediProxy.sendCommand(executionCmd).catch(reason => { + return this.jediProxy.sendCommand(executionCmd).catch((reason) => { traceError(reason); return undefined; }); @@ -915,7 +915,7 @@ export class JediProxyHandler implements Disposable { executionCmd.token = token; } - return this.jediProxy.sendCommand(executionCmd).catch(reason => { + return this.jediProxy.sendCommand(executionCmd).catch((reason) => { traceError(reason); return undefined; }); diff --git a/src/client/providers/linterProvider.ts b/src/client/providers/linterProvider.ts index 42dc4676f7c5..8bfde47e192b 100644 --- a/src/client/providers/linterProvider.ts +++ b/src/client/providers/linterProvider.ts @@ -46,9 +46,9 @@ export class LinterProvider implements IExtensionActivationService, Disposable { this.activatedOnce = true; this.disposables.push(this.interpreterService.onDidChangeInterpreter(() => this.engine.lintOpenPythonFiles())); - this.documents.onDidOpenTextDocument(e => this.onDocumentOpened(e), this.disposables); - this.documents.onDidCloseTextDocument(e => this.onDocumentClosed(e), this.disposables); - this.documents.onDidSaveTextDocument(e => this.onDocumentSaved(e), this.disposables); + this.documents.onDidOpenTextDocument((e) => this.onDocumentOpened(e), this.disposables); + this.documents.onDidCloseTextDocument((e) => this.onDocumentClosed(e), this.disposables); + this.documents.onDidSaveTextDocument((e) => this.onDocumentSaved(e), this.disposables); const disposable = this.workspaceService.onDidChangeConfiguration(this.lintSettingsChangedHandler.bind(this)); this.disposables.push(disposable); @@ -62,16 +62,16 @@ export class LinterProvider implements IExtensionActivationService, Disposable { } public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } private isDocumentOpen(uri: Uri): boolean { - return this.documents.textDocuments.some(document => this.fs.arePathsSame(document.uri.fsPath, uri.fsPath)); + return this.documents.textDocuments.some((document) => this.fs.arePathsSame(document.uri.fsPath, uri.fsPath)); } private lintSettingsChangedHandler(e: ConfigurationChangeEvent) { // Look for python files that belong to the specified workspace folder. - workspace.textDocuments.forEach(document => { + workspace.textDocuments.forEach((document) => { if (e.affectsConfiguration('python.linting', document.uri)) { this.engine.lintDocument(document, 'auto').ignoreErrors(); } @@ -91,9 +91,9 @@ export class LinterProvider implements IExtensionActivationService, Disposable { this.linterManager .getActiveLinters(false, document.uri) - .then(linters => { + .then((linters) => { const fileName = path.basename(document.uri.fsPath).toLowerCase(); - const watchers = linters.filter(info => info.configFileNames.indexOf(fileName) >= 0); + const watchers = linters.filter((info) => info.configFileNames.indexOf(fileName) >= 0); if (watchers.length > 0) { setTimeout(() => this.engine.lintOpenPythonFiles(), 1000); } diff --git a/src/client/providers/referenceProvider.ts b/src/client/providers/referenceProvider.ts index 63febcbece6e..1a392dcb79d3 100644 --- a/src/client/providers/referenceProvider.ts +++ b/src/client/providers/referenceProvider.ts @@ -12,7 +12,7 @@ export class PythonReferenceProvider implements vscode.ReferenceProvider { if (data && data.references.length > 0) { // tslint:disable-next-line:no-unnecessary-local-variable const references = data.references - .filter(ref => { + .filter((ref) => { if ( !ref || typeof ref.columnIndex !== 'number' || @@ -26,7 +26,7 @@ export class PythonReferenceProvider implements vscode.ReferenceProvider { } return true; }) - .map(ref => { + .map((ref) => { const definitionResource = vscode.Uri.file(ref.fileName); const range = new vscode.Range(ref.lineIndex, ref.columnIndex, ref.lineIndex, ref.columnIndex); diff --git a/src/client/providers/renameProvider.ts b/src/client/providers/renameProvider.ts index 60f399c09105..500923e0afbf 100644 --- a/src/client/providers/renameProvider.ts +++ b/src/client/providers/renameProvider.ts @@ -69,17 +69,17 @@ export class PythonRenameProvider implements RenameProvider { const proxy = new RefactorProxy(EXTENSION_ROOT_DIR, pythonSettings, workspaceRoot, this.serviceContainer); return proxy .rename(document, newName, document.uri.fsPath, range) - .then(response => { - const fileDiffs = response.results.map(fileChanges => fileChanges.diff); + .then((response) => { + const fileDiffs = response.results.map((fileChanges) => fileChanges.diff); const fs = this.serviceContainer.get(IFileSystem); return getWorkspaceEditsFromPatch(fileDiffs, workspaceRoot, fs); }) - .catch(reason => { + .catch((reason) => { if (reason === 'Not installed') { const installer = this.serviceContainer.get(IInstaller); installer .promptToInstall(Product.rope, document.uri) - .catch(ex => traceError('Python Extension: promptToInstall', ex)); + .catch((ex) => traceError('Python Extension: promptToInstall', ex)); return Promise.reject(''); } else { window.showErrorMessage(reason); diff --git a/src/client/providers/replProvider.ts b/src/client/providers/replProvider.ts index a7a68b647fe7..5753df8d1d83 100644 --- a/src/client/providers/replProvider.ts +++ b/src/client/providers/replProvider.ts @@ -14,7 +14,7 @@ export class ReplProvider implements Disposable { this.registerCommand(); } public dispose() { - this.disposables.forEach(disposable => disposable.dispose()); + this.disposables.forEach((disposable) => disposable.dispose()); } private registerCommand() { const commandManager = this.serviceContainer.get(ICommandManager); diff --git a/src/client/providers/signatureProvider.ts b/src/client/providers/signatureProvider.ts index 4d918ff96f5d..df10f120b21a 100644 --- a/src/client/providers/signatureProvider.ts +++ b/src/client/providers/signatureProvider.ts @@ -33,7 +33,7 @@ function extractParamDocString(paramName: string, docString: string): string { // In docstring the '*' is escaped with a backslash paramName = paramName.replace(new RegExp('\\*', 'g'), '\\\\\\*'); - DOCSTRING_PARAM_PATTERNS.forEach(pattern => { + DOCSTRING_PARAM_PATTERNS.forEach((pattern) => { if (paramDocString.length > 0) { return; } @@ -60,7 +60,7 @@ export class PythonSignatureProvider implements SignatureHelpProvider { const signature = new SignatureHelp(); signature.activeSignature = 0; - data.definitions.forEach(def => { + data.definitions.forEach((def) => { signature.activeParameter = def.paramindex; // Don't display the documentation, as vs code doesn't format the documentation. // i.e. line feeds are not respected, long content is stripped. @@ -77,7 +77,7 @@ export class PythonSignatureProvider implements SignatureHelpProvider { documentation = docLines.join(EOL).trim(); } else { if (def.params && def.params.length > 0) { - label = `${def.name}(${def.params.map(p => p.name).join(', ')})`; + label = `${def.name}(${def.params.map((p) => p.name).join(', ')})`; documentation = def.docstring; } else { label = def.description; @@ -93,7 +93,7 @@ export class PythonSignatureProvider implements SignatureHelpProvider { }; if (def.params && def.params.length) { - sig.parameters = def.params.map(arg => { + sig.parameters = def.params.map((arg) => { if (arg.docstring.length === 0) { arg.docstring = extractParamDocString(arg.name, def.docstring); } @@ -132,7 +132,7 @@ export class PythonSignatureProvider implements SignatureHelpProvider { return this.jediFactory .getJediProxyHandler(document.uri) .sendCommand(cmd, token) - .then(data => { + .then((data) => { return data ? PythonSignatureProvider.parseData(data) : new SignatureHelp(); }); } diff --git a/src/client/providers/simpleRefactorProvider.ts b/src/client/providers/simpleRefactorProvider.ts index 1dbdb9dd7a55..0e0bdb53b714 100644 --- a/src/client/providers/simpleRefactorProvider.ts +++ b/src/client/providers/simpleRefactorProvider.ts @@ -83,7 +83,7 @@ export function extractVariable( range, textEditor.options ) - .then(response => { + .then((response) => { return response.results[0].diff; }); @@ -124,7 +124,7 @@ export function extractMethod( range, textEditor.options ) - .then(response => { + .then((response) => { return response.results[0].diff; }); @@ -140,7 +140,7 @@ function validateDocumentForRefactor(textEditor: vscode.TextEditor): Promise((resolve, reject) => { - vscode.window.showInformationMessage('Please save changes before refactoring', 'Save').then(item => { + vscode.window.showInformationMessage('Please save changes before refactoring', 'Save').then((item) => { if (item === 'Save') { textEditor.document.save().then(resolve, reject); } else { @@ -159,15 +159,15 @@ function extractName( ): Promise { let changeStartsAtLine = -1; return renameResponse - .then(diff => { + .then((diff) => { if (diff.length === 0) { return []; } return getTextEditsFromPatch(textEditor.document.getText(), diff); }) - .then(edits => { - return textEditor.edit(editBuilder => { - edits.forEach(edit => { + .then((edits) => { + return textEditor.edit((editBuilder) => { + edits.forEach((edit) => { if (changeStartsAtLine === -1 || changeStartsAtLine > edit.range.start.line) { changeStartsAtLine = edit.range.start.line; } @@ -175,7 +175,7 @@ function extractName( }); }); }) - .then(done => { + .then((done) => { if (done && changeStartsAtLine >= 0) { let newWordPosition: vscode.Position | undefined; for (let lineNumber = changeStartsAtLine; lineNumber < textEditor.document.lineCount; lineNumber += 1) { @@ -203,7 +203,7 @@ function extractName( } return null; }) - .then(newWordPosition => { + .then((newWordPosition) => { if (newWordPosition) { return textEditor.document.save().then(() => { // Now that we have selected the new variable, lets invoke the rename command @@ -211,11 +211,11 @@ function extractName( }); } }) - .catch(error => { + .catch((error) => { if (error === 'Not installed') { installer .promptToInstall(Product.rope, textEditor.document.uri) - .catch(ex => traceError('Python Extension: simpleRefactorProvider.promptToInstall', ex)); + .catch((ex) => traceError('Python Extension: simpleRefactorProvider.promptToInstall', ex)); return Promise.reject(''); } let errorMessage = `${error}`; diff --git a/src/client/providers/symbolProvider.ts b/src/client/providers/symbolProvider.ts index bef724bb4e1b..ca3d2b6959da 100644 --- a/src/client/providers/symbolProvider.ts +++ b/src/client/providers/symbolProvider.ts @@ -142,9 +142,9 @@ export class JediSymbolProvider implements DocumentSymbolProvider { this.jediFactory .getJediProxyHandler(document.uri) .sendCommand(cmd, token) - .then(data => this.parseData(document, data)) - .then(items => deferred.resolve(items)) - .catch(ex => deferred.reject(ex)); + .then((data) => this.parseData(document, data)) + .then((items) => deferred.resolve(items)) + .catch((ex) => deferred.reject(ex)); }, this.debounceTimeoutMs); token.onCancellationRequested(() => { @@ -183,8 +183,8 @@ export class JediSymbolProvider implements DocumentSymbolProvider { private parseData(document: TextDocument, data?: proxy.ISymbolResult): SymbolInformation[] { if (data) { - const symbols = data.definitions.filter(sym => this.fs.arePathsSame(sym.fileName, document.fileName)); - return symbols.map(sym => { + const symbols = data.definitions.filter((sym) => this.fs.arePathsSame(sym.fileName, document.fileName)); + return symbols.map((sym) => { const symbol = sym.kind; const range = new Range( sym.range.startLine, diff --git a/src/client/providers/terminalProvider.ts b/src/client/providers/terminalProvider.ts index 29a974ec016a..628bad8fa022 100644 --- a/src/client/providers/terminalProvider.ts +++ b/src/client/providers/terminalProvider.ts @@ -35,7 +35,7 @@ export class TerminalProvider implements Disposable { } } public dispose() { - this.disposables.forEach(disposable => disposable.dispose()); + this.disposables.forEach((disposable) => disposable.dispose()); } private registerCommands() { const commandManager = this.serviceContainer.get(ICommandManager); diff --git a/src/client/refactor/proxy.ts b/src/client/refactor/proxy.ts index 3136755603e0..29196a8e3786 100644 --- a/src/client/refactor/proxy.ts +++ b/src/client/refactor/proxy.ts @@ -141,7 +141,7 @@ export class RefactorProxy extends Disposable { const result = pythonProc.execObservable(args, { cwd }); this._process = result.proc; result.out.subscribe( - output => { + (output) => { if (output.source === 'stdout') { if (!this._startedSuccessfully && output.out.startsWith('STARTED')) { this._startedSuccessfully = true; @@ -152,7 +152,7 @@ export class RefactorProxy extends Disposable { this.handleStdError(output.out); } }, - error => this.handleError(error) + (error) => this.handleError(error) ); return this.initialized.promise; @@ -165,8 +165,8 @@ export class RefactorProxy extends Disposable { try { errorResponse = dataStr .split(/\r?\n/g) - .filter(line => line.length > 0) - .map(resp => JSON.parse(resp)); + .filter((line) => line.length > 0) + .map((resp) => JSON.parse(resp)); this._previousStdErrData = ''; } catch (ex) { traceError(ex); @@ -207,8 +207,8 @@ export class RefactorProxy extends Disposable { try { response = dataStr .split(/\r?\n/g) - .filter(line => line.length > 0) - .map(resp => JSON.parse(resp)); + .filter((line) => line.length > 0) + .map((resp) => JSON.parse(resp)); this._previousOutData = ''; } catch (ex) { // Possible we've only received part of the data, hence don't clear previousData diff --git a/src/client/sourceMapSupport.ts b/src/client/sourceMapSupport.ts index 661db3e5afaf..0303e1e43a93 100644 --- a/src/client/sourceMapSupport.ts +++ b/src/client/sourceMapSupport.ts @@ -28,7 +28,7 @@ export class SourceMapSupport { require('source-map-support').install(); const localize = require('./common/utils/localize') as typeof import('./common/utils/localize'); const disable = localize.Diagnostics.disableSourceMaps(); - this.vscode.window.showWarningMessage(localize.Diagnostics.warnSourceMaps(), disable).then(selection => { + this.vscode.window.showWarningMessage(localize.Diagnostics.warnSourceMaps(), disable).then((selection) => { if (selection === disable) { this.disable().ignoreErrors(); } @@ -80,7 +80,7 @@ export function initialize(vscode: VSCode = require('vscode')) { new SourceMapSupport(vscode).disable().ignoreErrors(); return; } - new SourceMapSupport(vscode).initialize().catch(_ex => { + new SourceMapSupport(vscode).initialize().catch((_ex) => { traceError('Failed to initialize source map support in extension'); }); } diff --git a/src/client/startupTelemetry.ts b/src/client/startupTelemetry.ts index e87fe8bbd32e..e4c137bc54b8 100644 --- a/src/client/startupTelemetry.ts +++ b/src/client/startupTelemetry.ts @@ -111,7 +111,7 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer): const [condaVersion, interpreter, interpreters] = await Promise.all([ condaLocator .getCondaVersion() - .then(ver => (ver ? ver.raw : '')) + .then((ver) => (ver ? ver.raw : '')) .catch(() => ''), interpreterService.getActiveInterpreter().catch(() => undefined), interpreterService.getInterpreters(mainWorkspaceUri).catch(() => []) @@ -126,7 +126,7 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer): ? settings.pythonPath === getPreferredWorkspaceInterpreter(mainWorkspaceUri, serviceContainer) : false; const hasPython3 = - interpreters!.filter(item => (item && item.version ? item.version.major === 3 : false)).length > 0; + interpreters!.filter((item) => (item && item.version ? item.version.major === 3 : false)).length > 0; return { condaVersion, diff --git a/src/client/telemetry/importTracker.ts b/src/client/telemetry/importTracker.ts index 31234fface29..85566d235c82 100644 --- a/src/client/telemetry/importTracker.ts +++ b/src/client/telemetry/importTracker.ts @@ -1,209 +1,207 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -import { inject, injectable } from 'inversify'; -import * as path from 'path'; -import { TextDocument } from 'vscode'; -import { captureTelemetry, sendTelemetryEvent } from '.'; -import { splitMultilineString } from '../../datascience-ui/common'; -import { IExtensionSingleActivationService } from '../activation/types'; -import { IDocumentManager } from '../common/application/types'; -import { isTestExecution } from '../common/constants'; -import '../common/extensions'; -import { noop } from '../common/utils/misc'; -import { ICell, INotebookEditor, INotebookEditorProvider, INotebookExecutionLogger } from '../datascience/types'; -import { EventName } from './constants'; - -/* -Python has a fairly rich import statement. Originally the matching regexp was kept simple for -performance worries, but it led to false-positives due to matching things like docstrings with -phrases along the lines of "from the thing" or "import the thing". To minimize false-positives the -regexp does its best to validate the structure of the import line _within reason_. This leads to -us supporting the following (where `pkg` represents what we are actually capturing for telemetry): - -- `from pkg import _` -- `from pkg import _, _` -- `from pkg import _ as _` -- `import pkg` -- `import pkg, pkg` -- `import pkg as _` - -Things we are ignoring the following for simplicity/performance: - -- `from pkg import (...)` (this includes single-line and multi-line imports with parentheses) -- `import pkg # ... and anything else with a trailing comment.` -- Non-standard whitespace separators within the import statement (i.e. more than a single space, tabs) - -*/ -const ImportRegEx = /^\s*(from (?\w+)(?:\.\w+)* import \w+(?:, \w+)*(?: as \w+)?|import (?\w+(?:, \w+)*)(?: as \w+)?)$/; -const MAX_DOCUMENT_LINES = 1000; - -// Capture isTestExecution on module load so that a test can turn it off and still -// have this value set. -const testExecution = isTestExecution(); - -@injectable() -export class ImportTracker implements IExtensionSingleActivationService, INotebookExecutionLogger { - private pendingChecks = new Map(); - private sentMatches: Set = new Set(); - // tslint:disable-next-line:no-require-imports - private hashFn = require('hash.js').sha256; - - constructor( - @inject(IDocumentManager) private documentManager: IDocumentManager, - @inject(INotebookEditorProvider) private notebookEditorProvider: INotebookEditorProvider - ) { - this.documentManager.onDidOpenTextDocument(t => this.onOpenedOrSavedDocument(t)); - this.documentManager.onDidSaveTextDocument(t => this.onOpenedOrSavedDocument(t)); - this.notebookEditorProvider.onDidOpenNotebookEditor(t => this.onOpenedOrClosedNotebook(t)); - this.notebookEditorProvider.onDidCloseNotebookEditor(t => this.onOpenedOrClosedNotebook(t)); - } - public async preExecute(_cell: ICell, _silent: boolean): Promise { - // Do nothing on pre execute - } - public async postExecute(cell: ICell, silent: boolean): Promise { - // Check for imports in the cell itself. - if (!silent && cell.data.cell_type === 'code') { - this.scheduleCheck(this.createCellKey(cell), this.checkCell.bind(this, cell)); - } - } - - public async activate(): Promise { - // Act like all of our open documents just opened; our timeout will make sure this is delayed. - this.documentManager.textDocuments.forEach(d => this.onOpenedOrSavedDocument(d)); - this.notebookEditorProvider.editors.forEach(e => this.onOpenedOrClosedNotebook(e)); - } - - private getDocumentLines(document: TextDocument): (string | undefined)[] { - const array = Array(Math.min(document.lineCount, MAX_DOCUMENT_LINES)).fill(''); - return array - .map((_a: string, i: number) => { - const line = document.lineAt(i); - if (line && !line.isEmptyOrWhitespace) { - return line.text; - } - return undefined; - }) - .filter((f: string | undefined) => f); - } - - private getNotebookLines(e: INotebookEditor): (string | undefined)[] { - let result: (string | undefined)[] = []; - if (e.model) { - e.model.cells - .filter(c => c.data.cell_type === 'code') - .forEach(c => { - const cellArray = this.getCellLines(c); - if (result.length < MAX_DOCUMENT_LINES) { - result = [...result, ...cellArray]; - } - }); - } - return result; - } - - private getCellLines(cell: ICell): (string | undefined)[] { - // Split into multiple lines removing line feeds on the end. - return splitMultilineString(cell.data.source).map(s => s.replace(/\n/g, '')); - } - - private onOpenedOrSavedDocument(document: TextDocument) { - // Make sure this is a Python file. - if (path.extname(document.fileName) === '.py') { - this.scheduleDocument(document); - } - } - - private onOpenedOrClosedNotebook(e: INotebookEditor) { - if (e.file) { - this.scheduleCheck(e.file.fsPath, this.checkNotebook.bind(this, e)); - } - } - - private scheduleDocument(document: TextDocument) { - this.scheduleCheck(document.fileName, this.checkDocument.bind(this, document)); - } - - private scheduleCheck(file: string, check: () => void) { - // If already scheduled, cancel. - const currentTimeout = this.pendingChecks.get(file); - if (currentTimeout) { - // tslint:disable-next-line: no-any - clearTimeout(currentTimeout as any); - this.pendingChecks.delete(file); - } - - // Now schedule a new one. - if (testExecution) { - // During a test, check right away. It needs to be synchronous. - check(); - } else { - // Wait five seconds to make sure we don't already have this document pending. - this.pendingChecks.set(file, setTimeout(check, 5000)); - } - } - - private createCellKey(cell: ICell): string { - return `${cell.file}${cell.id}`; - } - - @captureTelemetry(EventName.HASHED_PACKAGE_PERF) - private checkCell(cell: ICell) { - this.pendingChecks.delete(this.createCellKey(cell)); - const lines = this.getCellLines(cell); - this.lookForImports(lines); - } - - @captureTelemetry(EventName.HASHED_PACKAGE_PERF) - private checkNotebook(e: INotebookEditor) { - this.pendingChecks.delete(e.file.fsPath); - const lines = this.getNotebookLines(e); - this.lookForImports(lines); - } - - @captureTelemetry(EventName.HASHED_PACKAGE_PERF) - private checkDocument(document: TextDocument) { - this.pendingChecks.delete(document.fileName); - const lines = this.getDocumentLines(document); - this.lookForImports(lines); - } - - private sendTelemetry(packageName: string) { - // No need to send duplicate telemetry or waste CPU cycles on an unneeded hash. - if (this.sentMatches.has(packageName)) { - return; - } - this.sentMatches.add(packageName); - // Hash the package name so that we will never accidentally see a - // user's private package name. - const hash = this.hashFn() - .update(packageName) - .digest('hex'); - sendTelemetryEvent(EventName.HASHED_PACKAGE_NAME, undefined, { hashedName: hash }); - } - - private lookForImports(lines: (string | undefined)[]) { - try { - for (const s of lines) { - const match = s ? ImportRegEx.exec(s) : null; - if (match !== null && match.groups !== undefined) { - if (match.groups.fromImport !== undefined) { - // `from pkg ...` - this.sendTelemetry(match.groups.fromImport); - } else if (match.groups.importImport !== undefined) { - // `import pkg1, pkg2, ...` - const packageNames = match.groups.importImport - .split(',') - .map(rawPackageName => rawPackageName.trim()); - // Can't pass in `this.sendTelemetry` directly as that rebinds `this`. - packageNames.forEach(p => this.sendTelemetry(p)); - } - } - } - } catch { - // Don't care about failures since this is just telemetry. - noop(); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +import { inject, injectable } from 'inversify'; +import * as path from 'path'; +import { TextDocument } from 'vscode'; +import { captureTelemetry, sendTelemetryEvent } from '.'; +import { splitMultilineString } from '../../datascience-ui/common'; +import { IExtensionSingleActivationService } from '../activation/types'; +import { IDocumentManager } from '../common/application/types'; +import { isTestExecution } from '../common/constants'; +import '../common/extensions'; +import { noop } from '../common/utils/misc'; +import { ICell, INotebookEditor, INotebookEditorProvider, INotebookExecutionLogger } from '../datascience/types'; +import { EventName } from './constants'; + +/* +Python has a fairly rich import statement. Originally the matching regexp was kept simple for +performance worries, but it led to false-positives due to matching things like docstrings with +phrases along the lines of "from the thing" or "import the thing". To minimize false-positives the +regexp does its best to validate the structure of the import line _within reason_. This leads to +us supporting the following (where `pkg` represents what we are actually capturing for telemetry): + +- `from pkg import _` +- `from pkg import _, _` +- `from pkg import _ as _` +- `import pkg` +- `import pkg, pkg` +- `import pkg as _` + +Things we are ignoring the following for simplicity/performance: + +- `from pkg import (...)` (this includes single-line and multi-line imports with parentheses) +- `import pkg # ... and anything else with a trailing comment.` +- Non-standard whitespace separators within the import statement (i.e. more than a single space, tabs) + +*/ +const ImportRegEx = /^\s*(from (?\w+)(?:\.\w+)* import \w+(?:, \w+)*(?: as \w+)?|import (?\w+(?:, \w+)*)(?: as \w+)?)$/; +const MAX_DOCUMENT_LINES = 1000; + +// Capture isTestExecution on module load so that a test can turn it off and still +// have this value set. +const testExecution = isTestExecution(); + +@injectable() +export class ImportTracker implements IExtensionSingleActivationService, INotebookExecutionLogger { + private pendingChecks = new Map(); + private sentMatches: Set = new Set(); + // tslint:disable-next-line:no-require-imports + private hashFn = require('hash.js').sha256; + + constructor( + @inject(IDocumentManager) private documentManager: IDocumentManager, + @inject(INotebookEditorProvider) private notebookEditorProvider: INotebookEditorProvider + ) { + this.documentManager.onDidOpenTextDocument((t) => this.onOpenedOrSavedDocument(t)); + this.documentManager.onDidSaveTextDocument((t) => this.onOpenedOrSavedDocument(t)); + this.notebookEditorProvider.onDidOpenNotebookEditor((t) => this.onOpenedOrClosedNotebook(t)); + this.notebookEditorProvider.onDidCloseNotebookEditor((t) => this.onOpenedOrClosedNotebook(t)); + } + public async preExecute(_cell: ICell, _silent: boolean): Promise { + // Do nothing on pre execute + } + public async postExecute(cell: ICell, silent: boolean): Promise { + // Check for imports in the cell itself. + if (!silent && cell.data.cell_type === 'code') { + this.scheduleCheck(this.createCellKey(cell), this.checkCell.bind(this, cell)); + } + } + + public async activate(): Promise { + // Act like all of our open documents just opened; our timeout will make sure this is delayed. + this.documentManager.textDocuments.forEach((d) => this.onOpenedOrSavedDocument(d)); + this.notebookEditorProvider.editors.forEach((e) => this.onOpenedOrClosedNotebook(e)); + } + + private getDocumentLines(document: TextDocument): (string | undefined)[] { + const array = Array(Math.min(document.lineCount, MAX_DOCUMENT_LINES)).fill(''); + return array + .map((_a: string, i: number) => { + const line = document.lineAt(i); + if (line && !line.isEmptyOrWhitespace) { + return line.text; + } + return undefined; + }) + .filter((f: string | undefined) => f); + } + + private getNotebookLines(e: INotebookEditor): (string | undefined)[] { + let result: (string | undefined)[] = []; + if (e.model) { + e.model.cells + .filter((c) => c.data.cell_type === 'code') + .forEach((c) => { + const cellArray = this.getCellLines(c); + if (result.length < MAX_DOCUMENT_LINES) { + result = [...result, ...cellArray]; + } + }); + } + return result; + } + + private getCellLines(cell: ICell): (string | undefined)[] { + // Split into multiple lines removing line feeds on the end. + return splitMultilineString(cell.data.source).map((s) => s.replace(/\n/g, '')); + } + + private onOpenedOrSavedDocument(document: TextDocument) { + // Make sure this is a Python file. + if (path.extname(document.fileName) === '.py') { + this.scheduleDocument(document); + } + } + + private onOpenedOrClosedNotebook(e: INotebookEditor) { + if (e.file) { + this.scheduleCheck(e.file.fsPath, this.checkNotebook.bind(this, e)); + } + } + + private scheduleDocument(document: TextDocument) { + this.scheduleCheck(document.fileName, this.checkDocument.bind(this, document)); + } + + private scheduleCheck(file: string, check: () => void) { + // If already scheduled, cancel. + const currentTimeout = this.pendingChecks.get(file); + if (currentTimeout) { + // tslint:disable-next-line: no-any + clearTimeout(currentTimeout as any); + this.pendingChecks.delete(file); + } + + // Now schedule a new one. + if (testExecution) { + // During a test, check right away. It needs to be synchronous. + check(); + } else { + // Wait five seconds to make sure we don't already have this document pending. + this.pendingChecks.set(file, setTimeout(check, 5000)); + } + } + + private createCellKey(cell: ICell): string { + return `${cell.file}${cell.id}`; + } + + @captureTelemetry(EventName.HASHED_PACKAGE_PERF) + private checkCell(cell: ICell) { + this.pendingChecks.delete(this.createCellKey(cell)); + const lines = this.getCellLines(cell); + this.lookForImports(lines); + } + + @captureTelemetry(EventName.HASHED_PACKAGE_PERF) + private checkNotebook(e: INotebookEditor) { + this.pendingChecks.delete(e.file.fsPath); + const lines = this.getNotebookLines(e); + this.lookForImports(lines); + } + + @captureTelemetry(EventName.HASHED_PACKAGE_PERF) + private checkDocument(document: TextDocument) { + this.pendingChecks.delete(document.fileName); + const lines = this.getDocumentLines(document); + this.lookForImports(lines); + } + + private sendTelemetry(packageName: string) { + // No need to send duplicate telemetry or waste CPU cycles on an unneeded hash. + if (this.sentMatches.has(packageName)) { + return; + } + this.sentMatches.add(packageName); + // Hash the package name so that we will never accidentally see a + // user's private package name. + const hash = this.hashFn().update(packageName).digest('hex'); + sendTelemetryEvent(EventName.HASHED_PACKAGE_NAME, undefined, { hashedName: hash }); + } + + private lookForImports(lines: (string | undefined)[]) { + try { + for (const s of lines) { + const match = s ? ImportRegEx.exec(s) : null; + if (match !== null && match.groups !== undefined) { + if (match.groups.fromImport !== undefined) { + // `from pkg ...` + this.sendTelemetry(match.groups.fromImport); + } else if (match.groups.importImport !== undefined) { + // `import pkg1, pkg2, ...` + const packageNames = match.groups.importImport + .split(',') + .map((rawPackageName) => rawPackageName.trim()); + // Can't pass in `this.sendTelemetry` directly as that rebinds `this`. + packageNames.forEach((p) => this.sendTelemetry(p)); + } + } + } + } catch { + // Don't care about failures since this is just telemetry. + noop(); + } + } +} diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index c3ca1356197d..785bc02db4de 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -104,7 +104,7 @@ export function sendTelemetryEvent

{ + Object.getOwnPropertyNames(data).forEach((prop) => { if (data[prop] === undefined || data[prop] === null) { return; } @@ -141,10 +141,10 @@ export function captureTelemetry

) { + return function (_target: Object, _propertyKey: string, descriptor: TypedPropertyDescriptor) { const originalMethod = descriptor.value; // tslint:disable-next-line:no-function-expression no-any - descriptor.value = function(...args: any[]) { + descriptor.value = function (...args: any[]) { if (!captureDuration) { sendTelemetryEvent(eventName, undefined, properties); // tslint:disable-next-line:no-invalid-this @@ -160,12 +160,12 @@ export function captureTelemetry

) - .then(data => { + .then((data) => { sendTelemetryEvent(eventName, stopWatch.elapsedTime, properties); return data; }) // tslint:disable-next-line:promise-function-async - .catch(ex => { + .catch((ex) => { // tslint:disable-next-line:no-any properties = properties || ({} as any); (properties as any).failed = true; @@ -198,13 +198,13 @@ export function sendTelemetryWhenDone

).then( - data => { + (data) => { // tslint:disable-next-line:no-non-null-assertion sendTelemetryEvent(eventName, stopWatch!.elapsedTime, properties); return data; // tslint:disable-next-line:promise-function-async }, - ex => { + (ex) => { // tslint:disable-next-line:no-non-null-assertion sendTelemetryEvent(eventName, stopWatch!.elapsedTime, properties, ex); return Promise.reject(ex); diff --git a/src/client/terminals/codeExecution/codeExecutionManager.ts b/src/client/terminals/codeExecution/codeExecutionManager.ts index 48e26e7d36f7..312eef37b180 100644 --- a/src/client/terminals/codeExecution/codeExecutionManager.ts +++ b/src/client/terminals/codeExecution/codeExecutionManager.ts @@ -42,14 +42,14 @@ export class CodeExecutionManager implements ICodeExecutionManager { } public registerCommands() { - [Commands.Exec_In_Terminal, Commands.Exec_In_Terminal_Icon].forEach(cmd => { + [Commands.Exec_In_Terminal, Commands.Exec_In_Terminal_Icon].forEach((cmd) => { this.disposableRegistry.push( this.commandManager.registerCommand( // tslint:disable-next-line:no-any cmd as any, async (file: Resource) => { const trigger = cmd === Commands.Exec_In_Terminal ? 'command' : 'icon'; - await this.executeFileInTerminal(file, trigger).catch(ex => + await this.executeFileInTerminal(file, trigger).catch((ex) => traceError('Failed to execute file in terminal', ex) ); } diff --git a/src/client/terminals/codeExecution/djangoContext.ts b/src/client/terminals/codeExecution/djangoContext.ts index c96ed1738ca9..2c3de7b79a07 100644 --- a/src/client/terminals/codeExecution/djangoContext.ts +++ b/src/client/terminals/codeExecution/djangoContext.ts @@ -24,14 +24,14 @@ export class DjangoContextInitializer implements Disposable { commandManager: ICommandManager ) { this.isDjangoProject = new ContextKey('python.isDjangoProject', commandManager); - this.ensureContextStateIsSet().catch(ex => traceError('Python Extension: ensureState', ex)); + this.ensureContextStateIsSet().catch((ex) => traceError('Python Extension: ensureState', ex)); this.disposables.push( this.workpaceService.onDidChangeWorkspaceFolders(() => this.updateContextKeyBasedOnActiveWorkspace()) ); } public dispose() { - this.disposables.forEach(disposable => disposable.dispose()); + this.disposables.forEach((disposable) => disposable.dispose()); } private updateContextKeyBasedOnActiveWorkspace() { if (this.monitoringActiveTextEditor) { diff --git a/src/client/terminals/codeExecution/helper.ts b/src/client/terminals/codeExecution/helper.ts index c2172d490391..ca2802b4517f 100644 --- a/src/client/terminals/codeExecution/helper.ts +++ b/src/client/terminals/codeExecution/helper.ts @@ -82,7 +82,7 @@ export class CodeExecutionHelper implements ICodeExecutionHelper { return code; } public async saveFileIfDirty(file: Uri): Promise { - const docs = this.documentManager.textDocuments.filter(d => d.uri.path === file.path); + const docs = this.documentManager.textDocuments.filter((d) => d.uri.path === file.path); if (docs.length === 1 && docs[0].isDirty) { await docs[0].save(); } diff --git a/src/client/terminals/codeExecution/terminalCodeExecution.ts b/src/client/terminals/codeExecution/terminalCodeExecution.ts index a8e72ba0f58d..7b99434bd7bb 100644 --- a/src/client/terminals/codeExecution/terminalCodeExecution.ts +++ b/src/client/terminals/codeExecution/terminalCodeExecution.ts @@ -47,7 +47,7 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService { await this._terminalService!.show(); return; } - this.replActive = new Promise(async resolve => { + this.replActive = new Promise(async (resolve) => { const replCommandArgs = await this.getExecutableInfo(resource); await this.getTerminalService(resource).sendCommand(replCommandArgs.command, replCommandArgs.args); diff --git a/src/client/testing/codeLenses/main.ts b/src/client/testing/codeLenses/main.ts index 9cfc6897348b..2179c10d2ec8 100644 --- a/src/client/testing/codeLenses/main.ts +++ b/src/client/testing/codeLenses/main.ts @@ -1,27 +1,27 @@ -import * as vscode from 'vscode'; -import { IServiceContainer } from '../../../client/ioc/types'; -import { PYTHON } from '../../common/constants'; -import { ITestCollectionStorageService } from '../common/types'; -import { TestFileCodeLensProvider } from './testFiles'; - -export function activateCodeLenses( - onDidChange: vscode.EventEmitter, - symbolProvider: vscode.DocumentSymbolProvider, - testCollectionStorage: ITestCollectionStorageService, - serviceContainer: IServiceContainer -): vscode.Disposable { - const disposables: vscode.Disposable[] = []; - const codeLensProvider = new TestFileCodeLensProvider( - onDidChange, - symbolProvider, - testCollectionStorage, - serviceContainer - ); - disposables.push(vscode.languages.registerCodeLensProvider(PYTHON, codeLensProvider)); - - return { - dispose: () => { - disposables.forEach(d => d.dispose()); - } - }; -} +import * as vscode from 'vscode'; +import { IServiceContainer } from '../../../client/ioc/types'; +import { PYTHON } from '../../common/constants'; +import { ITestCollectionStorageService } from '../common/types'; +import { TestFileCodeLensProvider } from './testFiles'; + +export function activateCodeLenses( + onDidChange: vscode.EventEmitter, + symbolProvider: vscode.DocumentSymbolProvider, + testCollectionStorage: ITestCollectionStorageService, + serviceContainer: IServiceContainer +): vscode.Disposable { + const disposables: vscode.Disposable[] = []; + const codeLensProvider = new TestFileCodeLensProvider( + onDidChange, + symbolProvider, + testCollectionStorage, + serviceContainer + ); + disposables.push(vscode.languages.registerCodeLensProvider(PYTHON, codeLensProvider)); + + return { + dispose: () => { + disposables.forEach((d) => d.dispose()); + } + }; +} diff --git a/src/client/testing/codeLenses/testFiles.ts b/src/client/testing/codeLenses/testFiles.ts index db9a4c898f47..269f7f7d4539 100644 --- a/src/client/testing/codeLenses/testFiles.ts +++ b/src/client/testing/codeLenses/testFiles.ts @@ -94,7 +94,7 @@ export class TestFileCodeLensProvider implements CodeLensProvider { if (!tests) { return; } - return tests.testFiles.find(item => this.fileSystem.arePathsSame(item.fullPath, document.uri.fsPath)); + return tests.testFiles.find((item) => this.fileSystem.arePathsSame(item.fullPath, document.uri.fsPath)); } private async getCodeLenses( @@ -115,12 +115,12 @@ export class TestFileCodeLensProvider implements CodeLensProvider { } return symbols .filter( - symbol => + (symbol) => symbol.kind === SymbolKind.Function || symbol.kind === SymbolKind.Method || symbol.kind === SymbolKind.Class ) - .map(symbol => { + .map((symbol) => { // This is bloody crucial, if the start and end columns are the same // then vscode goes bonkers when ever you edit a line (start scrolling magically). const range = new Range( @@ -138,7 +138,7 @@ export class TestFileCodeLensProvider implements CodeLensProvider { ); }) .reduce((previous, current) => previous.concat(current), []) - .filter(codeLens => codeLens !== null); + .filter((codeLens) => codeLens !== null); } catch (reason) { if (token.isCancellationRequested) { return []; @@ -161,7 +161,7 @@ export class TestFileCodeLensProvider implements CodeLensProvider { return getFunctionCodeLens(file, allFuncsAndSuites, symbolName, range, symbolContainer); } case SymbolKind.Class: { - const cls = allFuncsAndSuites.suites.find(item => item.name === symbolName); + const cls = allFuncsAndSuites.suites.find((item) => item.name === symbolName); if (!cls) { return []; } @@ -207,19 +207,19 @@ function getTestStatusIcon(status?: TestStatus): string { function getTestStatusIcons(fns: TestFunction[]): string { const statuses: string[] = []; - let count = fns.filter(fn => fn.status === TestStatus.Pass).length; + let count = fns.filter((fn) => fn.status === TestStatus.Pass).length; if (count > 0) { statuses.push(`${constants.Octicons.Test_Pass} ${count}`); } - count = fns.filter(fn => fn.status === TestStatus.Skipped).length; + count = fns.filter((fn) => fn.status === TestStatus.Skipped).length; if (count > 0) { statuses.push(`${constants.Octicons.Test_Skip} ${count}`); } - count = fns.filter(fn => fn.status === TestStatus.Fail).length; + count = fns.filter((fn) => fn.status === TestStatus.Fail).length; if (count > 0) { statuses.push(`${constants.Octicons.Test_Fail} ${count}`); } - count = fns.filter(fn => fn.status === TestStatus.Error).length; + count = fns.filter((fn) => fn.status === TestStatus.Error).length; if (count > 0) { statuses.push(`${constants.Octicons.Test_Error} ${count}`); } @@ -235,13 +235,13 @@ function getFunctionCodeLens( ): CodeLens[] { let fn: TestFunction | undefined; if (symbolContainer.length === 0) { - fn = functionsAndSuites.functions.find(func => func.name === symbolName); + fn = functionsAndSuites.functions.find((func) => func.name === symbolName); } else { // Assume single levels for now. functionsAndSuites.suites - .filter(s => s.name === symbolContainer) - .forEach(s => { - const f = s.functions.find(item => item.name === symbolName); + .filter((s) => s.name === symbolContainer) + .forEach((s) => { + const f = s.functions.find((item) => item.name === symbolName); if (f) { fn = f; } @@ -266,7 +266,7 @@ function getFunctionCodeLens( // Ok, possible we're dealing with parameterized unit tests. // If we have [ in the name, then this is a parameterized function. const functions = functionsAndSuites.functions.filter( - func => func.name.startsWith(`${symbolName}[`) && func.name.endsWith(']') + (func) => func.name.startsWith(`${symbolName}[`) && func.name.endsWith(']') ); if (functions.length === 0) { return []; @@ -290,7 +290,7 @@ function getFunctionCodeLens( function getAllTestSuitesAndFunctionsPerFile(testFile: TestFile): FunctionsAndSuites { // tslint:disable-next-line:prefer-type-cast const all = { functions: [...testFile.functions], suites: [] as TestSuite[] }; - testFile.suites.forEach(suite => { + testFile.suites.forEach((suite) => { all.suites.push(suite); const allChildItems = getAllTestSuitesAndFunctions(suite); @@ -301,10 +301,10 @@ function getAllTestSuitesAndFunctionsPerFile(testFile: TestFile): FunctionsAndSu } function getAllTestSuitesAndFunctions(testSuite: TestSuite): FunctionsAndSuites { const all: { functions: TestFunction[]; suites: TestSuite[] } = { functions: [], suites: [] }; - testSuite.functions.forEach(fn => { + testSuite.functions.forEach((fn) => { all.functions.push(fn); }); - testSuite.suites.forEach(suite => { + testSuite.suites.forEach((suite) => { all.suites.push(suite); const allChildItems = getAllTestSuitesAndFunctions(suite); diff --git a/src/client/testing/common/argumentsHelper.ts b/src/client/testing/common/argumentsHelper.ts index 60da8a3d060c..2633f99ab50c 100644 --- a/src/client/testing/common/argumentsHelper.ts +++ b/src/client/testing/common/argumentsHelper.ts @@ -52,7 +52,7 @@ export class ArgumentsHelper implements IArgumentsHelper { nonPositionalIndexes.push(index); // Cuz the next item is the value. nonPositionalIndexes.push(index + 1); - } else if (optionsWithArguments.findIndex(item => arg.startsWith(`${item}=`)) !== -1) { + } else if (optionsWithArguments.findIndex((item) => arg.startsWith(`${item}=`)) !== -1) { nonPositionalIndexes.push(index); return; } else if (arg.startsWith('-')) { @@ -85,7 +85,7 @@ export class ArgumentsHelper implements IArgumentsHelper { // Options can use willd cards (with trailing '*') if ( optionsWithoutArguments.indexOf(arg) >= 0 || - optionsWithoutArguments.filter(option => option.endsWith('*') && arg.startsWith(option.slice(0, -1))) + optionsWithoutArguments.filter((option) => option.endsWith('*') && arg.startsWith(option.slice(0, -1))) .length > 0 ) { return false; @@ -96,14 +96,14 @@ export class ArgumentsHelper implements IArgumentsHelper { return false; } // Ignore args that match exactly with wild cards & do not have inline values. - if (optionsWithArguments.filter(option => arg.startsWith(`${option}=`)).length > 0) { + if (optionsWithArguments.filter((option) => arg.startsWith(`${option}=`)).length > 0) { return false; } // Ignore args that match a wild card (ending with *) and no ineline values. // Eg. arg='--log-cli-level' and optionsArguments=['--log-*'] if ( arg.indexOf('=') === -1 && - optionsWithoutArguments.filter(option => option.endsWith('*') && arg.startsWith(option.slice(0, -1))) + optionsWithoutArguments.filter((option) => option.endsWith('*') && arg.startsWith(option.slice(0, -1))) .length > 0 ) { ignoreIndex = index + 1; @@ -113,7 +113,7 @@ export class ArgumentsHelper implements IArgumentsHelper { // Eg. arg='--log-cli-level=XYZ' and optionsArguments=['--log-*'] if ( arg.indexOf('=') >= 0 && - optionsWithoutArguments.filter(option => option.endsWith('*') && arg.startsWith(option.slice(0, -1))) + optionsWithoutArguments.filter((option) => option.endsWith('*') && arg.startsWith(option.slice(0, -1))) .length > 0 ) { return false; diff --git a/src/client/testing/common/debugLauncher.ts b/src/client/testing/common/debugLauncher.ts index 24f668b833aa..8e707629bc69 100644 --- a/src/client/testing/common/debugLauncher.ts +++ b/src/client/testing/common/debugLauncher.ts @@ -44,7 +44,7 @@ export class DebugLauncher implements ITestDebugLauncher { const debugManager = this.serviceContainer.get(IDebugService); return debugManager .startDebugging(workspaceFolder, launchArgs) - .then(noop, ex => traceError('Failed to start debugging tests', ex)); + .then(noop, (ex) => traceError('Failed to start debugging tests', ex)); } public async readAllDebugConfigs(workspaceFolder: WorkspaceFolder): Promise { const filename = path.join(workspaceFolder.uri.fsPath, '.vscode', 'launch.json'); @@ -175,7 +175,7 @@ export class DebugLauncher implements ITestDebugLauncher { private fixArgs(args: string[], testProvider: TestProvider): string[] { if (testProvider === 'unittest') { - return args.filter(item => item !== '--debug'); + return args.filter((item) => item !== '--debug'); } else { return args; } diff --git a/src/client/testing/common/enablementTracker.ts b/src/client/testing/common/enablementTracker.ts index 6ed8271287ec..fe6b319873ec 100644 --- a/src/client/testing/common/enablementTracker.ts +++ b/src/client/testing/common/enablementTracker.ts @@ -27,13 +27,13 @@ export class EnablementTracker implements IExtensionSingleActivationService { public onDidChangeConfiguration(args: ConfigurationChangeEvent) { const resourcesToCheck: Resource[] = [undefined]; if (Array.isArray(this.workspaceService.workspaceFolders)) { - this.workspaceService.workspaceFolders.forEach(item => resourcesToCheck.push(item.uri)); + this.workspaceService.workspaceFolders.forEach((item) => resourcesToCheck.push(item.uri)); } const testProviders: TestProvider[] = ['nosetest', 'pytest', 'unittest']; - resourcesToCheck.forEach(resource => { + resourcesToCheck.forEach((resource) => { const telemetry: Partial> = {}; - testProviders.forEach(item => { + testProviders.forEach((item) => { const product = this.testsHelper.parseProduct(item); const testingSetting = this.testConfig.getTestEnablingSetting(product); const settingToCheck = `python.${testingSetting}`; diff --git a/src/client/testing/common/managers/baseTestManager.ts b/src/client/testing/common/managers/baseTestManager.ts index fc9a6d22a4ef..ded6581f62cf 100644 --- a/src/client/testing/common/managers/baseTestManager.ts +++ b/src/client/testing/common/managers/baseTestManager.ts @@ -204,7 +204,7 @@ export abstract class BaseTestManager implements ITestManager { ); return discoveryService .discoverTests(discoveryOptions) - .then(tests => { + .then((tests) => { const wkspace = this.workspaceService.getWorkspaceFolder(Uri.file(this.rootDirectory))!.uri; const existingTests = this.testCollectionStorage.getTests(wkspace)!; if (clearTestStatus) { @@ -220,7 +220,7 @@ export abstract class BaseTestManager implements ITestManager { // have errors in Discovering let haveErrorsInDiscovering = false; - tests.testFiles.forEach(file => { + tests.testFiles.forEach((file) => { if (file.errorsWhenDiscovering && file.errorsWhenDiscovering.length > 0) { haveErrorsInDiscovering = true; this.outputChannel.append('_'.repeat(10)); @@ -248,7 +248,7 @@ export abstract class BaseTestManager implements ITestManager { ) { this.installer .promptToInstall(this.product, this.workspaceFolder) - .catch(ex => traceError('isNotInstalledError', ex)); + .catch((ex) => traceError('isNotInstalledError', ex)); } this.tests = undefined; @@ -286,7 +286,7 @@ export abstract class BaseTestManager implements ITestManager { Run_Specific_Function: 'false' }; //Ensure valid values are sent. - const validCmdSourceValues = getNamesAndValues(CommandSource).map(item => item.value); + const validCmdSourceValues = getNamesAndValues(CommandSource).map((item) => item.value); const telementryProperties: TestRunTelemetry = { tool: this.testProvider, scope: 'all', @@ -341,7 +341,7 @@ export abstract class BaseTestManager implements ITestManager { ? false : true; return this.discoverTests(cmdSource, clearDiscoveredTestCache, true, true) - .catch(reason => { + .catch((reason) => { if ( this.testDiscoveryCancellationToken && this.testDiscoveryCancellationToken.isCancellationRequested @@ -359,7 +359,7 @@ export abstract class BaseTestManager implements ITestManager { summary: { errors: 0, failures: 0, passed: 0, skipped: 0 } }; }) - .then(tests => { + .then((tests) => { this.updateStatus(TestStatus.Running); this.createCancellationToken(CancellationTokenType.testRunner); return this.runTestImpl(tests, testsToRun, runFailedTests, debug); @@ -372,7 +372,7 @@ export abstract class BaseTestManager implements ITestManager { this.testsStatusUpdaterService.triggerUpdatesToTests(this.workspaceFolder, this.tests); return this.tests!; }) - .catch(reason => { + .catch((reason) => { this.testsStatusUpdaterService.updateStatusOfRunningTestsAsIdle(this.workspaceFolder, this.tests); this.testsStatusUpdaterService.triggerUpdatesToTests(this.workspaceFolder, this.tests); if (this.testRunnerCancellationToken && this.testRunnerCancellationToken.isCancellationRequested) { @@ -476,10 +476,12 @@ export abstract class BaseTestManager implements ITestManager { this.diagnosticCollection.forEach((diagnosticUri, oldDiagnostics, collection) => { const newDiagnostics: Diagnostic[] = []; for (const diagnostic of oldDiagnostics) { - const matchingMsg = messages.find(msg => msg.code === diagnostic.code); + const matchingMsg = messages.find((msg) => msg.code === diagnostic.code); if (matchingMsg === undefined) { // No matching message was found, so this test was not included in the test run. - const matchingTest = tests.testFunctions.find(tf => tf.testFunction.nameToRun === diagnostic.code); + const matchingTest = tests.testFunctions.find( + (tf) => tf.testFunction.nameToRun === diagnostic.code + ); if (matchingTest !== undefined) { // Matching test was found, so the diagnostic is still relevant. newDiagnostics.push(diagnostic); diff --git a/src/client/testing/common/managers/testConfigurationManager.ts b/src/client/testing/common/managers/testConfigurationManager.ts index 4297d72037f6..22c19e0b821d 100644 --- a/src/client/testing/common/managers/testConfigurationManager.ts +++ b/src/client/testing/common/managers/testConfigurationManager.ts @@ -31,7 +31,7 @@ export abstract class TestConfigurationManager implements ITestConfigurationMana public async enable() { // Disable other test frameworks. await Promise.all( - UNIT_TEST_PRODUCTS.filter(prod => prod !== this.product).map(prod => + UNIT_TEST_PRODUCTS.filter((prod) => prod !== this.product).map((prod) => this.testConfigSettingsService.disable(this.workspace, prod) ) ); @@ -49,7 +49,7 @@ export abstract class TestConfigurationManager implements ITestConfigurationMana placeHolder: 'Select the directory containing the tests' }; let items: QuickPickItem[] = subDirs - .map(dir => { + .map((dir) => { const dirName = path.relative(rootDir, dir); if (dirName.indexOf('.') === 0) { return; @@ -59,14 +59,14 @@ export abstract class TestConfigurationManager implements ITestConfigurationMana description: '' }; }) - .filter(item => item !== undefined) - .map(item => item!); + .filter((item) => item !== undefined) + .map((item) => item!); items = [{ label: '.', description: 'Root directory' }, ...items]; items = customOptions.concat(items); const def = createDeferred(); const appShell = this.serviceContainer.get(IApplicationShell); - appShell.showQuickPick(items, options).then(item => { + appShell.showQuickPick(items, options).then((item) => { if (!item) { this.handleCancelled(); // This will throw an exception. return; @@ -95,7 +95,7 @@ export abstract class TestConfigurationManager implements ITestConfigurationMana const def = createDeferred(); const appShell = this.serviceContainer.get(IApplicationShell); - appShell.showQuickPick(items, options).then(item => { + appShell.showQuickPick(items, options).then((item) => { if (!item) { this.handleCancelled(); // This will throw an exception. return; @@ -108,12 +108,12 @@ export abstract class TestConfigurationManager implements ITestConfigurationMana } protected getTestDirs(rootDir: string): Promise { const fs = this.serviceContainer.get(IFileSystem); - return fs.getSubDirectories(rootDir).then(subDirs => { + return fs.getSubDirectories(rootDir).then((subDirs) => { subDirs.sort(); // Find out if there are any dirs with the name test and place them on the top. - const possibleTestDirs = subDirs.filter(dir => dir.match(/test/i)); - const nonTestDirs = subDirs.filter(dir => possibleTestDirs.indexOf(dir) === -1); + const possibleTestDirs = subDirs.filter((dir) => dir.match(/test/i)); + const nonTestDirs = subDirs.filter((dir) => possibleTestDirs.indexOf(dir) === -1); possibleTestDirs.push(...nonTestDirs); // The test dirs are now on top. diff --git a/src/client/testing/common/runner.ts b/src/client/testing/common/runner.ts index a83aa20de021..fd2f3145b7d8 100644 --- a/src/client/testing/common/runner.ts +++ b/src/client/testing/common/runner.ts @@ -53,12 +53,12 @@ export async function run( promise = serviceContainer .get(IPythonExecutionFactory) .createActivatedEnvironment({ resource: options.workspaceFolder }) - .then(executionService => executionService.execObservable(options.args, { ...spawnOptions })); + .then((executionService) => executionService.execObservable(options.args, { ...spawnOptions })); } else if (typeof executionInfo.moduleName === 'string' && executionInfo.moduleName.length > 0) { pythonExecutionServicePromise = serviceContainer .get(IPythonExecutionFactory) .createActivatedEnvironment({ resource: options.workspaceFolder }); - promise = pythonExecutionServicePromise.then(executionService => + promise = pythonExecutionServicePromise.then((executionService) => executionService.execModuleObservable(executionInfo.moduleName!, executionInfo.args, options) ); } else { @@ -68,12 +68,12 @@ export async function run( promise = pythonToolsExecutionService.execObservable(executionInfo, spawnOptions, options.workspaceFolder); } - return promise.then(result => { + return promise.then((result) => { return new Promise((resolve, reject) => { let stdOut = ''; let stdErr = ''; result.out.subscribe( - output => { + (output) => { stdOut += output.out; // If the test runner python module is not installed we'll have something in stderr. // Hence track that separately and check at the end. diff --git a/src/client/testing/common/services/contextService.ts b/src/client/testing/common/services/contextService.ts index 66e25ae12d74..40b70561b649 100644 --- a/src/client/testing/common/services/contextService.ts +++ b/src/client/testing/common/services/contextService.ts @@ -29,7 +29,7 @@ export class TestContextService implements ITestContextService { this.busyTests = new ContextKey('busyTests', cmdManager); } public dispose(): void { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } public register(): void { this.testManager.onDidStatusChange(this.onStatusChange, this, this.disposables); diff --git a/src/client/testing/common/services/discoveredTestParser.ts b/src/client/testing/common/services/discoveredTestParser.ts index e4fbbf6dfbea..c3b88a298790 100644 --- a/src/client/testing/common/services/discoveredTestParser.ts +++ b/src/client/testing/common/services/discoveredTestParser.ts @@ -107,18 +107,18 @@ export class TestDiscoveredTestParser implements discovery.ITestDiscoveredTestPa tests: testing.Tests ) { const folders = discoveredTests.parents - .filter(child => child.kind === 'folder' && child.parentid === parentFolder.nameToRun) - .map(folder => createTestFolder(rootFolder, folder as discovery.TestFolder)); - folders.forEach(folder => { + .filter((child) => child.kind === 'folder' && child.parentid === parentFolder.nameToRun) + .map((folder) => createTestFolder(rootFolder, folder as discovery.TestFolder)); + folders.forEach((folder) => { parentFolder.folders.push(folder); tests.testFolders.push(folder); this.buildChildren(rootFolder, folder, discoveredTests, tests); }); const files = discoveredTests.parents - .filter(child => child.kind === 'file' && child.parentid === parentFolder.nameToRun) - .map(file => createTestFile(rootFolder, file as discovery.TestFile)); - files.forEach(file => { + .filter((child) => child.kind === 'file' && child.parentid === parentFolder.nameToRun) + .map((file) => createTestFile(rootFolder, file as discovery.TestFile)); + files.forEach((file) => { parentFolder.testFiles.push(file); tests.testFiles.push(file); this.buildChildren(rootFolder, file, discoveredTests, tests); @@ -144,26 +144,26 @@ export class TestDiscoveredTestParser implements discovery.ITestDiscoveredTestPa tests: testing.Tests ) { const suites = discoveredTests.parents - .filter(child => child.kind === 'suite' && child.parentid === parentFile.nameToRun) - .map(suite => createTestSuite(parentFile, rootFolder.resource, suite as discovery.TestSuite)); - suites.forEach(suite => { + .filter((child) => child.kind === 'suite' && child.parentid === parentFile.nameToRun) + .map((suite) => createTestSuite(parentFile, rootFolder.resource, suite as discovery.TestSuite)); + suites.forEach((suite) => { parentFile.suites.push(suite); tests.testSuites.push(createFlattenedSuite(tests, suite)); this.buildChildren(rootFolder, suite, discoveredTests, tests); }); const functions = discoveredTests.tests - .filter(test => test.parentid === parentFile.nameToRun) - .map(test => createTestFunction(rootFolder, test)); - functions.forEach(func => { + .filter((test) => test.parentid === parentFile.nameToRun) + .map((test) => createTestFunction(rootFolder, test)); + functions.forEach((func) => { parentFile.functions.push(func); tests.testFunctions.push(createFlattenedFunction(tests, func)); }); const parameterizedFunctions = discoveredTests.parents - .filter(child => child.kind === 'function' && child.parentid === parentFile.nameToRun) - .map(func => createParameterizedTestFunction(rootFolder, func as discovery.TestFunction)); - parameterizedFunctions.forEach(func => + .filter((child) => child.kind === 'function' && child.parentid === parentFile.nameToRun) + .map((func) => createParameterizedTestFunction(rootFolder, func as discovery.TestFunction)); + parameterizedFunctions.forEach((func) => this.processParameterizedFunction(rootFolder, parentFile, func, discoveredTests, tests) ); } @@ -187,26 +187,26 @@ export class TestDiscoveredTestParser implements discovery.ITestDiscoveredTestPa tests: testing.Tests ) { const suites = discoveredTests.parents - .filter(child => child.kind === 'suite' && child.parentid === parentSuite.nameToRun) - .map(suite => createTestSuite(parentSuite, rootFolder.resource, suite as discovery.TestSuite)); - suites.forEach(suite => { + .filter((child) => child.kind === 'suite' && child.parentid === parentSuite.nameToRun) + .map((suite) => createTestSuite(parentSuite, rootFolder.resource, suite as discovery.TestSuite)); + suites.forEach((suite) => { parentSuite.suites.push(suite); tests.testSuites.push(createFlattenedSuite(tests, suite)); this.buildChildren(rootFolder, suite, discoveredTests, tests); }); const functions = discoveredTests.tests - .filter(test => test.parentid === parentSuite.nameToRun) - .map(test => createTestFunction(rootFolder, test)); - functions.forEach(func => { + .filter((test) => test.parentid === parentSuite.nameToRun) + .map((test) => createTestFunction(rootFolder, test)); + functions.forEach((func) => { parentSuite.functions.push(func); tests.testFunctions.push(createFlattenedFunction(tests, func)); }); const parameterizedFunctions = discoveredTests.parents - .filter(child => child.kind === 'function' && child.parentid === parentSuite.nameToRun) - .map(func => createParameterizedTestFunction(rootFolder, func as discovery.TestFunction)); - parameterizedFunctions.forEach(func => + .filter((child) => child.kind === 'function' && child.parentid === parentSuite.nameToRun) + .map((func) => createParameterizedTestFunction(rootFolder, func as discovery.TestFunction)); + parameterizedFunctions.forEach((func) => this.processParameterizedFunction(rootFolder, parentSuite, func, discoveredTests, tests) ); } @@ -235,9 +235,9 @@ export class TestDiscoveredTestParser implements discovery.ITestDiscoveredTestPa return; } const functions = discoveredTests.tests - .filter(test => test.parentid === parentFunction.nameToRun) - .map(test => createTestFunction(rootFolder, test)); - functions.forEach(func => { + .filter((test) => test.parentid === parentFunction.nameToRun) + .map((test) => createTestFunction(rootFolder, test)); + functions.forEach((func) => { func.subtestParent = parentFunction; parentFunction.asSuite.functions.push(func); parent.functions.push(func); diff --git a/src/client/testing/common/services/storageService.ts b/src/client/testing/common/services/storageService.ts index 3f7fbc51f875..46aafe1c74de 100644 --- a/src/client/testing/common/services/storageService.ts +++ b/src/client/testing/common/services/storageService.ts @@ -42,14 +42,14 @@ export class TestCollectionStorageService implements ITestCollectionStorageServi if (!tests) { return; } - return tests.testFunctions.find(f => f.testFunction === func); + return tests.testFunctions.find((f) => f.testFunction === func); } public findFlattendTestSuite(resource: Uri, suite: TestSuite): FlattenedTestSuite | undefined { const tests = this.getTests(resource); if (!tests) { return; } - return tests.testSuites.find(f => f.testSuite === suite); + return tests.testSuites.find((f) => f.testSuite === suite); } public dispose() { this.testsIndexedByWorkspaceUri.clear(); diff --git a/src/client/testing/common/services/testManagerService.ts b/src/client/testing/common/services/testManagerService.ts index 33557985832b..42a96877ef0c 100644 --- a/src/client/testing/common/services/testManagerService.ts +++ b/src/client/testing/common/services/testManagerService.ts @@ -12,7 +12,7 @@ export class TestManagerService implements ITestManagerService { disposables.push(this); } public dispose() { - this.cachedTestManagers.forEach(info => { + this.cachedTestManagers.forEach((info) => { info.dispose(); }); } diff --git a/src/client/testing/common/services/testResultsService.ts b/src/client/testing/common/services/testResultsService.ts index b4154cda0c62..0112b3599521 100644 --- a/src/client/testing/common/services/testResultsService.ts +++ b/src/client/testing/common/services/testResultsService.ts @@ -7,16 +7,16 @@ import { ITestResultsService, ITestVisitor, Tests, TestStatus } from '../types'; export class TestResultsService implements ITestResultsService { constructor(@inject(ITestVisitor) @named('TestResultResetVisitor') private resultResetVisitor: ITestVisitor) {} public resetResults(tests: Tests): void { - tests.testFolders.forEach(f => this.resultResetVisitor.visitTestFolder(f)); - tests.testFunctions.forEach(fn => this.resultResetVisitor.visitTestFunction(fn.testFunction)); - tests.testSuites.forEach(suite => this.resultResetVisitor.visitTestSuite(suite.testSuite)); - tests.testFiles.forEach(testFile => this.resultResetVisitor.visitTestFile(testFile)); + tests.testFolders.forEach((f) => this.resultResetVisitor.visitTestFolder(f)); + tests.testFunctions.forEach((fn) => this.resultResetVisitor.visitTestFunction(fn.testFunction)); + tests.testSuites.forEach((suite) => this.resultResetVisitor.visitTestSuite(suite.testSuite)); + tests.testFiles.forEach((testFile) => this.resultResetVisitor.visitTestFile(testFile)); } public updateResults(tests: Tests): void { // Update Test tree bottom to top const testQueue: TestDataItem[] = []; const testStack: TestDataItem[] = []; - tests.rootTestFolders.forEach(folder => testQueue.push(folder)); + tests.rootTestFolders.forEach((folder) => testQueue.push(folder)); while (testQueue.length > 0) { const item = testQueue.shift(); @@ -25,7 +25,7 @@ export class TestResultsService implements ITestResultsService { } testStack.push(item); const children = getChildren(item); - children.forEach(child => testQueue.push(child)); + children.forEach((child) => testQueue.push(child)); } while (testStack.length > 0) { const item = testStack.pop(); @@ -41,7 +41,7 @@ export class TestResultsService implements ITestResultsService { test.functionsPassed = test.functionsFailed = test.functionsDidNotRun = 0; const children = getChildren(test); - children.forEach(child => { + children.forEach((child) => { if (getTestDataItemType(child) === TestDataItemType.function) { if (typeof child.passed === 'boolean') { noChildrenRan = false; diff --git a/src/client/testing/common/services/testsStatusService.ts b/src/client/testing/common/services/testsStatusService.ts index e0ea070917f0..b2094a3e26b1 100644 --- a/src/client/testing/common/services/testsStatusService.ts +++ b/src/client/testing/common/services/testsStatusService.ts @@ -20,7 +20,7 @@ export class TestsStatusUpdaterService implements ITestsStatusUpdaterService { item.status = TestStatus.Discovering; this.storage.update(resource, item); }; - tests.rootTestFolders.forEach(item => visitRecursive(tests, item, visitor)); + tests.rootTestFolders.forEach((item) => visitRecursive(tests, item, visitor)); } public updateStatusAsUnknown(resource: Uri, tests?: Tests): void { if (!tests) { @@ -30,7 +30,7 @@ export class TestsStatusUpdaterService implements ITestsStatusUpdaterService { item.status = TestStatus.Unknown; this.storage.update(resource, item); }; - tests.rootTestFolders.forEach(item => visitRecursive(tests, item, visitor)); + tests.rootTestFolders.forEach((item) => visitRecursive(tests, item, visitor)); } public updateStatusAsRunning(resource: Uri, tests?: Tests): void { if (!tests) { @@ -40,7 +40,7 @@ export class TestsStatusUpdaterService implements ITestsStatusUpdaterService { item.status = TestStatus.Running; this.storage.update(resource, item); }; - tests.rootTestFolders.forEach(item => visitRecursive(tests, item, visitor)); + tests.rootTestFolders.forEach((item) => visitRecursive(tests, item, visitor)); } public updateStatusAsRunningFailedTests(resource: Uri, tests?: Tests): void { if (!tests) { @@ -54,10 +54,10 @@ export class TestsStatusUpdaterService implements ITestsStatusUpdaterService { } }; const failedItems = [ - ...tests.testFunctions.map(f => f.testFunction).filter(predicate), - ...tests.testSuites.map(f => f.testSuite).filter(predicate) + ...tests.testFunctions.map((f) => f.testFunction).filter(predicate), + ...tests.testSuites.map((f) => f.testSuite).filter(predicate) ]; - failedItems.forEach(failedItem => visitRecursive(tests, failedItem, visitor)); + failedItems.forEach((failedItem) => visitRecursive(tests, failedItem, visitor)); } public updateStatusAsRunningSpecificTests(resource: Uri, testsToRun: TestsToRun, tests?: Tests): void { if (!tests) { @@ -72,7 +72,7 @@ export class TestsStatusUpdaterService implements ITestsStatusUpdaterService { item.status = TestStatus.Running; this.storage.update(resource, item); }; - itemsRunning.forEach(item => visitRecursive(tests, item, visitor)); + itemsRunning.forEach((item) => visitRecursive(tests, item, visitor)); } public updateStatusOfRunningTestsAsIdle(resource: Uri, tests?: Tests): void { if (!tests) { @@ -84,13 +84,13 @@ export class TestsStatusUpdaterService implements ITestsStatusUpdaterService { this.storage.update(resource, item); } }; - tests.rootTestFolders.forEach(item => visitRecursive(tests, item, visitor)); + tests.rootTestFolders.forEach((item) => visitRecursive(tests, item, visitor)); } public triggerUpdatesToTests(resource: Uri, tests?: Tests): void { if (!tests) { return; } const visitor = (item: TestDataItem) => this.storage.update(resource, item); - tests.rootTestFolders.forEach(item => visitRecursive(tests, item, visitor)); + tests.rootTestFolders.forEach((item) => visitRecursive(tests, item, visitor)); } } diff --git a/src/client/testing/common/services/workspaceTestManagerService.ts b/src/client/testing/common/services/workspaceTestManagerService.ts index 9791c371c23b..e0061997e8dd 100644 --- a/src/client/testing/common/services/workspaceTestManagerService.ts +++ b/src/client/testing/common/services/workspaceTestManagerService.ts @@ -21,7 +21,7 @@ export class WorkspaceTestManagerService implements IWorkspaceTestManagerService disposables.push(this); } public dispose() { - this.workspaceTestManagers.forEach(info => info.dispose()); + this.workspaceTestManagers.forEach((info) => info.dispose()); } public getTestManager(resource: Uri): ITestManager | undefined { const wkspace = this.getWorkspace(resource); diff --git a/src/client/testing/common/testUtils.ts b/src/client/testing/common/testUtils.ts index 1bb7e8efcca4..2aa97f6950d8 100644 --- a/src/client/testing/common/testUtils.ts +++ b/src/client/testing/common/testUtils.ts @@ -42,10 +42,7 @@ export function extractBetweenDelimiters(content: string, startDelimiter: string export function convertFileToPackage(filePath: string): string { const lastIndex = filePath.lastIndexOf('.'); - return filePath - .substring(0, lastIndex) - .replace(/\//g, '.') - .replace(/\\/g, '.'); + return filePath.substring(0, lastIndex).replace(/\//g, '.').replace(/\\/g, '.'); } @injectable() @@ -114,7 +111,7 @@ export class TestsHelper implements ITestsHelper { } } public flattenTestFiles(testFiles: TestFile[], workspaceFolder: string): Tests { - testFiles.forEach(testFile => this.flatteningVisitor.visitTestFile(testFile)); + testFiles.forEach((testFile) => this.flatteningVisitor.visitTestFile(testFile)); // tslint:disable-next-line:no-object-literal-type-assertion const tests = { @@ -133,7 +130,7 @@ export class TestsHelper implements ITestsHelper { public placeTestFilesIntoFolders(tests: Tests, workspaceFolder: string): void { // First get all the unique folders const folders: string[] = []; - tests.testFiles.forEach(file => { + tests.testFiles.forEach((file) => { const relativePath = path.relative(workspaceFolder, file.fullPath); const dir = path.dirname(relativePath); if (folders.indexOf(dir) === -1) { @@ -145,7 +142,7 @@ export class TestsHelper implements ITestsHelper { const folderMap = new Map(); folders.sort(); const resource = Uri.file(workspaceFolder); - folders.forEach(dir => { + folders.forEach((dir) => { dir.split(path.sep).reduce((parentPath, currentName, _index, _values) => { let newPath = currentName; let parentFolder: TestFolder | undefined; @@ -172,8 +169,8 @@ export class TestsHelper implements ITestsHelper { tests.rootTestFolders.push(testFolder); } tests.testFiles - .filter(fl => path.dirname(path.relative(workspaceFolder, fl.fullPath)) === newPath) - .forEach(testFile => { + .filter((fl) => path.dirname(path.relative(workspaceFolder, fl.fullPath)) === newPath) + .forEach((testFile) => { testFolder.testFiles.push(testFile); }); tests.testFolders.push(testFolder); @@ -192,22 +189,22 @@ export class TestsHelper implements ITestsHelper { } const absolutePath = path.isAbsolute(name) ? name : path.resolve(rootDirectory, name); const testFolders = tests.testFolders.filter( - folder => folder.nameToRun === name || folder.name === name || folder.name === absolutePath + (folder) => folder.nameToRun === name || folder.name === name || folder.name === absolutePath ); if (testFolders.length > 0) { return { testFolder: testFolders }; } const testFiles = tests.testFiles.filter( - file => file.nameToRun === name || file.name === name || file.fullPath === absolutePath + (file) => file.nameToRun === name || file.name === name || file.fullPath === absolutePath ); if (testFiles.length > 0) { return { testFile: testFiles }; } const testFns = tests.testFunctions - .filter(fn => fn.testFunction.nameToRun === name || fn.testFunction.name === name) - .map(fn => fn.testFunction); + .filter((fn) => fn.testFunction.nameToRun === name || fn.testFunction.name === name) + .map((fn) => fn.testFunction); if (testFns.length > 0) { return { testFunction: testFns }; } @@ -232,7 +229,7 @@ export class TestsHelper implements ITestsHelper { }; } public displayTestErrorMessage(message: string) { - this.appShell.showErrorMessage(message, constants.Button_Text_Tests_View_Output).then(action => { + this.appShell.showErrorMessage(message, constants.Button_Text_Tests_View_Output).then((action) => { if (action === constants.Button_Text_Tests_View_Output) { this.commandManager.executeCommand(constants.Commands.Tests_ViewOutput, undefined, CommandSource.ui); } @@ -347,28 +344,28 @@ export function getParent(tests: Tests, data: TestDataItem): TestDataItem | unde const suite = data as TestSuite; if (isSubtestsParent(suite)) { const fn = suite.functions[0]; - const parent = tests.testSuites.find(item => item.testSuite.functions.indexOf(fn) >= 0); + const parent = tests.testSuites.find((item) => item.testSuite.functions.indexOf(fn) >= 0); if (parent) { return parent.testSuite; } - return tests.testFiles.find(item => item.functions.indexOf(fn) >= 0); + return tests.testFiles.find((item) => item.functions.indexOf(fn) >= 0); } - const parentSuite = tests.testSuites.find(item => item.testSuite.suites.indexOf(suite) >= 0); + const parentSuite = tests.testSuites.find((item) => item.testSuite.suites.indexOf(suite) >= 0); if (parentSuite) { return parentSuite.testSuite; } - return tests.testFiles.find(item => item.suites.indexOf(suite) >= 0); + return tests.testFiles.find((item) => item.suites.indexOf(suite) >= 0); } case TestDataItemType.function: { const fn = data as TestFunction; if (fn.subtestParent) { return fn.subtestParent.asSuite; } - const parentSuite = tests.testSuites.find(item => item.testSuite.functions.indexOf(fn) >= 0); + const parentSuite = tests.testSuites.find((item) => item.testSuite.functions.indexOf(fn) >= 0); if (parentSuite) { return parentSuite.testSuite; } - return tests.testFiles.find(item => item.functions.indexOf(fn) >= 0); + return tests.testFiles.find((item) => item.functions.indexOf(fn) >= 0); } default: { throw new Error('Unknown test type'); @@ -434,7 +431,7 @@ export function getParentSuite(tests: Tests, suite: TestSuite | TestFunction): T * @returns {(TestFolder | undefined)} */ function getParentTestFolderForFile(tests: Tests, file: TestFile): TestFolder | undefined { - return tests.testFolders.find(folder => folder.testFiles.some(item => item === file)); + return tests.testFolders.find((folder) => folder.testFiles.some((item) => item === file)); } /** @@ -448,7 +445,7 @@ function getParentTestFolderForFolder(tests: Tests, folder: TestFolder): TestFol if (tests.rootTestFolders.indexOf(folder) >= 0) { return; } - return tests.testFolders.find(item => item.folders.some(child => child === folder)); + return tests.testFolders.find((item) => item.folders.some((child) => child === folder)); } /** @@ -460,7 +457,7 @@ function getParentTestFolderForFolder(tests: Tests, folder: TestFolder): TestFol * @returns {(FlattenedTestFunction | undefined)} */ export function findFlattendTestFunction(tests: Tests, func: TestFunction): FlattenedTestFunction | undefined { - return tests.testFunctions.find(f => f.testFunction === func); + return tests.testFunctions.find((f) => f.testFunction === func); } /** @@ -472,7 +469,7 @@ export function findFlattendTestFunction(tests: Tests, func: TestFunction): Flat * @returns {(FlattenedTestSuite | undefined)} */ export function findFlattendTestSuite(tests: Tests, suite: TestSuite): FlattenedTestSuite | undefined { - return tests.testSuites.find(f => f.testSuite === suite); + return tests.testSuites.find((f) => f.testSuite === suite); } /** @@ -512,7 +509,7 @@ export function getChildren(item: TestDataItem): TestDataItem[] { function divideSubtests(mixed: TestFunction[]): [TestSuite[], TestFunction[]] { const suites: TestSuite[] = []; const functions: TestFunction[] = []; - mixed.forEach(func => { + mixed.forEach((func) => { if (!func.subtestParent) { functions.push(func); return; @@ -542,9 +539,9 @@ export function copyDesiredTestResults(source: Tests, target: Tests): void { } function copyResultsForFolders(source: TestFolder[], target: TestFolder[]): void { - source.forEach(sourceFolder => { + source.forEach((sourceFolder) => { const targetFolder = target.find( - folder => folder.name === sourceFolder.name && folder.nameToRun === sourceFolder.nameToRun + (folder) => folder.name === sourceFolder.name && folder.nameToRun === sourceFolder.nameToRun ); if (!targetFolder) { return; @@ -556,8 +553,8 @@ function copyResultsForFolders(source: TestFolder[], target: TestFolder[]): void }); } function copyResultsForFiles(source: TestFile[], target: TestFile[]): void { - source.forEach(sourceFile => { - const targetFile = target.find(file => file.name === sourceFile.name); + source.forEach((sourceFile) => { + const targetFile = target.find((file) => file.name === sourceFile.name); if (!targetFile) { return; } @@ -570,8 +567,8 @@ function copyResultsForFiles(source: TestFile[], target: TestFile[]): void { } function copyResultsForFunctions(source: TestFunction[], target: TestFunction[]): void { - source.forEach(sourceFn => { - const targetFn = target.find(fn => fn.name === sourceFn.name && fn.nameToRun === sourceFn.nameToRun); + source.forEach((sourceFn) => { + const targetFn = target.find((fn) => fn.name === sourceFn.name && fn.nameToRun === sourceFn.nameToRun); if (!targetFn) { return; } @@ -580,9 +577,9 @@ function copyResultsForFunctions(source: TestFunction[], target: TestFunction[]) } function copyResultsForSuites(source: TestSuite[], target: TestSuite[]): void { - source.forEach(sourceSuite => { + source.forEach((sourceSuite) => { const targetSuite = target.find( - suite => + (suite) => suite.name === sourceSuite.name && suite.nameToRun === sourceSuite.nameToRun && suite.xmlName === sourceSuite.xmlName @@ -599,7 +596,7 @@ function copyResultsForSuites(source: TestSuite[], target: TestSuite[]): void { } function copyValueTypes(source: T, target: T): void { - Object.keys(source).forEach(key => { + Object.keys(source).forEach((key) => { // tslint:disable-next-line:no-any const value = (source as any)[key]; if (['boolean', 'number', 'string', 'undefined'].indexOf(typeof value) >= 0) { diff --git a/src/client/testing/common/testVisitors/flatteningVisitor.ts b/src/client/testing/common/testVisitors/flatteningVisitor.ts index 40d6118d16c9..bca5cc0cc271 100644 --- a/src/client/testing/common/testVisitors/flatteningVisitor.ts +++ b/src/client/testing/common/testVisitors/flatteningVisitor.ts @@ -30,14 +30,14 @@ export class TestFlatteningVisitor implements ITestVisitor { // sample test_three (file name without extension and all / replaced with ., meaning this is the package) const packageName = convertFileToPackage(testFile.name); - testFile.functions.forEach(fn => this.addTestFunction(fn, testFile, packageName)); - testFile.suites.forEach(suite => this.visitTestSuiteOfAFile(suite, testFile)); + testFile.functions.forEach((fn) => this.addTestFunction(fn, testFile, packageName)); + testFile.suites.forEach((suite) => this.visitTestSuiteOfAFile(suite, testFile)); } // tslint:disable-next-line:no-empty public visitTestFolder(_testFile: TestFolder) {} private visitTestSuiteOfAFile(testSuite: TestSuite, parentTestFile: TestFile): void { - testSuite.functions.forEach(fn => this.visitTestFunctionOfASuite(fn, testSuite, parentTestFile)); - testSuite.suites.forEach(suite => this.visitTestSuiteOfAFile(suite, parentTestFile)); + testSuite.functions.forEach((fn) => this.visitTestFunctionOfASuite(fn, testSuite, parentTestFile)); + testSuite.suites.forEach((suite) => this.visitTestSuiteOfAFile(suite, parentTestFile)); this.addTestSuite(testSuite, parentTestFile); } private visitTestFunctionOfASuite( diff --git a/src/client/testing/common/testVisitors/visitor.ts b/src/client/testing/common/testVisitors/visitor.ts index 5056d61e3743..6b511f5200be 100644 --- a/src/client/testing/common/testVisitors/visitor.ts +++ b/src/client/testing/common/testVisitors/visitor.ts @@ -37,7 +37,7 @@ export function visitRecursive(tests: Tests, arg1: TestDataItem | Visitor, arg2? } else { children = tests.rootTestFolders; } - children.forEach(folder => visitRecursive(tests, folder, visitor)); + children.forEach((folder) => visitRecursive(tests, folder, visitor)); } /** diff --git a/src/client/testing/common/updateTestSettings.ts b/src/client/testing/common/updateTestSettings.ts index b4f253b089e1..b1a6d7750420 100644 --- a/src/client/testing/common/updateTestSettings.ts +++ b/src/client/testing/common/updateTestSettings.ts @@ -26,7 +26,7 @@ export class UpdateTestSettingService implements IExtensionActivationService { @traceDecorators.error('Failed to update test settings') public async updateTestSettings(resource: Resource): Promise { const filesToBeFixed = await this.getFilesToBeFixed(resource); - await Promise.all(filesToBeFixed.map(file => this.fixSettingInFile(file))); + await Promise.all(filesToBeFixed.map((file) => this.fixSettingInFile(file))); } public getSettingsFiles(resource: Resource) { const settingsFiles: string[] = []; @@ -42,12 +42,12 @@ export class UpdateTestSettingService implements IExtensionActivationService { public async getFilesToBeFixed(resource: Resource) { const files = this.getSettingsFiles(resource); const result = await Promise.all( - files.map(async file => { + files.map(async (file) => { const needsFixing = await this.doesFileNeedToBeFixed(file); return { file, needsFixing }; }) ); - return result.filter(item => item.needsFixing).map(item => item.file); + return result.filter((item) => item.needsFixing).map((item) => item.file); } @swallowExceptions('Failed to update settings.json') public async fixSettingInFile(filePath: string) { diff --git a/src/client/testing/common/xUnitParser.ts b/src/client/testing/common/xUnitParser.ts index cd1ecd6549f2..76ed830672ba 100644 --- a/src/client/testing/common/xUnitParser.ts +++ b/src/client/testing/common/xUnitParser.ts @@ -124,7 +124,7 @@ function updateTests(tests: Tests, testSuiteResult: TestSuiteResult) { // fn.parentTestSuite && fn.parentTestSuite.name === testcase.$.classname); // Look for failed file test - const fileTest = testcase.$.file && tests.testFiles.find(file => file.nameToRun === testcase.$.file); + const fileTest = testcase.$.file && tests.testFiles.find((file) => file.nameToRun === testcase.$.file); if (fileTest && testcase.error) { updateResultStatus(fileTest, testcase); } @@ -146,12 +146,8 @@ function findTestFunction( className: string, funcName: string ): TestFunction | undefined { - const xmlClassName = className - .replace(/\(\)/g, '') - .replace(/\.\./g, '.') - .replace(/\.\./g, '.') - .replace(/\.+$/, ''); - const flattened = candidates.find(fn => fn.xmlClassName === xmlClassName && fn.testFunction.name === funcName); + const xmlClassName = className.replace(/\(\)/g, '').replace(/\.\./g, '.').replace(/\.\./g, '.').replace(/\.+$/, ''); + const flattened = candidates.find((fn) => fn.xmlClassName === xmlClassName && fn.testFunction.name === funcName); if (!flattened) { return; } diff --git a/src/client/testing/configuration.ts b/src/client/testing/configuration.ts index 374f421cb62b..6c7adde7ede1 100644 --- a/src/client/testing/configuration.ts +++ b/src/client/testing/configuration.ts @@ -102,7 +102,7 @@ export class UnitTestConfigurationService implements ITestConfigurationService { () => { return configMgr.enable(); }, - reason => { + (reason) => { return configMgr.enable().then(() => Promise.reject(reason)); } ); @@ -139,7 +139,7 @@ export class UnitTestConfigurationService implements ITestConfigurationService { await configMgr .configure(wkspace) .then(() => this._enableTest(wkspace, configMgr)) - .catch(reason => { + .catch((reason) => { return this._enableTest(wkspace, configMgr).then(() => Promise.reject(reason)); }); } diff --git a/src/client/testing/display/main.ts b/src/client/testing/display/main.ts index 70ea6e0d9fd4..319ec5d2e3f9 100644 --- a/src/client/testing/display/main.ts +++ b/src/client/testing/display/main.ts @@ -60,7 +60,7 @@ export class TestResultDisplay implements ITestResultDisplay { constants.Commands.Tests_Ask_To_Stop_Test ); testRunResult - .then(tests => this.updateTestRunWithSuccess(tests, debug)) + .then((tests) => this.updateTestRunWithSuccess(tests, debug)) .catch(this.updateTestRunWithFailure.bind(this)) // We don't care about any other exceptions returned by updateTestRunWithFailure .catch(noop); @@ -72,11 +72,11 @@ export class TestResultDisplay implements ITestResultDisplay { constants.Commands.Tests_Ask_To_Stop_Discovery ); return testDiscovery - .then(tests => { + .then((tests) => { this.updateWithDiscoverSuccess(tests, quietMode); return tests; }) - .catch(reason => { + .catch((reason) => { this.updateWithDiscoverFailure(reason); return Promise.reject(reason); }); @@ -187,9 +187,9 @@ export class TestResultDisplay implements ITestResultDisplay { Testing.disableTests(), Testing.configureTests() ) - .then(item => { + .then((item) => { if (item === Testing.disableTests()) { - this.disableTests().catch(ex => traceError('Python Extension: disableTests', ex)); + this.disableTests().catch((ex) => traceError('Python Extension: disableTests', ex)); } else if (item === Testing.configureTests()) { this.cmdManager .executeCommand(constants.Commands.Tests_Configure, undefined, undefined, undefined) diff --git a/src/client/testing/display/picker.ts b/src/client/testing/display/picker.ts index 92ea49421368..7ad8657d337f 100644 --- a/src/client/testing/display/picker.ts +++ b/src/client/testing/display/picker.ts @@ -29,7 +29,7 @@ export class TestDisplay implements ITestDisplay { this.appShell = serviceRegistry.get(IApplicationShell); } public displayStopTestUI(workspace: Uri, message: string) { - this.appShell.showQuickPick([message]).then(item => { + this.appShell.showQuickPick([message]).then((item) => { if (item === message) { this.commandManager.executeCommand(constants.Commands.Tests_Stop, undefined, workspace); } @@ -39,7 +39,7 @@ export class TestDisplay implements ITestDisplay { const tests = this.testCollectionStorage.getTests(wkspace); this.appShell .showQuickPick(buildItems(tests), { matchOnDescription: true, matchOnDetail: true }) - .then(item => + .then((item) => item ? onItemSelected(this.commandManager, cmdSource, wkspace, item, false) : Promise.resolve() ); } @@ -50,7 +50,7 @@ export class TestDisplay implements ITestDisplay { matchOnDescription: true, matchOnDetail: true }) - .then(item => { + .then((item) => { if (item && item.fn) { return resolve(item.fn); } @@ -65,7 +65,7 @@ export class TestDisplay implements ITestDisplay { matchOnDescription: true, matchOnDetail: true }) - .then(item => { + .then((item) => { if (item && item.testFile) { return resolve(item.testFile); } @@ -88,22 +88,22 @@ export class TestDisplay implements ITestDisplay { const fileName = file.fsPath; const fs = this.serviceRegistry.get(IFileSystem); const testFile = tests.testFiles.find( - item => item.name === fileName || fs.arePathsSame(item.fullPath, fileName) + (item) => item.name === fileName || fs.arePathsSame(item.fullPath, fileName) ); if (!testFile) { return; } - const flattenedFunctions = tests.testFunctions.filter(fn => { + const flattenedFunctions = tests.testFunctions.filter((fn) => { return ( fn.parentTestFile.name === testFile.name && - testFunctions.some(testFunc => testFunc.nameToRun === fn.testFunction.nameToRun) + testFunctions.some((testFunc) => testFunc.nameToRun === fn.testFunction.nameToRun) ); }); const runAllItem = buildRunAllParametrizedItem(flattenedFunctions, debug); const functionItems = buildItemsForFunctions(rootDirectory, flattenedFunctions, undefined, undefined, debug); this.appShell .showQuickPick(runAllItem.concat(...functionItems), { matchOnDescription: true, matchOnDetail: true }) - .then(testItem => + .then((testItem) => testItem ? onItemSelected(this.commandManager, cmdSource, wkspace, testItem, debug) : Promise.resolve() ); } @@ -196,7 +196,7 @@ const statusSortPrefix = { function buildRunAllParametrizedItem(tests: FlattenedTestFunction[], debug: boolean = false): TestItem[] { const testFunctions: TestFunction[] = []; - tests.forEach(fn => { + tests.forEach((fn) => { testFunctions.push(fn.testFunction); }); return [ @@ -216,7 +216,7 @@ function buildItemsForFunctions( debug: boolean = false ): TestItem[] { const functionItems: TestItem[] = []; - tests.forEach(fn => { + tests.forEach((fn) => { let icon = ''; if (displayStatusIcons && fn.testFunction.status && statusIconMapping.has(fn.testFunction.status)) { icon = `${statusIconMapping.get(fn.testFunction.status)} `; @@ -252,7 +252,7 @@ function buildItemsForFunctions( return functionItems; } function buildItemsForTestFiles(rootDirectory: string, testFiles: TestFile[]): TestFileItem[] { - const fileItems: TestFileItem[] = testFiles.map(testFile => { + const fileItems: TestFileItem[] = testFiles.map((testFile) => { return { description: '', detail: path.relative(rootDirectory, testFile.fullPath), diff --git a/src/client/testing/explorer/commandHandlers.ts b/src/client/testing/explorer/commandHandlers.ts index e1604f82d86a..2dd96039c491 100644 --- a/src/client/testing/explorer/commandHandlers.ts +++ b/src/client/testing/explorer/commandHandlers.ts @@ -40,7 +40,7 @@ export class TestExplorerCommandHandler implements ITestExplorerCommandHandler { ); } public dispose(): void { - this.disposables.forEach(item => item.dispose()); + this.disposables.forEach((item) => item.dispose()); } @swallowExceptions('Run test node') @traceDecorators.error('Run test node failed') diff --git a/src/client/testing/explorer/failedTestHandler.ts b/src/client/testing/explorer/failedTestHandler.ts index d9abe37a8e6c..ee2ae7a3ba7b 100644 --- a/src/client/testing/explorer/failedTestHandler.ts +++ b/src/client/testing/explorer/failedTestHandler.ts @@ -27,7 +27,7 @@ export class FailedTestHandler implements IExtensionSingleActivationService, IDi disposableRegistry.push(this); } public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } public async activate(): Promise { this.storage.onDidChange(this.onDidChangeTestData, this, this.disposables); diff --git a/src/client/testing/explorer/testTreeViewProvider.ts b/src/client/testing/explorer/testTreeViewProvider.ts index 5a3af0c501b7..54c8a5fa00de 100644 --- a/src/client/testing/explorer/testTreeViewProvider.ts +++ b/src/client/testing/explorer/testTreeViewProvider.ts @@ -45,7 +45,7 @@ export class TestTreeViewProvider implements ITestTreeViewProvider, ITestDataIte disposableRegistry.push(this); this.testsAreBeingDiscovered = new Map(); this.disposables.push(this.testService.onDidStatusChange(this.onTestStatusChanged, this)); - this.testStore.onDidChange(e => this._onDidChangeTreeData.fire(e.data), this, this.disposables); + this.testStore.onDidChange((e) => this._onDidChangeTreeData.fire(e.data), this, this.disposables); this.workspace.onDidChangeWorkspaceFolders(() => this._onDidChangeTreeData.fire(), this, this.disposables); if (Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 0) { @@ -70,7 +70,7 @@ export class TestTreeViewProvider implements ITestTreeViewProvider, ITestDataIte * from our internal emitter. */ public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); this._onDidChangeTreeData.dispose(); } @@ -128,7 +128,7 @@ export class TestTreeViewProvider implements ITestTreeViewProvider, ITestDataIte // If we are in a mult-root workspace, then nest the test data within a // virtual node, represending the workspace folder. - return this.workspace.workspaceFolders.map(workspaceFolder => new TestWorkspaceFolder(workspaceFolder)); + return this.workspace.workspaceFolders.map((workspaceFolder) => new TestWorkspaceFolder(workspaceFolder)); } /** diff --git a/src/client/testing/explorer/treeView.ts b/src/client/testing/explorer/treeView.ts index acd4d64ccf0b..72872bb0853c 100644 --- a/src/client/testing/explorer/treeView.ts +++ b/src/client/testing/explorer/treeView.ts @@ -27,7 +27,7 @@ export class TreeViewService implements IExtensionSingleActivationService, IDisp disposableRegistry.push(this); } public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } public async activate(): Promise { this._treeView = this.appShell.createTreeView('python_tests', { diff --git a/src/client/testing/main.ts b/src/client/testing/main.ts index 14ae0c71e0ab..f7e84b4b1ff0 100644 --- a/src/client/testing/main.ts +++ b/src/client/testing/main.ts @@ -103,7 +103,9 @@ export class UnitTestManagementService implements ITestManagementService, Dispos this.registerHandlers(); this.registerCommands(); this.checkExperiments(); - this.autoDiscoverTests(undefined).catch(ex => traceError('Failed to auto discover tests upon activation', ex)); + this.autoDiscoverTests(undefined).catch((ex) => + traceError('Failed to auto discover tests upon activation', ex) + ); await this.registerSymbolProvider(symbolProvider); } public checkExperiments() { @@ -134,7 +136,7 @@ export class UnitTestManagementService implements ITestManagementService, Dispos if (testManager) { if (!this.testManagers.has(testManager)) { this.testManagers.add(testManager); - const handler = testManager.onDidStatusChange(e => this._onDidStatusChange.fire(e)); + const handler = testManager.onDidStatusChange((e) => this._onDidStatusChange.fire(e)); this.disposableRegistry.push(handler); } return testManager; @@ -155,7 +157,7 @@ export class UnitTestManagementService implements ITestManagementService, Dispos if (!Array.isArray(this.workspaceService.workspaceFolders)) { return; } - const workspaceFolderUri = this.workspaceService.workspaceFolders.find(w => + const workspaceFolderUri = this.workspaceService.workspaceFolders.find((w) => eventArgs.affectsConfiguration('python.testing', w.uri) ); if (!workspaceFolderUri) { @@ -183,7 +185,7 @@ export class UnitTestManagementService implements ITestManagementService, Dispos if (this.testResultDisplay) { this.testResultDisplay.enabled = true; } - this.autoDiscoverTests(workspaceUri).catch(ex => + this.autoDiscoverTests(workspaceUri).catch((ex) => traceError('Failed to auto discover tests upon activation', ex) ); } @@ -258,7 +260,7 @@ export class UnitTestManagementService implements ITestManagementService, Dispos ); this.testResultDisplay .displayDiscoverStatus(discoveryPromise, quietMode) - .catch(ex => traceError('Python Extension: displayDiscoverStatus', ex)); + .catch((ex) => traceError('Python Extension: displayDiscoverStatus', ex)); await discoveryPromise; } public async stopTests(resource: Uri) { @@ -387,7 +389,7 @@ export class UnitTestManagementService implements ITestManagementService, Dispos ITestCollectionStorageService ); const tests = testCollectionStorage.getTests(testManager.workspaceFolder)!; - const testFiles = tests.testFiles.filter(testFile => { + const testFiles = tests.testFiles.filter((testFile) => { return testFile.fullPath === this.documentManager.activeTextEditor!.document.uri.fsPath; }); if (testFiles.length < 1) { @@ -412,7 +414,7 @@ export class UnitTestManagementService implements ITestManagementService, Dispos this.testResultDisplay = this.serviceContainer.get(ITestResultDisplay); } - const promise = testManager.runTest(cmdSource, testsToRun, runFailedTests, debug).catch(reason => { + const promise = testManager.runTest(cmdSource, testsToRun, runFailedTests, debug).catch((reason) => { if (reason !== CANCELLATION_REASON) { this.outputChannel.appendLine(`Error: ${reason}`); } @@ -429,7 +431,7 @@ export class UnitTestManagementService implements ITestManagementService, Dispos ); const event = new EventEmitter(); this.disposableRegistry.push(event); - const handler = this._onDidStatusChange.event(e => { + const handler = this._onDidStatusChange.event((e) => { if (e.status !== TestStatus.Discovering && e.status !== TestStatus.Running) { event.fire(); } @@ -601,7 +603,7 @@ export class UnitTestManagementService implements ITestManagementService, Dispos this.disposableRegistry.push(documentManager.onDidSaveTextDocument(this.onDocumentSaved.bind(this))); this.disposableRegistry.push( - this.workspaceService.onDidChangeConfiguration(e => { + this.workspaceService.onDidChangeConfiguration((e) => { if (this.configChangedTimer) { clearTimeout(this.configChangedTimer as any); } @@ -610,7 +612,7 @@ export class UnitTestManagementService implements ITestManagementService, Dispos ); this.disposableRegistry.push( interpreterService.onDidChangeInterpreter(() => - this.autoDiscoverTests(undefined).catch(ex => + this.autoDiscoverTests(undefined).catch((ex) => traceError('Failed to auto discover tests upon changing interpreter', ex) ) ) diff --git a/src/client/testing/navigation/commandHandler.ts b/src/client/testing/navigation/commandHandler.ts index 6f6b92b06798..27df132391bb 100644 --- a/src/client/testing/navigation/commandHandler.ts +++ b/src/client/testing/navigation/commandHandler.ts @@ -28,7 +28,7 @@ export class TestCodeNavigatorCommandHandler implements ITestCodeNavigatorComman disposableRegistry.push(this); } public dispose() { - this.disposables.forEach(item => item.dispose()); + this.disposables.forEach((item) => item.dispose()); } public register(): void { if (this.disposables.length > 0) { diff --git a/src/client/testing/navigation/symbolProvider.ts b/src/client/testing/navigation/symbolProvider.ts index 676654094c34..7a308990985d 100644 --- a/src/client/testing/navigation/symbolProvider.ts +++ b/src/client/testing/navigation/symbolProvider.ts @@ -38,9 +38,9 @@ export class TestFileSymbolProvider implements DocumentSymbolProvider { return []; } return [ - ...rawSymbols.classes.map(item => this.parseRawSymbol(document.uri, item, SymbolKind.Class)), - ...rawSymbols.methods.map(item => this.parseRawSymbol(document.uri, item, SymbolKind.Method)), - ...rawSymbols.functions.map(item => this.parseRawSymbol(document.uri, item, SymbolKind.Function)) + ...rawSymbols.classes.map((item) => this.parseRawSymbol(document.uri, item, SymbolKind.Class)), + ...rawSymbols.methods.map((item) => this.parseRawSymbol(document.uri, item, SymbolKind.Method)), + ...rawSymbols.functions.map((item) => this.parseRawSymbol(document.uri, item, SymbolKind.Function)) ]; } private parseRawSymbol(uri: Uri, symbol: RawSymbol, kind: SymbolKind): SymbolInformation { diff --git a/src/client/testing/nosetest/runner.ts b/src/client/testing/nosetest/runner.ts index 3db55dfdeb79..e29a95e155ce 100644 --- a/src/client/testing/nosetest/runner.ts +++ b/src/client/testing/nosetest/runner.ts @@ -42,16 +42,16 @@ export class TestManagerRunner implements ITestManagerRunner { ): Promise { let testPaths: string[] = []; if (options.testsToRun && options.testsToRun.testFolder) { - testPaths = testPaths.concat(options.testsToRun.testFolder.map(f => f.nameToRun)); + testPaths = testPaths.concat(options.testsToRun.testFolder.map((f) => f.nameToRun)); } if (options.testsToRun && options.testsToRun.testFile) { - testPaths = testPaths.concat(options.testsToRun.testFile.map(f => f.nameToRun)); + testPaths = testPaths.concat(options.testsToRun.testFile.map((f) => f.nameToRun)); } if (options.testsToRun && options.testsToRun.testSuite) { - testPaths = testPaths.concat(options.testsToRun.testSuite.map(f => f.nameToRun)); + testPaths = testPaths.concat(options.testsToRun.testSuite.map((f) => f.nameToRun)); } if (options.testsToRun && options.testsToRun.testFunction) { - testPaths = testPaths.concat(options.testsToRun.testFunction.map(f => f.nameToRun)); + testPaths = testPaths.concat(options.testsToRun.testFunction.map((f) => f.nameToRun)); } let deleteJUnitXmlFile: Function = noop; diff --git a/src/client/testing/nosetest/services/argsService.ts b/src/client/testing/nosetest/services/argsService.ts index 0e038ea41b7d..cba04ef343b2 100644 --- a/src/client/testing/nosetest/services/argsService.ts +++ b/src/client/testing/nosetest/services/argsService.ts @@ -130,7 +130,7 @@ export class ArgumentsService implements IArgumentsService { // So if we want to run a specific test, then remove positional args. let removePositionalArgs = false; if (Array.isArray(argumentToRemoveOrFilter)) { - argumentToRemoveOrFilter.forEach(item => { + argumentToRemoveOrFilter.forEach((item) => { if (OptionsWithArguments.indexOf(item) >= 0) { optionsWithArgsToRemove.push(item); } @@ -154,10 +154,10 @@ export class ArgumentsService implements IArgumentsService { '-x', '--stop', '--with-coverage', - ...OptionsWithoutArguments.filter(item => item.startsWith('--cover')), - ...OptionsWithoutArguments.filter(item => item.startsWith('--logging')), - ...OptionsWithoutArguments.filter(item => item.startsWith('--pdb')), - ...OptionsWithoutArguments.filter(item => item.indexOf('xunit') >= 0) + ...OptionsWithoutArguments.filter((item) => item.startsWith('--cover')), + ...OptionsWithoutArguments.filter((item) => item.startsWith('--logging')), + ...OptionsWithoutArguments.filter((item) => item.startsWith('--pdb')), + ...OptionsWithoutArguments.filter((item) => item.indexOf('xunit') >= 0) ] ); optionsWithArgsToRemove.push( @@ -166,9 +166,9 @@ export class ArgumentsService implements IArgumentsService { '-l', '--debug', '--cover-package', - ...OptionsWithoutArguments.filter(item => item.startsWith('--cover')), - ...OptionsWithArguments.filter(item => item.startsWith('--logging')), - ...OptionsWithoutArguments.filter(item => item.indexOf('xunit') >= 0) + ...OptionsWithoutArguments.filter((item) => item.startsWith('--cover')), + ...OptionsWithArguments.filter((item) => item.startsWith('--logging')), + ...OptionsWithoutArguments.filter((item) => item.indexOf('xunit') >= 0) ] ); break; @@ -195,7 +195,7 @@ export class ArgumentsService implements IArgumentsService { OptionsWithArguments, OptionsWithoutArguments ); - filteredArgs = filteredArgs.filter(item => positionalArgs.indexOf(item) === -1); + filteredArgs = filteredArgs.filter((item) => positionalArgs.indexOf(item) === -1); } return this.helper.filterArguments(filteredArgs, optionsWithArgsToRemove, optionsWithoutArgsToRemove); } diff --git a/src/client/testing/nosetest/services/parserService.ts b/src/client/testing/nosetest/services/parserService.ts index e67844cf9591..80fb184dfc97 100644 --- a/src/client/testing/nosetest/services/parserService.ts +++ b/src/client/testing/nosetest/services/parserService.ts @@ -26,7 +26,7 @@ export class TestsParser implements ITestsParser { public parse(content: string, options: ParserOptions): Tests { let testFiles = this.getTestFiles(content, options); // Exclude tests that don't have any functions or test suites. - testFiles = testFiles.filter(testFile => testFile.suites.length > 0 || testFile.functions.length > 0); + testFiles = testFiles.filter((testFile) => testFile.suites.length > 0 || testFile.functions.length > 0); return this.testsHelper.flattenTestFiles(testFiles, options.cwd); } @@ -82,7 +82,7 @@ export class TestsParser implements ITestsParser { let testFile: TestFile; const resource = Uri.file(rootDirectory); // tslint:disable-next-line: max-func-body-length - lines.forEach(line => { + lines.forEach((line) => { if (line.startsWith(NOSE_WANT_FILE_PREFIX) && line.endsWith(NOSE_WANT_FILE_SUFFIX)) { fileName = line.substring(NOSE_WANT_FILE_PREFIX.length); fileName = fileName.substring(0, fileName.lastIndexOf(NOSE_WANT_FILE_SUFFIX_WITHOUT_EXT)); @@ -165,7 +165,7 @@ export class TestsParser implements ITestsParser { functionsPassed: 0 }; - const cls = testFile.suites.find(suite => suite.name === clsName); + const cls = testFile.suites.find((suite) => suite.name === clsName); if (cls) { cls.functions.push(fn); } diff --git a/src/client/testing/pytest/runner.ts b/src/client/testing/pytest/runner.ts index acdf21fa0629..6b1b885db74b 100644 --- a/src/client/testing/pytest/runner.ts +++ b/src/client/testing/pytest/runner.ts @@ -39,16 +39,16 @@ export class TestManagerRunner implements ITestManagerRunner { ): Promise { let testPaths: string[] = []; if (options.testsToRun && options.testsToRun.testFolder) { - testPaths = testPaths.concat(options.testsToRun.testFolder.map(f => f.nameToRun)); + testPaths = testPaths.concat(options.testsToRun.testFolder.map((f) => f.nameToRun)); } if (options.testsToRun && options.testsToRun.testFile) { - testPaths = testPaths.concat(options.testsToRun.testFile.map(f => f.nameToRun)); + testPaths = testPaths.concat(options.testsToRun.testFile.map((f) => f.nameToRun)); } if (options.testsToRun && options.testsToRun.testSuite) { - testPaths = testPaths.concat(options.testsToRun.testSuite.map(f => f.nameToRun)); + testPaths = testPaths.concat(options.testsToRun.testSuite.map((f) => f.nameToRun)); } if (options.testsToRun && options.testsToRun.testFunction) { - testPaths = testPaths.concat(options.testsToRun.testFunction.map(f => f.nameToRun)); + testPaths = testPaths.concat(options.testsToRun.testFunction.map((f) => f.nameToRun)); } let deleteJUnitXmlFile: Function = noop; diff --git a/src/client/testing/pytest/services/argsService.ts b/src/client/testing/pytest/services/argsService.ts index fbc4c1e9102a..8223841f46fc 100644 --- a/src/client/testing/pytest/services/argsService.ts +++ b/src/client/testing/pytest/services/argsService.ts @@ -152,7 +152,7 @@ export class ArgumentsService implements IArgumentsService { // So if we want to run a specific test, then remove positional args. let removePositionalArgs = false; if (Array.isArray(argumentToRemoveOrFilter)) { - argumentToRemoveOrFilter.forEach(item => { + argumentToRemoveOrFilter.forEach((item) => { if (OptionsWithArguments.indexOf(item) >= 0) { optionsWithArgsToRemove.push(item); } @@ -261,7 +261,7 @@ export class ArgumentsService implements IArgumentsService { OptionsWithArguments, OptionsWithoutArguments ); - filteredArgs = filteredArgs.filter(item => positionalArgs.indexOf(item) === -1); + filteredArgs = filteredArgs.filter((item) => positionalArgs.indexOf(item) === -1); } return this.helper.filterArguments(filteredArgs, optionsWithArgsToRemove, optionsWithoutArgsToRemove); } @@ -277,6 +277,6 @@ export class ArgumentsService implements IArgumentsService { // Positional args in pytest are files or directories. // Remove files from the args, and what's left are test directories. // If users enter test modules/methods, then its not supported. - return positionalArgs.filter(arg => !arg.toUpperCase().endsWith('.PY')); + return positionalArgs.filter((arg) => !arg.toUpperCase().endsWith('.PY')); } } diff --git a/src/client/testing/pytest/services/discoveryService.ts b/src/client/testing/pytest/services/discoveryService.ts index 8931b65d04ec..965336217405 100644 --- a/src/client/testing/pytest/services/discoveryService.ts +++ b/src/client/testing/pytest/services/discoveryService.ts @@ -29,7 +29,7 @@ export class TestDiscoveryService implements ITestDiscoveryService { return this.discoverTestsInTestDirectory(opts); } const results = await Promise.all( - testDirectories.map(testDir => { + testDirectories.map((testDir) => { // Add test directory as a positional argument. const opts = { ...options, diff --git a/src/client/testing/pytest/testConfigurationManager.ts b/src/client/testing/pytest/testConfigurationManager.ts index e0c618231408..2c3b627342a1 100644 --- a/src/client/testing/pytest/testConfigurationManager.ts +++ b/src/client/testing/pytest/testConfigurationManager.ts @@ -47,10 +47,10 @@ export class ConfigurationManager extends TestConfigurationManager { } private async getConfigFiles(rootDir: string): Promise { const fs = this.serviceContainer.get(IFileSystem); - const promises = ['pytest.ini', 'tox.ini', 'setup.cfg'].map(async cfg => + const promises = ['pytest.ini', 'tox.ini', 'setup.cfg'].map(async (cfg) => (await fs.fileExists(path.join(rootDir, cfg))) ? cfg : '' ); const values = await Promise.all(promises); - return values.filter(exists => exists.length > 0); + return values.filter((exists) => exists.length > 0); } } diff --git a/src/client/testing/serviceRegistry.ts b/src/client/testing/serviceRegistry.ts index 5cea701c7231..5ebb8dc4f36f 100644 --- a/src/client/testing/serviceRegistry.ts +++ b/src/client/testing/serviceRegistry.ts @@ -159,7 +159,7 @@ export function registerTypes(serviceManager: IServiceManager) { ); serviceManager.addSingleton(IExtensionActivationService, UpdateTestSettingService); - serviceManager.addFactory(ITestManagerFactory, context => { + serviceManager.addFactory(ITestManagerFactory, (context) => { return (testProvider: TestProvider, workspaceFolder: Uri, rootDirectory: string) => { const serviceContainer = context.container.get(IServiceContainer); @@ -180,7 +180,7 @@ export function registerTypes(serviceManager: IServiceManager) { }; }); - serviceManager.addFactory(ITestManagerServiceFactory, context => { + serviceManager.addFactory(ITestManagerServiceFactory, (context) => { return (workspaceFolder: Uri) => { const serviceContainer = context.container.get(IServiceContainer); const testsHelper = context.container.get(ITestsHelper); diff --git a/src/client/testing/unittest/helper.ts b/src/client/testing/unittest/helper.ts index 1a972775b62e..be77ed07a7eb 100644 --- a/src/client/testing/unittest/helper.ts +++ b/src/client/testing/unittest/helper.ts @@ -29,8 +29,8 @@ export class UnitTestHelper implements IUnitTestHelper { const testIds: string[] = []; if (testsToRun && testsToRun.testFolder) { // Get test ids of files in these folders. - testsToRun.testFolder.forEach(folder => { - tests.testFiles.forEach(f => { + testsToRun.testFolder.forEach((folder) => { + tests.testFiles.forEach((f) => { if (f.fullPath.startsWith(folder.name)) { testIds.push(f.nameToRun); } @@ -38,13 +38,13 @@ export class UnitTestHelper implements IUnitTestHelper { }); } if (testsToRun && testsToRun.testFile) { - testIds.push(...testsToRun.testFile.map(f => f.nameToRun)); + testIds.push(...testsToRun.testFile.map((f) => f.nameToRun)); } if (testsToRun && testsToRun.testSuite) { - testIds.push(...testsToRun.testSuite.map(f => f.nameToRun)); + testIds.push(...testsToRun.testSuite.map((f) => f.nameToRun)); } if (testsToRun && testsToRun.testFunction) { - testIds.push(...testsToRun.testFunction.map(f => f.nameToRun)); + testIds.push(...testsToRun.testFunction.map((f) => f.nameToRun)); } return testIds; } diff --git a/src/client/testing/unittest/main.ts b/src/client/testing/unittest/main.ts index 6d91cd9a2a6e..cb81d3de5f23 100644 --- a/src/client/testing/unittest/main.ts +++ b/src/client/testing/unittest/main.ts @@ -43,10 +43,10 @@ export class TestManager extends BaseTestManager { if (runFailedTests === true && this.tests) { testsToRun = { testFile: [], testFolder: [], testSuite: [], testFunction: [] }; testsToRun.testFunction = this.tests.testFunctions - .filter(fn => { + .filter((fn) => { return fn.testFunction.status === TestStatus.Error || fn.testFunction.status === TestStatus.Fail; }) - .map(fn => fn.testFunction); + .map((fn) => fn.testFunction); } return super.runTest(cmdSource, testsToRun, runFailedTests, debug); } diff --git a/src/client/testing/unittest/runner.ts b/src/client/testing/unittest/runner.ts index e660bee5a8d2..a5fe4353cfb9 100644 --- a/src/client/testing/unittest/runner.ts +++ b/src/client/testing/unittest/runner.ts @@ -79,7 +79,7 @@ export class TestManagerRunner implements ITestManagerRunner { this.server.on('connect', noop); this.server.on('start', noop); this.server.on('result', (data: ITestData) => { - const test = options.tests.testFunctions.find(t => t.testFunction.nameToRun === data.test); + const test = options.tests.testFunctions.find((t) => t.testFunction.nameToRun === data.test); const statusDetails = outcomeMapping.get(data.outcome)!; if (test) { test.testFunction.status = statusDetails.status; @@ -123,7 +123,7 @@ export class TestManagerRunner implements ITestManagerRunner { const runTestInternal = async (testFile: string = '', testId: string = '') => { let testArgs = this.buildTestArgs(options.args); failFast = testArgs.indexOf('--uf') >= 0; - testArgs = testArgs.filter(arg => arg !== '--uf'); + testArgs = testArgs.filter((arg) => arg !== '--uf'); testArgs.push(`--result-port=${port}`); if (testId.length > 0) { @@ -168,7 +168,7 @@ export class TestManagerRunner implements ITestManagerRunner { } if (Array.isArray(options.testsToRun.testSuite)) { for (const testSuite of options.testsToRun.testSuite) { - const item = options.tests.testSuites.find(t => t.testSuite === testSuite); + const item = options.tests.testSuites.find((t) => t.testSuite === testSuite); if (item) { const testFileName = item.parentTestFile.fullPath; await runTestInternal(testFileName, testSuite.nameToRun); @@ -177,7 +177,7 @@ export class TestManagerRunner implements ITestManagerRunner { } if (Array.isArray(options.testsToRun.testFunction)) { for (const testFn of options.testsToRun.testFunction) { - const item = options.tests.testFunctions.find(t => t.testFunction === testFn); + const item = options.tests.testFunctions.find((t) => t.testFunction === testFn); if (item) { const testFileName = item.parentTestFile.fullPath; await runTestInternal(testFileName, testFn.nameToRun); @@ -201,7 +201,7 @@ export class TestManagerRunner implements ITestManagerRunner { private async removeListenersAfter(after: Promise): Promise { return after .then(() => this.server.removeAllListeners()) - .catch(err => { + .catch((err) => { this.server.removeAllListeners(); throw err; // keep propagating this downward }); @@ -217,8 +217,8 @@ export class TestManagerRunner implements ITestManagerRunner { } else if (typeof longValueValue === 'string') { pattern = longValueValue; } - const failFast = args.some(arg => arg.trim() === '-f' || arg.trim() === '--failfast'); - const verbosity = args.some(arg => arg.trim().indexOf('-v') === 0) ? 2 : 1; + const failFast = args.some((arg) => arg.trim() === '-f' || arg.trim() === '--failfast'); + const verbosity = args.some((arg) => arg.trim().indexOf('-v') === 0) ? 2 : 1; const testArgs = [`--us=${startTestDiscoveryDirectory}`, `--up=${pattern}`, `--uvInt=${verbosity}`]; if (failFast) { testArgs.push('--uf'); diff --git a/src/client/testing/unittest/services/argsService.ts b/src/client/testing/unittest/services/argsService.ts index 13a50b5a50dd..73e27087a01e 100644 --- a/src/client/testing/unittest/services/argsService.ts +++ b/src/client/testing/unittest/services/argsService.ts @@ -47,7 +47,7 @@ export class ArgumentsService implements IArgumentsService { // So if we want to run a specific test, then remove positional args. let removePositionalArgs = false; if (Array.isArray(argumentToRemoveOrFilter)) { - argumentToRemoveOrFilter.forEach(item => { + argumentToRemoveOrFilter.forEach((item) => { if (OptionsWithArguments.indexOf(item) >= 0) { optionsWithArgsToRemove.push(item); } @@ -66,7 +66,7 @@ export class ArgumentsService implements IArgumentsService { OptionsWithArguments, OptionsWithoutArguments ); - filteredArgs = filteredArgs.filter(item => positionalArgs.indexOf(item) === -1); + filteredArgs = filteredArgs.filter((item) => positionalArgs.indexOf(item) === -1); } return this.helper.filterArguments(filteredArgs, optionsWithArgsToRemove, optionsWithoutArgsToRemove); } diff --git a/src/client/testing/unittest/services/parserService.ts b/src/client/testing/unittest/services/parserService.ts index ee01d95e32f1..c097acc6a7cd 100644 --- a/src/client/testing/unittest/services/parserService.ts +++ b/src/client/testing/unittest/services/parserService.ts @@ -31,7 +31,7 @@ export class TestsParser implements ITestsParser { let startedCollecting = false; return content .split(/\r?\n/g) - .map(line => { + .map((line) => { if (!startedCollecting) { if (line === 'start') { startedCollecting = true; @@ -40,11 +40,11 @@ export class TestsParser implements ITestsParser { } return line.trim(); }) - .filter(line => line.length > 0); + .filter((line) => line.length > 0); } private parseTestIds(workspaceDirectory: string, testsDirectory: string, testIds: string[]): Tests { const testFiles: TestFile[] = []; - testIds.forEach(testId => this.addTestId(testsDirectory, testId, testFiles)); + testIds.forEach((testId) => this.addTestId(testsDirectory, testId, testFiles)); return this.testsHelper.flattenTestFiles(testFiles, workspaceDirectory); } @@ -76,7 +76,7 @@ export class TestsParser implements ITestsParser { const resource = Uri.file(rootDirectory); // Check if we already have this test file - let testFile = testFiles.find(test => test.fullPath === filePath); + let testFile = testFiles.find((test) => test.fullPath === filePath); if (!testFile) { testFile = { resource, @@ -94,7 +94,7 @@ export class TestsParser implements ITestsParser { // Check if we already have this suite // nameToRun = testId - method name - let testSuite = testFile.suites.find(cls => cls.nameToRun === suiteToRun); + let testSuite = testFile.suites.find((cls) => cls.nameToRun === suiteToRun); if (!testSuite) { testSuite = { resource, diff --git a/src/client/testing/unittest/socketServer.ts b/src/client/testing/unittest/socketServer.ts index 56c153775567..9dc23b87e33a 100644 --- a/src/client/testing/unittest/socketServer.ts +++ b/src/client/testing/unittest/socketServer.ts @@ -34,7 +34,7 @@ export class UnitTestSocketServer extends EventEmitter implements IUnitTestSocke this.startedDef = createDeferred(); this.server = net.createServer(this.connectionListener.bind(this)); this.server!.maxConnections = MaxConnections; - this.server!.on('error', err => { + this.server!.on('error', (err) => { if (this.startedDef) { this.startedDef.reject(err); this.startedDef = undefined; @@ -61,11 +61,11 @@ export class UnitTestSocketServer extends EventEmitter implements IUnitTestSocke this.ipcBuffer = ''; this.onCloseSocket(); }); - socket.on('error', err => { + socket.on('error', (err) => { this.log('server socket error', err); this.emit('error', err); }); - socket.on('data', data => { + socket.on('data', (data) => { const sock = socket; // Assume we have just one client socket connection let dataStr = (this.ipcBuffer += data); diff --git a/src/client/typeFormatters/blockFormatProvider.ts b/src/client/typeFormatters/blockFormatProvider.ts index a75ef8640ad5..822caa5632e9 100644 --- a/src/client/typeFormatters/blockFormatProvider.ts +++ b/src/client/typeFormatters/blockFormatProvider.ts @@ -70,7 +70,7 @@ export class BlockFormatProviders implements OnTypeFormattingEditProvider { } const currentLineText = currentLine.text; - const provider = this.providers.find(p => p.canProvideEdits(currentLineText)); + const provider = this.providers.find((p) => p.canProvideEdits(currentLineText)); if (provider) { return provider.provideEdits(document, position, ch, options, currentLine); } diff --git a/src/client/typeFormatters/codeBlockFormatProvider.ts b/src/client/typeFormatters/codeBlockFormatProvider.ts index 80d1d383cf18..25d677003bb9 100644 --- a/src/client/typeFormatters/codeBlockFormatProvider.ts +++ b/src/client/typeFormatters/codeBlockFormatProvider.ts @@ -31,11 +31,11 @@ export class CodeBlockFormatProvider { // Oops, we've reached a boundary (like the function or class definition) // Get out of here - if (this.boundaryRegExps.some(value => value.test(prevLineText))) { + if (this.boundaryRegExps.some((value) => value.test(prevLineText))) { return []; } - const blockRegEx = this.previousBlockRegExps.find(value => value.test(prevLineText)); + const blockRegEx = this.previousBlockRegExps.find((value) => value.test(prevLineText)); if (!blockRegEx) { continue; } diff --git a/src/client/workspaceSymbols/generator.ts b/src/client/workspaceSymbols/generator.ts index 57f9cfc474ef..e90db302d26f 100644 --- a/src/client/workspaceSymbols/generator.ts +++ b/src/client/workspaceSymbols/generator.ts @@ -32,7 +32,7 @@ export class Generator implements Disposable { } public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } public async generateWorkspaceTags(): Promise { if (!this.pythonSettings.workspaceSymbols.enabled) { @@ -42,7 +42,7 @@ export class Generator implements Disposable { } private buildCmdArgs(): string[] { const exclusions = this.pythonSettings.workspaceSymbols.exclusionPatterns; - const excludes = exclusions.length === 0 ? [] : exclusions.map(pattern => `--exclude=${pattern}`); + const excludes = exclusions.length === 0 ? [] : exclusions.map((pattern) => `--exclude=${pattern}`); return [`--options=${this.optionsFile}`, '--languages=Python'].concat(excludes); } @@ -72,7 +72,7 @@ export class Generator implements Disposable { const result = processService.execObservable(cmd, args, { cwd: source.directory }); let errorMsg = ''; result.out.subscribe( - output => { + (output) => { if (output.source === 'stderr') { errorMsg += output.out; } diff --git a/src/client/workspaceSymbols/main.ts b/src/client/workspaceSymbols/main.ts index 40f0b3d6a022..256373af7a9c 100644 --- a/src/client/workspaceSymbols/main.ts +++ b/src/client/workspaceSymbols/main.ts @@ -40,11 +40,11 @@ export class WorkspaceSymbols implements Disposable { new WorkspaceSymbolProvider(this.fs, this.commandMgr, this.generators) ); this.disposables.push(this.workspace.onDidChangeWorkspaceFolders(() => this.initializeGenerators())); - this.disposables.push(this.documents.onDidSaveTextDocument(e => this.onDocumentSaved(e))); + this.disposables.push(this.documents.onDidSaveTextDocument((e) => this.onDocumentSaved(e))); this.buildSymbolsOnStart(); } public dispose() { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach((d) => d.dispose()); } private initializeGenerators() { while (this.generators.length > 0) { @@ -53,7 +53,7 @@ export class WorkspaceSymbols implements Disposable { } if (Array.isArray(this.workspace.workspaceFolders)) { - this.workspace.workspaceFolders.forEach(wkSpc => { + this.workspace.workspaceFolders.forEach((wkSpc) => { this.generators.push( new Generator( wkSpc.uri, @@ -70,7 +70,7 @@ export class WorkspaceSymbols implements Disposable { private buildSymbolsOnStart() { if (Array.isArray(this.workspace.workspaceFolders)) { - this.workspace.workspaceFolders.forEach(workspaceFolder => { + this.workspace.workspaceFolders.forEach((workspaceFolder) => { const pythonSettings = this.configurationService.getSettings(workspaceFolder.uri); if (pythonSettings.workspaceSymbols.rebuildOnStart) { const promises = this.buildWorkspaceSymbols(true); @@ -112,7 +112,7 @@ export class WorkspaceSymbols implements Disposable { let promptPromise: Promise; let promptResponse: InstallerResponse; - return this.generators.map(async generator => { + return this.generators.map(async (generator) => { if (!generator.enabled) { return; } diff --git a/src/client/workspaceSymbols/parser.ts b/src/client/workspaceSymbols/parser.ts index 419d2821b048..7ec49ba082ce 100644 --- a/src/client/workspaceSymbols/parser.ts +++ b/src/client/workspaceSymbols/parser.ts @@ -99,7 +99,7 @@ const newValuesAndKeys = {}; CTagKinMapping.forEach((value, key) => { (newValuesAndKeys as any)[key.substring(1)] = value; }); -Object.keys(newValuesAndKeys).forEach(key => { +Object.keys(newValuesAndKeys).forEach((key) => { CTagKinMapping.set(key, (newValuesAndKeys as any)[key]); }); @@ -110,7 +110,7 @@ export function parseTags( token: vscode.CancellationToken, fs: IFileSystem ): Promise { - return fs.fileExists(tagFile).then(exists => { + return fs.fileExists(tagFile).then((exists) => { if (!exists) { return Promise.resolve([]); } diff --git a/src/client/workspaceSymbols/provider.ts b/src/client/workspaceSymbols/provider.ts index 521a44648f4c..1719445aa0ae 100644 --- a/src/client/workspaceSymbols/provider.ts +++ b/src/client/workspaceSymbols/provider.ts @@ -30,15 +30,15 @@ export class WorkspaceSymbolProvider implements IWorspaceSymbolProvider { return []; } const generatorsWithTagFiles = await Promise.all( - this.tagGenerators.map(generator => this.fs.fileExists(generator.tagFilePath)) + this.tagGenerators.map((generator) => this.fs.fileExists(generator.tagFilePath)) ); - if (generatorsWithTagFiles.filter(exists => exists).length !== this.tagGenerators.length) { + if (generatorsWithTagFiles.filter((exists) => exists).length !== this.tagGenerators.length) { await this.commands.executeCommand(Commands.Build_Workspace_Symbols, true, token); } const generators: Generator[] = []; await Promise.all( - this.tagGenerators.map(async generator => { + this.tagGenerators.map(async (generator) => { if (await this.fs.fileExists(generator.tagFilePath)) { generators.push(generator); } @@ -46,8 +46,8 @@ export class WorkspaceSymbolProvider implements IWorspaceSymbolProvider { ); const promises = generators - .filter(generator => generator !== undefined && generator.enabled) - .map(async generator => { + .filter((generator) => generator !== undefined && generator.enabled) + .map(async (generator) => { // load tags const items = await parseTags( generator!.workspaceFolder.fsPath, @@ -60,7 +60,7 @@ export class WorkspaceSymbolProvider implements IWorspaceSymbolProvider { return []; } return items.map( - item => + (item) => new SymbolInformation( item.symbolName, item.symbolKind, diff --git a/src/datascience-ui/common/index.ts b/src/datascience-ui/common/index.ts index 6293da3f237d..3815071f17b3 100644 --- a/src/datascience-ui/common/index.ts +++ b/src/datascience-ui/common/index.ts @@ -49,7 +49,7 @@ export function splitMultilineString(source: nbformat.MultilineString): string[] } return s; }) - .filter(s => s.length > 0); // Skip last one if empty (it's the only one that could be length 0) + .filter((s) => s.length > 0); // Skip last one if empty (it's the only one that could be length 0) } return []; } @@ -90,8 +90,8 @@ export function stripComments(str: string): string { let result: string = ''; parseForComments( str.splitLines({ trim: false, removeEmptyEntries: false }), - _s => noop, - s => (result = result.concat(`${s}\n`)) + (_s) => noop, + (s) => (result = result.concat(`${s}\n`)) ); return result; } @@ -231,8 +231,8 @@ function extractComments(lines: string[]): string[] { const result: string[] = []; parseForComments( lines, - s => result.push(s), - _s => noop() + (s) => result.push(s), + (_s) => noop() ); return result; } diff --git a/src/datascience-ui/data-explorer/cellFormatter.tsx b/src/datascience-ui/data-explorer/cellFormatter.tsx index bb179b12a155..7ac5674fe51e 100644 --- a/src/datascience-ui/data-explorer/cellFormatter.tsx +++ b/src/datascience-ui/data-explorer/cellFormatter.tsx @@ -1,79 +1,79 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import './cellFormatter.css'; - -import * as React from 'react'; -import * as ReactDOMServer from 'react-dom/server'; -import { ISlickRow } from './reactSlickGrid'; - -interface ICellFormatterProps { - value: string | number | object | boolean; - columnDef: Slick.Column; -} - -class CellFormatter extends React.Component { - constructor(props: ICellFormatterProps) { - super(props); - } - - public render() { - // Render based on type - if (this.props.value !== null && this.props.columnDef && this.props.columnDef.hasOwnProperty('type')) { - // tslint:disable-next-line: no-any - const columnType = (this.props.columnDef as any).type; - switch (columnType) { - case 'bool': - return this.renderBool(this.props.value as boolean); - break; - - case 'integer': - case 'float': - case 'int64': - case 'float64': - case 'number': - return this.renderNumber(this.props.value as number); - break; - - default: - break; - } - } - - // Otherwise an unknown type or a string - const val = this.props.value !== null ? this.props.value.toString() : ''; - return ( -

- {val} -
- ); - } - - private renderBool(value: boolean) { - return ( -
- {value.toString()} -
- ); - } - - private renderNumber(value: number) { - const val = value.toString(); - return ( -
- {val} -
- ); - } -} - -export function cellFormatterFunc( - _row: number, - _cell: number, - // tslint:disable-next-line: no-any - value: any, - columnDef: Slick.Column, - _dataContext: Slick.SlickData -): string { - return ReactDOMServer.renderToString(); -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import './cellFormatter.css'; + +import * as React from 'react'; +import * as ReactDOMServer from 'react-dom/server'; +import { ISlickRow } from './reactSlickGrid'; + +interface ICellFormatterProps { + value: string | number | object | boolean; + columnDef: Slick.Column; +} + +class CellFormatter extends React.Component { + constructor(props: ICellFormatterProps) { + super(props); + } + + public render() { + // Render based on type + if (this.props.value !== null && this.props.columnDef && this.props.columnDef.hasOwnProperty('type')) { + // tslint:disable-next-line: no-any + const columnType = (this.props.columnDef as any).type; + switch (columnType) { + case 'bool': + return this.renderBool(this.props.value as boolean); + break; + + case 'integer': + case 'float': + case 'int64': + case 'float64': + case 'number': + return this.renderNumber(this.props.value as number); + break; + + default: + break; + } + } + + // Otherwise an unknown type or a string + const val = this.props.value !== null ? this.props.value.toString() : ''; + return ( +
+ {val} +
+ ); + } + + private renderBool(value: boolean) { + return ( +
+ {value.toString()} +
+ ); + } + + private renderNumber(value: number) { + const val = value.toString(); + return ( +
+ {val} +
+ ); + } +} + +export function cellFormatterFunc( + _row: number, + _cell: number, + // tslint:disable-next-line: no-any + value: any, + columnDef: Slick.Column, + _dataContext: Slick.SlickData +): string { + return ReactDOMServer.renderToString(); +} diff --git a/src/datascience-ui/data-explorer/emptyRowsView.tsx b/src/datascience-ui/data-explorer/emptyRowsView.tsx index 6ba37add173f..96eb82385872 100644 --- a/src/datascience-ui/data-explorer/emptyRowsView.tsx +++ b/src/datascience-ui/data-explorer/emptyRowsView.tsx @@ -1,15 +1,15 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import './emptyRowsView.css'; - -import * as React from 'react'; -import { getLocString } from '../react-common/locReactSide'; - -export interface IEmptyRowsProps {} - -export const EmptyRows = (_props: IEmptyRowsProps) => { - const message = getLocString('DataScience.noRowsInDataViewer', 'No rows match current filter'); - - return
{message}
; -}; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import './emptyRowsView.css'; + +import * as React from 'react'; +import { getLocString } from '../react-common/locReactSide'; + +export interface IEmptyRowsProps {} + +export const EmptyRows = (_props: IEmptyRowsProps) => { + const message = getLocString('DataScience.noRowsInDataViewer', 'No rows match current filter'); + + return
{message}
; +}; diff --git a/src/datascience-ui/data-explorer/index.tsx b/src/datascience-ui/data-explorer/index.tsx index fefb231ec430..aa6136295e4e 100644 --- a/src/datascience-ui/data-explorer/index.tsx +++ b/src/datascience-ui/data-explorer/index.tsx @@ -1,28 +1,28 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -// This must be on top, do not change. Required by webpack. -import '../common/main'; -// This must be on top, do not change. Required by webpack. - -// tslint:disable-next-line: ordered-imports -import '../common/index.css'; - -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; - -import { IVsCodeApi } from '../react-common/postOffice'; -import { detectBaseTheme } from '../react-common/themeDetector'; -import { MainPanel } from './mainPanel'; - -// This special function talks to vscode from a web panel -export declare function acquireVsCodeApi(): IVsCodeApi; - -const baseTheme = detectBaseTheme(); - -// tslint:disable:no-typeof-undefined -ReactDOM.render( - , // Turn this back off when we have real variable explorer data - document.getElementById('root') as HTMLElement -); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +// This must be on top, do not change. Required by webpack. +import '../common/main'; +// This must be on top, do not change. Required by webpack. + +// tslint:disable-next-line: ordered-imports +import '../common/index.css'; + +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; + +import { IVsCodeApi } from '../react-common/postOffice'; +import { detectBaseTheme } from '../react-common/themeDetector'; +import { MainPanel } from './mainPanel'; + +// This special function talks to vscode from a web panel +export declare function acquireVsCodeApi(): IVsCodeApi; + +const baseTheme = detectBaseTheme(); + +// tslint:disable:no-typeof-undefined +ReactDOM.render( + , // Turn this back off when we have real variable explorer data + document.getElementById('root') as HTMLElement +); diff --git a/src/datascience-ui/data-explorer/mainPanel.tsx b/src/datascience-ui/data-explorer/mainPanel.tsx index dfdaa7382032..1f1a9ececb2c 100644 --- a/src/datascience-ui/data-explorer/mainPanel.tsx +++ b/src/datascience-ui/data-explorer/mainPanel.tsx @@ -1,333 +1,333 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import './mainPanel.css'; - -import { JSONArray, JSONObject } from '@phosphor/coreutils'; -import * as React from 'react'; -import * as uuid from 'uuid/v4'; - -import { - CellFetchAllLimit, - CellFetchSizeFirst, - CellFetchSizeSubsequent, - DataViewerMessages, - IDataViewerMapping, - IGetRowsResponse -} from '../../client/datascience/data-viewing/types'; -import { SharedMessages } from '../../client/datascience/messages'; -import { IDataScienceExtraSettings, IJupyterVariable } from '../../client/datascience/types'; -import { getLocString, storeLocStrings } from '../react-common/locReactSide'; -import { IMessageHandler, PostOffice } from '../react-common/postOffice'; -import { Progress } from '../react-common/progress'; -import { StyleInjector } from '../react-common/styleInjector'; -import { cellFormatterFunc } from './cellFormatter'; -import { ISlickGridAdd, ISlickRow, ReactSlickGrid } from './reactSlickGrid'; -import { generateTestData } from './testData'; - -// Our css has to come after in order to override body styles -export interface IMainPanelProps { - skipDefault?: boolean; - baseTheme: string; - testMode?: boolean; -} - -//tslint:disable:no-any -interface IMainPanelState { - gridColumns: Slick.Column[]; - gridRows: ISlickRow[]; - fetchedRowCount: number; - totalRowCount: number; - filters: {}; - indexColumn: string; - styleReady: boolean; - settings?: IDataScienceExtraSettings; -} - -export class MainPanel extends React.Component implements IMessageHandler { - private container: React.Ref = React.createRef(); - private sentDone = false; - private postOffice: PostOffice = new PostOffice(); - private gridAddEvent: Slick.Event = new Slick.Event(); - private rowFetchSizeFirst: number = 0; - private rowFetchSizeSubsequent: number = 0; - private rowFetchSizeAll: number = 0; - // Just used for testing. - private grid: React.RefObject = React.createRef(); - private updateTimeout?: NodeJS.Timer | number; - - // tslint:disable-next-line:max-func-body-length - constructor(props: IMainPanelProps, _state: IMainPanelState) { - super(props); - - if (!this.props.skipDefault) { - const data = generateTestData(5000); - this.state = { - gridColumns: data.columns.map(c => { - return { ...c, formatter: cellFormatterFunc }; - }), - gridRows: [], - totalRowCount: data.rows.length, - fetchedRowCount: -1, - filters: {}, - indexColumn: data.primaryKeys[0], - styleReady: false - }; - - // Fire off a timer to mimic dynamic loading - setTimeout(() => this.handleGetAllRowsResponse({ data: data.rows }), 1000); - } else { - this.state = { - gridColumns: [], - gridRows: [], - totalRowCount: 0, - fetchedRowCount: -1, - filters: {}, - indexColumn: 'index', - styleReady: false - }; - } - } - - public componentWillMount() { - // Add ourselves as a handler for the post office - this.postOffice.addHandler(this); - - // Tell the dataviewer code we have started. - this.postOffice.sendMessage(DataViewerMessages.Started); - } - - public componentWillUnmount() { - this.postOffice.removeHandler(this); - this.postOffice.dispose(); - } - - public render = () => { - if (!this.state.settings) { - return
; - } - - // Send our done message if we haven't yet and we just reached full capacity. Do it here so we - // can guarantee our render will run before somebody checks our rendered output. - if (this.state.totalRowCount && this.state.totalRowCount === this.state.fetchedRowCount && !this.sentDone) { - this.sentDone = true; - this.sendMessage(DataViewerMessages.CompletedData); - } - - const progressBar = this.state.totalRowCount > this.state.fetchedRowCount ? : undefined; - - return ( -
- - {progressBar} - {this.state.totalRowCount > 0 && this.state.styleReady && this.renderGrid()} -
- ); - }; - - // tslint:disable-next-line:no-any - public handleMessage = (msg: string, payload?: any) => { - switch (msg) { - case DataViewerMessages.InitializeData: - this.initializeData(payload); - break; - - case DataViewerMessages.GetAllRowsResponse: - this.handleGetAllRowsResponse(payload as JSONObject); - break; - - case DataViewerMessages.GetRowsResponse: - this.handleGetRowChunkResponse(payload as IGetRowsResponse); - break; - - case SharedMessages.UpdateSettings: - this.updateSettings(payload); - break; - - case SharedMessages.LocInit: - this.initializeLoc(payload); - break; - - default: - break; - } - - return false; - }; - - private initializeLoc(content: string) { - const locJSON = JSON.parse(content); - storeLocStrings(locJSON); - } - - private updateSettings(content: string) { - const newSettingsJSON = JSON.parse(content); - const newSettings = newSettingsJSON as IDataScienceExtraSettings; - this.setState({ - settings: newSettings - }); - } - - private saveReadyState = () => { - this.setState({ styleReady: true }); - }; - - private renderGrid() { - const filterRowsText = getLocString('DataScience.filterRowsButton', 'Filter Rows'); - const filterRowsTooltip = getLocString('DataScience.filterRowsTooltip', 'Click to filter.'); - - return ( - - ); - } - - // tslint:disable-next-line:no-any - private initializeData(payload: any) { - // Payload should be an IJupyterVariable with the first 100 rows filled out - if (payload) { - const variable = payload as IJupyterVariable; - if (variable) { - const columns = this.generateColumns(variable); - const totalRowCount = variable.rowCount ? variable.rowCount : 0; - const initialRows: ISlickRow[] = []; - const indexColumn = variable.indexColumn ? variable.indexColumn : 'index'; - - this.setState({ - gridColumns: columns, - gridRows: initialRows, - totalRowCount, - fetchedRowCount: initialRows.length, - indexColumn: indexColumn - }); - - // Compute our row fetch sizes based on the number of columns - this.rowFetchSizeAll = Math.round(CellFetchAllLimit / columns.length); - this.rowFetchSizeFirst = Math.round(Math.max(2, CellFetchSizeFirst / columns.length)); - this.rowFetchSizeSubsequent = Math.round(Math.max(2, CellFetchSizeSubsequent / columns.length)); - - // Request the rest of the data if necessary - if (initialRows.length !== totalRowCount) { - // Get all at once if less than 1000 - if (totalRowCount < this.rowFetchSizeAll) { - this.getAllRows(); - } else { - this.getRowsInChunks(initialRows.length, totalRowCount); - } - } - } - } - } - - private getAllRows() { - this.sendMessage(DataViewerMessages.GetAllRowsRequest); - } - - private getRowsInChunks(startIndex: number, endIndex: number) { - // Ask for our first chunk. Don't spam jupyter though with all requests at once - // Instead, do them one at a time. - const chunkEnd = startIndex + Math.min(this.rowFetchSizeFirst, endIndex); - const chunkStart = startIndex; - this.sendMessage(DataViewerMessages.GetRowsRequest, { start: chunkStart, end: chunkEnd }); - } - - private handleGetAllRowsResponse(response: JSONObject) { - const rows = response.data ? (response.data as JSONArray) : []; - const normalized = this.normalizeRows(rows); - - // Update our fetched count and actual rows - this.setState({ - gridRows: this.state.gridRows.concat(normalized), - fetchedRowCount: this.state.totalRowCount - }); - - // Add all of these rows to the grid - this.updateRows(normalized); - } - - private handleGetRowChunkResponse(response: IGetRowsResponse) { - // We have a new fetched row count - const rows = response.rows.data ? (response.rows.data as JSONArray) : []; - const normalized = this.normalizeRows(rows); - const newFetched = this.state.fetchedRowCount + (response.end - response.start); - - // gridRows should have our entire list. We need to replace our part with our new results - const before = this.state.gridRows.slice(0, response.start); - const after = response.end < this.state.gridRows.length ? this.state.gridRows.slice(response.end) : []; - const newActual = before.concat(normalized.concat(after)); - - // Apply this to our state - this.setState({ - fetchedRowCount: newFetched, - gridRows: newActual - }); - - // Tell our grid about the new ros - this.updateRows(normalized); - - // Get the next chunk - if (newFetched < this.state.totalRowCount) { - const chunkStart = response.end; - const chunkEnd = Math.min(chunkStart + this.rowFetchSizeSubsequent, this.state.totalRowCount); - this.sendMessage(DataViewerMessages.GetRowsRequest, { start: chunkStart, end: chunkEnd }); - } - } - - private generateColumns(variable: IJupyterVariable): Slick.Column[] { - if (variable.columns) { - return variable.columns.map((c: { key: string; type: string }, i: number) => { - return { - type: c.type, - field: c.key.toString(), - id: `${i}`, - name: c.key.toString(), - sortable: true, - formatter: cellFormatterFunc - }; - }); - } - return []; - } - - private normalizeRows(rows: JSONArray): ISlickRow[] { - // Make sure we have an index field and all rows have an item - return rows.map((r: any | undefined) => { - if (!r) { - r = {}; - } - if (!r.hasOwnProperty(this.state.indexColumn)) { - r[this.state.indexColumn] = uuid(); - } - return r; - }); - } - - private sendMessage(type: T, payload?: M[T]) { - this.postOffice.sendMessage(type, payload); - } - - private updateRows(newRows: ISlickRow[]) { - if (this.updateTimeout !== undefined) { - clearTimeout(this.updateTimeout as any); - this.updateTimeout = undefined; - } - if (!this.grid.current) { - // This might happen before we render the grid. Postpone till then. - this.updateTimeout = setTimeout(() => this.updateRows(newRows), 10); - } else { - this.gridAddEvent.notify({ newRows }); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import './mainPanel.css'; + +import { JSONArray, JSONObject } from '@phosphor/coreutils'; +import * as React from 'react'; +import * as uuid from 'uuid/v4'; + +import { + CellFetchAllLimit, + CellFetchSizeFirst, + CellFetchSizeSubsequent, + DataViewerMessages, + IDataViewerMapping, + IGetRowsResponse +} from '../../client/datascience/data-viewing/types'; +import { SharedMessages } from '../../client/datascience/messages'; +import { IDataScienceExtraSettings, IJupyterVariable } from '../../client/datascience/types'; +import { getLocString, storeLocStrings } from '../react-common/locReactSide'; +import { IMessageHandler, PostOffice } from '../react-common/postOffice'; +import { Progress } from '../react-common/progress'; +import { StyleInjector } from '../react-common/styleInjector'; +import { cellFormatterFunc } from './cellFormatter'; +import { ISlickGridAdd, ISlickRow, ReactSlickGrid } from './reactSlickGrid'; +import { generateTestData } from './testData'; + +// Our css has to come after in order to override body styles +export interface IMainPanelProps { + skipDefault?: boolean; + baseTheme: string; + testMode?: boolean; +} + +//tslint:disable:no-any +interface IMainPanelState { + gridColumns: Slick.Column[]; + gridRows: ISlickRow[]; + fetchedRowCount: number; + totalRowCount: number; + filters: {}; + indexColumn: string; + styleReady: boolean; + settings?: IDataScienceExtraSettings; +} + +export class MainPanel extends React.Component implements IMessageHandler { + private container: React.Ref = React.createRef(); + private sentDone = false; + private postOffice: PostOffice = new PostOffice(); + private gridAddEvent: Slick.Event = new Slick.Event(); + private rowFetchSizeFirst: number = 0; + private rowFetchSizeSubsequent: number = 0; + private rowFetchSizeAll: number = 0; + // Just used for testing. + private grid: React.RefObject = React.createRef(); + private updateTimeout?: NodeJS.Timer | number; + + // tslint:disable-next-line:max-func-body-length + constructor(props: IMainPanelProps, _state: IMainPanelState) { + super(props); + + if (!this.props.skipDefault) { + const data = generateTestData(5000); + this.state = { + gridColumns: data.columns.map((c) => { + return { ...c, formatter: cellFormatterFunc }; + }), + gridRows: [], + totalRowCount: data.rows.length, + fetchedRowCount: -1, + filters: {}, + indexColumn: data.primaryKeys[0], + styleReady: false + }; + + // Fire off a timer to mimic dynamic loading + setTimeout(() => this.handleGetAllRowsResponse({ data: data.rows }), 1000); + } else { + this.state = { + gridColumns: [], + gridRows: [], + totalRowCount: 0, + fetchedRowCount: -1, + filters: {}, + indexColumn: 'index', + styleReady: false + }; + } + } + + public componentWillMount() { + // Add ourselves as a handler for the post office + this.postOffice.addHandler(this); + + // Tell the dataviewer code we have started. + this.postOffice.sendMessage(DataViewerMessages.Started); + } + + public componentWillUnmount() { + this.postOffice.removeHandler(this); + this.postOffice.dispose(); + } + + public render = () => { + if (!this.state.settings) { + return
; + } + + // Send our done message if we haven't yet and we just reached full capacity. Do it here so we + // can guarantee our render will run before somebody checks our rendered output. + if (this.state.totalRowCount && this.state.totalRowCount === this.state.fetchedRowCount && !this.sentDone) { + this.sentDone = true; + this.sendMessage(DataViewerMessages.CompletedData); + } + + const progressBar = this.state.totalRowCount > this.state.fetchedRowCount ? : undefined; + + return ( +
+ + {progressBar} + {this.state.totalRowCount > 0 && this.state.styleReady && this.renderGrid()} +
+ ); + }; + + // tslint:disable-next-line:no-any + public handleMessage = (msg: string, payload?: any) => { + switch (msg) { + case DataViewerMessages.InitializeData: + this.initializeData(payload); + break; + + case DataViewerMessages.GetAllRowsResponse: + this.handleGetAllRowsResponse(payload as JSONObject); + break; + + case DataViewerMessages.GetRowsResponse: + this.handleGetRowChunkResponse(payload as IGetRowsResponse); + break; + + case SharedMessages.UpdateSettings: + this.updateSettings(payload); + break; + + case SharedMessages.LocInit: + this.initializeLoc(payload); + break; + + default: + break; + } + + return false; + }; + + private initializeLoc(content: string) { + const locJSON = JSON.parse(content); + storeLocStrings(locJSON); + } + + private updateSettings(content: string) { + const newSettingsJSON = JSON.parse(content); + const newSettings = newSettingsJSON as IDataScienceExtraSettings; + this.setState({ + settings: newSettings + }); + } + + private saveReadyState = () => { + this.setState({ styleReady: true }); + }; + + private renderGrid() { + const filterRowsText = getLocString('DataScience.filterRowsButton', 'Filter Rows'); + const filterRowsTooltip = getLocString('DataScience.filterRowsTooltip', 'Click to filter.'); + + return ( + + ); + } + + // tslint:disable-next-line:no-any + private initializeData(payload: any) { + // Payload should be an IJupyterVariable with the first 100 rows filled out + if (payload) { + const variable = payload as IJupyterVariable; + if (variable) { + const columns = this.generateColumns(variable); + const totalRowCount = variable.rowCount ? variable.rowCount : 0; + const initialRows: ISlickRow[] = []; + const indexColumn = variable.indexColumn ? variable.indexColumn : 'index'; + + this.setState({ + gridColumns: columns, + gridRows: initialRows, + totalRowCount, + fetchedRowCount: initialRows.length, + indexColumn: indexColumn + }); + + // Compute our row fetch sizes based on the number of columns + this.rowFetchSizeAll = Math.round(CellFetchAllLimit / columns.length); + this.rowFetchSizeFirst = Math.round(Math.max(2, CellFetchSizeFirst / columns.length)); + this.rowFetchSizeSubsequent = Math.round(Math.max(2, CellFetchSizeSubsequent / columns.length)); + + // Request the rest of the data if necessary + if (initialRows.length !== totalRowCount) { + // Get all at once if less than 1000 + if (totalRowCount < this.rowFetchSizeAll) { + this.getAllRows(); + } else { + this.getRowsInChunks(initialRows.length, totalRowCount); + } + } + } + } + } + + private getAllRows() { + this.sendMessage(DataViewerMessages.GetAllRowsRequest); + } + + private getRowsInChunks(startIndex: number, endIndex: number) { + // Ask for our first chunk. Don't spam jupyter though with all requests at once + // Instead, do them one at a time. + const chunkEnd = startIndex + Math.min(this.rowFetchSizeFirst, endIndex); + const chunkStart = startIndex; + this.sendMessage(DataViewerMessages.GetRowsRequest, { start: chunkStart, end: chunkEnd }); + } + + private handleGetAllRowsResponse(response: JSONObject) { + const rows = response.data ? (response.data as JSONArray) : []; + const normalized = this.normalizeRows(rows); + + // Update our fetched count and actual rows + this.setState({ + gridRows: this.state.gridRows.concat(normalized), + fetchedRowCount: this.state.totalRowCount + }); + + // Add all of these rows to the grid + this.updateRows(normalized); + } + + private handleGetRowChunkResponse(response: IGetRowsResponse) { + // We have a new fetched row count + const rows = response.rows.data ? (response.rows.data as JSONArray) : []; + const normalized = this.normalizeRows(rows); + const newFetched = this.state.fetchedRowCount + (response.end - response.start); + + // gridRows should have our entire list. We need to replace our part with our new results + const before = this.state.gridRows.slice(0, response.start); + const after = response.end < this.state.gridRows.length ? this.state.gridRows.slice(response.end) : []; + const newActual = before.concat(normalized.concat(after)); + + // Apply this to our state + this.setState({ + fetchedRowCount: newFetched, + gridRows: newActual + }); + + // Tell our grid about the new ros + this.updateRows(normalized); + + // Get the next chunk + if (newFetched < this.state.totalRowCount) { + const chunkStart = response.end; + const chunkEnd = Math.min(chunkStart + this.rowFetchSizeSubsequent, this.state.totalRowCount); + this.sendMessage(DataViewerMessages.GetRowsRequest, { start: chunkStart, end: chunkEnd }); + } + } + + private generateColumns(variable: IJupyterVariable): Slick.Column[] { + if (variable.columns) { + return variable.columns.map((c: { key: string; type: string }, i: number) => { + return { + type: c.type, + field: c.key.toString(), + id: `${i}`, + name: c.key.toString(), + sortable: true, + formatter: cellFormatterFunc + }; + }); + } + return []; + } + + private normalizeRows(rows: JSONArray): ISlickRow[] { + // Make sure we have an index field and all rows have an item + return rows.map((r: any | undefined) => { + if (!r) { + r = {}; + } + if (!r.hasOwnProperty(this.state.indexColumn)) { + r[this.state.indexColumn] = uuid(); + } + return r; + }); + } + + private sendMessage(type: T, payload?: M[T]) { + this.postOffice.sendMessage(type, payload); + } + + private updateRows(newRows: ISlickRow[]) { + if (this.updateTimeout !== undefined) { + clearTimeout(this.updateTimeout as any); + this.updateTimeout = undefined; + } + if (!this.grid.current) { + // This might happen before we render the grid. Postpone till then. + this.updateTimeout = setTimeout(() => this.updateRows(newRows), 10); + } else { + this.gridAddEvent.notify({ newRows }); + } + } +} diff --git a/src/datascience-ui/data-explorer/reactSlickGrid.tsx b/src/datascience-ui/data-explorer/reactSlickGrid.tsx index 07d82cc00900..4beb77d1d9f7 100644 --- a/src/datascience-ui/data-explorer/reactSlickGrid.tsx +++ b/src/datascience-ui/data-explorer/reactSlickGrid.tsx @@ -175,7 +175,7 @@ export class ReactSlickGrid extends React.Component { + const columns = this.props.columns.map((c) => { c.sortable = true; c.headerCssClass = 'react-grid-header-cell'; c.cssClass = 'react-grid-cell'; @@ -220,18 +220,10 @@ export class ReactSlickGrid extends React.Component c.field === this.props.idProperty); + const indexColumn = columns.find((c) => c.field === this.props.idProperty); if (indexColumn && indexColumn.id) { grid.setSortColumn(indexColumn.id, true); } @@ -435,7 +427,7 @@ export class ReactSlickGrid extends React.Component { + columns.forEach((c) => { let colWidth = MinColumnWidth; rows.forEach((r: any) => { const field = c.field ? r[c.field] : ''; diff --git a/src/datascience-ui/data-explorer/testData.ts b/src/datascience-ui/data-explorer/testData.ts index 783b26b8729c..963a702ee1a0 100644 --- a/src/datascience-ui/data-explorer/testData.ts +++ b/src/datascience-ui/data-explorer/testData.ts @@ -1,12518 +1,12518 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -export interface ITestData { - columns: { id: string; name: string; type: string }[]; - primaryKeys: string[]; - rows: {}[]; - loadingRows: {}[]; -} - -// tslint:disable -export function generateTestData(_numberOfRows: number): ITestData { - const columns = [ - { id: 'PassengerId', name: 'PassengerId', field: 'PassengerId', type: 'integer' }, - { id: 'SibSp', name: 'SibSp', field: 'SibSp', type: 'integer' }, - { id: 'Ticket', name: 'Ticket', field: 'Ticket', type: 'string' }, - { id: 'Parch', name: 'Parch', field: 'Parch', type: 'integer' }, - { id: 'Cabin', name: 'Cabin', field: 'Cabin', type: 'string' }, - { id: 'Age', name: 'Age', field: 'Age', type: 'integer' }, - { id: 'Fare', name: 'Fare', field: 'Fare', type: 'number' }, - { id: 'Name', name: 'Name', field: 'Name', type: 'string' }, - { id: 'Survived', name: 'Survived', field: 'Survived', type: 'bool' }, - { id: 'Pclass', name: 'Pclass', field: 'Pclass', type: 'integer' }, - { id: 'Embarked', name: 'Embarked', field: 'Embarked', type: 'string' }, - { id: 'Sex', name: 'Sex', field: 'Sex', type: 'string' } - ]; - - const keys = ['PassengerId']; - - const rows: {}[] = titanicData; - - return { - columns, - primaryKeys: keys, - rows, - loadingRows: titanicData.map(_t => { - return {}; - }) - }; -} - -const titanicData = [ - { - SibSp: 1, - Ticket: 'A/5 21171', - Parch: 0, - Cabin: null, - PassengerId: 1, - Age: 22, - Fare: 7.25, - Name: 'Braund, Mr. Owen Harris', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'PC 17599', - Parch: 0, - Cabin: 'C85', - PassengerId: 2, - Age: 38, - Fare: 71.2833, - Name: 'Cumings, Mrs. John Bradley (Florence Briggs Thayer)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'STON/O2. 3101282', - Parch: 0, - Cabin: null, - PassengerId: 3, - Age: 26, - Fare: 7.925, - Name: 'Heikkinen, Miss. Laina', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '113803', - Parch: 0, - Cabin: 'C123', - PassengerId: 4, - Age: 35, - Fare: 53.1, - Name: 'Futrelle, Mrs. Jacques Heath (Lily May Peel)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '373450', - Parch: 0, - Cabin: null, - PassengerId: 5, - Age: 35, - Fare: 8.05, - Name: 'Allen, Mr. William Henry', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '330877', - Parch: 0, - Cabin: null, - PassengerId: 6, - Age: null, - Fare: 8.4583, - Name: 'Moran, Mr. James', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '17463', - Parch: 0, - Cabin: 'E46', - PassengerId: 7, - Age: 54, - Fare: 51.8625, - Name: 'McCarthy, Mr. Timothy J', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '349909', - Parch: 1, - Cabin: null, - PassengerId: 8, - Age: 2, - Fare: 21.075, - Name: 'Palsson, Master. Gosta Leonard', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347742', - Parch: 2, - Cabin: null, - PassengerId: 9, - Age: 27, - Fare: 11.1333, - Name: 'Johnson, Mrs. Oscar W (Elisabeth Vilhelmina Berg)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '237736', - Parch: 0, - Cabin: null, - PassengerId: 10, - Age: 14, - Fare: 30.0708, - Name: 'Nasser, Mrs. Nicholas (Adele Achem)', - Survived: true, - Pclass: 2, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'PP 9549', - Parch: 1, - Cabin: 'G6', - PassengerId: 11, - Age: 4, - Fare: 16.7, - Name: 'Sandstrom, Miss. Marguerite Rut', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113783', - Parch: 0, - Cabin: 'C103', - PassengerId: 12, - Age: 58, - Fare: 26.55, - Name: 'Bonnell, Miss. Elizabeth', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'A/5. 2151', - Parch: 0, - Cabin: null, - PassengerId: 13, - Age: 20, - Fare: 8.05, - Name: 'Saundercock, Mr. William Henry', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '347082', - Parch: 5, - Cabin: null, - PassengerId: 14, - Age: 39, - Fare: 31.275, - Name: 'Andersson, Mr. Anders Johan', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350406', - Parch: 0, - Cabin: null, - PassengerId: 15, - Age: 14, - Fare: 7.8542, - Name: 'Vestrom, Miss. Hulda Amanda Adolfina', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '248706', - Parch: 0, - Cabin: null, - PassengerId: 16, - Age: 55, - Fare: 16, - Name: 'Hewlett, Mrs. (Mary D Kingcome) ', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 4, - Ticket: '382652', - Parch: 1, - Cabin: null, - PassengerId: 17, - Age: 2, - Fare: 29.125, - Name: 'Rice, Master. Eugene', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '244373', - Parch: 0, - Cabin: null, - PassengerId: 18, - Age: null, - Fare: 13, - Name: 'Williams, Mr. Charles Eugene', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '345763', - Parch: 0, - Cabin: null, - PassengerId: 19, - Age: 31, - Fare: 18, - Name: 'Vander Planke, Mrs. Julius (Emelia Maria Vandemoortele)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2649', - Parch: 0, - Cabin: null, - PassengerId: 20, - Age: null, - Fare: 7.225, - Name: 'Masselmani, Mrs. Fatima', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '239865', - Parch: 0, - Cabin: null, - PassengerId: 21, - Age: 35, - Fare: 26, - Name: 'Fynney, Mr. Joseph J', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '248698', - Parch: 0, - Cabin: 'D56', - PassengerId: 22, - Age: 34, - Fare: 13, - Name: 'Beesley, Mr. Lawrence', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '330923', - Parch: 0, - Cabin: null, - PassengerId: 23, - Age: 15, - Fare: 8.0292, - Name: 'McGowan, Miss. Anna "Annie"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113788', - Parch: 0, - Cabin: 'A6', - PassengerId: 24, - Age: 28, - Fare: 35.5, - Name: 'Sloper, Mr. William Thompson', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '349909', - Parch: 1, - Cabin: null, - PassengerId: 25, - Age: 8, - Fare: 21.075, - Name: 'Palsson, Miss. Torborg Danira', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '347077', - Parch: 5, - Cabin: null, - PassengerId: 26, - Age: 38, - Fare: 31.3875, - Name: 'Asplund, Mrs. Carl Oscar (Selma Augusta Emilia Johansson)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2631', - Parch: 0, - Cabin: null, - PassengerId: 27, - Age: null, - Fare: 7.225, - Name: 'Emir, Mr. Farred Chehab', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '19950', - Parch: 2, - Cabin: 'C23 C25 C27', - PassengerId: 28, - Age: 19, - Fare: 263, - Name: 'Fortune, Mr. Charles Alexander', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '330959', - Parch: 0, - Cabin: null, - PassengerId: 29, - Age: null, - Fare: 7.8792, - Name: 'O\'Dwyer, Miss. Ellen "Nellie"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349216', - Parch: 0, - Cabin: null, - PassengerId: 30, - Age: null, - Fare: 7.8958, - Name: 'Todoroff, Mr. Lalio', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17601', - Parch: 0, - Cabin: null, - PassengerId: 31, - Age: 40, - Fare: 27.7208, - Name: 'Uruchurtu, Don. Manuel E', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'PC 17569', - Parch: 0, - Cabin: 'B78', - PassengerId: 32, - Age: null, - Fare: 146.5208, - Name: 'Spencer, Mrs. William Augustus (Marie Eugenie)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '335677', - Parch: 0, - Cabin: null, - PassengerId: 33, - Age: null, - Fare: 7.75, - Name: 'Glynn, Miss. Mary Agatha', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'C.A. 24579', - Parch: 0, - Cabin: null, - PassengerId: 34, - Age: 66, - Fare: 10.5, - Name: 'Wheadon, Mr. Edward H', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'PC 17604', - Parch: 0, - Cabin: null, - PassengerId: 35, - Age: 28, - Fare: 82.1708, - Name: 'Meyer, Mr. Edgar Joseph', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113789', - Parch: 0, - Cabin: null, - PassengerId: 36, - Age: 42, - Fare: 52, - Name: 'Holverson, Mr. Alexander Oskar', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2677', - Parch: 0, - Cabin: null, - PassengerId: 37, - Age: null, - Fare: 7.2292, - Name: 'Mamee, Mr. Hanna', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A./5. 2152', - Parch: 0, - Cabin: null, - PassengerId: 38, - Age: 21, - Fare: 8.05, - Name: 'Cann, Mr. Ernest Charles', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '345764', - Parch: 0, - Cabin: null, - PassengerId: 39, - Age: 18, - Fare: 18, - Name: 'Vander Planke, Miss. Augusta Maria', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '2651', - Parch: 0, - Cabin: null, - PassengerId: 40, - Age: 14, - Fare: 11.2417, - Name: 'Nicola-Yarred, Miss. Jamila', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '7546', - Parch: 0, - Cabin: null, - PassengerId: 41, - Age: 40, - Fare: 9.475, - Name: 'Ahlin, Mrs. Johan (Johanna Persdotter Larsson)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '11668', - Parch: 0, - Cabin: null, - PassengerId: 42, - Age: 27, - Fare: 21, - Name: 'Turpin, Mrs. William John Robert (Dorothy Ann Wonnacott)', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349253', - Parch: 0, - Cabin: null, - PassengerId: 43, - Age: null, - Fare: 7.8958, - Name: 'Kraeff, Mr. Theodor', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'SC/Paris 2123', - Parch: 2, - Cabin: null, - PassengerId: 44, - Age: 3, - Fare: 41.5792, - Name: 'Laroche, Miss. Simonne Marie Anne Andree', - Survived: true, - Pclass: 2, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '330958', - Parch: 0, - Cabin: null, - PassengerId: 45, - Age: 19, - Fare: 7.8792, - Name: 'Devaney, Miss. Margaret Delia', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'S.C./A.4. 23567', - Parch: 0, - Cabin: null, - PassengerId: 46, - Age: null, - Fare: 8.05, - Name: 'Rogers, Mr. William John', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '370371', - Parch: 0, - Cabin: null, - PassengerId: 47, - Age: null, - Fare: 15.5, - Name: 'Lennon, Mr. Denis', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '14311', - Parch: 0, - Cabin: null, - PassengerId: 48, - Age: null, - Fare: 7.75, - Name: "O'Driscoll, Miss. Bridget", - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: '2662', - Parch: 0, - Cabin: null, - PassengerId: 49, - Age: null, - Fare: 21.6792, - Name: 'Samaan, Mr. Youssef', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '349237', - Parch: 0, - Cabin: null, - PassengerId: 50, - Age: 18, - Fare: 17.8, - Name: 'Arnold-Franchi, Mrs. Josef (Josefine Franchi)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 4, - Ticket: '3101295', - Parch: 1, - Cabin: null, - PassengerId: 51, - Age: 7, - Fare: 39.6875, - Name: 'Panula, Master. Juha Niilo', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/4. 39886', - Parch: 0, - Cabin: null, - PassengerId: 52, - Age: 21, - Fare: 7.8, - Name: 'Nosworthy, Mr. Richard Cater', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'PC 17572', - Parch: 0, - Cabin: 'D33', - PassengerId: 53, - Age: 49, - Fare: 76.7292, - Name: 'Harper, Mrs. Henry Sleeper (Myna Haxtun)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '2926', - Parch: 0, - Cabin: null, - PassengerId: 54, - Age: 29, - Fare: 26, - Name: 'Faunthorpe, Mrs. Lizzie (Elizabeth Anne Wilkinson)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113509', - Parch: 1, - Cabin: 'B30', - PassengerId: 55, - Age: 65, - Fare: 61.9792, - Name: 'Ostby, Mr. Engelhart Cornelius', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '19947', - Parch: 0, - Cabin: 'C52', - PassengerId: 56, - Age: null, - Fare: 35.5, - Name: 'Woolner, Mr. Hugh', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C.A. 31026', - Parch: 0, - Cabin: null, - PassengerId: 57, - Age: 21, - Fare: 10.5, - Name: 'Rugg, Miss. Emily', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2697', - Parch: 0, - Cabin: null, - PassengerId: 58, - Age: 28.5, - Fare: 7.2292, - Name: 'Novel, Mr. Mansouer', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 34651', - Parch: 2, - Cabin: null, - PassengerId: 59, - Age: 5, - Fare: 27.75, - Name: 'West, Miss. Constance Mirium', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 5, - Ticket: 'CA 2144', - Parch: 2, - Cabin: null, - PassengerId: 60, - Age: 11, - Fare: 46.9, - Name: 'Goodwin, Master. William Frederick', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2669', - Parch: 0, - Cabin: null, - PassengerId: 61, - Age: 22, - Fare: 7.2292, - Name: 'Sirayanian, Mr. Orsen', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113572', - Parch: 0, - Cabin: 'B28', - PassengerId: 62, - Age: 38, - Fare: 80, - Name: 'Icard, Miss. Amelie', - Survived: true, - Pclass: 1, - Embarked: null, - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '36973', - Parch: 0, - Cabin: 'C83', - PassengerId: 63, - Age: 45, - Fare: 83.475, - Name: 'Harris, Mr. Henry Birkhardt', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '347088', - Parch: 2, - Cabin: null, - PassengerId: 64, - Age: 4, - Fare: 27.9, - Name: 'Skoog, Master. Harald', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17605', - Parch: 0, - Cabin: null, - PassengerId: 65, - Age: null, - Fare: 27.7208, - Name: 'Stewart, Mr. Albert A', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2661', - Parch: 1, - Cabin: null, - PassengerId: 66, - Age: null, - Fare: 15.2458, - Name: 'Moubarek, Master. Gerios', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C.A. 29395', - Parch: 0, - Cabin: 'F33', - PassengerId: 67, - Age: 29, - Fare: 10.5, - Name: 'Nye, Mrs. (Elizabeth Ramell)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'S.P. 3464', - Parch: 0, - Cabin: null, - PassengerId: 68, - Age: 19, - Fare: 8.1583, - Name: 'Crease, Mr. Ernest James', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '3101281', - Parch: 2, - Cabin: null, - PassengerId: 69, - Age: 17, - Fare: 7.925, - Name: 'Andersson, Miss. Erna Alexandra', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: '315151', - Parch: 0, - Cabin: null, - PassengerId: 70, - Age: 26, - Fare: 8.6625, - Name: 'Kink, Mr. Vincenz', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C.A. 33111', - Parch: 0, - Cabin: null, - PassengerId: 71, - Age: 32, - Fare: 10.5, - Name: 'Jenkin, Mr. Stephen Curnow', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 5, - Ticket: 'CA 2144', - Parch: 2, - Cabin: null, - PassengerId: 72, - Age: 16, - Fare: 46.9, - Name: 'Goodwin, Miss. Lillian Amy', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'S.O.C. 14879', - Parch: 0, - Cabin: null, - PassengerId: 73, - Age: 21, - Fare: 73.5, - Name: 'Hood, Mr. Ambrose Jr', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2680', - Parch: 0, - Cabin: null, - PassengerId: 74, - Age: 26, - Fare: 14.4542, - Name: 'Chronopoulos, Mr. Apostolos', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '1601', - Parch: 0, - Cabin: null, - PassengerId: 75, - Age: 32, - Fare: 56.4958, - Name: 'Bing, Mr. Lee', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '348123', - Parch: 0, - Cabin: 'F G73', - PassengerId: 76, - Age: 25, - Fare: 7.65, - Name: 'Moen, Mr. Sigurd Hansen', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349208', - Parch: 0, - Cabin: null, - PassengerId: 77, - Age: null, - Fare: 7.8958, - Name: 'Staneff, Mr. Ivan', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '374746', - Parch: 0, - Cabin: null, - PassengerId: 78, - Age: null, - Fare: 8.05, - Name: 'Moutal, Mr. Rahamin Haim', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '248738', - Parch: 2, - Cabin: null, - PassengerId: 79, - Age: 0.83, - Fare: 29, - Name: 'Caldwell, Master. Alden Gates', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '364516', - Parch: 0, - Cabin: null, - PassengerId: 80, - Age: 30, - Fare: 12.475, - Name: 'Dowdell, Miss. Elizabeth', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '345767', - Parch: 0, - Cabin: null, - PassengerId: 81, - Age: 22, - Fare: 9, - Name: 'Waelens, Mr. Achille', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '345779', - Parch: 0, - Cabin: null, - PassengerId: 82, - Age: 29, - Fare: 9.5, - Name: 'Sheerlinck, Mr. Jan Baptist', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '330932', - Parch: 0, - Cabin: null, - PassengerId: 83, - Age: null, - Fare: 7.7875, - Name: 'McDermott, Miss. Brigdet Delia', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113059', - Parch: 0, - Cabin: null, - PassengerId: 84, - Age: 28, - Fare: 47.1, - Name: 'Carrau, Mr. Francisco M', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SO/C 14885', - Parch: 0, - Cabin: null, - PassengerId: 85, - Age: 17, - Fare: 10.5, - Name: 'Ilett, Miss. Bertha', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 3, - Ticket: '3101278', - Parch: 0, - Cabin: null, - PassengerId: 86, - Age: 33, - Fare: 15.85, - Name: 'Backstrom, Mrs. Karl Alfred (Maria Mathilda Gustafsson)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'W./C. 6608', - Parch: 3, - Cabin: null, - PassengerId: 87, - Age: 16, - Fare: 34.375, - Name: 'Ford, Mr. William Neal', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/OQ 392086', - Parch: 0, - Cabin: null, - PassengerId: 88, - Age: null, - Fare: 8.05, - Name: 'Slocovski, Mr. Selman Francis', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '19950', - Parch: 2, - Cabin: 'C23 C25 C27', - PassengerId: 89, - Age: 23, - Fare: 263, - Name: 'Fortune, Miss. Mabel Helen', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '343275', - Parch: 0, - Cabin: null, - PassengerId: 90, - Age: 24, - Fare: 8.05, - Name: 'Celotti, Mr. Francesco', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '343276', - Parch: 0, - Cabin: null, - PassengerId: 91, - Age: 29, - Fare: 8.05, - Name: 'Christmann, Mr. Emil', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347466', - Parch: 0, - Cabin: null, - PassengerId: 92, - Age: 20, - Fare: 7.8542, - Name: 'Andreasson, Mr. Paul Edvin', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'W.E.P. 5734', - Parch: 0, - Cabin: 'E31', - PassengerId: 93, - Age: 46, - Fare: 61.175, - Name: 'Chaffee, Mr. Herbert Fuller', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 2315', - Parch: 2, - Cabin: null, - PassengerId: 94, - Age: 26, - Fare: 20.575, - Name: 'Dean, Mr. Bertram Frank', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '364500', - Parch: 0, - Cabin: null, - PassengerId: 95, - Age: 59, - Fare: 7.25, - Name: 'Coxon, Mr. Daniel', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '374910', - Parch: 0, - Cabin: null, - PassengerId: 96, - Age: null, - Fare: 8.05, - Name: 'Shorney, Mr. Charles Joseph', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17754', - Parch: 0, - Cabin: 'A5', - PassengerId: 97, - Age: 71, - Fare: 34.6542, - Name: 'Goldschmidt, Mr. George B', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17759', - Parch: 1, - Cabin: 'D10 D12', - PassengerId: 98, - Age: 23, - Fare: 63.3583, - Name: 'Greenfield, Mr. William Bertram', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '231919', - Parch: 1, - Cabin: null, - PassengerId: 99, - Age: 34, - Fare: 23, - Name: 'Doling, Mrs. John T (Ada Julia Bone)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '244367', - Parch: 0, - Cabin: null, - PassengerId: 100, - Age: 34, - Fare: 26, - Name: 'Kantor, Mr. Sinai', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349245', - Parch: 0, - Cabin: null, - PassengerId: 101, - Age: 28, - Fare: 7.8958, - Name: 'Petranec, Miss. Matilda', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349215', - Parch: 0, - Cabin: null, - PassengerId: 102, - Age: null, - Fare: 7.8958, - Name: 'Petroff, Mr. Pastcho ("Pentcho")', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '35281', - Parch: 1, - Cabin: 'D26', - PassengerId: 103, - Age: 21, - Fare: 77.2875, - Name: 'White, Mr. Richard Frasar', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '7540', - Parch: 0, - Cabin: null, - PassengerId: 104, - Age: 33, - Fare: 8.6542, - Name: 'Johansson, Mr. Gustaf Joel', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '3101276', - Parch: 0, - Cabin: null, - PassengerId: 105, - Age: 37, - Fare: 7.925, - Name: 'Gustafsson, Mr. Anders Vilhelm', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349207', - Parch: 0, - Cabin: null, - PassengerId: 106, - Age: 28, - Fare: 7.8958, - Name: 'Mionoff, Mr. Stoytcho', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '343120', - Parch: 0, - Cabin: null, - PassengerId: 107, - Age: 21, - Fare: 7.65, - Name: 'Salkjelsvik, Miss. Anna Kristine', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '312991', - Parch: 0, - Cabin: null, - PassengerId: 108, - Age: null, - Fare: 7.775, - Name: 'Moss, Mr. Albert Johan', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349249', - Parch: 0, - Cabin: null, - PassengerId: 109, - Age: 38, - Fare: 7.8958, - Name: 'Rekic, Mr. Tido', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '371110', - Parch: 0, - Cabin: null, - PassengerId: 110, - Age: null, - Fare: 24.15, - Name: 'Moran, Miss. Bertha', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '110465', - Parch: 0, - Cabin: 'C110', - PassengerId: 111, - Age: 47, - Fare: 52, - Name: 'Porter, Mr. Walter Chamberlain', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2665', - Parch: 0, - Cabin: null, - PassengerId: 112, - Age: 14.5, - Fare: 14.4542, - Name: 'Zabour, Miss. Hileni', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '324669', - Parch: 0, - Cabin: null, - PassengerId: 113, - Age: 22, - Fare: 8.05, - Name: 'Barton, Mr. David John', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '4136', - Parch: 0, - Cabin: null, - PassengerId: 114, - Age: 20, - Fare: 9.825, - Name: 'Jussila, Miss. Katriina', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2627', - Parch: 0, - Cabin: null, - PassengerId: 115, - Age: 17, - Fare: 14.4583, - Name: 'Attalah, Miss. Malake', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101294', - Parch: 0, - Cabin: null, - PassengerId: 116, - Age: 21, - Fare: 7.925, - Name: 'Pekoniemi, Mr. Edvard', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '370369', - Parch: 0, - Cabin: null, - PassengerId: 117, - Age: 70.5, - Fare: 7.75, - Name: 'Connors, Mr. Patrick', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '11668', - Parch: 0, - Cabin: null, - PassengerId: 118, - Age: 29, - Fare: 21, - Name: 'Turpin, Mr. William John Robert', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17558', - Parch: 1, - Cabin: 'B58 B60', - PassengerId: 119, - Age: 24, - Fare: 247.5208, - Name: 'Baxter, Mr. Quigg Edmond', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '347082', - Parch: 2, - Cabin: null, - PassengerId: 120, - Age: 2, - Fare: 31.275, - Name: 'Andersson, Miss. Ellis Anna Maria', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: 'S.O.C. 14879', - Parch: 0, - Cabin: null, - PassengerId: 121, - Age: 21, - Fare: 73.5, - Name: 'Hickman, Mr. Stanley George', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A4. 54510', - Parch: 0, - Cabin: null, - PassengerId: 122, - Age: null, - Fare: 8.05, - Name: 'Moore, Mr. Leonard Charles', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '237736', - Parch: 0, - Cabin: null, - PassengerId: 123, - Age: 32.5, - Fare: 30.0708, - Name: 'Nasser, Mr. Nicholas', - Survived: false, - Pclass: 2, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '27267', - Parch: 0, - Cabin: 'E101', - PassengerId: 124, - Age: 32.5, - Fare: 13, - Name: 'Webber, Miss. Susan', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '35281', - Parch: 1, - Cabin: 'D26', - PassengerId: 125, - Age: 54, - Fare: 77.2875, - Name: 'White, Mr. Percival Wayland', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2651', - Parch: 0, - Cabin: null, - PassengerId: 126, - Age: 12, - Fare: 11.2417, - Name: 'Nicola-Yarred, Master. Elias', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '370372', - Parch: 0, - Cabin: null, - PassengerId: 127, - Age: null, - Fare: 7.75, - Name: 'McMahon, Mr. Martin', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C 17369', - Parch: 0, - Cabin: null, - PassengerId: 128, - Age: 24, - Fare: 7.1417, - Name: 'Madsen, Mr. Fridtjof Arne', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2668', - Parch: 1, - Cabin: 'F E69', - PassengerId: 129, - Age: null, - Fare: 22.3583, - Name: 'Peter, Miss. Anna', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '347061', - Parch: 0, - Cabin: null, - PassengerId: 130, - Age: 45, - Fare: 6.975, - Name: 'Ekstrom, Mr. Johan', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349241', - Parch: 0, - Cabin: null, - PassengerId: 131, - Age: 33, - Fare: 7.8958, - Name: 'Drazenoic, Mr. Jozef', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/O.Q. 3101307', - Parch: 0, - Cabin: null, - PassengerId: 132, - Age: 20, - Fare: 7.05, - Name: 'Coelho, Mr. Domingos Fernandeo', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'A/5. 3337', - Parch: 0, - Cabin: null, - PassengerId: 133, - Age: 47, - Fare: 14.5, - Name: 'Robins, Mrs. Alexander A (Grace Charity Laury)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '228414', - Parch: 0, - Cabin: null, - PassengerId: 134, - Age: 29, - Fare: 26, - Name: 'Weisz, Mrs. Leopold (Mathilde Francoise Pede)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'C.A. 29178', - Parch: 0, - Cabin: null, - PassengerId: 135, - Age: 25, - Fare: 13, - Name: 'Sobey, Mr. Samuel James Hayden', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SC/PARIS 2133', - Parch: 0, - Cabin: null, - PassengerId: 136, - Age: 23, - Fare: 15.0458, - Name: 'Richard, Mr. Emile', - Survived: false, - Pclass: 2, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '11752', - Parch: 2, - Cabin: 'D47', - PassengerId: 137, - Age: 19, - Fare: 26.2833, - Name: 'Newsom, Miss. Helen Monypeny', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '113803', - Parch: 0, - Cabin: 'C123', - PassengerId: 138, - Age: 37, - Fare: 53.1, - Name: 'Futrelle, Mr. Jacques Heath', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '7534', - Parch: 0, - Cabin: null, - PassengerId: 139, - Age: 16, - Fare: 9.2167, - Name: 'Osen, Mr. Olaf Elon', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17593', - Parch: 0, - Cabin: 'B86', - PassengerId: 140, - Age: 24, - Fare: 79.2, - Name: 'Giglio, Mr. Victor', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2678', - Parch: 2, - Cabin: null, - PassengerId: 141, - Age: null, - Fare: 15.2458, - Name: 'Boulos, Mrs. Joseph (Sultana)', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '347081', - Parch: 0, - Cabin: null, - PassengerId: 142, - Age: 22, - Fare: 7.75, - Name: 'Nysten, Miss. Anna Sofia', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'STON/O2. 3101279', - Parch: 0, - Cabin: null, - PassengerId: 143, - Age: 24, - Fare: 15.85, - Name: 'Hakkarainen, Mrs. Pekka Pietari (Elin Matilda Dolck)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '365222', - Parch: 0, - Cabin: null, - PassengerId: 144, - Age: 19, - Fare: 6.75, - Name: 'Burke, Mr. Jeremiah', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '231945', - Parch: 0, - Cabin: null, - PassengerId: 145, - Age: 18, - Fare: 11.5, - Name: 'Andrew, Mr. Edgardo Samuel', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 33112', - Parch: 1, - Cabin: null, - PassengerId: 146, - Age: 19, - Fare: 36.75, - Name: 'Nicholls, Mr. Joseph Charles', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350043', - Parch: 0, - Cabin: null, - PassengerId: 147, - Age: 27, - Fare: 7.7958, - Name: 'Andersson, Mr. August Edvard ("Wennerstrom")', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: 'W./C. 6608', - Parch: 2, - Cabin: null, - PassengerId: 148, - Age: 9, - Fare: 34.375, - Name: 'Ford, Miss. Robina Maggie "Ruby"', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '230080', - Parch: 2, - Cabin: 'F2', - PassengerId: 149, - Age: 36.5, - Fare: 26, - Name: 'Navratil, Mr. Michel ("Louis M Hoffman")', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '244310', - Parch: 0, - Cabin: null, - PassengerId: 150, - Age: 42, - Fare: 13, - Name: 'Byles, Rev. Thomas Roussel Davids', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'S.O.P. 1166', - Parch: 0, - Cabin: null, - PassengerId: 151, - Age: 51, - Fare: 12.525, - Name: 'Bateman, Rev. Robert James', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113776', - Parch: 0, - Cabin: 'C2', - PassengerId: 152, - Age: 22, - Fare: 66.6, - Name: 'Pears, Mrs. Thomas (Edith Wearne)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'A.5. 11206', - Parch: 0, - Cabin: null, - PassengerId: 153, - Age: 55.5, - Fare: 8.05, - Name: 'Meo, Mr. Alfonzo', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/5. 851', - Parch: 2, - Cabin: null, - PassengerId: 154, - Age: 40.5, - Fare: 14.5, - Name: 'van Billiard, Mr. Austin Blyler', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'Fa 265302', - Parch: 0, - Cabin: null, - PassengerId: 155, - Age: null, - Fare: 7.3125, - Name: 'Olsen, Mr. Ole Martin', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17597', - Parch: 1, - Cabin: null, - PassengerId: 156, - Age: 51, - Fare: 61.3792, - Name: 'Williams, Mr. Charles Duane', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '35851', - Parch: 0, - Cabin: null, - PassengerId: 157, - Age: 16, - Fare: 7.7333, - Name: 'Gilnagh, Miss. Katherine "Katie"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'SOTON/OQ 392090', - Parch: 0, - Cabin: null, - PassengerId: 158, - Age: 30, - Fare: 8.05, - Name: 'Corn, Mr. Harry', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '315037', - Parch: 0, - Cabin: null, - PassengerId: 159, - Age: null, - Fare: 8.6625, - Name: 'Smiljanic, Mr. Mile', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 8, - Ticket: 'CA. 2343', - Parch: 2, - Cabin: null, - PassengerId: 160, - Age: null, - Fare: 69.55, - Name: 'Sage, Master. Thomas Henry', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '371362', - Parch: 1, - Cabin: null, - PassengerId: 161, - Age: 44, - Fare: 16.1, - Name: 'Cribb, Mr. John Hatfield', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C.A. 33595', - Parch: 0, - Cabin: null, - PassengerId: 162, - Age: 40, - Fare: 15.75, - Name: 'Watt, Mrs. James (Elizabeth "Bessie" Inglis Milne)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '347068', - Parch: 0, - Cabin: null, - PassengerId: 163, - Age: 26, - Fare: 7.775, - Name: 'Bengtsson, Mr. John Viktor', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '315093', - Parch: 0, - Cabin: null, - PassengerId: 164, - Age: 17, - Fare: 8.6625, - Name: 'Calic, Mr. Jovo', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '3101295', - Parch: 1, - Cabin: null, - PassengerId: 165, - Age: 1, - Fare: 39.6875, - Name: 'Panula, Master. Eino Viljami', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '363291', - Parch: 2, - Cabin: null, - PassengerId: 166, - Age: 9, - Fare: 20.525, - Name: 'Goldsmith, Master. Frank John William "Frankie"', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113505', - Parch: 1, - Cabin: 'E33', - PassengerId: 167, - Age: null, - Fare: 55, - Name: 'Chibnall, Mrs. (Edith Martha Bowerman)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '347088', - Parch: 4, - Cabin: null, - PassengerId: 168, - Age: 45, - Fare: 27.9, - Name: 'Skoog, Mrs. William (Anna Bernhardina Karlsson)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17318', - Parch: 0, - Cabin: null, - PassengerId: 169, - Age: null, - Fare: 25.925, - Name: 'Baumann, Mr. John D', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '1601', - Parch: 0, - Cabin: null, - PassengerId: 170, - Age: 28, - Fare: 56.4958, - Name: 'Ling, Mr. Lee', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '111240', - Parch: 0, - Cabin: 'B19', - PassengerId: 171, - Age: 61, - Fare: 33.5, - Name: 'Van der hoef, Mr. Wyckoff', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '382652', - Parch: 1, - Cabin: null, - PassengerId: 172, - Age: 4, - Fare: 29.125, - Name: 'Rice, Master. Arthur', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '347742', - Parch: 1, - Cabin: null, - PassengerId: 173, - Age: 1, - Fare: 11.1333, - Name: 'Johnson, Miss. Eleanor Ileen', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101280', - Parch: 0, - Cabin: null, - PassengerId: 174, - Age: 21, - Fare: 7.925, - Name: 'Sivola, Mr. Antti Wilhelm', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '17764', - Parch: 0, - Cabin: 'A7', - PassengerId: 175, - Age: 56, - Fare: 30.6958, - Name: 'Smith, Mr. James Clinch', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '350404', - Parch: 1, - Cabin: null, - PassengerId: 176, - Age: 18, - Fare: 7.8542, - Name: 'Klasen, Mr. Klas Albin', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '4133', - Parch: 1, - Cabin: null, - PassengerId: 177, - Age: null, - Fare: 25.4667, - Name: 'Lefebre, Master. Henry Forbes', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17595', - Parch: 0, - Cabin: 'C49', - PassengerId: 178, - Age: 50, - Fare: 28.7125, - Name: 'Isham, Miss. Ann Elizabeth', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '250653', - Parch: 0, - Cabin: null, - PassengerId: 179, - Age: 30, - Fare: 13, - Name: 'Hale, Mr. Reginald', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'LINE', - Parch: 0, - Cabin: null, - PassengerId: 180, - Age: 36, - Fare: 0, - Name: 'Leonard, Mr. Lionel', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 8, - Ticket: 'CA. 2343', - Parch: 2, - Cabin: null, - PassengerId: 181, - Age: null, - Fare: 69.55, - Name: 'Sage, Miss. Constance Gladys', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'SC/PARIS 2131', - Parch: 0, - Cabin: null, - PassengerId: 182, - Age: null, - Fare: 15.05, - Name: 'Pernot, Mr. Rene', - Survived: false, - Pclass: 2, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '347077', - Parch: 2, - Cabin: null, - PassengerId: 183, - Age: 9, - Fare: 31.3875, - Name: 'Asplund, Master. Clarence Gustaf Hugo', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '230136', - Parch: 1, - Cabin: 'F4', - PassengerId: 184, - Age: 1, - Fare: 39, - Name: 'Becker, Master. Richard F', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '315153', - Parch: 2, - Cabin: null, - PassengerId: 185, - Age: 4, - Fare: 22.025, - Name: 'Kink-Heilmann, Miss. Luise Gretchen', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113767', - Parch: 0, - Cabin: 'A32', - PassengerId: 186, - Age: null, - Fare: 50, - Name: 'Rood, Mr. Hugh Roscoe', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '370365', - Parch: 0, - Cabin: null, - PassengerId: 187, - Age: null, - Fare: 15.5, - Name: 'O\'Brien, Mrs. Thomas (Johanna "Hannah" Godfrey)', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '111428', - Parch: 0, - Cabin: null, - PassengerId: 188, - Age: 45, - Fare: 26.55, - Name: 'Romaine, Mr. Charles Hallace ("Mr C Rolmane")', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '364849', - Parch: 1, - Cabin: null, - PassengerId: 189, - Age: 40, - Fare: 15.5, - Name: 'Bourke, Mr. John', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349247', - Parch: 0, - Cabin: null, - PassengerId: 190, - Age: 36, - Fare: 7.8958, - Name: 'Turcin, Mr. Stjepan', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '234604', - Parch: 0, - Cabin: null, - PassengerId: 191, - Age: 32, - Fare: 13, - Name: 'Pinsky, Mrs. (Rosa)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '28424', - Parch: 0, - Cabin: null, - PassengerId: 192, - Age: 19, - Fare: 13, - Name: 'Carbines, Mr. William', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '350046', - Parch: 0, - Cabin: null, - PassengerId: 193, - Age: 19, - Fare: 7.8542, - Name: 'Andersen-Jensen, Miss. Carla Christine Nielsine', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '230080', - Parch: 1, - Cabin: 'F2', - PassengerId: 194, - Age: 3, - Fare: 26, - Name: 'Navratil, Master. Michel M', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17610', - Parch: 0, - Cabin: 'B4', - PassengerId: 195, - Age: 44, - Fare: 27.7208, - Name: 'Brown, Mrs. James Joseph (Margaret Tobin)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17569', - Parch: 0, - Cabin: 'B80', - PassengerId: 196, - Age: 58, - Fare: 146.5208, - Name: 'Lurette, Miss. Elise', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '368703', - Parch: 0, - Cabin: null, - PassengerId: 197, - Age: null, - Fare: 7.75, - Name: 'Mernagh, Mr. Robert', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '4579', - Parch: 1, - Cabin: null, - PassengerId: 198, - Age: 42, - Fare: 8.4042, - Name: 'Olsen, Mr. Karl Siegwart Andreas', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '370370', - Parch: 0, - Cabin: null, - PassengerId: 199, - Age: null, - Fare: 7.75, - Name: 'Madigan, Miss. Margaret "Maggie"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '248747', - Parch: 0, - Cabin: null, - PassengerId: 200, - Age: 24, - Fare: 13, - Name: 'Yrois, Miss. Henriette ("Mrs Harbeck")', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '345770', - Parch: 0, - Cabin: null, - PassengerId: 201, - Age: 28, - Fare: 9.5, - Name: 'Vande Walle, Mr. Nestor Cyriel', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 8, - Ticket: 'CA. 2343', - Parch: 2, - Cabin: null, - PassengerId: 202, - Age: null, - Fare: 69.55, - Name: 'Sage, Mr. Frederick', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '3101264', - Parch: 0, - Cabin: null, - PassengerId: 203, - Age: 34, - Fare: 6.4958, - Name: 'Johanson, Mr. Jakob Alfred', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2628', - Parch: 0, - Cabin: null, - PassengerId: 204, - Age: 45.5, - Fare: 7.225, - Name: 'Youseff, Mr. Gerious', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/5 3540', - Parch: 0, - Cabin: null, - PassengerId: 205, - Age: 18, - Fare: 8.05, - Name: 'Cohen, Mr. Gurshon "Gus"', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347054', - Parch: 1, - Cabin: 'G6', - PassengerId: 206, - Age: 2, - Fare: 10.4625, - Name: 'Strom, Miss. Telma Matilda', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '3101278', - Parch: 0, - Cabin: null, - PassengerId: 207, - Age: 32, - Fare: 15.85, - Name: 'Backstrom, Mr. Karl Alfred', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2699', - Parch: 0, - Cabin: null, - PassengerId: 208, - Age: 26, - Fare: 18.7875, - Name: 'Albimona, Mr. Nassef Cassem', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '367231', - Parch: 0, - Cabin: null, - PassengerId: 209, - Age: 16, - Fare: 7.75, - Name: 'Carr, Miss. Helen "Ellen"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '112277', - Parch: 0, - Cabin: 'A31', - PassengerId: 210, - Age: 40, - Fare: 31, - Name: 'Blank, Mr. Henry', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/O.Q. 3101311', - Parch: 0, - Cabin: null, - PassengerId: 211, - Age: 24, - Fare: 7.05, - Name: 'Ali, Mr. Ahmed', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'F.C.C. 13528', - Parch: 0, - Cabin: null, - PassengerId: 212, - Age: 35, - Fare: 21, - Name: 'Cameron, Miss. Clear Annie', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'A/5 21174', - Parch: 0, - Cabin: null, - PassengerId: 213, - Age: 22, - Fare: 7.25, - Name: 'Perkin, Mr. John Henry', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '250646', - Parch: 0, - Cabin: null, - PassengerId: 214, - Age: 30, - Fare: 13, - Name: 'Givard, Mr. Hans Kristensen', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '367229', - Parch: 0, - Cabin: null, - PassengerId: 215, - Age: null, - Fare: 7.75, - Name: 'Kiernan, Mr. Philip', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '35273', - Parch: 0, - Cabin: 'D36', - PassengerId: 216, - Age: 31, - Fare: 113.275, - Name: 'Newell, Miss. Madeleine', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'STON/O2. 3101283', - Parch: 0, - Cabin: null, - PassengerId: 217, - Age: 27, - Fare: 7.925, - Name: 'Honkanen, Miss. Eliina', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '243847', - Parch: 0, - Cabin: null, - PassengerId: 218, - Age: 42, - Fare: 27, - Name: 'Jacobsohn, Mr. Sidney Samuel', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '11813', - Parch: 0, - Cabin: 'D15', - PassengerId: 219, - Age: 32, - Fare: 76.2917, - Name: 'Bazzani, Miss. Albina', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'W/C 14208', - Parch: 0, - Cabin: null, - PassengerId: 220, - Age: 30, - Fare: 10.5, - Name: 'Harris, Mr. Walter', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/OQ 392089', - Parch: 0, - Cabin: null, - PassengerId: 221, - Age: 16, - Fare: 8.05, - Name: 'Sunderland, Mr. Victor Francis', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '220367', - Parch: 0, - Cabin: null, - PassengerId: 222, - Age: 27, - Fare: 13, - Name: 'Bracken, Mr. James H', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '21440', - Parch: 0, - Cabin: null, - PassengerId: 223, - Age: 51, - Fare: 8.05, - Name: 'Green, Mr. George Henry', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349234', - Parch: 0, - Cabin: null, - PassengerId: 224, - Age: null, - Fare: 7.8958, - Name: 'Nenkoff, Mr. Christo', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '19943', - Parch: 0, - Cabin: 'C93', - PassengerId: 225, - Age: 38, - Fare: 90, - Name: 'Hoyt, Mr. Frederick Maxfield', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PP 4348', - Parch: 0, - Cabin: null, - PassengerId: 226, - Age: 22, - Fare: 9.35, - Name: 'Berglund, Mr. Karl Ivar Sven', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SW/PP 751', - Parch: 0, - Cabin: null, - PassengerId: 227, - Age: 19, - Fare: 10.5, - Name: 'Mellors, Mr. William John', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/5 21173', - Parch: 0, - Cabin: null, - PassengerId: 228, - Age: 20.5, - Fare: 7.25, - Name: 'Lovell, Mr. John Hall ("Henry")', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '236171', - Parch: 0, - Cabin: null, - PassengerId: 229, - Age: 18, - Fare: 13, - Name: 'Fahlstrom, Mr. Arne Jonas', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '4133', - Parch: 1, - Cabin: null, - PassengerId: 230, - Age: null, - Fare: 25.4667, - Name: 'Lefebre, Miss. Mathilde', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '36973', - Parch: 0, - Cabin: 'C83', - PassengerId: 231, - Age: 35, - Fare: 83.475, - Name: 'Harris, Mrs. Henry Birkhardt (Irene Wallach)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '347067', - Parch: 0, - Cabin: null, - PassengerId: 232, - Age: 29, - Fare: 7.775, - Name: 'Larsson, Mr. Bengt Edvin', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '237442', - Parch: 0, - Cabin: null, - PassengerId: 233, - Age: 59, - Fare: 13.5, - Name: 'Sjostedt, Mr. Ernst Adolf', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '347077', - Parch: 2, - Cabin: null, - PassengerId: 234, - Age: 5, - Fare: 31.3875, - Name: 'Asplund, Miss. Lillian Gertrud', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'C.A. 29566', - Parch: 0, - Cabin: null, - PassengerId: 235, - Age: 24, - Fare: 10.5, - Name: 'Leyson, Mr. Robert William Norman', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'W./C. 6609', - Parch: 0, - Cabin: null, - PassengerId: 236, - Age: null, - Fare: 7.55, - Name: 'Harknett, Miss. Alice Phoebe', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '26707', - Parch: 0, - Cabin: null, - PassengerId: 237, - Age: 44, - Fare: 26, - Name: 'Hold, Mr. Stephen', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C.A. 31921', - Parch: 2, - Cabin: null, - PassengerId: 238, - Age: 8, - Fare: 26.25, - Name: 'Collyer, Miss. Marjorie "Lottie"', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '28665', - Parch: 0, - Cabin: null, - PassengerId: 239, - Age: 19, - Fare: 10.5, - Name: 'Pengelly, Mr. Frederick William', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SCO/W 1585', - Parch: 0, - Cabin: null, - PassengerId: 240, - Age: 33, - Fare: 12.275, - Name: 'Hunt, Mr. George Henry', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2665', - Parch: 0, - Cabin: null, - PassengerId: 241, - Age: null, - Fare: 14.4542, - Name: 'Zabour, Miss. Thamine', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '367230', - Parch: 0, - Cabin: null, - PassengerId: 242, - Age: null, - Fare: 15.5, - Name: 'Murphy, Miss. Katherine "Kate"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'W./C. 14263', - Parch: 0, - Cabin: null, - PassengerId: 243, - Age: 29, - Fare: 10.5, - Name: 'Coleridge, Mr. Reginald Charles', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101275', - Parch: 0, - Cabin: null, - PassengerId: 244, - Age: 22, - Fare: 7.125, - Name: 'Maenpaa, Mr. Matti Alexanteri', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2694', - Parch: 0, - Cabin: null, - PassengerId: 245, - Age: 30, - Fare: 7.225, - Name: 'Attalah, Mr. Sleiman', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '19928', - Parch: 0, - Cabin: 'C78', - PassengerId: 246, - Age: 44, - Fare: 90, - Name: 'Minahan, Dr. William Edward', - Survived: false, - Pclass: 1, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347071', - Parch: 0, - Cabin: null, - PassengerId: 247, - Age: 25, - Fare: 7.775, - Name: 'Lindahl, Miss. Agda Thorilda Viktoria', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '250649', - Parch: 2, - Cabin: null, - PassengerId: 248, - Age: 24, - Fare: 14.5, - Name: 'Hamalainen, Mrs. William (Anna)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '11751', - Parch: 1, - Cabin: 'D35', - PassengerId: 249, - Age: 37, - Fare: 52.5542, - Name: 'Beckwith, Mr. Richard Leonard', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '244252', - Parch: 0, - Cabin: null, - PassengerId: 250, - Age: 54, - Fare: 26, - Name: 'Carter, Rev. Ernest Courtenay', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '362316', - Parch: 0, - Cabin: null, - PassengerId: 251, - Age: null, - Fare: 7.25, - Name: 'Reed, Mr. James George', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '347054', - Parch: 1, - Cabin: 'G6', - PassengerId: 252, - Age: 29, - Fare: 10.4625, - Name: 'Strom, Mrs. Wilhelm (Elna Matilda Persson)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113514', - Parch: 0, - Cabin: 'C87', - PassengerId: 253, - Age: 62, - Fare: 26.55, - Name: 'Stead, Mr. William Thomas', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'A/5. 3336', - Parch: 0, - Cabin: null, - PassengerId: 254, - Age: 30, - Fare: 16.1, - Name: 'Lobb, Mr. William Arthur', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '370129', - Parch: 2, - Cabin: null, - PassengerId: 255, - Age: 41, - Fare: 20.2125, - Name: 'Rosblom, Mrs. Viktor (Helena Wilhelmina)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2650', - Parch: 2, - Cabin: null, - PassengerId: 256, - Age: 29, - Fare: 15.2458, - Name: 'Touma, Mrs. Darwis (Hanne Youssef Razi)', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17585', - Parch: 0, - Cabin: null, - PassengerId: 257, - Age: null, - Fare: 79.2, - Name: 'Thorne, Mrs. Gertrude Maybelle', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '110152', - Parch: 0, - Cabin: 'B77', - PassengerId: 258, - Age: 30, - Fare: 86.5, - Name: 'Cherry, Miss. Gladys', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17755', - Parch: 0, - Cabin: null, - PassengerId: 259, - Age: 35, - Fare: 512.3292, - Name: 'Ward, Miss. Anna', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '230433', - Parch: 1, - Cabin: null, - PassengerId: 260, - Age: 50, - Fare: 26, - Name: 'Parrish, Mrs. (Lutie Davis)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '384461', - Parch: 0, - Cabin: null, - PassengerId: 261, - Age: null, - Fare: 7.75, - Name: 'Smith, Mr. Thomas', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '347077', - Parch: 2, - Cabin: null, - PassengerId: 262, - Age: 3, - Fare: 31.3875, - Name: 'Asplund, Master. Edvin Rojj Felix', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '110413', - Parch: 1, - Cabin: 'E67', - PassengerId: 263, - Age: 52, - Fare: 79.65, - Name: 'Taussig, Mr. Emil', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '112059', - Parch: 0, - Cabin: 'B94', - PassengerId: 264, - Age: 40, - Fare: 0, - Name: 'Harrison, Mr. William', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '382649', - Parch: 0, - Cabin: null, - PassengerId: 265, - Age: null, - Fare: 7.75, - Name: 'Henry, Miss. Delia', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'C.A. 17248', - Parch: 0, - Cabin: null, - PassengerId: 266, - Age: 36, - Fare: 10.5, - Name: 'Reeves, Mr. David', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '3101295', - Parch: 1, - Cabin: null, - PassengerId: 267, - Age: 16, - Fare: 39.6875, - Name: 'Panula, Mr. Ernesti Arvid', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '347083', - Parch: 0, - Cabin: null, - PassengerId: 268, - Age: 25, - Fare: 7.775, - Name: 'Persson, Mr. Ernst Ulrik', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17582', - Parch: 1, - Cabin: 'C125', - PassengerId: 269, - Age: 58, - Fare: 153.4625, - Name: 'Graham, Mrs. William Thompson (Edith Junkins)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17760', - Parch: 0, - Cabin: 'C99', - PassengerId: 270, - Age: 35, - Fare: 135.6333, - Name: 'Bissette, Miss. Amelia', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113798', - Parch: 0, - Cabin: null, - PassengerId: 271, - Age: null, - Fare: 31, - Name: 'Cairns, Mr. Alexander', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'LINE', - Parch: 0, - Cabin: null, - PassengerId: 272, - Age: 25, - Fare: 0, - Name: 'Tornquist, Mr. William Henry', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '250644', - Parch: 1, - Cabin: null, - PassengerId: 273, - Age: 41, - Fare: 19.5, - Name: 'Mellinger, Mrs. (Elizabeth Anne Maidment)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17596', - Parch: 1, - Cabin: 'C118', - PassengerId: 274, - Age: 37, - Fare: 29.7, - Name: 'Natsch, Mr. Charles H', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '370375', - Parch: 0, - Cabin: null, - PassengerId: 275, - Age: null, - Fare: 7.75, - Name: 'Healy, Miss. Hanora "Nora"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '13502', - Parch: 0, - Cabin: 'D7', - PassengerId: 276, - Age: 63, - Fare: 77.9583, - Name: 'Andrews, Miss. Kornelia Theodosia', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '347073', - Parch: 0, - Cabin: null, - PassengerId: 277, - Age: 45, - Fare: 7.75, - Name: 'Lindblom, Miss. Augusta Charlotta', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '239853', - Parch: 0, - Cabin: null, - PassengerId: 278, - Age: null, - Fare: 0, - Name: 'Parkes, Mr. Francis "Frank"', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '382652', - Parch: 1, - Cabin: null, - PassengerId: 279, - Age: 7, - Fare: 29.125, - Name: 'Rice, Master. Eric', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 2673', - Parch: 1, - Cabin: null, - PassengerId: 280, - Age: 35, - Fare: 20.25, - Name: 'Abbott, Mrs. Stanton (Rosa Hunt)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '336439', - Parch: 0, - Cabin: null, - PassengerId: 281, - Age: 65, - Fare: 7.75, - Name: 'Duane, Mr. Frank', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347464', - Parch: 0, - Cabin: null, - PassengerId: 282, - Age: 28, - Fare: 7.8542, - Name: 'Olsson, Mr. Nils Johan Goransson', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '345778', - Parch: 0, - Cabin: null, - PassengerId: 283, - Age: 16, - Fare: 9.5, - Name: 'de Pelsmaeker, Mr. Alfons', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/5. 10482', - Parch: 0, - Cabin: null, - PassengerId: 284, - Age: 19, - Fare: 8.05, - Name: 'Dorking, Mr. Edward Arthur', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113056', - Parch: 0, - Cabin: 'A19', - PassengerId: 285, - Age: null, - Fare: 26, - Name: 'Smith, Mr. Richard William', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349239', - Parch: 0, - Cabin: null, - PassengerId: 286, - Age: 33, - Fare: 8.6625, - Name: 'Stankovic, Mr. Ivan', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '345774', - Parch: 0, - Cabin: null, - PassengerId: 287, - Age: 30, - Fare: 9.5, - Name: 'de Mulder, Mr. Theodore', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349206', - Parch: 0, - Cabin: null, - PassengerId: 288, - Age: 22, - Fare: 7.8958, - Name: 'Naidenoff, Mr. Penko', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '237798', - Parch: 0, - Cabin: null, - PassengerId: 289, - Age: 42, - Fare: 13, - Name: 'Hosono, Mr. Masabumi', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '370373', - Parch: 0, - Cabin: null, - PassengerId: 290, - Age: 22, - Fare: 7.75, - Name: 'Connolly, Miss. Kate', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '19877', - Parch: 0, - Cabin: null, - PassengerId: 291, - Age: 26, - Fare: 78.85, - Name: 'Barber, Miss. Ellen "Nellie"', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '11967', - Parch: 0, - Cabin: 'B49', - PassengerId: 292, - Age: 19, - Fare: 91.0792, - Name: 'Bishop, Mrs. Dickinson H (Helen Walton)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'SC/Paris 2163', - Parch: 0, - Cabin: 'D', - PassengerId: 293, - Age: 36, - Fare: 12.875, - Name: 'Levy, Mr. Rene Jacques', - Survived: false, - Pclass: 2, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349236', - Parch: 0, - Cabin: null, - PassengerId: 294, - Age: 24, - Fare: 8.85, - Name: 'Haas, Miss. Aloisia', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349233', - Parch: 0, - Cabin: null, - PassengerId: 295, - Age: 24, - Fare: 7.8958, - Name: 'Mineff, Mr. Ivan', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17612', - Parch: 0, - Cabin: null, - PassengerId: 296, - Age: null, - Fare: 27.7208, - Name: 'Lewy, Mr. Ervin G', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2693', - Parch: 0, - Cabin: null, - PassengerId: 297, - Age: 23.5, - Fare: 7.2292, - Name: 'Hanna, Mr. Mansour', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113781', - Parch: 2, - Cabin: 'C22 C26', - PassengerId: 298, - Age: 2, - Fare: 151.55, - Name: 'Allison, Miss. Helen Loraine', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '19988', - Parch: 0, - Cabin: 'C106', - PassengerId: 299, - Age: null, - Fare: 30.5, - Name: 'Saalfeld, Mr. Adolphe', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17558', - Parch: 1, - Cabin: 'B58 B60', - PassengerId: 300, - Age: 50, - Fare: 247.5208, - Name: 'Baxter, Mrs. James (Helene DeLaudeniere Chaput)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '9234', - Parch: 0, - Cabin: null, - PassengerId: 301, - Age: null, - Fare: 7.75, - Name: 'Kelly, Miss. Anna Katherine "Annie Kate"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: '367226', - Parch: 0, - Cabin: null, - PassengerId: 302, - Age: null, - Fare: 23.25, - Name: 'McCoy, Mr. Bernard', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'LINE', - Parch: 0, - Cabin: null, - PassengerId: 303, - Age: 19, - Fare: 0, - Name: 'Johnson, Mr. William Cahoone Jr', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '226593', - Parch: 0, - Cabin: 'E101', - PassengerId: 304, - Age: null, - Fare: 12.35, - Name: 'Keane, Miss. Nora A', - Survived: true, - Pclass: 2, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'A/5 2466', - Parch: 0, - Cabin: null, - PassengerId: 305, - Age: null, - Fare: 8.05, - Name: 'Williams, Mr. Howard Hugh "Harry"', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113781', - Parch: 2, - Cabin: 'C22 C26', - PassengerId: 306, - Age: 0.92, - Fare: 151.55, - Name: 'Allison, Master. Hudson Trevor', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '17421', - Parch: 0, - Cabin: null, - PassengerId: 307, - Age: null, - Fare: 110.8833, - Name: 'Fleming, Miss. Margaret', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'PC 17758', - Parch: 0, - Cabin: 'C65', - PassengerId: 308, - Age: 17, - Fare: 108.9, - Name: 'Penasco y Castellana, Mrs. Victor de Satode (Maria Josefa Perez de Soto y Vallejo)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'P/PP 3381', - Parch: 0, - Cabin: null, - PassengerId: 309, - Age: 30, - Fare: 24, - Name: 'Abelson, Mr. Samuel', - Survived: false, - Pclass: 2, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17485', - Parch: 0, - Cabin: 'E36', - PassengerId: 310, - Age: 30, - Fare: 56.9292, - Name: 'Francatelli, Miss. Laura Mabel', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '11767', - Parch: 0, - Cabin: 'C54', - PassengerId: 311, - Age: 24, - Fare: 83.1583, - Name: 'Hays, Miss. Margaret Bechstein', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: 'PC 17608', - Parch: 2, - Cabin: 'B57 B59 B63 B66', - PassengerId: 312, - Age: 18, - Fare: 262.375, - Name: 'Ryerson, Miss. Emily Borie', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '250651', - Parch: 1, - Cabin: null, - PassengerId: 313, - Age: 26, - Fare: 26, - Name: 'Lahtinen, Mrs. William (Anna Sylfven)', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349243', - Parch: 0, - Cabin: null, - PassengerId: 314, - Age: 28, - Fare: 7.8958, - Name: 'Hendekovic, Mr. Ignjac', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'F.C.C. 13529', - Parch: 1, - Cabin: null, - PassengerId: 315, - Age: 43, - Fare: 26.25, - Name: 'Hart, Mr. Benjamin', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347470', - Parch: 0, - Cabin: null, - PassengerId: 316, - Age: 26, - Fare: 7.8542, - Name: 'Nilsson, Miss. Helmina Josefina', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '244367', - Parch: 0, - Cabin: null, - PassengerId: 317, - Age: 24, - Fare: 26, - Name: 'Kantor, Mrs. Sinai (Miriam Sternin)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '29011', - Parch: 0, - Cabin: null, - PassengerId: 318, - Age: 54, - Fare: 14, - Name: 'Moraweck, Dr. Ernest', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '36928', - Parch: 2, - Cabin: 'C7', - PassengerId: 319, - Age: 31, - Fare: 164.8667, - Name: 'Wick, Miss. Mary Natalie', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '16966', - Parch: 1, - Cabin: 'E34', - PassengerId: 320, - Age: 40, - Fare: 134.5, - Name: 'Spedden, Mrs. Frederic Oakley (Margaretta Corning Stone)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'A/5 21172', - Parch: 0, - Cabin: null, - PassengerId: 321, - Age: 22, - Fare: 7.25, - Name: 'Dennis, Mr. Samuel', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349219', - Parch: 0, - Cabin: null, - PassengerId: 322, - Age: 27, - Fare: 7.8958, - Name: 'Danoff, Mr. Yoto', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '234818', - Parch: 0, - Cabin: null, - PassengerId: 323, - Age: 30, - Fare: 12.35, - Name: 'Slayter, Miss. Hilda Mary', - Survived: true, - Pclass: 2, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '248738', - Parch: 1, - Cabin: null, - PassengerId: 324, - Age: 22, - Fare: 29, - Name: 'Caldwell, Mrs. Albert Francis (Sylvia Mae Harbaugh)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 8, - Ticket: 'CA. 2343', - Parch: 2, - Cabin: null, - PassengerId: 325, - Age: null, - Fare: 69.55, - Name: 'Sage, Mr. George John Jr', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17760', - Parch: 0, - Cabin: 'C32', - PassengerId: 326, - Age: 36, - Fare: 135.6333, - Name: 'Young, Miss. Marie Grice', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '345364', - Parch: 0, - Cabin: null, - PassengerId: 327, - Age: 61, - Fare: 6.2375, - Name: 'Nysveen, Mr. Johan Hansen', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '28551', - Parch: 0, - Cabin: 'D', - PassengerId: 328, - Age: 36, - Fare: 13, - Name: 'Ball, Mrs. (Ada E Hall)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '363291', - Parch: 1, - Cabin: null, - PassengerId: 329, - Age: 31, - Fare: 20.525, - Name: 'Goldsmith, Mrs. Frank John (Emily Alice Brown)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '111361', - Parch: 1, - Cabin: 'B18', - PassengerId: 330, - Age: 16, - Fare: 57.9792, - Name: 'Hippach, Miss. Jean Gertrude', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: '367226', - Parch: 0, - Cabin: null, - PassengerId: 331, - Age: null, - Fare: 23.25, - Name: 'McCoy, Miss. Agnes', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113043', - Parch: 0, - Cabin: 'C124', - PassengerId: 332, - Age: 45.5, - Fare: 28.5, - Name: 'Partner, Mr. Austen', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17582', - Parch: 1, - Cabin: 'C91', - PassengerId: 333, - Age: 38, - Fare: 153.4625, - Name: 'Graham, Mr. George Edward', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '345764', - Parch: 0, - Cabin: null, - PassengerId: 334, - Age: 16, - Fare: 18, - Name: 'Vander Planke, Mr. Leo Edmondus', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'PC 17611', - Parch: 0, - Cabin: null, - PassengerId: 335, - Age: null, - Fare: 133.65, - Name: 'Frauenthal, Mrs. Henry William (Clara Heinsheimer)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349225', - Parch: 0, - Cabin: null, - PassengerId: 336, - Age: null, - Fare: 7.8958, - Name: 'Denkoff, Mr. Mitto', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113776', - Parch: 0, - Cabin: 'C2', - PassengerId: 337, - Age: 29, - Fare: 66.6, - Name: 'Pears, Mr. Thomas Clinton', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '16966', - Parch: 0, - Cabin: 'E40', - PassengerId: 338, - Age: 41, - Fare: 134.5, - Name: 'Burns, Miss. Elizabeth Margaret', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '7598', - Parch: 0, - Cabin: null, - PassengerId: 339, - Age: 45, - Fare: 8.05, - Name: 'Dahl, Mr. Karl Edwart', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113784', - Parch: 0, - Cabin: 'T', - PassengerId: 340, - Age: 45, - Fare: 35.5, - Name: 'Blackwell, Mr. Stephen Weart', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '230080', - Parch: 1, - Cabin: 'F2', - PassengerId: 341, - Age: 2, - Fare: 26, - Name: 'Navratil, Master. Edmond Roger', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '19950', - Parch: 2, - Cabin: 'C23 C25 C27', - PassengerId: 342, - Age: 24, - Fare: 263, - Name: 'Fortune, Miss. Alice Elizabeth', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '248740', - Parch: 0, - Cabin: null, - PassengerId: 343, - Age: 28, - Fare: 13, - Name: 'Collander, Mr. Erik Gustaf', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '244361', - Parch: 0, - Cabin: null, - PassengerId: 344, - Age: 25, - Fare: 13, - Name: 'Sedgwick, Mr. Charles Frederick Waddington', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '229236', - Parch: 0, - Cabin: null, - PassengerId: 345, - Age: 36, - Fare: 13, - Name: 'Fox, Mr. Stanley Hubert', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '248733', - Parch: 0, - Cabin: 'F33', - PassengerId: 346, - Age: 24, - Fare: 13, - Name: 'Brown, Miss. Amelia "Mildred"', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '31418', - Parch: 0, - Cabin: null, - PassengerId: 347, - Age: 40, - Fare: 13, - Name: 'Smith, Miss. Marion Elsie', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '386525', - Parch: 0, - Cabin: null, - PassengerId: 348, - Age: null, - Fare: 16.1, - Name: 'Davison, Mrs. Thomas Henry (Mary E Finck)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'C.A. 37671', - Parch: 1, - Cabin: null, - PassengerId: 349, - Age: 3, - Fare: 15.9, - Name: 'Coutts, Master. William Loch "William"', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '315088', - Parch: 0, - Cabin: null, - PassengerId: 350, - Age: 42, - Fare: 8.6625, - Name: 'Dimic, Mr. Jovan', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '7267', - Parch: 0, - Cabin: null, - PassengerId: 351, - Age: 23, - Fare: 9.225, - Name: 'Odahl, Mr. Nils Martin', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113510', - Parch: 0, - Cabin: 'C128', - PassengerId: 352, - Age: null, - Fare: 35, - Name: 'Williams-Lambert, Mr. Fletcher Fellows', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2695', - Parch: 1, - Cabin: null, - PassengerId: 353, - Age: 15, - Fare: 7.2292, - Name: 'Elias, Mr. Tannous', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '349237', - Parch: 0, - Cabin: null, - PassengerId: 354, - Age: 25, - Fare: 17.8, - Name: 'Arnold-Franchi, Mr. Josef', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2647', - Parch: 0, - Cabin: null, - PassengerId: 355, - Age: null, - Fare: 7.225, - Name: 'Yousif, Mr. Wazli', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '345783', - Parch: 0, - Cabin: null, - PassengerId: 356, - Age: 28, - Fare: 9.5, - Name: 'Vanden Steen, Mr. Leo Peter', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113505', - Parch: 1, - Cabin: 'E33', - PassengerId: 357, - Age: 22, - Fare: 55, - Name: 'Bowerman, Miss. Elsie Edith', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '237671', - Parch: 0, - Cabin: null, - PassengerId: 358, - Age: 38, - Fare: 13, - Name: 'Funk, Miss. Annie Clemmer', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '330931', - Parch: 0, - Cabin: null, - PassengerId: 359, - Age: null, - Fare: 7.8792, - Name: 'McGovern, Miss. Mary', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '330980', - Parch: 0, - Cabin: null, - PassengerId: 360, - Age: null, - Fare: 7.8792, - Name: 'Mockler, Miss. Helen Mary "Ellie"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '347088', - Parch: 4, - Cabin: null, - PassengerId: 361, - Age: 40, - Fare: 27.9, - Name: 'Skoog, Mr. Wilhelm', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'SC/PARIS 2167', - Parch: 0, - Cabin: null, - PassengerId: 362, - Age: 29, - Fare: 27.7208, - Name: 'del Carlo, Mr. Sebastiano', - Survived: false, - Pclass: 2, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2691', - Parch: 1, - Cabin: null, - PassengerId: 363, - Age: 45, - Fare: 14.4542, - Name: 'Barbara, Mrs. (Catherine David)', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'SOTON/O.Q. 3101310', - Parch: 0, - Cabin: null, - PassengerId: 364, - Age: 35, - Fare: 7.05, - Name: 'Asim, Mr. Adola', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '370365', - Parch: 0, - Cabin: null, - PassengerId: 365, - Age: null, - Fare: 15.5, - Name: "O'Brien, Mr. Thomas", - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C 7076', - Parch: 0, - Cabin: null, - PassengerId: 366, - Age: 30, - Fare: 7.25, - Name: 'Adahl, Mr. Mauritz Nils Martin', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '110813', - Parch: 0, - Cabin: 'D37', - PassengerId: 367, - Age: 60, - Fare: 75.25, - Name: 'Warren, Mrs. Frank Manley (Anna Sophia Atkinson)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2626', - Parch: 0, - Cabin: null, - PassengerId: 368, - Age: null, - Fare: 7.2292, - Name: 'Moussa, Mrs. (Mantoura Boulos)', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '14313', - Parch: 0, - Cabin: null, - PassengerId: 369, - Age: null, - Fare: 7.75, - Name: 'Jermyn, Miss. Annie', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17477', - Parch: 0, - Cabin: 'B35', - PassengerId: 370, - Age: 24, - Fare: 69.3, - Name: 'Aubart, Mme. Leontine Pauline', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '11765', - Parch: 0, - Cabin: 'E50', - PassengerId: 371, - Age: 25, - Fare: 55.4417, - Name: 'Harder, Mr. George Achilles', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '3101267', - Parch: 0, - Cabin: null, - PassengerId: 372, - Age: 18, - Fare: 6.4958, - Name: 'Wiklund, Mr. Jakob Alfred', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '323951', - Parch: 0, - Cabin: null, - PassengerId: 373, - Age: 19, - Fare: 8.05, - Name: 'Beavan, Mr. William Thomas', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17760', - Parch: 0, - Cabin: null, - PassengerId: 374, - Age: 22, - Fare: 135.6333, - Name: 'Ringhini, Mr. Sante', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '349909', - Parch: 1, - Cabin: null, - PassengerId: 375, - Age: 3, - Fare: 21.075, - Name: 'Palsson, Miss. Stina Viola', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'PC 17604', - Parch: 0, - Cabin: null, - PassengerId: 376, - Age: null, - Fare: 82.1708, - Name: 'Meyer, Mrs. Edgar Joseph (Leila Saks)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'C 7077', - Parch: 0, - Cabin: null, - PassengerId: 377, - Age: 22, - Fare: 7.25, - Name: 'Landergren, Miss. Aurora Adelia', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113503', - Parch: 2, - Cabin: 'C82', - PassengerId: 378, - Age: 27, - Fare: 211.5, - Name: 'Widener, Mr. Harry Elkins', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2648', - Parch: 0, - Cabin: null, - PassengerId: 379, - Age: 20, - Fare: 4.0125, - Name: 'Betros, Mr. Tannous', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347069', - Parch: 0, - Cabin: null, - PassengerId: 380, - Age: 19, - Fare: 7.775, - Name: 'Gustafsson, Mr. Karl Gideon', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17757', - Parch: 0, - Cabin: null, - PassengerId: 381, - Age: 42, - Fare: 227.525, - Name: 'Bidois, Miss. Rosalie', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2653', - Parch: 2, - Cabin: null, - PassengerId: 382, - Age: 1, - Fare: 15.7417, - Name: 'Nakid, Miss. Maria ("Mary")', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101293', - Parch: 0, - Cabin: null, - PassengerId: 383, - Age: 32, - Fare: 7.925, - Name: 'Tikkanen, Mr. Juho', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113789', - Parch: 0, - Cabin: null, - PassengerId: 384, - Age: 35, - Fare: 52, - Name: 'Holverson, Mrs. Alexander Oskar (Mary Aline Towner)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349227', - Parch: 0, - Cabin: null, - PassengerId: 385, - Age: null, - Fare: 7.8958, - Name: 'Plotcharsky, Mr. Vasil', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'S.O.C. 14879', - Parch: 0, - Cabin: null, - PassengerId: 386, - Age: 18, - Fare: 73.5, - Name: 'Davies, Mr. Charles Henry', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 5, - Ticket: 'CA 2144', - Parch: 2, - Cabin: null, - PassengerId: 387, - Age: 1, - Fare: 46.9, - Name: 'Goodwin, Master. Sidney Leonard', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '27849', - Parch: 0, - Cabin: null, - PassengerId: 388, - Age: 36, - Fare: 13, - Name: 'Buss, Miss. Kate', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '367655', - Parch: 0, - Cabin: null, - PassengerId: 389, - Age: null, - Fare: 7.7292, - Name: 'Sadlier, Mr. Matthew', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SC 1748', - Parch: 0, - Cabin: null, - PassengerId: 390, - Age: 17, - Fare: 12, - Name: 'Lehmann, Miss. Bertha', - Survived: true, - Pclass: 2, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '113760', - Parch: 2, - Cabin: 'B96 B98', - PassengerId: 391, - Age: 36, - Fare: 120, - Name: 'Carter, Mr. William Ernest', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350034', - Parch: 0, - Cabin: null, - PassengerId: 392, - Age: 21, - Fare: 7.7958, - Name: 'Jansson, Mr. Carl Olof', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '3101277', - Parch: 0, - Cabin: null, - PassengerId: 393, - Age: 28, - Fare: 7.925, - Name: 'Gustafsson, Mr. Johan Birger', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '35273', - Parch: 0, - Cabin: 'D36', - PassengerId: 394, - Age: 23, - Fare: 113.275, - Name: 'Newell, Miss. Marjorie', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PP 9549', - Parch: 2, - Cabin: 'G6', - PassengerId: 395, - Age: 24, - Fare: 16.7, - Name: 'Sandstrom, Mrs. Hjalmar (Agnes Charlotta Bengtsson)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '350052', - Parch: 0, - Cabin: null, - PassengerId: 396, - Age: 22, - Fare: 7.7958, - Name: 'Johansson, Mr. Erik', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350407', - Parch: 0, - Cabin: null, - PassengerId: 397, - Age: 31, - Fare: 7.8542, - Name: 'Olsson, Miss. Elina', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '28403', - Parch: 0, - Cabin: null, - PassengerId: 398, - Age: 46, - Fare: 26, - Name: 'McKane, Mr. Peter David', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '244278', - Parch: 0, - Cabin: null, - PassengerId: 399, - Age: 23, - Fare: 10.5, - Name: 'Pain, Dr. Alfred', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '240929', - Parch: 0, - Cabin: null, - PassengerId: 400, - Age: 28, - Fare: 12.65, - Name: 'Trout, Mrs. William H (Jessie L)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101289', - Parch: 0, - Cabin: null, - PassengerId: 401, - Age: 39, - Fare: 7.925, - Name: 'Niskanen, Mr. Juha', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '341826', - Parch: 0, - Cabin: null, - PassengerId: 402, - Age: 26, - Fare: 8.05, - Name: 'Adams, Mr. John', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '4137', - Parch: 0, - Cabin: null, - PassengerId: 403, - Age: 21, - Fare: 9.825, - Name: 'Jussila, Miss. Mari Aina', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'STON/O2. 3101279', - Parch: 0, - Cabin: null, - PassengerId: 404, - Age: 28, - Fare: 15.85, - Name: 'Hakkarainen, Mr. Pekka Pietari', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '315096', - Parch: 0, - Cabin: null, - PassengerId: 405, - Age: 20, - Fare: 8.6625, - Name: 'Oreskovic, Miss. Marija', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '28664', - Parch: 0, - Cabin: null, - PassengerId: 406, - Age: 34, - Fare: 21, - Name: 'Gale, Mr. Shadrach', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347064', - Parch: 0, - Cabin: null, - PassengerId: 407, - Age: 51, - Fare: 7.75, - Name: 'Widegren, Mr. Carl/Charles Peter', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '29106', - Parch: 1, - Cabin: null, - PassengerId: 408, - Age: 3, - Fare: 18.75, - Name: 'Richards, Master. William Rowe', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '312992', - Parch: 0, - Cabin: null, - PassengerId: 409, - Age: 21, - Fare: 7.775, - Name: 'Birkeland, Mr. Hans Martin Monsen', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '4133', - Parch: 1, - Cabin: null, - PassengerId: 410, - Age: null, - Fare: 25.4667, - Name: 'Lefebre, Miss. Ida', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349222', - Parch: 0, - Cabin: null, - PassengerId: 411, - Age: null, - Fare: 7.8958, - Name: 'Sdycoff, Mr. Todor', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '394140', - Parch: 0, - Cabin: null, - PassengerId: 412, - Age: null, - Fare: 6.8583, - Name: 'Hart, Mr. Henry', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '19928', - Parch: 0, - Cabin: 'C78', - PassengerId: 413, - Age: 33, - Fare: 90, - Name: 'Minahan, Miss. Daisy E', - Survived: true, - Pclass: 1, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '239853', - Parch: 0, - Cabin: null, - PassengerId: 414, - Age: null, - Fare: 0, - Name: 'Cunningham, Mr. Alfred Fleming', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101269', - Parch: 0, - Cabin: null, - PassengerId: 415, - Age: 44, - Fare: 7.925, - Name: 'Sundman, Mr. Johan Julian', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '343095', - Parch: 0, - Cabin: null, - PassengerId: 416, - Age: null, - Fare: 8.05, - Name: 'Meek, Mrs. Thomas (Annie Louise Rowley)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '28220', - Parch: 1, - Cabin: null, - PassengerId: 417, - Age: 34, - Fare: 32.5, - Name: 'Drew, Mrs. James Vivian (Lulu Thorne Christian)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '250652', - Parch: 2, - Cabin: null, - PassengerId: 418, - Age: 18, - Fare: 13, - Name: 'Silven, Miss. Lyyli Karoliina', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '28228', - Parch: 0, - Cabin: null, - PassengerId: 419, - Age: 30, - Fare: 13, - Name: 'Matthews, Mr. William John', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '345773', - Parch: 2, - Cabin: null, - PassengerId: 420, - Age: 10, - Fare: 24.15, - Name: 'Van Impe, Miss. Catharina', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349254', - Parch: 0, - Cabin: null, - PassengerId: 421, - Age: null, - Fare: 7.8958, - Name: 'Gheorgheff, Mr. Stanio', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/5. 13032', - Parch: 0, - Cabin: null, - PassengerId: 422, - Age: 21, - Fare: 7.7333, - Name: 'Charters, Mr. David', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '315082', - Parch: 0, - Cabin: null, - PassengerId: 423, - Age: 29, - Fare: 7.875, - Name: 'Zimmerman, Mr. Leo', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '347080', - Parch: 1, - Cabin: null, - PassengerId: 424, - Age: 28, - Fare: 14.4, - Name: 'Danbom, Mrs. Ernst Gilbert (Anna Sigrid Maria Brogren)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '370129', - Parch: 1, - Cabin: null, - PassengerId: 425, - Age: 18, - Fare: 20.2125, - Name: 'Rosblom, Mr. Viktor Richard', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/4. 34244', - Parch: 0, - Cabin: null, - PassengerId: 426, - Age: null, - Fare: 7.25, - Name: 'Wiseman, Mr. Phillippe', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2003', - Parch: 0, - Cabin: null, - PassengerId: 427, - Age: 28, - Fare: 26, - Name: 'Clarke, Mrs. Charles V (Ada Maria Winfield)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '250655', - Parch: 0, - Cabin: null, - PassengerId: 428, - Age: 19, - Fare: 26, - Name: 'Phillips, Miss. Kate Florence ("Mrs Kate Louise Phillips Marshall")', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '364851', - Parch: 0, - Cabin: null, - PassengerId: 429, - Age: null, - Fare: 7.75, - Name: 'Flynn, Mr. James', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/O.Q. 392078', - Parch: 0, - Cabin: 'E10', - PassengerId: 430, - Age: 32, - Fare: 8.05, - Name: 'Pickard, Mr. Berk (Berk Trembisky)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '110564', - Parch: 0, - Cabin: 'C52', - PassengerId: 431, - Age: 28, - Fare: 26.55, - Name: 'Bjornstrom-Steffansson, Mr. Mauritz Hakan', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '376564', - Parch: 0, - Cabin: null, - PassengerId: 432, - Age: null, - Fare: 16.1, - Name: 'Thorneycroft, Mrs. Percival (Florence Kate White)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'SC/AH 3085', - Parch: 0, - Cabin: null, - PassengerId: 433, - Age: 42, - Fare: 26, - Name: 'Louch, Mrs. Charles Alexander (Alice Adelaide Slow)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101274', - Parch: 0, - Cabin: null, - PassengerId: 434, - Age: 17, - Fare: 7.125, - Name: 'Kallio, Mr. Nikolai Erland', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '13507', - Parch: 0, - Cabin: 'E44', - PassengerId: 435, - Age: 50, - Fare: 55.9, - Name: 'Silvey, Mr. William Baird', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113760', - Parch: 2, - Cabin: 'B96 B98', - PassengerId: 436, - Age: 14, - Fare: 120, - Name: 'Carter, Miss. Lucile Polk', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: 'W./C. 6608', - Parch: 2, - Cabin: null, - PassengerId: 437, - Age: 21, - Fare: 34.375, - Name: 'Ford, Miss. Doolina Margaret "Daisy"', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: '29106', - Parch: 3, - Cabin: null, - PassengerId: 438, - Age: 24, - Fare: 18.75, - Name: 'Richards, Mrs. Sidney (Emily Hocking)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '19950', - Parch: 4, - Cabin: 'C23 C25 C27', - PassengerId: 439, - Age: 64, - Fare: 263, - Name: 'Fortune, Mr. Mark', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C.A. 18723', - Parch: 0, - Cabin: null, - PassengerId: 440, - Age: 31, - Fare: 10.5, - Name: 'Kvillner, Mr. Johan Henrik Johannesson', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'F.C.C. 13529', - Parch: 1, - Cabin: null, - PassengerId: 441, - Age: 45, - Fare: 26.25, - Name: 'Hart, Mrs. Benjamin (Esther Ada Bloomfield)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '345769', - Parch: 0, - Cabin: null, - PassengerId: 442, - Age: 20, - Fare: 9.5, - Name: 'Hampe, Mr. Leon', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '347076', - Parch: 0, - Cabin: null, - PassengerId: 443, - Age: 25, - Fare: 7.775, - Name: 'Petterson, Mr. Johan Emil', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '230434', - Parch: 0, - Cabin: null, - PassengerId: 444, - Age: 28, - Fare: 13, - Name: 'Reynaldo, Ms. Encarnacion', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '65306', - Parch: 0, - Cabin: null, - PassengerId: 445, - Age: null, - Fare: 8.1125, - Name: 'Johannesen-Bratthammer, Mr. Bernt', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '33638', - Parch: 2, - Cabin: 'A34', - PassengerId: 446, - Age: 4, - Fare: 81.8583, - Name: 'Dodge, Master. Washington', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '250644', - Parch: 1, - Cabin: null, - PassengerId: 447, - Age: 13, - Fare: 19.5, - Name: 'Mellinger, Miss. Madeleine Violet', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113794', - Parch: 0, - Cabin: null, - PassengerId: 448, - Age: 34, - Fare: 26.55, - Name: 'Seward, Mr. Frederic Kimber', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '2666', - Parch: 1, - Cabin: null, - PassengerId: 449, - Age: 5, - Fare: 19.2583, - Name: 'Baclini, Miss. Marie Catherine', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113786', - Parch: 0, - Cabin: 'C104', - PassengerId: 450, - Age: 52, - Fare: 30.5, - Name: 'Peuchen, Major. Arthur Godfrey', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 34651', - Parch: 2, - Cabin: null, - PassengerId: 451, - Age: 36, - Fare: 27.75, - Name: 'West, Mr. Edwy Arthur', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '65303', - Parch: 0, - Cabin: null, - PassengerId: 452, - Age: null, - Fare: 19.9667, - Name: 'Hagland, Mr. Ingvald Olai Olsen', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113051', - Parch: 0, - Cabin: 'C111', - PassengerId: 453, - Age: 30, - Fare: 27.75, - Name: 'Foreman, Mr. Benjamin Laventall', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '17453', - Parch: 0, - Cabin: 'C92', - PassengerId: 454, - Age: 49, - Fare: 89.1042, - Name: 'Goldenberg, Mr. Samuel L', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/5 2817', - Parch: 0, - Cabin: null, - PassengerId: 455, - Age: null, - Fare: 8.05, - Name: 'Peduzzi, Mr. Joseph', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349240', - Parch: 0, - Cabin: null, - PassengerId: 456, - Age: 29, - Fare: 7.8958, - Name: 'Jalsevac, Mr. Ivan', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '13509', - Parch: 0, - Cabin: 'E38', - PassengerId: 457, - Age: 65, - Fare: 26.55, - Name: 'Millet, Mr. Francis Davis', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '17464', - Parch: 0, - Cabin: 'D21', - PassengerId: 458, - Age: null, - Fare: 51.8625, - Name: 'Kenyon, Mrs. Frederick R (Marion)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'F.C.C. 13531', - Parch: 0, - Cabin: null, - PassengerId: 459, - Age: 50, - Fare: 10.5, - Name: 'Toomey, Miss. Ellen', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '371060', - Parch: 0, - Cabin: null, - PassengerId: 460, - Age: null, - Fare: 7.75, - Name: "O'Connor, Mr. Maurice", - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '19952', - Parch: 0, - Cabin: 'E12', - PassengerId: 461, - Age: 48, - Fare: 26.55, - Name: 'Anderson, Mr. Harry', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '364506', - Parch: 0, - Cabin: null, - PassengerId: 462, - Age: 34, - Fare: 8.05, - Name: 'Morley, Mr. William', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '111320', - Parch: 0, - Cabin: 'E63', - PassengerId: 463, - Age: 47, - Fare: 38.5, - Name: 'Gee, Mr. Arthur H', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '234360', - Parch: 0, - Cabin: null, - PassengerId: 464, - Age: 48, - Fare: 13, - Name: 'Milling, Mr. Jacob Christian', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/S 2816', - Parch: 0, - Cabin: null, - PassengerId: 465, - Age: null, - Fare: 8.05, - Name: 'Maisner, Mr. Simon', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/O.Q. 3101306', - Parch: 0, - Cabin: null, - PassengerId: 466, - Age: 38, - Fare: 7.05, - Name: 'Goncalves, Mr. Manuel Estanslas', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '239853', - Parch: 0, - Cabin: null, - PassengerId: 467, - Age: null, - Fare: 0, - Name: 'Campbell, Mr. William', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113792', - Parch: 0, - Cabin: null, - PassengerId: 468, - Age: 56, - Fare: 26.55, - Name: 'Smart, Mr. John Montgomery', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '36209', - Parch: 0, - Cabin: null, - PassengerId: 469, - Age: null, - Fare: 7.725, - Name: 'Scanlan, Mr. James', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '2666', - Parch: 1, - Cabin: null, - PassengerId: 470, - Age: 0.75, - Fare: 19.2583, - Name: 'Baclini, Miss. Helene Barbara', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '323592', - Parch: 0, - Cabin: null, - PassengerId: 471, - Age: null, - Fare: 7.25, - Name: 'Keefe, Mr. Arthur', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '315089', - Parch: 0, - Cabin: null, - PassengerId: 472, - Age: 38, - Fare: 8.6625, - Name: 'Cacic, Mr. Luka', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 34651', - Parch: 2, - Cabin: null, - PassengerId: 473, - Age: 33, - Fare: 27.75, - Name: 'West, Mrs. Edwy Arthur (Ada Mary Worth)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'SC/AH Basle 541', - Parch: 0, - Cabin: 'D', - PassengerId: 474, - Age: 23, - Fare: 13.7917, - Name: 'Jerwan, Mrs. Amin S (Marie Marthe Thuillard)', - Survived: true, - Pclass: 2, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '7553', - Parch: 0, - Cabin: null, - PassengerId: 475, - Age: 22, - Fare: 9.8375, - Name: 'Strandberg, Miss. Ida Sofia', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '110465', - Parch: 0, - Cabin: 'A14', - PassengerId: 476, - Age: null, - Fare: 52, - Name: 'Clifford, Mr. George Quincy', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '31027', - Parch: 0, - Cabin: null, - PassengerId: 477, - Age: 34, - Fare: 21, - Name: 'Renouf, Mr. Peter Henry', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '3460', - Parch: 0, - Cabin: null, - PassengerId: 478, - Age: 29, - Fare: 7.0458, - Name: 'Braund, Mr. Lewis Richard', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350060', - Parch: 0, - Cabin: null, - PassengerId: 479, - Age: 22, - Fare: 7.5208, - Name: 'Karlsson, Mr. Nils August', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '3101298', - Parch: 1, - Cabin: null, - PassengerId: 480, - Age: 2, - Fare: 12.2875, - Name: 'Hirvonen, Miss. Hildur E', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 5, - Ticket: 'CA 2144', - Parch: 2, - Cabin: null, - PassengerId: 481, - Age: 9, - Fare: 46.9, - Name: 'Goodwin, Master. Harold Victor', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '239854', - Parch: 0, - Cabin: null, - PassengerId: 482, - Age: null, - Fare: 0, - Name: 'Frost, Mr. Anthony Wood "Archie"', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/5 3594', - Parch: 0, - Cabin: null, - PassengerId: 483, - Age: 50, - Fare: 8.05, - Name: 'Rouse, Mr. Richard Henry', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '4134', - Parch: 0, - Cabin: null, - PassengerId: 484, - Age: 63, - Fare: 9.5875, - Name: 'Turkula, Mrs. (Hedwig)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '11967', - Parch: 0, - Cabin: 'B49', - PassengerId: 485, - Age: 25, - Fare: 91.0792, - Name: 'Bishop, Mr. Dickinson H', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '4133', - Parch: 1, - Cabin: null, - PassengerId: 486, - Age: null, - Fare: 25.4667, - Name: 'Lefebre, Miss. Jeannie', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '19943', - Parch: 0, - Cabin: 'C93', - PassengerId: 487, - Age: 35, - Fare: 90, - Name: 'Hoyt, Mrs. Frederick Maxfield (Jane Anne Forby)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '11771', - Parch: 0, - Cabin: 'B37', - PassengerId: 488, - Age: 58, - Fare: 29.7, - Name: 'Kent, Mr. Edward Austin', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A.5. 18509', - Parch: 0, - Cabin: null, - PassengerId: 489, - Age: 30, - Fare: 8.05, - Name: 'Somerton, Mr. Francis William', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 37671', - Parch: 1, - Cabin: null, - PassengerId: 490, - Age: 9, - Fare: 15.9, - Name: 'Coutts, Master. Eden Leslie "Neville"', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '65304', - Parch: 0, - Cabin: null, - PassengerId: 491, - Age: null, - Fare: 19.9667, - Name: 'Hagland, Mr. Konrad Mathias Reiersen', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/OQ 3101317', - Parch: 0, - Cabin: null, - PassengerId: 492, - Age: 21, - Fare: 7.25, - Name: 'Windelov, Mr. Einar', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113787', - Parch: 0, - Cabin: 'C30', - PassengerId: 493, - Age: 55, - Fare: 30.5, - Name: 'Molson, Mr. Harry Markland', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17609', - Parch: 0, - Cabin: null, - PassengerId: 494, - Age: 71, - Fare: 49.5042, - Name: 'Artagaveytia, Mr. Ramon', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/4 45380', - Parch: 0, - Cabin: null, - PassengerId: 495, - Age: 21, - Fare: 8.05, - Name: 'Stanley, Mr. Edward Roland', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2627', - Parch: 0, - Cabin: null, - PassengerId: 496, - Age: null, - Fare: 14.4583, - Name: 'Yousseff, Mr. Gerious', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '36947', - Parch: 0, - Cabin: 'D20', - PassengerId: 497, - Age: 54, - Fare: 78.2667, - Name: 'Eustis, Miss. Elizabeth Mussey', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'C.A. 6212', - Parch: 0, - Cabin: null, - PassengerId: 498, - Age: null, - Fare: 15.1, - Name: 'Shellard, Mr. Frederick William', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113781', - Parch: 2, - Cabin: 'C22 C26', - PassengerId: 499, - Age: 25, - Fare: 151.55, - Name: 'Allison, Mrs. Hudson J C (Bessie Waldo Daniels)', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '350035', - Parch: 0, - Cabin: null, - PassengerId: 500, - Age: 24, - Fare: 7.7958, - Name: 'Svensson, Mr. Olof', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '315086', - Parch: 0, - Cabin: null, - PassengerId: 501, - Age: 17, - Fare: 8.6625, - Name: 'Calic, Mr. Petar', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '364846', - Parch: 0, - Cabin: null, - PassengerId: 502, - Age: 21, - Fare: 7.75, - Name: 'Canavan, Miss. Mary', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '330909', - Parch: 0, - Cabin: null, - PassengerId: 503, - Age: null, - Fare: 7.6292, - Name: "O'Sullivan, Miss. Bridget Mary", - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '4135', - Parch: 0, - Cabin: null, - PassengerId: 504, - Age: 37, - Fare: 9.5875, - Name: 'Laitinen, Miss. Kristina Sofia', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '110152', - Parch: 0, - Cabin: 'B79', - PassengerId: 505, - Age: 16, - Fare: 86.5, - Name: 'Maioni, Miss. Roberta', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'PC 17758', - Parch: 0, - Cabin: 'C65', - PassengerId: 506, - Age: 18, - Fare: 108.9, - Name: 'Penasco y Castellana, Mr. Victor de Satode', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '26360', - Parch: 2, - Cabin: null, - PassengerId: 507, - Age: 33, - Fare: 26, - Name: 'Quick, Mrs. Frederick Charles (Jane Richards)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '111427', - Parch: 0, - Cabin: null, - PassengerId: 508, - Age: null, - Fare: 26.55, - Name: 'Bradley, Mr. George ("George Arthur Brayton")', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C 4001', - Parch: 0, - Cabin: null, - PassengerId: 509, - Age: 28, - Fare: 22.525, - Name: 'Olsen, Mr. Henry Margido', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '1601', - Parch: 0, - Cabin: null, - PassengerId: 510, - Age: 26, - Fare: 56.4958, - Name: 'Lang, Mr. Fang', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '382651', - Parch: 0, - Cabin: null, - PassengerId: 511, - Age: 29, - Fare: 7.75, - Name: 'Daly, Mr. Eugene Patrick', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/OQ 3101316', - Parch: 0, - Cabin: null, - PassengerId: 512, - Age: null, - Fare: 8.05, - Name: 'Webber, Mr. James', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17473', - Parch: 0, - Cabin: 'E25', - PassengerId: 513, - Age: 36, - Fare: 26.2875, - Name: 'McGough, Mr. James Robert', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'PC 17603', - Parch: 0, - Cabin: null, - PassengerId: 514, - Age: 54, - Fare: 59.4, - Name: 'Rothschild, Mrs. Martin (Elizabeth L. Barrett)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349209', - Parch: 0, - Cabin: null, - PassengerId: 515, - Age: 24, - Fare: 7.4958, - Name: 'Coleff, Mr. Satio', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '36967', - Parch: 0, - Cabin: 'D46', - PassengerId: 516, - Age: 47, - Fare: 34.0208, - Name: 'Walker, Mr. William Anderson', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C.A. 34260', - Parch: 0, - Cabin: 'F33', - PassengerId: 517, - Age: 34, - Fare: 10.5, - Name: 'Lemore, Mrs. (Amelia Milley)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '371110', - Parch: 0, - Cabin: null, - PassengerId: 518, - Age: null, - Fare: 24.15, - Name: 'Ryan, Mr. Patrick', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '226875', - Parch: 0, - Cabin: null, - PassengerId: 519, - Age: 36, - Fare: 26, - Name: 'Angle, Mrs. William A (Florence "Mary" Agnes Hughes)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349242', - Parch: 0, - Cabin: null, - PassengerId: 520, - Age: 32, - Fare: 7.8958, - Name: 'Pavlovic, Mr. Stefo', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '12749', - Parch: 0, - Cabin: 'B73', - PassengerId: 521, - Age: 30, - Fare: 93.5, - Name: 'Perreault, Miss. Anne', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349252', - Parch: 0, - Cabin: null, - PassengerId: 522, - Age: 22, - Fare: 7.8958, - Name: 'Vovk, Mr. Janko', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2624', - Parch: 0, - Cabin: null, - PassengerId: 523, - Age: null, - Fare: 7.225, - Name: 'Lahoud, Mr. Sarkis', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '111361', - Parch: 1, - Cabin: 'B18', - PassengerId: 524, - Age: 44, - Fare: 57.9792, - Name: 'Hippach, Mrs. Louis Albert (Ida Sophia Fischer)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2700', - Parch: 0, - Cabin: null, - PassengerId: 525, - Age: null, - Fare: 7.2292, - Name: 'Kassem, Mr. Fared', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '367232', - Parch: 0, - Cabin: null, - PassengerId: 526, - Age: 40.5, - Fare: 7.75, - Name: 'Farrell, Mr. James', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'W./C. 14258', - Parch: 0, - Cabin: null, - PassengerId: 527, - Age: 50, - Fare: 10.5, - Name: 'Ridsdale, Miss. Lucy', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17483', - Parch: 0, - Cabin: 'C95', - PassengerId: 528, - Age: null, - Fare: 221.7792, - Name: 'Farthing, Mr. John', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '3101296', - Parch: 0, - Cabin: null, - PassengerId: 529, - Age: 39, - Fare: 7.925, - Name: 'Salonen, Mr. Johan Werner', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '29104', - Parch: 1, - Cabin: null, - PassengerId: 530, - Age: 23, - Fare: 11.5, - Name: 'Hocking, Mr. Richard George', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '26360', - Parch: 1, - Cabin: null, - PassengerId: 531, - Age: 2, - Fare: 26, - Name: 'Quick, Miss. Phyllis May', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2641', - Parch: 0, - Cabin: null, - PassengerId: 532, - Age: null, - Fare: 7.2292, - Name: 'Toufik, Mr. Nakli', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2690', - Parch: 1, - Cabin: null, - PassengerId: 533, - Age: 17, - Fare: 7.2292, - Name: 'Elias, Mr. Joseph Jr', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2668', - Parch: 2, - Cabin: null, - PassengerId: 534, - Age: null, - Fare: 22.3583, - Name: 'Peter, Mrs. Catherine (Catherine Rizk)', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '315084', - Parch: 0, - Cabin: null, - PassengerId: 535, - Age: 30, - Fare: 8.6625, - Name: 'Cacic, Miss. Marija', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'F.C.C. 13529', - Parch: 2, - Cabin: null, - PassengerId: 536, - Age: 7, - Fare: 26.25, - Name: 'Hart, Miss. Eva Miriam', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113050', - Parch: 0, - Cabin: 'B38', - PassengerId: 537, - Age: 45, - Fare: 26.55, - Name: 'Butt, Major. Archibald Willingham', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17761', - Parch: 0, - Cabin: null, - PassengerId: 538, - Age: 30, - Fare: 106.425, - Name: 'LeRoy, Miss. Bertha', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '364498', - Parch: 0, - Cabin: null, - PassengerId: 539, - Age: null, - Fare: 14.5, - Name: 'Risien, Mr. Samuel Beard', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '13568', - Parch: 2, - Cabin: 'B39', - PassengerId: 540, - Age: 22, - Fare: 49.5, - Name: 'Frolicher, Miss. Hedwig Margaritha', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'WE/P 5735', - Parch: 2, - Cabin: 'B22', - PassengerId: 541, - Age: 36, - Fare: 71, - Name: 'Crosby, Miss. Harriet R', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 4, - Ticket: '347082', - Parch: 2, - Cabin: null, - PassengerId: 542, - Age: 9, - Fare: 31.275, - Name: 'Andersson, Miss. Ingeborg Constanzia', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 4, - Ticket: '347082', - Parch: 2, - Cabin: null, - PassengerId: 543, - Age: 11, - Fare: 31.275, - Name: 'Andersson, Miss. Sigrid Elisabeth', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '2908', - Parch: 0, - Cabin: null, - PassengerId: 544, - Age: 32, - Fare: 26, - Name: 'Beane, Mr. Edward', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'PC 17761', - Parch: 0, - Cabin: 'C86', - PassengerId: 545, - Age: 50, - Fare: 106.425, - Name: 'Douglas, Mr. Walter Donald', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '693', - Parch: 0, - Cabin: null, - PassengerId: 546, - Age: 64, - Fare: 26, - Name: 'Nicholson, Mr. Arthur Ernest', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2908', - Parch: 0, - Cabin: null, - PassengerId: 547, - Age: 19, - Fare: 26, - Name: 'Beane, Mrs. Edward (Ethel Clarke)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'SC/PARIS 2146', - Parch: 0, - Cabin: null, - PassengerId: 548, - Age: null, - Fare: 13.8625, - Name: 'Padro y Manent, Mr. Julian', - Survived: true, - Pclass: 2, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '363291', - Parch: 1, - Cabin: null, - PassengerId: 549, - Age: 33, - Fare: 20.525, - Name: 'Goldsmith, Mr. Frank John', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 33112', - Parch: 1, - Cabin: null, - PassengerId: 550, - Age: 8, - Fare: 36.75, - Name: 'Davies, Master. John Morgan Jr', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '17421', - Parch: 2, - Cabin: 'C70', - PassengerId: 551, - Age: 17, - Fare: 110.8833, - Name: 'Thayer, Mr. John Borland Jr', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '244358', - Parch: 0, - Cabin: null, - PassengerId: 552, - Age: 27, - Fare: 26, - Name: 'Sharp, Mr. Percival James R', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '330979', - Parch: 0, - Cabin: null, - PassengerId: 553, - Age: null, - Fare: 7.8292, - Name: "O'Brien, Mr. Timothy", - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2620', - Parch: 0, - Cabin: null, - PassengerId: 554, - Age: 22, - Fare: 7.225, - Name: 'Leeni, Mr. Fahim ("Philip Zenni")', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347085', - Parch: 0, - Cabin: null, - PassengerId: 555, - Age: 22, - Fare: 7.775, - Name: 'Ohman, Miss. Velin', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113807', - Parch: 0, - Cabin: null, - PassengerId: 556, - Age: 62, - Fare: 26.55, - Name: 'Wright, Mr. George', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '11755', - Parch: 0, - Cabin: 'A16', - PassengerId: 557, - Age: 48, - Fare: 39.6, - Name: 'Duff Gordon, Lady. (Lucille Christiana Sutherland) ("Mrs Morgan")', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17757', - Parch: 0, - Cabin: null, - PassengerId: 558, - Age: null, - Fare: 227.525, - Name: 'Robbins, Mr. Victor', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '110413', - Parch: 1, - Cabin: 'E67', - PassengerId: 559, - Age: 39, - Fare: 79.65, - Name: 'Taussig, Mrs. Emil (Tillie Mandelbaum)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '345572', - Parch: 0, - Cabin: null, - PassengerId: 560, - Age: 36, - Fare: 17.4, - Name: 'de Messemaeker, Mrs. Guillaume Joseph (Emma)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '372622', - Parch: 0, - Cabin: null, - PassengerId: 561, - Age: null, - Fare: 7.75, - Name: 'Morrow, Mr. Thomas Rowan', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349251', - Parch: 0, - Cabin: null, - PassengerId: 562, - Age: 40, - Fare: 7.8958, - Name: 'Sivic, Mr. Husein', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '218629', - Parch: 0, - Cabin: null, - PassengerId: 563, - Age: 28, - Fare: 13.5, - Name: 'Norman, Mr. Robert Douglas', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/OQ 392082', - Parch: 0, - Cabin: null, - PassengerId: 564, - Age: null, - Fare: 8.05, - Name: 'Simmons, Mr. John', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/O.Q. 392087', - Parch: 0, - Cabin: null, - PassengerId: 565, - Age: null, - Fare: 8.05, - Name: 'Meanwell, Miss. (Marion Ogden)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: 'A/4 48871', - Parch: 0, - Cabin: null, - PassengerId: 566, - Age: 24, - Fare: 24.15, - Name: 'Davies, Mr. Alfred J', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349205', - Parch: 0, - Cabin: null, - PassengerId: 567, - Age: 19, - Fare: 7.8958, - Name: 'Stoytcheff, Mr. Ilia', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349909', - Parch: 4, - Cabin: null, - PassengerId: 568, - Age: 29, - Fare: 21.075, - Name: 'Palsson, Mrs. Nils (Alma Cornelia Berglund)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2686', - Parch: 0, - Cabin: null, - PassengerId: 569, - Age: null, - Fare: 7.2292, - Name: 'Doharr, Mr. Tannous', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350417', - Parch: 0, - Cabin: null, - PassengerId: 570, - Age: 32, - Fare: 7.8542, - Name: 'Jonsson, Mr. Carl', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'S.W./PP 752', - Parch: 0, - Cabin: null, - PassengerId: 571, - Age: 62, - Fare: 10.5, - Name: 'Harris, Mr. George', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '11769', - Parch: 0, - Cabin: 'C101', - PassengerId: 572, - Age: 53, - Fare: 51.4792, - Name: 'Appleton, Mrs. Edward Dale (Charlotte Lamson)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17474', - Parch: 0, - Cabin: 'E25', - PassengerId: 573, - Age: 36, - Fare: 26.3875, - Name: 'Flynn, Mr. John Irwin ("Irving")', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '14312', - Parch: 0, - Cabin: null, - PassengerId: 574, - Age: null, - Fare: 7.75, - Name: 'Kelly, Miss. Mary', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'A/4. 20589', - Parch: 0, - Cabin: null, - PassengerId: 575, - Age: 16, - Fare: 8.05, - Name: 'Rush, Mr. Alfred George John', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '358585', - Parch: 0, - Cabin: null, - PassengerId: 576, - Age: 19, - Fare: 14.5, - Name: 'Patchett, Mr. George', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '243880', - Parch: 0, - Cabin: null, - PassengerId: 577, - Age: 34, - Fare: 13, - Name: 'Garside, Miss. Ethel', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '13507', - Parch: 0, - Cabin: 'E44', - PassengerId: 578, - Age: 39, - Fare: 55.9, - Name: 'Silvey, Mrs. William Baird (Alice Munger)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '2689', - Parch: 0, - Cabin: null, - PassengerId: 579, - Age: null, - Fare: 14.4583, - Name: 'Caram, Mrs. Joseph (Maria Elias)', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101286', - Parch: 0, - Cabin: null, - PassengerId: 580, - Age: 32, - Fare: 7.925, - Name: 'Jussila, Mr. Eiriik', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '237789', - Parch: 1, - Cabin: null, - PassengerId: 581, - Age: 25, - Fare: 30, - Name: 'Christy, Miss. Julie Rachel', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '17421', - Parch: 1, - Cabin: 'C68', - PassengerId: 582, - Age: 39, - Fare: 110.8833, - Name: 'Thayer, Mrs. John Borland (Marian Longstreth Morris)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '28403', - Parch: 0, - Cabin: null, - PassengerId: 583, - Age: 54, - Fare: 26, - Name: 'Downton, Mr. William James', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '13049', - Parch: 0, - Cabin: 'A10', - PassengerId: 584, - Age: 36, - Fare: 40.125, - Name: 'Ross, Mr. John Hugo', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '3411', - Parch: 0, - Cabin: null, - PassengerId: 585, - Age: null, - Fare: 8.7125, - Name: 'Paulner, Mr. Uscher', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '110413', - Parch: 2, - Cabin: 'E68', - PassengerId: 586, - Age: 18, - Fare: 79.65, - Name: 'Taussig, Miss. Ruth', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '237565', - Parch: 0, - Cabin: null, - PassengerId: 587, - Age: 47, - Fare: 15, - Name: 'Jarvis, Mr. John Denzil', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '13567', - Parch: 1, - Cabin: 'B41', - PassengerId: 588, - Age: 60, - Fare: 79.2, - Name: 'Frolicher-Stehli, Mr. Maxmillian', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '14973', - Parch: 0, - Cabin: null, - PassengerId: 589, - Age: 22, - Fare: 8.05, - Name: 'Gilinski, Mr. Eliezer', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A./5. 3235', - Parch: 0, - Cabin: null, - PassengerId: 590, - Age: null, - Fare: 8.05, - Name: 'Murdlin, Mr. Joseph', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101273', - Parch: 0, - Cabin: null, - PassengerId: 591, - Age: 35, - Fare: 7.125, - Name: 'Rintamaki, Mr. Matti', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '36947', - Parch: 0, - Cabin: 'D20', - PassengerId: 592, - Age: 52, - Fare: 78.2667, - Name: 'Stephenson, Mrs. Walter Bertram (Martha Eustis)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'A/5 3902', - Parch: 0, - Cabin: null, - PassengerId: 593, - Age: 47, - Fare: 7.25, - Name: 'Elsbury, Mr. William James', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '364848', - Parch: 2, - Cabin: null, - PassengerId: 594, - Age: null, - Fare: 7.75, - Name: 'Bourke, Miss. Mary', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'SC/AH 29037', - Parch: 0, - Cabin: null, - PassengerId: 595, - Age: 37, - Fare: 26, - Name: 'Chapman, Mr. John Henry', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '345773', - Parch: 1, - Cabin: null, - PassengerId: 596, - Age: 36, - Fare: 24.15, - Name: 'Van Impe, Mr. Jean Baptiste', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '248727', - Parch: 0, - Cabin: null, - PassengerId: 597, - Age: null, - Fare: 33, - Name: 'Leitch, Miss. Jessie Wills', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'LINE', - Parch: 0, - Cabin: null, - PassengerId: 598, - Age: 49, - Fare: 0, - Name: 'Johnson, Mr. Alfred', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2664', - Parch: 0, - Cabin: null, - PassengerId: 599, - Age: null, - Fare: 7.225, - Name: 'Boulos, Mr. Hanna', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'PC 17485', - Parch: 0, - Cabin: 'A20', - PassengerId: 600, - Age: 49, - Fare: 56.9292, - Name: 'Duff Gordon, Sir. Cosmo Edmund ("Mr Morgan")', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '243847', - Parch: 1, - Cabin: null, - PassengerId: 601, - Age: 24, - Fare: 27, - Name: 'Jacobsohn, Mrs. Sidney Samuel (Amy Frances Christy)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349214', - Parch: 0, - Cabin: null, - PassengerId: 602, - Age: null, - Fare: 7.8958, - Name: 'Slabenoff, Mr. Petco', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113796', - Parch: 0, - Cabin: null, - PassengerId: 603, - Age: null, - Fare: 42.4, - Name: 'Harrington, Mr. Charles H', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '364511', - Parch: 0, - Cabin: null, - PassengerId: 604, - Age: 44, - Fare: 8.05, - Name: 'Torber, Mr. Ernst William', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '111426', - Parch: 0, - Cabin: null, - PassengerId: 605, - Age: 35, - Fare: 26.55, - Name: 'Homer, Mr. Harry ("Mr E Haven")', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '349910', - Parch: 0, - Cabin: null, - PassengerId: 606, - Age: 36, - Fare: 15.55, - Name: 'Lindell, Mr. Edvard Bengtsson', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349246', - Parch: 0, - Cabin: null, - PassengerId: 607, - Age: 30, - Fare: 7.8958, - Name: 'Karaic, Mr. Milan', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113804', - Parch: 0, - Cabin: null, - PassengerId: 608, - Age: 27, - Fare: 30.5, - Name: 'Daniel, Mr. Robert Williams', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'SC/Paris 2123', - Parch: 2, - Cabin: null, - PassengerId: 609, - Age: 22, - Fare: 41.5792, - Name: 'Laroche, Mrs. Joseph (Juliette Marie Louise Lafargue)', - Survived: true, - Pclass: 2, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17582', - Parch: 0, - Cabin: 'C125', - PassengerId: 610, - Age: 40, - Fare: 153.4625, - Name: 'Shutes, Miss. Elizabeth W', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '347082', - Parch: 5, - Cabin: null, - PassengerId: 611, - Age: 39, - Fare: 31.275, - Name: 'Andersson, Mrs. Anders Johan (Alfrida Konstantia Brogren)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'SOTON/O.Q. 3101305', - Parch: 0, - Cabin: null, - PassengerId: 612, - Age: null, - Fare: 7.05, - Name: 'Jardin, Mr. Jose Neto', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '367230', - Parch: 0, - Cabin: null, - PassengerId: 613, - Age: null, - Fare: 15.5, - Name: 'Murphy, Miss. Margaret Jane', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '370377', - Parch: 0, - Cabin: null, - PassengerId: 614, - Age: null, - Fare: 7.75, - Name: 'Horgan, Mr. John', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '364512', - Parch: 0, - Cabin: null, - PassengerId: 615, - Age: 35, - Fare: 8.05, - Name: 'Brocklebank, Mr. William Alfred', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '220845', - Parch: 2, - Cabin: null, - PassengerId: 616, - Age: 24, - Fare: 65, - Name: 'Herman, Miss. Alice', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '347080', - Parch: 1, - Cabin: null, - PassengerId: 617, - Age: 34, - Fare: 14.4, - Name: 'Danbom, Mr. Ernst Gilbert', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'A/5. 3336', - Parch: 0, - Cabin: null, - PassengerId: 618, - Age: 26, - Fare: 16.1, - Name: 'Lobb, Mrs. William Arthur (Cordelia K Stanlick)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: '230136', - Parch: 1, - Cabin: 'F4', - PassengerId: 619, - Age: 4, - Fare: 39, - Name: 'Becker, Miss. Marion Louise', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '31028', - Parch: 0, - Cabin: null, - PassengerId: 620, - Age: 26, - Fare: 10.5, - Name: 'Gavey, Mr. Lawrence', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2659', - Parch: 0, - Cabin: null, - PassengerId: 621, - Age: 27, - Fare: 14.4542, - Name: 'Yasbeck, Mr. Antoni', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '11753', - Parch: 0, - Cabin: 'D19', - PassengerId: 622, - Age: 42, - Fare: 52.5542, - Name: 'Kimball, Mr. Edwin Nelson Jr', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2653', - Parch: 1, - Cabin: null, - PassengerId: 623, - Age: 20, - Fare: 15.7417, - Name: 'Nakid, Mr. Sahid', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350029', - Parch: 0, - Cabin: null, - PassengerId: 624, - Age: 21, - Fare: 7.8542, - Name: 'Hansen, Mr. Henry Damsgaard', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '54636', - Parch: 0, - Cabin: null, - PassengerId: 625, - Age: 21, - Fare: 16.1, - Name: 'Bowen, Mr. David John "Dai"', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '36963', - Parch: 0, - Cabin: 'D50', - PassengerId: 626, - Age: 61, - Fare: 32.3208, - Name: 'Sutton, Mr. Frederick', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '219533', - Parch: 0, - Cabin: null, - PassengerId: 627, - Age: 57, - Fare: 12.35, - Name: 'Kirkland, Rev. Charles Leonard', - Survived: false, - Pclass: 2, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '13502', - Parch: 0, - Cabin: 'D9', - PassengerId: 628, - Age: 21, - Fare: 77.9583, - Name: 'Longley, Miss. Gretchen Fiske', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349224', - Parch: 0, - Cabin: null, - PassengerId: 629, - Age: 26, - Fare: 7.8958, - Name: 'Bostandyeff, Mr. Guentcho', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '334912', - Parch: 0, - Cabin: null, - PassengerId: 630, - Age: null, - Fare: 7.7333, - Name: "O'Connell, Mr. Patrick D", - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '27042', - Parch: 0, - Cabin: 'A23', - PassengerId: 631, - Age: 80, - Fare: 30, - Name: 'Barkworth, Mr. Algernon Henry Wilson', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347743', - Parch: 0, - Cabin: null, - PassengerId: 632, - Age: 51, - Fare: 7.0542, - Name: 'Lundahl, Mr. Johan Svensson', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '13214', - Parch: 0, - Cabin: 'B50', - PassengerId: 633, - Age: 32, - Fare: 30.5, - Name: 'Stahelin-Maeglin, Dr. Max', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '112052', - Parch: 0, - Cabin: null, - PassengerId: 634, - Age: null, - Fare: 0, - Name: 'Parr, Mr. William Henry Marsh', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '347088', - Parch: 2, - Cabin: null, - PassengerId: 635, - Age: 9, - Fare: 27.9, - Name: 'Skoog, Miss. Mabel', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '237668', - Parch: 0, - Cabin: null, - PassengerId: 636, - Age: 28, - Fare: 13, - Name: 'Davis, Miss. Mary', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101292', - Parch: 0, - Cabin: null, - PassengerId: 637, - Age: 32, - Fare: 7.925, - Name: 'Leinonen, Mr. Antti Gustaf', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 31921', - Parch: 1, - Cabin: null, - PassengerId: 638, - Age: 31, - Fare: 26.25, - Name: 'Collyer, Mr. Harvey', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '3101295', - Parch: 5, - Cabin: null, - PassengerId: 639, - Age: 41, - Fare: 39.6875, - Name: 'Panula, Mrs. Juha (Maria Emilia Ojala)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '376564', - Parch: 0, - Cabin: null, - PassengerId: 640, - Age: null, - Fare: 16.1, - Name: 'Thorneycroft, Mr. Percival', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350050', - Parch: 0, - Cabin: null, - PassengerId: 641, - Age: 20, - Fare: 7.8542, - Name: 'Jensen, Mr. Hans Peder', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17477', - Parch: 0, - Cabin: 'B35', - PassengerId: 642, - Age: 24, - Fare: 69.3, - Name: 'Sagesser, Mlle. Emma', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 3, - Ticket: '347088', - Parch: 2, - Cabin: null, - PassengerId: 643, - Age: 2, - Fare: 27.9, - Name: 'Skoog, Miss. Margit Elizabeth', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '1601', - Parch: 0, - Cabin: null, - PassengerId: 644, - Age: null, - Fare: 56.4958, - Name: 'Foo, Mr. Choong', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '2666', - Parch: 1, - Cabin: null, - PassengerId: 645, - Age: 0.75, - Fare: 19.2583, - Name: 'Baclini, Miss. Eugenie', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'PC 17572', - Parch: 0, - Cabin: 'D33', - PassengerId: 646, - Age: 48, - Fare: 76.7292, - Name: 'Harper, Mr. Henry Sleeper', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349231', - Parch: 0, - Cabin: null, - PassengerId: 647, - Age: 19, - Fare: 7.8958, - Name: 'Cor, Mr. Liudevit', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '13213', - Parch: 0, - Cabin: 'A26', - PassengerId: 648, - Age: 56, - Fare: 35.5, - Name: 'Simonius-Blumer, Col. Oberst Alfons', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'S.O./P.P. 751', - Parch: 0, - Cabin: null, - PassengerId: 649, - Age: null, - Fare: 7.55, - Name: 'Willey, Mr. Edward', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'CA. 2314', - Parch: 0, - Cabin: null, - PassengerId: 650, - Age: 23, - Fare: 7.55, - Name: 'Stanley, Miss. Amy Zillah Elsie', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349221', - Parch: 0, - Cabin: null, - PassengerId: 651, - Age: null, - Fare: 7.8958, - Name: 'Mitkoff, Mr. Mito', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '231919', - Parch: 1, - Cabin: null, - PassengerId: 652, - Age: 18, - Fare: 23, - Name: 'Doling, Miss. Elsie', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '8475', - Parch: 0, - Cabin: null, - PassengerId: 653, - Age: 21, - Fare: 8.4333, - Name: 'Kalvik, Mr. Johannes Halvorsen', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '330919', - Parch: 0, - Cabin: null, - PassengerId: 654, - Age: null, - Fare: 7.8292, - Name: 'O\'Leary, Miss. Hanora "Norah"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '365226', - Parch: 0, - Cabin: null, - PassengerId: 655, - Age: 18, - Fare: 6.75, - Name: 'Hegarty, Miss. Hanora "Nora"', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 2, - Ticket: 'S.O.C. 14879', - Parch: 0, - Cabin: null, - PassengerId: 656, - Age: 24, - Fare: 73.5, - Name: 'Hickman, Mr. Leonard Mark', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349223', - Parch: 0, - Cabin: null, - PassengerId: 657, - Age: null, - Fare: 7.8958, - Name: 'Radeff, Mr. Alexander', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '364849', - Parch: 1, - Cabin: null, - PassengerId: 658, - Age: 32, - Fare: 15.5, - Name: 'Bourke, Mrs. John (Catherine)', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '29751', - Parch: 0, - Cabin: null, - PassengerId: 659, - Age: 23, - Fare: 13, - Name: 'Eitemiller, Mr. George Floyd', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '35273', - Parch: 2, - Cabin: 'D48', - PassengerId: 660, - Age: 58, - Fare: 113.275, - Name: 'Newell, Mr. Arthur Webster', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: 'PC 17611', - Parch: 0, - Cabin: null, - PassengerId: 661, - Age: 50, - Fare: 133.65, - Name: 'Frauenthal, Dr. Henry William', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2623', - Parch: 0, - Cabin: null, - PassengerId: 662, - Age: 40, - Fare: 7.225, - Name: 'Badt, Mr. Mohamed', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '5727', - Parch: 0, - Cabin: 'E58', - PassengerId: 663, - Age: 47, - Fare: 25.5875, - Name: 'Colley, Mr. Edward Pomeroy', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349210', - Parch: 0, - Cabin: null, - PassengerId: 664, - Age: 36, - Fare: 7.4958, - Name: 'Coleff, Mr. Peju', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'STON/O 2. 3101285', - Parch: 0, - Cabin: null, - PassengerId: 665, - Age: 20, - Fare: 7.925, - Name: 'Lindqvist, Mr. Eino William', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: 'S.O.C. 14879', - Parch: 0, - Cabin: null, - PassengerId: 666, - Age: 32, - Fare: 73.5, - Name: 'Hickman, Mr. Lewis', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '234686', - Parch: 0, - Cabin: null, - PassengerId: 667, - Age: 25, - Fare: 13, - Name: 'Butler, Mr. Reginald Fenton', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '312993', - Parch: 0, - Cabin: null, - PassengerId: 668, - Age: null, - Fare: 7.775, - Name: 'Rommetvedt, Mr. Knud Paust', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/5 3536', - Parch: 0, - Cabin: null, - PassengerId: 669, - Age: 43, - Fare: 8.05, - Name: 'Cook, Mr. Jacob', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '19996', - Parch: 0, - Cabin: 'C126', - PassengerId: 670, - Age: null, - Fare: 52, - Name: 'Taylor, Mrs. Elmer Zebley (Juliet Cummins Wright)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '29750', - Parch: 1, - Cabin: null, - PassengerId: 671, - Age: 40, - Fare: 39, - Name: 'Brown, Mrs. Thomas William Solomon (Elizabeth Catherine Ford)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'F.C. 12750', - Parch: 0, - Cabin: 'B71', - PassengerId: 672, - Age: 31, - Fare: 52, - Name: 'Davidson, Mr. Thornton', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C.A. 24580', - Parch: 0, - Cabin: null, - PassengerId: 673, - Age: 70, - Fare: 10.5, - Name: 'Mitchell, Mr. Henry Michael', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '244270', - Parch: 0, - Cabin: null, - PassengerId: 674, - Age: 31, - Fare: 13, - Name: 'Wilhelms, Mr. Charles', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '239856', - Parch: 0, - Cabin: null, - PassengerId: 675, - Age: null, - Fare: 0, - Name: 'Watson, Mr. Ennis Hastings', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349912', - Parch: 0, - Cabin: null, - PassengerId: 676, - Age: 18, - Fare: 7.775, - Name: 'Edvardsson, Mr. Gustaf Hjalmar', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '342826', - Parch: 0, - Cabin: null, - PassengerId: 677, - Age: 24.5, - Fare: 8.05, - Name: 'Sawyer, Mr. Frederick Charles', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '4138', - Parch: 0, - Cabin: null, - PassengerId: 678, - Age: 18, - Fare: 9.8417, - Name: 'Turja, Miss. Anna Sofia', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'CA 2144', - Parch: 6, - Cabin: null, - PassengerId: 679, - Age: 43, - Fare: 46.9, - Name: 'Goodwin, Mrs. Frederick (Augusta Tyler)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17755', - Parch: 1, - Cabin: 'B51 B53 B55', - PassengerId: 680, - Age: 36, - Fare: 512.3292, - Name: 'Cardeza, Mr. Thomas Drake Martinez', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '330935', - Parch: 0, - Cabin: null, - PassengerId: 681, - Age: null, - Fare: 8.1375, - Name: 'Peters, Miss. Katie', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17572', - Parch: 0, - Cabin: 'D49', - PassengerId: 682, - Age: 27, - Fare: 76.7292, - Name: 'Hassab, Mr. Hammad', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '6563', - Parch: 0, - Cabin: null, - PassengerId: 683, - Age: 20, - Fare: 9.225, - Name: 'Olsvigen, Mr. Thor Anderson', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 5, - Ticket: 'CA 2144', - Parch: 2, - Cabin: null, - PassengerId: 684, - Age: 14, - Fare: 46.9, - Name: 'Goodwin, Mr. Charles Edward', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '29750', - Parch: 1, - Cabin: null, - PassengerId: 685, - Age: 60, - Fare: 39, - Name: 'Brown, Mr. Thomas William Solomon', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'SC/Paris 2123', - Parch: 2, - Cabin: null, - PassengerId: 686, - Age: 25, - Fare: 41.5792, - Name: 'Laroche, Mr. Joseph Philippe Lemercier', - Survived: false, - Pclass: 2, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '3101295', - Parch: 1, - Cabin: null, - PassengerId: 687, - Age: 14, - Fare: 39.6875, - Name: 'Panula, Mr. Jaako Arnold', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349228', - Parch: 0, - Cabin: null, - PassengerId: 688, - Age: 19, - Fare: 10.1708, - Name: 'Dakic, Mr. Branko', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350036', - Parch: 0, - Cabin: null, - PassengerId: 689, - Age: 18, - Fare: 7.7958, - Name: 'Fischer, Mr. Eberhard Thelander', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '24160', - Parch: 1, - Cabin: 'B5', - PassengerId: 690, - Age: 15, - Fare: 211.3375, - Name: 'Madill, Miss. Georgette Alexandra', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '17474', - Parch: 0, - Cabin: 'B20', - PassengerId: 691, - Age: 31, - Fare: 57, - Name: 'Dick, Mr. Albert Adrian', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349256', - Parch: 1, - Cabin: null, - PassengerId: 692, - Age: 4, - Fare: 13.4167, - Name: 'Karun, Miss. Manca', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '1601', - Parch: 0, - Cabin: null, - PassengerId: 693, - Age: null, - Fare: 56.4958, - Name: 'Lam, Mr. Ali', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2672', - Parch: 0, - Cabin: null, - PassengerId: 694, - Age: 25, - Fare: 7.225, - Name: 'Saad, Mr. Khalil', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113800', - Parch: 0, - Cabin: null, - PassengerId: 695, - Age: 60, - Fare: 26.55, - Name: 'Weir, Col. John', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '248731', - Parch: 0, - Cabin: null, - PassengerId: 696, - Age: 52, - Fare: 13.5, - Name: 'Chapman, Mr. Charles Henry', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '363592', - Parch: 0, - Cabin: null, - PassengerId: 697, - Age: 44, - Fare: 8.05, - Name: 'Kelly, Mr. James', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '35852', - Parch: 0, - Cabin: null, - PassengerId: 698, - Age: null, - Fare: 7.7333, - Name: 'Mullens, Miss. Katherine "Katie"', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '17421', - Parch: 1, - Cabin: 'C68', - PassengerId: 699, - Age: 49, - Fare: 110.8833, - Name: 'Thayer, Mr. John Borland', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '348121', - Parch: 0, - Cabin: 'F G63', - PassengerId: 700, - Age: 42, - Fare: 7.65, - Name: 'Humblen, Mr. Adolf Mathias Nicolai Olsen', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'PC 17757', - Parch: 0, - Cabin: 'C62 C64', - PassengerId: 701, - Age: 18, - Fare: 227.525, - Name: 'Astor, Mrs. John Jacob (Madeleine Talmadge Force)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17475', - Parch: 0, - Cabin: 'E24', - PassengerId: 702, - Age: 35, - Fare: 26.2875, - Name: 'Silverthorne, Mr. Spencer Victor', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2691', - Parch: 1, - Cabin: null, - PassengerId: 703, - Age: 18, - Fare: 14.4542, - Name: 'Barbara, Miss. Saiide', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '36864', - Parch: 0, - Cabin: null, - PassengerId: 704, - Age: 25, - Fare: 7.7417, - Name: 'Gallagher, Mr. Martin', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '350025', - Parch: 0, - Cabin: null, - PassengerId: 705, - Age: 26, - Fare: 7.8542, - Name: 'Hansen, Mr. Henrik Juul', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '250655', - Parch: 0, - Cabin: null, - PassengerId: 706, - Age: 39, - Fare: 26, - Name: 'Morley, Mr. Henry Samuel ("Mr Henry Marshall")', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '223596', - Parch: 0, - Cabin: null, - PassengerId: 707, - Age: 45, - Fare: 13.5, - Name: 'Kelly, Mrs. Florence "Fannie"', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17476', - Parch: 0, - Cabin: 'E24', - PassengerId: 708, - Age: 42, - Fare: 26.2875, - Name: 'Calderhead, Mr. Edward Pennington', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113781', - Parch: 0, - Cabin: null, - PassengerId: 709, - Age: 22, - Fare: 151.55, - Name: 'Cleaver, Miss. Alice', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '2661', - Parch: 1, - Cabin: null, - PassengerId: 710, - Age: null, - Fare: 15.2458, - Name: 'Moubarek, Master. Halim Gonios ("William George")', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17482', - Parch: 0, - Cabin: 'C90', - PassengerId: 711, - Age: 24, - Fare: 49.5042, - Name: 'Mayne, Mlle. Berthe Antonine ("Mrs de Villiers")', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113028', - Parch: 0, - Cabin: 'C124', - PassengerId: 712, - Age: null, - Fare: 26.55, - Name: 'Klaber, Mr. Herman', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '19996', - Parch: 0, - Cabin: 'C126', - PassengerId: 713, - Age: 48, - Fare: 52, - Name: 'Taylor, Mr. Elmer Zebley', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '7545', - Parch: 0, - Cabin: null, - PassengerId: 714, - Age: 29, - Fare: 9.4833, - Name: 'Larsson, Mr. August Viktor', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '250647', - Parch: 0, - Cabin: null, - PassengerId: 715, - Age: 52, - Fare: 13, - Name: 'Greenberg, Mr. Samuel', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '348124', - Parch: 0, - Cabin: 'F G73', - PassengerId: 716, - Age: 19, - Fare: 7.65, - Name: 'Soholt, Mr. Peter Andreas Lauritz Andersen', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17757', - Parch: 0, - Cabin: 'C45', - PassengerId: 717, - Age: 38, - Fare: 227.525, - Name: 'Endres, Miss. Caroline Louise', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '34218', - Parch: 0, - Cabin: 'E101', - PassengerId: 718, - Age: 27, - Fare: 10.5, - Name: 'Troutt, Miss. Edwina Celia "Winnie"', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '36568', - Parch: 0, - Cabin: null, - PassengerId: 719, - Age: null, - Fare: 15.5, - Name: 'McEvoy, Mr. Michael', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347062', - Parch: 0, - Cabin: null, - PassengerId: 720, - Age: 33, - Fare: 7.775, - Name: 'Johnson, Mr. Malkolm Joackim', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '248727', - Parch: 1, - Cabin: null, - PassengerId: 721, - Age: 6, - Fare: 33, - Name: 'Harper, Miss. Annie Jessie "Nina"', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '350048', - Parch: 0, - Cabin: null, - PassengerId: 722, - Age: 17, - Fare: 7.0542, - Name: 'Jensen, Mr. Svend Lauritz', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '12233', - Parch: 0, - Cabin: null, - PassengerId: 723, - Age: 34, - Fare: 13, - Name: 'Gillespie, Mr. William Henry', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '250643', - Parch: 0, - Cabin: null, - PassengerId: 724, - Age: 50, - Fare: 13, - Name: 'Hodges, Mr. Henry Price', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113806', - Parch: 0, - Cabin: 'E8', - PassengerId: 725, - Age: 27, - Fare: 53.1, - Name: 'Chambers, Mr. Norman Campbell', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '315094', - Parch: 0, - Cabin: null, - PassengerId: 726, - Age: 20, - Fare: 8.6625, - Name: 'Oreskovic, Mr. Luka', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '31027', - Parch: 0, - Cabin: null, - PassengerId: 727, - Age: 30, - Fare: 21, - Name: 'Renouf, Mrs. Peter Henry (Lillian Jefferys)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '36866', - Parch: 0, - Cabin: null, - PassengerId: 728, - Age: null, - Fare: 7.7375, - Name: 'Mannion, Miss. Margareth', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '236853', - Parch: 0, - Cabin: null, - PassengerId: 729, - Age: 25, - Fare: 26, - Name: 'Bryhl, Mr. Kurt Arnold Gottfrid', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'STON/O2. 3101271', - Parch: 0, - Cabin: null, - PassengerId: 730, - Age: 25, - Fare: 7.925, - Name: 'Ilmakangas, Miss. Pieta Sofia', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '24160', - Parch: 0, - Cabin: 'B5', - PassengerId: 731, - Age: 29, - Fare: 211.3375, - Name: 'Allen, Miss. Elisabeth Walton', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2699', - Parch: 0, - Cabin: null, - PassengerId: 732, - Age: 11, - Fare: 18.7875, - Name: 'Hassan, Mr. Houssein G N', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '239855', - Parch: 0, - Cabin: null, - PassengerId: 733, - Age: null, - Fare: 0, - Name: 'Knight, Mr. Robert J', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '28425', - Parch: 0, - Cabin: null, - PassengerId: 734, - Age: 23, - Fare: 13, - Name: 'Berriman, Mr. William John', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '233639', - Parch: 0, - Cabin: null, - PassengerId: 735, - Age: 23, - Fare: 13, - Name: 'Troupiansky, Mr. Moses Aaron', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '54636', - Parch: 0, - Cabin: null, - PassengerId: 736, - Age: 28.5, - Fare: 16.1, - Name: 'Williams, Mr. Leslie', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'W./C. 6608', - Parch: 3, - Cabin: null, - PassengerId: 737, - Age: 48, - Fare: 34.375, - Name: 'Ford, Mrs. Edward (Margaret Ann Watson)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17755', - Parch: 0, - Cabin: 'B101', - PassengerId: 738, - Age: 35, - Fare: 512.3292, - Name: 'Lesurer, Mr. Gustave J', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349201', - Parch: 0, - Cabin: null, - PassengerId: 739, - Age: null, - Fare: 7.8958, - Name: 'Ivanoff, Mr. Kanio', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349218', - Parch: 0, - Cabin: null, - PassengerId: 740, - Age: null, - Fare: 7.8958, - Name: 'Nankoff, Mr. Minko', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '16988', - Parch: 0, - Cabin: 'D45', - PassengerId: 741, - Age: null, - Fare: 30, - Name: 'Hawksford, Mr. Walter James', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '19877', - Parch: 0, - Cabin: 'C46', - PassengerId: 742, - Age: 36, - Fare: 78.85, - Name: 'Cavendish, Mr. Tyrell William', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: 'PC 17608', - Parch: 2, - Cabin: 'B57 B59 B63 B66', - PassengerId: 743, - Age: 21, - Fare: 262.375, - Name: 'Ryerson, Miss. Susan Parker "Suzette"', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '376566', - Parch: 0, - Cabin: null, - PassengerId: 744, - Age: 24, - Fare: 16.1, - Name: 'McNamee, Mr. Neal', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'STON/O 2. 3101288', - Parch: 0, - Cabin: null, - PassengerId: 745, - Age: 31, - Fare: 7.925, - Name: 'Stranden, Mr. Juho', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'WE/P 5735', - Parch: 1, - Cabin: 'B22', - PassengerId: 746, - Age: 70, - Fare: 71, - Name: 'Crosby, Capt. Edward Gifford', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 2673', - Parch: 1, - Cabin: null, - PassengerId: 747, - Age: 16, - Fare: 20.25, - Name: 'Abbott, Mr. Rossmore Edward', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '250648', - Parch: 0, - Cabin: null, - PassengerId: 748, - Age: 30, - Fare: 13, - Name: 'Sinkkonen, Miss. Anna', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '113773', - Parch: 0, - Cabin: 'D30', - PassengerId: 749, - Age: 19, - Fare: 53.1, - Name: 'Marvin, Mr. Daniel Warner', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '335097', - Parch: 0, - Cabin: null, - PassengerId: 750, - Age: 31, - Fare: 7.75, - Name: 'Connaghton, Mr. Michael', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '29103', - Parch: 1, - Cabin: null, - PassengerId: 751, - Age: 4, - Fare: 23, - Name: 'Wells, Miss. Joan', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '392096', - Parch: 1, - Cabin: 'E121', - PassengerId: 752, - Age: 6, - Fare: 12.475, - Name: 'Moor, Master. Meier', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '345780', - Parch: 0, - Cabin: null, - PassengerId: 753, - Age: 33, - Fare: 9.5, - Name: 'Vande Velde, Mr. Johannes Joseph', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349204', - Parch: 0, - Cabin: null, - PassengerId: 754, - Age: 23, - Fare: 7.8958, - Name: 'Jonkoff, Mr. Lalio', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '220845', - Parch: 2, - Cabin: null, - PassengerId: 755, - Age: 48, - Fare: 65, - Name: 'Herman, Mrs. Samuel (Jane Laver)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '250649', - Parch: 1, - Cabin: null, - PassengerId: 756, - Age: 0.67, - Fare: 14.5, - Name: 'Hamalainen, Master. Viljo', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350042', - Parch: 0, - Cabin: null, - PassengerId: 757, - Age: 28, - Fare: 7.7958, - Name: 'Carlsson, Mr. August Sigfrid', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '29108', - Parch: 0, - Cabin: null, - PassengerId: 758, - Age: 18, - Fare: 11.5, - Name: 'Bailey, Mr. Percy Andrew', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '363294', - Parch: 0, - Cabin: null, - PassengerId: 759, - Age: 34, - Fare: 8.05, - Name: 'Theobald, Mr. Thomas Leonard', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '110152', - Parch: 0, - Cabin: 'B77', - PassengerId: 760, - Age: 33, - Fare: 86.5, - Name: 'Rothes, the Countess. of (Lucy Noel Martha Dyer-Edwards)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '358585', - Parch: 0, - Cabin: null, - PassengerId: 761, - Age: null, - Fare: 14.5, - Name: 'Garfirth, Mr. John', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/O2 3101272', - Parch: 0, - Cabin: null, - PassengerId: 762, - Age: 41, - Fare: 7.125, - Name: 'Nirva, Mr. Iisakki Antino Aijo', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2663', - Parch: 0, - Cabin: null, - PassengerId: 763, - Age: 20, - Fare: 7.2292, - Name: 'Barah, Mr. Hanna Assi', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113760', - Parch: 2, - Cabin: 'B96 B98', - PassengerId: 764, - Age: 36, - Fare: 120, - Name: 'Carter, Mrs. William Ernest (Lucile Polk)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '347074', - Parch: 0, - Cabin: null, - PassengerId: 765, - Age: 16, - Fare: 7.775, - Name: 'Eklund, Mr. Hans Linus', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '13502', - Parch: 0, - Cabin: 'D11', - PassengerId: 766, - Age: 51, - Fare: 77.9583, - Name: 'Hogeboom, Mrs. John C (Anna Andrews)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '112379', - Parch: 0, - Cabin: null, - PassengerId: 767, - Age: null, - Fare: 39.6, - Name: 'Brewe, Dr. Arthur Jackson', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '364850', - Parch: 0, - Cabin: null, - PassengerId: 768, - Age: 30.5, - Fare: 7.75, - Name: 'Mangan, Miss. Mary', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '371110', - Parch: 0, - Cabin: null, - PassengerId: 769, - Age: null, - Fare: 24.15, - Name: 'Moran, Mr. Daniel J', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '8471', - Parch: 0, - Cabin: null, - PassengerId: 770, - Age: 32, - Fare: 8.3625, - Name: 'Gronnestad, Mr. Daniel Danielsen', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '345781', - Parch: 0, - Cabin: null, - PassengerId: 771, - Age: 24, - Fare: 9.5, - Name: 'Lievens, Mr. Rene Aime', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '350047', - Parch: 0, - Cabin: null, - PassengerId: 772, - Age: 48, - Fare: 7.8542, - Name: 'Jensen, Mr. Niels Peder', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'S.O./P.P. 3', - Parch: 0, - Cabin: 'E77', - PassengerId: 773, - Age: 57, - Fare: 10.5, - Name: 'Mack, Mrs. (Mary)', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2674', - Parch: 0, - Cabin: null, - PassengerId: 774, - Age: null, - Fare: 7.225, - Name: 'Elias, Mr. Dibo', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '29105', - Parch: 3, - Cabin: null, - PassengerId: 775, - Age: 54, - Fare: 23, - Name: 'Hocking, Mrs. Elizabeth (Eliza Needs)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '347078', - Parch: 0, - Cabin: null, - PassengerId: 776, - Age: 18, - Fare: 7.75, - Name: 'Myhrman, Mr. Pehr Fabian Oliver Malkolm', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '383121', - Parch: 0, - Cabin: 'F38', - PassengerId: 777, - Age: null, - Fare: 7.75, - Name: 'Tobin, Mr. Roger', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '364516', - Parch: 0, - Cabin: null, - PassengerId: 778, - Age: 5, - Fare: 12.475, - Name: 'Emanuel, Miss. Virginia Ethel', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '36865', - Parch: 0, - Cabin: null, - PassengerId: 779, - Age: null, - Fare: 7.7375, - Name: 'Kilgannon, Mr. Thomas J', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '24160', - Parch: 1, - Cabin: 'B3', - PassengerId: 780, - Age: 43, - Fare: 211.3375, - Name: 'Robert, Mrs. Edward Scott (Elisabeth Walton McMillan)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2687', - Parch: 0, - Cabin: null, - PassengerId: 781, - Age: 13, - Fare: 7.2292, - Name: 'Ayoub, Miss. Banoura', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '17474', - Parch: 0, - Cabin: 'B20', - PassengerId: 782, - Age: 17, - Fare: 57, - Name: 'Dick, Mrs. Albert Adrian (Vera Gillespie)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113501', - Parch: 0, - Cabin: 'D6', - PassengerId: 783, - Age: 29, - Fare: 30, - Name: 'Long, Mr. Milton Clyde', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'W./C. 6607', - Parch: 2, - Cabin: null, - PassengerId: 784, - Age: null, - Fare: 23.45, - Name: 'Johnston, Mr. Andrew G', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/O.Q. 3101312', - Parch: 0, - Cabin: null, - PassengerId: 785, - Age: 25, - Fare: 7.05, - Name: 'Ali, Mr. William', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '374887', - Parch: 0, - Cabin: null, - PassengerId: 786, - Age: 25, - Fare: 7.25, - Name: 'Harmer, Mr. Abraham (David Lishin)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '3101265', - Parch: 0, - Cabin: null, - PassengerId: 787, - Age: 18, - Fare: 7.4958, - Name: 'Sjoblom, Miss. Anna Sofia', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 4, - Ticket: '382652', - Parch: 1, - Cabin: null, - PassengerId: 788, - Age: 8, - Fare: 29.125, - Name: 'Rice, Master. George Hugh', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 2315', - Parch: 2, - Cabin: null, - PassengerId: 789, - Age: 1, - Fare: 20.575, - Name: 'Dean, Master. Bertram Vere', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'PC 17593', - Parch: 0, - Cabin: 'B82 B84', - PassengerId: 790, - Age: 46, - Fare: 79.2, - Name: 'Guggenheim, Mr. Benjamin', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '12460', - Parch: 0, - Cabin: null, - PassengerId: 791, - Age: null, - Fare: 7.75, - Name: 'Keane, Mr. Andrew "Andy"', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '239865', - Parch: 0, - Cabin: null, - PassengerId: 792, - Age: 16, - Fare: 26, - Name: 'Gaskell, Mr. Alfred', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 8, - Ticket: 'CA. 2343', - Parch: 2, - Cabin: null, - PassengerId: 793, - Age: null, - Fare: 69.55, - Name: 'Sage, Miss. Stella Anna', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17600', - Parch: 0, - Cabin: null, - PassengerId: 794, - Age: null, - Fare: 30.6958, - Name: 'Hoyt, Mr. William Fisher', - Survived: false, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349203', - Parch: 0, - Cabin: null, - PassengerId: 795, - Age: 25, - Fare: 7.8958, - Name: 'Dantcheff, Mr. Ristiu', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '28213', - Parch: 0, - Cabin: null, - PassengerId: 796, - Age: 39, - Fare: 13, - Name: 'Otter, Mr. Richard', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '17465', - Parch: 0, - Cabin: 'D17', - PassengerId: 797, - Age: 49, - Fare: 25.9292, - Name: 'Leader, Dr. Alice (Farnham)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349244', - Parch: 0, - Cabin: null, - PassengerId: 798, - Age: 31, - Fare: 8.6833, - Name: 'Osman, Mrs. Mara', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2685', - Parch: 0, - Cabin: null, - PassengerId: 799, - Age: 30, - Fare: 7.2292, - Name: 'Ibrahim Shawah, Mr. Yousseff', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '345773', - Parch: 1, - Cabin: null, - PassengerId: 800, - Age: 30, - Fare: 24.15, - Name: 'Van Impe, Mrs. Jean Baptiste (Rosalie Paula Govaert)', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '250647', - Parch: 0, - Cabin: null, - PassengerId: 801, - Age: 34, - Fare: 13, - Name: 'Ponesell, Mr. Martin', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'C.A. 31921', - Parch: 1, - Cabin: null, - PassengerId: 802, - Age: 31, - Fare: 26.25, - Name: 'Collyer, Mrs. Harvey (Charlotte Annie Tate)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '113760', - Parch: 2, - Cabin: 'B96 B98', - PassengerId: 803, - Age: 11, - Fare: 120, - Name: 'Carter, Master. William Thornton II', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2625', - Parch: 1, - Cabin: null, - PassengerId: 804, - Age: 0.42, - Fare: 8.5167, - Name: 'Thomas, Master. Assad Alexander', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347089', - Parch: 0, - Cabin: null, - PassengerId: 805, - Age: 27, - Fare: 6.975, - Name: 'Hedman, Mr. Oskar Arvid', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347063', - Parch: 0, - Cabin: null, - PassengerId: 806, - Age: 31, - Fare: 7.775, - Name: 'Johansson, Mr. Karl Johan', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '112050', - Parch: 0, - Cabin: 'A36', - PassengerId: 807, - Age: 39, - Fare: 0, - Name: 'Andrews, Mr. Thomas Jr', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347087', - Parch: 0, - Cabin: null, - PassengerId: 808, - Age: 18, - Fare: 7.775, - Name: 'Pettersson, Miss. Ellen Natalia', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '248723', - Parch: 0, - Cabin: null, - PassengerId: 809, - Age: 39, - Fare: 13, - Name: 'Meyer, Mr. August', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '113806', - Parch: 0, - Cabin: 'E8', - PassengerId: 810, - Age: 33, - Fare: 53.1, - Name: 'Chambers, Mrs. Norman Campbell (Bertha Griggs)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '3474', - Parch: 0, - Cabin: null, - PassengerId: 811, - Age: 26, - Fare: 7.8875, - Name: 'Alexander, Mr. William', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'A/4 48871', - Parch: 0, - Cabin: null, - PassengerId: 812, - Age: 39, - Fare: 24.15, - Name: 'Lester, Mr. James', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '28206', - Parch: 0, - Cabin: null, - PassengerId: 813, - Age: 35, - Fare: 10.5, - Name: 'Slemen, Mr. Richard James', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 4, - Ticket: '347082', - Parch: 2, - Cabin: null, - PassengerId: 814, - Age: 6, - Fare: 31.275, - Name: 'Andersson, Miss. Ebba Iris Alfrida', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '364499', - Parch: 0, - Cabin: null, - PassengerId: 815, - Age: 30.5, - Fare: 8.05, - Name: 'Tomlin, Mr. Ernest Portage', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '112058', - Parch: 0, - Cabin: 'B102', - PassengerId: 816, - Age: null, - Fare: 0, - Name: 'Fry, Mr. Richard', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'STON/O2. 3101290', - Parch: 0, - Cabin: null, - PassengerId: 817, - Age: 23, - Fare: 7.925, - Name: 'Heininen, Miss. Wendla Maria', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'S.C./PARIS 2079', - Parch: 1, - Cabin: null, - PassengerId: 818, - Age: 31, - Fare: 37.0042, - Name: 'Mallet, Mr. Albert', - Survived: false, - Pclass: 2, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C 7075', - Parch: 0, - Cabin: null, - PassengerId: 819, - Age: 43, - Fare: 6.45, - Name: 'Holm, Mr. John Fredrik Alexander', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 3, - Ticket: '347088', - Parch: 2, - Cabin: null, - PassengerId: 820, - Age: 10, - Fare: 27.9, - Name: 'Skoog, Master. Karl Thorsten', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '12749', - Parch: 1, - Cabin: 'B69', - PassengerId: 821, - Age: 52, - Fare: 93.5, - Name: 'Hays, Mrs. Charles Melville (Clara Jennings Gregg)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '315098', - Parch: 0, - Cabin: null, - PassengerId: 822, - Age: 27, - Fare: 8.6625, - Name: 'Lulic, Mr. Nikola', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '19972', - Parch: 0, - Cabin: null, - PassengerId: 823, - Age: 38, - Fare: 0, - Name: 'Reuchlin, Jonkheer. John George', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '392096', - Parch: 1, - Cabin: 'E121', - PassengerId: 824, - Age: 27, - Fare: 12.475, - Name: 'Moor, Mrs. (Beila)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 4, - Ticket: '3101295', - Parch: 1, - Cabin: null, - PassengerId: 825, - Age: 2, - Fare: 39.6875, - Name: 'Panula, Master. Urho Abraham', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '368323', - Parch: 0, - Cabin: null, - PassengerId: 826, - Age: null, - Fare: 6.95, - Name: 'Flynn, Mr. John', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '1601', - Parch: 0, - Cabin: null, - PassengerId: 827, - Age: null, - Fare: 56.4958, - Name: 'Lam, Mr. Len', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'S.C./PARIS 2079', - Parch: 2, - Cabin: null, - PassengerId: 828, - Age: 1, - Fare: 37.0042, - Name: 'Mallet, Master. Andre', - Survived: true, - Pclass: 2, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '367228', - Parch: 0, - Cabin: null, - PassengerId: 829, - Age: null, - Fare: 7.75, - Name: 'McCormack, Mr. Thomas Joseph', - Survived: true, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113572', - Parch: 0, - Cabin: 'B28', - PassengerId: 830, - Age: 62, - Fare: 80, - Name: 'Stone, Mrs. George Nelson (Martha Evelyn)', - Survived: true, - Pclass: 1, - Embarked: null, - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '2659', - Parch: 0, - Cabin: null, - PassengerId: 831, - Age: 15, - Fare: 14.4542, - Name: 'Yasbeck, Mrs. Antoni (Selini Alexander)', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '29106', - Parch: 1, - Cabin: null, - PassengerId: 832, - Age: 0.83, - Fare: 18.75, - Name: 'Richards, Master. George Sibley', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2671', - Parch: 0, - Cabin: null, - PassengerId: 833, - Age: null, - Fare: 7.2292, - Name: 'Saad, Mr. Amin', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347468', - Parch: 0, - Cabin: null, - PassengerId: 834, - Age: 23, - Fare: 7.8542, - Name: 'Augustsson, Mr. Albert', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2223', - Parch: 0, - Cabin: null, - PassengerId: 835, - Age: 18, - Fare: 8.3, - Name: 'Allum, Mr. Owen George', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'PC 17756', - Parch: 1, - Cabin: 'E49', - PassengerId: 836, - Age: 39, - Fare: 83.1583, - Name: 'Compton, Miss. Sara Rebecca', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '315097', - Parch: 0, - Cabin: null, - PassengerId: 837, - Age: 21, - Fare: 8.6625, - Name: 'Pasic, Mr. Jakob', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '392092', - Parch: 0, - Cabin: null, - PassengerId: 838, - Age: null, - Fare: 8.05, - Name: 'Sirota, Mr. Maurice', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '1601', - Parch: 0, - Cabin: null, - PassengerId: 839, - Age: 32, - Fare: 56.4958, - Name: 'Chip, Mr. Chang', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '11774', - Parch: 0, - Cabin: 'C47', - PassengerId: 840, - Age: null, - Fare: 29.7, - Name: 'Marechal, Mr. Pierre', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/O2 3101287', - Parch: 0, - Cabin: null, - PassengerId: 841, - Age: 20, - Fare: 7.925, - Name: 'Alhomaki, Mr. Ilmari Rudolf', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'S.O./P.P. 3', - Parch: 0, - Cabin: null, - PassengerId: 842, - Age: 16, - Fare: 10.5, - Name: 'Mudd, Mr. Thomas Charles', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '113798', - Parch: 0, - Cabin: null, - PassengerId: 843, - Age: 30, - Fare: 31, - Name: 'Serepeca, Miss. Augusta', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2683', - Parch: 0, - Cabin: null, - PassengerId: 844, - Age: 34.5, - Fare: 6.4375, - Name: 'Lemberopolous, Mr. Peter L', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '315090', - Parch: 0, - Cabin: null, - PassengerId: 845, - Age: 17, - Fare: 8.6625, - Name: 'Culumovic, Mr. Jeso', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'C.A. 5547', - Parch: 0, - Cabin: null, - PassengerId: 846, - Age: 42, - Fare: 7.55, - Name: 'Abbing, Mr. Anthony', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 8, - Ticket: 'CA. 2343', - Parch: 2, - Cabin: null, - PassengerId: 847, - Age: null, - Fare: 69.55, - Name: 'Sage, Mr. Douglas Bullen', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349213', - Parch: 0, - Cabin: null, - PassengerId: 848, - Age: 35, - Fare: 7.8958, - Name: 'Markoff, Mr. Marin', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '248727', - Parch: 1, - Cabin: null, - PassengerId: 849, - Age: 28, - Fare: 33, - Name: 'Harper, Rev. John', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '17453', - Parch: 0, - Cabin: 'C92', - PassengerId: 850, - Age: null, - Fare: 89.1042, - Name: 'Goldenberg, Mrs. Samuel L (Edwiga Grabowska)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 4, - Ticket: '347082', - Parch: 2, - Cabin: null, - PassengerId: 851, - Age: 4, - Fare: 31.275, - Name: 'Andersson, Master. Sigvard Harald Elias', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '347060', - Parch: 0, - Cabin: null, - PassengerId: 852, - Age: 74, - Fare: 7.775, - Name: 'Svensson, Mr. Johan', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '2678', - Parch: 1, - Cabin: null, - PassengerId: 853, - Age: 9, - Fare: 15.2458, - Name: 'Boulos, Miss. Nourelain', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17592', - Parch: 1, - Cabin: 'D28', - PassengerId: 854, - Age: 16, - Fare: 39.4, - Name: 'Lines, Miss. Mary Conover', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '244252', - Parch: 0, - Cabin: null, - PassengerId: 855, - Age: 44, - Fare: 26, - Name: 'Carter, Mrs. Ernest Courtenay (Lilian Hughes)', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '392091', - Parch: 1, - Cabin: null, - PassengerId: 856, - Age: 18, - Fare: 9.35, - Name: 'Aks, Mrs. Sam (Leah Rosen)', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: '36928', - Parch: 1, - Cabin: null, - PassengerId: 857, - Age: 45, - Fare: 164.8667, - Name: 'Wick, Mrs. George Dennick (Mary Hitchcock)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '113055', - Parch: 0, - Cabin: 'E17', - PassengerId: 858, - Age: 51, - Fare: 26.55, - Name: 'Daly, Mr. Peter Denis ', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '2666', - Parch: 3, - Cabin: null, - PassengerId: 859, - Age: 24, - Fare: 19.2583, - Name: 'Baclini, Mrs. Solomon (Latifa Qurban)', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2629', - Parch: 0, - Cabin: null, - PassengerId: 860, - Age: null, - Fare: 7.2292, - Name: 'Razi, Mr. Raihed', - Survived: false, - Pclass: 3, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 2, - Ticket: '350026', - Parch: 0, - Cabin: null, - PassengerId: 861, - Age: 41, - Fare: 14.1083, - Name: 'Hansen, Mr. Claus Peter', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '28134', - Parch: 0, - Cabin: null, - PassengerId: 862, - Age: 21, - Fare: 11.5, - Name: 'Giles, Mr. Frederick Edward', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '17466', - Parch: 0, - Cabin: 'D17', - PassengerId: 863, - Age: 48, - Fare: 25.9292, - Name: 'Swift, Mrs. Frederick Joel (Margaret Welles Barron)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 8, - Ticket: 'CA. 2343', - Parch: 2, - Cabin: null, - PassengerId: 864, - Age: null, - Fare: 69.55, - Name: 'Sage, Miss. Dorothy Edith "Dolly"', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '233866', - Parch: 0, - Cabin: null, - PassengerId: 865, - Age: 24, - Fare: 13, - Name: 'Gill, Mr. John William', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '236852', - Parch: 0, - Cabin: null, - PassengerId: 866, - Age: 42, - Fare: 13, - Name: 'Bystrom, Mrs. (Karolina)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'SC/PARIS 2149', - Parch: 0, - Cabin: null, - PassengerId: 867, - Age: 27, - Fare: 13.8583, - Name: 'Duran y More, Miss. Asuncion', - Survived: true, - Pclass: 2, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'PC 17590', - Parch: 0, - Cabin: 'A24', - PassengerId: 868, - Age: 31, - Fare: 50.4958, - Name: 'Roebling, Mr. Washington Augustus II', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '345777', - Parch: 0, - Cabin: null, - PassengerId: 869, - Age: null, - Fare: 9.5, - Name: 'van Melkebeke, Mr. Philemon', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '347742', - Parch: 1, - Cabin: null, - PassengerId: 870, - Age: 4, - Fare: 11.1333, - Name: 'Johnson, Master. Harold Theodor', - Survived: true, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349248', - Parch: 0, - Cabin: null, - PassengerId: 871, - Age: 26, - Fare: 7.8958, - Name: 'Balkic, Mr. Cerin', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: '11751', - Parch: 1, - Cabin: 'D35', - PassengerId: 872, - Age: 47, - Fare: 52.5542, - Name: 'Beckwith, Mrs. Richard Leonard (Sallie Monypeny)', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '695', - Parch: 0, - Cabin: 'B51 B53 B55', - PassengerId: 873, - Age: 33, - Fare: 5, - Name: 'Carlsson, Mr. Frans Olof', - Survived: false, - Pclass: 1, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '345765', - Parch: 0, - Cabin: null, - PassengerId: 874, - Age: 47, - Fare: 9, - Name: 'Vander Cruyssen, Mr. Victor', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 1, - Ticket: 'P/PP 3381', - Parch: 0, - Cabin: null, - PassengerId: 875, - Age: 28, - Fare: 24, - Name: 'Abelson, Mrs. Samuel (Hannah Wizosky)', - Survived: true, - Pclass: 2, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '2667', - Parch: 0, - Cabin: null, - PassengerId: 876, - Age: 15, - Fare: 7.225, - Name: 'Najib, Miss. Adele Kiamie "Jane"', - Survived: true, - Pclass: 3, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '7534', - Parch: 0, - Cabin: null, - PassengerId: 877, - Age: 20, - Fare: 9.8458, - Name: 'Gustafsson, Mr. Alfred Ossian', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349212', - Parch: 0, - Cabin: null, - PassengerId: 878, - Age: 19, - Fare: 7.8958, - Name: 'Petroff, Mr. Nedelio', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '349217', - Parch: 0, - Cabin: null, - PassengerId: 879, - Age: null, - Fare: 7.8958, - Name: 'Laleff, Mr. Kristo', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '11767', - Parch: 1, - Cabin: 'C50', - PassengerId: 880, - Age: 56, - Fare: 83.1583, - Name: 'Potter, Mrs. Thomas Jr (Lily Alexenia Wilson)', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '230433', - Parch: 1, - Cabin: null, - PassengerId: 881, - Age: 25, - Fare: 26, - Name: 'Shelley, Mrs. William (Imanita Parrish Hall)', - Survived: true, - Pclass: 2, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '349257', - Parch: 0, - Cabin: null, - PassengerId: 882, - Age: 33, - Fare: 7.8958, - Name: 'Markun, Mr. Johann', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '7552', - Parch: 0, - Cabin: null, - PassengerId: 883, - Age: 22, - Fare: 10.5167, - Name: 'Dahlberg, Miss. Gerda Ulrika', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: 'C.A./SOTON 34068', - Parch: 0, - Cabin: null, - PassengerId: 884, - Age: 28, - Fare: 10.5, - Name: 'Banfield, Mr. Frederick James', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: 'SOTON/OQ 392076', - Parch: 0, - Cabin: null, - PassengerId: 885, - Age: 25, - Fare: 7.05, - Name: 'Sutehall, Mr. Henry Jr', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '382652', - Parch: 5, - Cabin: null, - PassengerId: 886, - Age: 39, - Fare: 29.125, - Name: 'Rice, Mrs. William (Margaret Norton)', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '211536', - Parch: 0, - Cabin: null, - PassengerId: 887, - Age: 27, - Fare: 13, - Name: 'Montvila, Rev. Juozas', - Survived: false, - Pclass: 2, - Embarked: 'S', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '112053', - Parch: 0, - Cabin: 'B42', - PassengerId: 888, - Age: 19, - Fare: 30, - Name: 'Graham, Miss. Margaret Edith', - Survived: true, - Pclass: 1, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 1, - Ticket: 'W./C. 6607', - Parch: 2, - Cabin: null, - PassengerId: 889, - Age: null, - Fare: 23.45, - Name: 'Johnston, Miss. Catherine Helen "Carrie"', - Survived: false, - Pclass: 3, - Embarked: 'S', - Sex: 'female' - }, - { - SibSp: 0, - Ticket: '111369', - Parch: 0, - Cabin: 'C148', - PassengerId: 890, - Age: 26, - Fare: 30, - Name: 'Behr, Mr. Karl Howell', - Survived: true, - Pclass: 1, - Embarked: 'C', - Sex: 'male' - }, - { - SibSp: 0, - Ticket: '370376', - Parch: 0, - Cabin: null, - PassengerId: 891, - Age: 32, - Fare: 7.75, - Name: 'Dooley, Mr. Patrick', - Survived: false, - Pclass: 3, - Embarked: 'Q', - Sex: 'male' - } -]; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +export interface ITestData { + columns: { id: string; name: string; type: string }[]; + primaryKeys: string[]; + rows: {}[]; + loadingRows: {}[]; +} + +// tslint:disable +export function generateTestData(_numberOfRows: number): ITestData { + const columns = [ + { id: 'PassengerId', name: 'PassengerId', field: 'PassengerId', type: 'integer' }, + { id: 'SibSp', name: 'SibSp', field: 'SibSp', type: 'integer' }, + { id: 'Ticket', name: 'Ticket', field: 'Ticket', type: 'string' }, + { id: 'Parch', name: 'Parch', field: 'Parch', type: 'integer' }, + { id: 'Cabin', name: 'Cabin', field: 'Cabin', type: 'string' }, + { id: 'Age', name: 'Age', field: 'Age', type: 'integer' }, + { id: 'Fare', name: 'Fare', field: 'Fare', type: 'number' }, + { id: 'Name', name: 'Name', field: 'Name', type: 'string' }, + { id: 'Survived', name: 'Survived', field: 'Survived', type: 'bool' }, + { id: 'Pclass', name: 'Pclass', field: 'Pclass', type: 'integer' }, + { id: 'Embarked', name: 'Embarked', field: 'Embarked', type: 'string' }, + { id: 'Sex', name: 'Sex', field: 'Sex', type: 'string' } + ]; + + const keys = ['PassengerId']; + + const rows: {}[] = titanicData; + + return { + columns, + primaryKeys: keys, + rows, + loadingRows: titanicData.map((_t) => { + return {}; + }) + }; +} + +const titanicData = [ + { + SibSp: 1, + Ticket: 'A/5 21171', + Parch: 0, + Cabin: null, + PassengerId: 1, + Age: 22, + Fare: 7.25, + Name: 'Braund, Mr. Owen Harris', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'PC 17599', + Parch: 0, + Cabin: 'C85', + PassengerId: 2, + Age: 38, + Fare: 71.2833, + Name: 'Cumings, Mrs. John Bradley (Florence Briggs Thayer)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'STON/O2. 3101282', + Parch: 0, + Cabin: null, + PassengerId: 3, + Age: 26, + Fare: 7.925, + Name: 'Heikkinen, Miss. Laina', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '113803', + Parch: 0, + Cabin: 'C123', + PassengerId: 4, + Age: 35, + Fare: 53.1, + Name: 'Futrelle, Mrs. Jacques Heath (Lily May Peel)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '373450', + Parch: 0, + Cabin: null, + PassengerId: 5, + Age: 35, + Fare: 8.05, + Name: 'Allen, Mr. William Henry', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '330877', + Parch: 0, + Cabin: null, + PassengerId: 6, + Age: null, + Fare: 8.4583, + Name: 'Moran, Mr. James', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '17463', + Parch: 0, + Cabin: 'E46', + PassengerId: 7, + Age: 54, + Fare: 51.8625, + Name: 'McCarthy, Mr. Timothy J', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '349909', + Parch: 1, + Cabin: null, + PassengerId: 8, + Age: 2, + Fare: 21.075, + Name: 'Palsson, Master. Gosta Leonard', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347742', + Parch: 2, + Cabin: null, + PassengerId: 9, + Age: 27, + Fare: 11.1333, + Name: 'Johnson, Mrs. Oscar W (Elisabeth Vilhelmina Berg)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '237736', + Parch: 0, + Cabin: null, + PassengerId: 10, + Age: 14, + Fare: 30.0708, + Name: 'Nasser, Mrs. Nicholas (Adele Achem)', + Survived: true, + Pclass: 2, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'PP 9549', + Parch: 1, + Cabin: 'G6', + PassengerId: 11, + Age: 4, + Fare: 16.7, + Name: 'Sandstrom, Miss. Marguerite Rut', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113783', + Parch: 0, + Cabin: 'C103', + PassengerId: 12, + Age: 58, + Fare: 26.55, + Name: 'Bonnell, Miss. Elizabeth', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'A/5. 2151', + Parch: 0, + Cabin: null, + PassengerId: 13, + Age: 20, + Fare: 8.05, + Name: 'Saundercock, Mr. William Henry', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '347082', + Parch: 5, + Cabin: null, + PassengerId: 14, + Age: 39, + Fare: 31.275, + Name: 'Andersson, Mr. Anders Johan', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350406', + Parch: 0, + Cabin: null, + PassengerId: 15, + Age: 14, + Fare: 7.8542, + Name: 'Vestrom, Miss. Hulda Amanda Adolfina', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '248706', + Parch: 0, + Cabin: null, + PassengerId: 16, + Age: 55, + Fare: 16, + Name: 'Hewlett, Mrs. (Mary D Kingcome) ', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 4, + Ticket: '382652', + Parch: 1, + Cabin: null, + PassengerId: 17, + Age: 2, + Fare: 29.125, + Name: 'Rice, Master. Eugene', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '244373', + Parch: 0, + Cabin: null, + PassengerId: 18, + Age: null, + Fare: 13, + Name: 'Williams, Mr. Charles Eugene', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '345763', + Parch: 0, + Cabin: null, + PassengerId: 19, + Age: 31, + Fare: 18, + Name: 'Vander Planke, Mrs. Julius (Emelia Maria Vandemoortele)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2649', + Parch: 0, + Cabin: null, + PassengerId: 20, + Age: null, + Fare: 7.225, + Name: 'Masselmani, Mrs. Fatima', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '239865', + Parch: 0, + Cabin: null, + PassengerId: 21, + Age: 35, + Fare: 26, + Name: 'Fynney, Mr. Joseph J', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '248698', + Parch: 0, + Cabin: 'D56', + PassengerId: 22, + Age: 34, + Fare: 13, + Name: 'Beesley, Mr. Lawrence', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '330923', + Parch: 0, + Cabin: null, + PassengerId: 23, + Age: 15, + Fare: 8.0292, + Name: 'McGowan, Miss. Anna "Annie"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113788', + Parch: 0, + Cabin: 'A6', + PassengerId: 24, + Age: 28, + Fare: 35.5, + Name: 'Sloper, Mr. William Thompson', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '349909', + Parch: 1, + Cabin: null, + PassengerId: 25, + Age: 8, + Fare: 21.075, + Name: 'Palsson, Miss. Torborg Danira', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '347077', + Parch: 5, + Cabin: null, + PassengerId: 26, + Age: 38, + Fare: 31.3875, + Name: 'Asplund, Mrs. Carl Oscar (Selma Augusta Emilia Johansson)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2631', + Parch: 0, + Cabin: null, + PassengerId: 27, + Age: null, + Fare: 7.225, + Name: 'Emir, Mr. Farred Chehab', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '19950', + Parch: 2, + Cabin: 'C23 C25 C27', + PassengerId: 28, + Age: 19, + Fare: 263, + Name: 'Fortune, Mr. Charles Alexander', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '330959', + Parch: 0, + Cabin: null, + PassengerId: 29, + Age: null, + Fare: 7.8792, + Name: 'O\'Dwyer, Miss. Ellen "Nellie"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349216', + Parch: 0, + Cabin: null, + PassengerId: 30, + Age: null, + Fare: 7.8958, + Name: 'Todoroff, Mr. Lalio', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17601', + Parch: 0, + Cabin: null, + PassengerId: 31, + Age: 40, + Fare: 27.7208, + Name: 'Uruchurtu, Don. Manuel E', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'PC 17569', + Parch: 0, + Cabin: 'B78', + PassengerId: 32, + Age: null, + Fare: 146.5208, + Name: 'Spencer, Mrs. William Augustus (Marie Eugenie)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '335677', + Parch: 0, + Cabin: null, + PassengerId: 33, + Age: null, + Fare: 7.75, + Name: 'Glynn, Miss. Mary Agatha', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'C.A. 24579', + Parch: 0, + Cabin: null, + PassengerId: 34, + Age: 66, + Fare: 10.5, + Name: 'Wheadon, Mr. Edward H', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'PC 17604', + Parch: 0, + Cabin: null, + PassengerId: 35, + Age: 28, + Fare: 82.1708, + Name: 'Meyer, Mr. Edgar Joseph', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113789', + Parch: 0, + Cabin: null, + PassengerId: 36, + Age: 42, + Fare: 52, + Name: 'Holverson, Mr. Alexander Oskar', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2677', + Parch: 0, + Cabin: null, + PassengerId: 37, + Age: null, + Fare: 7.2292, + Name: 'Mamee, Mr. Hanna', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A./5. 2152', + Parch: 0, + Cabin: null, + PassengerId: 38, + Age: 21, + Fare: 8.05, + Name: 'Cann, Mr. Ernest Charles', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '345764', + Parch: 0, + Cabin: null, + PassengerId: 39, + Age: 18, + Fare: 18, + Name: 'Vander Planke, Miss. Augusta Maria', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '2651', + Parch: 0, + Cabin: null, + PassengerId: 40, + Age: 14, + Fare: 11.2417, + Name: 'Nicola-Yarred, Miss. Jamila', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '7546', + Parch: 0, + Cabin: null, + PassengerId: 41, + Age: 40, + Fare: 9.475, + Name: 'Ahlin, Mrs. Johan (Johanna Persdotter Larsson)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '11668', + Parch: 0, + Cabin: null, + PassengerId: 42, + Age: 27, + Fare: 21, + Name: 'Turpin, Mrs. William John Robert (Dorothy Ann Wonnacott)', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349253', + Parch: 0, + Cabin: null, + PassengerId: 43, + Age: null, + Fare: 7.8958, + Name: 'Kraeff, Mr. Theodor', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'SC/Paris 2123', + Parch: 2, + Cabin: null, + PassengerId: 44, + Age: 3, + Fare: 41.5792, + Name: 'Laroche, Miss. Simonne Marie Anne Andree', + Survived: true, + Pclass: 2, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '330958', + Parch: 0, + Cabin: null, + PassengerId: 45, + Age: 19, + Fare: 7.8792, + Name: 'Devaney, Miss. Margaret Delia', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'S.C./A.4. 23567', + Parch: 0, + Cabin: null, + PassengerId: 46, + Age: null, + Fare: 8.05, + Name: 'Rogers, Mr. William John', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '370371', + Parch: 0, + Cabin: null, + PassengerId: 47, + Age: null, + Fare: 15.5, + Name: 'Lennon, Mr. Denis', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '14311', + Parch: 0, + Cabin: null, + PassengerId: 48, + Age: null, + Fare: 7.75, + Name: "O'Driscoll, Miss. Bridget", + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: '2662', + Parch: 0, + Cabin: null, + PassengerId: 49, + Age: null, + Fare: 21.6792, + Name: 'Samaan, Mr. Youssef', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '349237', + Parch: 0, + Cabin: null, + PassengerId: 50, + Age: 18, + Fare: 17.8, + Name: 'Arnold-Franchi, Mrs. Josef (Josefine Franchi)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 4, + Ticket: '3101295', + Parch: 1, + Cabin: null, + PassengerId: 51, + Age: 7, + Fare: 39.6875, + Name: 'Panula, Master. Juha Niilo', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/4. 39886', + Parch: 0, + Cabin: null, + PassengerId: 52, + Age: 21, + Fare: 7.8, + Name: 'Nosworthy, Mr. Richard Cater', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'PC 17572', + Parch: 0, + Cabin: 'D33', + PassengerId: 53, + Age: 49, + Fare: 76.7292, + Name: 'Harper, Mrs. Henry Sleeper (Myna Haxtun)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '2926', + Parch: 0, + Cabin: null, + PassengerId: 54, + Age: 29, + Fare: 26, + Name: 'Faunthorpe, Mrs. Lizzie (Elizabeth Anne Wilkinson)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113509', + Parch: 1, + Cabin: 'B30', + PassengerId: 55, + Age: 65, + Fare: 61.9792, + Name: 'Ostby, Mr. Engelhart Cornelius', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '19947', + Parch: 0, + Cabin: 'C52', + PassengerId: 56, + Age: null, + Fare: 35.5, + Name: 'Woolner, Mr. Hugh', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C.A. 31026', + Parch: 0, + Cabin: null, + PassengerId: 57, + Age: 21, + Fare: 10.5, + Name: 'Rugg, Miss. Emily', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2697', + Parch: 0, + Cabin: null, + PassengerId: 58, + Age: 28.5, + Fare: 7.2292, + Name: 'Novel, Mr. Mansouer', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 34651', + Parch: 2, + Cabin: null, + PassengerId: 59, + Age: 5, + Fare: 27.75, + Name: 'West, Miss. Constance Mirium', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 5, + Ticket: 'CA 2144', + Parch: 2, + Cabin: null, + PassengerId: 60, + Age: 11, + Fare: 46.9, + Name: 'Goodwin, Master. William Frederick', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2669', + Parch: 0, + Cabin: null, + PassengerId: 61, + Age: 22, + Fare: 7.2292, + Name: 'Sirayanian, Mr. Orsen', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113572', + Parch: 0, + Cabin: 'B28', + PassengerId: 62, + Age: 38, + Fare: 80, + Name: 'Icard, Miss. Amelie', + Survived: true, + Pclass: 1, + Embarked: null, + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '36973', + Parch: 0, + Cabin: 'C83', + PassengerId: 63, + Age: 45, + Fare: 83.475, + Name: 'Harris, Mr. Henry Birkhardt', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '347088', + Parch: 2, + Cabin: null, + PassengerId: 64, + Age: 4, + Fare: 27.9, + Name: 'Skoog, Master. Harald', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17605', + Parch: 0, + Cabin: null, + PassengerId: 65, + Age: null, + Fare: 27.7208, + Name: 'Stewart, Mr. Albert A', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2661', + Parch: 1, + Cabin: null, + PassengerId: 66, + Age: null, + Fare: 15.2458, + Name: 'Moubarek, Master. Gerios', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C.A. 29395', + Parch: 0, + Cabin: 'F33', + PassengerId: 67, + Age: 29, + Fare: 10.5, + Name: 'Nye, Mrs. (Elizabeth Ramell)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'S.P. 3464', + Parch: 0, + Cabin: null, + PassengerId: 68, + Age: 19, + Fare: 8.1583, + Name: 'Crease, Mr. Ernest James', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '3101281', + Parch: 2, + Cabin: null, + PassengerId: 69, + Age: 17, + Fare: 7.925, + Name: 'Andersson, Miss. Erna Alexandra', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: '315151', + Parch: 0, + Cabin: null, + PassengerId: 70, + Age: 26, + Fare: 8.6625, + Name: 'Kink, Mr. Vincenz', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C.A. 33111', + Parch: 0, + Cabin: null, + PassengerId: 71, + Age: 32, + Fare: 10.5, + Name: 'Jenkin, Mr. Stephen Curnow', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 5, + Ticket: 'CA 2144', + Parch: 2, + Cabin: null, + PassengerId: 72, + Age: 16, + Fare: 46.9, + Name: 'Goodwin, Miss. Lillian Amy', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'S.O.C. 14879', + Parch: 0, + Cabin: null, + PassengerId: 73, + Age: 21, + Fare: 73.5, + Name: 'Hood, Mr. Ambrose Jr', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2680', + Parch: 0, + Cabin: null, + PassengerId: 74, + Age: 26, + Fare: 14.4542, + Name: 'Chronopoulos, Mr. Apostolos', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '1601', + Parch: 0, + Cabin: null, + PassengerId: 75, + Age: 32, + Fare: 56.4958, + Name: 'Bing, Mr. Lee', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '348123', + Parch: 0, + Cabin: 'F G73', + PassengerId: 76, + Age: 25, + Fare: 7.65, + Name: 'Moen, Mr. Sigurd Hansen', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349208', + Parch: 0, + Cabin: null, + PassengerId: 77, + Age: null, + Fare: 7.8958, + Name: 'Staneff, Mr. Ivan', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '374746', + Parch: 0, + Cabin: null, + PassengerId: 78, + Age: null, + Fare: 8.05, + Name: 'Moutal, Mr. Rahamin Haim', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '248738', + Parch: 2, + Cabin: null, + PassengerId: 79, + Age: 0.83, + Fare: 29, + Name: 'Caldwell, Master. Alden Gates', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '364516', + Parch: 0, + Cabin: null, + PassengerId: 80, + Age: 30, + Fare: 12.475, + Name: 'Dowdell, Miss. Elizabeth', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '345767', + Parch: 0, + Cabin: null, + PassengerId: 81, + Age: 22, + Fare: 9, + Name: 'Waelens, Mr. Achille', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '345779', + Parch: 0, + Cabin: null, + PassengerId: 82, + Age: 29, + Fare: 9.5, + Name: 'Sheerlinck, Mr. Jan Baptist', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '330932', + Parch: 0, + Cabin: null, + PassengerId: 83, + Age: null, + Fare: 7.7875, + Name: 'McDermott, Miss. Brigdet Delia', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113059', + Parch: 0, + Cabin: null, + PassengerId: 84, + Age: 28, + Fare: 47.1, + Name: 'Carrau, Mr. Francisco M', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SO/C 14885', + Parch: 0, + Cabin: null, + PassengerId: 85, + Age: 17, + Fare: 10.5, + Name: 'Ilett, Miss. Bertha', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 3, + Ticket: '3101278', + Parch: 0, + Cabin: null, + PassengerId: 86, + Age: 33, + Fare: 15.85, + Name: 'Backstrom, Mrs. Karl Alfred (Maria Mathilda Gustafsson)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'W./C. 6608', + Parch: 3, + Cabin: null, + PassengerId: 87, + Age: 16, + Fare: 34.375, + Name: 'Ford, Mr. William Neal', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/OQ 392086', + Parch: 0, + Cabin: null, + PassengerId: 88, + Age: null, + Fare: 8.05, + Name: 'Slocovski, Mr. Selman Francis', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '19950', + Parch: 2, + Cabin: 'C23 C25 C27', + PassengerId: 89, + Age: 23, + Fare: 263, + Name: 'Fortune, Miss. Mabel Helen', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '343275', + Parch: 0, + Cabin: null, + PassengerId: 90, + Age: 24, + Fare: 8.05, + Name: 'Celotti, Mr. Francesco', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '343276', + Parch: 0, + Cabin: null, + PassengerId: 91, + Age: 29, + Fare: 8.05, + Name: 'Christmann, Mr. Emil', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347466', + Parch: 0, + Cabin: null, + PassengerId: 92, + Age: 20, + Fare: 7.8542, + Name: 'Andreasson, Mr. Paul Edvin', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'W.E.P. 5734', + Parch: 0, + Cabin: 'E31', + PassengerId: 93, + Age: 46, + Fare: 61.175, + Name: 'Chaffee, Mr. Herbert Fuller', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 2315', + Parch: 2, + Cabin: null, + PassengerId: 94, + Age: 26, + Fare: 20.575, + Name: 'Dean, Mr. Bertram Frank', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '364500', + Parch: 0, + Cabin: null, + PassengerId: 95, + Age: 59, + Fare: 7.25, + Name: 'Coxon, Mr. Daniel', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '374910', + Parch: 0, + Cabin: null, + PassengerId: 96, + Age: null, + Fare: 8.05, + Name: 'Shorney, Mr. Charles Joseph', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17754', + Parch: 0, + Cabin: 'A5', + PassengerId: 97, + Age: 71, + Fare: 34.6542, + Name: 'Goldschmidt, Mr. George B', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17759', + Parch: 1, + Cabin: 'D10 D12', + PassengerId: 98, + Age: 23, + Fare: 63.3583, + Name: 'Greenfield, Mr. William Bertram', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '231919', + Parch: 1, + Cabin: null, + PassengerId: 99, + Age: 34, + Fare: 23, + Name: 'Doling, Mrs. John T (Ada Julia Bone)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '244367', + Parch: 0, + Cabin: null, + PassengerId: 100, + Age: 34, + Fare: 26, + Name: 'Kantor, Mr. Sinai', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349245', + Parch: 0, + Cabin: null, + PassengerId: 101, + Age: 28, + Fare: 7.8958, + Name: 'Petranec, Miss. Matilda', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349215', + Parch: 0, + Cabin: null, + PassengerId: 102, + Age: null, + Fare: 7.8958, + Name: 'Petroff, Mr. Pastcho ("Pentcho")', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '35281', + Parch: 1, + Cabin: 'D26', + PassengerId: 103, + Age: 21, + Fare: 77.2875, + Name: 'White, Mr. Richard Frasar', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '7540', + Parch: 0, + Cabin: null, + PassengerId: 104, + Age: 33, + Fare: 8.6542, + Name: 'Johansson, Mr. Gustaf Joel', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '3101276', + Parch: 0, + Cabin: null, + PassengerId: 105, + Age: 37, + Fare: 7.925, + Name: 'Gustafsson, Mr. Anders Vilhelm', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349207', + Parch: 0, + Cabin: null, + PassengerId: 106, + Age: 28, + Fare: 7.8958, + Name: 'Mionoff, Mr. Stoytcho', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '343120', + Parch: 0, + Cabin: null, + PassengerId: 107, + Age: 21, + Fare: 7.65, + Name: 'Salkjelsvik, Miss. Anna Kristine', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '312991', + Parch: 0, + Cabin: null, + PassengerId: 108, + Age: null, + Fare: 7.775, + Name: 'Moss, Mr. Albert Johan', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349249', + Parch: 0, + Cabin: null, + PassengerId: 109, + Age: 38, + Fare: 7.8958, + Name: 'Rekic, Mr. Tido', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '371110', + Parch: 0, + Cabin: null, + PassengerId: 110, + Age: null, + Fare: 24.15, + Name: 'Moran, Miss. Bertha', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '110465', + Parch: 0, + Cabin: 'C110', + PassengerId: 111, + Age: 47, + Fare: 52, + Name: 'Porter, Mr. Walter Chamberlain', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2665', + Parch: 0, + Cabin: null, + PassengerId: 112, + Age: 14.5, + Fare: 14.4542, + Name: 'Zabour, Miss. Hileni', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '324669', + Parch: 0, + Cabin: null, + PassengerId: 113, + Age: 22, + Fare: 8.05, + Name: 'Barton, Mr. David John', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '4136', + Parch: 0, + Cabin: null, + PassengerId: 114, + Age: 20, + Fare: 9.825, + Name: 'Jussila, Miss. Katriina', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2627', + Parch: 0, + Cabin: null, + PassengerId: 115, + Age: 17, + Fare: 14.4583, + Name: 'Attalah, Miss. Malake', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101294', + Parch: 0, + Cabin: null, + PassengerId: 116, + Age: 21, + Fare: 7.925, + Name: 'Pekoniemi, Mr. Edvard', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '370369', + Parch: 0, + Cabin: null, + PassengerId: 117, + Age: 70.5, + Fare: 7.75, + Name: 'Connors, Mr. Patrick', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '11668', + Parch: 0, + Cabin: null, + PassengerId: 118, + Age: 29, + Fare: 21, + Name: 'Turpin, Mr. William John Robert', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17558', + Parch: 1, + Cabin: 'B58 B60', + PassengerId: 119, + Age: 24, + Fare: 247.5208, + Name: 'Baxter, Mr. Quigg Edmond', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '347082', + Parch: 2, + Cabin: null, + PassengerId: 120, + Age: 2, + Fare: 31.275, + Name: 'Andersson, Miss. Ellis Anna Maria', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: 'S.O.C. 14879', + Parch: 0, + Cabin: null, + PassengerId: 121, + Age: 21, + Fare: 73.5, + Name: 'Hickman, Mr. Stanley George', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A4. 54510', + Parch: 0, + Cabin: null, + PassengerId: 122, + Age: null, + Fare: 8.05, + Name: 'Moore, Mr. Leonard Charles', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '237736', + Parch: 0, + Cabin: null, + PassengerId: 123, + Age: 32.5, + Fare: 30.0708, + Name: 'Nasser, Mr. Nicholas', + Survived: false, + Pclass: 2, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '27267', + Parch: 0, + Cabin: 'E101', + PassengerId: 124, + Age: 32.5, + Fare: 13, + Name: 'Webber, Miss. Susan', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '35281', + Parch: 1, + Cabin: 'D26', + PassengerId: 125, + Age: 54, + Fare: 77.2875, + Name: 'White, Mr. Percival Wayland', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2651', + Parch: 0, + Cabin: null, + PassengerId: 126, + Age: 12, + Fare: 11.2417, + Name: 'Nicola-Yarred, Master. Elias', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '370372', + Parch: 0, + Cabin: null, + PassengerId: 127, + Age: null, + Fare: 7.75, + Name: 'McMahon, Mr. Martin', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C 17369', + Parch: 0, + Cabin: null, + PassengerId: 128, + Age: 24, + Fare: 7.1417, + Name: 'Madsen, Mr. Fridtjof Arne', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2668', + Parch: 1, + Cabin: 'F E69', + PassengerId: 129, + Age: null, + Fare: 22.3583, + Name: 'Peter, Miss. Anna', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '347061', + Parch: 0, + Cabin: null, + PassengerId: 130, + Age: 45, + Fare: 6.975, + Name: 'Ekstrom, Mr. Johan', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349241', + Parch: 0, + Cabin: null, + PassengerId: 131, + Age: 33, + Fare: 7.8958, + Name: 'Drazenoic, Mr. Jozef', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/O.Q. 3101307', + Parch: 0, + Cabin: null, + PassengerId: 132, + Age: 20, + Fare: 7.05, + Name: 'Coelho, Mr. Domingos Fernandeo', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'A/5. 3337', + Parch: 0, + Cabin: null, + PassengerId: 133, + Age: 47, + Fare: 14.5, + Name: 'Robins, Mrs. Alexander A (Grace Charity Laury)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '228414', + Parch: 0, + Cabin: null, + PassengerId: 134, + Age: 29, + Fare: 26, + Name: 'Weisz, Mrs. Leopold (Mathilde Francoise Pede)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'C.A. 29178', + Parch: 0, + Cabin: null, + PassengerId: 135, + Age: 25, + Fare: 13, + Name: 'Sobey, Mr. Samuel James Hayden', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SC/PARIS 2133', + Parch: 0, + Cabin: null, + PassengerId: 136, + Age: 23, + Fare: 15.0458, + Name: 'Richard, Mr. Emile', + Survived: false, + Pclass: 2, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '11752', + Parch: 2, + Cabin: 'D47', + PassengerId: 137, + Age: 19, + Fare: 26.2833, + Name: 'Newsom, Miss. Helen Monypeny', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '113803', + Parch: 0, + Cabin: 'C123', + PassengerId: 138, + Age: 37, + Fare: 53.1, + Name: 'Futrelle, Mr. Jacques Heath', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '7534', + Parch: 0, + Cabin: null, + PassengerId: 139, + Age: 16, + Fare: 9.2167, + Name: 'Osen, Mr. Olaf Elon', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17593', + Parch: 0, + Cabin: 'B86', + PassengerId: 140, + Age: 24, + Fare: 79.2, + Name: 'Giglio, Mr. Victor', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2678', + Parch: 2, + Cabin: null, + PassengerId: 141, + Age: null, + Fare: 15.2458, + Name: 'Boulos, Mrs. Joseph (Sultana)', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '347081', + Parch: 0, + Cabin: null, + PassengerId: 142, + Age: 22, + Fare: 7.75, + Name: 'Nysten, Miss. Anna Sofia', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'STON/O2. 3101279', + Parch: 0, + Cabin: null, + PassengerId: 143, + Age: 24, + Fare: 15.85, + Name: 'Hakkarainen, Mrs. Pekka Pietari (Elin Matilda Dolck)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '365222', + Parch: 0, + Cabin: null, + PassengerId: 144, + Age: 19, + Fare: 6.75, + Name: 'Burke, Mr. Jeremiah', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '231945', + Parch: 0, + Cabin: null, + PassengerId: 145, + Age: 18, + Fare: 11.5, + Name: 'Andrew, Mr. Edgardo Samuel', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 33112', + Parch: 1, + Cabin: null, + PassengerId: 146, + Age: 19, + Fare: 36.75, + Name: 'Nicholls, Mr. Joseph Charles', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350043', + Parch: 0, + Cabin: null, + PassengerId: 147, + Age: 27, + Fare: 7.7958, + Name: 'Andersson, Mr. August Edvard ("Wennerstrom")', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: 'W./C. 6608', + Parch: 2, + Cabin: null, + PassengerId: 148, + Age: 9, + Fare: 34.375, + Name: 'Ford, Miss. Robina Maggie "Ruby"', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '230080', + Parch: 2, + Cabin: 'F2', + PassengerId: 149, + Age: 36.5, + Fare: 26, + Name: 'Navratil, Mr. Michel ("Louis M Hoffman")', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '244310', + Parch: 0, + Cabin: null, + PassengerId: 150, + Age: 42, + Fare: 13, + Name: 'Byles, Rev. Thomas Roussel Davids', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'S.O.P. 1166', + Parch: 0, + Cabin: null, + PassengerId: 151, + Age: 51, + Fare: 12.525, + Name: 'Bateman, Rev. Robert James', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113776', + Parch: 0, + Cabin: 'C2', + PassengerId: 152, + Age: 22, + Fare: 66.6, + Name: 'Pears, Mrs. Thomas (Edith Wearne)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'A.5. 11206', + Parch: 0, + Cabin: null, + PassengerId: 153, + Age: 55.5, + Fare: 8.05, + Name: 'Meo, Mr. Alfonzo', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/5. 851', + Parch: 2, + Cabin: null, + PassengerId: 154, + Age: 40.5, + Fare: 14.5, + Name: 'van Billiard, Mr. Austin Blyler', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'Fa 265302', + Parch: 0, + Cabin: null, + PassengerId: 155, + Age: null, + Fare: 7.3125, + Name: 'Olsen, Mr. Ole Martin', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17597', + Parch: 1, + Cabin: null, + PassengerId: 156, + Age: 51, + Fare: 61.3792, + Name: 'Williams, Mr. Charles Duane', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '35851', + Parch: 0, + Cabin: null, + PassengerId: 157, + Age: 16, + Fare: 7.7333, + Name: 'Gilnagh, Miss. Katherine "Katie"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'SOTON/OQ 392090', + Parch: 0, + Cabin: null, + PassengerId: 158, + Age: 30, + Fare: 8.05, + Name: 'Corn, Mr. Harry', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '315037', + Parch: 0, + Cabin: null, + PassengerId: 159, + Age: null, + Fare: 8.6625, + Name: 'Smiljanic, Mr. Mile', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 8, + Ticket: 'CA. 2343', + Parch: 2, + Cabin: null, + PassengerId: 160, + Age: null, + Fare: 69.55, + Name: 'Sage, Master. Thomas Henry', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '371362', + Parch: 1, + Cabin: null, + PassengerId: 161, + Age: 44, + Fare: 16.1, + Name: 'Cribb, Mr. John Hatfield', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C.A. 33595', + Parch: 0, + Cabin: null, + PassengerId: 162, + Age: 40, + Fare: 15.75, + Name: 'Watt, Mrs. James (Elizabeth "Bessie" Inglis Milne)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '347068', + Parch: 0, + Cabin: null, + PassengerId: 163, + Age: 26, + Fare: 7.775, + Name: 'Bengtsson, Mr. John Viktor', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '315093', + Parch: 0, + Cabin: null, + PassengerId: 164, + Age: 17, + Fare: 8.6625, + Name: 'Calic, Mr. Jovo', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '3101295', + Parch: 1, + Cabin: null, + PassengerId: 165, + Age: 1, + Fare: 39.6875, + Name: 'Panula, Master. Eino Viljami', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '363291', + Parch: 2, + Cabin: null, + PassengerId: 166, + Age: 9, + Fare: 20.525, + Name: 'Goldsmith, Master. Frank John William "Frankie"', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113505', + Parch: 1, + Cabin: 'E33', + PassengerId: 167, + Age: null, + Fare: 55, + Name: 'Chibnall, Mrs. (Edith Martha Bowerman)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '347088', + Parch: 4, + Cabin: null, + PassengerId: 168, + Age: 45, + Fare: 27.9, + Name: 'Skoog, Mrs. William (Anna Bernhardina Karlsson)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17318', + Parch: 0, + Cabin: null, + PassengerId: 169, + Age: null, + Fare: 25.925, + Name: 'Baumann, Mr. John D', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '1601', + Parch: 0, + Cabin: null, + PassengerId: 170, + Age: 28, + Fare: 56.4958, + Name: 'Ling, Mr. Lee', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '111240', + Parch: 0, + Cabin: 'B19', + PassengerId: 171, + Age: 61, + Fare: 33.5, + Name: 'Van der hoef, Mr. Wyckoff', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '382652', + Parch: 1, + Cabin: null, + PassengerId: 172, + Age: 4, + Fare: 29.125, + Name: 'Rice, Master. Arthur', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '347742', + Parch: 1, + Cabin: null, + PassengerId: 173, + Age: 1, + Fare: 11.1333, + Name: 'Johnson, Miss. Eleanor Ileen', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101280', + Parch: 0, + Cabin: null, + PassengerId: 174, + Age: 21, + Fare: 7.925, + Name: 'Sivola, Mr. Antti Wilhelm', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '17764', + Parch: 0, + Cabin: 'A7', + PassengerId: 175, + Age: 56, + Fare: 30.6958, + Name: 'Smith, Mr. James Clinch', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '350404', + Parch: 1, + Cabin: null, + PassengerId: 176, + Age: 18, + Fare: 7.8542, + Name: 'Klasen, Mr. Klas Albin', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '4133', + Parch: 1, + Cabin: null, + PassengerId: 177, + Age: null, + Fare: 25.4667, + Name: 'Lefebre, Master. Henry Forbes', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17595', + Parch: 0, + Cabin: 'C49', + PassengerId: 178, + Age: 50, + Fare: 28.7125, + Name: 'Isham, Miss. Ann Elizabeth', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '250653', + Parch: 0, + Cabin: null, + PassengerId: 179, + Age: 30, + Fare: 13, + Name: 'Hale, Mr. Reginald', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'LINE', + Parch: 0, + Cabin: null, + PassengerId: 180, + Age: 36, + Fare: 0, + Name: 'Leonard, Mr. Lionel', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 8, + Ticket: 'CA. 2343', + Parch: 2, + Cabin: null, + PassengerId: 181, + Age: null, + Fare: 69.55, + Name: 'Sage, Miss. Constance Gladys', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'SC/PARIS 2131', + Parch: 0, + Cabin: null, + PassengerId: 182, + Age: null, + Fare: 15.05, + Name: 'Pernot, Mr. Rene', + Survived: false, + Pclass: 2, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '347077', + Parch: 2, + Cabin: null, + PassengerId: 183, + Age: 9, + Fare: 31.3875, + Name: 'Asplund, Master. Clarence Gustaf Hugo', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '230136', + Parch: 1, + Cabin: 'F4', + PassengerId: 184, + Age: 1, + Fare: 39, + Name: 'Becker, Master. Richard F', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '315153', + Parch: 2, + Cabin: null, + PassengerId: 185, + Age: 4, + Fare: 22.025, + Name: 'Kink-Heilmann, Miss. Luise Gretchen', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113767', + Parch: 0, + Cabin: 'A32', + PassengerId: 186, + Age: null, + Fare: 50, + Name: 'Rood, Mr. Hugh Roscoe', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '370365', + Parch: 0, + Cabin: null, + PassengerId: 187, + Age: null, + Fare: 15.5, + Name: 'O\'Brien, Mrs. Thomas (Johanna "Hannah" Godfrey)', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '111428', + Parch: 0, + Cabin: null, + PassengerId: 188, + Age: 45, + Fare: 26.55, + Name: 'Romaine, Mr. Charles Hallace ("Mr C Rolmane")', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '364849', + Parch: 1, + Cabin: null, + PassengerId: 189, + Age: 40, + Fare: 15.5, + Name: 'Bourke, Mr. John', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349247', + Parch: 0, + Cabin: null, + PassengerId: 190, + Age: 36, + Fare: 7.8958, + Name: 'Turcin, Mr. Stjepan', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '234604', + Parch: 0, + Cabin: null, + PassengerId: 191, + Age: 32, + Fare: 13, + Name: 'Pinsky, Mrs. (Rosa)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '28424', + Parch: 0, + Cabin: null, + PassengerId: 192, + Age: 19, + Fare: 13, + Name: 'Carbines, Mr. William', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '350046', + Parch: 0, + Cabin: null, + PassengerId: 193, + Age: 19, + Fare: 7.8542, + Name: 'Andersen-Jensen, Miss. Carla Christine Nielsine', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '230080', + Parch: 1, + Cabin: 'F2', + PassengerId: 194, + Age: 3, + Fare: 26, + Name: 'Navratil, Master. Michel M', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17610', + Parch: 0, + Cabin: 'B4', + PassengerId: 195, + Age: 44, + Fare: 27.7208, + Name: 'Brown, Mrs. James Joseph (Margaret Tobin)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17569', + Parch: 0, + Cabin: 'B80', + PassengerId: 196, + Age: 58, + Fare: 146.5208, + Name: 'Lurette, Miss. Elise', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '368703', + Parch: 0, + Cabin: null, + PassengerId: 197, + Age: null, + Fare: 7.75, + Name: 'Mernagh, Mr. Robert', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '4579', + Parch: 1, + Cabin: null, + PassengerId: 198, + Age: 42, + Fare: 8.4042, + Name: 'Olsen, Mr. Karl Siegwart Andreas', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '370370', + Parch: 0, + Cabin: null, + PassengerId: 199, + Age: null, + Fare: 7.75, + Name: 'Madigan, Miss. Margaret "Maggie"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '248747', + Parch: 0, + Cabin: null, + PassengerId: 200, + Age: 24, + Fare: 13, + Name: 'Yrois, Miss. Henriette ("Mrs Harbeck")', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '345770', + Parch: 0, + Cabin: null, + PassengerId: 201, + Age: 28, + Fare: 9.5, + Name: 'Vande Walle, Mr. Nestor Cyriel', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 8, + Ticket: 'CA. 2343', + Parch: 2, + Cabin: null, + PassengerId: 202, + Age: null, + Fare: 69.55, + Name: 'Sage, Mr. Frederick', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '3101264', + Parch: 0, + Cabin: null, + PassengerId: 203, + Age: 34, + Fare: 6.4958, + Name: 'Johanson, Mr. Jakob Alfred', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2628', + Parch: 0, + Cabin: null, + PassengerId: 204, + Age: 45.5, + Fare: 7.225, + Name: 'Youseff, Mr. Gerious', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/5 3540', + Parch: 0, + Cabin: null, + PassengerId: 205, + Age: 18, + Fare: 8.05, + Name: 'Cohen, Mr. Gurshon "Gus"', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347054', + Parch: 1, + Cabin: 'G6', + PassengerId: 206, + Age: 2, + Fare: 10.4625, + Name: 'Strom, Miss. Telma Matilda', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '3101278', + Parch: 0, + Cabin: null, + PassengerId: 207, + Age: 32, + Fare: 15.85, + Name: 'Backstrom, Mr. Karl Alfred', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2699', + Parch: 0, + Cabin: null, + PassengerId: 208, + Age: 26, + Fare: 18.7875, + Name: 'Albimona, Mr. Nassef Cassem', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '367231', + Parch: 0, + Cabin: null, + PassengerId: 209, + Age: 16, + Fare: 7.75, + Name: 'Carr, Miss. Helen "Ellen"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '112277', + Parch: 0, + Cabin: 'A31', + PassengerId: 210, + Age: 40, + Fare: 31, + Name: 'Blank, Mr. Henry', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/O.Q. 3101311', + Parch: 0, + Cabin: null, + PassengerId: 211, + Age: 24, + Fare: 7.05, + Name: 'Ali, Mr. Ahmed', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'F.C.C. 13528', + Parch: 0, + Cabin: null, + PassengerId: 212, + Age: 35, + Fare: 21, + Name: 'Cameron, Miss. Clear Annie', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'A/5 21174', + Parch: 0, + Cabin: null, + PassengerId: 213, + Age: 22, + Fare: 7.25, + Name: 'Perkin, Mr. John Henry', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '250646', + Parch: 0, + Cabin: null, + PassengerId: 214, + Age: 30, + Fare: 13, + Name: 'Givard, Mr. Hans Kristensen', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '367229', + Parch: 0, + Cabin: null, + PassengerId: 215, + Age: null, + Fare: 7.75, + Name: 'Kiernan, Mr. Philip', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '35273', + Parch: 0, + Cabin: 'D36', + PassengerId: 216, + Age: 31, + Fare: 113.275, + Name: 'Newell, Miss. Madeleine', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'STON/O2. 3101283', + Parch: 0, + Cabin: null, + PassengerId: 217, + Age: 27, + Fare: 7.925, + Name: 'Honkanen, Miss. Eliina', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '243847', + Parch: 0, + Cabin: null, + PassengerId: 218, + Age: 42, + Fare: 27, + Name: 'Jacobsohn, Mr. Sidney Samuel', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '11813', + Parch: 0, + Cabin: 'D15', + PassengerId: 219, + Age: 32, + Fare: 76.2917, + Name: 'Bazzani, Miss. Albina', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'W/C 14208', + Parch: 0, + Cabin: null, + PassengerId: 220, + Age: 30, + Fare: 10.5, + Name: 'Harris, Mr. Walter', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/OQ 392089', + Parch: 0, + Cabin: null, + PassengerId: 221, + Age: 16, + Fare: 8.05, + Name: 'Sunderland, Mr. Victor Francis', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '220367', + Parch: 0, + Cabin: null, + PassengerId: 222, + Age: 27, + Fare: 13, + Name: 'Bracken, Mr. James H', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '21440', + Parch: 0, + Cabin: null, + PassengerId: 223, + Age: 51, + Fare: 8.05, + Name: 'Green, Mr. George Henry', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349234', + Parch: 0, + Cabin: null, + PassengerId: 224, + Age: null, + Fare: 7.8958, + Name: 'Nenkoff, Mr. Christo', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '19943', + Parch: 0, + Cabin: 'C93', + PassengerId: 225, + Age: 38, + Fare: 90, + Name: 'Hoyt, Mr. Frederick Maxfield', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PP 4348', + Parch: 0, + Cabin: null, + PassengerId: 226, + Age: 22, + Fare: 9.35, + Name: 'Berglund, Mr. Karl Ivar Sven', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SW/PP 751', + Parch: 0, + Cabin: null, + PassengerId: 227, + Age: 19, + Fare: 10.5, + Name: 'Mellors, Mr. William John', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/5 21173', + Parch: 0, + Cabin: null, + PassengerId: 228, + Age: 20.5, + Fare: 7.25, + Name: 'Lovell, Mr. John Hall ("Henry")', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '236171', + Parch: 0, + Cabin: null, + PassengerId: 229, + Age: 18, + Fare: 13, + Name: 'Fahlstrom, Mr. Arne Jonas', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '4133', + Parch: 1, + Cabin: null, + PassengerId: 230, + Age: null, + Fare: 25.4667, + Name: 'Lefebre, Miss. Mathilde', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '36973', + Parch: 0, + Cabin: 'C83', + PassengerId: 231, + Age: 35, + Fare: 83.475, + Name: 'Harris, Mrs. Henry Birkhardt (Irene Wallach)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '347067', + Parch: 0, + Cabin: null, + PassengerId: 232, + Age: 29, + Fare: 7.775, + Name: 'Larsson, Mr. Bengt Edvin', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '237442', + Parch: 0, + Cabin: null, + PassengerId: 233, + Age: 59, + Fare: 13.5, + Name: 'Sjostedt, Mr. Ernst Adolf', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '347077', + Parch: 2, + Cabin: null, + PassengerId: 234, + Age: 5, + Fare: 31.3875, + Name: 'Asplund, Miss. Lillian Gertrud', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'C.A. 29566', + Parch: 0, + Cabin: null, + PassengerId: 235, + Age: 24, + Fare: 10.5, + Name: 'Leyson, Mr. Robert William Norman', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'W./C. 6609', + Parch: 0, + Cabin: null, + PassengerId: 236, + Age: null, + Fare: 7.55, + Name: 'Harknett, Miss. Alice Phoebe', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '26707', + Parch: 0, + Cabin: null, + PassengerId: 237, + Age: 44, + Fare: 26, + Name: 'Hold, Mr. Stephen', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C.A. 31921', + Parch: 2, + Cabin: null, + PassengerId: 238, + Age: 8, + Fare: 26.25, + Name: 'Collyer, Miss. Marjorie "Lottie"', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '28665', + Parch: 0, + Cabin: null, + PassengerId: 239, + Age: 19, + Fare: 10.5, + Name: 'Pengelly, Mr. Frederick William', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SCO/W 1585', + Parch: 0, + Cabin: null, + PassengerId: 240, + Age: 33, + Fare: 12.275, + Name: 'Hunt, Mr. George Henry', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2665', + Parch: 0, + Cabin: null, + PassengerId: 241, + Age: null, + Fare: 14.4542, + Name: 'Zabour, Miss. Thamine', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '367230', + Parch: 0, + Cabin: null, + PassengerId: 242, + Age: null, + Fare: 15.5, + Name: 'Murphy, Miss. Katherine "Kate"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'W./C. 14263', + Parch: 0, + Cabin: null, + PassengerId: 243, + Age: 29, + Fare: 10.5, + Name: 'Coleridge, Mr. Reginald Charles', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101275', + Parch: 0, + Cabin: null, + PassengerId: 244, + Age: 22, + Fare: 7.125, + Name: 'Maenpaa, Mr. Matti Alexanteri', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2694', + Parch: 0, + Cabin: null, + PassengerId: 245, + Age: 30, + Fare: 7.225, + Name: 'Attalah, Mr. Sleiman', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '19928', + Parch: 0, + Cabin: 'C78', + PassengerId: 246, + Age: 44, + Fare: 90, + Name: 'Minahan, Dr. William Edward', + Survived: false, + Pclass: 1, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347071', + Parch: 0, + Cabin: null, + PassengerId: 247, + Age: 25, + Fare: 7.775, + Name: 'Lindahl, Miss. Agda Thorilda Viktoria', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '250649', + Parch: 2, + Cabin: null, + PassengerId: 248, + Age: 24, + Fare: 14.5, + Name: 'Hamalainen, Mrs. William (Anna)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '11751', + Parch: 1, + Cabin: 'D35', + PassengerId: 249, + Age: 37, + Fare: 52.5542, + Name: 'Beckwith, Mr. Richard Leonard', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '244252', + Parch: 0, + Cabin: null, + PassengerId: 250, + Age: 54, + Fare: 26, + Name: 'Carter, Rev. Ernest Courtenay', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '362316', + Parch: 0, + Cabin: null, + PassengerId: 251, + Age: null, + Fare: 7.25, + Name: 'Reed, Mr. James George', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '347054', + Parch: 1, + Cabin: 'G6', + PassengerId: 252, + Age: 29, + Fare: 10.4625, + Name: 'Strom, Mrs. Wilhelm (Elna Matilda Persson)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113514', + Parch: 0, + Cabin: 'C87', + PassengerId: 253, + Age: 62, + Fare: 26.55, + Name: 'Stead, Mr. William Thomas', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'A/5. 3336', + Parch: 0, + Cabin: null, + PassengerId: 254, + Age: 30, + Fare: 16.1, + Name: 'Lobb, Mr. William Arthur', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '370129', + Parch: 2, + Cabin: null, + PassengerId: 255, + Age: 41, + Fare: 20.2125, + Name: 'Rosblom, Mrs. Viktor (Helena Wilhelmina)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2650', + Parch: 2, + Cabin: null, + PassengerId: 256, + Age: 29, + Fare: 15.2458, + Name: 'Touma, Mrs. Darwis (Hanne Youssef Razi)', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17585', + Parch: 0, + Cabin: null, + PassengerId: 257, + Age: null, + Fare: 79.2, + Name: 'Thorne, Mrs. Gertrude Maybelle', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '110152', + Parch: 0, + Cabin: 'B77', + PassengerId: 258, + Age: 30, + Fare: 86.5, + Name: 'Cherry, Miss. Gladys', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17755', + Parch: 0, + Cabin: null, + PassengerId: 259, + Age: 35, + Fare: 512.3292, + Name: 'Ward, Miss. Anna', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '230433', + Parch: 1, + Cabin: null, + PassengerId: 260, + Age: 50, + Fare: 26, + Name: 'Parrish, Mrs. (Lutie Davis)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '384461', + Parch: 0, + Cabin: null, + PassengerId: 261, + Age: null, + Fare: 7.75, + Name: 'Smith, Mr. Thomas', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '347077', + Parch: 2, + Cabin: null, + PassengerId: 262, + Age: 3, + Fare: 31.3875, + Name: 'Asplund, Master. Edvin Rojj Felix', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '110413', + Parch: 1, + Cabin: 'E67', + PassengerId: 263, + Age: 52, + Fare: 79.65, + Name: 'Taussig, Mr. Emil', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '112059', + Parch: 0, + Cabin: 'B94', + PassengerId: 264, + Age: 40, + Fare: 0, + Name: 'Harrison, Mr. William', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '382649', + Parch: 0, + Cabin: null, + PassengerId: 265, + Age: null, + Fare: 7.75, + Name: 'Henry, Miss. Delia', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'C.A. 17248', + Parch: 0, + Cabin: null, + PassengerId: 266, + Age: 36, + Fare: 10.5, + Name: 'Reeves, Mr. David', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '3101295', + Parch: 1, + Cabin: null, + PassengerId: 267, + Age: 16, + Fare: 39.6875, + Name: 'Panula, Mr. Ernesti Arvid', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '347083', + Parch: 0, + Cabin: null, + PassengerId: 268, + Age: 25, + Fare: 7.775, + Name: 'Persson, Mr. Ernst Ulrik', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17582', + Parch: 1, + Cabin: 'C125', + PassengerId: 269, + Age: 58, + Fare: 153.4625, + Name: 'Graham, Mrs. William Thompson (Edith Junkins)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17760', + Parch: 0, + Cabin: 'C99', + PassengerId: 270, + Age: 35, + Fare: 135.6333, + Name: 'Bissette, Miss. Amelia', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113798', + Parch: 0, + Cabin: null, + PassengerId: 271, + Age: null, + Fare: 31, + Name: 'Cairns, Mr. Alexander', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'LINE', + Parch: 0, + Cabin: null, + PassengerId: 272, + Age: 25, + Fare: 0, + Name: 'Tornquist, Mr. William Henry', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '250644', + Parch: 1, + Cabin: null, + PassengerId: 273, + Age: 41, + Fare: 19.5, + Name: 'Mellinger, Mrs. (Elizabeth Anne Maidment)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17596', + Parch: 1, + Cabin: 'C118', + PassengerId: 274, + Age: 37, + Fare: 29.7, + Name: 'Natsch, Mr. Charles H', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '370375', + Parch: 0, + Cabin: null, + PassengerId: 275, + Age: null, + Fare: 7.75, + Name: 'Healy, Miss. Hanora "Nora"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '13502', + Parch: 0, + Cabin: 'D7', + PassengerId: 276, + Age: 63, + Fare: 77.9583, + Name: 'Andrews, Miss. Kornelia Theodosia', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '347073', + Parch: 0, + Cabin: null, + PassengerId: 277, + Age: 45, + Fare: 7.75, + Name: 'Lindblom, Miss. Augusta Charlotta', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '239853', + Parch: 0, + Cabin: null, + PassengerId: 278, + Age: null, + Fare: 0, + Name: 'Parkes, Mr. Francis "Frank"', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '382652', + Parch: 1, + Cabin: null, + PassengerId: 279, + Age: 7, + Fare: 29.125, + Name: 'Rice, Master. Eric', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 2673', + Parch: 1, + Cabin: null, + PassengerId: 280, + Age: 35, + Fare: 20.25, + Name: 'Abbott, Mrs. Stanton (Rosa Hunt)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '336439', + Parch: 0, + Cabin: null, + PassengerId: 281, + Age: 65, + Fare: 7.75, + Name: 'Duane, Mr. Frank', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347464', + Parch: 0, + Cabin: null, + PassengerId: 282, + Age: 28, + Fare: 7.8542, + Name: 'Olsson, Mr. Nils Johan Goransson', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '345778', + Parch: 0, + Cabin: null, + PassengerId: 283, + Age: 16, + Fare: 9.5, + Name: 'de Pelsmaeker, Mr. Alfons', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/5. 10482', + Parch: 0, + Cabin: null, + PassengerId: 284, + Age: 19, + Fare: 8.05, + Name: 'Dorking, Mr. Edward Arthur', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113056', + Parch: 0, + Cabin: 'A19', + PassengerId: 285, + Age: null, + Fare: 26, + Name: 'Smith, Mr. Richard William', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349239', + Parch: 0, + Cabin: null, + PassengerId: 286, + Age: 33, + Fare: 8.6625, + Name: 'Stankovic, Mr. Ivan', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '345774', + Parch: 0, + Cabin: null, + PassengerId: 287, + Age: 30, + Fare: 9.5, + Name: 'de Mulder, Mr. Theodore', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349206', + Parch: 0, + Cabin: null, + PassengerId: 288, + Age: 22, + Fare: 7.8958, + Name: 'Naidenoff, Mr. Penko', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '237798', + Parch: 0, + Cabin: null, + PassengerId: 289, + Age: 42, + Fare: 13, + Name: 'Hosono, Mr. Masabumi', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '370373', + Parch: 0, + Cabin: null, + PassengerId: 290, + Age: 22, + Fare: 7.75, + Name: 'Connolly, Miss. Kate', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '19877', + Parch: 0, + Cabin: null, + PassengerId: 291, + Age: 26, + Fare: 78.85, + Name: 'Barber, Miss. Ellen "Nellie"', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '11967', + Parch: 0, + Cabin: 'B49', + PassengerId: 292, + Age: 19, + Fare: 91.0792, + Name: 'Bishop, Mrs. Dickinson H (Helen Walton)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'SC/Paris 2163', + Parch: 0, + Cabin: 'D', + PassengerId: 293, + Age: 36, + Fare: 12.875, + Name: 'Levy, Mr. Rene Jacques', + Survived: false, + Pclass: 2, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349236', + Parch: 0, + Cabin: null, + PassengerId: 294, + Age: 24, + Fare: 8.85, + Name: 'Haas, Miss. Aloisia', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349233', + Parch: 0, + Cabin: null, + PassengerId: 295, + Age: 24, + Fare: 7.8958, + Name: 'Mineff, Mr. Ivan', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17612', + Parch: 0, + Cabin: null, + PassengerId: 296, + Age: null, + Fare: 27.7208, + Name: 'Lewy, Mr. Ervin G', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2693', + Parch: 0, + Cabin: null, + PassengerId: 297, + Age: 23.5, + Fare: 7.2292, + Name: 'Hanna, Mr. Mansour', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113781', + Parch: 2, + Cabin: 'C22 C26', + PassengerId: 298, + Age: 2, + Fare: 151.55, + Name: 'Allison, Miss. Helen Loraine', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '19988', + Parch: 0, + Cabin: 'C106', + PassengerId: 299, + Age: null, + Fare: 30.5, + Name: 'Saalfeld, Mr. Adolphe', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17558', + Parch: 1, + Cabin: 'B58 B60', + PassengerId: 300, + Age: 50, + Fare: 247.5208, + Name: 'Baxter, Mrs. James (Helene DeLaudeniere Chaput)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '9234', + Parch: 0, + Cabin: null, + PassengerId: 301, + Age: null, + Fare: 7.75, + Name: 'Kelly, Miss. Anna Katherine "Annie Kate"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: '367226', + Parch: 0, + Cabin: null, + PassengerId: 302, + Age: null, + Fare: 23.25, + Name: 'McCoy, Mr. Bernard', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'LINE', + Parch: 0, + Cabin: null, + PassengerId: 303, + Age: 19, + Fare: 0, + Name: 'Johnson, Mr. William Cahoone Jr', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '226593', + Parch: 0, + Cabin: 'E101', + PassengerId: 304, + Age: null, + Fare: 12.35, + Name: 'Keane, Miss. Nora A', + Survived: true, + Pclass: 2, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'A/5 2466', + Parch: 0, + Cabin: null, + PassengerId: 305, + Age: null, + Fare: 8.05, + Name: 'Williams, Mr. Howard Hugh "Harry"', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113781', + Parch: 2, + Cabin: 'C22 C26', + PassengerId: 306, + Age: 0.92, + Fare: 151.55, + Name: 'Allison, Master. Hudson Trevor', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '17421', + Parch: 0, + Cabin: null, + PassengerId: 307, + Age: null, + Fare: 110.8833, + Name: 'Fleming, Miss. Margaret', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'PC 17758', + Parch: 0, + Cabin: 'C65', + PassengerId: 308, + Age: 17, + Fare: 108.9, + Name: 'Penasco y Castellana, Mrs. Victor de Satode (Maria Josefa Perez de Soto y Vallejo)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'P/PP 3381', + Parch: 0, + Cabin: null, + PassengerId: 309, + Age: 30, + Fare: 24, + Name: 'Abelson, Mr. Samuel', + Survived: false, + Pclass: 2, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17485', + Parch: 0, + Cabin: 'E36', + PassengerId: 310, + Age: 30, + Fare: 56.9292, + Name: 'Francatelli, Miss. Laura Mabel', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '11767', + Parch: 0, + Cabin: 'C54', + PassengerId: 311, + Age: 24, + Fare: 83.1583, + Name: 'Hays, Miss. Margaret Bechstein', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: 'PC 17608', + Parch: 2, + Cabin: 'B57 B59 B63 B66', + PassengerId: 312, + Age: 18, + Fare: 262.375, + Name: 'Ryerson, Miss. Emily Borie', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '250651', + Parch: 1, + Cabin: null, + PassengerId: 313, + Age: 26, + Fare: 26, + Name: 'Lahtinen, Mrs. William (Anna Sylfven)', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349243', + Parch: 0, + Cabin: null, + PassengerId: 314, + Age: 28, + Fare: 7.8958, + Name: 'Hendekovic, Mr. Ignjac', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'F.C.C. 13529', + Parch: 1, + Cabin: null, + PassengerId: 315, + Age: 43, + Fare: 26.25, + Name: 'Hart, Mr. Benjamin', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347470', + Parch: 0, + Cabin: null, + PassengerId: 316, + Age: 26, + Fare: 7.8542, + Name: 'Nilsson, Miss. Helmina Josefina', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '244367', + Parch: 0, + Cabin: null, + PassengerId: 317, + Age: 24, + Fare: 26, + Name: 'Kantor, Mrs. Sinai (Miriam Sternin)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '29011', + Parch: 0, + Cabin: null, + PassengerId: 318, + Age: 54, + Fare: 14, + Name: 'Moraweck, Dr. Ernest', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '36928', + Parch: 2, + Cabin: 'C7', + PassengerId: 319, + Age: 31, + Fare: 164.8667, + Name: 'Wick, Miss. Mary Natalie', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '16966', + Parch: 1, + Cabin: 'E34', + PassengerId: 320, + Age: 40, + Fare: 134.5, + Name: 'Spedden, Mrs. Frederic Oakley (Margaretta Corning Stone)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'A/5 21172', + Parch: 0, + Cabin: null, + PassengerId: 321, + Age: 22, + Fare: 7.25, + Name: 'Dennis, Mr. Samuel', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349219', + Parch: 0, + Cabin: null, + PassengerId: 322, + Age: 27, + Fare: 7.8958, + Name: 'Danoff, Mr. Yoto', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '234818', + Parch: 0, + Cabin: null, + PassengerId: 323, + Age: 30, + Fare: 12.35, + Name: 'Slayter, Miss. Hilda Mary', + Survived: true, + Pclass: 2, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '248738', + Parch: 1, + Cabin: null, + PassengerId: 324, + Age: 22, + Fare: 29, + Name: 'Caldwell, Mrs. Albert Francis (Sylvia Mae Harbaugh)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 8, + Ticket: 'CA. 2343', + Parch: 2, + Cabin: null, + PassengerId: 325, + Age: null, + Fare: 69.55, + Name: 'Sage, Mr. George John Jr', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17760', + Parch: 0, + Cabin: 'C32', + PassengerId: 326, + Age: 36, + Fare: 135.6333, + Name: 'Young, Miss. Marie Grice', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '345364', + Parch: 0, + Cabin: null, + PassengerId: 327, + Age: 61, + Fare: 6.2375, + Name: 'Nysveen, Mr. Johan Hansen', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '28551', + Parch: 0, + Cabin: 'D', + PassengerId: 328, + Age: 36, + Fare: 13, + Name: 'Ball, Mrs. (Ada E Hall)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '363291', + Parch: 1, + Cabin: null, + PassengerId: 329, + Age: 31, + Fare: 20.525, + Name: 'Goldsmith, Mrs. Frank John (Emily Alice Brown)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '111361', + Parch: 1, + Cabin: 'B18', + PassengerId: 330, + Age: 16, + Fare: 57.9792, + Name: 'Hippach, Miss. Jean Gertrude', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: '367226', + Parch: 0, + Cabin: null, + PassengerId: 331, + Age: null, + Fare: 23.25, + Name: 'McCoy, Miss. Agnes', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113043', + Parch: 0, + Cabin: 'C124', + PassengerId: 332, + Age: 45.5, + Fare: 28.5, + Name: 'Partner, Mr. Austen', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17582', + Parch: 1, + Cabin: 'C91', + PassengerId: 333, + Age: 38, + Fare: 153.4625, + Name: 'Graham, Mr. George Edward', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '345764', + Parch: 0, + Cabin: null, + PassengerId: 334, + Age: 16, + Fare: 18, + Name: 'Vander Planke, Mr. Leo Edmondus', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'PC 17611', + Parch: 0, + Cabin: null, + PassengerId: 335, + Age: null, + Fare: 133.65, + Name: 'Frauenthal, Mrs. Henry William (Clara Heinsheimer)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349225', + Parch: 0, + Cabin: null, + PassengerId: 336, + Age: null, + Fare: 7.8958, + Name: 'Denkoff, Mr. Mitto', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113776', + Parch: 0, + Cabin: 'C2', + PassengerId: 337, + Age: 29, + Fare: 66.6, + Name: 'Pears, Mr. Thomas Clinton', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '16966', + Parch: 0, + Cabin: 'E40', + PassengerId: 338, + Age: 41, + Fare: 134.5, + Name: 'Burns, Miss. Elizabeth Margaret', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '7598', + Parch: 0, + Cabin: null, + PassengerId: 339, + Age: 45, + Fare: 8.05, + Name: 'Dahl, Mr. Karl Edwart', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113784', + Parch: 0, + Cabin: 'T', + PassengerId: 340, + Age: 45, + Fare: 35.5, + Name: 'Blackwell, Mr. Stephen Weart', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '230080', + Parch: 1, + Cabin: 'F2', + PassengerId: 341, + Age: 2, + Fare: 26, + Name: 'Navratil, Master. Edmond Roger', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '19950', + Parch: 2, + Cabin: 'C23 C25 C27', + PassengerId: 342, + Age: 24, + Fare: 263, + Name: 'Fortune, Miss. Alice Elizabeth', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '248740', + Parch: 0, + Cabin: null, + PassengerId: 343, + Age: 28, + Fare: 13, + Name: 'Collander, Mr. Erik Gustaf', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '244361', + Parch: 0, + Cabin: null, + PassengerId: 344, + Age: 25, + Fare: 13, + Name: 'Sedgwick, Mr. Charles Frederick Waddington', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '229236', + Parch: 0, + Cabin: null, + PassengerId: 345, + Age: 36, + Fare: 13, + Name: 'Fox, Mr. Stanley Hubert', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '248733', + Parch: 0, + Cabin: 'F33', + PassengerId: 346, + Age: 24, + Fare: 13, + Name: 'Brown, Miss. Amelia "Mildred"', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '31418', + Parch: 0, + Cabin: null, + PassengerId: 347, + Age: 40, + Fare: 13, + Name: 'Smith, Miss. Marion Elsie', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '386525', + Parch: 0, + Cabin: null, + PassengerId: 348, + Age: null, + Fare: 16.1, + Name: 'Davison, Mrs. Thomas Henry (Mary E Finck)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'C.A. 37671', + Parch: 1, + Cabin: null, + PassengerId: 349, + Age: 3, + Fare: 15.9, + Name: 'Coutts, Master. William Loch "William"', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '315088', + Parch: 0, + Cabin: null, + PassengerId: 350, + Age: 42, + Fare: 8.6625, + Name: 'Dimic, Mr. Jovan', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '7267', + Parch: 0, + Cabin: null, + PassengerId: 351, + Age: 23, + Fare: 9.225, + Name: 'Odahl, Mr. Nils Martin', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113510', + Parch: 0, + Cabin: 'C128', + PassengerId: 352, + Age: null, + Fare: 35, + Name: 'Williams-Lambert, Mr. Fletcher Fellows', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2695', + Parch: 1, + Cabin: null, + PassengerId: 353, + Age: 15, + Fare: 7.2292, + Name: 'Elias, Mr. Tannous', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '349237', + Parch: 0, + Cabin: null, + PassengerId: 354, + Age: 25, + Fare: 17.8, + Name: 'Arnold-Franchi, Mr. Josef', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2647', + Parch: 0, + Cabin: null, + PassengerId: 355, + Age: null, + Fare: 7.225, + Name: 'Yousif, Mr. Wazli', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '345783', + Parch: 0, + Cabin: null, + PassengerId: 356, + Age: 28, + Fare: 9.5, + Name: 'Vanden Steen, Mr. Leo Peter', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113505', + Parch: 1, + Cabin: 'E33', + PassengerId: 357, + Age: 22, + Fare: 55, + Name: 'Bowerman, Miss. Elsie Edith', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '237671', + Parch: 0, + Cabin: null, + PassengerId: 358, + Age: 38, + Fare: 13, + Name: 'Funk, Miss. Annie Clemmer', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '330931', + Parch: 0, + Cabin: null, + PassengerId: 359, + Age: null, + Fare: 7.8792, + Name: 'McGovern, Miss. Mary', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '330980', + Parch: 0, + Cabin: null, + PassengerId: 360, + Age: null, + Fare: 7.8792, + Name: 'Mockler, Miss. Helen Mary "Ellie"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '347088', + Parch: 4, + Cabin: null, + PassengerId: 361, + Age: 40, + Fare: 27.9, + Name: 'Skoog, Mr. Wilhelm', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'SC/PARIS 2167', + Parch: 0, + Cabin: null, + PassengerId: 362, + Age: 29, + Fare: 27.7208, + Name: 'del Carlo, Mr. Sebastiano', + Survived: false, + Pclass: 2, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2691', + Parch: 1, + Cabin: null, + PassengerId: 363, + Age: 45, + Fare: 14.4542, + Name: 'Barbara, Mrs. (Catherine David)', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'SOTON/O.Q. 3101310', + Parch: 0, + Cabin: null, + PassengerId: 364, + Age: 35, + Fare: 7.05, + Name: 'Asim, Mr. Adola', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '370365', + Parch: 0, + Cabin: null, + PassengerId: 365, + Age: null, + Fare: 15.5, + Name: "O'Brien, Mr. Thomas", + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C 7076', + Parch: 0, + Cabin: null, + PassengerId: 366, + Age: 30, + Fare: 7.25, + Name: 'Adahl, Mr. Mauritz Nils Martin', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '110813', + Parch: 0, + Cabin: 'D37', + PassengerId: 367, + Age: 60, + Fare: 75.25, + Name: 'Warren, Mrs. Frank Manley (Anna Sophia Atkinson)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2626', + Parch: 0, + Cabin: null, + PassengerId: 368, + Age: null, + Fare: 7.2292, + Name: 'Moussa, Mrs. (Mantoura Boulos)', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '14313', + Parch: 0, + Cabin: null, + PassengerId: 369, + Age: null, + Fare: 7.75, + Name: 'Jermyn, Miss. Annie', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17477', + Parch: 0, + Cabin: 'B35', + PassengerId: 370, + Age: 24, + Fare: 69.3, + Name: 'Aubart, Mme. Leontine Pauline', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '11765', + Parch: 0, + Cabin: 'E50', + PassengerId: 371, + Age: 25, + Fare: 55.4417, + Name: 'Harder, Mr. George Achilles', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '3101267', + Parch: 0, + Cabin: null, + PassengerId: 372, + Age: 18, + Fare: 6.4958, + Name: 'Wiklund, Mr. Jakob Alfred', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '323951', + Parch: 0, + Cabin: null, + PassengerId: 373, + Age: 19, + Fare: 8.05, + Name: 'Beavan, Mr. William Thomas', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17760', + Parch: 0, + Cabin: null, + PassengerId: 374, + Age: 22, + Fare: 135.6333, + Name: 'Ringhini, Mr. Sante', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '349909', + Parch: 1, + Cabin: null, + PassengerId: 375, + Age: 3, + Fare: 21.075, + Name: 'Palsson, Miss. Stina Viola', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'PC 17604', + Parch: 0, + Cabin: null, + PassengerId: 376, + Age: null, + Fare: 82.1708, + Name: 'Meyer, Mrs. Edgar Joseph (Leila Saks)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'C 7077', + Parch: 0, + Cabin: null, + PassengerId: 377, + Age: 22, + Fare: 7.25, + Name: 'Landergren, Miss. Aurora Adelia', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113503', + Parch: 2, + Cabin: 'C82', + PassengerId: 378, + Age: 27, + Fare: 211.5, + Name: 'Widener, Mr. Harry Elkins', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2648', + Parch: 0, + Cabin: null, + PassengerId: 379, + Age: 20, + Fare: 4.0125, + Name: 'Betros, Mr. Tannous', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347069', + Parch: 0, + Cabin: null, + PassengerId: 380, + Age: 19, + Fare: 7.775, + Name: 'Gustafsson, Mr. Karl Gideon', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17757', + Parch: 0, + Cabin: null, + PassengerId: 381, + Age: 42, + Fare: 227.525, + Name: 'Bidois, Miss. Rosalie', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2653', + Parch: 2, + Cabin: null, + PassengerId: 382, + Age: 1, + Fare: 15.7417, + Name: 'Nakid, Miss. Maria ("Mary")', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101293', + Parch: 0, + Cabin: null, + PassengerId: 383, + Age: 32, + Fare: 7.925, + Name: 'Tikkanen, Mr. Juho', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113789', + Parch: 0, + Cabin: null, + PassengerId: 384, + Age: 35, + Fare: 52, + Name: 'Holverson, Mrs. Alexander Oskar (Mary Aline Towner)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349227', + Parch: 0, + Cabin: null, + PassengerId: 385, + Age: null, + Fare: 7.8958, + Name: 'Plotcharsky, Mr. Vasil', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'S.O.C. 14879', + Parch: 0, + Cabin: null, + PassengerId: 386, + Age: 18, + Fare: 73.5, + Name: 'Davies, Mr. Charles Henry', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 5, + Ticket: 'CA 2144', + Parch: 2, + Cabin: null, + PassengerId: 387, + Age: 1, + Fare: 46.9, + Name: 'Goodwin, Master. Sidney Leonard', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '27849', + Parch: 0, + Cabin: null, + PassengerId: 388, + Age: 36, + Fare: 13, + Name: 'Buss, Miss. Kate', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '367655', + Parch: 0, + Cabin: null, + PassengerId: 389, + Age: null, + Fare: 7.7292, + Name: 'Sadlier, Mr. Matthew', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SC 1748', + Parch: 0, + Cabin: null, + PassengerId: 390, + Age: 17, + Fare: 12, + Name: 'Lehmann, Miss. Bertha', + Survived: true, + Pclass: 2, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '113760', + Parch: 2, + Cabin: 'B96 B98', + PassengerId: 391, + Age: 36, + Fare: 120, + Name: 'Carter, Mr. William Ernest', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350034', + Parch: 0, + Cabin: null, + PassengerId: 392, + Age: 21, + Fare: 7.7958, + Name: 'Jansson, Mr. Carl Olof', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '3101277', + Parch: 0, + Cabin: null, + PassengerId: 393, + Age: 28, + Fare: 7.925, + Name: 'Gustafsson, Mr. Johan Birger', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '35273', + Parch: 0, + Cabin: 'D36', + PassengerId: 394, + Age: 23, + Fare: 113.275, + Name: 'Newell, Miss. Marjorie', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PP 9549', + Parch: 2, + Cabin: 'G6', + PassengerId: 395, + Age: 24, + Fare: 16.7, + Name: 'Sandstrom, Mrs. Hjalmar (Agnes Charlotta Bengtsson)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '350052', + Parch: 0, + Cabin: null, + PassengerId: 396, + Age: 22, + Fare: 7.7958, + Name: 'Johansson, Mr. Erik', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350407', + Parch: 0, + Cabin: null, + PassengerId: 397, + Age: 31, + Fare: 7.8542, + Name: 'Olsson, Miss. Elina', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '28403', + Parch: 0, + Cabin: null, + PassengerId: 398, + Age: 46, + Fare: 26, + Name: 'McKane, Mr. Peter David', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '244278', + Parch: 0, + Cabin: null, + PassengerId: 399, + Age: 23, + Fare: 10.5, + Name: 'Pain, Dr. Alfred', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '240929', + Parch: 0, + Cabin: null, + PassengerId: 400, + Age: 28, + Fare: 12.65, + Name: 'Trout, Mrs. William H (Jessie L)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101289', + Parch: 0, + Cabin: null, + PassengerId: 401, + Age: 39, + Fare: 7.925, + Name: 'Niskanen, Mr. Juha', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '341826', + Parch: 0, + Cabin: null, + PassengerId: 402, + Age: 26, + Fare: 8.05, + Name: 'Adams, Mr. John', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '4137', + Parch: 0, + Cabin: null, + PassengerId: 403, + Age: 21, + Fare: 9.825, + Name: 'Jussila, Miss. Mari Aina', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'STON/O2. 3101279', + Parch: 0, + Cabin: null, + PassengerId: 404, + Age: 28, + Fare: 15.85, + Name: 'Hakkarainen, Mr. Pekka Pietari', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '315096', + Parch: 0, + Cabin: null, + PassengerId: 405, + Age: 20, + Fare: 8.6625, + Name: 'Oreskovic, Miss. Marija', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '28664', + Parch: 0, + Cabin: null, + PassengerId: 406, + Age: 34, + Fare: 21, + Name: 'Gale, Mr. Shadrach', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347064', + Parch: 0, + Cabin: null, + PassengerId: 407, + Age: 51, + Fare: 7.75, + Name: 'Widegren, Mr. Carl/Charles Peter', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '29106', + Parch: 1, + Cabin: null, + PassengerId: 408, + Age: 3, + Fare: 18.75, + Name: 'Richards, Master. William Rowe', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '312992', + Parch: 0, + Cabin: null, + PassengerId: 409, + Age: 21, + Fare: 7.775, + Name: 'Birkeland, Mr. Hans Martin Monsen', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '4133', + Parch: 1, + Cabin: null, + PassengerId: 410, + Age: null, + Fare: 25.4667, + Name: 'Lefebre, Miss. Ida', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349222', + Parch: 0, + Cabin: null, + PassengerId: 411, + Age: null, + Fare: 7.8958, + Name: 'Sdycoff, Mr. Todor', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '394140', + Parch: 0, + Cabin: null, + PassengerId: 412, + Age: null, + Fare: 6.8583, + Name: 'Hart, Mr. Henry', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '19928', + Parch: 0, + Cabin: 'C78', + PassengerId: 413, + Age: 33, + Fare: 90, + Name: 'Minahan, Miss. Daisy E', + Survived: true, + Pclass: 1, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '239853', + Parch: 0, + Cabin: null, + PassengerId: 414, + Age: null, + Fare: 0, + Name: 'Cunningham, Mr. Alfred Fleming', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101269', + Parch: 0, + Cabin: null, + PassengerId: 415, + Age: 44, + Fare: 7.925, + Name: 'Sundman, Mr. Johan Julian', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '343095', + Parch: 0, + Cabin: null, + PassengerId: 416, + Age: null, + Fare: 8.05, + Name: 'Meek, Mrs. Thomas (Annie Louise Rowley)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '28220', + Parch: 1, + Cabin: null, + PassengerId: 417, + Age: 34, + Fare: 32.5, + Name: 'Drew, Mrs. James Vivian (Lulu Thorne Christian)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '250652', + Parch: 2, + Cabin: null, + PassengerId: 418, + Age: 18, + Fare: 13, + Name: 'Silven, Miss. Lyyli Karoliina', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '28228', + Parch: 0, + Cabin: null, + PassengerId: 419, + Age: 30, + Fare: 13, + Name: 'Matthews, Mr. William John', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '345773', + Parch: 2, + Cabin: null, + PassengerId: 420, + Age: 10, + Fare: 24.15, + Name: 'Van Impe, Miss. Catharina', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349254', + Parch: 0, + Cabin: null, + PassengerId: 421, + Age: null, + Fare: 7.8958, + Name: 'Gheorgheff, Mr. Stanio', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/5. 13032', + Parch: 0, + Cabin: null, + PassengerId: 422, + Age: 21, + Fare: 7.7333, + Name: 'Charters, Mr. David', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '315082', + Parch: 0, + Cabin: null, + PassengerId: 423, + Age: 29, + Fare: 7.875, + Name: 'Zimmerman, Mr. Leo', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '347080', + Parch: 1, + Cabin: null, + PassengerId: 424, + Age: 28, + Fare: 14.4, + Name: 'Danbom, Mrs. Ernst Gilbert (Anna Sigrid Maria Brogren)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '370129', + Parch: 1, + Cabin: null, + PassengerId: 425, + Age: 18, + Fare: 20.2125, + Name: 'Rosblom, Mr. Viktor Richard', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/4. 34244', + Parch: 0, + Cabin: null, + PassengerId: 426, + Age: null, + Fare: 7.25, + Name: 'Wiseman, Mr. Phillippe', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2003', + Parch: 0, + Cabin: null, + PassengerId: 427, + Age: 28, + Fare: 26, + Name: 'Clarke, Mrs. Charles V (Ada Maria Winfield)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '250655', + Parch: 0, + Cabin: null, + PassengerId: 428, + Age: 19, + Fare: 26, + Name: 'Phillips, Miss. Kate Florence ("Mrs Kate Louise Phillips Marshall")', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '364851', + Parch: 0, + Cabin: null, + PassengerId: 429, + Age: null, + Fare: 7.75, + Name: 'Flynn, Mr. James', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/O.Q. 392078', + Parch: 0, + Cabin: 'E10', + PassengerId: 430, + Age: 32, + Fare: 8.05, + Name: 'Pickard, Mr. Berk (Berk Trembisky)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '110564', + Parch: 0, + Cabin: 'C52', + PassengerId: 431, + Age: 28, + Fare: 26.55, + Name: 'Bjornstrom-Steffansson, Mr. Mauritz Hakan', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '376564', + Parch: 0, + Cabin: null, + PassengerId: 432, + Age: null, + Fare: 16.1, + Name: 'Thorneycroft, Mrs. Percival (Florence Kate White)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'SC/AH 3085', + Parch: 0, + Cabin: null, + PassengerId: 433, + Age: 42, + Fare: 26, + Name: 'Louch, Mrs. Charles Alexander (Alice Adelaide Slow)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101274', + Parch: 0, + Cabin: null, + PassengerId: 434, + Age: 17, + Fare: 7.125, + Name: 'Kallio, Mr. Nikolai Erland', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '13507', + Parch: 0, + Cabin: 'E44', + PassengerId: 435, + Age: 50, + Fare: 55.9, + Name: 'Silvey, Mr. William Baird', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113760', + Parch: 2, + Cabin: 'B96 B98', + PassengerId: 436, + Age: 14, + Fare: 120, + Name: 'Carter, Miss. Lucile Polk', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: 'W./C. 6608', + Parch: 2, + Cabin: null, + PassengerId: 437, + Age: 21, + Fare: 34.375, + Name: 'Ford, Miss. Doolina Margaret "Daisy"', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: '29106', + Parch: 3, + Cabin: null, + PassengerId: 438, + Age: 24, + Fare: 18.75, + Name: 'Richards, Mrs. Sidney (Emily Hocking)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '19950', + Parch: 4, + Cabin: 'C23 C25 C27', + PassengerId: 439, + Age: 64, + Fare: 263, + Name: 'Fortune, Mr. Mark', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C.A. 18723', + Parch: 0, + Cabin: null, + PassengerId: 440, + Age: 31, + Fare: 10.5, + Name: 'Kvillner, Mr. Johan Henrik Johannesson', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'F.C.C. 13529', + Parch: 1, + Cabin: null, + PassengerId: 441, + Age: 45, + Fare: 26.25, + Name: 'Hart, Mrs. Benjamin (Esther Ada Bloomfield)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '345769', + Parch: 0, + Cabin: null, + PassengerId: 442, + Age: 20, + Fare: 9.5, + Name: 'Hampe, Mr. Leon', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '347076', + Parch: 0, + Cabin: null, + PassengerId: 443, + Age: 25, + Fare: 7.775, + Name: 'Petterson, Mr. Johan Emil', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '230434', + Parch: 0, + Cabin: null, + PassengerId: 444, + Age: 28, + Fare: 13, + Name: 'Reynaldo, Ms. Encarnacion', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '65306', + Parch: 0, + Cabin: null, + PassengerId: 445, + Age: null, + Fare: 8.1125, + Name: 'Johannesen-Bratthammer, Mr. Bernt', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '33638', + Parch: 2, + Cabin: 'A34', + PassengerId: 446, + Age: 4, + Fare: 81.8583, + Name: 'Dodge, Master. Washington', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '250644', + Parch: 1, + Cabin: null, + PassengerId: 447, + Age: 13, + Fare: 19.5, + Name: 'Mellinger, Miss. Madeleine Violet', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113794', + Parch: 0, + Cabin: null, + PassengerId: 448, + Age: 34, + Fare: 26.55, + Name: 'Seward, Mr. Frederic Kimber', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '2666', + Parch: 1, + Cabin: null, + PassengerId: 449, + Age: 5, + Fare: 19.2583, + Name: 'Baclini, Miss. Marie Catherine', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113786', + Parch: 0, + Cabin: 'C104', + PassengerId: 450, + Age: 52, + Fare: 30.5, + Name: 'Peuchen, Major. Arthur Godfrey', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 34651', + Parch: 2, + Cabin: null, + PassengerId: 451, + Age: 36, + Fare: 27.75, + Name: 'West, Mr. Edwy Arthur', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '65303', + Parch: 0, + Cabin: null, + PassengerId: 452, + Age: null, + Fare: 19.9667, + Name: 'Hagland, Mr. Ingvald Olai Olsen', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113051', + Parch: 0, + Cabin: 'C111', + PassengerId: 453, + Age: 30, + Fare: 27.75, + Name: 'Foreman, Mr. Benjamin Laventall', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '17453', + Parch: 0, + Cabin: 'C92', + PassengerId: 454, + Age: 49, + Fare: 89.1042, + Name: 'Goldenberg, Mr. Samuel L', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/5 2817', + Parch: 0, + Cabin: null, + PassengerId: 455, + Age: null, + Fare: 8.05, + Name: 'Peduzzi, Mr. Joseph', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349240', + Parch: 0, + Cabin: null, + PassengerId: 456, + Age: 29, + Fare: 7.8958, + Name: 'Jalsevac, Mr. Ivan', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '13509', + Parch: 0, + Cabin: 'E38', + PassengerId: 457, + Age: 65, + Fare: 26.55, + Name: 'Millet, Mr. Francis Davis', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '17464', + Parch: 0, + Cabin: 'D21', + PassengerId: 458, + Age: null, + Fare: 51.8625, + Name: 'Kenyon, Mrs. Frederick R (Marion)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'F.C.C. 13531', + Parch: 0, + Cabin: null, + PassengerId: 459, + Age: 50, + Fare: 10.5, + Name: 'Toomey, Miss. Ellen', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '371060', + Parch: 0, + Cabin: null, + PassengerId: 460, + Age: null, + Fare: 7.75, + Name: "O'Connor, Mr. Maurice", + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '19952', + Parch: 0, + Cabin: 'E12', + PassengerId: 461, + Age: 48, + Fare: 26.55, + Name: 'Anderson, Mr. Harry', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '364506', + Parch: 0, + Cabin: null, + PassengerId: 462, + Age: 34, + Fare: 8.05, + Name: 'Morley, Mr. William', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '111320', + Parch: 0, + Cabin: 'E63', + PassengerId: 463, + Age: 47, + Fare: 38.5, + Name: 'Gee, Mr. Arthur H', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '234360', + Parch: 0, + Cabin: null, + PassengerId: 464, + Age: 48, + Fare: 13, + Name: 'Milling, Mr. Jacob Christian', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/S 2816', + Parch: 0, + Cabin: null, + PassengerId: 465, + Age: null, + Fare: 8.05, + Name: 'Maisner, Mr. Simon', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/O.Q. 3101306', + Parch: 0, + Cabin: null, + PassengerId: 466, + Age: 38, + Fare: 7.05, + Name: 'Goncalves, Mr. Manuel Estanslas', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '239853', + Parch: 0, + Cabin: null, + PassengerId: 467, + Age: null, + Fare: 0, + Name: 'Campbell, Mr. William', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113792', + Parch: 0, + Cabin: null, + PassengerId: 468, + Age: 56, + Fare: 26.55, + Name: 'Smart, Mr. John Montgomery', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '36209', + Parch: 0, + Cabin: null, + PassengerId: 469, + Age: null, + Fare: 7.725, + Name: 'Scanlan, Mr. James', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '2666', + Parch: 1, + Cabin: null, + PassengerId: 470, + Age: 0.75, + Fare: 19.2583, + Name: 'Baclini, Miss. Helene Barbara', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '323592', + Parch: 0, + Cabin: null, + PassengerId: 471, + Age: null, + Fare: 7.25, + Name: 'Keefe, Mr. Arthur', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '315089', + Parch: 0, + Cabin: null, + PassengerId: 472, + Age: 38, + Fare: 8.6625, + Name: 'Cacic, Mr. Luka', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 34651', + Parch: 2, + Cabin: null, + PassengerId: 473, + Age: 33, + Fare: 27.75, + Name: 'West, Mrs. Edwy Arthur (Ada Mary Worth)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'SC/AH Basle 541', + Parch: 0, + Cabin: 'D', + PassengerId: 474, + Age: 23, + Fare: 13.7917, + Name: 'Jerwan, Mrs. Amin S (Marie Marthe Thuillard)', + Survived: true, + Pclass: 2, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '7553', + Parch: 0, + Cabin: null, + PassengerId: 475, + Age: 22, + Fare: 9.8375, + Name: 'Strandberg, Miss. Ida Sofia', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '110465', + Parch: 0, + Cabin: 'A14', + PassengerId: 476, + Age: null, + Fare: 52, + Name: 'Clifford, Mr. George Quincy', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '31027', + Parch: 0, + Cabin: null, + PassengerId: 477, + Age: 34, + Fare: 21, + Name: 'Renouf, Mr. Peter Henry', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '3460', + Parch: 0, + Cabin: null, + PassengerId: 478, + Age: 29, + Fare: 7.0458, + Name: 'Braund, Mr. Lewis Richard', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350060', + Parch: 0, + Cabin: null, + PassengerId: 479, + Age: 22, + Fare: 7.5208, + Name: 'Karlsson, Mr. Nils August', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '3101298', + Parch: 1, + Cabin: null, + PassengerId: 480, + Age: 2, + Fare: 12.2875, + Name: 'Hirvonen, Miss. Hildur E', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 5, + Ticket: 'CA 2144', + Parch: 2, + Cabin: null, + PassengerId: 481, + Age: 9, + Fare: 46.9, + Name: 'Goodwin, Master. Harold Victor', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '239854', + Parch: 0, + Cabin: null, + PassengerId: 482, + Age: null, + Fare: 0, + Name: 'Frost, Mr. Anthony Wood "Archie"', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/5 3594', + Parch: 0, + Cabin: null, + PassengerId: 483, + Age: 50, + Fare: 8.05, + Name: 'Rouse, Mr. Richard Henry', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '4134', + Parch: 0, + Cabin: null, + PassengerId: 484, + Age: 63, + Fare: 9.5875, + Name: 'Turkula, Mrs. (Hedwig)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '11967', + Parch: 0, + Cabin: 'B49', + PassengerId: 485, + Age: 25, + Fare: 91.0792, + Name: 'Bishop, Mr. Dickinson H', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '4133', + Parch: 1, + Cabin: null, + PassengerId: 486, + Age: null, + Fare: 25.4667, + Name: 'Lefebre, Miss. Jeannie', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '19943', + Parch: 0, + Cabin: 'C93', + PassengerId: 487, + Age: 35, + Fare: 90, + Name: 'Hoyt, Mrs. Frederick Maxfield (Jane Anne Forby)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '11771', + Parch: 0, + Cabin: 'B37', + PassengerId: 488, + Age: 58, + Fare: 29.7, + Name: 'Kent, Mr. Edward Austin', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A.5. 18509', + Parch: 0, + Cabin: null, + PassengerId: 489, + Age: 30, + Fare: 8.05, + Name: 'Somerton, Mr. Francis William', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 37671', + Parch: 1, + Cabin: null, + PassengerId: 490, + Age: 9, + Fare: 15.9, + Name: 'Coutts, Master. Eden Leslie "Neville"', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '65304', + Parch: 0, + Cabin: null, + PassengerId: 491, + Age: null, + Fare: 19.9667, + Name: 'Hagland, Mr. Konrad Mathias Reiersen', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/OQ 3101317', + Parch: 0, + Cabin: null, + PassengerId: 492, + Age: 21, + Fare: 7.25, + Name: 'Windelov, Mr. Einar', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113787', + Parch: 0, + Cabin: 'C30', + PassengerId: 493, + Age: 55, + Fare: 30.5, + Name: 'Molson, Mr. Harry Markland', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17609', + Parch: 0, + Cabin: null, + PassengerId: 494, + Age: 71, + Fare: 49.5042, + Name: 'Artagaveytia, Mr. Ramon', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/4 45380', + Parch: 0, + Cabin: null, + PassengerId: 495, + Age: 21, + Fare: 8.05, + Name: 'Stanley, Mr. Edward Roland', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2627', + Parch: 0, + Cabin: null, + PassengerId: 496, + Age: null, + Fare: 14.4583, + Name: 'Yousseff, Mr. Gerious', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '36947', + Parch: 0, + Cabin: 'D20', + PassengerId: 497, + Age: 54, + Fare: 78.2667, + Name: 'Eustis, Miss. Elizabeth Mussey', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'C.A. 6212', + Parch: 0, + Cabin: null, + PassengerId: 498, + Age: null, + Fare: 15.1, + Name: 'Shellard, Mr. Frederick William', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113781', + Parch: 2, + Cabin: 'C22 C26', + PassengerId: 499, + Age: 25, + Fare: 151.55, + Name: 'Allison, Mrs. Hudson J C (Bessie Waldo Daniels)', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '350035', + Parch: 0, + Cabin: null, + PassengerId: 500, + Age: 24, + Fare: 7.7958, + Name: 'Svensson, Mr. Olof', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '315086', + Parch: 0, + Cabin: null, + PassengerId: 501, + Age: 17, + Fare: 8.6625, + Name: 'Calic, Mr. Petar', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '364846', + Parch: 0, + Cabin: null, + PassengerId: 502, + Age: 21, + Fare: 7.75, + Name: 'Canavan, Miss. Mary', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '330909', + Parch: 0, + Cabin: null, + PassengerId: 503, + Age: null, + Fare: 7.6292, + Name: "O'Sullivan, Miss. Bridget Mary", + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '4135', + Parch: 0, + Cabin: null, + PassengerId: 504, + Age: 37, + Fare: 9.5875, + Name: 'Laitinen, Miss. Kristina Sofia', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '110152', + Parch: 0, + Cabin: 'B79', + PassengerId: 505, + Age: 16, + Fare: 86.5, + Name: 'Maioni, Miss. Roberta', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'PC 17758', + Parch: 0, + Cabin: 'C65', + PassengerId: 506, + Age: 18, + Fare: 108.9, + Name: 'Penasco y Castellana, Mr. Victor de Satode', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '26360', + Parch: 2, + Cabin: null, + PassengerId: 507, + Age: 33, + Fare: 26, + Name: 'Quick, Mrs. Frederick Charles (Jane Richards)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '111427', + Parch: 0, + Cabin: null, + PassengerId: 508, + Age: null, + Fare: 26.55, + Name: 'Bradley, Mr. George ("George Arthur Brayton")', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C 4001', + Parch: 0, + Cabin: null, + PassengerId: 509, + Age: 28, + Fare: 22.525, + Name: 'Olsen, Mr. Henry Margido', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '1601', + Parch: 0, + Cabin: null, + PassengerId: 510, + Age: 26, + Fare: 56.4958, + Name: 'Lang, Mr. Fang', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '382651', + Parch: 0, + Cabin: null, + PassengerId: 511, + Age: 29, + Fare: 7.75, + Name: 'Daly, Mr. Eugene Patrick', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/OQ 3101316', + Parch: 0, + Cabin: null, + PassengerId: 512, + Age: null, + Fare: 8.05, + Name: 'Webber, Mr. James', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17473', + Parch: 0, + Cabin: 'E25', + PassengerId: 513, + Age: 36, + Fare: 26.2875, + Name: 'McGough, Mr. James Robert', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'PC 17603', + Parch: 0, + Cabin: null, + PassengerId: 514, + Age: 54, + Fare: 59.4, + Name: 'Rothschild, Mrs. Martin (Elizabeth L. Barrett)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349209', + Parch: 0, + Cabin: null, + PassengerId: 515, + Age: 24, + Fare: 7.4958, + Name: 'Coleff, Mr. Satio', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '36967', + Parch: 0, + Cabin: 'D46', + PassengerId: 516, + Age: 47, + Fare: 34.0208, + Name: 'Walker, Mr. William Anderson', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C.A. 34260', + Parch: 0, + Cabin: 'F33', + PassengerId: 517, + Age: 34, + Fare: 10.5, + Name: 'Lemore, Mrs. (Amelia Milley)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '371110', + Parch: 0, + Cabin: null, + PassengerId: 518, + Age: null, + Fare: 24.15, + Name: 'Ryan, Mr. Patrick', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '226875', + Parch: 0, + Cabin: null, + PassengerId: 519, + Age: 36, + Fare: 26, + Name: 'Angle, Mrs. William A (Florence "Mary" Agnes Hughes)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349242', + Parch: 0, + Cabin: null, + PassengerId: 520, + Age: 32, + Fare: 7.8958, + Name: 'Pavlovic, Mr. Stefo', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '12749', + Parch: 0, + Cabin: 'B73', + PassengerId: 521, + Age: 30, + Fare: 93.5, + Name: 'Perreault, Miss. Anne', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349252', + Parch: 0, + Cabin: null, + PassengerId: 522, + Age: 22, + Fare: 7.8958, + Name: 'Vovk, Mr. Janko', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2624', + Parch: 0, + Cabin: null, + PassengerId: 523, + Age: null, + Fare: 7.225, + Name: 'Lahoud, Mr. Sarkis', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '111361', + Parch: 1, + Cabin: 'B18', + PassengerId: 524, + Age: 44, + Fare: 57.9792, + Name: 'Hippach, Mrs. Louis Albert (Ida Sophia Fischer)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2700', + Parch: 0, + Cabin: null, + PassengerId: 525, + Age: null, + Fare: 7.2292, + Name: 'Kassem, Mr. Fared', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '367232', + Parch: 0, + Cabin: null, + PassengerId: 526, + Age: 40.5, + Fare: 7.75, + Name: 'Farrell, Mr. James', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'W./C. 14258', + Parch: 0, + Cabin: null, + PassengerId: 527, + Age: 50, + Fare: 10.5, + Name: 'Ridsdale, Miss. Lucy', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17483', + Parch: 0, + Cabin: 'C95', + PassengerId: 528, + Age: null, + Fare: 221.7792, + Name: 'Farthing, Mr. John', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '3101296', + Parch: 0, + Cabin: null, + PassengerId: 529, + Age: 39, + Fare: 7.925, + Name: 'Salonen, Mr. Johan Werner', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '29104', + Parch: 1, + Cabin: null, + PassengerId: 530, + Age: 23, + Fare: 11.5, + Name: 'Hocking, Mr. Richard George', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '26360', + Parch: 1, + Cabin: null, + PassengerId: 531, + Age: 2, + Fare: 26, + Name: 'Quick, Miss. Phyllis May', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2641', + Parch: 0, + Cabin: null, + PassengerId: 532, + Age: null, + Fare: 7.2292, + Name: 'Toufik, Mr. Nakli', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2690', + Parch: 1, + Cabin: null, + PassengerId: 533, + Age: 17, + Fare: 7.2292, + Name: 'Elias, Mr. Joseph Jr', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2668', + Parch: 2, + Cabin: null, + PassengerId: 534, + Age: null, + Fare: 22.3583, + Name: 'Peter, Mrs. Catherine (Catherine Rizk)', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '315084', + Parch: 0, + Cabin: null, + PassengerId: 535, + Age: 30, + Fare: 8.6625, + Name: 'Cacic, Miss. Marija', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'F.C.C. 13529', + Parch: 2, + Cabin: null, + PassengerId: 536, + Age: 7, + Fare: 26.25, + Name: 'Hart, Miss. Eva Miriam', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113050', + Parch: 0, + Cabin: 'B38', + PassengerId: 537, + Age: 45, + Fare: 26.55, + Name: 'Butt, Major. Archibald Willingham', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17761', + Parch: 0, + Cabin: null, + PassengerId: 538, + Age: 30, + Fare: 106.425, + Name: 'LeRoy, Miss. Bertha', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '364498', + Parch: 0, + Cabin: null, + PassengerId: 539, + Age: null, + Fare: 14.5, + Name: 'Risien, Mr. Samuel Beard', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '13568', + Parch: 2, + Cabin: 'B39', + PassengerId: 540, + Age: 22, + Fare: 49.5, + Name: 'Frolicher, Miss. Hedwig Margaritha', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'WE/P 5735', + Parch: 2, + Cabin: 'B22', + PassengerId: 541, + Age: 36, + Fare: 71, + Name: 'Crosby, Miss. Harriet R', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 4, + Ticket: '347082', + Parch: 2, + Cabin: null, + PassengerId: 542, + Age: 9, + Fare: 31.275, + Name: 'Andersson, Miss. Ingeborg Constanzia', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 4, + Ticket: '347082', + Parch: 2, + Cabin: null, + PassengerId: 543, + Age: 11, + Fare: 31.275, + Name: 'Andersson, Miss. Sigrid Elisabeth', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '2908', + Parch: 0, + Cabin: null, + PassengerId: 544, + Age: 32, + Fare: 26, + Name: 'Beane, Mr. Edward', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'PC 17761', + Parch: 0, + Cabin: 'C86', + PassengerId: 545, + Age: 50, + Fare: 106.425, + Name: 'Douglas, Mr. Walter Donald', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '693', + Parch: 0, + Cabin: null, + PassengerId: 546, + Age: 64, + Fare: 26, + Name: 'Nicholson, Mr. Arthur Ernest', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2908', + Parch: 0, + Cabin: null, + PassengerId: 547, + Age: 19, + Fare: 26, + Name: 'Beane, Mrs. Edward (Ethel Clarke)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'SC/PARIS 2146', + Parch: 0, + Cabin: null, + PassengerId: 548, + Age: null, + Fare: 13.8625, + Name: 'Padro y Manent, Mr. Julian', + Survived: true, + Pclass: 2, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '363291', + Parch: 1, + Cabin: null, + PassengerId: 549, + Age: 33, + Fare: 20.525, + Name: 'Goldsmith, Mr. Frank John', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 33112', + Parch: 1, + Cabin: null, + PassengerId: 550, + Age: 8, + Fare: 36.75, + Name: 'Davies, Master. John Morgan Jr', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '17421', + Parch: 2, + Cabin: 'C70', + PassengerId: 551, + Age: 17, + Fare: 110.8833, + Name: 'Thayer, Mr. John Borland Jr', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '244358', + Parch: 0, + Cabin: null, + PassengerId: 552, + Age: 27, + Fare: 26, + Name: 'Sharp, Mr. Percival James R', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '330979', + Parch: 0, + Cabin: null, + PassengerId: 553, + Age: null, + Fare: 7.8292, + Name: "O'Brien, Mr. Timothy", + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2620', + Parch: 0, + Cabin: null, + PassengerId: 554, + Age: 22, + Fare: 7.225, + Name: 'Leeni, Mr. Fahim ("Philip Zenni")', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347085', + Parch: 0, + Cabin: null, + PassengerId: 555, + Age: 22, + Fare: 7.775, + Name: 'Ohman, Miss. Velin', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113807', + Parch: 0, + Cabin: null, + PassengerId: 556, + Age: 62, + Fare: 26.55, + Name: 'Wright, Mr. George', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '11755', + Parch: 0, + Cabin: 'A16', + PassengerId: 557, + Age: 48, + Fare: 39.6, + Name: 'Duff Gordon, Lady. (Lucille Christiana Sutherland) ("Mrs Morgan")', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17757', + Parch: 0, + Cabin: null, + PassengerId: 558, + Age: null, + Fare: 227.525, + Name: 'Robbins, Mr. Victor', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '110413', + Parch: 1, + Cabin: 'E67', + PassengerId: 559, + Age: 39, + Fare: 79.65, + Name: 'Taussig, Mrs. Emil (Tillie Mandelbaum)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '345572', + Parch: 0, + Cabin: null, + PassengerId: 560, + Age: 36, + Fare: 17.4, + Name: 'de Messemaeker, Mrs. Guillaume Joseph (Emma)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '372622', + Parch: 0, + Cabin: null, + PassengerId: 561, + Age: null, + Fare: 7.75, + Name: 'Morrow, Mr. Thomas Rowan', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349251', + Parch: 0, + Cabin: null, + PassengerId: 562, + Age: 40, + Fare: 7.8958, + Name: 'Sivic, Mr. Husein', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '218629', + Parch: 0, + Cabin: null, + PassengerId: 563, + Age: 28, + Fare: 13.5, + Name: 'Norman, Mr. Robert Douglas', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/OQ 392082', + Parch: 0, + Cabin: null, + PassengerId: 564, + Age: null, + Fare: 8.05, + Name: 'Simmons, Mr. John', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/O.Q. 392087', + Parch: 0, + Cabin: null, + PassengerId: 565, + Age: null, + Fare: 8.05, + Name: 'Meanwell, Miss. (Marion Ogden)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: 'A/4 48871', + Parch: 0, + Cabin: null, + PassengerId: 566, + Age: 24, + Fare: 24.15, + Name: 'Davies, Mr. Alfred J', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349205', + Parch: 0, + Cabin: null, + PassengerId: 567, + Age: 19, + Fare: 7.8958, + Name: 'Stoytcheff, Mr. Ilia', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349909', + Parch: 4, + Cabin: null, + PassengerId: 568, + Age: 29, + Fare: 21.075, + Name: 'Palsson, Mrs. Nils (Alma Cornelia Berglund)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2686', + Parch: 0, + Cabin: null, + PassengerId: 569, + Age: null, + Fare: 7.2292, + Name: 'Doharr, Mr. Tannous', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350417', + Parch: 0, + Cabin: null, + PassengerId: 570, + Age: 32, + Fare: 7.8542, + Name: 'Jonsson, Mr. Carl', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'S.W./PP 752', + Parch: 0, + Cabin: null, + PassengerId: 571, + Age: 62, + Fare: 10.5, + Name: 'Harris, Mr. George', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '11769', + Parch: 0, + Cabin: 'C101', + PassengerId: 572, + Age: 53, + Fare: 51.4792, + Name: 'Appleton, Mrs. Edward Dale (Charlotte Lamson)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17474', + Parch: 0, + Cabin: 'E25', + PassengerId: 573, + Age: 36, + Fare: 26.3875, + Name: 'Flynn, Mr. John Irwin ("Irving")', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '14312', + Parch: 0, + Cabin: null, + PassengerId: 574, + Age: null, + Fare: 7.75, + Name: 'Kelly, Miss. Mary', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'A/4. 20589', + Parch: 0, + Cabin: null, + PassengerId: 575, + Age: 16, + Fare: 8.05, + Name: 'Rush, Mr. Alfred George John', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '358585', + Parch: 0, + Cabin: null, + PassengerId: 576, + Age: 19, + Fare: 14.5, + Name: 'Patchett, Mr. George', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '243880', + Parch: 0, + Cabin: null, + PassengerId: 577, + Age: 34, + Fare: 13, + Name: 'Garside, Miss. Ethel', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '13507', + Parch: 0, + Cabin: 'E44', + PassengerId: 578, + Age: 39, + Fare: 55.9, + Name: 'Silvey, Mrs. William Baird (Alice Munger)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '2689', + Parch: 0, + Cabin: null, + PassengerId: 579, + Age: null, + Fare: 14.4583, + Name: 'Caram, Mrs. Joseph (Maria Elias)', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101286', + Parch: 0, + Cabin: null, + PassengerId: 580, + Age: 32, + Fare: 7.925, + Name: 'Jussila, Mr. Eiriik', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '237789', + Parch: 1, + Cabin: null, + PassengerId: 581, + Age: 25, + Fare: 30, + Name: 'Christy, Miss. Julie Rachel', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '17421', + Parch: 1, + Cabin: 'C68', + PassengerId: 582, + Age: 39, + Fare: 110.8833, + Name: 'Thayer, Mrs. John Borland (Marian Longstreth Morris)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '28403', + Parch: 0, + Cabin: null, + PassengerId: 583, + Age: 54, + Fare: 26, + Name: 'Downton, Mr. William James', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '13049', + Parch: 0, + Cabin: 'A10', + PassengerId: 584, + Age: 36, + Fare: 40.125, + Name: 'Ross, Mr. John Hugo', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '3411', + Parch: 0, + Cabin: null, + PassengerId: 585, + Age: null, + Fare: 8.7125, + Name: 'Paulner, Mr. Uscher', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '110413', + Parch: 2, + Cabin: 'E68', + PassengerId: 586, + Age: 18, + Fare: 79.65, + Name: 'Taussig, Miss. Ruth', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '237565', + Parch: 0, + Cabin: null, + PassengerId: 587, + Age: 47, + Fare: 15, + Name: 'Jarvis, Mr. John Denzil', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '13567', + Parch: 1, + Cabin: 'B41', + PassengerId: 588, + Age: 60, + Fare: 79.2, + Name: 'Frolicher-Stehli, Mr. Maxmillian', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '14973', + Parch: 0, + Cabin: null, + PassengerId: 589, + Age: 22, + Fare: 8.05, + Name: 'Gilinski, Mr. Eliezer', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A./5. 3235', + Parch: 0, + Cabin: null, + PassengerId: 590, + Age: null, + Fare: 8.05, + Name: 'Murdlin, Mr. Joseph', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101273', + Parch: 0, + Cabin: null, + PassengerId: 591, + Age: 35, + Fare: 7.125, + Name: 'Rintamaki, Mr. Matti', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '36947', + Parch: 0, + Cabin: 'D20', + PassengerId: 592, + Age: 52, + Fare: 78.2667, + Name: 'Stephenson, Mrs. Walter Bertram (Martha Eustis)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'A/5 3902', + Parch: 0, + Cabin: null, + PassengerId: 593, + Age: 47, + Fare: 7.25, + Name: 'Elsbury, Mr. William James', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '364848', + Parch: 2, + Cabin: null, + PassengerId: 594, + Age: null, + Fare: 7.75, + Name: 'Bourke, Miss. Mary', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'SC/AH 29037', + Parch: 0, + Cabin: null, + PassengerId: 595, + Age: 37, + Fare: 26, + Name: 'Chapman, Mr. John Henry', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '345773', + Parch: 1, + Cabin: null, + PassengerId: 596, + Age: 36, + Fare: 24.15, + Name: 'Van Impe, Mr. Jean Baptiste', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '248727', + Parch: 0, + Cabin: null, + PassengerId: 597, + Age: null, + Fare: 33, + Name: 'Leitch, Miss. Jessie Wills', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'LINE', + Parch: 0, + Cabin: null, + PassengerId: 598, + Age: 49, + Fare: 0, + Name: 'Johnson, Mr. Alfred', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2664', + Parch: 0, + Cabin: null, + PassengerId: 599, + Age: null, + Fare: 7.225, + Name: 'Boulos, Mr. Hanna', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'PC 17485', + Parch: 0, + Cabin: 'A20', + PassengerId: 600, + Age: 49, + Fare: 56.9292, + Name: 'Duff Gordon, Sir. Cosmo Edmund ("Mr Morgan")', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '243847', + Parch: 1, + Cabin: null, + PassengerId: 601, + Age: 24, + Fare: 27, + Name: 'Jacobsohn, Mrs. Sidney Samuel (Amy Frances Christy)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349214', + Parch: 0, + Cabin: null, + PassengerId: 602, + Age: null, + Fare: 7.8958, + Name: 'Slabenoff, Mr. Petco', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113796', + Parch: 0, + Cabin: null, + PassengerId: 603, + Age: null, + Fare: 42.4, + Name: 'Harrington, Mr. Charles H', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '364511', + Parch: 0, + Cabin: null, + PassengerId: 604, + Age: 44, + Fare: 8.05, + Name: 'Torber, Mr. Ernst William', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '111426', + Parch: 0, + Cabin: null, + PassengerId: 605, + Age: 35, + Fare: 26.55, + Name: 'Homer, Mr. Harry ("Mr E Haven")', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '349910', + Parch: 0, + Cabin: null, + PassengerId: 606, + Age: 36, + Fare: 15.55, + Name: 'Lindell, Mr. Edvard Bengtsson', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349246', + Parch: 0, + Cabin: null, + PassengerId: 607, + Age: 30, + Fare: 7.8958, + Name: 'Karaic, Mr. Milan', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113804', + Parch: 0, + Cabin: null, + PassengerId: 608, + Age: 27, + Fare: 30.5, + Name: 'Daniel, Mr. Robert Williams', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'SC/Paris 2123', + Parch: 2, + Cabin: null, + PassengerId: 609, + Age: 22, + Fare: 41.5792, + Name: 'Laroche, Mrs. Joseph (Juliette Marie Louise Lafargue)', + Survived: true, + Pclass: 2, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17582', + Parch: 0, + Cabin: 'C125', + PassengerId: 610, + Age: 40, + Fare: 153.4625, + Name: 'Shutes, Miss. Elizabeth W', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '347082', + Parch: 5, + Cabin: null, + PassengerId: 611, + Age: 39, + Fare: 31.275, + Name: 'Andersson, Mrs. Anders Johan (Alfrida Konstantia Brogren)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'SOTON/O.Q. 3101305', + Parch: 0, + Cabin: null, + PassengerId: 612, + Age: null, + Fare: 7.05, + Name: 'Jardin, Mr. Jose Neto', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '367230', + Parch: 0, + Cabin: null, + PassengerId: 613, + Age: null, + Fare: 15.5, + Name: 'Murphy, Miss. Margaret Jane', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '370377', + Parch: 0, + Cabin: null, + PassengerId: 614, + Age: null, + Fare: 7.75, + Name: 'Horgan, Mr. John', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '364512', + Parch: 0, + Cabin: null, + PassengerId: 615, + Age: 35, + Fare: 8.05, + Name: 'Brocklebank, Mr. William Alfred', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '220845', + Parch: 2, + Cabin: null, + PassengerId: 616, + Age: 24, + Fare: 65, + Name: 'Herman, Miss. Alice', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '347080', + Parch: 1, + Cabin: null, + PassengerId: 617, + Age: 34, + Fare: 14.4, + Name: 'Danbom, Mr. Ernst Gilbert', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'A/5. 3336', + Parch: 0, + Cabin: null, + PassengerId: 618, + Age: 26, + Fare: 16.1, + Name: 'Lobb, Mrs. William Arthur (Cordelia K Stanlick)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: '230136', + Parch: 1, + Cabin: 'F4', + PassengerId: 619, + Age: 4, + Fare: 39, + Name: 'Becker, Miss. Marion Louise', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '31028', + Parch: 0, + Cabin: null, + PassengerId: 620, + Age: 26, + Fare: 10.5, + Name: 'Gavey, Mr. Lawrence', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2659', + Parch: 0, + Cabin: null, + PassengerId: 621, + Age: 27, + Fare: 14.4542, + Name: 'Yasbeck, Mr. Antoni', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '11753', + Parch: 0, + Cabin: 'D19', + PassengerId: 622, + Age: 42, + Fare: 52.5542, + Name: 'Kimball, Mr. Edwin Nelson Jr', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2653', + Parch: 1, + Cabin: null, + PassengerId: 623, + Age: 20, + Fare: 15.7417, + Name: 'Nakid, Mr. Sahid', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350029', + Parch: 0, + Cabin: null, + PassengerId: 624, + Age: 21, + Fare: 7.8542, + Name: 'Hansen, Mr. Henry Damsgaard', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '54636', + Parch: 0, + Cabin: null, + PassengerId: 625, + Age: 21, + Fare: 16.1, + Name: 'Bowen, Mr. David John "Dai"', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '36963', + Parch: 0, + Cabin: 'D50', + PassengerId: 626, + Age: 61, + Fare: 32.3208, + Name: 'Sutton, Mr. Frederick', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '219533', + Parch: 0, + Cabin: null, + PassengerId: 627, + Age: 57, + Fare: 12.35, + Name: 'Kirkland, Rev. Charles Leonard', + Survived: false, + Pclass: 2, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '13502', + Parch: 0, + Cabin: 'D9', + PassengerId: 628, + Age: 21, + Fare: 77.9583, + Name: 'Longley, Miss. Gretchen Fiske', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349224', + Parch: 0, + Cabin: null, + PassengerId: 629, + Age: 26, + Fare: 7.8958, + Name: 'Bostandyeff, Mr. Guentcho', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '334912', + Parch: 0, + Cabin: null, + PassengerId: 630, + Age: null, + Fare: 7.7333, + Name: "O'Connell, Mr. Patrick D", + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '27042', + Parch: 0, + Cabin: 'A23', + PassengerId: 631, + Age: 80, + Fare: 30, + Name: 'Barkworth, Mr. Algernon Henry Wilson', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347743', + Parch: 0, + Cabin: null, + PassengerId: 632, + Age: 51, + Fare: 7.0542, + Name: 'Lundahl, Mr. Johan Svensson', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '13214', + Parch: 0, + Cabin: 'B50', + PassengerId: 633, + Age: 32, + Fare: 30.5, + Name: 'Stahelin-Maeglin, Dr. Max', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '112052', + Parch: 0, + Cabin: null, + PassengerId: 634, + Age: null, + Fare: 0, + Name: 'Parr, Mr. William Henry Marsh', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '347088', + Parch: 2, + Cabin: null, + PassengerId: 635, + Age: 9, + Fare: 27.9, + Name: 'Skoog, Miss. Mabel', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '237668', + Parch: 0, + Cabin: null, + PassengerId: 636, + Age: 28, + Fare: 13, + Name: 'Davis, Miss. Mary', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101292', + Parch: 0, + Cabin: null, + PassengerId: 637, + Age: 32, + Fare: 7.925, + Name: 'Leinonen, Mr. Antti Gustaf', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 31921', + Parch: 1, + Cabin: null, + PassengerId: 638, + Age: 31, + Fare: 26.25, + Name: 'Collyer, Mr. Harvey', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '3101295', + Parch: 5, + Cabin: null, + PassengerId: 639, + Age: 41, + Fare: 39.6875, + Name: 'Panula, Mrs. Juha (Maria Emilia Ojala)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '376564', + Parch: 0, + Cabin: null, + PassengerId: 640, + Age: null, + Fare: 16.1, + Name: 'Thorneycroft, Mr. Percival', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350050', + Parch: 0, + Cabin: null, + PassengerId: 641, + Age: 20, + Fare: 7.8542, + Name: 'Jensen, Mr. Hans Peder', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17477', + Parch: 0, + Cabin: 'B35', + PassengerId: 642, + Age: 24, + Fare: 69.3, + Name: 'Sagesser, Mlle. Emma', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 3, + Ticket: '347088', + Parch: 2, + Cabin: null, + PassengerId: 643, + Age: 2, + Fare: 27.9, + Name: 'Skoog, Miss. Margit Elizabeth', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '1601', + Parch: 0, + Cabin: null, + PassengerId: 644, + Age: null, + Fare: 56.4958, + Name: 'Foo, Mr. Choong', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '2666', + Parch: 1, + Cabin: null, + PassengerId: 645, + Age: 0.75, + Fare: 19.2583, + Name: 'Baclini, Miss. Eugenie', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'PC 17572', + Parch: 0, + Cabin: 'D33', + PassengerId: 646, + Age: 48, + Fare: 76.7292, + Name: 'Harper, Mr. Henry Sleeper', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349231', + Parch: 0, + Cabin: null, + PassengerId: 647, + Age: 19, + Fare: 7.8958, + Name: 'Cor, Mr. Liudevit', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '13213', + Parch: 0, + Cabin: 'A26', + PassengerId: 648, + Age: 56, + Fare: 35.5, + Name: 'Simonius-Blumer, Col. Oberst Alfons', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'S.O./P.P. 751', + Parch: 0, + Cabin: null, + PassengerId: 649, + Age: null, + Fare: 7.55, + Name: 'Willey, Mr. Edward', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'CA. 2314', + Parch: 0, + Cabin: null, + PassengerId: 650, + Age: 23, + Fare: 7.55, + Name: 'Stanley, Miss. Amy Zillah Elsie', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349221', + Parch: 0, + Cabin: null, + PassengerId: 651, + Age: null, + Fare: 7.8958, + Name: 'Mitkoff, Mr. Mito', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '231919', + Parch: 1, + Cabin: null, + PassengerId: 652, + Age: 18, + Fare: 23, + Name: 'Doling, Miss. Elsie', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '8475', + Parch: 0, + Cabin: null, + PassengerId: 653, + Age: 21, + Fare: 8.4333, + Name: 'Kalvik, Mr. Johannes Halvorsen', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '330919', + Parch: 0, + Cabin: null, + PassengerId: 654, + Age: null, + Fare: 7.8292, + Name: 'O\'Leary, Miss. Hanora "Norah"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '365226', + Parch: 0, + Cabin: null, + PassengerId: 655, + Age: 18, + Fare: 6.75, + Name: 'Hegarty, Miss. Hanora "Nora"', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 2, + Ticket: 'S.O.C. 14879', + Parch: 0, + Cabin: null, + PassengerId: 656, + Age: 24, + Fare: 73.5, + Name: 'Hickman, Mr. Leonard Mark', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349223', + Parch: 0, + Cabin: null, + PassengerId: 657, + Age: null, + Fare: 7.8958, + Name: 'Radeff, Mr. Alexander', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '364849', + Parch: 1, + Cabin: null, + PassengerId: 658, + Age: 32, + Fare: 15.5, + Name: 'Bourke, Mrs. John (Catherine)', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '29751', + Parch: 0, + Cabin: null, + PassengerId: 659, + Age: 23, + Fare: 13, + Name: 'Eitemiller, Mr. George Floyd', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '35273', + Parch: 2, + Cabin: 'D48', + PassengerId: 660, + Age: 58, + Fare: 113.275, + Name: 'Newell, Mr. Arthur Webster', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: 'PC 17611', + Parch: 0, + Cabin: null, + PassengerId: 661, + Age: 50, + Fare: 133.65, + Name: 'Frauenthal, Dr. Henry William', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2623', + Parch: 0, + Cabin: null, + PassengerId: 662, + Age: 40, + Fare: 7.225, + Name: 'Badt, Mr. Mohamed', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '5727', + Parch: 0, + Cabin: 'E58', + PassengerId: 663, + Age: 47, + Fare: 25.5875, + Name: 'Colley, Mr. Edward Pomeroy', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349210', + Parch: 0, + Cabin: null, + PassengerId: 664, + Age: 36, + Fare: 7.4958, + Name: 'Coleff, Mr. Peju', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'STON/O 2. 3101285', + Parch: 0, + Cabin: null, + PassengerId: 665, + Age: 20, + Fare: 7.925, + Name: 'Lindqvist, Mr. Eino William', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: 'S.O.C. 14879', + Parch: 0, + Cabin: null, + PassengerId: 666, + Age: 32, + Fare: 73.5, + Name: 'Hickman, Mr. Lewis', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '234686', + Parch: 0, + Cabin: null, + PassengerId: 667, + Age: 25, + Fare: 13, + Name: 'Butler, Mr. Reginald Fenton', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '312993', + Parch: 0, + Cabin: null, + PassengerId: 668, + Age: null, + Fare: 7.775, + Name: 'Rommetvedt, Mr. Knud Paust', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/5 3536', + Parch: 0, + Cabin: null, + PassengerId: 669, + Age: 43, + Fare: 8.05, + Name: 'Cook, Mr. Jacob', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '19996', + Parch: 0, + Cabin: 'C126', + PassengerId: 670, + Age: null, + Fare: 52, + Name: 'Taylor, Mrs. Elmer Zebley (Juliet Cummins Wright)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '29750', + Parch: 1, + Cabin: null, + PassengerId: 671, + Age: 40, + Fare: 39, + Name: 'Brown, Mrs. Thomas William Solomon (Elizabeth Catherine Ford)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'F.C. 12750', + Parch: 0, + Cabin: 'B71', + PassengerId: 672, + Age: 31, + Fare: 52, + Name: 'Davidson, Mr. Thornton', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C.A. 24580', + Parch: 0, + Cabin: null, + PassengerId: 673, + Age: 70, + Fare: 10.5, + Name: 'Mitchell, Mr. Henry Michael', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '244270', + Parch: 0, + Cabin: null, + PassengerId: 674, + Age: 31, + Fare: 13, + Name: 'Wilhelms, Mr. Charles', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '239856', + Parch: 0, + Cabin: null, + PassengerId: 675, + Age: null, + Fare: 0, + Name: 'Watson, Mr. Ennis Hastings', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349912', + Parch: 0, + Cabin: null, + PassengerId: 676, + Age: 18, + Fare: 7.775, + Name: 'Edvardsson, Mr. Gustaf Hjalmar', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '342826', + Parch: 0, + Cabin: null, + PassengerId: 677, + Age: 24.5, + Fare: 8.05, + Name: 'Sawyer, Mr. Frederick Charles', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '4138', + Parch: 0, + Cabin: null, + PassengerId: 678, + Age: 18, + Fare: 9.8417, + Name: 'Turja, Miss. Anna Sofia', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'CA 2144', + Parch: 6, + Cabin: null, + PassengerId: 679, + Age: 43, + Fare: 46.9, + Name: 'Goodwin, Mrs. Frederick (Augusta Tyler)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17755', + Parch: 1, + Cabin: 'B51 B53 B55', + PassengerId: 680, + Age: 36, + Fare: 512.3292, + Name: 'Cardeza, Mr. Thomas Drake Martinez', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '330935', + Parch: 0, + Cabin: null, + PassengerId: 681, + Age: null, + Fare: 8.1375, + Name: 'Peters, Miss. Katie', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17572', + Parch: 0, + Cabin: 'D49', + PassengerId: 682, + Age: 27, + Fare: 76.7292, + Name: 'Hassab, Mr. Hammad', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '6563', + Parch: 0, + Cabin: null, + PassengerId: 683, + Age: 20, + Fare: 9.225, + Name: 'Olsvigen, Mr. Thor Anderson', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 5, + Ticket: 'CA 2144', + Parch: 2, + Cabin: null, + PassengerId: 684, + Age: 14, + Fare: 46.9, + Name: 'Goodwin, Mr. Charles Edward', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '29750', + Parch: 1, + Cabin: null, + PassengerId: 685, + Age: 60, + Fare: 39, + Name: 'Brown, Mr. Thomas William Solomon', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'SC/Paris 2123', + Parch: 2, + Cabin: null, + PassengerId: 686, + Age: 25, + Fare: 41.5792, + Name: 'Laroche, Mr. Joseph Philippe Lemercier', + Survived: false, + Pclass: 2, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '3101295', + Parch: 1, + Cabin: null, + PassengerId: 687, + Age: 14, + Fare: 39.6875, + Name: 'Panula, Mr. Jaako Arnold', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349228', + Parch: 0, + Cabin: null, + PassengerId: 688, + Age: 19, + Fare: 10.1708, + Name: 'Dakic, Mr. Branko', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350036', + Parch: 0, + Cabin: null, + PassengerId: 689, + Age: 18, + Fare: 7.7958, + Name: 'Fischer, Mr. Eberhard Thelander', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '24160', + Parch: 1, + Cabin: 'B5', + PassengerId: 690, + Age: 15, + Fare: 211.3375, + Name: 'Madill, Miss. Georgette Alexandra', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '17474', + Parch: 0, + Cabin: 'B20', + PassengerId: 691, + Age: 31, + Fare: 57, + Name: 'Dick, Mr. Albert Adrian', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349256', + Parch: 1, + Cabin: null, + PassengerId: 692, + Age: 4, + Fare: 13.4167, + Name: 'Karun, Miss. Manca', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '1601', + Parch: 0, + Cabin: null, + PassengerId: 693, + Age: null, + Fare: 56.4958, + Name: 'Lam, Mr. Ali', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2672', + Parch: 0, + Cabin: null, + PassengerId: 694, + Age: 25, + Fare: 7.225, + Name: 'Saad, Mr. Khalil', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113800', + Parch: 0, + Cabin: null, + PassengerId: 695, + Age: 60, + Fare: 26.55, + Name: 'Weir, Col. John', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '248731', + Parch: 0, + Cabin: null, + PassengerId: 696, + Age: 52, + Fare: 13.5, + Name: 'Chapman, Mr. Charles Henry', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '363592', + Parch: 0, + Cabin: null, + PassengerId: 697, + Age: 44, + Fare: 8.05, + Name: 'Kelly, Mr. James', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '35852', + Parch: 0, + Cabin: null, + PassengerId: 698, + Age: null, + Fare: 7.7333, + Name: 'Mullens, Miss. Katherine "Katie"', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '17421', + Parch: 1, + Cabin: 'C68', + PassengerId: 699, + Age: 49, + Fare: 110.8833, + Name: 'Thayer, Mr. John Borland', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '348121', + Parch: 0, + Cabin: 'F G63', + PassengerId: 700, + Age: 42, + Fare: 7.65, + Name: 'Humblen, Mr. Adolf Mathias Nicolai Olsen', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'PC 17757', + Parch: 0, + Cabin: 'C62 C64', + PassengerId: 701, + Age: 18, + Fare: 227.525, + Name: 'Astor, Mrs. John Jacob (Madeleine Talmadge Force)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17475', + Parch: 0, + Cabin: 'E24', + PassengerId: 702, + Age: 35, + Fare: 26.2875, + Name: 'Silverthorne, Mr. Spencer Victor', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2691', + Parch: 1, + Cabin: null, + PassengerId: 703, + Age: 18, + Fare: 14.4542, + Name: 'Barbara, Miss. Saiide', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '36864', + Parch: 0, + Cabin: null, + PassengerId: 704, + Age: 25, + Fare: 7.7417, + Name: 'Gallagher, Mr. Martin', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '350025', + Parch: 0, + Cabin: null, + PassengerId: 705, + Age: 26, + Fare: 7.8542, + Name: 'Hansen, Mr. Henrik Juul', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '250655', + Parch: 0, + Cabin: null, + PassengerId: 706, + Age: 39, + Fare: 26, + Name: 'Morley, Mr. Henry Samuel ("Mr Henry Marshall")', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '223596', + Parch: 0, + Cabin: null, + PassengerId: 707, + Age: 45, + Fare: 13.5, + Name: 'Kelly, Mrs. Florence "Fannie"', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17476', + Parch: 0, + Cabin: 'E24', + PassengerId: 708, + Age: 42, + Fare: 26.2875, + Name: 'Calderhead, Mr. Edward Pennington', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113781', + Parch: 0, + Cabin: null, + PassengerId: 709, + Age: 22, + Fare: 151.55, + Name: 'Cleaver, Miss. Alice', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '2661', + Parch: 1, + Cabin: null, + PassengerId: 710, + Age: null, + Fare: 15.2458, + Name: 'Moubarek, Master. Halim Gonios ("William George")', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17482', + Parch: 0, + Cabin: 'C90', + PassengerId: 711, + Age: 24, + Fare: 49.5042, + Name: 'Mayne, Mlle. Berthe Antonine ("Mrs de Villiers")', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113028', + Parch: 0, + Cabin: 'C124', + PassengerId: 712, + Age: null, + Fare: 26.55, + Name: 'Klaber, Mr. Herman', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '19996', + Parch: 0, + Cabin: 'C126', + PassengerId: 713, + Age: 48, + Fare: 52, + Name: 'Taylor, Mr. Elmer Zebley', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '7545', + Parch: 0, + Cabin: null, + PassengerId: 714, + Age: 29, + Fare: 9.4833, + Name: 'Larsson, Mr. August Viktor', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '250647', + Parch: 0, + Cabin: null, + PassengerId: 715, + Age: 52, + Fare: 13, + Name: 'Greenberg, Mr. Samuel', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '348124', + Parch: 0, + Cabin: 'F G73', + PassengerId: 716, + Age: 19, + Fare: 7.65, + Name: 'Soholt, Mr. Peter Andreas Lauritz Andersen', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17757', + Parch: 0, + Cabin: 'C45', + PassengerId: 717, + Age: 38, + Fare: 227.525, + Name: 'Endres, Miss. Caroline Louise', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '34218', + Parch: 0, + Cabin: 'E101', + PassengerId: 718, + Age: 27, + Fare: 10.5, + Name: 'Troutt, Miss. Edwina Celia "Winnie"', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '36568', + Parch: 0, + Cabin: null, + PassengerId: 719, + Age: null, + Fare: 15.5, + Name: 'McEvoy, Mr. Michael', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347062', + Parch: 0, + Cabin: null, + PassengerId: 720, + Age: 33, + Fare: 7.775, + Name: 'Johnson, Mr. Malkolm Joackim', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '248727', + Parch: 1, + Cabin: null, + PassengerId: 721, + Age: 6, + Fare: 33, + Name: 'Harper, Miss. Annie Jessie "Nina"', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '350048', + Parch: 0, + Cabin: null, + PassengerId: 722, + Age: 17, + Fare: 7.0542, + Name: 'Jensen, Mr. Svend Lauritz', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '12233', + Parch: 0, + Cabin: null, + PassengerId: 723, + Age: 34, + Fare: 13, + Name: 'Gillespie, Mr. William Henry', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '250643', + Parch: 0, + Cabin: null, + PassengerId: 724, + Age: 50, + Fare: 13, + Name: 'Hodges, Mr. Henry Price', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113806', + Parch: 0, + Cabin: 'E8', + PassengerId: 725, + Age: 27, + Fare: 53.1, + Name: 'Chambers, Mr. Norman Campbell', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '315094', + Parch: 0, + Cabin: null, + PassengerId: 726, + Age: 20, + Fare: 8.6625, + Name: 'Oreskovic, Mr. Luka', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '31027', + Parch: 0, + Cabin: null, + PassengerId: 727, + Age: 30, + Fare: 21, + Name: 'Renouf, Mrs. Peter Henry (Lillian Jefferys)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '36866', + Parch: 0, + Cabin: null, + PassengerId: 728, + Age: null, + Fare: 7.7375, + Name: 'Mannion, Miss. Margareth', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '236853', + Parch: 0, + Cabin: null, + PassengerId: 729, + Age: 25, + Fare: 26, + Name: 'Bryhl, Mr. Kurt Arnold Gottfrid', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'STON/O2. 3101271', + Parch: 0, + Cabin: null, + PassengerId: 730, + Age: 25, + Fare: 7.925, + Name: 'Ilmakangas, Miss. Pieta Sofia', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '24160', + Parch: 0, + Cabin: 'B5', + PassengerId: 731, + Age: 29, + Fare: 211.3375, + Name: 'Allen, Miss. Elisabeth Walton', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2699', + Parch: 0, + Cabin: null, + PassengerId: 732, + Age: 11, + Fare: 18.7875, + Name: 'Hassan, Mr. Houssein G N', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '239855', + Parch: 0, + Cabin: null, + PassengerId: 733, + Age: null, + Fare: 0, + Name: 'Knight, Mr. Robert J', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '28425', + Parch: 0, + Cabin: null, + PassengerId: 734, + Age: 23, + Fare: 13, + Name: 'Berriman, Mr. William John', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '233639', + Parch: 0, + Cabin: null, + PassengerId: 735, + Age: 23, + Fare: 13, + Name: 'Troupiansky, Mr. Moses Aaron', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '54636', + Parch: 0, + Cabin: null, + PassengerId: 736, + Age: 28.5, + Fare: 16.1, + Name: 'Williams, Mr. Leslie', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'W./C. 6608', + Parch: 3, + Cabin: null, + PassengerId: 737, + Age: 48, + Fare: 34.375, + Name: 'Ford, Mrs. Edward (Margaret Ann Watson)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17755', + Parch: 0, + Cabin: 'B101', + PassengerId: 738, + Age: 35, + Fare: 512.3292, + Name: 'Lesurer, Mr. Gustave J', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349201', + Parch: 0, + Cabin: null, + PassengerId: 739, + Age: null, + Fare: 7.8958, + Name: 'Ivanoff, Mr. Kanio', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349218', + Parch: 0, + Cabin: null, + PassengerId: 740, + Age: null, + Fare: 7.8958, + Name: 'Nankoff, Mr. Minko', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '16988', + Parch: 0, + Cabin: 'D45', + PassengerId: 741, + Age: null, + Fare: 30, + Name: 'Hawksford, Mr. Walter James', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '19877', + Parch: 0, + Cabin: 'C46', + PassengerId: 742, + Age: 36, + Fare: 78.85, + Name: 'Cavendish, Mr. Tyrell William', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: 'PC 17608', + Parch: 2, + Cabin: 'B57 B59 B63 B66', + PassengerId: 743, + Age: 21, + Fare: 262.375, + Name: 'Ryerson, Miss. Susan Parker "Suzette"', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '376566', + Parch: 0, + Cabin: null, + PassengerId: 744, + Age: 24, + Fare: 16.1, + Name: 'McNamee, Mr. Neal', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'STON/O 2. 3101288', + Parch: 0, + Cabin: null, + PassengerId: 745, + Age: 31, + Fare: 7.925, + Name: 'Stranden, Mr. Juho', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'WE/P 5735', + Parch: 1, + Cabin: 'B22', + PassengerId: 746, + Age: 70, + Fare: 71, + Name: 'Crosby, Capt. Edward Gifford', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 2673', + Parch: 1, + Cabin: null, + PassengerId: 747, + Age: 16, + Fare: 20.25, + Name: 'Abbott, Mr. Rossmore Edward', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '250648', + Parch: 0, + Cabin: null, + PassengerId: 748, + Age: 30, + Fare: 13, + Name: 'Sinkkonen, Miss. Anna', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '113773', + Parch: 0, + Cabin: 'D30', + PassengerId: 749, + Age: 19, + Fare: 53.1, + Name: 'Marvin, Mr. Daniel Warner', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '335097', + Parch: 0, + Cabin: null, + PassengerId: 750, + Age: 31, + Fare: 7.75, + Name: 'Connaghton, Mr. Michael', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '29103', + Parch: 1, + Cabin: null, + PassengerId: 751, + Age: 4, + Fare: 23, + Name: 'Wells, Miss. Joan', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '392096', + Parch: 1, + Cabin: 'E121', + PassengerId: 752, + Age: 6, + Fare: 12.475, + Name: 'Moor, Master. Meier', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '345780', + Parch: 0, + Cabin: null, + PassengerId: 753, + Age: 33, + Fare: 9.5, + Name: 'Vande Velde, Mr. Johannes Joseph', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349204', + Parch: 0, + Cabin: null, + PassengerId: 754, + Age: 23, + Fare: 7.8958, + Name: 'Jonkoff, Mr. Lalio', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '220845', + Parch: 2, + Cabin: null, + PassengerId: 755, + Age: 48, + Fare: 65, + Name: 'Herman, Mrs. Samuel (Jane Laver)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '250649', + Parch: 1, + Cabin: null, + PassengerId: 756, + Age: 0.67, + Fare: 14.5, + Name: 'Hamalainen, Master. Viljo', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350042', + Parch: 0, + Cabin: null, + PassengerId: 757, + Age: 28, + Fare: 7.7958, + Name: 'Carlsson, Mr. August Sigfrid', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '29108', + Parch: 0, + Cabin: null, + PassengerId: 758, + Age: 18, + Fare: 11.5, + Name: 'Bailey, Mr. Percy Andrew', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '363294', + Parch: 0, + Cabin: null, + PassengerId: 759, + Age: 34, + Fare: 8.05, + Name: 'Theobald, Mr. Thomas Leonard', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '110152', + Parch: 0, + Cabin: 'B77', + PassengerId: 760, + Age: 33, + Fare: 86.5, + Name: 'Rothes, the Countess. of (Lucy Noel Martha Dyer-Edwards)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '358585', + Parch: 0, + Cabin: null, + PassengerId: 761, + Age: null, + Fare: 14.5, + Name: 'Garfirth, Mr. John', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/O2 3101272', + Parch: 0, + Cabin: null, + PassengerId: 762, + Age: 41, + Fare: 7.125, + Name: 'Nirva, Mr. Iisakki Antino Aijo', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2663', + Parch: 0, + Cabin: null, + PassengerId: 763, + Age: 20, + Fare: 7.2292, + Name: 'Barah, Mr. Hanna Assi', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113760', + Parch: 2, + Cabin: 'B96 B98', + PassengerId: 764, + Age: 36, + Fare: 120, + Name: 'Carter, Mrs. William Ernest (Lucile Polk)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '347074', + Parch: 0, + Cabin: null, + PassengerId: 765, + Age: 16, + Fare: 7.775, + Name: 'Eklund, Mr. Hans Linus', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '13502', + Parch: 0, + Cabin: 'D11', + PassengerId: 766, + Age: 51, + Fare: 77.9583, + Name: 'Hogeboom, Mrs. John C (Anna Andrews)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '112379', + Parch: 0, + Cabin: null, + PassengerId: 767, + Age: null, + Fare: 39.6, + Name: 'Brewe, Dr. Arthur Jackson', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '364850', + Parch: 0, + Cabin: null, + PassengerId: 768, + Age: 30.5, + Fare: 7.75, + Name: 'Mangan, Miss. Mary', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '371110', + Parch: 0, + Cabin: null, + PassengerId: 769, + Age: null, + Fare: 24.15, + Name: 'Moran, Mr. Daniel J', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '8471', + Parch: 0, + Cabin: null, + PassengerId: 770, + Age: 32, + Fare: 8.3625, + Name: 'Gronnestad, Mr. Daniel Danielsen', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '345781', + Parch: 0, + Cabin: null, + PassengerId: 771, + Age: 24, + Fare: 9.5, + Name: 'Lievens, Mr. Rene Aime', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '350047', + Parch: 0, + Cabin: null, + PassengerId: 772, + Age: 48, + Fare: 7.8542, + Name: 'Jensen, Mr. Niels Peder', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'S.O./P.P. 3', + Parch: 0, + Cabin: 'E77', + PassengerId: 773, + Age: 57, + Fare: 10.5, + Name: 'Mack, Mrs. (Mary)', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2674', + Parch: 0, + Cabin: null, + PassengerId: 774, + Age: null, + Fare: 7.225, + Name: 'Elias, Mr. Dibo', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '29105', + Parch: 3, + Cabin: null, + PassengerId: 775, + Age: 54, + Fare: 23, + Name: 'Hocking, Mrs. Elizabeth (Eliza Needs)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '347078', + Parch: 0, + Cabin: null, + PassengerId: 776, + Age: 18, + Fare: 7.75, + Name: 'Myhrman, Mr. Pehr Fabian Oliver Malkolm', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '383121', + Parch: 0, + Cabin: 'F38', + PassengerId: 777, + Age: null, + Fare: 7.75, + Name: 'Tobin, Mr. Roger', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '364516', + Parch: 0, + Cabin: null, + PassengerId: 778, + Age: 5, + Fare: 12.475, + Name: 'Emanuel, Miss. Virginia Ethel', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '36865', + Parch: 0, + Cabin: null, + PassengerId: 779, + Age: null, + Fare: 7.7375, + Name: 'Kilgannon, Mr. Thomas J', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '24160', + Parch: 1, + Cabin: 'B3', + PassengerId: 780, + Age: 43, + Fare: 211.3375, + Name: 'Robert, Mrs. Edward Scott (Elisabeth Walton McMillan)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2687', + Parch: 0, + Cabin: null, + PassengerId: 781, + Age: 13, + Fare: 7.2292, + Name: 'Ayoub, Miss. Banoura', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '17474', + Parch: 0, + Cabin: 'B20', + PassengerId: 782, + Age: 17, + Fare: 57, + Name: 'Dick, Mrs. Albert Adrian (Vera Gillespie)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113501', + Parch: 0, + Cabin: 'D6', + PassengerId: 783, + Age: 29, + Fare: 30, + Name: 'Long, Mr. Milton Clyde', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'W./C. 6607', + Parch: 2, + Cabin: null, + PassengerId: 784, + Age: null, + Fare: 23.45, + Name: 'Johnston, Mr. Andrew G', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/O.Q. 3101312', + Parch: 0, + Cabin: null, + PassengerId: 785, + Age: 25, + Fare: 7.05, + Name: 'Ali, Mr. William', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '374887', + Parch: 0, + Cabin: null, + PassengerId: 786, + Age: 25, + Fare: 7.25, + Name: 'Harmer, Mr. Abraham (David Lishin)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '3101265', + Parch: 0, + Cabin: null, + PassengerId: 787, + Age: 18, + Fare: 7.4958, + Name: 'Sjoblom, Miss. Anna Sofia', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 4, + Ticket: '382652', + Parch: 1, + Cabin: null, + PassengerId: 788, + Age: 8, + Fare: 29.125, + Name: 'Rice, Master. George Hugh', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 2315', + Parch: 2, + Cabin: null, + PassengerId: 789, + Age: 1, + Fare: 20.575, + Name: 'Dean, Master. Bertram Vere', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'PC 17593', + Parch: 0, + Cabin: 'B82 B84', + PassengerId: 790, + Age: 46, + Fare: 79.2, + Name: 'Guggenheim, Mr. Benjamin', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '12460', + Parch: 0, + Cabin: null, + PassengerId: 791, + Age: null, + Fare: 7.75, + Name: 'Keane, Mr. Andrew "Andy"', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '239865', + Parch: 0, + Cabin: null, + PassengerId: 792, + Age: 16, + Fare: 26, + Name: 'Gaskell, Mr. Alfred', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 8, + Ticket: 'CA. 2343', + Parch: 2, + Cabin: null, + PassengerId: 793, + Age: null, + Fare: 69.55, + Name: 'Sage, Miss. Stella Anna', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17600', + Parch: 0, + Cabin: null, + PassengerId: 794, + Age: null, + Fare: 30.6958, + Name: 'Hoyt, Mr. William Fisher', + Survived: false, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349203', + Parch: 0, + Cabin: null, + PassengerId: 795, + Age: 25, + Fare: 7.8958, + Name: 'Dantcheff, Mr. Ristiu', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '28213', + Parch: 0, + Cabin: null, + PassengerId: 796, + Age: 39, + Fare: 13, + Name: 'Otter, Mr. Richard', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '17465', + Parch: 0, + Cabin: 'D17', + PassengerId: 797, + Age: 49, + Fare: 25.9292, + Name: 'Leader, Dr. Alice (Farnham)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349244', + Parch: 0, + Cabin: null, + PassengerId: 798, + Age: 31, + Fare: 8.6833, + Name: 'Osman, Mrs. Mara', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2685', + Parch: 0, + Cabin: null, + PassengerId: 799, + Age: 30, + Fare: 7.2292, + Name: 'Ibrahim Shawah, Mr. Yousseff', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '345773', + Parch: 1, + Cabin: null, + PassengerId: 800, + Age: 30, + Fare: 24.15, + Name: 'Van Impe, Mrs. Jean Baptiste (Rosalie Paula Govaert)', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '250647', + Parch: 0, + Cabin: null, + PassengerId: 801, + Age: 34, + Fare: 13, + Name: 'Ponesell, Mr. Martin', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'C.A. 31921', + Parch: 1, + Cabin: null, + PassengerId: 802, + Age: 31, + Fare: 26.25, + Name: 'Collyer, Mrs. Harvey (Charlotte Annie Tate)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '113760', + Parch: 2, + Cabin: 'B96 B98', + PassengerId: 803, + Age: 11, + Fare: 120, + Name: 'Carter, Master. William Thornton II', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2625', + Parch: 1, + Cabin: null, + PassengerId: 804, + Age: 0.42, + Fare: 8.5167, + Name: 'Thomas, Master. Assad Alexander', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347089', + Parch: 0, + Cabin: null, + PassengerId: 805, + Age: 27, + Fare: 6.975, + Name: 'Hedman, Mr. Oskar Arvid', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347063', + Parch: 0, + Cabin: null, + PassengerId: 806, + Age: 31, + Fare: 7.775, + Name: 'Johansson, Mr. Karl Johan', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '112050', + Parch: 0, + Cabin: 'A36', + PassengerId: 807, + Age: 39, + Fare: 0, + Name: 'Andrews, Mr. Thomas Jr', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347087', + Parch: 0, + Cabin: null, + PassengerId: 808, + Age: 18, + Fare: 7.775, + Name: 'Pettersson, Miss. Ellen Natalia', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '248723', + Parch: 0, + Cabin: null, + PassengerId: 809, + Age: 39, + Fare: 13, + Name: 'Meyer, Mr. August', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '113806', + Parch: 0, + Cabin: 'E8', + PassengerId: 810, + Age: 33, + Fare: 53.1, + Name: 'Chambers, Mrs. Norman Campbell (Bertha Griggs)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '3474', + Parch: 0, + Cabin: null, + PassengerId: 811, + Age: 26, + Fare: 7.8875, + Name: 'Alexander, Mr. William', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'A/4 48871', + Parch: 0, + Cabin: null, + PassengerId: 812, + Age: 39, + Fare: 24.15, + Name: 'Lester, Mr. James', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '28206', + Parch: 0, + Cabin: null, + PassengerId: 813, + Age: 35, + Fare: 10.5, + Name: 'Slemen, Mr. Richard James', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 4, + Ticket: '347082', + Parch: 2, + Cabin: null, + PassengerId: 814, + Age: 6, + Fare: 31.275, + Name: 'Andersson, Miss. Ebba Iris Alfrida', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '364499', + Parch: 0, + Cabin: null, + PassengerId: 815, + Age: 30.5, + Fare: 8.05, + Name: 'Tomlin, Mr. Ernest Portage', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '112058', + Parch: 0, + Cabin: 'B102', + PassengerId: 816, + Age: null, + Fare: 0, + Name: 'Fry, Mr. Richard', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'STON/O2. 3101290', + Parch: 0, + Cabin: null, + PassengerId: 817, + Age: 23, + Fare: 7.925, + Name: 'Heininen, Miss. Wendla Maria', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'S.C./PARIS 2079', + Parch: 1, + Cabin: null, + PassengerId: 818, + Age: 31, + Fare: 37.0042, + Name: 'Mallet, Mr. Albert', + Survived: false, + Pclass: 2, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C 7075', + Parch: 0, + Cabin: null, + PassengerId: 819, + Age: 43, + Fare: 6.45, + Name: 'Holm, Mr. John Fredrik Alexander', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 3, + Ticket: '347088', + Parch: 2, + Cabin: null, + PassengerId: 820, + Age: 10, + Fare: 27.9, + Name: 'Skoog, Master. Karl Thorsten', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '12749', + Parch: 1, + Cabin: 'B69', + PassengerId: 821, + Age: 52, + Fare: 93.5, + Name: 'Hays, Mrs. Charles Melville (Clara Jennings Gregg)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '315098', + Parch: 0, + Cabin: null, + PassengerId: 822, + Age: 27, + Fare: 8.6625, + Name: 'Lulic, Mr. Nikola', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '19972', + Parch: 0, + Cabin: null, + PassengerId: 823, + Age: 38, + Fare: 0, + Name: 'Reuchlin, Jonkheer. John George', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '392096', + Parch: 1, + Cabin: 'E121', + PassengerId: 824, + Age: 27, + Fare: 12.475, + Name: 'Moor, Mrs. (Beila)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 4, + Ticket: '3101295', + Parch: 1, + Cabin: null, + PassengerId: 825, + Age: 2, + Fare: 39.6875, + Name: 'Panula, Master. Urho Abraham', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '368323', + Parch: 0, + Cabin: null, + PassengerId: 826, + Age: null, + Fare: 6.95, + Name: 'Flynn, Mr. John', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '1601', + Parch: 0, + Cabin: null, + PassengerId: 827, + Age: null, + Fare: 56.4958, + Name: 'Lam, Mr. Len', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'S.C./PARIS 2079', + Parch: 2, + Cabin: null, + PassengerId: 828, + Age: 1, + Fare: 37.0042, + Name: 'Mallet, Master. Andre', + Survived: true, + Pclass: 2, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '367228', + Parch: 0, + Cabin: null, + PassengerId: 829, + Age: null, + Fare: 7.75, + Name: 'McCormack, Mr. Thomas Joseph', + Survived: true, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113572', + Parch: 0, + Cabin: 'B28', + PassengerId: 830, + Age: 62, + Fare: 80, + Name: 'Stone, Mrs. George Nelson (Martha Evelyn)', + Survived: true, + Pclass: 1, + Embarked: null, + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '2659', + Parch: 0, + Cabin: null, + PassengerId: 831, + Age: 15, + Fare: 14.4542, + Name: 'Yasbeck, Mrs. Antoni (Selini Alexander)', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '29106', + Parch: 1, + Cabin: null, + PassengerId: 832, + Age: 0.83, + Fare: 18.75, + Name: 'Richards, Master. George Sibley', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2671', + Parch: 0, + Cabin: null, + PassengerId: 833, + Age: null, + Fare: 7.2292, + Name: 'Saad, Mr. Amin', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347468', + Parch: 0, + Cabin: null, + PassengerId: 834, + Age: 23, + Fare: 7.8542, + Name: 'Augustsson, Mr. Albert', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2223', + Parch: 0, + Cabin: null, + PassengerId: 835, + Age: 18, + Fare: 8.3, + Name: 'Allum, Mr. Owen George', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'PC 17756', + Parch: 1, + Cabin: 'E49', + PassengerId: 836, + Age: 39, + Fare: 83.1583, + Name: 'Compton, Miss. Sara Rebecca', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '315097', + Parch: 0, + Cabin: null, + PassengerId: 837, + Age: 21, + Fare: 8.6625, + Name: 'Pasic, Mr. Jakob', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '392092', + Parch: 0, + Cabin: null, + PassengerId: 838, + Age: null, + Fare: 8.05, + Name: 'Sirota, Mr. Maurice', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '1601', + Parch: 0, + Cabin: null, + PassengerId: 839, + Age: 32, + Fare: 56.4958, + Name: 'Chip, Mr. Chang', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '11774', + Parch: 0, + Cabin: 'C47', + PassengerId: 840, + Age: null, + Fare: 29.7, + Name: 'Marechal, Mr. Pierre', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/O2 3101287', + Parch: 0, + Cabin: null, + PassengerId: 841, + Age: 20, + Fare: 7.925, + Name: 'Alhomaki, Mr. Ilmari Rudolf', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'S.O./P.P. 3', + Parch: 0, + Cabin: null, + PassengerId: 842, + Age: 16, + Fare: 10.5, + Name: 'Mudd, Mr. Thomas Charles', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '113798', + Parch: 0, + Cabin: null, + PassengerId: 843, + Age: 30, + Fare: 31, + Name: 'Serepeca, Miss. Augusta', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2683', + Parch: 0, + Cabin: null, + PassengerId: 844, + Age: 34.5, + Fare: 6.4375, + Name: 'Lemberopolous, Mr. Peter L', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '315090', + Parch: 0, + Cabin: null, + PassengerId: 845, + Age: 17, + Fare: 8.6625, + Name: 'Culumovic, Mr. Jeso', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'C.A. 5547', + Parch: 0, + Cabin: null, + PassengerId: 846, + Age: 42, + Fare: 7.55, + Name: 'Abbing, Mr. Anthony', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 8, + Ticket: 'CA. 2343', + Parch: 2, + Cabin: null, + PassengerId: 847, + Age: null, + Fare: 69.55, + Name: 'Sage, Mr. Douglas Bullen', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349213', + Parch: 0, + Cabin: null, + PassengerId: 848, + Age: 35, + Fare: 7.8958, + Name: 'Markoff, Mr. Marin', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '248727', + Parch: 1, + Cabin: null, + PassengerId: 849, + Age: 28, + Fare: 33, + Name: 'Harper, Rev. John', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '17453', + Parch: 0, + Cabin: 'C92', + PassengerId: 850, + Age: null, + Fare: 89.1042, + Name: 'Goldenberg, Mrs. Samuel L (Edwiga Grabowska)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 4, + Ticket: '347082', + Parch: 2, + Cabin: null, + PassengerId: 851, + Age: 4, + Fare: 31.275, + Name: 'Andersson, Master. Sigvard Harald Elias', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '347060', + Parch: 0, + Cabin: null, + PassengerId: 852, + Age: 74, + Fare: 7.775, + Name: 'Svensson, Mr. Johan', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '2678', + Parch: 1, + Cabin: null, + PassengerId: 853, + Age: 9, + Fare: 15.2458, + Name: 'Boulos, Miss. Nourelain', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17592', + Parch: 1, + Cabin: 'D28', + PassengerId: 854, + Age: 16, + Fare: 39.4, + Name: 'Lines, Miss. Mary Conover', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '244252', + Parch: 0, + Cabin: null, + PassengerId: 855, + Age: 44, + Fare: 26, + Name: 'Carter, Mrs. Ernest Courtenay (Lilian Hughes)', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '392091', + Parch: 1, + Cabin: null, + PassengerId: 856, + Age: 18, + Fare: 9.35, + Name: 'Aks, Mrs. Sam (Leah Rosen)', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: '36928', + Parch: 1, + Cabin: null, + PassengerId: 857, + Age: 45, + Fare: 164.8667, + Name: 'Wick, Mrs. George Dennick (Mary Hitchcock)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '113055', + Parch: 0, + Cabin: 'E17', + PassengerId: 858, + Age: 51, + Fare: 26.55, + Name: 'Daly, Mr. Peter Denis ', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '2666', + Parch: 3, + Cabin: null, + PassengerId: 859, + Age: 24, + Fare: 19.2583, + Name: 'Baclini, Mrs. Solomon (Latifa Qurban)', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2629', + Parch: 0, + Cabin: null, + PassengerId: 860, + Age: null, + Fare: 7.2292, + Name: 'Razi, Mr. Raihed', + Survived: false, + Pclass: 3, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 2, + Ticket: '350026', + Parch: 0, + Cabin: null, + PassengerId: 861, + Age: 41, + Fare: 14.1083, + Name: 'Hansen, Mr. Claus Peter', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '28134', + Parch: 0, + Cabin: null, + PassengerId: 862, + Age: 21, + Fare: 11.5, + Name: 'Giles, Mr. Frederick Edward', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '17466', + Parch: 0, + Cabin: 'D17', + PassengerId: 863, + Age: 48, + Fare: 25.9292, + Name: 'Swift, Mrs. Frederick Joel (Margaret Welles Barron)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 8, + Ticket: 'CA. 2343', + Parch: 2, + Cabin: null, + PassengerId: 864, + Age: null, + Fare: 69.55, + Name: 'Sage, Miss. Dorothy Edith "Dolly"', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '233866', + Parch: 0, + Cabin: null, + PassengerId: 865, + Age: 24, + Fare: 13, + Name: 'Gill, Mr. John William', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '236852', + Parch: 0, + Cabin: null, + PassengerId: 866, + Age: 42, + Fare: 13, + Name: 'Bystrom, Mrs. (Karolina)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'SC/PARIS 2149', + Parch: 0, + Cabin: null, + PassengerId: 867, + Age: 27, + Fare: 13.8583, + Name: 'Duran y More, Miss. Asuncion', + Survived: true, + Pclass: 2, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'PC 17590', + Parch: 0, + Cabin: 'A24', + PassengerId: 868, + Age: 31, + Fare: 50.4958, + Name: 'Roebling, Mr. Washington Augustus II', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '345777', + Parch: 0, + Cabin: null, + PassengerId: 869, + Age: null, + Fare: 9.5, + Name: 'van Melkebeke, Mr. Philemon', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '347742', + Parch: 1, + Cabin: null, + PassengerId: 870, + Age: 4, + Fare: 11.1333, + Name: 'Johnson, Master. Harold Theodor', + Survived: true, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349248', + Parch: 0, + Cabin: null, + PassengerId: 871, + Age: 26, + Fare: 7.8958, + Name: 'Balkic, Mr. Cerin', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: '11751', + Parch: 1, + Cabin: 'D35', + PassengerId: 872, + Age: 47, + Fare: 52.5542, + Name: 'Beckwith, Mrs. Richard Leonard (Sallie Monypeny)', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '695', + Parch: 0, + Cabin: 'B51 B53 B55', + PassengerId: 873, + Age: 33, + Fare: 5, + Name: 'Carlsson, Mr. Frans Olof', + Survived: false, + Pclass: 1, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '345765', + Parch: 0, + Cabin: null, + PassengerId: 874, + Age: 47, + Fare: 9, + Name: 'Vander Cruyssen, Mr. Victor', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 1, + Ticket: 'P/PP 3381', + Parch: 0, + Cabin: null, + PassengerId: 875, + Age: 28, + Fare: 24, + Name: 'Abelson, Mrs. Samuel (Hannah Wizosky)', + Survived: true, + Pclass: 2, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '2667', + Parch: 0, + Cabin: null, + PassengerId: 876, + Age: 15, + Fare: 7.225, + Name: 'Najib, Miss. Adele Kiamie "Jane"', + Survived: true, + Pclass: 3, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '7534', + Parch: 0, + Cabin: null, + PassengerId: 877, + Age: 20, + Fare: 9.8458, + Name: 'Gustafsson, Mr. Alfred Ossian', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349212', + Parch: 0, + Cabin: null, + PassengerId: 878, + Age: 19, + Fare: 7.8958, + Name: 'Petroff, Mr. Nedelio', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '349217', + Parch: 0, + Cabin: null, + PassengerId: 879, + Age: null, + Fare: 7.8958, + Name: 'Laleff, Mr. Kristo', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '11767', + Parch: 1, + Cabin: 'C50', + PassengerId: 880, + Age: 56, + Fare: 83.1583, + Name: 'Potter, Mrs. Thomas Jr (Lily Alexenia Wilson)', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '230433', + Parch: 1, + Cabin: null, + PassengerId: 881, + Age: 25, + Fare: 26, + Name: 'Shelley, Mrs. William (Imanita Parrish Hall)', + Survived: true, + Pclass: 2, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '349257', + Parch: 0, + Cabin: null, + PassengerId: 882, + Age: 33, + Fare: 7.8958, + Name: 'Markun, Mr. Johann', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '7552', + Parch: 0, + Cabin: null, + PassengerId: 883, + Age: 22, + Fare: 10.5167, + Name: 'Dahlberg, Miss. Gerda Ulrika', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: 'C.A./SOTON 34068', + Parch: 0, + Cabin: null, + PassengerId: 884, + Age: 28, + Fare: 10.5, + Name: 'Banfield, Mr. Frederick James', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: 'SOTON/OQ 392076', + Parch: 0, + Cabin: null, + PassengerId: 885, + Age: 25, + Fare: 7.05, + Name: 'Sutehall, Mr. Henry Jr', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '382652', + Parch: 5, + Cabin: null, + PassengerId: 886, + Age: 39, + Fare: 29.125, + Name: 'Rice, Mrs. William (Margaret Norton)', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '211536', + Parch: 0, + Cabin: null, + PassengerId: 887, + Age: 27, + Fare: 13, + Name: 'Montvila, Rev. Juozas', + Survived: false, + Pclass: 2, + Embarked: 'S', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '112053', + Parch: 0, + Cabin: 'B42', + PassengerId: 888, + Age: 19, + Fare: 30, + Name: 'Graham, Miss. Margaret Edith', + Survived: true, + Pclass: 1, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 1, + Ticket: 'W./C. 6607', + Parch: 2, + Cabin: null, + PassengerId: 889, + Age: null, + Fare: 23.45, + Name: 'Johnston, Miss. Catherine Helen "Carrie"', + Survived: false, + Pclass: 3, + Embarked: 'S', + Sex: 'female' + }, + { + SibSp: 0, + Ticket: '111369', + Parch: 0, + Cabin: 'C148', + PassengerId: 890, + Age: 26, + Fare: 30, + Name: 'Behr, Mr. Karl Howell', + Survived: true, + Pclass: 1, + Embarked: 'C', + Sex: 'male' + }, + { + SibSp: 0, + Ticket: '370376', + Parch: 0, + Cabin: null, + PassengerId: 891, + Age: 32, + Fare: 7.75, + Name: 'Dooley, Mr. Patrick', + Survived: false, + Pclass: 3, + Embarked: 'Q', + Sex: 'male' + } +]; diff --git a/src/datascience-ui/history-react/index.tsx b/src/datascience-ui/history-react/index.tsx index 2b28730275f2..8e3fabff5237 100644 --- a/src/datascience-ui/history-react/index.tsx +++ b/src/datascience-ui/history-react/index.tsx @@ -1,45 +1,45 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -// This must be on top, do not change. Required by webpack. -import '../common/main'; -// This must be on top, do not change. Required by webpack. - -// tslint:disable-next-line: ordered-imports -import '../common/index.css'; - -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { Provider } from 'react-redux'; - -import { WidgetManagerComponent } from '../ipywidgets/container'; -import { IVsCodeApi, PostOffice } from '../react-common/postOffice'; -import { detectBaseTheme } from '../react-common/themeDetector'; -import { getConnectedInteractiveEditor } from './interactivePanel'; -import { createStore } from './redux/store'; - -// This special function talks to vscode from a web panel -export declare function acquireVsCodeApi(): IVsCodeApi; -const baseTheme = detectBaseTheme(); -// tslint:disable-next-line: no-any -const testMode = (window as any).inTestMode; -// tslint:disable-next-line: no-typeof-undefined -const skipDefault = testMode ? false : typeof acquireVsCodeApi !== 'undefined'; - -// Create the redux store -const postOffice = new PostOffice(); -const store = createStore(skipDefault, baseTheme, testMode, postOffice); - -// Wire up a connected react control for our InteractiveEditor -const ConnectedInteractiveEditor = getConnectedInteractiveEditor(); - -// Stick them all together -// tslint:disable:no-typeof-undefined -ReactDOM.render( - - - - , - document.getElementById('root') as HTMLElement -); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +// This must be on top, do not change. Required by webpack. +import '../common/main'; +// This must be on top, do not change. Required by webpack. + +// tslint:disable-next-line: ordered-imports +import '../common/index.css'; + +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { Provider } from 'react-redux'; + +import { WidgetManagerComponent } from '../ipywidgets/container'; +import { IVsCodeApi, PostOffice } from '../react-common/postOffice'; +import { detectBaseTheme } from '../react-common/themeDetector'; +import { getConnectedInteractiveEditor } from './interactivePanel'; +import { createStore } from './redux/store'; + +// This special function talks to vscode from a web panel +export declare function acquireVsCodeApi(): IVsCodeApi; +const baseTheme = detectBaseTheme(); +// tslint:disable-next-line: no-any +const testMode = (window as any).inTestMode; +// tslint:disable-next-line: no-typeof-undefined +const skipDefault = testMode ? false : typeof acquireVsCodeApi !== 'undefined'; + +// Create the redux store +const postOffice = new PostOffice(); +const store = createStore(skipDefault, baseTheme, testMode, postOffice); + +// Wire up a connected react control for our InteractiveEditor +const ConnectedInteractiveEditor = getConnectedInteractiveEditor(); + +// Stick them all together +// tslint:disable:no-typeof-undefined +ReactDOM.render( + + + + , + document.getElementById('root') as HTMLElement +); diff --git a/src/datascience-ui/history-react/redux/reducers/creation.ts b/src/datascience-ui/history-react/redux/reducers/creation.ts index 4586967ea4a2..a824b3c8a3f0 100644 --- a/src/datascience-ui/history-react/redux/reducers/creation.ts +++ b/src/datascience-ui/history-react/redux/reducers/creation.ts @@ -151,7 +151,7 @@ export namespace Creation { } export function deleteCell(arg: InteractiveReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (index >= 0 && arg.payload.data.cellId) { // Send messages to other side to indicate the delete postActionToExtension(arg, InteractiveWindowMessages.UpdateModel, { diff --git a/src/datascience-ui/history-react/redux/reducers/effects.ts b/src/datascience-ui/history-react/redux/reducers/effects.ts index d23215ade11d..83cf3d14104d 100644 --- a/src/datascience-ui/history-react/redux/reducers/effects.ts +++ b/src/datascience-ui/history-react/redux/reducers/effects.ts @@ -16,7 +16,7 @@ import { Creation } from './creation'; export namespace Effects { export function expandAll(arg: InteractiveReducerArg): IMainState { if (arg.prevState.settings?.showCellInputCode) { - const newVMs = arg.prevState.cellVMs.map(c => + const newVMs = arg.prevState.cellVMs.map((c) => Creation.alterCellVM({ ...c }, arg.prevState.settings, true, true) ); return { @@ -29,7 +29,7 @@ export namespace Effects { export function collapseAll(arg: InteractiveReducerArg): IMainState { if (arg.prevState.settings?.showCellInputCode) { - const newVMs = arg.prevState.cellVMs.map(c => + const newVMs = arg.prevState.cellVMs.map((c) => Creation.alterCellVM({ ...c }, arg.prevState.settings, true, false) ); return { @@ -43,7 +43,7 @@ export namespace Effects { export function toggleInputBlock(arg: InteractiveReducerArg): IMainState { if (arg.payload.data.cellId) { const newVMs = [...arg.prevState.cellVMs]; - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); const oldVM = arg.prevState.cellVMs[index]; newVMs[index] = Creation.alterCellVM({ ...oldVM }, arg.prevState.settings, true, !oldVM.inputBlockOpen); return { @@ -81,7 +81,7 @@ export namespace Effects { // Update our input cell state if the user changed this setting let newVMs = arg.prevState.cellVMs; if (newSettings.showCellInputCode !== arg.prevState.settings?.showCellInputCode) { - newVMs = arg.prevState.cellVMs.map(c => + newVMs = arg.prevState.cellVMs.map((c) => Creation.alterCellVM( c, newSettings, @@ -105,7 +105,7 @@ export namespace Effects { export function scrollToCell(arg: InteractiveReducerArg): IMainState { // Up the scroll count on the necessary cell - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.id); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.id); if (index >= 0) { const newVMs = [...arg.prevState.cellVMs]; diff --git a/src/datascience-ui/interactive-common/cellOutput.tsx b/src/datascience-ui/interactive-common/cellOutput.tsx index 3394bd795557..06ac78171f99 100644 --- a/src/datascience-ui/interactive-common/cellOutput.tsx +++ b/src/datascience-ui/interactive-common/cellOutput.tsx @@ -212,7 +212,7 @@ export class CellOutput extends React.Component { return getLocString('DataScience.unknownMimeTypeFormat', 'Unknown Mime Type'); } private destroyIPyWidgets() { - this.renderedView.forEach(view => { + this.renderedView.forEach((view) => { try { view.dispose(); } catch { @@ -317,9 +317,9 @@ export class CellOutput extends React.Component { if (this.isCodeCell()) { return ( this.renderCodeOutputs() - .filter(item => !!item) + .filter((item) => !!item) // tslint:disable-next-line: no-any - .map(item => (item as any) as JSX.Element) + .map((item) => (item as any) as JSX.Element) ); } else if (this.props.cellVM.cell.id !== Identifiers.EditCellId) { return this.renderMarkdownOutputs(); @@ -594,7 +594,7 @@ export class CellOutput extends React.Component { const style: React.CSSProperties = {}; // Create a scrollbar style if necessary - if (transformedList.some(transformed => transformed.output.renderWithScrollbars) && this.props.maxTextSize) { + if (transformedList.some((transformed) => transformed.output.renderWithScrollbars) && this.props.maxTextSize) { style.overflowY = 'auto'; style.maxHeight = `${this.props.maxTextSize}px`; } diff --git a/src/datascience-ui/interactive-common/editor.tsx b/src/datascience-ui/interactive-common/editor.tsx index 0bb4bb7efcf6..a4759305cf55 100644 --- a/src/datascience-ui/interactive-common/editor.tsx +++ b/src/datascience-ui/interactive-common/editor.tsx @@ -48,7 +48,7 @@ export class Editor extends React.Component { } public componentWillUnmount = () => { - this.subscriptions.forEach(d => d.dispose()); + this.subscriptions.forEach((d) => d.dispose()); }; public render() { diff --git a/src/datascience-ui/interactive-common/intellisenseProvider.ts b/src/datascience-ui/interactive-common/intellisenseProvider.ts index ea18250f2635..7cc6f7c9b95c 100644 --- a/src/datascience-ui/interactive-common/intellisenseProvider.ts +++ b/src/datascience-ui/interactive-common/intellisenseProvider.ts @@ -173,10 +173,10 @@ export class IntellisenseProvider public dispose() { this.disposed = true; - this.registerDisposables.forEach(r => r.dispose()); - this.completionRequests.forEach(r => r.promise.resolve()); - this.resolveCompletionRequests.forEach(r => r.promise.resolve()); - this.hoverRequests.forEach(r => r.promise.resolve()); + this.registerDisposables.forEach((r) => r.dispose()); + this.completionRequests.forEach((r) => r.promise.resolve()); + this.resolveCompletionRequests.forEach((r) => r.promise.resolve()); + this.hoverRequests.forEach((r) => r.promise.resolve()); this.registerDisposables = []; this.completionRequests.clear(); diff --git a/src/datascience-ui/interactive-common/mainState.ts b/src/datascience-ui/interactive-common/mainState.ts index 6e7b516e798f..3f287fcc1456 100644 --- a/src/datascience-ui/interactive-common/mainState.ts +++ b/src/datascience-ui/interactive-common/mainState.ts @@ -224,7 +224,7 @@ export function extractInputText(inputCellVM: ICellViewModel, settings: IDataSci } // Eliminate the lines to hide if we're debugging if (inputCell.extraLines) { - inputCell.extraLines.forEach(i => source.splice(i, 1)); + inputCell.extraLines.forEach((i) => source.splice(i, 1)); inputCell.extraLines = undefined; } } diff --git a/src/datascience-ui/interactive-common/redux/postOffice.ts b/src/datascience-ui/interactive-common/redux/postOffice.ts index 5d123cb64928..bfddb6a37a90 100644 --- a/src/datascience-ui/interactive-common/redux/postOffice.ts +++ b/src/datascience-ui/interactive-common/redux/postOffice.ts @@ -16,7 +16,7 @@ export const AllowedIPyWidgetMessages = [...Object.values(IPyWidgetMessages)]; export function generatePostOfficeSendReducer(postOffice: PostOffice): Redux.Reducer<{}, Redux.AnyAction> { // tslint:disable-next-line: no-function-expression - return function(_state: {} | undefined, action: Redux.AnyAction): {} { + return function (_state: {} | undefined, action: Redux.AnyAction): {} { if (isAllowedAction(action)) { // Make sure a valid message if (action.type === CommonActionType.PostOutgoingMessage) { diff --git a/src/datascience-ui/interactive-common/redux/reducers/helpers.ts b/src/datascience-ui/interactive-common/redux/reducers/helpers.ts index 19ae011b45fe..c6c69865db42 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/helpers.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/helpers.ts @@ -30,8 +30,8 @@ export namespace Helpers { } export function firstCodeCellAbove(state: IMainState, cellId: string | undefined) { - const codeCells = state.cellVMs.filter(c => c.cell.data.cell_type === 'code'); - const index = codeCells.findIndex(c => c.cell.id === cellId); + const codeCells = state.cellVMs.filter((c) => c.cell.data.cell_type === 'code'); + const index = codeCells.findIndex((c) => c.cell.id === cellId); if (index > 0) { return codeCells[index - 1].cell.id; } diff --git a/src/datascience-ui/interactive-common/redux/reducers/monaco.ts b/src/datascience-ui/interactive-common/redux/reducers/monaco.ts index 7a3ee736d173..5ce1827f747f 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/monaco.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/monaco.ts @@ -69,7 +69,7 @@ function handleStarted(arg: MonacoReducerArg): IMonacoState { } function finishTokenizer(buffer: ArrayBuffer, tmJson: string, arg: MonacoReducerArg) { - initializeTokenizer(buffer, tmJson, e => { + initializeTokenizer(buffer, tmJson, (e) => { if (e) { logMessage(`ERROR from onigasm: ${e}`); } diff --git a/src/datascience-ui/interactive-common/redux/reducers/transfer.ts b/src/datascience-ui/interactive-common/redux/reducers/transfer.ts index f39d35bac67c..497b7527f7ac 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/transfer.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/transfer.ts @@ -25,7 +25,7 @@ import { // These are all reducers that don't actually change state. They merely dispatch a message to the other side. export namespace Transfer { export function exportCells(arg: CommonReducerArg): IMainState { - const cellContents = arg.prevState.cellVMs.map(v => v.cell); + const cellContents = arg.prevState.cellVMs.map((v) => v.cell); postActionToExtension(arg, InteractiveWindowMessages.Export, cellContents); // Indicate busy @@ -40,7 +40,7 @@ export namespace Transfer { // Actually waiting for save results before marking as not dirty, so don't do it here. postActionToExtension(arg, InteractiveWindowMessages.SaveAll, { - cells: arg.prevState.cellVMs.map(cvm => cvm.cell) + cells: arg.prevState.cellVMs.map((cvm) => cvm.cell) }); return arg.prevState; } @@ -80,13 +80,13 @@ export namespace Transfer { } export function getAllCells(arg: CommonReducerArg): IMainState { - const cells = arg.prevState.cellVMs.map(c => c.cell); + const cells = arg.prevState.cellVMs.map((c) => c.cell); postActionToExtension(arg, InteractiveWindowMessages.ReturnAllCells, cells); return arg.prevState; } export function gotoCell(arg: CommonReducerArg): IMainState { - const cellVM = arg.prevState.cellVMs.find(c => c.cell.id === arg.payload.data.cellId); + const cellVM = arg.prevState.cellVMs.find((c) => c.cell.id === arg.payload.data.cellId); if (cellVM && cellVM.cell.data.cell_type === 'code') { postActionToExtension(arg, InteractiveWindowMessages.GotoCodeCell, { file: cellVM.cell.file, @@ -97,7 +97,7 @@ export namespace Transfer { } export function copyCellCode(arg: CommonReducerArg): IMainState { - let cellVM = arg.prevState.cellVMs.find(c => c.cell.id === arg.payload.data.cellId); + let cellVM = arg.prevState.cellVMs.find((c) => c.cell.id === arg.payload.data.cellId); if (!cellVM && arg.prevState.editCellVM && arg.payload.data.cellId === arg.prevState.editCellVM.cell.id) { cellVM = arg.prevState.editCellVM; } @@ -113,7 +113,7 @@ export namespace Transfer { } export function gather(arg: CommonReducerArg): IMainState { - const cellVM = arg.prevState.cellVMs.find(c => c.cell.id === arg.payload.data.cellId); + const cellVM = arg.prevState.cellVMs.find((c) => c.cell.id === arg.payload.data.cellId); if (cellVM) { postActionToExtension(arg, InteractiveWindowMessages.GatherCodeRequest, cellVM.cell); } @@ -186,7 +186,7 @@ export namespace Transfer { oldDirty: arg.prevState.dirty, newDirty: true, // tslint:disable-next-line: no-any - oldCells: arg.prevState.cellVMs.map(c => c.cell as any) as ICell[] + oldCells: arg.prevState.cellVMs.map((c) => c.cell as any) as ICell[] }); } @@ -197,7 +197,7 @@ export namespace Transfer { oldDirty: arg.prevState.dirty, newDirty: true, // tslint:disable-next-line: no-any - oldCells: arg.prevState.cellVMs.map(c => c.cell as any) as ICell[], + oldCells: arg.prevState.cellVMs.map((c) => c.cell as any) as ICell[], newCellId }); } @@ -221,7 +221,7 @@ export namespace Transfer { const cellVM = arg.payload.data.cellId === Identifiers.EditCellId ? arg.prevState.editCellVM - : arg.prevState.cellVMs.find(c => c.cell.id === arg.payload.data.cellId); + : arg.prevState.cellVMs.find((c) => c.cell.id === arg.payload.data.cellId); if (cellVM) { // Tell the underlying model on the extension side postModelEdit(arg, arg.payload.data.forward, arg.payload.data.reverse, cellVM.cell.id); @@ -229,7 +229,7 @@ export namespace Transfer { // Update the uncommitted text on the cell view model // We keep this saved here so we don't re-render and we put this code into the input / code data // when focus is lost - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); const selectionInfo = getSelectedAndFocusedInfo(arg.prevState); // If this is the focused cell, then user is editing it, hence it needs to be updated. const isThisTheFocusedCell = selectionInfo.focusedCellId === arg.payload.data.cellId; @@ -276,7 +276,7 @@ export namespace Transfer { export function loadedAllCells(arg: CommonReducerArg): IMainState { postActionToExtension(arg, InteractiveWindowMessages.LoadAllCellsComplete, { - cells: arg.prevState.cellVMs.map(c => c.cell) + cells: arg.prevState.cellVMs.map((c) => c.cell) }); return arg.prevState; } diff --git a/src/datascience-ui/interactive-common/redux/store.ts b/src/datascience-ui/interactive-common/redux/store.ts index ef5a5699cb4f..93e50fb56d0e 100644 --- a/src/datascience-ui/interactive-common/redux/store.ts +++ b/src/datascience-ui/interactive-common/redux/store.ts @@ -84,7 +84,7 @@ function generateMainReducer( } function createSendInfoMiddleware(): Redux.Middleware<{}, IStore> { - return store => next => action => { + return (store) => (next) => (action) => { const prevState = store.getState(); const res = next(action); const afterState = store.getState(); @@ -135,7 +135,7 @@ function createTestMiddleware(): Redux.Middleware<{}, IStore> { // Make sure all dynamic imports are loaded. const transformPromise = forceLoad(); - return store => next => action => { + return (store) => (next) => (action) => { const prevState = store.getState(); const res = next(action); const afterState = store.getState(); @@ -189,17 +189,17 @@ function createTestMiddleware(): Redux.Middleware<{}, IStore> { // Special case for rendering complete const prevFinished = prevState.main.cellVMs - .filter(c => c.cell.state === CellState.finished || c.cell.state === CellState.error) - .map(c => c.cell.id); + .filter((c) => c.cell.state === CellState.finished || c.cell.state === CellState.error) + .map((c) => c.cell.id); const afterFinished = afterState.main.cellVMs - .filter(c => c.cell.state === CellState.finished || c.cell.state === CellState.error) - .map(c => c.cell.id); + .filter((c) => c.cell.state === CellState.finished || c.cell.state === CellState.error) + .map((c) => c.cell.id); if ( afterFinished.length > prevFinished.length || (afterFinished.length !== prevFinished.length && afterState.main.cellVMs.length !== prevState.main.cellVMs.length) ) { - const diff = afterFinished.filter(r => prevFinished.indexOf(r) < 0); + const diff = afterFinished.filter((r) => prevFinished.indexOf(r) < 0); // Send async so happens after the render is actually finished. sendMessage(InteractiveWindowMessages.ExecutionRendered, { ids: diff }); } @@ -302,7 +302,7 @@ export interface IMainWithVariables extends IMainState { /** * Middleware that will ensure all actions have `messageDirection` property. */ -const addMessageDirectionMiddleware: Redux.Middleware = _store => next => (action: Redux.AnyAction) => { +const addMessageDirectionMiddleware: Redux.Middleware = (_store) => (next) => (action: Redux.AnyAction) => { if (isAllowedAction(action)) { // Ensure all dispatched messages have been flagged as `incoming`. const payload: BaseReduxActionPayload<{}> = action.payload || {}; diff --git a/src/datascience-ui/interactive-common/tokenizer.ts b/src/datascience-ui/interactive-common/tokenizer.ts index 500699548ab1..7b42bb9bfbf6 100644 --- a/src/datascience-ui/interactive-common/tokenizer.ts +++ b/src/datascience-ui/interactive-common/tokenizer.ts @@ -73,7 +73,7 @@ export async function initializeTokenizer( // Setup our registry of different const registry = new Registry({ - getGrammarDefinition: async _scopeName => { + getGrammarDefinition: async (_scopeName) => { return { format: 'json', content: tmlanguageJSON diff --git a/src/datascience-ui/interactive-common/transforms.tsx b/src/datascience-ui/interactive-common/transforms.tsx index 35b5864dcf13..18725852df42 100644 --- a/src/datascience-ui/interactive-common/transforms.tsx +++ b/src/datascience-ui/interactive-common/transforms.tsx @@ -115,8 +115,8 @@ export function getRichestMimetype(data: any): string { // Go through the keys of this object and find their index in the map let index = mimeTypeToImport.length; const keys = Object.keys(data); - keys.forEach(k => { - const keyIndex = mimeTypeToImport.findIndex(m => m.mimeType === k); + keys.forEach((k) => { + const keyIndex = mimeTypeToImport.findIndex((m) => m.mimeType === k); if (keyIndex >= 0 && keyIndex < index) { // If higher up the chain, pick the higher up key index = keyIndex; @@ -135,7 +135,7 @@ export function getRichestMimetype(data: any): string { export function getTransform(mimeType: string): LoadableComponent<{ data: any }> { return Loadable<{ data: any }>( async () => { - const match = mimeTypeToImport.find(m => m.mimeType === mimeType); + const match = mimeTypeToImport.find((m) => m.mimeType === mimeType); if (match) { const transform = await match.getComponent(); return transform; @@ -149,11 +149,11 @@ export function getTransform(mimeType: string): LoadableComponent<{ data: any }> export async function forceLoad() { // Used for tests to make sure we don't end up with 'Loading ...' anywhere in a test - await Promise.all(mimeTypeToImport.map(m => m.getComponent())); + await Promise.all(mimeTypeToImport.map((m) => m.getComponent())); } export function isMimeTypeSupported(mimeType: string): boolean { - const match = mimeTypeToImport.find(m => m.mimeType === mimeType); + const match = mimeTypeToImport.find((m) => m.mimeType === mimeType); return match ? true : false; } diff --git a/src/datascience-ui/interactive-common/variableExplorer.tsx b/src/datascience-ui/interactive-common/variableExplorer.tsx index 562abc337452..8780e163cc9d 100644 --- a/src/datascience-ui/interactive-common/variableExplorer.tsx +++ b/src/datascience-ui/interactive-common/variableExplorer.tsx @@ -188,7 +188,7 @@ export class VariableExplorer extends React.Component { aria-label={getLocString('DataScience.collapseVariableExplorerLabel', 'Variables')} > { + columns={this.gridColumns.map((c) => { return { ...defaultColumnProperties, ...c }; })} // tslint:disable-next-line: react-this-binding-issue @@ -272,7 +272,7 @@ export class VariableExplorer extends React.Component { const haveValue = this.props.variables[index]?.value; const newExecution = this.props.executionCount !== this.requestedPagesExecutionCount; // tslint:disable-next-line: restrict-plus-operands - const notRequested = !this.requestedPages.find(n => n <= index && index < n + pageSize); + const notRequested = !this.requestedPages.find((n) => n <= index && index < n + pageSize); if (!haveValue && (newExecution || notRequested)) { // Try to find a page of data around this index. let pageIndex = index; diff --git a/src/datascience-ui/interactive-common/variableExplorerRowRenderer.tsx b/src/datascience-ui/interactive-common/variableExplorerRowRenderer.tsx index cbe71584b6fb..c346a7ef3ead 100644 --- a/src/datascience-ui/interactive-common/variableExplorerRowRenderer.tsx +++ b/src/datascience-ui/interactive-common/variableExplorerRowRenderer.tsx @@ -9,6 +9,6 @@ interface IVariableExplorerRowProps { renderBaseRow(props: any): JSX.Element; } -export const VariableExplorerRowRenderer: React.SFC = props => { +export const VariableExplorerRowRenderer: React.SFC = (props) => { return
{props.renderBaseRow(props)}
; }; diff --git a/src/datascience-ui/ipywidgets/classicComm.ts b/src/datascience-ui/ipywidgets/classicComm.ts index 4d8432e97c05..a091da42d515 100644 --- a/src/datascience-ui/ipywidgets/classicComm.ts +++ b/src/datascience-ui/ipywidgets/classicComm.ts @@ -72,11 +72,11 @@ export class ClassicComm implements Kernel.IComm { _metadata?: JSONObject, _buffers?: (ArrayBuffer | ArrayBufferView)[] | undefined ): Kernel.IShellFuture { - this.registeredFutures.forEach(item => this.callbackManager.unregisterFuture(item)); + this.registeredFutures.forEach((item) => this.callbackManager.unregisterFuture(item)); throw new Error('VSCPython.IClassicComm.Close method not implemented!'); } public dispose(): void { - this.registeredFutures.forEach(item => this.callbackManager.unregisterFuture(item)); + this.registeredFutures.forEach((item) => this.callbackManager.unregisterFuture(item)); } // tslint:disable-next-line: no-any public send( diff --git a/src/datascience-ui/ipywidgets/container.tsx b/src/datascience-ui/ipywidgets/container.tsx index ca11aa1eb6a0..1e49638de3ef 100644 --- a/src/datascience-ui/ipywidgets/container.tsx +++ b/src/datascience-ui/ipywidgets/container.tsx @@ -25,7 +25,7 @@ export class WidgetManagerComponent extends React.Component { // tslint:disable-next-line: no-any handleMessage(message: string, payload?: any): boolean { // Double check this is one of our messages. React will actually post messages here too during development - if (AllowedIPyWidgetMessages.find(k => k === message)) { + if (AllowedIPyWidgetMessages.find((k) => k === message)) { widgetMessages.next({ type: message, payload }); } return true; diff --git a/src/datascience-ui/ipywidgets/kernel.ts b/src/datascience-ui/ipywidgets/kernel.ts index af837fd76622..3b50af6092f2 100644 --- a/src/datascience-ui/ipywidgets/kernel.ts +++ b/src/datascience-ui/ipywidgets/kernel.ts @@ -41,7 +41,7 @@ export class ProxyKernel implements Partial { */ public registerCommTarget(targetName: string, callback: CommTargetCallback): void { this.commRegistrationMessagesToSend.push(targetName); - this.handlers.forEach(handler => handler(targetName, callback)); + this.handlers.forEach((handler) => handler(targetName, callback)); this.commTargetCallbacks.set(targetName, callback); } public connectToComm(targetName: string, commId: string = uuid()): Kernel.IComm { @@ -79,7 +79,7 @@ export class ProxyKernel implements Partial { } } public initialize(): void { - this.commRegistrationMessagesToSend.forEach(targetName => + this.commRegistrationMessagesToSend.forEach((targetName) => this.messageSender.sendMessage(IPyWidgetMessages.IPyWidgets_registerCommTarget, targetName) ); this.commRegistrationMessagesToSend = []; diff --git a/src/datascience-ui/ipywidgets/manager.ts b/src/datascience-ui/ipywidgets/manager.ts index 8c925fd80a5d..234b0a0d0b59 100644 --- a/src/datascience-ui/ipywidgets/manager.ts +++ b/src/datascience-ui/ipywidgets/manager.ts @@ -161,7 +161,7 @@ export class WidgetManager implements IIPyWidgetManager, IMessageSender { public registerPostOffice(): void { // Process all messages sequentially. this.messages - .concatMap(async msg => { + .concatMap(async (msg) => { this.restoreBuffers(msg.payload); await this.proxyKernel.handleMessageAsync(msg.type, msg.payload); await this.handleMessageAsync(msg.type, msg.payload); diff --git a/src/datascience-ui/native-editor/redux/reducers/creation.ts b/src/datascience-ui/native-editor/redux/reducers/creation.ts index 27656085b293..76ae98fd9373 100644 --- a/src/datascience-ui/native-editor/redux/reducers/creation.ts +++ b/src/datascience-ui/native-editor/redux/reducers/creation.ts @@ -94,7 +94,7 @@ export namespace Creation { const newList = [...arg.prevState.cellVMs]; // Find the position where we want to insert - let position = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + let position = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (position >= 0) { newList.splice(position, 0, newVM); } else { @@ -119,7 +119,7 @@ export namespace Creation { const newList = [...arg.prevState.cellVMs]; // Find the position where we want to insert - let position = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + let position = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (position >= 0) { position += 1; newList.splice(position, 0, newVM); @@ -205,10 +205,10 @@ export namespace Creation { export function applyCellEdit( arg: NativeEditorReducerArg<{ id: string; changes: IEditorContentChange[] }> ): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.id); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.id); if (index >= 0) { const newVM = { ...arg.prevState.cellVMs[index] }; - arg.payload.data.changes.forEach(c => { + arg.payload.data.changes.forEach((c) => { const source = newVM.inputBlockText; const before = source.slice(0, c.rangeOffset); // tslint:disable-next-line: restrict-plus-operands @@ -259,13 +259,13 @@ export namespace Creation { }; } else if (arg.payload.data.cellId) { // Otherwise just a straight delete - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (index >= 0) { Transfer.postModelRemove(arg, 0, cells[index].cell); // Recompute select/focus if this item has either const previousSelection = getSelectedAndFocusedInfo(arg.prevState); - const newVMs = [...arg.prevState.cellVMs.filter(c => c.cell.id !== arg.payload.data.cellId)]; + const newVMs = [...arg.prevState.cellVMs.filter((c) => c.cell.id !== arg.payload.data.cellId)]; const nextOrPrev = index === arg.prevState.cellVMs.length - 1 ? index - 1 : index; if ( previousSelection.selectedCellId === arg.payload.data.cellId || @@ -293,7 +293,7 @@ export namespace Creation { } export function loadAllCells(arg: NativeEditorReducerArg): IMainState { - const vms = arg.payload.data.cells.map(c => prepareCellVM(c, false, arg.prevState.settings)); + const vms = arg.payload.data.cells.map((c) => prepareCellVM(c, false, arg.prevState.settings)); return { ...arg.prevState, busy: false, @@ -355,13 +355,16 @@ export namespace Creation { ...disabledQueueArg, payload: { ...arg.payload, - data: { firstCellId: arg.payload.data.secondCellId, secondCellId: arg.payload.data.firstCellId } + data: { + firstCellId: arg.payload.data.secondCellId, + secondCellId: arg.payload.data.firstCellId + } } }); case 'modify': // Undo for modify should reapply the outputs. Go through each and apply the update let result = arg.prevState; - arg.payload.data.oldCells.forEach(c => { + arg.payload.data.oldCells.forEach((c) => { result = updateCell({ ...disabledQueueArg, prevState: result, @@ -414,13 +417,16 @@ export namespace Creation { ...disabledQueueArg, payload: { ...arg.payload, - data: { firstCellId: arg.payload.data.secondCellId, secondCellId: arg.payload.data.firstCellId } + data: { + firstCellId: arg.payload.data.secondCellId, + secondCellId: arg.payload.data.firstCellId + } } }); case 'modify': // Redo for modify should reapply the outputs. Go through each and apply the update let result = arg.prevState; - arg.payload.data.newCells.forEach(c => { + arg.payload.data.newCells.forEach((c) => { result = updateCell({ ...disabledQueueArg, prevState: result, diff --git a/src/datascience-ui/native-editor/redux/reducers/effects.ts b/src/datascience-ui/native-editor/redux/reducers/effects.ts index 0869b7e4cead..982e4776d290 100644 --- a/src/datascience-ui/native-editor/redux/reducers/effects.ts +++ b/src/datascience-ui/native-editor/redux/reducers/effects.ts @@ -46,7 +46,7 @@ export namespace Effects { const newVMs = [...prevState.cellVMs]; // Add focus on new cell - const addFocusIndex = newVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const addFocusIndex = newVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (addFocusIndex >= 0) { newVMs[addFocusIndex] = { ...newVMs[addFocusIndex], @@ -67,7 +67,7 @@ export namespace Effects { export function unfocusCell(arg: NativeEditorReducerArg): IMainState { // Unfocus the cell - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); const selectionInfo = getSelectedAndFocusedInfo(arg.prevState); if (index >= 0 && selectionInfo.focusedCellId === arg.payload.data.cellId) { const newVMs = [...arg.prevState.cellVMs]; @@ -105,7 +105,7 @@ export namespace Effects { } export function deselectCell(arg: NativeEditorReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); const selectionInfo = getSelectedAndFocusedInfo(arg.prevState); if (index >= 0 && selectionInfo.selectedCellId === arg.payload.data.cellId) { const newVMs = [...arg.prevState.cellVMs]; @@ -140,13 +140,13 @@ export namespace Effects { const selectionInfo = getSelectedAndFocusedInfo(arg.prevState); if (arg.payload.data.cellId !== selectionInfo.selectedCellId) { let prevState = arg.prevState; - const addIndex = prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const addIndex = prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); const someOtherCellWasFocusedAndSelected = selectionInfo.focusedCellId === selectionInfo.selectedCellId && !!selectionInfo.focusedCellId; // First find the old focused cell and unfocus it - let removeFocusIndex = arg.prevState.cellVMs.findIndex(c => c.cell.id === selectionInfo.focusedCellId); + let removeFocusIndex = arg.prevState.cellVMs.findIndex((c) => c.cell.id === selectionInfo.focusedCellId); if (removeFocusIndex < 0) { - removeFocusIndex = arg.prevState.cellVMs.findIndex(c => c.cell.id === selectionInfo.selectedCellId); + removeFocusIndex = arg.prevState.cellVMs.findIndex((c) => c.cell.id === selectionInfo.selectedCellId); } if (removeFocusIndex >= 0) { @@ -185,7 +185,7 @@ export namespace Effects { } export function toggleLineNumbers(arg: NativeEditorReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (index >= 0) { const newVMs = [...arg.prevState.cellVMs]; newVMs[index] = { ...newVMs[index], showLineNumbers: !newVMs[index].showLineNumbers }; @@ -198,7 +198,7 @@ export namespace Effects { } export function toggleOutput(arg: NativeEditorReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (index >= 0) { const newVMs = [...arg.prevState.cellVMs]; newVMs[index] = { ...newVMs[index], hideOutput: !newVMs[index].hideOutput }; diff --git a/src/datascience-ui/native-editor/redux/reducers/execution.ts b/src/datascience-ui/native-editor/redux/reducers/execution.ts index 78073f8f9299..84f86f2477c9 100644 --- a/src/datascience-ui/native-editor/redux/reducers/execution.ts +++ b/src/datascience-ui/native-editor/redux/reducers/execution.ts @@ -35,8 +35,8 @@ export namespace Execution { ): IMainState { const newVMs = [...prevState.cellVMs]; const cellIdsToExecute: string[] = []; - cellIds.forEach(cellId => { - const index = prevState.cellVMs.findIndex(cell => cell.cell.id === cellId); + cellIds.forEach((cellId) => { + const index = prevState.cellVMs.findIndex((cell) => cell.cell.id === cellId); if (index === -1) { return; } @@ -70,10 +70,10 @@ export namespace Execution { } export function executeAbove(arg: NativeEditorReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (index > 0) { // Get all cellIds until `index`. - const cellIds = arg.prevState.cellVMs.slice(0, index).map(cellVm => cellVm.cell.id); + const cellIds = arg.prevState.cellVMs.slice(0, index).map((cellVm) => cellVm.cell.id); return executeRange(arg.prevState, cellIds, arg); } return arg.prevState; @@ -99,7 +99,7 @@ export namespace Execution { } export function executeCell(arg: NativeEditorReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (index >= 0 && arg.payload.data.cellId) { // Start executing this cell. const executeResult = executeRange(arg.prevState, [arg.payload.data.cellId], arg); @@ -136,10 +136,10 @@ export namespace Execution { } export function executeCellAndBelow(arg: NativeEditorReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (index >= 0) { // Get all cellIds starting from `index`. - const cellIds = arg.prevState.cellVMs.slice(index).map(cellVm => cellVm.cell.id); + const cellIds = arg.prevState.cellVMs.slice(index).map((cellVm) => cellVm.cell.id); return executeRange(arg.prevState, cellIds, arg); } return arg.prevState; @@ -147,7 +147,7 @@ export namespace Execution { export function executeAllCells(arg: NativeEditorReducerArg): IMainState { if (arg.prevState.cellVMs.length > 0) { - const cellIds = arg.prevState.cellVMs.map(cellVm => cellVm.cell.id); + const cellIds = arg.prevState.cellVMs.map((cellVm) => cellVm.cell.id); return executeRange(arg.prevState, cellIds, arg); } return arg.prevState; @@ -156,7 +156,7 @@ export namespace Execution { export function executeSelectedCell(arg: NativeEditorReducerArg): IMainState { // This is the same thing as executing the selected cell const selectionInfo = getSelectedAndFocusedInfo(arg.prevState); - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === selectionInfo.selectedCellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === selectionInfo.selectedCellId); if (selectionInfo.selectedCellId && index >= 0) { return executeCell({ ...arg, @@ -174,7 +174,7 @@ export namespace Execution { } export function clearAllOutputs(arg: NativeEditorReducerArg): IMainState { - const newList = arg.prevState.cellVMs.map(cellVM => { + const newList = arg.prevState.cellVMs.map((cellVM) => { return Helpers.asCellViewModel({ ...cellVM, cell: { ...cellVM.cell, data: { ...cellVM.cell.data, outputs: [], execution_count: null } } @@ -190,7 +190,7 @@ export namespace Execution { } export function changeCellType(arg: NativeEditorReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (index >= 0) { const cellVMs = [...arg.prevState.cellVMs]; const current = arg.prevState.cellVMs[index]; diff --git a/src/datascience-ui/native-editor/redux/reducers/movement.ts b/src/datascience-ui/native-editor/redux/reducers/movement.ts index 7890b9489a7b..62dbd9013126 100644 --- a/src/datascience-ui/native-editor/redux/reducers/movement.ts +++ b/src/datascience-ui/native-editor/redux/reducers/movement.ts @@ -11,8 +11,8 @@ import { NativeEditorReducerArg } from '../mapping'; export namespace Movement { export function swapCells(arg: NativeEditorReducerArg<{ firstCellId: string; secondCellId: string }>) { const newVMs = [...arg.prevState.cellVMs]; - const first = newVMs.findIndex(cvm => cvm.cell.id === arg.payload.data.firstCellId); - const second = newVMs.findIndex(cvm => cvm.cell.id === arg.payload.data.secondCellId); + const first = newVMs.findIndex((cvm) => cvm.cell.id === arg.payload.data.firstCellId); + const second = newVMs.findIndex((cvm) => cvm.cell.id === arg.payload.data.secondCellId); if (first >= 0 && second >= 0 && first !== second) { const temp = newVMs[first]; newVMs[first] = newVMs[second]; @@ -29,7 +29,7 @@ export namespace Movement { } export function moveCellUp(arg: NativeEditorReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(cvm => cvm.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((cvm) => cvm.cell.id === arg.payload.data.cellId); if (index > 0 && arg.payload.data.cellId) { return swapCells({ ...arg, @@ -48,7 +48,7 @@ export namespace Movement { export function moveCellDown(arg: NativeEditorReducerArg): IMainState { const newVMs = [...arg.prevState.cellVMs]; - const index = newVMs.findIndex(cvm => cvm.cell.id === arg.payload.data.cellId); + const index = newVMs.findIndex((cvm) => cvm.cell.id === arg.payload.data.cellId); if (index < newVMs.length - 1 && arg.payload.data.cellId) { return swapCells({ ...arg, @@ -66,7 +66,7 @@ export namespace Movement { } export function arrowUp(arg: NativeEditorReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (index > 0) { queueIncomingActionWithPayload(arg, CommonActionType.SELECT_CELL, { cellId: arg.prevState.cellVMs[index - 1].cell.id, @@ -78,7 +78,7 @@ export namespace Movement { } export function arrowDown(arg: NativeEditorReducerArg): IMainState { - const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); + const index = arg.prevState.cellVMs.findIndex((c) => c.cell.id === arg.payload.data.cellId); if (index < arg.prevState.cellVMs.length - 1) { queueIncomingActionWithPayload(arg, CommonActionType.SELECT_CELL, { cellId: arg.prevState.cellVMs[index + 1].cell.id, diff --git a/src/datascience-ui/plot/mainPanel.tsx b/src/datascience-ui/plot/mainPanel.tsx index 3eef0cf8085b..a706aabe6f61 100644 --- a/src/datascience-ui/plot/mainPanel.tsx +++ b/src/datascience-ui/plot/mainPanel.tsx @@ -60,8 +60,8 @@ export class MainPanel extends React.Component const images = !props.skipDefault ? [TestSvg, TestSvg, TestSvg] : []; const thumbnails = images.map(this.generateThumbnail); const sizes = images.map(this.extractSize); - const values = images.map(_i => undefined); - const ids = images.map(_i => uuid()); + const values = images.map((_i) => undefined); + const ids = images.map((_i) => uuid()); this.state = { images, diff --git a/src/datascience-ui/react-common/errorBoundary.tsx b/src/datascience-ui/react-common/errorBoundary.tsx index 1cef7ae5245e..a37bc55cc147 100644 --- a/src/datascience-ui/react-common/errorBoundary.tsx +++ b/src/datascience-ui/react-common/errorBoundary.tsx @@ -1,36 +1,36 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; -import * as React from 'react'; - -interface IErrorState { - hasError: boolean; - errorMessage: string; -} - -export class ErrorBoundary extends React.Component<{}, IErrorState> { - constructor(props: {}) { - super(props); - this.state = { hasError: false, errorMessage: '' }; - } - - public componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { - const stack = errorInfo.componentStack; - - // Display fallback UI - this.setState({ hasError: true, errorMessage: `${error} at \n ${stack}` }); - } - - public render() { - if (this.state.hasError) { - // Render our error message; - const style: React.CSSProperties = {}; - // tslint:disable-next-line:no-string-literal - style['whiteSpace'] = 'pre'; - - return

{this.state.errorMessage}

; - } - return this.props.children; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; +import * as React from 'react'; + +interface IErrorState { + hasError: boolean; + errorMessage: string; +} + +export class ErrorBoundary extends React.Component<{}, IErrorState> { + constructor(props: {}) { + super(props); + this.state = { hasError: false, errorMessage: '' }; + } + + public componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + const stack = errorInfo.componentStack; + + // Display fallback UI + this.setState({ hasError: true, errorMessage: `${error} at \n ${stack}` }); + } + + public render() { + if (this.state.hasError) { + // Render our error message; + const style: React.CSSProperties = {}; + // tslint:disable-next-line:no-string-literal + style['whiteSpace'] = 'pre'; + + return

{this.state.errorMessage}

; + } + return this.props.children; + } +} diff --git a/src/datascience-ui/react-common/event.ts b/src/datascience-ui/react-common/event.ts index 3f35c6359b95..ab2fed369556 100644 --- a/src/datascience-ui/react-common/event.ts +++ b/src/datascience-ui/react-common/event.ts @@ -22,7 +22,7 @@ export class EventEmitter { } public fire(data?: T): void { - this._listeners.forEach(c => c(data)); + this._listeners.forEach((c) => c(data)); } public dispose(): void { diff --git a/src/datascience-ui/react-common/locReactSide.ts b/src/datascience-ui/react-common/locReactSide.ts index 5acdc63ad8c1..dec1f5fa6946 100644 --- a/src/datascience-ui/react-common/locReactSide.ts +++ b/src/datascience-ui/react-common/locReactSide.ts @@ -1,20 +1,20 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; -// The react code can't use the localize.ts module because it reads from -// disk. This isn't allowed inside a browser, so we pass the collection -// through the javascript. -let loadedCollection: Record | undefined; - -export function getLocString(key: string, defValue: string): string { - if (loadedCollection && loadedCollection.hasOwnProperty(key)) { - return loadedCollection[key]; - } - - return defValue; -} - -export function storeLocStrings(collection: Record) { - loadedCollection = collection; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; +// The react code can't use the localize.ts module because it reads from +// disk. This isn't allowed inside a browser, so we pass the collection +// through the javascript. +let loadedCollection: Record | undefined; + +export function getLocString(key: string, defValue: string): string { + if (loadedCollection && loadedCollection.hasOwnProperty(key)) { + return loadedCollection[key]; + } + + return defValue; +} + +export function storeLocStrings(collection: Record) { + loadedCollection = collection; +} diff --git a/src/datascience-ui/react-common/monacoEditor.tsx b/src/datascience-ui/react-common/monacoEditor.tsx index 20fe5983ea99..80bfdb5f896c 100644 --- a/src/datascience-ui/react-common/monacoEditor.tsx +++ b/src/datascience-ui/react-common/monacoEditor.tsx @@ -193,7 +193,7 @@ export class MonacoEditor extends React.Component { + editor.onContextMenu((e) => { if (this.state.editor) { const domNode = this.state.editor.getDomNode(); const contextMenuElement = domNode @@ -294,7 +294,7 @@ export class MonacoEditor extends React.Component d.dispose()); + this.subscriptions.forEach((d) => d.dispose()); if (this.state.editor) { this.state.editor.dispose(); } @@ -770,13 +770,13 @@ export class MonacoEditor extends React.Component { + this.parameterWidget = hoverElements.find((item) => { if (typeof item.className !== 'string') { return false; } // Check if user is hovering over a parameter widget. const classes = item.className.split(' '); - if (!parameterWidgetClasses.every(cls => classes.indexOf(cls) >= 0)) { + if (!parameterWidgetClasses.every((cls) => classes.indexOf(cls) >= 0)) { // Not all classes required in a parameter hint widget are in this element. // Hence this is not a parameter widget. return false; @@ -786,7 +786,7 @@ export class MonacoEditor extends React.Component widget === item); + return knownParameterHintsWidgets.some((widget) => widget === item); }); if (this.parameterWidget) { @@ -860,8 +860,8 @@ export class MonacoEditor extends React.Component widgetParent !== this.widgetParent) - .forEach(widgetParent => + .filter((widgetParent) => widgetParent !== this.widgetParent) + .forEach((widgetParent) => this.hideWidgets(widgetParent, [WidgetCSSSelector.Parameters, WidgetCSSSelector.Hover]) ); } diff --git a/src/datascience-ui/react-common/monacoHelpers.ts b/src/datascience-ui/react-common/monacoHelpers.ts index 2f08e7c39f8b..0258d9c86fea 100644 --- a/src/datascience-ui/react-common/monacoHelpers.ts +++ b/src/datascience-ui/react-common/monacoHelpers.ts @@ -97,10 +97,10 @@ export function generateChangeEvent( // Combine position and change to create result return { - forward: ev.changes.map(c => { + forward: ev.changes.map((c) => { return { ...c, position: currentPosition! }; }), - reverse: reverseChanges.map(r => { + reverse: reverseChanges.map((r) => { return { ...r, position: oldPosition! }; }), eol: ev.eol, diff --git a/src/datascience-ui/react-common/postOffice.ts b/src/datascience-ui/react-common/postOffice.ts index bc749413b53a..90a06ff5d16b 100644 --- a/src/datascience-ui/react-common/postOffice.ts +++ b/src/datascience-ui/react-common/postOffice.ts @@ -1,116 +1,116 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -import { Observable } from 'rxjs/Observable'; -import { Subject } from 'rxjs/Subject'; -import { WebPanelMessage } from '../../client/common/application/types'; -import { IDisposable } from '../../client/common/types'; -import { logMessage } from './logger'; - -export interface IVsCodeApi { - // tslint:disable-next-line:no-any - postMessage(msg: any): void; - // tslint:disable-next-line:no-any - setState(state: any): void; - // tslint:disable-next-line:no-any - getState(): any; -} - -export interface IMessageHandler { - // tslint:disable-next-line:no-any - handleMessage(type: string, payload?: any): boolean; - dispose?(): void; -} - -// This special function talks to vscode from a web panel -export declare function acquireVsCodeApi(): IVsCodeApi; -// tslint:disable-next-line: no-any -export type PostOfficeMessage = { type: string; payload?: any }; -// tslint:disable-next-line: no-unnecessary-class -export class PostOffice implements IDisposable { - private registered: boolean = false; - private vscodeApi: IVsCodeApi | undefined; - private handlers: IMessageHandler[] = []; - private baseHandler = this.handleMessages.bind(this); - private readonly subject = new Subject(); - private readonly observable: Observable; - constructor() { - this.observable = this.subject.asObservable(); - } - public asObservable(): Observable { - return this.observable; - } - public dispose() { - if (this.registered) { - this.registered = false; - window.removeEventListener('message', this.baseHandler); - } - } - - public sendMessage(type: T, payload?: M[T]) { - return this.sendUnsafeMessage(type.toString(), payload); - } - - // tslint:disable-next-line:no-any - public sendUnsafeMessage(type: string, payload?: any) { - const api = this.acquireApi(); - if (api) { - logMessage(`Posting message ${type} to extension.`); - api.postMessage({ type: type, payload }); - } else { - logMessage(`No vscode API to post message ${type}`); - } - } - - public addHandler(handler: IMessageHandler) { - // Acquire here too so that the message handlers are setup during tests. - this.acquireApi(); - this.handlers.push(handler); - } - - public removeHandler(handler: IMessageHandler) { - this.handlers = this.handlers.filter(f => f !== handler); - } - - private acquireApi(): IVsCodeApi | undefined { - // Only do this once as it crashes if we ask more than once - // tslint:disable-next-line:no-typeof-undefined - if (!this.vscodeApi && typeof acquireVsCodeApi !== 'undefined') { - this.vscodeApi = acquireVsCodeApi(); // NOSONAR - } - if (!this.registered) { - this.registered = true; - window.addEventListener('message', this.baseHandler); - - try { - // For testing, we might use a browser to load the stuff. - // In such instances the `acquireVSCodeApi` will return the event handler to get messages from extension. - // See ./src/datascience-ui/native-editor/index.html - // tslint:disable-next-line: no-any - const api = (this.vscodeApi as any) as { handleMessage?: Function }; - if (api.handleMessage) { - api.handleMessage(this.handleMessages.bind(this)); - } - } catch { - // Ignore. - } - } - - return this.vscodeApi; - } - - private async handleMessages(ev: MessageEvent) { - if (this.handlers) { - const msg = ev.data as WebPanelMessage; - if (msg) { - this.subject.next({ type: msg.type, payload: msg.payload }); - this.handlers.forEach((h: IMessageHandler | null) => { - if (h) { - h.handleMessage(msg.type, msg.payload); - } - }); - } - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +import { WebPanelMessage } from '../../client/common/application/types'; +import { IDisposable } from '../../client/common/types'; +import { logMessage } from './logger'; + +export interface IVsCodeApi { + // tslint:disable-next-line:no-any + postMessage(msg: any): void; + // tslint:disable-next-line:no-any + setState(state: any): void; + // tslint:disable-next-line:no-any + getState(): any; +} + +export interface IMessageHandler { + // tslint:disable-next-line:no-any + handleMessage(type: string, payload?: any): boolean; + dispose?(): void; +} + +// This special function talks to vscode from a web panel +export declare function acquireVsCodeApi(): IVsCodeApi; +// tslint:disable-next-line: no-any +export type PostOfficeMessage = { type: string; payload?: any }; +// tslint:disable-next-line: no-unnecessary-class +export class PostOffice implements IDisposable { + private registered: boolean = false; + private vscodeApi: IVsCodeApi | undefined; + private handlers: IMessageHandler[] = []; + private baseHandler = this.handleMessages.bind(this); + private readonly subject = new Subject(); + private readonly observable: Observable; + constructor() { + this.observable = this.subject.asObservable(); + } + public asObservable(): Observable { + return this.observable; + } + public dispose() { + if (this.registered) { + this.registered = false; + window.removeEventListener('message', this.baseHandler); + } + } + + public sendMessage(type: T, payload?: M[T]) { + return this.sendUnsafeMessage(type.toString(), payload); + } + + // tslint:disable-next-line:no-any + public sendUnsafeMessage(type: string, payload?: any) { + const api = this.acquireApi(); + if (api) { + logMessage(`Posting message ${type} to extension.`); + api.postMessage({ type: type, payload }); + } else { + logMessage(`No vscode API to post message ${type}`); + } + } + + public addHandler(handler: IMessageHandler) { + // Acquire here too so that the message handlers are setup during tests. + this.acquireApi(); + this.handlers.push(handler); + } + + public removeHandler(handler: IMessageHandler) { + this.handlers = this.handlers.filter((f) => f !== handler); + } + + private acquireApi(): IVsCodeApi | undefined { + // Only do this once as it crashes if we ask more than once + // tslint:disable-next-line:no-typeof-undefined + if (!this.vscodeApi && typeof acquireVsCodeApi !== 'undefined') { + this.vscodeApi = acquireVsCodeApi(); // NOSONAR + } + if (!this.registered) { + this.registered = true; + window.addEventListener('message', this.baseHandler); + + try { + // For testing, we might use a browser to load the stuff. + // In such instances the `acquireVSCodeApi` will return the event handler to get messages from extension. + // See ./src/datascience-ui/native-editor/index.html + // tslint:disable-next-line: no-any + const api = (this.vscodeApi as any) as { handleMessage?: Function }; + if (api.handleMessage) { + api.handleMessage(this.handleMessages.bind(this)); + } + } catch { + // Ignore. + } + } + + return this.vscodeApi; + } + + private async handleMessages(ev: MessageEvent) { + if (this.handlers) { + const msg = ev.data as WebPanelMessage; + if (msg) { + this.subject.next({ type: msg.type, payload: msg.payload }); + this.handlers.forEach((h: IMessageHandler | null) => { + if (h) { + h.handleMessage(msg.type, msg.payload); + } + }); + } + } + } +} diff --git a/src/datascience-ui/react-common/progress.tsx b/src/datascience-ui/react-common/progress.tsx index 320cf0b79ff9..4bbf7981f108 100644 --- a/src/datascience-ui/react-common/progress.tsx +++ b/src/datascience-ui/react-common/progress.tsx @@ -1,21 +1,21 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import './progress.css'; - -import * as React from 'react'; - -export class Progress extends React.Component { - constructor(props: {}) { - super(props); - } - - public render() { - // Vscode does this with two parts, a progress container and a progress bit - return ( -
-
-
- ); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import './progress.css'; + +import * as React from 'react'; + +export class Progress extends React.Component { + constructor(props: {}) { + super(props); + } + + public render() { + // Vscode does this with two parts, a progress container and a progress bit + return ( +
+
+
+ ); + } +} diff --git a/src/datascience-ui/react-common/reduxUtils.ts b/src/datascience-ui/react-common/reduxUtils.ts index 451fa74115bb..b651517aff8a 100644 --- a/src/datascience-ui/react-common/reduxUtils.ts +++ b/src/datascience-ui/react-common/reduxUtils.ts @@ -60,12 +60,12 @@ export function combineReducers(defaultState: S, map: M): Reducer next => action => { + return (store) => (next) => (action) => { let pendingActions: Action[] = []; let complete = false; function flush() { - pendingActions.forEach(a => store.dispatch(a)); + pendingActions.forEach((a) => store.dispatch(a)); pendingActions = []; } diff --git a/src/datascience-ui/react-common/relativeImage.tsx b/src/datascience-ui/react-common/relativeImage.tsx index 4f1bc6a6223b..9c67995b7c51 100644 --- a/src/datascience-ui/react-common/relativeImage.tsx +++ b/src/datascience-ui/react-common/relativeImage.tsx @@ -1,34 +1,34 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; -import * as path from 'path'; -import * as React from 'react'; - -// This special function finds relative paths when loading inside of vscode. It's not defined -// when loading outside, so the Image component should still work. -export declare function resolvePath(relativePath: string): string; - -interface IRelativeImageProps { - class: string; - path: string; -} - -export class RelativeImage extends React.Component { - constructor(props: IRelativeImageProps) { - super(props); - } - - public render() { - return {path.basename(this.props.path)}; - } - - private getImageSource = () => { - // tslint:disable-next-line:no-typeof-undefined - if (typeof resolvePath === 'undefined') { - return this.props.path; - } else { - return resolvePath(this.props.path); - } - }; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; +import * as path from 'path'; +import * as React from 'react'; + +// This special function finds relative paths when loading inside of vscode. It's not defined +// when loading outside, so the Image component should still work. +export declare function resolvePath(relativePath: string): string; + +interface IRelativeImageProps { + class: string; + path: string; +} + +export class RelativeImage extends React.Component { + constructor(props: IRelativeImageProps) { + super(props); + } + + public render() { + return {path.basename(this.props.path)}; + } + + private getImageSource = () => { + // tslint:disable-next-line:no-typeof-undefined + if (typeof resolvePath === 'undefined') { + return this.props.path; + } else { + return resolvePath(this.props.path); + } + }; +} diff --git a/src/datascience-ui/react-common/styleInjector.tsx b/src/datascience-ui/react-common/styleInjector.tsx index fe19600b007e..4ff54f1786e5 100644 --- a/src/datascience-ui/react-common/styleInjector.tsx +++ b/src/datascience-ui/react-common/styleInjector.tsx @@ -1,120 +1,120 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import * as React from 'react'; - -import { CssMessages, IGetCssResponse, SharedMessages } from '../../client/datascience/messages'; -import { IDataScienceExtraSettings } from '../../client/datascience/types'; -import { IMessageHandler, PostOffice } from './postOffice'; -import { detectBaseTheme } from './themeDetector'; - -export interface IStyleInjectorProps { - expectingDark: boolean; - postOffice: PostOffice; - settings: IDataScienceExtraSettings; - darkChanged?(newDark: boolean): void; - onReady?(): void; -} - -interface IStyleInjectorState { - rootCss?: string; - theme?: string; - knownDark?: boolean; -} - -export class StyleInjector extends React.Component - implements IMessageHandler { - constructor(props: IStyleInjectorProps) { - super(props); - this.state = { rootCss: undefined, theme: undefined }; - } - - public componentWillMount() { - // Add ourselves as a handler for the post office - this.props.postOffice.addHandler(this); - } - - public componentWillUnmount() { - // Remove ourselves as a handler for the post office - this.props.postOffice.removeHandler(this); - } - - public componentDidMount() { - if (!this.state.rootCss) { - // Set to a temporary value. - this.setState({ rootCss: ' ' }); - this.props.postOffice.sendUnsafeMessage(CssMessages.GetCssRequest, { isDark: this.props.expectingDark }); - } - } - - public render() { - return ( -
- - {this.props.children} -
- ); - } - - // tslint:disable-next-line:no-any - public handleMessage = (msg: string, payload?: any): boolean => { - switch (msg) { - case CssMessages.GetCssResponse: - this.handleCssResponse(payload); - break; - - case SharedMessages.UpdateSettings: - this.updateSettings(payload); - break; - - default: - break; - } - - return true; - }; - - // tslint:disable-next-line:no-any - private handleCssResponse(payload?: any) { - const response = payload as IGetCssResponse; - if (response && response.css) { - // Recompute our known dark value from the class name in the body - // VS code should update this dynamically when the theme changes - const computedKnownDark = this.computeKnownDark(); - - // We also get this in our response, but computing is more reliable - // than searching for it. - - if (this.state.knownDark !== computedKnownDark && this.props.darkChanged) { - this.props.darkChanged(computedKnownDark); - } - - this.setState( - { - rootCss: response.css, - theme: response.theme, - knownDark: computedKnownDark - }, - this.props.onReady - ); - } - } - - // tslint:disable-next-line:no-any - private updateSettings(payload: any) { - if (payload) { - const newSettings = JSON.parse(payload as string); - const dsSettings = newSettings as IDataScienceExtraSettings; - if (dsSettings && dsSettings.extraSettings && dsSettings.extraSettings.theme !== this.state.theme) { - // User changed the current theme. Rerender - this.props.postOffice.sendUnsafeMessage(CssMessages.GetCssRequest, { isDark: this.computeKnownDark() }); - } - } - } - - private computeKnownDark(): boolean { - const ignore = this.props.settings.ignoreVscodeTheme ? true : false; - const baseTheme = ignore ? 'vscode-light' : detectBaseTheme(); - return baseTheme !== 'vscode-light'; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import * as React from 'react'; + +import { CssMessages, IGetCssResponse, SharedMessages } from '../../client/datascience/messages'; +import { IDataScienceExtraSettings } from '../../client/datascience/types'; +import { IMessageHandler, PostOffice } from './postOffice'; +import { detectBaseTheme } from './themeDetector'; + +export interface IStyleInjectorProps { + expectingDark: boolean; + postOffice: PostOffice; + settings: IDataScienceExtraSettings; + darkChanged?(newDark: boolean): void; + onReady?(): void; +} + +interface IStyleInjectorState { + rootCss?: string; + theme?: string; + knownDark?: boolean; +} + +export class StyleInjector extends React.Component + implements IMessageHandler { + constructor(props: IStyleInjectorProps) { + super(props); + this.state = { rootCss: undefined, theme: undefined }; + } + + public componentWillMount() { + // Add ourselves as a handler for the post office + this.props.postOffice.addHandler(this); + } + + public componentWillUnmount() { + // Remove ourselves as a handler for the post office + this.props.postOffice.removeHandler(this); + } + + public componentDidMount() { + if (!this.state.rootCss) { + // Set to a temporary value. + this.setState({ rootCss: ' ' }); + this.props.postOffice.sendUnsafeMessage(CssMessages.GetCssRequest, { isDark: this.props.expectingDark }); + } + } + + public render() { + return ( +
+ + {this.props.children} +
+ ); + } + + // tslint:disable-next-line:no-any + public handleMessage = (msg: string, payload?: any): boolean => { + switch (msg) { + case CssMessages.GetCssResponse: + this.handleCssResponse(payload); + break; + + case SharedMessages.UpdateSettings: + this.updateSettings(payload); + break; + + default: + break; + } + + return true; + }; + + // tslint:disable-next-line:no-any + private handleCssResponse(payload?: any) { + const response = payload as IGetCssResponse; + if (response && response.css) { + // Recompute our known dark value from the class name in the body + // VS code should update this dynamically when the theme changes + const computedKnownDark = this.computeKnownDark(); + + // We also get this in our response, but computing is more reliable + // than searching for it. + + if (this.state.knownDark !== computedKnownDark && this.props.darkChanged) { + this.props.darkChanged(computedKnownDark); + } + + this.setState( + { + rootCss: response.css, + theme: response.theme, + knownDark: computedKnownDark + }, + this.props.onReady + ); + } + } + + // tslint:disable-next-line:no-any + private updateSettings(payload: any) { + if (payload) { + const newSettings = JSON.parse(payload as string); + const dsSettings = newSettings as IDataScienceExtraSettings; + if (dsSettings && dsSettings.extraSettings && dsSettings.extraSettings.theme !== this.state.theme) { + // User changed the current theme. Rerender + this.props.postOffice.sendUnsafeMessage(CssMessages.GetCssRequest, { isDark: this.computeKnownDark() }); + } + } + } + + private computeKnownDark(): boolean { + const ignore = this.props.settings.ignoreVscodeTheme ? true : false; + const baseTheme = ignore ? 'vscode-light' : detectBaseTheme(); + return baseTheme !== 'vscode-light'; + } +} diff --git a/src/datascience-ui/react-common/themeDetector.ts b/src/datascience-ui/react-common/themeDetector.ts index 7ed2cdfe9d61..b217a7334f22 100644 --- a/src/datascience-ui/react-common/themeDetector.ts +++ b/src/datascience-ui/react-common/themeDetector.ts @@ -1,24 +1,24 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -// From here: -// https://stackoverflow.com/questions/37257911/detect-light-dark-theme-programatically-in-visual-studio-code -// Detect vscode-light, vscode-dark, and vscode-high-contrast class name on the body element. -export function detectBaseTheme(): 'vscode-light' | 'vscode-dark' | 'vscode-high-contrast' { - const body = document.body; - if (body) { - switch (body.className) { - default: - case 'vscode-light': - return 'vscode-light'; - case 'vscode-dark': - return 'vscode-dark'; - case 'vscode-high-contrast': - return 'vscode-high-contrast'; - } - } - - return 'vscode-light'; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// From here: +// https://stackoverflow.com/questions/37257911/detect-light-dark-theme-programatically-in-visual-studio-code +// Detect vscode-light, vscode-dark, and vscode-high-contrast class name on the body element. +export function detectBaseTheme(): 'vscode-light' | 'vscode-dark' | 'vscode-high-contrast' { + const body = document.body; + if (body) { + switch (body.className) { + default: + case 'vscode-light': + return 'vscode-light'; + case 'vscode-dark': + return 'vscode-dark'; + case 'vscode-high-contrast': + return 'vscode-high-contrast'; + } + } + + return 'vscode-light'; +} diff --git a/src/ipywidgets/src/embed.ts b/src/ipywidgets/src/embed.ts index d08972a5138f..9f6ffd6cc12f 100644 --- a/src/ipywidgets/src/embed.ts +++ b/src/ipywidgets/src/embed.ts @@ -20,7 +20,7 @@ Array.prototype.forEach.call(scripts, (script: HTMLScriptElement) => { * @param pkg Package name or names to load */ // tslint:disable-next-line: no-function-expression -const requirePromise = function(pkg: string | string[]): Promise { +const requirePromise = function (pkg: string | string[]): Promise { return new Promise((resolve, reject) => { const require = (window as any).requirejs; if (require === undefined) { @@ -78,12 +78,12 @@ export function requireLoader(moduleName: string, moduleVersion: string): Promis window.console.log(`Loading from ${cdn} for ${moduleName}@${moduleVersion}`); return loadFromCDN(); } - return requirePromise([`${moduleName}`]).catch(err => { + return requirePromise([`${moduleName}`]).catch((err) => { const failedId = err.requireModules && err.requireModules[0]; if (failedId) { require.undef(failedId); window.console.log(`Falling back to ${cdn} for ${moduleName}@${moduleVersion}`); - loadFromCDN().catch(x => { + loadFromCDN().catch((x) => { window.console.error(x); }); } @@ -101,7 +101,7 @@ export function renderWidgets(element = document.documentElement): void { const managerFactory = (): any => { return new wm.WidgetManager(undefined, element); }; - libembed.renderWidgets(managerFactory, element).catch(x => { + libembed.renderWidgets(managerFactory, element).catch((x) => { window.console.error(x); }); } diff --git a/src/ipywidgets/src/index.ts b/src/ipywidgets/src/index.ts index b47e23345632..d780fd02b35c 100644 --- a/src/ipywidgets/src/index.ts +++ b/src/ipywidgets/src/index.ts @@ -16,7 +16,7 @@ const azureMLWidgets = require('./azureml/index'); // Export the following for `requirejs`. // tslint:disable-next-line: no-any no-function-expression no-empty -const define = (window as any).define || function() {}; +const define = (window as any).define || function () {}; define('@jupyter-widgets/controls', () => widgets); define('@jupyter-widgets/base', () => base); define('@jupyter-widgets/output', () => outputWidgets); diff --git a/src/ipywidgets/src/libembed.ts b/src/ipywidgets/src/libembed.ts index 1125a23fa3a8..29f1f1c74ba8 100644 --- a/src/ipywidgets/src/libembed.ts +++ b/src/ipywidgets/src/libembed.ts @@ -32,7 +32,9 @@ export async function renderWidgets( element: HTMLElement = document.documentElement ): Promise { const tags = element.querySelectorAll('script[type="application/vnd.jupyter.widget-state+json"]'); - await Promise.all(Array.from(tags).map(async t => renderManager(element, JSON.parse(t.innerHTML), managerFactory))); + await Promise.all( + Array.from(tags).map(async (t) => renderManager(element, JSON.parse(t.innerHTML), managerFactory)) + ); } /** @@ -62,14 +64,14 @@ async function renderManager( const models = await manager.set_state(widgetState); const tags = element.querySelectorAll('script[type="application/vnd.jupyter.widget-view+json"]'); await Promise.all( - Array.from(tags).map(async viewtag => { + Array.from(tags).map(async (viewtag) => { const widgetViewObject = JSON.parse(viewtag.innerHTML); const valid2 = view_validate(widgetViewObject); if (!valid2) { throw new Error(`View state has errors: ${view_validate.errors}`); } const model_id: string = widgetViewObject.model_id; - const model = models.find(item => item.model_id === model_id); + const model = models.find((item) => item.model_id === model_id); if (model !== undefined && viewtag.parentElement !== null) { const prev = viewtag.previousElementSibling; if (prev && prev.tagName === 'img' && prev.classList.contains('jupyter-widget')) { @@ -79,7 +81,7 @@ async function renderManager( widgetTag.className = 'widget-subarea'; viewtag.parentElement.insertBefore(widgetTag, viewtag); const view = await manager.create_view(model, { node: widgetTag }); - manager.display_view('display_view', view, {}).catch(x => { + manager.display_view('display_view', view, {}).catch((x) => { window.console.error(x); }); } diff --git a/src/ipywidgets/src/manager.ts b/src/ipywidgets/src/manager.ts index 2447c492117a..b06d3c048351 100644 --- a/src/ipywidgets/src/manager.ts +++ b/src/ipywidgets/src/manager.ts @@ -34,7 +34,7 @@ export class WidgetManager extends jupyterlab.WidgetManager { { safe: false, mimeTypes: [WIDGET_MIMETYPE], - createRenderer: options => new jupyterlab.WidgetRenderer(options, this) + createRenderer: (options) => new jupyterlab.WidgetRenderer(options, this) }, 0 ); @@ -67,7 +67,7 @@ export class WidgetManager extends jupyterlab.WidgetManager { public _get_comm_info(): Promise { return this.kernel .requestCommInfo({ target: this.comm_target_name }) - .then(reply => (reply.content as any).comms); + .then((reply) => (reply.content as any).comms); } public async display_view(msg: any, view: Backbone.View, options: any): Promise { const widget = await super.display_view(msg, view, options); diff --git a/src/ipywidgets/src/signal.ts b/src/ipywidgets/src/signal.ts index 230a7d210251..ea7063145c37 100644 --- a/src/ipywidgets/src/signal.ts +++ b/src/ipywidgets/src/signal.ts @@ -21,6 +21,6 @@ export class Signal implements ISignal { } public fire(sender: T, args: S): void { - this.slots.forEach(s => s(sender, args)); + this.slots.forEach((s) => s(sender, args)); } } diff --git a/src/test/activation/aaTesting.unit.test.ts b/src/test/activation/aaTesting.unit.test.ts index e796324ca637..78e1af84a2c9 100644 --- a/src/test/activation/aaTesting.unit.test.ts +++ b/src/test/activation/aaTesting.unit.test.ts @@ -1,31 +1,31 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as TypeMoq from 'typemoq'; -import { AATesting } from '../../client/activation/aaTesting'; -import { ValidateABTesting } from '../../client/common/experimentGroups'; -import { IExperimentsManager } from '../../client/common/types'; - -suite('A/A Testing', () => { - let experiments: TypeMoq.IMock; - let aaTesting: AATesting; - setup(() => { - experiments = TypeMoq.Mock.ofType(); - aaTesting = new AATesting(experiments.object); - }); - - test('Send telemetry corresponding to the experiment user is in', async () => { - experiments - .setup(exp => exp.sendTelemetryIfInExperiment(ValidateABTesting.experiment)) - .returns(() => undefined) - .verifiable(TypeMoq.Times.once()); - experiments - .setup(exp => exp.sendTelemetryIfInExperiment(ValidateABTesting.control)) - .returns(() => undefined) - .verifiable(TypeMoq.Times.once()); - await aaTesting.activate(); - experiments.verifyAll(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import * as TypeMoq from 'typemoq'; +import { AATesting } from '../../client/activation/aaTesting'; +import { ValidateABTesting } from '../../client/common/experimentGroups'; +import { IExperimentsManager } from '../../client/common/types'; + +suite('A/A Testing', () => { + let experiments: TypeMoq.IMock; + let aaTesting: AATesting; + setup(() => { + experiments = TypeMoq.Mock.ofType(); + aaTesting = new AATesting(experiments.object); + }); + + test('Send telemetry corresponding to the experiment user is in', async () => { + experiments + .setup((exp) => exp.sendTelemetryIfInExperiment(ValidateABTesting.experiment)) + .returns(() => undefined) + .verifiable(TypeMoq.Times.once()); + experiments + .setup((exp) => exp.sendTelemetryIfInExperiment(ValidateABTesting.control)) + .returns(() => undefined) + .verifiable(TypeMoq.Times.once()); + await aaTesting.activate(); + experiments.verifyAll(); + }); +}); diff --git a/src/test/activation/activationManager.unit.test.ts b/src/test/activation/activationManager.unit.test.ts index dcf7b33fe0a9..ea871eaed2e8 100644 --- a/src/test/activation/activationManager.unit.test.ts +++ b/src/test/activation/activationManager.unit.test.ts @@ -75,7 +75,7 @@ suite('Language Server Activation - ActivationManager', () => { when(workspaceService.hasWorkspaceFolders).thenReturn(true); const eventDef = () => disposable2.object; documentManager - .setup(d => d.onDidOpenTextDocument) + .setup((d) => d.onDidOpenTextDocument) .returns(() => eventDef) .verifiable(typemoq.Times.once()); @@ -87,8 +87,8 @@ suite('Language Server Activation - ActivationManager', () => { documentManager.verifyAll(); - disposable.setup(d => d.dispose()).verifiable(typemoq.Times.once()); - disposable2.setup(d => d.dispose()).verifiable(typemoq.Times.once()); + disposable.setup((d) => d.dispose()).verifiable(typemoq.Times.once()); + disposable2.setup((d) => d.dispose()).verifiable(typemoq.Times.once()); managerTest.dispose(); @@ -103,11 +103,11 @@ suite('Language Server Activation - ActivationManager', () => { when(workspaceService.hasWorkspaceFolders).thenReturn(true); const eventDef = () => disposable2.object; documentManager - .setup(d => d.onDidOpenTextDocument) + .setup((d) => d.onDidOpenTextDocument) .returns(() => eventDef) .verifiable(typemoq.Times.once()); - disposable.setup(d => d.dispose()); - disposable2.setup(d => d.dispose()); + disposable.setup((d) => d.dispose()); + disposable2.setup((d) => d.dispose()); await managerTest.initialize(); @@ -115,8 +115,8 @@ suite('Language Server Activation - ActivationManager', () => { verify(workspaceService.hasWorkspaceFolders).once(); verify(workspaceService.onDidChangeWorkspaceFolders).once(); documentManager.verifyAll(); - disposable.verify(d => d.dispose(), typemoq.Times.never()); - disposable2.verify(d => d.dispose(), typemoq.Times.never()); + disposable.verify((d) => d.dispose(), typemoq.Times.never()); + disposable2.verify((d) => d.dispose(), typemoq.Times.never()); when(workspaceService.workspaceFolders).thenReturn([]); when(workspaceService.hasWorkspaceFolders).thenReturn(false); @@ -124,13 +124,13 @@ suite('Language Server Activation - ActivationManager', () => { await managerTest.initialize(); verify(workspaceService.hasWorkspaceFolders).twice(); - disposable.verify(d => d.dispose(), typemoq.Times.never()); - disposable2.verify(d => d.dispose(), typemoq.Times.once()); + disposable.verify((d) => d.dispose(), typemoq.Times.never()); + disposable2.verify((d) => d.dispose(), typemoq.Times.once()); managerTest.dispose(); - disposable.verify(d => d.dispose(), typemoq.Times.atLeast(1)); - disposable2.verify(d => d.dispose(), typemoq.Times.once()); + disposable.verify((d) => d.dispose(), typemoq.Times.atLeast(1)); + disposable2.verify((d) => d.dispose(), typemoq.Times.once()); }); test('Activate workspace specific to the resource in case of Multiple workspaces when a file is opened', async () => { const disposable1 = typemoq.Mock.ofType(); @@ -139,16 +139,16 @@ suite('Language Server Activation - ActivationManager', () => { let workspaceFoldersChangedHandler!: Function; const documentUri = Uri.file('a'); const document = typemoq.Mock.ofType(); - document.setup(d => d.uri).returns(() => documentUri); - document.setup(d => d.languageId).returns(() => PYTHON_LANGUAGE); + document.setup((d) => d.uri).returns(() => documentUri); + document.setup((d) => d.languageId).returns(() => PYTHON_LANGUAGE); - when(workspaceService.onDidChangeWorkspaceFolders).thenReturn(cb => { + when(workspaceService.onDidChangeWorkspaceFolders).thenReturn((cb) => { workspaceFoldersChangedHandler = cb; return disposable1.object; }); documentManager - .setup(w => w.onDidOpenTextDocument(typemoq.It.isAny(), typemoq.It.isAny())) - .callback(cb => (fileOpenedHandler = cb)) + .setup((w) => w.onDidOpenTextDocument(typemoq.It.isAny(), typemoq.It.isAny())) + .callback((cb) => (fileOpenedHandler = cb)) .returns(() => disposable2.object) .verifiable(typemoq.Times.once()); @@ -165,11 +165,11 @@ suite('Language Server Activation - ActivationManager', () => { when(activationService2.activate(resource)).thenResolve(); when(interpreterService.getInterpreters(anything())).thenResolve(); autoSelection - .setup(a => a.autoSelectInterpreter(resource)) + .setup((a) => a.autoSelectInterpreter(resource)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); appDiagnostics - .setup(a => a.performPreStartupHealthCheck(resource)) + .setup((a) => a.performPreStartupHealthCheck(resource)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); // Add workspaceFoldersChangedHandler @@ -198,11 +198,11 @@ suite('Language Server Activation - ActivationManager', () => { when(activationService2.activate(resource)).thenResolve(); when(interpreterService.getInterpreters(anything())).thenResolve(); autoSelection - .setup(a => a.autoSelectInterpreter(resource)) + .setup((a) => a.autoSelectInterpreter(resource)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); appDiagnostics - .setup(a => a.performPreStartupHealthCheck(resource)) + .setup((a) => a.performPreStartupHealthCheck(resource)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); @@ -218,11 +218,11 @@ suite('Language Server Activation - ActivationManager', () => { when(activationService2.activate(resource)).thenResolve(); when(interpreterService.getInterpreters(anything())).thenResolve(); autoSelection - .setup(a => a.autoSelectInterpreter(resource)) + .setup((a) => a.autoSelectInterpreter(resource)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); appDiagnostics - .setup(a => a.performPreStartupHealthCheck(resource)) + .setup((a) => a.performPreStartupHealthCheck(resource)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); @@ -280,15 +280,15 @@ suite('Language Server Activation - ActivationManager', () => { let workspaceFoldersChangedHandler!: Function; const documentUri = Uri.file('a'); const document = typemoq.Mock.ofType(); - document.setup(d => d.uri).returns(() => documentUri); + document.setup((d) => d.uri).returns(() => documentUri); - when(workspaceService.onDidChangeWorkspaceFolders).thenReturn(cb => { + when(workspaceService.onDidChangeWorkspaceFolders).thenReturn((cb) => { workspaceFoldersChangedHandler = cb; return disposable1.object; }); documentManager - .setup(w => w.onDidOpenTextDocument(typemoq.It.isAny(), typemoq.It.isAny())) - .callback(cb => (docOpenedHandler = cb)) + .setup((w) => w.onDidOpenTextDocument(typemoq.It.isAny(), typemoq.It.isAny())) + .callback((cb) => (docOpenedHandler = cb)) .returns(() => disposable2.object) .verifiable(typemoq.Times.once()); @@ -320,7 +320,7 @@ suite('Language Server Activation - ActivationManager', () => { //Removed no. of folders to one when(workspaceService.workspaceFolders).thenReturn([folder1]); when(workspaceService.hasWorkspaceFolders).thenReturn(true); - disposable2.setup(d => d.dispose()).verifiable(typemoq.Times.once()); + disposable2.setup((d) => d.dispose()).verifiable(typemoq.Times.once()); workspaceFoldersChangedHandler.call(managerTest); @@ -378,11 +378,11 @@ suite('Language Server Activation - activate()', () => { test('Execution goes as expected if there are no errors', async () => { singleActivationService - .setup(s => s.activate()) + .setup((s) => s.activate()) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); autoSelection - .setup(a => a.autoSelectInterpreter(undefined)) + .setup((a) => a.autoSelectInterpreter(undefined)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); when(activeResourceService.getActiveResource()).thenReturn(resource); @@ -395,11 +395,11 @@ suite('Language Server Activation - activate()', () => { test('Throws error if execution fails', async () => { singleActivationService - .setup(s => s.activate()) + .setup((s) => s.activate()) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); autoSelection - .setup(a => a.autoSelectInterpreter(undefined)) + .setup((a) => a.autoSelectInterpreter(undefined)) .returns(() => Promise.reject(new Error('Kaboom'))) .verifiable(typemoq.Times.once()); when(activeResourceService.getActiveResource()).thenReturn(resource); diff --git a/src/test/activation/activationService.unit.test.ts b/src/test/activation/activationService.unit.test.ts index ab3e76a419c9..4dccd02b03f3 100644 --- a/src/test/activation/activationService.unit.test.ts +++ b/src/test/activation/activationService.unit.test.ts @@ -37,7 +37,7 @@ import { IServiceContainer } from '../../client/ioc/types'; // tslint:disable:max-func-body-length no-any suite('Language Server Activation - ActivationService', () => { - [true, false].forEach(jediIsEnabled => { + [true, false].forEach((jediIsEnabled) => { suite(`Test activation - ${jediIsEnabled ? 'Jedi is enabled' : 'Jedi is disabled'}`, () => { let serviceContainer: TypeMoq.IMock; let pythonSettings: TypeMoq.IMock; @@ -69,22 +69,22 @@ suite('Language Server Activation - ActivationService', () => { version: new SemVer('1.2.3') }; lsNotSupportedDiagnosticService = TypeMoq.Mock.ofType(); - workspaceService.setup(w => w.hasWorkspaceFolders).returns(() => false); - workspaceService.setup(w => w.workspaceFolders).returns(() => []); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + workspaceService.setup((w) => w.hasWorkspaceFolders).returns(() => false); + workspaceService.setup((w) => w.workspaceFolders).returns(() => []); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); interpreterService = TypeMoq.Mock.ofType(); const disposable = TypeMoq.Mock.ofType(); interpreterService - .setup(i => i.onDidChangeInterpreter(TypeMoq.It.isAny())) - .returns(cb => { + .setup((i) => i.onDidChangeInterpreter(TypeMoq.It.isAny())) + .returns((cb) => { interpreterChangedHandler = cb; return disposable.object; }); langFolderServiceMock - .setup(l => l.getCurrentLanguageServerDirectory()) + .setup((l) => l.getCurrentLanguageServerDirectory()) .returns(() => Promise.resolve(folderVer)); stateFactory - .setup(f => + .setup((f) => f.createGlobalPersistentState( TypeMoq.It.isValue('SWITCH_LS'), TypeMoq.It.isAny(), @@ -92,42 +92,42 @@ suite('Language Server Activation - ActivationService', () => { ) ) .returns(() => state.object); - state.setup(s => s.value).returns(() => undefined); - state.setup(s => s.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + state.setup((s) => s.value).returns(() => undefined); + state.setup((s) => s.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); const setting = { workspaceFolderValue: jediIsEnabled }; workspaceConfig = TypeMoq.Mock.ofType(); workspaceService - .setup(ws => ws.getConfiguration('python', TypeMoq.It.isAny())) + .setup((ws) => ws.getConfiguration('python', TypeMoq.It.isAny())) .returns(() => workspaceConfig.object); - workspaceConfig.setup(c => c.inspect('jediEnabled')).returns(() => setting as any); + workspaceConfig.setup((c) => c.inspect('jediEnabled')).returns(() => setting as any); const output = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny())) .returns(() => output.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IApplicationShell))) + .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell))) .returns(() => appShell.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ICommandManager))) + .setup((c) => c.get(TypeMoq.It.isValue(ICommandManager))) .returns(() => cmdManager.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService))) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) .returns(() => platformService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService))) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService))) .returns(() => interpreterService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILanguageServerFolderService))) + .setup((c) => c.get(TypeMoq.It.isValue(ILanguageServerFolderService))) .returns(() => langFolderServiceMock.object); serviceContainer - .setup(s => + .setup((s) => s.get( TypeMoq.It.isValue(IDiagnosticsService), TypeMoq.It.isValue(LSNotSupportedDiagnosticServiceId) @@ -143,10 +143,10 @@ suite('Language Server Activation - ActivationService', () => { activatorName: LanguageServerType = LanguageServerType.Jedi ) { activator - .setup(a => a.start(undefined, undefined)) + .setup((a) => a.start(undefined, undefined)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); - activator.setup(a => a.activate()).verifiable(TypeMoq.Times.once()); + activator.setup((a) => a.activate()).verifiable(TypeMoq.Times.once()); if (activatorName !== LanguageServerType.None && lsSupported && !jediIsEnabled) { activatorName = LanguageServerType.Microsoft; @@ -160,18 +160,20 @@ suite('Language Server Activation - ActivationService', () => { } lsNotSupportedDiagnosticService - .setup(l => l.diagnose(undefined)) + .setup((l) => l.diagnose(undefined)) .returns(() => Promise.resolve(diagnostics)); lsNotSupportedDiagnosticService - .setup(l => l.handle(TypeMoq.It.isValue(diagnostics))) + .setup((l) => l.handle(TypeMoq.It.isValue(diagnostics))) .returns(() => Promise.resolve()); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isValue(activatorName))) + .setup((c) => + c.get(TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isValue(activatorName)) + ) .returns(() => activator.object) .verifiable(TypeMoq.Times.once()); experiments - .setup(ex => ex.inExperiment(TypeMoq.It.isAny())) + .setup((ex) => ex.inExperiment(TypeMoq.It.isAny())) .returns(() => false) .verifiable(TypeMoq.Times.never()); @@ -186,13 +188,15 @@ suite('Language Server Activation - ActivationService', () => { let callbackHandler!: (e: ConfigurationChangeEvent) => Promise; let jediIsEnabledValueInSetting = jediIsEnabled; workspaceService - .setup(w => w.onDidChangeConfiguration(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .callback(cb => (callbackHandler = cb)) + .setup((w) => + w.onDidChangeConfiguration(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) + ) + .callback((cb) => (callbackHandler = cb)) .returns(() => TypeMoq.Mock.ofType().object) .verifiable(TypeMoq.Times.once()); - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabledValueInSetting); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabledValueInSetting); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activator = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, @@ -205,15 +209,17 @@ suite('Language Server Activation - ActivationService', () => { const event = TypeMoq.Mock.ofType(); event - .setup(e => e.affectsConfiguration(TypeMoq.It.isValue(`python.${settingName}`), TypeMoq.It.isAny())) + .setup((e) => + e.affectsConfiguration(TypeMoq.It.isValue(`python.${settingName}`), TypeMoq.It.isAny()) + ) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isValue('Reload'))) + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isValue('Reload'))) .returns(() => Promise.resolve('Reload')) .verifiable(TypeMoq.Times.once()); cmdManager - .setup(c => c.executeCommand(TypeMoq.It.isValue('workbench.action.reloadWindow'))) + .setup((c) => c.executeCommand(TypeMoq.It.isValue('workbench.action.reloadWindow'))) .verifiable(TypeMoq.Times.once()); // Toggle the value in the setting and invoke the callback. @@ -226,8 +232,8 @@ suite('Language Server Activation - ActivationService', () => { } test('LS is supported', async () => { - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activator = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, @@ -238,8 +244,8 @@ suite('Language Server Activation - ActivationService', () => { await testActivation(activationService, activator, true); }); test('LS is not supported', async () => { - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activator = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, @@ -251,8 +257,8 @@ suite('Language Server Activation - ActivationService', () => { }); test('Activator must be activated', async () => { - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activator = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, @@ -263,8 +269,8 @@ suite('Language Server Activation - ActivationService', () => { await testActivation(activationService, activator); }); test('Activator must be deactivated', async () => { - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activator = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, @@ -274,14 +280,14 @@ suite('Language Server Activation - ActivationService', () => { await testActivation(activationService, activator); - activator.setup(a => a.dispose()).verifiable(TypeMoq.Times.once()); + activator.setup((a) => a.dispose()).verifiable(TypeMoq.Times.once()); activationService.dispose(); activator.verifyAll(); }); test('No language service', async () => { - pythonSettings.setup(p => p.jediEnabled).returns(() => false); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.None); + pythonSettings.setup((p) => p.jediEnabled).returns(() => false); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.None); const activator = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, @@ -300,13 +306,15 @@ suite('Language Server Activation - ActivationService', () => { let callbackHandler!: (e: ConfigurationChangeEvent) => Promise; let jediIsEnabledValueInSetting = jediIsEnabled; workspaceService - .setup(w => w.onDidChangeConfiguration(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .callback(cb => (callbackHandler = cb)) + .setup((w) => + w.onDidChangeConfiguration(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) + ) + .callback((cb) => (callbackHandler = cb)) .returns(() => TypeMoq.Mock.ofType().object) .verifiable(TypeMoq.Times.once()); - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabledValueInSetting); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabledValueInSetting); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activator = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, @@ -319,15 +327,15 @@ suite('Language Server Activation - ActivationService', () => { const event = TypeMoq.Mock.ofType(); event - .setup(e => e.affectsConfiguration(TypeMoq.It.isValue('python.jediEnabled'), TypeMoq.It.isAny())) + .setup((e) => e.affectsConfiguration(TypeMoq.It.isValue('python.jediEnabled'), TypeMoq.It.isAny())) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isValue('Reload'))) + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isValue('Reload'))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); cmdManager - .setup(c => c.executeCommand(TypeMoq.It.isValue('workbench.action.reloadWindow'))) + .setup((c) => c.executeCommand(TypeMoq.It.isValue('workbench.action.reloadWindow'))) .verifiable(TypeMoq.Times.never()); // Toggle the value in the setting and invoke the callback. @@ -341,13 +349,15 @@ suite('Language Server Activation - ActivationService', () => { test('Do not prompt user to reload VS Code when setting is not toggled', async () => { let callbackHandler!: (e: ConfigurationChangeEvent) => Promise; workspaceService - .setup(w => w.onDidChangeConfiguration(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .callback(cb => (callbackHandler = cb)) + .setup((w) => + w.onDidChangeConfiguration(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) + ) + .callback((cb) => (callbackHandler = cb)) .returns(() => TypeMoq.Mock.ofType().object) .verifiable(TypeMoq.Times.once()); - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activator = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, @@ -360,15 +370,15 @@ suite('Language Server Activation - ActivationService', () => { const event = TypeMoq.Mock.ofType(); event - .setup(e => e.affectsConfiguration(TypeMoq.It.isValue('python.jediEnabled'), TypeMoq.It.isAny())) + .setup((e) => e.affectsConfiguration(TypeMoq.It.isValue('python.jediEnabled'), TypeMoq.It.isAny())) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isValue('Reload'))) + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isValue('Reload'))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); cmdManager - .setup(c => c.executeCommand(TypeMoq.It.isValue('workbench.action.reloadWindow'))) + .setup((c) => c.executeCommand(TypeMoq.It.isValue('workbench.action.reloadWindow'))) .verifiable(TypeMoq.Times.never()); // Invoke the config changed callback. @@ -381,13 +391,15 @@ suite('Language Server Activation - ActivationService', () => { test('Do not prompt user to reload VS Code when setting is not changed', async () => { let callbackHandler!: (e: ConfigurationChangeEvent) => Promise; workspaceService - .setup(w => w.onDidChangeConfiguration(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .callback(cb => (callbackHandler = cb)) + .setup((w) => + w.onDidChangeConfiguration(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) + ) + .callback((cb) => (callbackHandler = cb)) .returns(() => TypeMoq.Mock.ofType().object) .verifiable(TypeMoq.Times.once()); - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activator = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, @@ -400,15 +412,15 @@ suite('Language Server Activation - ActivationService', () => { const event = TypeMoq.Mock.ofType(); event - .setup(e => e.affectsConfiguration(TypeMoq.It.isValue('python.jediEnabled'), TypeMoq.It.isAny())) + .setup((e) => e.affectsConfiguration(TypeMoq.It.isValue('python.jediEnabled'), TypeMoq.It.isAny())) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isValue('Reload'))) + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isValue('Reload'))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); cmdManager - .setup(c => c.executeCommand(TypeMoq.It.isValue('workbench.action.reloadWindow'))) + .setup((c) => c.executeCommand(TypeMoq.It.isValue('workbench.action.reloadWindow'))) .verifiable(TypeMoq.Times.never()); // Invoke the config changed callback. @@ -438,21 +450,21 @@ suite('Language Server Activation - ActivationService', () => { const folder1 = { name: 'one', uri: Uri.parse('one'), index: 1 }; const activator = TypeMoq.Mock.ofType(); activator - .setup(a => a.start(TypeMoq.It.isValue(folder1.uri), TypeMoq.It.isValue(interpreter1))) + .setup((a) => a.start(TypeMoq.It.isValue(folder1.uri), TypeMoq.It.isValue(interpreter1))) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); activator - .setup(a => a.start(TypeMoq.It.isValue(folder1.uri), TypeMoq.It.isValue(interpreter2))) + .setup((a) => a.start(TypeMoq.It.isValue(folder1.uri), TypeMoq.It.isValue(interpreter2))) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); - activator.setup(a => a.deactivate()).verifiable(TypeMoq.Times.never()); - activator.setup(a => a.activate()).verifiable(TypeMoq.Times.never()); + activator.setup((a) => a.deactivate()).verifiable(TypeMoq.Times.never()); + activator.setup((a) => a.activate()).verifiable(TypeMoq.Times.never()); activator - .setup(a => a.dispose()) + .setup((a) => a.dispose()) .returns(noop) .verifiable(TypeMoq.Times.exactly(2)); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isAny())) .returns(() => activator.object); let diagnostics: IDiagnostic[]; if (!jediIsEnabled) { @@ -461,14 +473,14 @@ suite('Language Server Activation - ActivationService', () => { diagnostics = []; } lsNotSupportedDiagnosticService - .setup(l => l.diagnose(undefined)) + .setup((l) => l.diagnose(undefined)) .returns(() => Promise.resolve(diagnostics)); lsNotSupportedDiagnosticService - .setup(l => l.handle(TypeMoq.It.isValue(diagnostics))) + .setup((l) => l.handle(TypeMoq.It.isValue(diagnostics))) .returns(() => Promise.resolve()); - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, stateFactory.object, @@ -503,7 +515,7 @@ suite('Language Server Activation - ActivationService', () => { }; let getActiveCount = 0; interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => { if (getActiveCount % 2 === 0) { getActiveCount += 1; @@ -515,21 +527,21 @@ suite('Language Server Activation - ActivationService', () => { const folder1 = { name: 'one', uri: Uri.parse('one'), index: 1 }; const activator = TypeMoq.Mock.ofType(); activator - .setup(a => a.start(TypeMoq.It.isValue(folder1.uri), TypeMoq.It.isValue(interpreter1))) + .setup((a) => a.start(TypeMoq.It.isValue(folder1.uri), TypeMoq.It.isValue(interpreter1))) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); activator - .setup(a => a.start(TypeMoq.It.isValue(folder1.uri), TypeMoq.It.isValue(interpreter2))) + .setup((a) => a.start(TypeMoq.It.isValue(folder1.uri), TypeMoq.It.isValue(interpreter2))) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); let connectCount = 0; activator - .setup(a => a.activate()) + .setup((a) => a.activate()) .returns(() => { connectCount = connectCount + 1; }); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isAny())) .returns(() => activator.object); let diagnostics: IDiagnostic[]; if (!jediIsEnabled) { @@ -538,14 +550,14 @@ suite('Language Server Activation - ActivationService', () => { diagnostics = []; } lsNotSupportedDiagnosticService - .setup(l => l.diagnose(undefined)) + .setup((l) => l.diagnose(undefined)) .returns(() => Promise.resolve(diagnostics)); lsNotSupportedDiagnosticService - .setup(l => l.handle(TypeMoq.It.isValue(diagnostics))) + .setup((l) => l.handle(TypeMoq.It.isValue(diagnostics))) .returns(() => Promise.resolve()); - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, stateFactory.object, @@ -566,8 +578,8 @@ suite('Language Server Activation - ActivationService', () => { }); if (!jediIsEnabled) { test('Revert to jedi when LS activation fails', async () => { - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activatorDotNet = TypeMoq.Mock.ofType(); const activatorJedi = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( @@ -577,13 +589,13 @@ suite('Language Server Activation - ActivationService', () => { ); const diagnostics: IDiagnostic[] = []; lsNotSupportedDiagnosticService - .setup(l => l.diagnose(undefined)) + .setup((l) => l.diagnose(undefined)) .returns(() => Promise.resolve(diagnostics)); lsNotSupportedDiagnosticService - .setup(l => l.handle(TypeMoq.It.isValue(diagnostics))) + .setup((l) => l.handle(TypeMoq.It.isValue(diagnostics))) .returns(() => Promise.resolve()); serviceContainer - .setup(c => + .setup((c) => c.get( TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isValue(LanguageServerType.Microsoft) @@ -592,11 +604,11 @@ suite('Language Server Activation - ActivationService', () => { .returns(() => activatorDotNet.object) .verifiable(TypeMoq.Times.once()); activatorDotNet - .setup(a => a.start(undefined, undefined)) + .setup((a) => a.start(undefined, undefined)) .returns(() => Promise.reject(new Error(''))) .verifiable(TypeMoq.Times.once()); serviceContainer - .setup(c => + .setup((c) => c.get( TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isValue(LanguageServerType.Jedi) @@ -605,11 +617,11 @@ suite('Language Server Activation - ActivationService', () => { .returns(() => activatorJedi.object) .verifiable(TypeMoq.Times.once()); activatorJedi - .setup(a => a.start(undefined, undefined)) + .setup((a) => a.start(undefined, undefined)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); activatorJedi - .setup(a => a.activate()) + .setup((a) => a.activate()) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); @@ -625,18 +637,18 @@ suite('Language Server Activation - ActivationService', () => { resource: Resource ) { activator - .setup(a => a.start(TypeMoq.It.isValue(resource), undefined)) + .setup((a) => a.start(TypeMoq.It.isValue(resource), undefined)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); - activator.setup(a => a.activate()).verifiable(TypeMoq.Times.once()); + activator.setup((a) => a.activate()).verifiable(TypeMoq.Times.once()); lsNotSupportedDiagnosticService - .setup(l => l.diagnose(undefined)) + .setup((l) => l.diagnose(undefined)) .returns(() => Promise.resolve([])); lsNotSupportedDiagnosticService - .setup(l => l.handle(TypeMoq.It.isValue([]))) + .setup((l) => l.handle(TypeMoq.It.isValue([]))) .returns(() => Promise.resolve()); serviceContainer - .setup(c => + .setup((c) => c.get( TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isValue(LanguageServerType.Microsoft) @@ -645,11 +657,11 @@ suite('Language Server Activation - ActivationService', () => { .returns(() => activator.object) .verifiable(TypeMoq.Times.atLeastOnce()); workspaceService - .setup(w => w.getWorkspaceFolderIdentifier(resource, '')) + .setup((w) => w.getWorkspaceFolderIdentifier(resource, '')) .returns(() => resource!.fsPath) .verifiable(TypeMoq.Times.atLeastOnce()); experiments - .setup(ex => ex.inExperiment(TypeMoq.It.isAny())) + .setup((ex) => ex.inExperiment(TypeMoq.It.isAny())) .returns(() => false) .verifiable(TypeMoq.Times.never()); @@ -661,12 +673,12 @@ suite('Language Server Activation - ActivationService', () => { experiments.verifyAll(); } test('Activator is disposed if activated workspace is removed', async () => { - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); let workspaceFoldersChangedHandler!: Function; workspaceService - .setup(w => w.onDidChangeWorkspaceFolders(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .callback(cb => (workspaceFoldersChangedHandler = cb)) + .setup((w) => w.onDidChangeWorkspaceFolders(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .callback((cb) => (workspaceFoldersChangedHandler = cb)) .returns(() => TypeMoq.Mock.ofType().object) .verifiable(TypeMoq.Times.once()); const activationService = new LanguageServerExtensionActivationService( @@ -689,26 +701,26 @@ suite('Language Server Activation - ActivationService', () => { //Now remove folder3 workspaceService.reset(); - workspaceService.setup(w => w.workspaceFolders).returns(() => [folder1, folder2]); + workspaceService.setup((w) => w.workspaceFolders).returns(() => [folder1, folder2]); workspaceService - .setup(w => w.getWorkspaceFolderIdentifier(folder1.uri, '')) + .setup((w) => w.getWorkspaceFolderIdentifier(folder1.uri, '')) .returns(() => folder1.uri.fsPath) .verifiable(TypeMoq.Times.atLeastOnce()); workspaceService - .setup(w => w.getWorkspaceFolderIdentifier(folder2.uri, '')) + .setup((w) => w.getWorkspaceFolderIdentifier(folder2.uri, '')) .returns(() => folder2.uri.fsPath) .verifiable(TypeMoq.Times.atLeastOnce()); - activator1.setup(d => d.dispose()).verifiable(TypeMoq.Times.never()); - activator2.setup(d => d.dispose()).verifiable(TypeMoq.Times.never()); - activator3.setup(d => d.dispose()).verifiable(TypeMoq.Times.once()); + activator1.setup((d) => d.dispose()).verifiable(TypeMoq.Times.never()); + activator2.setup((d) => d.dispose()).verifiable(TypeMoq.Times.never()); + activator3.setup((d) => d.dispose()).verifiable(TypeMoq.Times.once()); await workspaceFoldersChangedHandler.call(activationService); workspaceService.verifyAll(); activator3.verifyAll(); }); } else { test('Jedi is only started once', async () => { - pythonSettings.setup(p => p.jediEnabled).returns(() => jediIsEnabled); - pythonSettings.setup(p => p.languageServer).returns(() => LanguageServerType.Microsoft); + pythonSettings.setup((p) => p.jediEnabled).returns(() => jediIsEnabled); + pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Microsoft); const activator1 = TypeMoq.Mock.ofType(); const activationService = new LanguageServerExtensionActivationService( serviceContainer.object, @@ -718,7 +730,7 @@ suite('Language Server Activation - ActivationService', () => { const folder1 = { name: 'one', uri: Uri.parse('one'), index: 1 }; const folder2 = { name: 'two', uri: Uri.parse('two'), index: 2 }; serviceContainer - .setup(c => + .setup((c) => c.get( TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isValue(LanguageServerType.Jedi) @@ -727,22 +739,22 @@ suite('Language Server Activation - ActivationService', () => { .returns(() => activator1.object) .verifiable(TypeMoq.Times.once()); activator1 - .setup(a => a.start(folder1.uri, undefined)) + .setup((a) => a.start(folder1.uri, undefined)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); experiments - .setup(ex => ex.inExperiment(TypeMoq.It.isAny())) + .setup((ex) => ex.inExperiment(TypeMoq.It.isAny())) .returns(() => false) .verifiable(TypeMoq.Times.never()); await activationService.activate(folder1.uri); activator1.verifyAll(); - activator1.verify(a => a.activate(), TypeMoq.Times.once()); + activator1.verify((a) => a.activate(), TypeMoq.Times.once()); serviceContainer.verifyAll(); experiments.verifyAll(); const activator2 = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => + .setup((c) => c.get( TypeMoq.It.isValue(ILanguageServerActivator), TypeMoq.It.isValue(LanguageServerType.Jedi) @@ -751,18 +763,18 @@ suite('Language Server Activation - ActivationService', () => { .returns(() => activator2.object) .verifiable(TypeMoq.Times.once()); activator2 - .setup(a => a.start(folder2.uri, undefined)) + .setup((a) => a.start(folder2.uri, undefined)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); - activator2.setup(a => a.activate()).verifiable(TypeMoq.Times.never()); + activator2.setup((a) => a.activate()).verifiable(TypeMoq.Times.never()); experiments - .setup(ex => ex.inExperiment(TypeMoq.It.isAny())) + .setup((ex) => ex.inExperiment(TypeMoq.It.isAny())) .returns(() => false) .verifiable(TypeMoq.Times.never()); await activationService.activate(folder2.uri); serviceContainer.verifyAll(); activator1.verifyAll(); - activator1.verify(a => a.activate(), TypeMoq.Times.exactly(2)); + activator1.verify((a) => a.activate(), TypeMoq.Times.exactly(2)); activator2.verifyAll(); experiments.verifyAll(); }); @@ -796,21 +808,21 @@ suite('Language Server Activation - ActivationService', () => { experiments = TypeMoq.Mock.ofType(); interpreterService = TypeMoq.Mock.ofType(); const e = new EventEmitter(); - interpreterService.setup(i => i.onDidChangeInterpreter).returns(() => e.event); + interpreterService.setup((i) => i.onDidChangeInterpreter).returns(() => e.event); const langFolderServiceMock = TypeMoq.Mock.ofType(); const folderVer: FolderVersionPair = { path: '', version: new SemVer('1.2.3') }; lsNotSupportedDiagnosticService = TypeMoq.Mock.ofType(); - workspaceService.setup(w => w.hasWorkspaceFolders).returns(() => false); - workspaceService.setup(w => w.workspaceFolders).returns(() => []); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + workspaceService.setup((w) => w.hasWorkspaceFolders).returns(() => false); + workspaceService.setup((w) => w.workspaceFolders).returns(() => []); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); langFolderServiceMock - .setup(l => l.getCurrentLanguageServerDirectory()) + .setup((l) => l.getCurrentLanguageServerDirectory()) .returns(() => Promise.resolve(folderVer)); stateFactory - .setup(f => + .setup((f) => f.createGlobalPersistentState( TypeMoq.It.isValue('SWITCH_LS'), TypeMoq.It.isAny(), @@ -818,36 +830,36 @@ suite('Language Server Activation - ActivationService', () => { ) ) .returns(() => state.object); - state.setup(s => s.value).returns(() => undefined); - state.setup(s => s.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + state.setup((s) => s.value).returns(() => undefined); + state.setup((s) => s.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); workspaceConfig = TypeMoq.Mock.ofType(); workspaceService - .setup(ws => ws.getConfiguration('python', TypeMoq.It.isAny())) + .setup((ws) => ws.getConfiguration('python', TypeMoq.It.isAny())) .returns(() => workspaceConfig.object); const output = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny())) .returns(() => output.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICommandManager))).returns(() => cmdManager.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICommandManager))).returns(() => cmdManager.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService))) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) .returns(() => platformService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService))) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService))) .returns(() => interpreterService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILanguageServerFolderService))) + .setup((c) => c.get(TypeMoq.It.isValue(ILanguageServerFolderService))) .returns(() => langFolderServiceMock.object); serviceContainer - .setup(s => + .setup((s) => s.get( TypeMoq.It.isValue(IDiagnosticsService), TypeMoq.It.isValue(LSNotSupportedDiagnosticServiceId) @@ -859,13 +871,13 @@ suite('Language Server Activation - ActivationService', () => { test('Track current LS usage for first usage', async () => { state.reset(); state - .setup(s => s.value) + .setup((s) => s.value) .returns(() => undefined) .verifiable(TypeMoq.Times.exactly(2)); state - .setup(s => s.updateValue(TypeMoq.It.isValue(true))) + .setup((s) => s.updateValue(TypeMoq.It.isValue(true))) .returns(() => { - state.setup(s => s.value).returns(() => true); + state.setup((s) => s.value).returns(() => true); return Promise.resolve(); }) .verifiable(TypeMoq.Times.once()); @@ -882,11 +894,11 @@ suite('Language Server Activation - ActivationService', () => { test('Track switch to LS', async () => { state.reset(); state - .setup(s => s.value) + .setup((s) => s.value) .returns(() => true) .verifiable(TypeMoq.Times.exactly(2)); state - .setup(s => s.updateValue(TypeMoq.It.isValue(false))) + .setup((s) => s.updateValue(TypeMoq.It.isValue(false))) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); @@ -902,11 +914,11 @@ suite('Language Server Activation - ActivationService', () => { test('Track switch to Jedi', async () => { state.reset(); state - .setup(s => s.value) + .setup((s) => s.value) .returns(() => false) .verifiable(TypeMoq.Times.exactly(2)); state - .setup(s => s.updateValue(TypeMoq.It.isValue(true))) + .setup((s) => s.updateValue(TypeMoq.It.isValue(true))) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); @@ -922,11 +934,11 @@ suite('Language Server Activation - ActivationService', () => { test('Track startup value', async () => { state.reset(); state - .setup(s => s.value) + .setup((s) => s.value) .returns(() => true) .verifiable(TypeMoq.Times.exactly(2)); state - .setup(s => s.updateValue(TypeMoq.It.isAny())) + .setup((s) => s.updateValue(TypeMoq.It.isAny())) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); @@ -967,21 +979,21 @@ suite('Language Server Activation - ActivationService', () => { experiments = TypeMoq.Mock.ofType(); interpreterService = TypeMoq.Mock.ofType(); const e = new EventEmitter(); - interpreterService.setup(i => i.onDidChangeInterpreter).returns(() => e.event); + interpreterService.setup((i) => i.onDidChangeInterpreter).returns(() => e.event); const langFolderServiceMock = TypeMoq.Mock.ofType(); const folderVer: FolderVersionPair = { path: '', version: new SemVer('1.2.3') }; lsNotSupportedDiagnosticService = TypeMoq.Mock.ofType(); - workspaceService.setup(w => w.hasWorkspaceFolders).returns(() => false); - workspaceService.setup(w => w.workspaceFolders).returns(() => []); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + workspaceService.setup((w) => w.hasWorkspaceFolders).returns(() => false); + workspaceService.setup((w) => w.workspaceFolders).returns(() => []); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); langFolderServiceMock - .setup(l => l.getCurrentLanguageServerDirectory()) + .setup((l) => l.getCurrentLanguageServerDirectory()) .returns(() => Promise.resolve(folderVer)); stateFactory - .setup(f => + .setup((f) => f.createGlobalPersistentState( TypeMoq.It.isValue('SWITCH_LS'), TypeMoq.It.isAny(), @@ -989,36 +1001,36 @@ suite('Language Server Activation - ActivationService', () => { ) ) .returns(() => state.object); - state.setup(s => s.value).returns(() => undefined); - state.setup(s => s.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + state.setup((s) => s.value).returns(() => undefined); + state.setup((s) => s.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); workspaceConfig = TypeMoq.Mock.ofType(); workspaceService - .setup(ws => ws.getConfiguration('python', TypeMoq.It.isAny())) + .setup((ws) => ws.getConfiguration('python', TypeMoq.It.isAny())) .returns(() => workspaceConfig.object); const output = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny())) .returns(() => output.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICommandManager))).returns(() => cmdManager.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICommandManager))).returns(() => cmdManager.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService))) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) .returns(() => platformService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService))) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService))) .returns(() => interpreterService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILanguageServerFolderService))) + .setup((c) => c.get(TypeMoq.It.isValue(ILanguageServerFolderService))) .returns(() => langFolderServiceMock.object); serviceContainer - .setup(s => + .setup((s) => s.get( TypeMoq.It.isValue(IDiagnosticsService), TypeMoq.It.isValue(LSNotSupportedDiagnosticServiceId) @@ -1030,15 +1042,15 @@ suite('Language Server Activation - ActivationService', () => { test('If default value of jedi is being used, and LSEnabled experiment is enabled, then return false', async () => { const settings = {}; experiments - .setup(ex => ex.inExperiment(LSEnabled)) + .setup((ex) => ex.inExperiment(LSEnabled)) .returns(() => true) .verifiable(TypeMoq.Times.once()); experiments - .setup(ex => ex.sendTelemetryIfInExperiment(TypeMoq.It.isAny())) + .setup((ex) => ex.sendTelemetryIfInExperiment(TypeMoq.It.isAny())) .returns(() => undefined) .verifiable(TypeMoq.Times.never()); workspaceConfig - .setup(c => c.inspect('jediEnabled')) + .setup((c) => c.inspect('jediEnabled')) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); @@ -1058,19 +1070,19 @@ suite('Language Server Activation - ActivationService', () => { test('If default value of jedi is being used, and LSEnabled experiment is disabled, then send telemetry if user is in Experiment LSControl and return python settings value (which will always be true as default value is true)', async () => { const settings = {}; experiments - .setup(ex => ex.inExperiment(LSEnabled)) + .setup((ex) => ex.inExperiment(LSEnabled)) .returns(() => false) .verifiable(TypeMoq.Times.once()); experiments - .setup(ex => ex.sendTelemetryIfInExperiment(LSControl)) + .setup((ex) => ex.sendTelemetryIfInExperiment(LSControl)) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); workspaceConfig - .setup(c => c.inspect('jediEnabled')) + .setup((c) => c.inspect('jediEnabled')) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); pythonSettings - .setup(p => p.jediEnabled) + .setup((p) => p.jediEnabled) .returns(() => true) .verifiable(TypeMoq.Times.once()); @@ -1102,23 +1114,23 @@ suite('Language Server Activation - ActivationService', () => { pythonSettingsValue: true, expectedResult: true } - ].forEach(testParams => { + ].forEach((testParams) => { test(testParams.testName, async () => { const settings = { workspaceFolderValue: true }; experiments - .setup(ex => ex.inExperiment(LSEnabled)) + .setup((ex) => ex.inExperiment(LSEnabled)) .returns(() => false) .verifiable(TypeMoq.Times.never()); experiments - .setup(ex => ex.sendTelemetryIfInExperiment(LSControl)) + .setup((ex) => ex.sendTelemetryIfInExperiment(LSControl)) .returns(() => undefined) .verifiable(TypeMoq.Times.never()); workspaceConfig - .setup(c => c.inspect('jediEnabled')) + .setup((c) => c.inspect('jediEnabled')) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); pythonSettings - .setup(p => p.jediEnabled) + .setup((p) => p.jediEnabled) .returns(() => testParams.pythonSettingsValue) .verifiable(TypeMoq.Times.once()); @@ -1169,21 +1181,21 @@ suite('Language Server Activation - ActivationService', () => { experiments = TypeMoq.Mock.ofType(); interpreterService = TypeMoq.Mock.ofType(); const e = new EventEmitter(); - interpreterService.setup(i => i.onDidChangeInterpreter).returns(() => e.event); + interpreterService.setup((i) => i.onDidChangeInterpreter).returns(() => e.event); const langFolderServiceMock = TypeMoq.Mock.ofType(); const folderVer: FolderVersionPair = { path: '', version: new SemVer('1.2.3') }; lsNotSupportedDiagnosticService = TypeMoq.Mock.ofType(); - workspaceService.setup(w => w.hasWorkspaceFolders).returns(() => false); - workspaceService.setup(w => w.workspaceFolders).returns(() => []); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + workspaceService.setup((w) => w.hasWorkspaceFolders).returns(() => false); + workspaceService.setup((w) => w.workspaceFolders).returns(() => []); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); langFolderServiceMock - .setup(l => l.getCurrentLanguageServerDirectory()) + .setup((l) => l.getCurrentLanguageServerDirectory()) .returns(() => Promise.resolve(folderVer)); stateFactory - .setup(f => + .setup((f) => f.createGlobalPersistentState( TypeMoq.It.isValue('SWITCH_LS'), TypeMoq.It.isAny(), @@ -1191,36 +1203,36 @@ suite('Language Server Activation - ActivationService', () => { ) ) .returns(() => state.object); - state.setup(s => s.value).returns(() => undefined); - state.setup(s => s.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + state.setup((s) => s.value).returns(() => undefined); + state.setup((s) => s.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); workspaceConfig = TypeMoq.Mock.ofType(); workspaceService - .setup(ws => ws.getConfiguration('python', TypeMoq.It.isAny())) + .setup((ws) => ws.getConfiguration('python', TypeMoq.It.isAny())) .returns(() => workspaceConfig.object); const output = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny())) .returns(() => output.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICommandManager))).returns(() => cmdManager.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICommandManager))).returns(() => cmdManager.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService))) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) .returns(() => platformService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService))) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService))) .returns(() => interpreterService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILanguageServerFolderService))) + .setup((c) => c.get(TypeMoq.It.isValue(ILanguageServerFolderService))) .returns(() => langFolderServiceMock.object); serviceContainer - .setup(s => + .setup((s) => s.get( TypeMoq.It.isValue(IDiagnosticsService), TypeMoq.It.isValue(LSNotSupportedDiagnosticServiceId) @@ -1251,7 +1263,7 @@ suite('Language Server Activation - ActivationService', () => { test(testName, async () => { workspaceConfig.reset(); workspaceConfig - .setup(c => c.inspect('jediEnabled')) + .setup((c) => c.inspect('jediEnabled')) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); @@ -1272,7 +1284,7 @@ suite('Language Server Activation - ActivationService', () => { test('Returns false for settings = undefined', async () => { workspaceConfig.reset(); workspaceConfig - .setup(c => c.inspect('jediEnabled')) + .setup((c) => c.inspect('jediEnabled')) .returns(() => undefined as any) .verifiable(TypeMoq.Times.once()); diff --git a/src/test/activation/activeResource.unit.test.ts b/src/test/activation/activeResource.unit.test.ts index 1ed39cd6f7b6..ca203155078c 100644 --- a/src/test/activation/activeResource.unit.test.ts +++ b/src/test/activation/activeResource.unit.test.ts @@ -1,101 +1,101 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { assert } from 'chai'; -import { instance, mock, verify, when } from 'ts-mockito'; -import { Uri } from 'vscode'; -import { ActiveResourceService } from '../../client/common/application/activeResource'; -import { DocumentManager } from '../../client/common/application/documentManager'; -import { IDocumentManager, IWorkspaceService } from '../../client/common/application/types'; -import { WorkspaceService } from '../../client/common/application/workspace'; - -// tslint:disable-next-line: max-func-body-length -suite('Active resource service', () => { - let documentManager: IDocumentManager; - let workspaceService: IWorkspaceService; - let activeResourceService: ActiveResourceService; - setup(() => { - documentManager = mock(DocumentManager); - workspaceService = mock(WorkspaceService); - activeResourceService = new ActiveResourceService(instance(documentManager), instance(workspaceService)); - }); - - test('Return document uri if the active document is not new (has been saved)', async () => { - const activeTextEditor = { - document: { - isUntitled: false, - uri: Uri.parse('a') - } - }; - // tslint:disable-next-line:no-any - when(documentManager.activeTextEditor).thenReturn(activeTextEditor as any); - - const activeResource = activeResourceService.getActiveResource(); - - assert.deepEqual(activeResource, activeTextEditor.document.uri); - verify(documentManager.activeTextEditor).atLeast(1); - verify(workspaceService.workspaceFolders).never(); - }); - - test("Don't return document uri if the active document is new (still unsaved)", async () => { - const activeTextEditor = { - document: { - isUntitled: true, - uri: Uri.parse('a') - } - }; - // tslint:disable-next-line:no-any - when(documentManager.activeTextEditor).thenReturn(activeTextEditor as any); - when(workspaceService.workspaceFolders).thenReturn([]); - - const activeResource = activeResourceService.getActiveResource(); - - assert.notDeepEqual(activeResource, activeTextEditor.document.uri); - verify(documentManager.activeTextEditor).atLeast(1); - verify(workspaceService.workspaceFolders).atLeast(1); - }); - - test('If no document is currently opened & the workspace opened contains workspace folders, return the uri of the first workspace folder', async () => { - const workspaceFolders = [ - { - uri: Uri.parse('a') - }, - { - uri: Uri.parse('b') - } - ]; - when(documentManager.activeTextEditor).thenReturn(undefined); - // tslint:disable-next-line:no-any - when(workspaceService.workspaceFolders).thenReturn(workspaceFolders as any); - - const activeResource = activeResourceService.getActiveResource(); - - assert.deepEqual(activeResource, workspaceFolders[0].uri); - verify(documentManager.activeTextEditor).atLeast(1); - verify(workspaceService.workspaceFolders).atLeast(1); - }); - - test('If no document is currently opened & no folder is opened, return undefined', async () => { - when(documentManager.activeTextEditor).thenReturn(undefined); - when(workspaceService.workspaceFolders).thenReturn(undefined); - - const activeResource = activeResourceService.getActiveResource(); - - assert.deepEqual(activeResource, undefined); - verify(documentManager.activeTextEditor).atLeast(1); - verify(workspaceService.workspaceFolders).atLeast(1); - }); - - test('If no document is currently opened & workspace contains no workspace folders, return undefined', async () => { - when(documentManager.activeTextEditor).thenReturn(undefined); - when(workspaceService.workspaceFolders).thenReturn([]); - - const activeResource = activeResourceService.getActiveResource(); - - assert.deepEqual(activeResource, undefined); - verify(documentManager.activeTextEditor).atLeast(1); - verify(workspaceService.workspaceFolders).atLeast(1); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { assert } from 'chai'; +import { instance, mock, verify, when } from 'ts-mockito'; +import { Uri } from 'vscode'; +import { ActiveResourceService } from '../../client/common/application/activeResource'; +import { DocumentManager } from '../../client/common/application/documentManager'; +import { IDocumentManager, IWorkspaceService } from '../../client/common/application/types'; +import { WorkspaceService } from '../../client/common/application/workspace'; + +// tslint:disable-next-line: max-func-body-length +suite('Active resource service', () => { + let documentManager: IDocumentManager; + let workspaceService: IWorkspaceService; + let activeResourceService: ActiveResourceService; + setup(() => { + documentManager = mock(DocumentManager); + workspaceService = mock(WorkspaceService); + activeResourceService = new ActiveResourceService(instance(documentManager), instance(workspaceService)); + }); + + test('Return document uri if the active document is not new (has been saved)', async () => { + const activeTextEditor = { + document: { + isUntitled: false, + uri: Uri.parse('a') + } + }; + // tslint:disable-next-line:no-any + when(documentManager.activeTextEditor).thenReturn(activeTextEditor as any); + + const activeResource = activeResourceService.getActiveResource(); + + assert.deepEqual(activeResource, activeTextEditor.document.uri); + verify(documentManager.activeTextEditor).atLeast(1); + verify(workspaceService.workspaceFolders).never(); + }); + + test("Don't return document uri if the active document is new (still unsaved)", async () => { + const activeTextEditor = { + document: { + isUntitled: true, + uri: Uri.parse('a') + } + }; + // tslint:disable-next-line:no-any + when(documentManager.activeTextEditor).thenReturn(activeTextEditor as any); + when(workspaceService.workspaceFolders).thenReturn([]); + + const activeResource = activeResourceService.getActiveResource(); + + assert.notDeepEqual(activeResource, activeTextEditor.document.uri); + verify(documentManager.activeTextEditor).atLeast(1); + verify(workspaceService.workspaceFolders).atLeast(1); + }); + + test('If no document is currently opened & the workspace opened contains workspace folders, return the uri of the first workspace folder', async () => { + const workspaceFolders = [ + { + uri: Uri.parse('a') + }, + { + uri: Uri.parse('b') + } + ]; + when(documentManager.activeTextEditor).thenReturn(undefined); + // tslint:disable-next-line:no-any + when(workspaceService.workspaceFolders).thenReturn(workspaceFolders as any); + + const activeResource = activeResourceService.getActiveResource(); + + assert.deepEqual(activeResource, workspaceFolders[0].uri); + verify(documentManager.activeTextEditor).atLeast(1); + verify(workspaceService.workspaceFolders).atLeast(1); + }); + + test('If no document is currently opened & no folder is opened, return undefined', async () => { + when(documentManager.activeTextEditor).thenReturn(undefined); + when(workspaceService.workspaceFolders).thenReturn(undefined); + + const activeResource = activeResourceService.getActiveResource(); + + assert.deepEqual(activeResource, undefined); + verify(documentManager.activeTextEditor).atLeast(1); + verify(workspaceService.workspaceFolders).atLeast(1); + }); + + test('If no document is currently opened & workspace contains no workspace folders, return undefined', async () => { + when(documentManager.activeTextEditor).thenReturn(undefined); + when(workspaceService.workspaceFolders).thenReturn([]); + + const activeResource = activeResourceService.getActiveResource(); + + assert.deepEqual(activeResource, undefined); + verify(documentManager.activeTextEditor).atLeast(1); + verify(workspaceService.workspaceFolders).atLeast(1); + }); +}); diff --git a/src/test/activation/extensionSurvey.unit.test.ts b/src/test/activation/extensionSurvey.unit.test.ts index b399c4151039..ddee96f38b58 100644 --- a/src/test/activation/extensionSurvey.unit.test.ts +++ b/src/test/activation/extensionSurvey.unit.test.ts @@ -71,10 +71,10 @@ suite('Extension survey prompt - shouldShowBanner()', () => { }); test('Returns false if do not show again is clicked', async () => { random - .setup(r => r.getRandomInt(0, 100)) + .setup((r) => r.getRandomInt(0, 100)) .returns(() => 10) .verifiable(TypeMoq.Times.never()); - doNotShowAgain.setup(d => d.value).returns(() => true); + doNotShowAgain.setup((d) => d.value).returns(() => true); const result = extensionSurveyPrompt.shouldShowBanner(); @@ -93,11 +93,11 @@ suite('Extension survey prompt - shouldShowBanner()', () => { }); test('Returns false if prompt is disabled for a while', async () => { random - .setup(r => r.getRandomInt(0, 100)) + .setup((r) => r.getRandomInt(0, 100)) .returns(() => 10) .verifiable(TypeMoq.Times.never()); - disableSurveyForTime.setup(d => d.value).returns(() => true); - doNotShowAgain.setup(d => d.value).returns(() => false); + disableSurveyForTime.setup((d) => d.value).returns(() => true); + doNotShowAgain.setup((d) => d.value).returns(() => false); const result = extensionSurveyPrompt.shouldShowBanner(); @@ -115,22 +115,22 @@ suite('Extension survey prompt - shouldShowBanner()', () => { random.verifyAll(); }); test('Returns false if user is not in the random sampling', async () => { - disableSurveyForTime.setup(d => d.value).returns(() => false); - doNotShowAgain.setup(d => d.value).returns(() => false); + disableSurveyForTime.setup((d) => d.value).returns(() => false); + doNotShowAgain.setup((d) => d.value).returns(() => false); // Default sample size is 10 for (let i = 10; i < 100; i = i + 1) { - random.setup(r => r.getRandomInt(0, 100)).returns(() => i); + random.setup((r) => r.getRandomInt(0, 100)).returns(() => i); const result = extensionSurveyPrompt.shouldShowBanner(); expect(result).to.equal(false, 'Banner should not be shown'); } random.verifyAll(); }); test('Returns true if user is in the random sampling', async () => { - disableSurveyForTime.setup(d => d.value).returns(() => false); - doNotShowAgain.setup(d => d.value).returns(() => false); + disableSurveyForTime.setup((d) => d.value).returns(() => false); + doNotShowAgain.setup((d) => d.value).returns(() => false); // Default sample size is 10 for (let i = 0; i < 10; i = i + 1) { - random.setup(r => r.getRandomInt(0, 100)).returns(() => i); + random.setup((r) => r.getRandomInt(0, 100)).returns(() => i); const result = extensionSurveyPrompt.shouldShowBanner(); expect(result).to.equal(true, 'Banner should be shown'); } @@ -147,10 +147,10 @@ suite('Extension survey prompt - shouldShowBanner()', () => { platformService.object, 100 ); - disableSurveyForTime.setup(d => d.value).returns(() => false); - doNotShowAgain.setup(d => d.value).returns(() => false); + disableSurveyForTime.setup((d) => d.value).returns(() => false); + doNotShowAgain.setup((d) => d.value).returns(() => false); for (let i = 0; i < 100; i = i + 1) { - random.setup(r => r.getRandomInt(0, 100)).returns(() => i); + random.setup((r) => r.getRandomInt(0, 100)).returns(() => i); const result = extensionSurveyPrompt.shouldShowBanner(); expect(result).to.equal(true, 'Banner should be shown'); } @@ -167,10 +167,10 @@ suite('Extension survey prompt - shouldShowBanner()', () => { platformService.object, 0 ); - disableSurveyForTime.setup(d => d.value).returns(() => false); - doNotShowAgain.setup(d => d.value).returns(() => false); + disableSurveyForTime.setup((d) => d.value).returns(() => false); + doNotShowAgain.setup((d) => d.value).returns(() => false); for (let i = 0; i < 100; i = i + 1) { - random.setup(r => r.getRandomInt(0, 100)).returns(() => i); + random.setup((r) => r.getRandomInt(0, 100)).returns(() => i); const result = extensionSurveyPrompt.shouldShowBanner(); expect(result).to.equal(false, 'Banner should not be shown'); } @@ -229,35 +229,35 @@ suite('Extension survey prompt - showSurvey()', () => { const prompts = [LanguageService.bannerLabelYes(), ExtensionSurveyBanner.maybeLater(), Common.doNotShowAgain()]; const expectedUrl = `https://aka.ms/AA5rjx5?o=Windows&v=vscodeVersion&e=extensionVersion&m=machineId`; appEnvironment - .setup(a => a.packageJson) + .setup((a) => a.packageJson) .returns(() => packageJson) .verifiable(TypeMoq.Times.once()); appEnvironment - .setup(a => a.vscodeVersion) + .setup((a) => a.vscodeVersion) .returns(() => 'vscodeVersion') .verifiable(TypeMoq.Times.once()); appEnvironment - .setup(a => a.machineId) + .setup((a) => a.machineId) .returns(() => 'machineId') .verifiable(TypeMoq.Times.once()); platformService - .setup(a => a.osType) + .setup((a) => a.osType) .returns(() => OSType.Windows) .verifiable(TypeMoq.Times.once()); appShell - .setup(a => a.showInformationMessage(ExtensionSurveyBanner.bannerMessage(), ...prompts)) + .setup((a) => a.showInformationMessage(ExtensionSurveyBanner.bannerMessage(), ...prompts)) .returns(() => Promise.resolve(LanguageService.bannerLabelYes())) .verifiable(TypeMoq.Times.once()); browserService - .setup(s => s.launch(expectedUrl)) + .setup((s) => s.launch(expectedUrl)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); disableSurveyForTime - .setup(d => d.updateValue(true)) + .setup((d) => d.updateValue(true)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); doNotShowAgain - .setup(d => d.updateValue(true)) + .setup((d) => d.updateValue(true)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); @@ -283,21 +283,21 @@ suite('Extension survey prompt - showSurvey()', () => { test("Do nothing if 'Maybe later' option is clicked", async () => { const prompts = [LanguageService.bannerLabelYes(), ExtensionSurveyBanner.maybeLater(), Common.doNotShowAgain()]; - platformService.setup(p => p.osType).verifiable(TypeMoq.Times.never()); + platformService.setup((p) => p.osType).verifiable(TypeMoq.Times.never()); appShell - .setup(a => a.showInformationMessage(ExtensionSurveyBanner.bannerMessage(), ...prompts)) + .setup((a) => a.showInformationMessage(ExtensionSurveyBanner.bannerMessage(), ...prompts)) .returns(() => Promise.resolve(ExtensionSurveyBanner.maybeLater())) .verifiable(TypeMoq.Times.once()); browserService - .setup(s => s.launch(TypeMoq.It.isAny())) + .setup((s) => s.launch(TypeMoq.It.isAny())) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); disableSurveyForTime - .setup(d => d.updateValue(true)) + .setup((d) => d.updateValue(true)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); doNotShowAgain - .setup(d => d.updateValue(true)) + .setup((d) => d.updateValue(true)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); @@ -322,21 +322,21 @@ suite('Extension survey prompt - showSurvey()', () => { test('Do nothing if no option is clicked', async () => { const prompts = [LanguageService.bannerLabelYes(), ExtensionSurveyBanner.maybeLater(), Common.doNotShowAgain()]; - platformService.setup(p => p.osType).verifiable(TypeMoq.Times.never()); + platformService.setup((p) => p.osType).verifiable(TypeMoq.Times.never()); appShell - .setup(a => a.showInformationMessage(ExtensionSurveyBanner.bannerMessage(), ...prompts)) + .setup((a) => a.showInformationMessage(ExtensionSurveyBanner.bannerMessage(), ...prompts)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); browserService - .setup(s => s.launch(TypeMoq.It.isAny())) + .setup((s) => s.launch(TypeMoq.It.isAny())) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); disableSurveyForTime - .setup(d => d.updateValue(true)) + .setup((d) => d.updateValue(true)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); doNotShowAgain - .setup(d => d.updateValue(true)) + .setup((d) => d.updateValue(true)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); @@ -361,21 +361,21 @@ suite('Extension survey prompt - showSurvey()', () => { test("Disable prompt if 'Do not show again' option is clicked", async () => { const prompts = [LanguageService.bannerLabelYes(), ExtensionSurveyBanner.maybeLater(), Common.doNotShowAgain()]; - platformService.setup(p => p.osType).verifiable(TypeMoq.Times.never()); + platformService.setup((p) => p.osType).verifiable(TypeMoq.Times.never()); appShell - .setup(a => a.showInformationMessage(ExtensionSurveyBanner.bannerMessage(), ...prompts)) + .setup((a) => a.showInformationMessage(ExtensionSurveyBanner.bannerMessage(), ...prompts)) .returns(() => Promise.resolve(Common.doNotShowAgain())) .verifiable(TypeMoq.Times.once()); browserService - .setup(s => s.launch(TypeMoq.It.isAny())) + .setup((s) => s.launch(TypeMoq.It.isAny())) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); disableSurveyForTime - .setup(d => d.updateValue(true)) + .setup((d) => d.updateValue(true)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); doNotShowAgain - .setup(d => d.updateValue(true)) + .setup((d) => d.updateValue(true)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); @@ -440,11 +440,11 @@ suite('Extension survey prompt - activate()', () => { 10 ); experiments - .setup(exp => exp.inExperiment(ShowExtensionSurveyPrompt.enabled)) + .setup((exp) => exp.inExperiment(ShowExtensionSurveyPrompt.enabled)) .returns(() => false) .verifiable(TypeMoq.Times.once()); experiments - .setup(exp => exp.sendTelemetryIfInExperiment(ShowExtensionSurveyPrompt.control)) + .setup((exp) => exp.sendTelemetryIfInExperiment(ShowExtensionSurveyPrompt.control)) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); await extensionSurveyPrompt.activate(); @@ -474,11 +474,11 @@ suite('Extension survey prompt - activate()', () => { 50 ); experiments - .setup(exp => exp.inExperiment(ShowExtensionSurveyPrompt.enabled)) + .setup((exp) => exp.inExperiment(ShowExtensionSurveyPrompt.enabled)) .returns(() => true) .verifiable(TypeMoq.Times.once()); experiments - .setup(exp => exp.sendTelemetryIfInExperiment(TypeMoq.It.isAny())) + .setup((exp) => exp.sendTelemetryIfInExperiment(TypeMoq.It.isAny())) .returns(() => undefined) .verifiable(TypeMoq.Times.never()); await extensionSurveyPrompt.activate(); @@ -512,11 +512,11 @@ suite('Extension survey prompt - activate()', () => { 50 ); experiments - .setup(exp => exp.inExperiment(ShowExtensionSurveyPrompt.enabled)) + .setup((exp) => exp.inExperiment(ShowExtensionSurveyPrompt.enabled)) .returns(() => true) .verifiable(TypeMoq.Times.once()); experiments - .setup(exp => exp.sendTelemetryIfInExperiment(TypeMoq.It.isAny())) + .setup((exp) => exp.sendTelemetryIfInExperiment(TypeMoq.It.isAny())) .returns(() => undefined) .verifiable(TypeMoq.Times.never()); await extensionSurveyPrompt.activate(); diff --git a/src/test/activation/languageServer/analysisOptions.unit.test.ts b/src/test/activation/languageServer/analysisOptions.unit.test.ts index 774abad42111..0513157e0453 100644 --- a/src/test/activation/languageServer/analysisOptions.unit.test.ts +++ b/src/test/activation/languageServer/analysisOptions.unit.test.ts @@ -67,7 +67,7 @@ suite('Language Server - Analysis Options', () => { workspace = mock(WorkspaceService); outputChannel = typemoq.Mock.ofType().object; lsOutputChannel = typemoq.Mock.ofType(); - lsOutputChannel.setup(l => l.channel).returns(() => outputChannel); + lsOutputChannel.setup((l) => l.channel).returns(() => outputChannel); pathUtils = mock(PathUtils); lsFolderService = mock(DotNetLanguageServerFolderService); analysisOptions = new TestClass( @@ -91,8 +91,8 @@ suite('Language Server - Analysis Options', () => { verify(workspace.onDidChangeConfiguration).once(); verify(envVarsProvider.onDidEnvironmentVariablesChange).once(); - disposable1.setup(d => d.dispose()).verifiable(typemoq.Times.once()); - disposable3.setup(d => d.dispose()).verifiable(typemoq.Times.once()); + disposable1.setup((d) => d.dispose()).verifiable(typemoq.Times.once()); + disposable3.setup((d) => d.dispose()).verifiable(typemoq.Times.once()); analysisOptions.dispose(); @@ -103,7 +103,7 @@ suite('Language Server - Analysis Options', () => { const disposable1 = typemoq.Mock.ofType(); const disposable3 = typemoq.Mock.ofType(); let configChangedHandler!: Function; - when(workspace.onDidChangeConfiguration).thenReturn(cb => { + when(workspace.onDidChangeConfiguration).thenReturn((cb) => { configChangedHandler = cb; return disposable1.object; }); @@ -174,11 +174,11 @@ suite('Language Server - Analysis Options', () => { const disposable3 = typemoq.Mock.ofType(); let configChangedHandler!: Function; let envVarChangedHandler!: Function; - when(workspace.onDidChangeConfiguration).thenReturn(cb => { + when(workspace.onDidChangeConfiguration).thenReturn((cb) => { configChangedHandler = cb; return disposable1.object; }); - when(envVarsProvider.onDidEnvironmentVariablesChange).thenReturn(cb => { + when(envVarsProvider.onDidEnvironmentVariablesChange).thenReturn((cb) => { envVarChangedHandler = cb; return disposable3.object; }); @@ -192,7 +192,7 @@ suite('Language Server - Analysis Options', () => { for (let i = 0; i < 100; i += 1) { const event = typemoq.Mock.ofType(); event - .setup(e => e.affectsConfiguration(typemoq.It.isValue('python'), typemoq.It.isValue(uri))) + .setup((e) => e.affectsConfiguration(typemoq.It.isValue('python'), typemoq.It.isValue(uri))) .returns(() => true) .verifiable(typemoq.Times.once()); configChangedHandler.call(analysisOptions, event.object); diff --git a/src/test/activation/languageServer/downloadChannelRules.unit.test.ts b/src/test/activation/languageServer/downloadChannelRules.unit.test.ts index 2a708b7fa50f..656f19c75f70 100644 --- a/src/test/activation/languageServer/downloadChannelRules.unit.test.ts +++ b/src/test/activation/languageServer/downloadChannelRules.unit.test.ts @@ -16,7 +16,7 @@ import { IPersistentState, IPersistentStateFactory } from '../../../client/commo import { IServiceContainer } from '../../../client/ioc/types'; suite('Language Server Download Channel Rules', () => { - [undefined, path.join('a', 'b')].forEach(currentFolderPath => { + [undefined, path.join('a', 'b')].forEach((currentFolderPath) => { const currentFolder = currentFolderPath ? { path: currentFolderPath, version: new SemVer('0.0.0') } : undefined; const testSuffix = ` (${currentFolderPath ? 'with' : 'without'} an existing Language Server Folder`; @@ -44,19 +44,19 @@ suite('Language Server Download Channel Rules', () => { stateFactory = typeMoq.Mock.ofType(); state = typeMoq.Mock.ofType>(); stateFactory - .setup(s => + .setup((s) => s.createGlobalPersistentState(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny()) ) .returns(() => state.object) .verifiable(typeMoq.Times.once()); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(IPersistentStateFactory))) + .setup((c) => c.get(typeMoq.It.isValue(IPersistentStateFactory))) .returns(() => stateFactory.object); }); function setupStateValue(value: boolean) { state - .setup(s => s.value) + .setup((s) => s.value) .returns(() => value) .verifiable(typeMoq.Times.atLeastOnce()); } diff --git a/src/test/activation/languageServer/downloader.unit.test.ts b/src/test/activation/languageServer/downloader.unit.test.ts index a294b9535d22..19f00f1dc689 100644 --- a/src/test/activation/languageServer/downloader.unit.test.ts +++ b/src/test/activation/languageServer/downloader.unit.test.ts @@ -42,7 +42,7 @@ suite('Language Server Activation - Downloader', () => { setup(() => { outputChannel = mock(MockOutputChannel); lsOutputChannel = TypeMoq.Mock.ofType(); - lsOutputChannel.setup(l => l.channel).returns(() => instance(outputChannel)); + lsOutputChannel.setup((l) => l.channel).returns(() => instance(outputChannel)); folderService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); workspaceService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); resource = Uri.file(__dirname); @@ -59,17 +59,17 @@ suite('Language Server Activation - Downloader', () => { test('Get download info - HTTPS with resource', async () => { const cfg = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - cfg.setup(c => c.get('proxyStrictSSL', true)) + cfg.setup((c) => c.get('proxyStrictSSL', true)) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.getConfiguration(TypeMoq.It.isValue('http'), TypeMoq.It.isValue(resource))) + .setup((w) => w.getConfiguration(TypeMoq.It.isValue('http'), TypeMoq.It.isValue(resource))) .returns(() => cfg.object) .verifiable(TypeMoq.Times.once()); const pkg = makePkgInfo('ls', 'https://a.b.com/x/y/z/ls.nupkg'); folderService - .setup(f => f.getLatestLanguageServerVersion(resource)) + .setup((f) => f.getLatestLanguageServerVersion(resource)) .returns(() => Promise.resolve(pkg)) .verifiable(TypeMoq.Times.once()); @@ -84,17 +84,17 @@ suite('Language Server Activation - Downloader', () => { test('Get download info - HTTPS without resource', async () => { const cfg = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - cfg.setup(c => c.get('proxyStrictSSL', true)) + cfg.setup((c) => c.get('proxyStrictSSL', true)) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.getConfiguration(TypeMoq.It.isValue('http'), undefined)) + .setup((w) => w.getConfiguration(TypeMoq.It.isValue('http'), undefined)) .returns(() => cfg.object) .verifiable(TypeMoq.Times.once()); const pkg = makePkgInfo('ls', 'https://a.b.com/x/y/z/ls.nupkg'); folderService - .setup(f => f.getLatestLanguageServerVersion(undefined)) + .setup((f) => f.getLatestLanguageServerVersion(undefined)) .returns(() => Promise.resolve(pkg)) .verifiable(TypeMoq.Times.once()); @@ -109,17 +109,17 @@ suite('Language Server Activation - Downloader', () => { test('Get download info - HTTPS disabled', async () => { const cfg = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - cfg.setup(c => c.get('proxyStrictSSL', true)) + cfg.setup((c) => c.get('proxyStrictSSL', true)) .returns(() => false) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.getConfiguration(TypeMoq.It.isValue('http'), TypeMoq.It.isValue(resource))) + .setup((w) => w.getConfiguration(TypeMoq.It.isValue('http'), TypeMoq.It.isValue(resource))) .returns(() => cfg.object) .verifiable(TypeMoq.Times.once()); const pkg = makePkgInfo('ls', 'https://a.b.com/x/y/z/ls.nupkg'); folderService - .setup(f => f.getLatestLanguageServerVersion(resource)) + .setup((f) => f.getLatestLanguageServerVersion(resource)) .returns(() => Promise.resolve(pkg)) .verifiable(TypeMoq.Times.once()); @@ -137,7 +137,7 @@ suite('Language Server Activation - Downloader', () => { // tslint:disable-next-line:no-http-string const pkg = makePkgInfo('ls', 'http://a.b.com/x/y/z/ls.nupkg'); folderService - .setup(f => f.getLatestLanguageServerVersion(resource)) + .setup((f) => f.getLatestLanguageServerVersion(resource)) .returns(() => Promise.resolve(pkg)) .verifiable(TypeMoq.Times.once()); @@ -153,7 +153,7 @@ suite('Language Server Activation - Downloader', () => { test('Get download info - bogus URL', async () => { const pkg = makePkgInfo('ls', 'xyz'); folderService - .setup(f => f.getLatestLanguageServerVersion(resource)) + .setup((f) => f.getLatestLanguageServerVersion(resource)) .returns(() => Promise.resolve(pkg)) .verifiable(TypeMoq.Times.once()); @@ -183,7 +183,7 @@ suite('Language Server Activation - Downloader', () => { // tslint:disable-next-line: no-shadowed-variable const workspaceService = mock(WorkspaceService); lsOutputChannelDownload = TypeMoq.Mock.ofType(); - lsOutputChannelDownload.setup(l => l.channel).returns(() => instance(outputChannelDownload)); + lsOutputChannelDownload.setup((l) => l.channel).returns(() => instance(outputChannelDownload)); lsDownloader = new LanguageServerDownloader( lsOutputChannelDownload.object, @@ -273,7 +273,7 @@ suite('Language Server Activation - Downloader', () => { fs = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); platformData = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); lsOutputChannel = TypeMoq.Mock.ofType(); - lsOutputChannel.setup(l => l.channel).returns(() => output.object); + lsOutputChannel.setup((l) => l.channel).returns(() => output.object); languageServerDownloaderTest = new LanguageServerDownloaderTest( lsOutputChannel.object, @@ -296,11 +296,11 @@ suite('Language Server Activation - Downloader', () => { }); test('Display error message if LS downloading fails', async () => { const pkg = makePkgInfo('ls', 'xyz'); - folderService.setup(f => f.getLatestLanguageServerVersion(resource)).returns(() => Promise.resolve(pkg)); - output.setup(o => o.appendLine(LanguageService.downloadFailedOutputMessage())); - output.setup(o => o.appendLine((failure as unknown) as string)); + folderService.setup((f) => f.getLatestLanguageServerVersion(resource)).returns(() => Promise.resolve(pkg)); + output.setup((o) => o.appendLine(LanguageService.downloadFailedOutputMessage())); + output.setup((o) => o.appendLine((failure as unknown) as string)); appShell - .setup(a => a.showErrorMessage(LanguageService.lsFailedToDownload(), Common.openOutputPanel())) + .setup((a) => a.showErrorMessage(LanguageService.lsFailedToDownload(), Common.openOutputPanel())) .returns(() => Promise.resolve(undefined)); let actualFailure: Error | undefined; @@ -319,11 +319,11 @@ suite('Language Server Activation - Downloader', () => { }); test('Display error message if LS extraction fails', async () => { const pkg = makePkgInfo('ls', 'xyz'); - folderService.setup(f => f.getLatestLanguageServerVersion(resource)).returns(() => Promise.resolve(pkg)); - output.setup(o => o.appendLine(LanguageService.extractionFailedOutputMessage())); - output.setup(o => o.appendLine((failure as unknown) as string)); + folderService.setup((f) => f.getLatestLanguageServerVersion(resource)).returns(() => Promise.resolve(pkg)); + output.setup((o) => o.appendLine(LanguageService.extractionFailedOutputMessage())); + output.setup((o) => o.appendLine((failure as unknown) as string)); appShell - .setup(a => a.showErrorMessage(LanguageService.lsFailedToExtract(), Common.openOutputPanel())) + .setup((a) => a.showErrorMessage(LanguageService.lsFailedToExtract(), Common.openOutputPanel())) .returns(() => Promise.resolve(undefined)); let actualFailure: Error | undefined; diff --git a/src/test/activation/languageServer/languageServer.unit.test.ts b/src/test/activation/languageServer/languageServer.unit.test.ts index 83e6ba98fa24..23ae20de07e6 100644 --- a/src/test/activation/languageServer/languageServer.unit.test.ts +++ b/src/test/activation/languageServer/languageServer.unit.test.ts @@ -41,14 +41,14 @@ suite('Language Server - LanguageServer', () => { commandManager = typemoq.Mock.ofType(); commandManager - .setup(c => c.registerCommand(typemoq.It.isAny(), typemoq.It.isAny(), typemoq.It.isAny())) + .setup((c) => c.registerCommand(typemoq.It.isAny(), typemoq.It.isAny(), typemoq.It.isAny())) .returns(() => { return typemoq.Mock.ofType().object; }); server = new LanguageServerTest(instance(clientFactory), instance(testManager), configService.object); }); teardown(() => { - client.setup(c => c.stop()).returns(() => Promise.resolve()); + client.setup((c) => c.stop()).returns(() => Promise.resolve()); server.dispose(); }); test('Loading extension will not throw an error if not activated', () => { @@ -59,36 +59,36 @@ suite('Language Server - LanguageServer', () => { expect(() => server.loadExtension({ a: '2' })).not.throw(); - client.verify(c => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); + client.verify((c) => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); const uri = Uri.file(__filename); const options = typemoq.Mock.ofType().object; const pythonSettings = typemoq.Mock.ofType(); - pythonSettings.setup(p => p.downloadLanguageServer).returns(() => true); - configService.setup(c => c.getSettings(uri)).returns(() => pythonSettings.object); + pythonSettings.setup((p) => p.downloadLanguageServer).returns(() => true); + configService.setup((c) => c.getSettings(uri)).returns(() => pythonSettings.object); const onTelemetryDisposable = typemoq.Mock.ofType(); - client.setup(c => c.onTelemetry(typemoq.It.isAny())).returns(() => onTelemetryDisposable.object); + client.setup((c) => c.onTelemetry(typemoq.It.isAny())).returns(() => onTelemetryDisposable.object); - client.setup(c => (c as any).then).returns(() => undefined); + client.setup((c) => (c as any).then).returns(() => undefined); when(clientFactory.createLanguageClient(uri, undefined, options)).thenResolve(client.object); const startDisposable = typemoq.Mock.ofType(); - client.setup(c => c.stop()).returns(() => Promise.resolve()); + client.setup((c) => c.stop()).returns(() => Promise.resolve()); client - .setup(c => c.start()) + .setup((c) => c.start()) .returns(() => startDisposable.object) .verifiable(typemoq.Times.once()); client - .setup(c => + .setup((c) => c.sendRequest(typemoq.It.isValue('python/loadExtension'), typemoq.It.isValue(loadExtensionArgs)) ) .returns(() => Promise.resolve(undefined) as any); expect(() => server.loadExtension(loadExtensionArgs)).not.throw(); - client.verify(c => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); + client.verify((c) => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); client - .setup(c => c.initializeResult) + .setup((c) => c.initializeResult) .returns(() => false as any) .verifiable(typemoq.Times.once()); @@ -97,17 +97,17 @@ suite('Language Server - LanguageServer', () => { // Even though server has started request should not yet be sent out. // Not until language client has initialized. expect(() => server.loadExtension(loadExtensionArgs)).not.throw(); - client.verify(c => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); + client.verify((c) => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); // // Initialize language client and verify that the request was sent out. client - .setup(c => c.initializeResult) + .setup((c) => c.initializeResult) .returns(() => true as any) .verifiable(typemoq.Times.once()); await sleep(120); verify(testManager.activate(anything())).once(); - client.verify(c => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.atLeast(2)); + client.verify((c) => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.atLeast(2)); }); test('Send telemetry when LS has started and disposes appropriately', async () => { const loadExtensionArgs = { x: 1 }; @@ -115,30 +115,30 @@ suite('Language Server - LanguageServer', () => { const options = typemoq.Mock.ofType().object; const pythonSettings = typemoq.Mock.ofType(); - pythonSettings.setup(p => p.downloadLanguageServer).returns(() => true); - configService.setup(c => c.getSettings(uri)).returns(() => pythonSettings.object); + pythonSettings.setup((p) => p.downloadLanguageServer).returns(() => true); + configService.setup((c) => c.getSettings(uri)).returns(() => pythonSettings.object); const onTelemetryDisposable = typemoq.Mock.ofType(); - client.setup(c => c.onTelemetry(typemoq.It.isAny())).returns(() => onTelemetryDisposable.object); + client.setup((c) => c.onTelemetry(typemoq.It.isAny())).returns(() => onTelemetryDisposable.object); - client.setup(c => (c as any).then).returns(() => undefined); + client.setup((c) => (c as any).then).returns(() => undefined); when(clientFactory.createLanguageClient(uri, undefined, options)).thenResolve(client.object); const startDisposable = typemoq.Mock.ofType(); - client.setup(c => c.stop()).returns(() => Promise.resolve()); + client.setup((c) => c.stop()).returns(() => Promise.resolve()); client - .setup(c => c.start()) + .setup((c) => c.start()) .returns(() => startDisposable.object) .verifiable(typemoq.Times.once()); client - .setup(c => + .setup((c) => c.sendRequest(typemoq.It.isValue('python/loadExtension'), typemoq.It.isValue(loadExtensionArgs)) ) .returns(() => Promise.resolve(undefined) as any); expect(() => server.loadExtension(loadExtensionArgs)).not.throw(); - client.verify(c => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); + client.verify((c) => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); client - .setup(c => c.initializeResult) + .setup((c) => c.initializeResult) .returns(() => false as any) .verifiable(typemoq.Times.once()); @@ -147,57 +147,57 @@ suite('Language Server - LanguageServer', () => { // Even though server has started request should not yet be sent out. // Not until language client has initialized. expect(() => server.loadExtension(loadExtensionArgs)).not.throw(); - client.verify(c => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); + client.verify((c) => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); // // Initialize language client and verify that the request was sent out. client - .setup(c => c.initializeResult) + .setup((c) => c.initializeResult) .returns(() => true as any) .verifiable(typemoq.Times.once()); await sleep(120); verify(testManager.activate(anything())).once(); expect(() => server.loadExtension(loadExtensionArgs)).to.not.throw(); - client.verify(c => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.once()); - client.verify(c => c.stop(), typemoq.Times.never()); + client.verify((c) => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.once()); + client.verify((c) => c.stop(), typemoq.Times.never()); await promise; server.dispose(); - client.verify(c => c.stop(), typemoq.Times.once()); - startDisposable.verify(d => d.dispose(), typemoq.Times.once()); + client.verify((c) => c.stop(), typemoq.Times.once()); + startDisposable.verify((d) => d.dispose(), typemoq.Times.once()); }); test('Ensure Errors raised when starting test manager are not bubbled up', async () => { await server.registerTestServices(); }); test('Register telemetry handler if LS was downloadeded', async () => { - client.verify(c => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); + client.verify((c) => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); const uri = Uri.file(__filename); const options = typemoq.Mock.ofType().object; const pythonSettings = typemoq.Mock.ofType(); pythonSettings - .setup(p => p.downloadLanguageServer) + .setup((p) => p.downloadLanguageServer) .returns(() => true) .verifiable(typemoq.Times.once()); configService - .setup(c => c.getSettings(uri)) + .setup((c) => c.getSettings(uri)) .returns(() => pythonSettings.object) .verifiable(typemoq.Times.once()); const onTelemetryDisposable = typemoq.Mock.ofType(); client - .setup(c => c.onTelemetry(typemoq.It.isAny())) + .setup((c) => c.onTelemetry(typemoq.It.isAny())) .returns(() => onTelemetryDisposable.object) .verifiable(typemoq.Times.once()); - client.setup(c => (c as any).then).returns(() => undefined); + client.setup((c) => (c as any).then).returns(() => undefined); when(clientFactory.createLanguageClient(uri, undefined, options)).thenResolve(client.object); const startDisposable = typemoq.Mock.ofType(); - client.setup(c => c.stop()).returns(() => Promise.resolve()); + client.setup((c) => c.stop()).returns(() => Promise.resolve()); client - .setup(c => c.start()) + .setup((c) => c.start()) .returns(() => startDisposable.object) .verifiable(typemoq.Times.once()); @@ -205,45 +205,45 @@ suite('Language Server - LanguageServer', () => { // Initialize language client and verify that the request was sent out. client - .setup(c => c.initializeResult) + .setup((c) => c.initializeResult) .returns(() => true as any) .verifiable(typemoq.Times.once()); await sleep(120); verify(testManager.activate(anything())).once(); - client.verify(c => c.onTelemetry(typemoq.It.isAny()), typemoq.Times.once()); + client.verify((c) => c.onTelemetry(typemoq.It.isAny()), typemoq.Times.once()); pythonSettings.verifyAll(); configService.verifyAll(); }); test('Do not register telemetry handler if LS was not downloadeded', async () => { - client.verify(c => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); + client.verify((c) => c.sendRequest(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); const uri = Uri.file(__filename); const options = typemoq.Mock.ofType().object; const pythonSettings = typemoq.Mock.ofType(); pythonSettings - .setup(p => p.downloadLanguageServer) + .setup((p) => p.downloadLanguageServer) .returns(() => false) .verifiable(typemoq.Times.once()); configService - .setup(c => c.getSettings(uri)) + .setup((c) => c.getSettings(uri)) .returns(() => pythonSettings.object) .verifiable(typemoq.Times.once()); const onTelemetryDisposable = typemoq.Mock.ofType(); client - .setup(c => c.onTelemetry(typemoq.It.isAny())) + .setup((c) => c.onTelemetry(typemoq.It.isAny())) .returns(() => onTelemetryDisposable.object) .verifiable(typemoq.Times.once()); - client.setup(c => (c as any).then).returns(() => undefined); + client.setup((c) => (c as any).then).returns(() => undefined); when(clientFactory.createLanguageClient(uri, undefined, options)).thenResolve(client.object); const startDisposable = typemoq.Mock.ofType(); - client.setup(c => c.stop()).returns(() => Promise.resolve()); + client.setup((c) => c.stop()).returns(() => Promise.resolve()); client - .setup(c => c.start()) + .setup((c) => c.start()) .returns(() => startDisposable.object) .verifiable(typemoq.Times.once()); @@ -251,14 +251,14 @@ suite('Language Server - LanguageServer', () => { // Initialize language client and verify that the request was sent out. client - .setup(c => c.initializeResult) + .setup((c) => c.initializeResult) .returns(() => true as any) .verifiable(typemoq.Times.once()); await sleep(120); verify(testManager.activate(anything())).once(); - client.verify(c => c.onTelemetry(typemoq.It.isAny()), typemoq.Times.never()); + client.verify((c) => c.onTelemetry(typemoq.It.isAny()), typemoq.Times.never()); pythonSettings.verifyAll(); configService.verifyAll(); }); @@ -268,24 +268,24 @@ suite('Language Server - LanguageServer', () => { const pythonSettings = typemoq.Mock.ofType(); pythonSettings - .setup(p => p.downloadLanguageServer) + .setup((p) => p.downloadLanguageServer) .returns(() => false) .verifiable(typemoq.Times.never()); configService - .setup(c => c.getSettings(uri)) + .setup((c) => c.getSettings(uri)) .returns(() => pythonSettings.object) .verifiable(typemoq.Times.never()); - client.setup(c => (c as any).then).returns(() => undefined); + client.setup((c) => (c as any).then).returns(() => undefined); client - .setup(c => c.initializeResult) + .setup((c) => c.initializeResult) .returns(() => undefined) .verifiable(typemoq.Times.atLeastOnce()); when(clientFactory.createLanguageClient(uri, undefined, options)).thenResolve(client.object); const startDisposable = typemoq.Mock.ofType(); - client.setup(c => c.stop()).returns(() => Promise.resolve()); + client.setup((c) => c.stop()).returns(() => Promise.resolve()); client - .setup(c => c.start()) + .setup((c) => c.start()) .returns(() => startDisposable.object) .verifiable(typemoq.Times.once()); @@ -303,7 +303,7 @@ suite('Language Server - LanguageServer', () => { await promise; verify(testManager.activate(anything())).never(); - client.verify(c => c.onTelemetry(typemoq.It.isAny()), typemoq.Times.never()); + client.verify((c) => c.onTelemetry(typemoq.It.isAny()), typemoq.Times.never()); pythonSettings.verifyAll(); configService.verifyAll(); }); diff --git a/src/test/activation/languageServer/languageServerCompatibilityService.unit.test.ts b/src/test/activation/languageServer/languageServerCompatibilityService.unit.test.ts index 92d1ac7895b7..8ea004ed6225 100644 --- a/src/test/activation/languageServer/languageServerCompatibilityService.unit.test.ts +++ b/src/test/activation/languageServer/languageServerCompatibilityService.unit.test.ts @@ -17,17 +17,17 @@ suite('Language Server Support', () => { service = new LanguageServerCompatibilityService(compatService.object); }); test('Not supported if there are errors ', async () => { - compatService.setup(c => c.isSupported()).returns(() => Promise.reject(new Error('kaboom'))); + compatService.setup((c) => c.isSupported()).returns(() => Promise.reject(new Error('kaboom'))); const supported = await service.isSupported(); expect(supported).to.equal(false, 'incorrect'); }); test('Not supported if there are not errors ', async () => { - compatService.setup(c => c.isSupported()).returns(() => Promise.resolve(false)); + compatService.setup((c) => c.isSupported()).returns(() => Promise.resolve(false)); const supported = await service.isSupported(); expect(supported).to.equal(false, 'incorrect'); }); test('Support if there are not errors ', async () => { - compatService.setup(c => c.isSupported()).returns(() => Promise.resolve(true)); + compatService.setup((c) => c.isSupported()).returns(() => Promise.resolve(true)); const supported = await service.isSupported(); expect(supported).to.equal(true, 'incorrect'); }); diff --git a/src/test/activation/languageServer/languageServerExtension.unit.test.ts b/src/test/activation/languageServer/languageServerExtension.unit.test.ts index 3c7739a690dd..d18bacd66e3d 100644 --- a/src/test/activation/languageServer/languageServerExtension.unit.test.ts +++ b/src/test/activation/languageServer/languageServerExtension.unit.test.ts @@ -44,6 +44,6 @@ suite('Language Server - Language Server Extension', () => { await extension.register(); verify(cmdManager.registerCommand(loadExtensionCommand, anything())).once(); extension.dispose(); - commandRegistrationDisposable.verify(d => d.dispose(), typemoq.Times.once()); + commandRegistrationDisposable.verify((d) => d.dispose(), typemoq.Times.once()); }); }); diff --git a/src/test/activation/languageServer/languageServerFolderService.unit.test.ts b/src/test/activation/languageServer/languageServerFolderService.unit.test.ts index b088c23da712..a653bc4e58ec 100644 --- a/src/test/activation/languageServer/languageServerFolderService.unit.test.ts +++ b/src/test/activation/languageServer/languageServerFolderService.unit.test.ts @@ -1,270 +1,270 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { assert, expect } from 'chai'; -import * as semver from 'semver'; -import * as sinon from 'sinon'; -import * as TypeMoq from 'typemoq'; -import { Uri } from 'vscode'; -import { DotNetLanguageServerFolderService } from '../../../client/activation/languageServer/languageServerFolderService'; -import { IDownloadChannelRule, ILanguageServerPackageService } from '../../../client/activation/types'; -import { IFileSystem } from '../../../client/common/platform/types'; -import { IConfigurationService } from '../../../client/common/types'; -import { IServiceContainer } from '../../../client/ioc/types'; - -// tslint:disable:max-func-body-length - -suite('Language Server Folder Service', () => { - let serviceContainer: TypeMoq.IMock; - let languageServerFolderService: DotNetLanguageServerFolderService; - const resource = Uri.parse('a'); - - // tslint:disable-next-line:max-func-body-length - suite('Method getLanguageServerFolderName()', async () => { - // tslint:disable-next-line: no-any - let shouldLookForNewLS: sinon.SinonStub; - // tslint:disable-next-line: no-any - let getCurrentLanguageServerDirectory: sinon.SinonStub; - const currentLSDirectory = { - path: 'path/to/currentLSDirectoryName', - version: new semver.SemVer('1.2.3') - }; - let languageServerPackageService: TypeMoq.IMock; - setup(() => { - languageServerPackageService = TypeMoq.Mock.ofType(); - serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer - .setup(s => s.get(ILanguageServerPackageService)) - .returns(() => languageServerPackageService.object); - }); - teardown(() => { - sinon.restore(); - }); - - test('Returns current Language server directory name if rule says we should not look for new LS', async () => { - shouldLookForNewLS = sinon.stub( - DotNetLanguageServerFolderService.prototype, - 'shouldLookForNewLanguageServer' - ); - shouldLookForNewLS.resolves(false); - getCurrentLanguageServerDirectory = sinon.stub( - DotNetLanguageServerFolderService.prototype, - 'getCurrentLanguageServerDirectory' - ); - getCurrentLanguageServerDirectory.resolves(currentLSDirectory); - languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); - const lsFolderName = await languageServerFolderService.getLanguageServerFolderName(resource); - expect(lsFolderName).to.equal('currentLSDirectoryName'); - }); - - test('Returns current Language server directory name if fetching latest LS version returns undefined', async () => { - shouldLookForNewLS = sinon.stub( - DotNetLanguageServerFolderService.prototype, - 'shouldLookForNewLanguageServer' - ); - shouldLookForNewLS.resolves(true); - languageServerPackageService - .setup(l => l.getLatestNugetPackageVersion(resource)) - // tslint:disable-next-line: no-any - .returns(() => Promise.resolve(undefined) as any); - getCurrentLanguageServerDirectory = sinon.stub( - DotNetLanguageServerFolderService.prototype, - 'getCurrentLanguageServerDirectory' - ); - getCurrentLanguageServerDirectory.resolves(currentLSDirectory); - languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); - const lsFolderName = await languageServerFolderService.getLanguageServerFolderName(resource); - expect(lsFolderName).to.equal('currentLSDirectoryName'); - }); - - test('Returns current Language server directory name if fetched latest LS version is less than the current LS version', async () => { - shouldLookForNewLS = sinon.stub( - DotNetLanguageServerFolderService.prototype, - 'shouldLookForNewLanguageServer' - ); - shouldLookForNewLS.resolves(true); - const nugetPackage = { - package: 'packageName', - version: new semver.SemVer('1.1.3'), - uri: 'nugetUri' - }; - languageServerPackageService - .setup(l => l.getLatestNugetPackageVersion(resource)) - .returns(() => Promise.resolve(nugetPackage)); - getCurrentLanguageServerDirectory = sinon.stub( - DotNetLanguageServerFolderService.prototype, - 'getCurrentLanguageServerDirectory' - ); - getCurrentLanguageServerDirectory.resolves(currentLSDirectory); - languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); - const lsFolderName = await languageServerFolderService.getLanguageServerFolderName(resource); - expect(lsFolderName).to.equal('currentLSDirectoryName'); - }); - - test('Returns expected Language server directory name otherwise', async () => { - shouldLookForNewLS = sinon.stub( - DotNetLanguageServerFolderService.prototype, - 'shouldLookForNewLanguageServer' - ); - shouldLookForNewLS.resolves(true); - const nugetPackage = { - package: 'packageName', - version: new semver.SemVer('1.3.2'), - uri: 'nugetUri' - }; - languageServerPackageService - .setup(l => l.getLatestNugetPackageVersion(resource, '0.0.0')) - .returns(() => Promise.resolve(nugetPackage)); - getCurrentLanguageServerDirectory = sinon.stub( - DotNetLanguageServerFolderService.prototype, - 'getCurrentLanguageServerDirectory' - ); - getCurrentLanguageServerDirectory.resolves(currentLSDirectory); - languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); - const lsFolderName = await languageServerFolderService.getLanguageServerFolderName(resource); - expect(lsFolderName).to.equal('languageServer.1.3.2'); - }); - }); - - suite('Method shouldLookForNewLanguageServer()', async () => { - let configurationService: TypeMoq.IMock; - let lsPackageService: TypeMoq.IMock; - let downloadChannelRule: TypeMoq.IMock; - const currentLSDirectory = { - path: 'path/to/currentLSDirectoryName', - version: new semver.SemVer('1.2.3') - }; - setup(() => { - configurationService = TypeMoq.Mock.ofType(); - lsPackageService = TypeMoq.Mock.ofType(); - downloadChannelRule = TypeMoq.Mock.ofType(); - serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer - .setup(s => s.get(IConfigurationService)) - .returns(() => configurationService.object); - serviceContainer - .setup(s => s.get(IDownloadChannelRule, 'beta')) - .returns(() => downloadChannelRule.object); - serviceContainer - .setup(s => s.get(ILanguageServerPackageService)) - .returns(() => lsPackageService.object); - lsPackageService.setup(l => l.getLanguageServerDownloadChannel()).returns(() => 'beta'); - languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); - }); - - test('Returns false if current folder is provided and setting `python.downloadLanguageServer` is set to false', async () => { - const settings = { - downloadLanguageServer: false, - autoUpdateLanguageServer: true - }; - configurationService - .setup(c => c.getSettings()) - // tslint:disable-next-line: no-any - .returns(() => settings as any); - const result = await languageServerFolderService.shouldLookForNewLanguageServer(currentLSDirectory); - expect(result).to.equal(false, 'Should be false'); - }); - - test('Returns false if current folder is provided and setting `python.autoUpdateLanguageServer` is set to false', async () => { - const settings = { - downloadLanguageServer: true, - autoUpdateLanguageServer: false - }; - configurationService - .setup(c => c.getSettings()) - // tslint:disable-next-line: no-any - .returns(() => settings as any); - const result = await languageServerFolderService.shouldLookForNewLanguageServer(currentLSDirectory); - expect(result).to.equal(false, 'Should be false'); - }); - - test('Returns whatever the rule to infer LS returns otherwise', async () => { - const settings = { - downloadLanguageServer: true, - autoUpdateLanguageServer: false - }; - configurationService - .setup(c => c.getSettings()) - // tslint:disable-next-line: no-any - .returns(() => settings as any); - downloadChannelRule - .setup(d => d.shouldLookForNewLanguageServer(undefined)) - .returns(() => Promise.resolve(true)) - .verifiable(TypeMoq.Times.once()); - const result = await languageServerFolderService.shouldLookForNewLanguageServer(); - expect(result).to.equal(true, 'Should be true'); - downloadChannelRule.verifyAll(); - }); - }); - - suite('Method getCurrentLanguageServerDirectory()', async () => { - let configurationService: TypeMoq.IMock; - let fs: TypeMoq.IMock; - setup(() => { - configurationService = TypeMoq.Mock.ofType(); - fs = TypeMoq.Mock.ofType(); - serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer - .setup(s => s.get(IConfigurationService)) - .returns(() => configurationService.object); - serviceContainer.setup(s => s.get(IFileSystem)).returns(() => fs.object); - languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); - }); - - test('Returns the expected LS directory if setting `python.downloadLanguageServer` is set to false', async () => { - const settings = { - downloadLanguageServer: false - }; - const expectedLSDirectory = { - path: 'languageServer', - version: new semver.SemVer('0.0.0') - }; - configurationService - .setup(c => c.getSettings()) - // tslint:disable-next-line: no-any - .returns(() => settings as any); - const result = await languageServerFolderService.getCurrentLanguageServerDirectory(); - assert.deepEqual(result, expectedLSDirectory); - }); - - test('Returns `undefined` if no LS directory exists', async () => { - const settings = { - downloadLanguageServer: true - }; - const directories = ['path/to/directory1', 'path/to/directory2']; - configurationService - .setup(c => c.getSettings()) - // tslint:disable-next-line: no-any - .returns(() => settings as any); - fs.setup(f => f.getSubDirectories(TypeMoq.It.isAny())).returns(() => Promise.resolve(directories)); - const result = await languageServerFolderService.getCurrentLanguageServerDirectory(); - assert.deepEqual(result, undefined); - }); - - test('Returns the LS directory with highest version if multiple LS directories exists', async () => { - const settings = { - downloadLanguageServer: true - }; - const directories = [ - 'path/to/languageServer', - 'path/to/languageServer.0.9.3', - 'path/to/languageServer.1.0.7', - 'path/to/languageServer.1.2.3', - 'path/to/languageServer.1.2.3.5' - ]; - const expectedLSDirectory = { - path: 'path/to/languageServer.1.2.3', - version: semver.parse('1.2.3', true)! - }; - configurationService - .setup(c => c.getSettings()) - // tslint:disable-next-line: no-any - .returns(() => settings as any); - fs.setup(f => f.getSubDirectories(TypeMoq.It.isAny())).returns(() => Promise.resolve(directories)); - const result = await languageServerFolderService.getCurrentLanguageServerDirectory(); - assert.deepEqual(result, expectedLSDirectory); - }); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { assert, expect } from 'chai'; +import * as semver from 'semver'; +import * as sinon from 'sinon'; +import * as TypeMoq from 'typemoq'; +import { Uri } from 'vscode'; +import { DotNetLanguageServerFolderService } from '../../../client/activation/languageServer/languageServerFolderService'; +import { IDownloadChannelRule, ILanguageServerPackageService } from '../../../client/activation/types'; +import { IFileSystem } from '../../../client/common/platform/types'; +import { IConfigurationService } from '../../../client/common/types'; +import { IServiceContainer } from '../../../client/ioc/types'; + +// tslint:disable:max-func-body-length + +suite('Language Server Folder Service', () => { + let serviceContainer: TypeMoq.IMock; + let languageServerFolderService: DotNetLanguageServerFolderService; + const resource = Uri.parse('a'); + + // tslint:disable-next-line:max-func-body-length + suite('Method getLanguageServerFolderName()', async () => { + // tslint:disable-next-line: no-any + let shouldLookForNewLS: sinon.SinonStub; + // tslint:disable-next-line: no-any + let getCurrentLanguageServerDirectory: sinon.SinonStub; + const currentLSDirectory = { + path: 'path/to/currentLSDirectoryName', + version: new semver.SemVer('1.2.3') + }; + let languageServerPackageService: TypeMoq.IMock; + setup(() => { + languageServerPackageService = TypeMoq.Mock.ofType(); + serviceContainer = TypeMoq.Mock.ofType(); + serviceContainer + .setup((s) => s.get(ILanguageServerPackageService)) + .returns(() => languageServerPackageService.object); + }); + teardown(() => { + sinon.restore(); + }); + + test('Returns current Language server directory name if rule says we should not look for new LS', async () => { + shouldLookForNewLS = sinon.stub( + DotNetLanguageServerFolderService.prototype, + 'shouldLookForNewLanguageServer' + ); + shouldLookForNewLS.resolves(false); + getCurrentLanguageServerDirectory = sinon.stub( + DotNetLanguageServerFolderService.prototype, + 'getCurrentLanguageServerDirectory' + ); + getCurrentLanguageServerDirectory.resolves(currentLSDirectory); + languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); + const lsFolderName = await languageServerFolderService.getLanguageServerFolderName(resource); + expect(lsFolderName).to.equal('currentLSDirectoryName'); + }); + + test('Returns current Language server directory name if fetching latest LS version returns undefined', async () => { + shouldLookForNewLS = sinon.stub( + DotNetLanguageServerFolderService.prototype, + 'shouldLookForNewLanguageServer' + ); + shouldLookForNewLS.resolves(true); + languageServerPackageService + .setup((l) => l.getLatestNugetPackageVersion(resource)) + // tslint:disable-next-line: no-any + .returns(() => Promise.resolve(undefined) as any); + getCurrentLanguageServerDirectory = sinon.stub( + DotNetLanguageServerFolderService.prototype, + 'getCurrentLanguageServerDirectory' + ); + getCurrentLanguageServerDirectory.resolves(currentLSDirectory); + languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); + const lsFolderName = await languageServerFolderService.getLanguageServerFolderName(resource); + expect(lsFolderName).to.equal('currentLSDirectoryName'); + }); + + test('Returns current Language server directory name if fetched latest LS version is less than the current LS version', async () => { + shouldLookForNewLS = sinon.stub( + DotNetLanguageServerFolderService.prototype, + 'shouldLookForNewLanguageServer' + ); + shouldLookForNewLS.resolves(true); + const nugetPackage = { + package: 'packageName', + version: new semver.SemVer('1.1.3'), + uri: 'nugetUri' + }; + languageServerPackageService + .setup((l) => l.getLatestNugetPackageVersion(resource)) + .returns(() => Promise.resolve(nugetPackage)); + getCurrentLanguageServerDirectory = sinon.stub( + DotNetLanguageServerFolderService.prototype, + 'getCurrentLanguageServerDirectory' + ); + getCurrentLanguageServerDirectory.resolves(currentLSDirectory); + languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); + const lsFolderName = await languageServerFolderService.getLanguageServerFolderName(resource); + expect(lsFolderName).to.equal('currentLSDirectoryName'); + }); + + test('Returns expected Language server directory name otherwise', async () => { + shouldLookForNewLS = sinon.stub( + DotNetLanguageServerFolderService.prototype, + 'shouldLookForNewLanguageServer' + ); + shouldLookForNewLS.resolves(true); + const nugetPackage = { + package: 'packageName', + version: new semver.SemVer('1.3.2'), + uri: 'nugetUri' + }; + languageServerPackageService + .setup((l) => l.getLatestNugetPackageVersion(resource, '0.0.0')) + .returns(() => Promise.resolve(nugetPackage)); + getCurrentLanguageServerDirectory = sinon.stub( + DotNetLanguageServerFolderService.prototype, + 'getCurrentLanguageServerDirectory' + ); + getCurrentLanguageServerDirectory.resolves(currentLSDirectory); + languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); + const lsFolderName = await languageServerFolderService.getLanguageServerFolderName(resource); + expect(lsFolderName).to.equal('languageServer.1.3.2'); + }); + }); + + suite('Method shouldLookForNewLanguageServer()', async () => { + let configurationService: TypeMoq.IMock; + let lsPackageService: TypeMoq.IMock; + let downloadChannelRule: TypeMoq.IMock; + const currentLSDirectory = { + path: 'path/to/currentLSDirectoryName', + version: new semver.SemVer('1.2.3') + }; + setup(() => { + configurationService = TypeMoq.Mock.ofType(); + lsPackageService = TypeMoq.Mock.ofType(); + downloadChannelRule = TypeMoq.Mock.ofType(); + serviceContainer = TypeMoq.Mock.ofType(); + serviceContainer + .setup((s) => s.get(IConfigurationService)) + .returns(() => configurationService.object); + serviceContainer + .setup((s) => s.get(IDownloadChannelRule, 'beta')) + .returns(() => downloadChannelRule.object); + serviceContainer + .setup((s) => s.get(ILanguageServerPackageService)) + .returns(() => lsPackageService.object); + lsPackageService.setup((l) => l.getLanguageServerDownloadChannel()).returns(() => 'beta'); + languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); + }); + + test('Returns false if current folder is provided and setting `python.downloadLanguageServer` is set to false', async () => { + const settings = { + downloadLanguageServer: false, + autoUpdateLanguageServer: true + }; + configurationService + .setup((c) => c.getSettings()) + // tslint:disable-next-line: no-any + .returns(() => settings as any); + const result = await languageServerFolderService.shouldLookForNewLanguageServer(currentLSDirectory); + expect(result).to.equal(false, 'Should be false'); + }); + + test('Returns false if current folder is provided and setting `python.autoUpdateLanguageServer` is set to false', async () => { + const settings = { + downloadLanguageServer: true, + autoUpdateLanguageServer: false + }; + configurationService + .setup((c) => c.getSettings()) + // tslint:disable-next-line: no-any + .returns(() => settings as any); + const result = await languageServerFolderService.shouldLookForNewLanguageServer(currentLSDirectory); + expect(result).to.equal(false, 'Should be false'); + }); + + test('Returns whatever the rule to infer LS returns otherwise', async () => { + const settings = { + downloadLanguageServer: true, + autoUpdateLanguageServer: false + }; + configurationService + .setup((c) => c.getSettings()) + // tslint:disable-next-line: no-any + .returns(() => settings as any); + downloadChannelRule + .setup((d) => d.shouldLookForNewLanguageServer(undefined)) + .returns(() => Promise.resolve(true)) + .verifiable(TypeMoq.Times.once()); + const result = await languageServerFolderService.shouldLookForNewLanguageServer(); + expect(result).to.equal(true, 'Should be true'); + downloadChannelRule.verifyAll(); + }); + }); + + suite('Method getCurrentLanguageServerDirectory()', async () => { + let configurationService: TypeMoq.IMock; + let fs: TypeMoq.IMock; + setup(() => { + configurationService = TypeMoq.Mock.ofType(); + fs = TypeMoq.Mock.ofType(); + serviceContainer = TypeMoq.Mock.ofType(); + serviceContainer + .setup((s) => s.get(IConfigurationService)) + .returns(() => configurationService.object); + serviceContainer.setup((s) => s.get(IFileSystem)).returns(() => fs.object); + languageServerFolderService = new DotNetLanguageServerFolderService(serviceContainer.object); + }); + + test('Returns the expected LS directory if setting `python.downloadLanguageServer` is set to false', async () => { + const settings = { + downloadLanguageServer: false + }; + const expectedLSDirectory = { + path: 'languageServer', + version: new semver.SemVer('0.0.0') + }; + configurationService + .setup((c) => c.getSettings()) + // tslint:disable-next-line: no-any + .returns(() => settings as any); + const result = await languageServerFolderService.getCurrentLanguageServerDirectory(); + assert.deepEqual(result, expectedLSDirectory); + }); + + test('Returns `undefined` if no LS directory exists', async () => { + const settings = { + downloadLanguageServer: true + }; + const directories = ['path/to/directory1', 'path/to/directory2']; + configurationService + .setup((c) => c.getSettings()) + // tslint:disable-next-line: no-any + .returns(() => settings as any); + fs.setup((f) => f.getSubDirectories(TypeMoq.It.isAny())).returns(() => Promise.resolve(directories)); + const result = await languageServerFolderService.getCurrentLanguageServerDirectory(); + assert.deepEqual(result, undefined); + }); + + test('Returns the LS directory with highest version if multiple LS directories exists', async () => { + const settings = { + downloadLanguageServer: true + }; + const directories = [ + 'path/to/languageServer', + 'path/to/languageServer.0.9.3', + 'path/to/languageServer.1.0.7', + 'path/to/languageServer.1.2.3', + 'path/to/languageServer.1.2.3.5' + ]; + const expectedLSDirectory = { + path: 'path/to/languageServer.1.2.3', + version: semver.parse('1.2.3', true)! + }; + configurationService + .setup((c) => c.getSettings()) + // tslint:disable-next-line: no-any + .returns(() => settings as any); + fs.setup((f) => f.getSubDirectories(TypeMoq.It.isAny())).returns(() => Promise.resolve(directories)); + const result = await languageServerFolderService.getCurrentLanguageServerDirectory(); + assert.deepEqual(result, expectedLSDirectory); + }); + }); +}); diff --git a/src/test/activation/languageServer/languageServerPackageService.test.ts b/src/test/activation/languageServer/languageServerPackageService.test.ts index abf65beebe48..9defd3ef9b03 100644 --- a/src/test/activation/languageServer/languageServerPackageService.test.ts +++ b/src/test/activation/languageServer/languageServerPackageService.test.ts @@ -27,14 +27,14 @@ suite('Language Server Package Service', () => { }); test('Ensure new Major versions of Language Server is accounted for (azure blob)', async () => { const nugetService = new NugetService(); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(INugetService))).returns(() => nugetService); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(INugetService))).returns(() => nugetService); const platformService = new PlatformService(); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(IPlatformService))).returns(() => platformService); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(IPlatformService))).returns(() => platformService); const workspace = typeMoq.Mock.ofType(); const cfg = typeMoq.Mock.ofType(); - cfg.setup(c => c.get('proxyStrictSSL', true)).returns(() => true); - workspace.setup(w => w.getConfiguration('http', undefined)).returns(() => cfg.object); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(IWorkspaceService))).returns(() => workspace.object); + cfg.setup((c) => c.get('proxyStrictSSL', true)).returns(() => true); + workspace.setup((w) => w.getConfiguration('http', undefined)).returns(() => cfg.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(IWorkspaceService))).returns(() => workspace.object); const defaultStorageChannel = 'python-language-server-daily'; const nugetRepo = new AzureBlobStoreNugetRepository( serviceContainer.object, @@ -42,10 +42,10 @@ suite('Language Server Package Service', () => { defaultStorageChannel, azureCDNBlobStorageAccount ); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(INugetRepository))).returns(() => nugetRepo); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(INugetRepository))).returns(() => nugetRepo); const appEnv = typeMoq.Mock.ofType(); const packageJson = { languageServerVersion: '0.0.1' }; - appEnv.setup(e => e.packageJson).returns(() => packageJson); + appEnv.setup((e) => e.packageJson).returns(() => packageJson); const platform = typeMoq.Mock.ofType(); const lsPackageService = new DotNetLanguageServerPackageService( serviceContainer.object, @@ -56,7 +56,7 @@ suite('Language Server Package Service', () => { const packages = await nugetRepo.getPackages(packageName, undefined); const latestReleases = packages - .filter(item => nugetService.isReleaseVersion(item.version)) + .filter((item) => nugetService.isReleaseVersion(item.version)) .sort((a, b) => a.version.compare(b.version)); const latestRelease = latestReleases[latestReleases.length - 1]; diff --git a/src/test/activation/languageServer/languageServerPackageService.unit.test.ts b/src/test/activation/languageServer/languageServerPackageService.unit.test.ts index 695bdc28b350..ab3055be776c 100644 --- a/src/test/activation/languageServer/languageServerPackageService.unit.test.ts +++ b/src/test/activation/languageServer/languageServerPackageService.unit.test.ts @@ -1,263 +1,263 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -// tslint:disable:no-any no-invalid-this max-func-body-length - -import { expect } from 'chai'; -import { SemVer } from 'semver'; -import * as typeMoq from 'typemoq'; -import { - azureCDNBlobStorageAccount, - LanguageServerDownloadChannel -} from '../../../client/activation/common/packageRepository'; -import { DotNetLanguageServerPackageService } from '../../../client/activation/languageServer/languageServerPackageService'; -import { PlatformName } from '../../../client/activation/types'; -import { IApplicationEnvironment } from '../../../client/common/application/types'; -import { NugetService } from '../../../client/common/nuget/nugetService'; -import { INugetRepository, INugetService, NugetPackage } from '../../../client/common/nuget/types'; -import { IPlatformService } from '../../../client/common/platform/types'; -import { IConfigurationService } from '../../../client/common/types'; -import { OSType } from '../../../client/common/utils/platform'; -import { IServiceContainer } from '../../../client/ioc/types'; - -const downloadBaseFileName = 'Python-Language-Server'; - -suite('Language Server - Package Service', () => { - let serviceContainer: typeMoq.IMock; - let platform: typeMoq.IMock; - let lsPackageService: DotNetLanguageServerPackageService; - let appVersion: typeMoq.IMock; - setup(() => { - serviceContainer = typeMoq.Mock.ofType(); - platform = typeMoq.Mock.ofType(); - appVersion = typeMoq.Mock.ofType(); - lsPackageService = new DotNetLanguageServerPackageService( - serviceContainer.object, - appVersion.object, - platform.object - ); - lsPackageService.getLanguageServerDownloadChannel = () => 'stable'; - }); - function setMinVersionOfLs(version: string) { - const packageJson = { languageServerVersion: version }; - appVersion.setup(e => e.packageJson).returns(() => packageJson); - } - [true, false].forEach(is64Bit => { - const bitness = is64Bit ? '64bit' : '32bit'; - test(`Get Package name for Windows (${bitness})`, async () => { - platform.setup(p => p.osType).returns(() => OSType.Windows); - platform.setup(p => p.is64bit).returns(() => is64Bit); - const expectedName = is64Bit - ? `${downloadBaseFileName}-${PlatformName.Windows64Bit}` - : `${downloadBaseFileName}-${PlatformName.Windows32Bit}`; - - const name = lsPackageService.getNugetPackageName(); - - platform.verifyAll(); - expect(name).to.be.equal(expectedName); - }); - test(`Get Package name for Mac (${bitness})`, async () => { - platform.setup(p => p.osType).returns(() => OSType.OSX); - const expectedName = `${downloadBaseFileName}-${PlatformName.Mac64Bit}`; - - const name = lsPackageService.getNugetPackageName(); - - platform.verifyAll(); - expect(name).to.be.equal(expectedName); - }); - test(`Get Package name for Linux (${bitness})`, async () => { - platform.setup(p => p.osType).returns(() => OSType.Linux); - const expectedName = `${downloadBaseFileName}-${PlatformName.Linux64Bit}`; - - const name = lsPackageService.getNugetPackageName(); - - platform.verifyAll(); - expect(name).to.be.equal(expectedName); - }); - }); - test('Get latest nuget package version', async () => { - const packageName = 'packageName'; - lsPackageService.getNugetPackageName = () => packageName; - lsPackageService.maxMajorVersion = 3; - setMinVersionOfLs('0.0.1'); - const packages: NugetPackage[] = [ - { package: '', uri: '', version: new SemVer('1.1.1') }, - { package: '', uri: '', version: new SemVer('3.4.1') }, - { package: '', uri: '', version: new SemVer('3.1.1') }, - { package: '', uri: '', version: new SemVer('2.1.1') } - ]; - const expectedPackage = packages[1]; - const repo = typeMoq.Mock.ofType(); - const nuget = typeMoq.Mock.ofType(); - serviceContainer - .setup(c => c.get(typeMoq.It.isValue(INugetRepository), typeMoq.It.isAny())) - .returns(() => repo.object); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(INugetService))).returns(() => nuget.object); - - repo.setup(n => n.getPackages(typeMoq.It.isValue(packageName), typeMoq.It.isAny())) - .returns(() => Promise.resolve(packages)) - .verifiable(typeMoq.Times.once()); - nuget - .setup(n => n.isReleaseVersion(typeMoq.It.isAny())) - .returns(() => true) - .verifiable(typeMoq.Times.atLeastOnce()); - - const info = await lsPackageService.getLatestNugetPackageVersion(undefined); - - repo.verifyAll(); - nuget.verifyAll(); - expect(info).to.deep.equal(expectedPackage); - }); - test('Get latest nuget package version (excluding non-release)', async () => { - setMinVersionOfLs('0.0.1'); - const packageName = 'packageName'; - lsPackageService.getNugetPackageName = () => packageName; - lsPackageService.maxMajorVersion = 1; - const packages: NugetPackage[] = [ - { package: '', uri: '', version: new SemVer('1.1.1') }, - { package: '', uri: '', version: new SemVer('1.3.1-alpha') }, - { package: '', uri: '', version: new SemVer('1.4.1-preview') }, - { package: '', uri: '', version: new SemVer('1.2.1-internal') } - ]; - const expectedPackage = packages[0]; - const repo = typeMoq.Mock.ofType(); - const nuget = new NugetService(); - serviceContainer - .setup(c => c.get(typeMoq.It.isValue(INugetRepository), typeMoq.It.isAny())) - .returns(() => repo.object); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(INugetService))).returns(() => nuget); - - repo.setup(n => n.getPackages(typeMoq.It.isValue(packageName), typeMoq.It.isAny())) - .returns(() => Promise.resolve(packages)) - .verifiable(typeMoq.Times.once()); - - const info = await lsPackageService.getLatestNugetPackageVersion(undefined); - - repo.verifyAll(); - expect(info).to.deep.equal(expectedPackage); - }); - test('Ensure minimum version of package is used', async () => { - const minimumVersion = '0.1.50'; - setMinVersionOfLs(minimumVersion); - const packageName = 'packageName'; - lsPackageService.getNugetPackageName = () => packageName; - lsPackageService.maxMajorVersion = 0; - const packages: NugetPackage[] = [ - { package: '', uri: '', version: new SemVer('0.1.48') }, - { package: '', uri: '', version: new SemVer('0.1.49') } - ]; - const repo = typeMoq.Mock.ofType(); - const nuget = new NugetService(); - serviceContainer - .setup(c => c.get(typeMoq.It.isValue(INugetRepository), typeMoq.It.isAny())) - .returns(() => repo.object); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(INugetService))).returns(() => nuget); - - repo.setup(n => n.getPackages(typeMoq.It.isValue(packageName), typeMoq.It.isAny())) - .returns(() => Promise.resolve(packages)) - .verifiable(typeMoq.Times.once()); - - const info = await lsPackageService.getLatestNugetPackageVersion(undefined, minimumVersion); - - repo.verifyAll(); - const expectedPackage: NugetPackage = { - version: new SemVer(minimumVersion), - package: LanguageServerDownloadChannel.stable, - uri: `${azureCDNBlobStorageAccount}/${LanguageServerDownloadChannel.stable}/${packageName}.${minimumVersion}.nupkg` - }; - expect(info).to.deep.equal(expectedPackage); - }); -}); -suite('Language Server Package Service - getLanguageServerDownloadChannel()', () => { - let serviceContainer: typeMoq.IMock; - let platform: typeMoq.IMock; - let lsPackageService: DotNetLanguageServerPackageService; - let appVersion: typeMoq.IMock; - let configService: typeMoq.IMock; - setup(() => { - serviceContainer = typeMoq.Mock.ofType(); - platform = typeMoq.Mock.ofType(); - appVersion = typeMoq.Mock.ofType(); - configService = typeMoq.Mock.ofType(); - serviceContainer.setup(s => s.get(IConfigurationService)).returns(() => configService.object); - lsPackageService = new DotNetLanguageServerPackageService( - serviceContainer.object, - appVersion.object, - platform.object - ); - lsPackageService.isAlphaVersionOfExtension = () => true; - }); - test("If 'python.analysis.downloadChannel' setting is specified, return the value of the setting", async () => { - const settings = { - analysis: { - downloadChannel: 'someValue' - } - }; - configService.setup(c => c.getSettings()).returns(() => settings as any); - - lsPackageService.isAlphaVersionOfExtension = () => { - throw new Error('Should not be here'); - }; - const downloadChannel = lsPackageService.getLanguageServerDownloadChannel(); - - expect(downloadChannel).to.be.equal('someValue'); - }); - - test("If 'python.analysis.downloadChannel' setting is not specified and insiders channel is 'weekly', return 'beta'", async () => { - const settings = { - analysis: {}, - insidersChannel: 'weekly' - }; - configService.setup(c => c.getSettings()).returns(() => settings as any); - - lsPackageService.isAlphaVersionOfExtension = () => { - throw new Error('Should not be here'); - }; - const downloadChannel = lsPackageService.getLanguageServerDownloadChannel(); - - expect(downloadChannel).to.be.equal('beta'); - }); - - test("If 'python.analysis.downloadChannel' setting is not specified and insiders channel is 'daily', return 'beta'", async () => { - const settings = { - analysis: {}, - insidersChannel: 'daily' - }; - configService.setup(c => c.getSettings()).returns(() => settings as any); - - lsPackageService.isAlphaVersionOfExtension = () => { - throw new Error('Should not be here'); - }; - const downloadChannel = lsPackageService.getLanguageServerDownloadChannel(); - - expect(downloadChannel).to.be.equal('beta'); - }); - - test("If 'python.analysis.downloadChannel' setting is not specified, user is not using insiders, and extension has Alpha version, return 'beta'", async () => { - const settings = { - analysis: {}, - insidersChannel: 'off' - }; - configService.setup(c => c.getSettings()).returns(() => settings as any); - - lsPackageService.isAlphaVersionOfExtension = () => true; - const downloadChannel = lsPackageService.getLanguageServerDownloadChannel(); - - expect(downloadChannel).to.be.equal('beta'); - }); - - test("If 'python.analysis.downloadChannel' setting is not specified, user is not using insiders, and extension does not have Alpha version, return 'stable'", async () => { - const settings = { - analysis: {}, - insidersChannel: 'off' - }; - configService.setup(c => c.getSettings()).returns(() => settings as any); - - lsPackageService.isAlphaVersionOfExtension = () => false; - const downloadChannel = lsPackageService.getLanguageServerDownloadChannel(); - - expect(downloadChannel).to.be.equal('stable'); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// tslint:disable:no-any no-invalid-this max-func-body-length + +import { expect } from 'chai'; +import { SemVer } from 'semver'; +import * as typeMoq from 'typemoq'; +import { + azureCDNBlobStorageAccount, + LanguageServerDownloadChannel +} from '../../../client/activation/common/packageRepository'; +import { DotNetLanguageServerPackageService } from '../../../client/activation/languageServer/languageServerPackageService'; +import { PlatformName } from '../../../client/activation/types'; +import { IApplicationEnvironment } from '../../../client/common/application/types'; +import { NugetService } from '../../../client/common/nuget/nugetService'; +import { INugetRepository, INugetService, NugetPackage } from '../../../client/common/nuget/types'; +import { IPlatformService } from '../../../client/common/platform/types'; +import { IConfigurationService } from '../../../client/common/types'; +import { OSType } from '../../../client/common/utils/platform'; +import { IServiceContainer } from '../../../client/ioc/types'; + +const downloadBaseFileName = 'Python-Language-Server'; + +suite('Language Server - Package Service', () => { + let serviceContainer: typeMoq.IMock; + let platform: typeMoq.IMock; + let lsPackageService: DotNetLanguageServerPackageService; + let appVersion: typeMoq.IMock; + setup(() => { + serviceContainer = typeMoq.Mock.ofType(); + platform = typeMoq.Mock.ofType(); + appVersion = typeMoq.Mock.ofType(); + lsPackageService = new DotNetLanguageServerPackageService( + serviceContainer.object, + appVersion.object, + platform.object + ); + lsPackageService.getLanguageServerDownloadChannel = () => 'stable'; + }); + function setMinVersionOfLs(version: string) { + const packageJson = { languageServerVersion: version }; + appVersion.setup((e) => e.packageJson).returns(() => packageJson); + } + [true, false].forEach((is64Bit) => { + const bitness = is64Bit ? '64bit' : '32bit'; + test(`Get Package name for Windows (${bitness})`, async () => { + platform.setup((p) => p.osType).returns(() => OSType.Windows); + platform.setup((p) => p.is64bit).returns(() => is64Bit); + const expectedName = is64Bit + ? `${downloadBaseFileName}-${PlatformName.Windows64Bit}` + : `${downloadBaseFileName}-${PlatformName.Windows32Bit}`; + + const name = lsPackageService.getNugetPackageName(); + + platform.verifyAll(); + expect(name).to.be.equal(expectedName); + }); + test(`Get Package name for Mac (${bitness})`, async () => { + platform.setup((p) => p.osType).returns(() => OSType.OSX); + const expectedName = `${downloadBaseFileName}-${PlatformName.Mac64Bit}`; + + const name = lsPackageService.getNugetPackageName(); + + platform.verifyAll(); + expect(name).to.be.equal(expectedName); + }); + test(`Get Package name for Linux (${bitness})`, async () => { + platform.setup((p) => p.osType).returns(() => OSType.Linux); + const expectedName = `${downloadBaseFileName}-${PlatformName.Linux64Bit}`; + + const name = lsPackageService.getNugetPackageName(); + + platform.verifyAll(); + expect(name).to.be.equal(expectedName); + }); + }); + test('Get latest nuget package version', async () => { + const packageName = 'packageName'; + lsPackageService.getNugetPackageName = () => packageName; + lsPackageService.maxMajorVersion = 3; + setMinVersionOfLs('0.0.1'); + const packages: NugetPackage[] = [ + { package: '', uri: '', version: new SemVer('1.1.1') }, + { package: '', uri: '', version: new SemVer('3.4.1') }, + { package: '', uri: '', version: new SemVer('3.1.1') }, + { package: '', uri: '', version: new SemVer('2.1.1') } + ]; + const expectedPackage = packages[1]; + const repo = typeMoq.Mock.ofType(); + const nuget = typeMoq.Mock.ofType(); + serviceContainer + .setup((c) => c.get(typeMoq.It.isValue(INugetRepository), typeMoq.It.isAny())) + .returns(() => repo.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(INugetService))).returns(() => nuget.object); + + repo.setup((n) => n.getPackages(typeMoq.It.isValue(packageName), typeMoq.It.isAny())) + .returns(() => Promise.resolve(packages)) + .verifiable(typeMoq.Times.once()); + nuget + .setup((n) => n.isReleaseVersion(typeMoq.It.isAny())) + .returns(() => true) + .verifiable(typeMoq.Times.atLeastOnce()); + + const info = await lsPackageService.getLatestNugetPackageVersion(undefined); + + repo.verifyAll(); + nuget.verifyAll(); + expect(info).to.deep.equal(expectedPackage); + }); + test('Get latest nuget package version (excluding non-release)', async () => { + setMinVersionOfLs('0.0.1'); + const packageName = 'packageName'; + lsPackageService.getNugetPackageName = () => packageName; + lsPackageService.maxMajorVersion = 1; + const packages: NugetPackage[] = [ + { package: '', uri: '', version: new SemVer('1.1.1') }, + { package: '', uri: '', version: new SemVer('1.3.1-alpha') }, + { package: '', uri: '', version: new SemVer('1.4.1-preview') }, + { package: '', uri: '', version: new SemVer('1.2.1-internal') } + ]; + const expectedPackage = packages[0]; + const repo = typeMoq.Mock.ofType(); + const nuget = new NugetService(); + serviceContainer + .setup((c) => c.get(typeMoq.It.isValue(INugetRepository), typeMoq.It.isAny())) + .returns(() => repo.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(INugetService))).returns(() => nuget); + + repo.setup((n) => n.getPackages(typeMoq.It.isValue(packageName), typeMoq.It.isAny())) + .returns(() => Promise.resolve(packages)) + .verifiable(typeMoq.Times.once()); + + const info = await lsPackageService.getLatestNugetPackageVersion(undefined); + + repo.verifyAll(); + expect(info).to.deep.equal(expectedPackage); + }); + test('Ensure minimum version of package is used', async () => { + const minimumVersion = '0.1.50'; + setMinVersionOfLs(minimumVersion); + const packageName = 'packageName'; + lsPackageService.getNugetPackageName = () => packageName; + lsPackageService.maxMajorVersion = 0; + const packages: NugetPackage[] = [ + { package: '', uri: '', version: new SemVer('0.1.48') }, + { package: '', uri: '', version: new SemVer('0.1.49') } + ]; + const repo = typeMoq.Mock.ofType(); + const nuget = new NugetService(); + serviceContainer + .setup((c) => c.get(typeMoq.It.isValue(INugetRepository), typeMoq.It.isAny())) + .returns(() => repo.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(INugetService))).returns(() => nuget); + + repo.setup((n) => n.getPackages(typeMoq.It.isValue(packageName), typeMoq.It.isAny())) + .returns(() => Promise.resolve(packages)) + .verifiable(typeMoq.Times.once()); + + const info = await lsPackageService.getLatestNugetPackageVersion(undefined, minimumVersion); + + repo.verifyAll(); + const expectedPackage: NugetPackage = { + version: new SemVer(minimumVersion), + package: LanguageServerDownloadChannel.stable, + uri: `${azureCDNBlobStorageAccount}/${LanguageServerDownloadChannel.stable}/${packageName}.${minimumVersion}.nupkg` + }; + expect(info).to.deep.equal(expectedPackage); + }); +}); +suite('Language Server Package Service - getLanguageServerDownloadChannel()', () => { + let serviceContainer: typeMoq.IMock; + let platform: typeMoq.IMock; + let lsPackageService: DotNetLanguageServerPackageService; + let appVersion: typeMoq.IMock; + let configService: typeMoq.IMock; + setup(() => { + serviceContainer = typeMoq.Mock.ofType(); + platform = typeMoq.Mock.ofType(); + appVersion = typeMoq.Mock.ofType(); + configService = typeMoq.Mock.ofType(); + serviceContainer.setup((s) => s.get(IConfigurationService)).returns(() => configService.object); + lsPackageService = new DotNetLanguageServerPackageService( + serviceContainer.object, + appVersion.object, + platform.object + ); + lsPackageService.isAlphaVersionOfExtension = () => true; + }); + test("If 'python.analysis.downloadChannel' setting is specified, return the value of the setting", async () => { + const settings = { + analysis: { + downloadChannel: 'someValue' + } + }; + configService.setup((c) => c.getSettings()).returns(() => settings as any); + + lsPackageService.isAlphaVersionOfExtension = () => { + throw new Error('Should not be here'); + }; + const downloadChannel = lsPackageService.getLanguageServerDownloadChannel(); + + expect(downloadChannel).to.be.equal('someValue'); + }); + + test("If 'python.analysis.downloadChannel' setting is not specified and insiders channel is 'weekly', return 'beta'", async () => { + const settings = { + analysis: {}, + insidersChannel: 'weekly' + }; + configService.setup((c) => c.getSettings()).returns(() => settings as any); + + lsPackageService.isAlphaVersionOfExtension = () => { + throw new Error('Should not be here'); + }; + const downloadChannel = lsPackageService.getLanguageServerDownloadChannel(); + + expect(downloadChannel).to.be.equal('beta'); + }); + + test("If 'python.analysis.downloadChannel' setting is not specified and insiders channel is 'daily', return 'beta'", async () => { + const settings = { + analysis: {}, + insidersChannel: 'daily' + }; + configService.setup((c) => c.getSettings()).returns(() => settings as any); + + lsPackageService.isAlphaVersionOfExtension = () => { + throw new Error('Should not be here'); + }; + const downloadChannel = lsPackageService.getLanguageServerDownloadChannel(); + + expect(downloadChannel).to.be.equal('beta'); + }); + + test("If 'python.analysis.downloadChannel' setting is not specified, user is not using insiders, and extension has Alpha version, return 'beta'", async () => { + const settings = { + analysis: {}, + insidersChannel: 'off' + }; + configService.setup((c) => c.getSettings()).returns(() => settings as any); + + lsPackageService.isAlphaVersionOfExtension = () => true; + const downloadChannel = lsPackageService.getLanguageServerDownloadChannel(); + + expect(downloadChannel).to.be.equal('beta'); + }); + + test("If 'python.analysis.downloadChannel' setting is not specified, user is not using insiders, and extension does not have Alpha version, return 'stable'", async () => { + const settings = { + analysis: {}, + insidersChannel: 'off' + }; + configService.setup((c) => c.getSettings()).returns(() => settings as any); + + lsPackageService.isAlphaVersionOfExtension = () => false; + const downloadChannel = lsPackageService.getLanguageServerDownloadChannel(); + + expect(downloadChannel).to.be.equal('stable'); + }); +}); diff --git a/src/test/activation/languageServer/manager.unit.test.ts b/src/test/activation/languageServer/manager.unit.test.ts index a9fd1f5880fb..43a40ef28f9c 100644 --- a/src/test/activation/languageServer/manager.unit.test.ts +++ b/src/test/activation/languageServer/manager.unit.test.ts @@ -56,7 +56,7 @@ suite('Language Server - Manager', () => { ); }); - [undefined, Uri.file(__filename)].forEach(resource => { + [undefined, Uri.file(__filename)].forEach((resource) => { async function startLanguageServer() { let invoked = false; const lsExtensionChangeFn = (_handler: Function) => { diff --git a/src/test/activation/languageServer/outputChannel.unit.test.ts b/src/test/activation/languageServer/outputChannel.unit.test.ts index b785695767bb..c120cfdc45ca 100644 --- a/src/test/activation/languageServer/outputChannel.unit.test.ts +++ b/src/test/activation/languageServer/outputChannel.unit.test.ts @@ -1,120 +1,120 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect } from 'chai'; -import * as TypeMoq from 'typemoq'; -import { LanguageServerOutputChannel } from '../../../client/activation/languageServer/outputChannel'; -import { IApplicationShell, ICommandManager } from '../../../client/common/application/types'; -import { IOutputChannel } from '../../../client/common/types'; -import { sleep } from '../../../client/common/utils/async'; -import { OutputChannelNames } from '../../../client/common/utils/localize'; - -// tslint:disable-next-line:max-func-body-length -suite('Language Server Output Channel', () => { - let appShell: TypeMoq.IMock; - let languageServerOutputChannel: LanguageServerOutputChannel; - let commandManager: TypeMoq.IMock; - let output: TypeMoq.IMock; - setup(() => { - appShell = TypeMoq.Mock.ofType(); - output = TypeMoq.Mock.ofType(); - commandManager = TypeMoq.Mock.ofType(); - languageServerOutputChannel = new LanguageServerOutputChannel(appShell.object, commandManager.object); - }); - - test('Create output channel if one does not exist before and return it', async () => { - appShell - .setup(a => a.createOutputChannel(OutputChannelNames.languageServer())) - .returns(() => output.object) - .verifiable(TypeMoq.Times.once()); - const channel = languageServerOutputChannel.channel; - appShell.verifyAll(); - expect(channel).to.not.equal(undefined, 'Channel should not be undefined'); - }); - - test('Do not create output channel if one already exists', async () => { - languageServerOutputChannel.output = output.object; - appShell - .setup(a => a.createOutputChannel(TypeMoq.It.isAny())) - .returns(() => output.object) - .verifiable(TypeMoq.Times.never()); - const channel = languageServerOutputChannel.channel; - appShell.verifyAll(); - expect(channel).to.not.equal(undefined, 'Channel should not be undefined'); - }); - test('Register Command to display output panel', async () => { - appShell - .setup(a => a.createOutputChannel(TypeMoq.It.isAny())) - .returns(() => output.object) - .verifiable(TypeMoq.Times.once()); - commandManager - .setup(c => - c.executeCommand( - TypeMoq.It.isValue('setContext'), - TypeMoq.It.isValue('python.hasLanguageServerOutputChannel'), - TypeMoq.It.isValue(true) - ) - ) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); - commandManager - .setup(c => c.registerCommand(TypeMoq.It.isValue('python.viewLanguageServerOutput'), TypeMoq.It.isAny())) - .verifiable(TypeMoq.Times.once()); - - // Doesn't matter how many times we access channel propery. - let channel = languageServerOutputChannel.channel; - channel = languageServerOutputChannel.channel; - channel = languageServerOutputChannel.channel; - - await sleep(1); - - appShell.verifyAll(); - commandManager.verifyAll(); - expect(channel).to.not.equal(undefined, 'Channel should not be undefined'); - }); - test('Display panel when invoking command python.viewLanguageServerOutput', async () => { - let cmdCallback: Function | undefined; - appShell - .setup(a => a.createOutputChannel(TypeMoq.It.isAny())) - .returns(() => output.object) - .verifiable(TypeMoq.Times.once()); - commandManager - .setup(c => - c.executeCommand( - TypeMoq.It.isValue('setContext'), - TypeMoq.It.isValue('python.hasLanguageServerOutputChannel'), - TypeMoq.It.isValue(true) - ) - ) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); - commandManager - .setup(c => c.registerCommand(TypeMoq.It.isValue('python.viewLanguageServerOutput'), TypeMoq.It.isAny())) - .callback((_: string, callback: Function) => (cmdCallback = callback)) - .verifiable(TypeMoq.Times.once()); - output.setup(o => o.show(true)).verifiable(TypeMoq.Times.never()); - // Doesn't matter how many times we access channel propery. - let channel = languageServerOutputChannel.channel; - channel = languageServerOutputChannel.channel; - channel = languageServerOutputChannel.channel; - - await sleep(1); - - appShell.verifyAll(); - commandManager.verifyAll(); - output.verifyAll(); - expect(channel).to.not.equal(undefined, 'Channel should not be undefined'); - expect(cmdCallback).to.not.equal(undefined, 'Command handler should not be undefined'); - - // Confirm panel is displayed when command handler is invoked. - output.reset(); - output.setup(o => o.show(true)).verifiable(TypeMoq.Times.once()); - - // Invoke callback. - cmdCallback!(); - - output.verifyAll(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { expect } from 'chai'; +import * as TypeMoq from 'typemoq'; +import { LanguageServerOutputChannel } from '../../../client/activation/languageServer/outputChannel'; +import { IApplicationShell, ICommandManager } from '../../../client/common/application/types'; +import { IOutputChannel } from '../../../client/common/types'; +import { sleep } from '../../../client/common/utils/async'; +import { OutputChannelNames } from '../../../client/common/utils/localize'; + +// tslint:disable-next-line:max-func-body-length +suite('Language Server Output Channel', () => { + let appShell: TypeMoq.IMock; + let languageServerOutputChannel: LanguageServerOutputChannel; + let commandManager: TypeMoq.IMock; + let output: TypeMoq.IMock; + setup(() => { + appShell = TypeMoq.Mock.ofType(); + output = TypeMoq.Mock.ofType(); + commandManager = TypeMoq.Mock.ofType(); + languageServerOutputChannel = new LanguageServerOutputChannel(appShell.object, commandManager.object); + }); + + test('Create output channel if one does not exist before and return it', async () => { + appShell + .setup((a) => a.createOutputChannel(OutputChannelNames.languageServer())) + .returns(() => output.object) + .verifiable(TypeMoq.Times.once()); + const channel = languageServerOutputChannel.channel; + appShell.verifyAll(); + expect(channel).to.not.equal(undefined, 'Channel should not be undefined'); + }); + + test('Do not create output channel if one already exists', async () => { + languageServerOutputChannel.output = output.object; + appShell + .setup((a) => a.createOutputChannel(TypeMoq.It.isAny())) + .returns(() => output.object) + .verifiable(TypeMoq.Times.never()); + const channel = languageServerOutputChannel.channel; + appShell.verifyAll(); + expect(channel).to.not.equal(undefined, 'Channel should not be undefined'); + }); + test('Register Command to display output panel', async () => { + appShell + .setup((a) => a.createOutputChannel(TypeMoq.It.isAny())) + .returns(() => output.object) + .verifiable(TypeMoq.Times.once()); + commandManager + .setup((c) => + c.executeCommand( + TypeMoq.It.isValue('setContext'), + TypeMoq.It.isValue('python.hasLanguageServerOutputChannel'), + TypeMoq.It.isValue(true) + ) + ) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); + commandManager + .setup((c) => c.registerCommand(TypeMoq.It.isValue('python.viewLanguageServerOutput'), TypeMoq.It.isAny())) + .verifiable(TypeMoq.Times.once()); + + // Doesn't matter how many times we access channel propery. + let channel = languageServerOutputChannel.channel; + channel = languageServerOutputChannel.channel; + channel = languageServerOutputChannel.channel; + + await sleep(1); + + appShell.verifyAll(); + commandManager.verifyAll(); + expect(channel).to.not.equal(undefined, 'Channel should not be undefined'); + }); + test('Display panel when invoking command python.viewLanguageServerOutput', async () => { + let cmdCallback: Function | undefined; + appShell + .setup((a) => a.createOutputChannel(TypeMoq.It.isAny())) + .returns(() => output.object) + .verifiable(TypeMoq.Times.once()); + commandManager + .setup((c) => + c.executeCommand( + TypeMoq.It.isValue('setContext'), + TypeMoq.It.isValue('python.hasLanguageServerOutputChannel'), + TypeMoq.It.isValue(true) + ) + ) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); + commandManager + .setup((c) => c.registerCommand(TypeMoq.It.isValue('python.viewLanguageServerOutput'), TypeMoq.It.isAny())) + .callback((_: string, callback: Function) => (cmdCallback = callback)) + .verifiable(TypeMoq.Times.once()); + output.setup((o) => o.show(true)).verifiable(TypeMoq.Times.never()); + // Doesn't matter how many times we access channel propery. + let channel = languageServerOutputChannel.channel; + channel = languageServerOutputChannel.channel; + channel = languageServerOutputChannel.channel; + + await sleep(1); + + appShell.verifyAll(); + commandManager.verifyAll(); + output.verifyAll(); + expect(channel).to.not.equal(undefined, 'Channel should not be undefined'); + expect(cmdCallback).to.not.equal(undefined, 'Command handler should not be undefined'); + + // Confirm panel is displayed when command handler is invoked. + output.reset(); + output.setup((o) => o.show(true)).verifiable(TypeMoq.Times.once()); + + // Invoke callback. + cmdCallback!(); + + output.verifyAll(); + }); +}); diff --git a/src/test/activation/languageServer/platformData.unit.test.ts b/src/test/activation/languageServer/platformData.unit.test.ts index 8206c428075d..d424aa63ceac 100644 --- a/src/test/activation/languageServer/platformData.unit.test.ts +++ b/src/test/activation/languageServer/platformData.unit.test.ts @@ -36,9 +36,9 @@ suite('Language Server Activation - platform data', () => { test('Name and hash (Windows/Mac)', async () => { for (const t of testDataWinMac) { const platformService = TypeMoq.Mock.ofType(); - platformService.setup(x => x.isWindows).returns(() => t.isWindows); - platformService.setup(x => x.isMac).returns(() => !t.isWindows); - platformService.setup(x => x.is64bit).returns(() => t.is64Bit); + platformService.setup((x) => x.isWindows).returns(() => t.isWindows); + platformService.setup((x) => x.isMac).returns(() => !t.isWindows); + platformService.setup((x) => x.is64bit).returns(() => t.is64Bit); const pd = new PlatformData(platformService.object); @@ -49,10 +49,10 @@ suite('Language Server Activation - platform data', () => { test('Name and hash (Linux)', async () => { for (const t of testDataLinux) { const platformService = TypeMoq.Mock.ofType(); - platformService.setup(x => x.isWindows).returns(() => false); - platformService.setup(x => x.isMac).returns(() => false); - platformService.setup(x => x.isLinux).returns(() => true); - platformService.setup(x => x.is64bit).returns(() => true); + platformService.setup((x) => x.isWindows).returns(() => false); + platformService.setup((x) => x.isMac).returns(() => false); + platformService.setup((x) => x.isLinux).returns(() => true); + platformService.setup((x) => x.is64bit).returns(() => true); const pd = new PlatformData(platformService.object); @@ -63,9 +63,9 @@ suite('Language Server Activation - platform data', () => { test('Module name', async () => { for (const t of testDataModuleName) { const platformService = TypeMoq.Mock.ofType(); - platformService.setup(x => x.isWindows).returns(() => t.isWindows); - platformService.setup(x => x.isLinux).returns(() => t.isLinux); - platformService.setup(x => x.isMac).returns(() => t.isMac); + platformService.setup((x) => x.isWindows).returns(() => t.isWindows); + platformService.setup((x) => x.isLinux).returns(() => t.isLinux); + platformService.setup((x) => x.isMac).returns(() => t.isMac); const pd = new PlatformData(platformService.object); diff --git a/src/test/application/diagnostics/applicationDiagnostics.unit.test.ts b/src/test/application/diagnostics/applicationDiagnostics.unit.test.ts index 698bb78315b0..997a9fdd3725 100644 --- a/src/test/application/diagnostics/applicationDiagnostics.unit.test.ts +++ b/src/test/application/diagnostics/applicationDiagnostics.unit.test.ts @@ -42,18 +42,18 @@ suite('Application Diagnostics - ApplicationDiagnostics', () => { delete process.env.VSC_PYTHON_CI_TEST; serviceContainer = typemoq.Mock.ofType(); envHealthCheck = typemoq.Mock.ofType(); - envHealthCheck.setup(service => service.runInBackground).returns(() => true); + envHealthCheck.setup((service) => service.runInBackground).returns(() => true); lsNotSupportedCheck = typemoq.Mock.ofType(); - lsNotSupportedCheck.setup(service => service.runInBackground).returns(() => false); + lsNotSupportedCheck.setup((service) => service.runInBackground).returns(() => false); pythonInterpreterCheck = typemoq.Mock.ofType(); - pythonInterpreterCheck.setup(service => service.runInBackground).returns(() => false); + pythonInterpreterCheck.setup((service) => service.runInBackground).returns(() => false); outputChannel = typemoq.Mock.ofType(); serviceContainer - .setup(d => d.getAll(typemoq.It.isValue(IDiagnosticsService))) + .setup((d) => d.getAll(typemoq.It.isValue(IDiagnosticsService))) .returns(() => [envHealthCheck.object, lsNotSupportedCheck.object, pythonInterpreterCheck.object]); serviceContainer - .setup(d => d.get(typemoq.It.isValue(IOutputChannel), typemoq.It.isValue(STANDARD_OUTPUT_CHANNEL))) + .setup((d) => d.get(typemoq.It.isValue(IOutputChannel), typemoq.It.isValue(STANDARD_OUTPUT_CHANNEL))) .returns(() => outputChannel.object); appDiagnostics = new ApplicationDiagnostics(serviceContainer.object, outputChannel.object); @@ -66,10 +66,10 @@ suite('Application Diagnostics - ApplicationDiagnostics', () => { test('Register should register source maps', () => { const sourceMapService = typemoq.Mock.ofType(); - sourceMapService.setup(s => s.register()).verifiable(typemoq.Times.once()); + sourceMapService.setup((s) => s.register()).verifiable(typemoq.Times.once()); serviceContainer - .setup(d => d.get(typemoq.It.isValue(ISourceMapSupportService), typemoq.It.isAny())) + .setup((d) => d.get(typemoq.It.isValue(ISourceMapSupportService), typemoq.It.isAny())) .returns(() => sourceMapService.object); appDiagnostics.register(); @@ -79,15 +79,15 @@ suite('Application Diagnostics - ApplicationDiagnostics', () => { test('Performing Pre Startup Health Check must diagnose all validation checks', async () => { envHealthCheck - .setup(e => e.diagnose(typemoq.It.isAny())) + .setup((e) => e.diagnose(typemoq.It.isAny())) .returns(() => Promise.resolve([])) .verifiable(typemoq.Times.once()); lsNotSupportedCheck - .setup(p => p.diagnose(typemoq.It.isAny())) + .setup((p) => p.diagnose(typemoq.It.isAny())) .returns(() => Promise.resolve([])) .verifiable(typemoq.Times.once()); pythonInterpreterCheck - .setup(p => p.diagnose(typemoq.It.isAny())) + .setup((p) => p.diagnose(typemoq.It.isAny())) .returns(() => Promise.resolve([])) .verifiable(typemoq.Times.once()); @@ -108,27 +108,27 @@ suite('Application Diagnostics - ApplicationDiagnostics', () => { invokeHandler: 'default' } as any; envHealthCheck - .setup(e => e.diagnose(typemoq.It.isAny())) + .setup((e) => e.diagnose(typemoq.It.isAny())) .returns(() => Promise.resolve([diagnostic])) .verifiable(typemoq.Times.once()); envHealthCheck - .setup(p => p.handle(typemoq.It.isValue([diagnostic]))) + .setup((p) => p.handle(typemoq.It.isValue([diagnostic]))) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); lsNotSupportedCheck - .setup(p => p.diagnose(typemoq.It.isAny())) + .setup((p) => p.diagnose(typemoq.It.isAny())) .returns(() => Promise.resolve([diagnostic])) .verifiable(typemoq.Times.once()); lsNotSupportedCheck - .setup(p => p.handle(typemoq.It.isValue([diagnostic]))) + .setup((p) => p.handle(typemoq.It.isValue([diagnostic]))) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); pythonInterpreterCheck - .setup(p => p.diagnose(typemoq.It.isAny())) + .setup((p) => p.diagnose(typemoq.It.isAny())) .returns(() => Promise.resolve([diagnostic])) .verifiable(typemoq.Times.once()); pythonInterpreterCheck - .setup(p => p.handle(typemoq.It.isValue([diagnostic]))) + .setup((p) => p.handle(typemoq.It.isValue([diagnostic]))) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); @@ -180,11 +180,11 @@ suite('Application Diagnostics - ApplicationDiagnostics', () => { const message = `Diagnostic Code: ${diagnostic.code}, Message: ${diagnostic.message}`; switch (diagnostic.severity) { case DiagnosticSeverity.Error: { - outputChannel.setup(o => o.appendLine(message)).verifiable(typemoq.Times.once()); + outputChannel.setup((o) => o.appendLine(message)).verifiable(typemoq.Times.once()); break; } case DiagnosticSeverity.Warning: { - outputChannel.setup(o => o.appendLine(message)).verifiable(typemoq.Times.once()); + outputChannel.setup((o) => o.appendLine(message)).verifiable(typemoq.Times.once()); break; } default: { @@ -194,15 +194,15 @@ suite('Application Diagnostics - ApplicationDiagnostics', () => { } envHealthCheck - .setup(e => e.diagnose(typemoq.It.isAny())) + .setup((e) => e.diagnose(typemoq.It.isAny())) .returns(() => Promise.resolve(diagnostics)) .verifiable(typemoq.Times.once()); lsNotSupportedCheck - .setup(p => p.diagnose(typemoq.It.isAny())) + .setup((p) => p.diagnose(typemoq.It.isAny())) .returns(() => Promise.resolve([])) .verifiable(typemoq.Times.once()); pythonInterpreterCheck - .setup(p => p.diagnose(typemoq.It.isAny())) + .setup((p) => p.diagnose(typemoq.It.isAny())) .returns(() => Promise.resolve([])) .verifiable(typemoq.Times.once()); diff --git a/src/test/application/diagnostics/checks/envPathVariable.unit.test.ts b/src/test/application/diagnostics/checks/envPathVariable.unit.test.ts index 0a7dfb7b6b9d..e0e37dd6c774 100644 --- a/src/test/application/diagnostics/checks/envPathVariable.unit.test.ts +++ b/src/test/application/diagnostics/checks/envPathVariable.unit.test.ts @@ -44,12 +44,14 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { setup(() => { const serviceContainer = typemoq.Mock.ofType(); platformService = typemoq.Mock.ofType(); - platformService.setup(p => p.pathVariableName).returns(() => pathVariableName); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IPlatformService))).returns(() => platformService.object); + platformService.setup((p) => p.pathVariableName).returns(() => pathVariableName); + serviceContainer + .setup((s) => s.get(typemoq.It.isValue(IPlatformService))) + .returns(() => platformService.object); messageHandler = typemoq.Mock.ofType>(); serviceContainer - .setup(s => + .setup((s) => s.get( typemoq.It.isValue(IDiagnosticHandlerService), typemoq.It.isValue(DiagnosticCommandPromptHandlerServiceId) @@ -58,32 +60,32 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { .returns(() => messageHandler.object); appEnv = typemoq.Mock.ofType(); - appEnv.setup(a => a.extensionName).returns(() => extensionName); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IApplicationEnvironment))).returns(() => appEnv.object); + appEnv.setup((a) => a.extensionName).returns(() => extensionName); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IApplicationEnvironment))).returns(() => appEnv.object); filterService = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IDiagnosticFilterService))) + .setup((s) => s.get(typemoq.It.isValue(IDiagnosticFilterService))) .returns(() => filterService.object); commandFactory = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IDiagnosticsCommandFactory))) + .setup((s) => s.get(typemoq.It.isValue(IDiagnosticsCommandFactory))) .returns(() => commandFactory.object); const currentProc = typemoq.Mock.ofType(); procEnv = typemoq.Mock.ofType(); - currentProc.setup(p => p.env).returns(() => procEnv.object); - serviceContainer.setup(s => s.get(typemoq.It.isValue(ICurrentProcess))).returns(() => currentProc.object); + currentProc.setup((p) => p.env).returns(() => procEnv.object); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(ICurrentProcess))).returns(() => currentProc.object); const pathUtils = typemoq.Mock.ofType(); - pathUtils.setup(p => p.delimiter).returns(() => pathDelimiter); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IPathUtils))).returns(() => pathUtils.object); + pathUtils.setup((p) => p.delimiter).returns(() => pathDelimiter); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IPathUtils))).returns(() => pathUtils.object); const workspaceService = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IWorkspaceService))) + .setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); - workspaceService.setup(w => w.getWorkspaceFolder(typemoq.It.isAny())).returns(() => undefined); + workspaceService.setup((w) => w.getWorkspaceFolder(typemoq.It.isAny())).returns(() => undefined); diagnosticService = new (class extends EnvironmentPathVariableDiagnosticsService { public _clear() { @@ -98,7 +100,7 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { test('Can handle EnvPathVariable diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.InvalidEnvironmentPathVariableDiagnostic) .verifiable(typemoq.Times.atLeastOnce()); @@ -109,7 +111,7 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { test('Can not handle non-EnvPathVariable diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => 'Something Else' as any) .verifiable(typemoq.Times.atLeastOnce()); @@ -118,23 +120,23 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { diagnostic.verifyAll(); }); test('Should return empty diagnostics for Mac', async () => { - platformService.setup(p => p.isMac).returns(() => true); - platformService.setup(p => p.isLinux).returns(() => false); - platformService.setup(p => p.isWindows).returns(() => false); + platformService.setup((p) => p.isMac).returns(() => true); + platformService.setup((p) => p.isLinux).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => false); const diagnostics = await diagnosticService.diagnose(undefined); expect(diagnostics).to.be.deep.equal([]); }); test('Should return empty diagnostics for Linux', async () => { - platformService.setup(p => p.isMac).returns(() => false); - platformService.setup(p => p.isLinux).returns(() => true); - platformService.setup(p => p.isWindows).returns(() => false); + platformService.setup((p) => p.isMac).returns(() => false); + platformService.setup((p) => p.isLinux).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => false); const diagnostics = await diagnosticService.diagnose(undefined); expect(diagnostics).to.be.deep.equal([]); }); test('Should return empty diagnostics for Windows if path variable is valid', async () => { - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); const paths = [path.join('one', 'two', 'three'), path.join('one', 'two', 'four')].join(pathDelimiter); - procEnv.setup(env => env[pathVariableName]).returns(() => paths); + procEnv.setup((env) => env[pathVariableName]).returns(() => paths); const diagnostics = await diagnosticService.diagnose(undefined); @@ -142,9 +144,9 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { }); // Note: On windows, when a path contains a `;` then Windows encloses the path within `"`. test("Should return single diagnostics for Windows if path contains '\"'", async () => { - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); const paths = [path.join('one', 'two', 'three"'), path.join('one', 'two', 'four')].join(pathDelimiter); - procEnv.setup(env => env[pathVariableName]).returns(() => paths); + procEnv.setup((env) => env[pathVariableName]).returns(() => paths); const diagnostics = await diagnosticService.diagnose(undefined); @@ -158,23 +160,23 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { test('Should not return diagnostics for Windows if path ends with delimiter', async () => { const paths = [path.join('one', 'two', 'three'), path.join('one', 'two', 'four')].join(pathDelimiter) + pathDelimiter; - platformService.setup(p => p.isWindows).returns(() => true); - procEnv.setup(env => env[pathVariableName]).returns(() => paths); + platformService.setup((p) => p.isWindows).returns(() => true); + procEnv.setup((env) => env[pathVariableName]).returns(() => paths); const diagnostics = await diagnosticService.diagnose(undefined); expect(diagnostics).to.be.lengthOf(0); }); test('Should display three options in message displayed with 2 commands', async () => { - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.InvalidEnvironmentPathVariableDiagnostic) .verifiable(typemoq.Times.atLeastOnce()); const alwaysIgnoreCommand = typemoq.Mock.ofType(); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -187,7 +189,7 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { .verifiable(typemoq.Times.once()); const launchBrowserCommand = typemoq.Mock.ofType(); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ type: 'launch' }) @@ -195,7 +197,7 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { ) .returns(() => launchBrowserCommand.object) .verifiable(typemoq.Times.once()); - messageHandler.setup(m => m.handle(typemoq.It.isAny(), typemoq.It.isAny())).verifiable(typemoq.Times.once()); + messageHandler.setup((m) => m.handle(typemoq.It.isAny(), typemoq.It.isAny())).verifiable(typemoq.Times.once()); await diagnosticService.handle([diagnostic.object]); @@ -204,23 +206,23 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { messageHandler.verifyAll(); }); test('Should not display a message if the diagnostic code has been ignored', async () => { - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); const diagnostic = typemoq.Mock.ofType(); filterService - .setup(f => + .setup((f) => f.shouldIgnoreDiagnostic(typemoq.It.isValue(DiagnosticCodes.InvalidEnvironmentPathVariableDiagnostic)) ) .returns(() => Promise.resolve(true)) .verifiable(typemoq.Times.once()); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.InvalidEnvironmentPathVariableDiagnostic) .verifiable(typemoq.Times.atLeastOnce()); commandFactory - .setup(f => f.createCommand(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((f) => f.createCommand(typemoq.It.isAny(), typemoq.It.isAny())) .verifiable(typemoq.Times.never()); - messageHandler.setup(m => m.handle(typemoq.It.isAny(), typemoq.It.isAny())).verifiable(typemoq.Times.never()); + messageHandler.setup((m) => m.handle(typemoq.It.isAny(), typemoq.It.isAny())).verifiable(typemoq.Times.never()); await diagnosticService.handle([diagnostic.object]); diff --git a/src/test/application/diagnostics/checks/invalidLaunchJsonDebugger.unit.test.ts b/src/test/application/diagnostics/checks/invalidLaunchJsonDebugger.unit.test.ts index d86fb4503ba6..508788d2b51a 100644 --- a/src/test/application/diagnostics/checks/invalidLaunchJsonDebugger.unit.test.ts +++ b/src/test/application/diagnostics/checks/invalidLaunchJsonDebugger.unit.test.ts @@ -43,7 +43,7 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { workspaceService = TypeMoq.Mock.ofType(); baseWorkspaceService = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((s) => s.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => baseWorkspaceService.object); diagnosticService = new (class extends InvalidLaunchJsonDebuggerService { @@ -67,7 +67,7 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { ]) { const diagnostic = TypeMoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => code) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -80,7 +80,7 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { test('Can not handle non-InvalidLaunchJsonDebugger diagnostics', async () => { const diagnostic = TypeMoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => 'Something Else' as any) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -91,7 +91,7 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { test('Should return empty diagnostics if there are no workspace folders', async () => { workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => false) .verifiable(TypeMoq.Times.once()); const diagnostics = await diagnosticService.diagnose(undefined); @@ -101,18 +101,18 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { test('Should return empty diagnostics if file launch.json does not exist', async () => { workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.getWorkspaceFolder(undefined)) + .setup((w) => w.getWorkspaceFolder(undefined)) .returns(() => undefined) .verifiable(TypeMoq.Times.never()); - fs.setup(w => w.fileExists(TypeMoq.It.isAny())) + fs.setup((w) => w.fileExists(TypeMoq.It.isAny())) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); const diagnostics = await diagnosticService.diagnose(undefined); @@ -124,17 +124,17 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { test('Should return empty diagnostics if file launch.json does not contain strings "pythonExperimental" and "debugStdLib" ', async () => { const fileContents = 'Hello I am launch.json, although I am not very jsony'; workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.fileExists(TypeMoq.It.isAny())) + fs.setup((w) => w.fileExists(TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.readFile(TypeMoq.It.isAny())) + fs.setup((w) => w.readFile(TypeMoq.It.isAny())) .returns(() => Promise.resolve(fileContents)) .verifiable(TypeMoq.Times.once()); const diagnostics = await diagnosticService.diagnose(undefined); @@ -146,17 +146,17 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { test('Should return InvalidDebuggerTypeDiagnostic if file launch.json contains string "pythonExperimental"', async () => { const fileContents = 'Hello I am launch.json, I contain string "pythonExperimental"'; workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.fileExists(TypeMoq.It.isAny())) + fs.setup((w) => w.fileExists(TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.readFile(TypeMoq.It.isAny())) + fs.setup((w) => w.readFile(TypeMoq.It.isAny())) .returns(() => Promise.resolve(fileContents)) .verifiable(TypeMoq.Times.once()); const diagnostics = await diagnosticService.diagnose(undefined); @@ -171,17 +171,17 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { test('Should return JustMyCodeDiagnostic if file launch.json contains string "debugStdLib"', async () => { const fileContents = 'Hello I am launch.json, I contain string "debugStdLib"'; workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.fileExists(TypeMoq.It.isAny())) + fs.setup((w) => w.fileExists(TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.readFile(TypeMoq.It.isAny())) + fs.setup((w) => w.readFile(TypeMoq.It.isAny())) .returns(() => Promise.resolve(fileContents)) .verifiable(TypeMoq.Times.once()); const diagnostics = await diagnosticService.diagnose(undefined); @@ -196,17 +196,17 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { test('Should return both diagnostics if file launch.json contains string "debugStdLib" and "pythonExperimental"', async () => { const fileContents = 'Hello I am launch.json, I contain both "debugStdLib" and "pythonExperimental"'; workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.fileExists(TypeMoq.It.isAny())) + fs.setup((w) => w.fileExists(TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.readFile(TypeMoq.It.isAny())) + fs.setup((w) => w.readFile(TypeMoq.It.isAny())) .returns(() => Promise.resolve(fileContents)) .verifiable(TypeMoq.Times.once()); const diagnostics = await diagnosticService.diagnose(undefined); @@ -230,15 +230,15 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { const diagnostic = TypeMoq.Mock.ofType(); let options: MessageCommandPrompt | undefined; diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => code) .verifiable(TypeMoq.Times.atLeastOnce()); messageHandler - .setup(m => m.handle(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((m) => m.handle(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .callback((_, opts: MessageCommandPrompt) => (options = opts)) .verifiable(TypeMoq.Times.atLeastOnce()); baseWorkspaceService - .setup(c => c.getWorkspaceFolder(TypeMoq.It.isAny())) + .setup((c) => c.getWorkspaceFolder(TypeMoq.It.isAny())) .returns(() => workspaceFolder) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -262,19 +262,19 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { ]) { const diagnostic = TypeMoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => code) .verifiable(TypeMoq.Times.atLeastOnce()); diagnostic - .setup(d => d.invokeHandler) + .setup((d) => d.invokeHandler) .returns(() => 'always') .verifiable(TypeMoq.Times.atLeastOnce()); messageHandler.reset(); messageHandler - .setup(m => m.handle(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((m) => m.handle(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .verifiable(TypeMoq.Times.exactly(2)); baseWorkspaceService - .setup(c => c.getWorkspaceFolder(TypeMoq.It.isAny())) + .setup((c) => c.getWorkspaceFolder(TypeMoq.It.isAny())) .returns(() => workspaceFolder) .verifiable(TypeMoq.Times.never()); @@ -295,11 +295,11 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { DiagnosticCodes.ConsoleTypeDiagnostic ]) { workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.never()); await (diagnosticService as any).fixLaunchJson(code); @@ -314,17 +314,17 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { DiagnosticCodes.ConsoleTypeDiagnostic ]) { workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.atLeastOnce()); - fs.setup(w => w.fileExists(TypeMoq.It.isAny())) + fs.setup((w) => w.fileExists(TypeMoq.It.isAny())) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.atLeastOnce()); - fs.setup(w => w.readFile(TypeMoq.It.isAny())) + fs.setup((w) => w.readFile(TypeMoq.It.isAny())) .returns(() => Promise.resolve('')) .verifiable(TypeMoq.Times.never()); await (diagnosticService as any).fixLaunchJson(code); @@ -337,20 +337,20 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { const launchJson = '{"debugStdLib": true, "debugStdLib": false}'; const correctedlaunchJson = '{"justMyCode": false, "justMyCode": true}'; workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.fileExists(TypeMoq.It.isAny())) + fs.setup((w) => w.fileExists(TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.readFile(TypeMoq.It.isAny())) + fs.setup((w) => w.readFile(TypeMoq.It.isAny())) .returns(() => Promise.resolve(launchJson)) .verifiable(TypeMoq.Times.atLeastOnce()); - fs.setup(w => w.writeFile(TypeMoq.It.isAnyString(), correctedlaunchJson)) + fs.setup((w) => w.writeFile(TypeMoq.It.isAnyString(), correctedlaunchJson)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); await (diagnosticService as any).fixLaunchJson(DiagnosticCodes.JustMyCodeDiagnostic); @@ -362,20 +362,20 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { const launchJson = '{"Python Experimental: task" "pythonExperimental"}'; const correctedlaunchJson = '{"Python: task" "python"}'; workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.fileExists(TypeMoq.It.isAny())) + fs.setup((w) => w.fileExists(TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.readFile(TypeMoq.It.isAny())) + fs.setup((w) => w.readFile(TypeMoq.It.isAny())) .returns(() => Promise.resolve(launchJson)) .verifiable(TypeMoq.Times.atLeastOnce()); - fs.setup(w => w.writeFile(TypeMoq.It.isAnyString(), correctedlaunchJson)) + fs.setup((w) => w.writeFile(TypeMoq.It.isAnyString(), correctedlaunchJson)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); await (diagnosticService as any).fixLaunchJson(DiagnosticCodes.InvalidDebuggerTypeDiagnostic); @@ -387,20 +387,20 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => { const launchJson = '{"console": "none"}'; const correctedlaunchJson = '{"console": "internalConsole"}'; workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.fileExists(TypeMoq.It.isAny())) + fs.setup((w) => w.fileExists(TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); - fs.setup(w => w.readFile(TypeMoq.It.isAny())) + fs.setup((w) => w.readFile(TypeMoq.It.isAny())) .returns(() => Promise.resolve(launchJson)) .verifiable(TypeMoq.Times.atLeastOnce()); - fs.setup(w => w.writeFile(TypeMoq.It.isAnyString(), correctedlaunchJson)) + fs.setup((w) => w.writeFile(TypeMoq.It.isAnyString(), correctedlaunchJson)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); await (diagnosticService as any).fixLaunchJson(DiagnosticCodes.ConsoleTypeDiagnostic); diff --git a/src/test/application/diagnostics/checks/invalidPythonPathInDebugger.unit.test.ts b/src/test/application/diagnostics/checks/invalidPythonPathInDebugger.unit.test.ts index 71a6f1aacc11..3120f88163b0 100644 --- a/src/test/application/diagnostics/checks/invalidPythonPathInDebugger.unit.test.ts +++ b/src/test/application/diagnostics/checks/invalidPythonPathInDebugger.unit.test.ts @@ -42,7 +42,7 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { const serviceContainer = typemoq.Mock.ofType(); messageHandler = typemoq.Mock.ofType>(); serviceContainer - .setup(s => + .setup((s) => s.get( typemoq.It.isValue(IDiagnosticHandlerService), typemoq.It.isValue(DiagnosticCommandPromptHandlerServiceId) @@ -52,17 +52,17 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { commandFactory = typemoq.Mock.ofType(); docMgr = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IDiagnosticsCommandFactory))) + .setup((s) => s.get(typemoq.It.isValue(IDiagnosticsCommandFactory))) .returns(() => commandFactory.object); configService = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IConfigurationService))) + .setup((s) => s.get(typemoq.It.isValue(IConfigurationService))) .returns(() => configService.object); helper = typemoq.Mock.ofType(); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IInterpreterHelper))).returns(() => helper.object); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IInterpreterHelper))).returns(() => helper.object); workspaceService = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IWorkspaceService))) + .setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); diagnosticService = new (class extends InvalidPythonPathInDebuggerService { @@ -91,7 +91,7 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { ]) { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => code) .verifiable(typemoq.Times.atLeastOnce()); @@ -103,7 +103,7 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { test('Can not handle non-InvalidPythonPathInDebugger diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => 'Something Else' as any) .verifiable(typemoq.Times.atLeastOnce()); @@ -118,12 +118,12 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { test('InvalidPythonPathInDebuggerSettings diagnostic should display one option to with a command', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.InvalidPythonPathInDebuggerSettingsDiagnostic) .verifiable(typemoq.Times.atLeastOnce()); const interpreterSelectionCommand = typemoq.Mock.ofType(); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -133,7 +133,7 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { ) .returns(() => interpreterSelectionCommand.object) .verifiable(typemoq.Times.once()); - messageHandler.setup(m => m.handle(typemoq.It.isAny(), typemoq.It.isAny())).verifiable(typemoq.Times.once()); + messageHandler.setup((m) => m.handle(typemoq.It.isAny(), typemoq.It.isAny())).verifiable(typemoq.Times.once()); await diagnosticService.handle([diagnostic.object]); @@ -144,16 +144,16 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { test('InvalidPythonPathInDebuggerSettings diagnostic should display message once if invoked twice', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.InvalidPythonPathInDebuggerSettingsDiagnostic) .verifiable(typemoq.Times.atLeastOnce()); diagnostic - .setup(d => d.invokeHandler) + .setup((d) => d.invokeHandler) .returns(() => 'default') .verifiable(typemoq.Times.atLeastOnce()); const interpreterSelectionCommand = typemoq.Mock.ofType(); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -164,7 +164,7 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { .returns(() => interpreterSelectionCommand.object) .verifiable(typemoq.Times.exactly(1)); messageHandler - .setup(m => m.handle(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((m) => m.handle(typemoq.It.isAny(), typemoq.It.isAny())) .verifiable(typemoq.Times.exactly(1)); await diagnosticService.handle([diagnostic.object]); @@ -177,16 +177,16 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { test('InvalidPythonPathInDebuggerSettings diagnostic should display message twice if invoked twice', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.InvalidPythonPathInDebuggerSettingsDiagnostic) .verifiable(typemoq.Times.atLeastOnce()); diagnostic - .setup(d => d.invokeHandler) + .setup((d) => d.invokeHandler) .returns(() => 'always') .verifiable(typemoq.Times.atLeastOnce()); const interpreterSelectionCommand = typemoq.Mock.ofType(); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -197,7 +197,7 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { .returns(() => interpreterSelectionCommand.object) .verifiable(typemoq.Times.exactly(2)); messageHandler - .setup(m => m.handle(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((m) => m.handle(typemoq.It.isAny(), typemoq.It.isAny())) .verifiable(typemoq.Times.exactly(2)); await diagnosticService.handle([diagnostic.object]); @@ -211,11 +211,11 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { const diagnostic = typemoq.Mock.ofType(); let options: MessageCommandPrompt | undefined; diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.InvalidPythonPathInDebuggerLaunchDiagnostic) .verifiable(typemoq.Times.atLeastOnce()); messageHandler - .setup(m => m.handle(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((m) => m.handle(typemoq.It.isAny(), typemoq.It.isAny())) .callback((_, opts: MessageCommandPrompt) => (options = opts)) .verifiable(typemoq.Times.once()); @@ -232,15 +232,15 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { const settings = typemoq.Mock.ofType(); settings - .setup(s => s.pythonPath) + .setup((s) => s.pythonPath) .returns(() => 'p') .verifiable(typemoq.Times.once()); configService - .setup(c => c.getSettings(typemoq.It.isAny())) + .setup((c) => c.getSettings(typemoq.It.isAny())) .returns(() => settings.object) .verifiable(typemoq.Times.once()); helper - .setup(h => h.getInterpreterInformation(typemoq.It.isValue('p'))) + .setup((h) => h.getInterpreterInformation(typemoq.It.isValue('p'))) .returns(() => Promise.resolve({})) .verifiable(typemoq.Times.once()); @@ -255,11 +255,11 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { const pythonPath = '${workspaceFolder}/venv/bin/python'; workspaceService - .setup(c => c.getWorkspaceFolder(typemoq.It.isAny())) + .setup((c) => c.getWorkspaceFolder(typemoq.It.isAny())) .returns(() => undefined) .verifiable(typemoq.Times.never()); helper - .setup(h => h.getInterpreterInformation(typemoq.It.isAny())) + .setup((h) => h.getInterpreterInformation(typemoq.It.isAny())) .returns(() => Promise.resolve({})) .verifiable(typemoq.Times.once()); @@ -275,11 +275,11 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { const expectedPath = `${workspaceFolder.uri.fsPath}/venv/bin/python`; workspaceService - .setup(c => c.getWorkspaceFolder(typemoq.It.isAny())) + .setup((c) => c.getWorkspaceFolder(typemoq.It.isAny())) .returns(() => workspaceFolder) .verifiable(typemoq.Times.once()); helper - .setup(h => h.getInterpreterInformation(typemoq.It.isValue(expectedPath))) + .setup((h) => h.getInterpreterInformation(typemoq.It.isValue(expectedPath))) .returns(() => Promise.resolve({})) .verifiable(typemoq.Times.once()); @@ -299,11 +299,11 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { process.env.XYZ123 = 'something/else'; const expectedPath = `${process.env.XYZ123}/venv/bin/python`; workspaceService - .setup(c => c.getWorkspaceFolder(typemoq.It.isAny())) + .setup((c) => c.getWorkspaceFolder(typemoq.It.isAny())) .returns(() => undefined) .verifiable(typemoq.Times.once()); helper - .setup(h => h.getInterpreterInformation(typemoq.It.isValue(expectedPath))) + .setup((h) => h.getInterpreterInformation(typemoq.It.isValue(expectedPath))) .returns(() => Promise.resolve({})) .verifiable(typemoq.Times.once()); @@ -318,15 +318,15 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { const settings = typemoq.Mock.ofType(); settings - .setup(s => s.pythonPath) + .setup((s) => s.pythonPath) .returns(() => 'p') .verifiable(typemoq.Times.once()); configService - .setup(c => c.getSettings(typemoq.It.isAny())) + .setup((c) => c.getSettings(typemoq.It.isAny())) .returns(() => settings.object) .verifiable(typemoq.Times.once()); helper - .setup(h => h.getInterpreterInformation(typemoq.It.isValue('p'))) + .setup((h) => h.getInterpreterInformation(typemoq.It.isValue('p'))) .returns(() => Promise.resolve({})) .verifiable(typemoq.Times.once()); @@ -342,11 +342,11 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { const settings = typemoq.Mock.ofType(); configService - .setup(c => c.getSettings(typemoq.It.isAny())) + .setup((c) => c.getSettings(typemoq.It.isAny())) .returns(() => settings.object) .verifiable(typemoq.Times.never()); helper - .setup(h => h.getInterpreterInformation(typemoq.It.isValue(pythonPath))) + .setup((h) => h.getInterpreterInformation(typemoq.It.isValue(pythonPath))) .returns(() => Promise.resolve({})) .verifiable(typemoq.Times.once()); @@ -360,11 +360,11 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { const pythonPath = path.join('a', 'b'); const settings = typemoq.Mock.ofType(); configService - .setup(c => c.getSettings(typemoq.It.isAny())) + .setup((c) => c.getSettings(typemoq.It.isAny())) .returns(() => settings.object) .verifiable(typemoq.Times.never()); let handleInvoked = false; - diagnosticService.handle = diagnostics => { + diagnosticService.handle = (diagnostics) => { if ( diagnostics.length !== 0 && diagnostics[0].code === DiagnosticCodes.InvalidPythonPathInDebuggerLaunchDiagnostic @@ -374,7 +374,7 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { return Promise.resolve(); }; helper - .setup(h => h.getInterpreterInformation(typemoq.It.isValue(pythonPath))) + .setup((h) => h.getInterpreterInformation(typemoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(undefined)) .verifiable(typemoq.Times.once()); @@ -388,15 +388,15 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { const pythonPath = undefined; const settings = typemoq.Mock.ofType(); settings - .setup(s => s.pythonPath) + .setup((s) => s.pythonPath) .returns(() => 'p') .verifiable(typemoq.Times.once()); configService - .setup(c => c.getSettings(typemoq.It.isAny())) + .setup((c) => c.getSettings(typemoq.It.isAny())) .returns(() => settings.object) .verifiable(typemoq.Times.once()); let handleInvoked = false; - diagnosticService.handle = diagnostics => { + diagnosticService.handle = (diagnostics) => { if ( diagnostics.length !== 0 && diagnostics[0].code === DiagnosticCodes.InvalidPythonPathInDebuggerSettingsDiagnostic @@ -406,7 +406,7 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { return Promise.resolve(); }; helper - .setup(h => h.getInterpreterInformation(typemoq.It.isValue('p'))) + .setup((h) => h.getInterpreterInformation(typemoq.It.isValue('p'))) .returns(() => Promise.resolve(undefined)) .verifiable(typemoq.Times.once()); diff --git a/src/test/application/diagnostics/checks/lsNotSupported.unit.test.ts b/src/test/application/diagnostics/checks/lsNotSupported.unit.test.ts index 772649fbaf77..4f537b62c5bf 100644 --- a/src/test/application/diagnostics/checks/lsNotSupported.unit.test.ts +++ b/src/test/application/diagnostics/checks/lsNotSupported.unit.test.ts @@ -40,13 +40,13 @@ suite('Application Diagnostics - Checks LS not supported', () => { messageHandler = TypeMoq.Mock.ofType>(); lsCompatibility = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IDiagnosticFilterService))) + .setup((s) => s.get(TypeMoq.It.isValue(IDiagnosticFilterService))) .returns(() => filterService.object); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IDiagnosticsCommandFactory))) + .setup((s) => s.get(TypeMoq.It.isValue(IDiagnosticsCommandFactory))) .returns(() => commandFactory.object); serviceContainer - .setup(s => + .setup((s) => s.get( TypeMoq.It.isValue(IDiagnosticHandlerService), TypeMoq.It.isValue(DiagnosticCommandPromptHandlerServiceId) @@ -55,9 +55,9 @@ suite('Application Diagnostics - Checks LS not supported', () => { .returns(() => messageHandler.object); const workspaceService = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((s) => s.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); - workspaceService.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); + workspaceService.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); diagnosticService = new (class extends LSNotSupportedDiagnosticService { public _clear() { @@ -73,12 +73,12 @@ suite('Application Diagnostics - Checks LS not supported', () => { let options: MessageCommandPrompt | undefined; const diagnostic = TypeMoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.LSNotSupportedDiagnostic) .verifiable(TypeMoq.Times.atLeastOnce()); const launchBrowserCommand = TypeMoq.Mock.ofType(); commandFactory - .setup(f => + .setup((f) => f.createCommand( TypeMoq.It.isAny(), TypeMoq.It.isObjectWith>({ type: 'launch' }) @@ -88,7 +88,7 @@ suite('Application Diagnostics - Checks LS not supported', () => { .verifiable(TypeMoq.Times.once()); const alwaysIgnoreCommand = TypeMoq.Mock.ofType(); commandFactory - .setup(f => + .setup((f) => f.createCommand( TypeMoq.It.isAny(), TypeMoq.It.isObjectWith>({ @@ -100,7 +100,7 @@ suite('Application Diagnostics - Checks LS not supported', () => { .returns(() => alwaysIgnoreCommand.object) .verifiable(TypeMoq.Times.once()); messageHandler - .setup(m => m.handle(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((m) => m.handle(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .callback((_, opts: MessageCommandPrompt) => (options = opts)) .verifiable(TypeMoq.Times.once()); @@ -116,17 +116,17 @@ suite('Application Diagnostics - Checks LS not supported', () => { const diagnostic = TypeMoq.Mock.ofType(); filterService - .setup(f => f.shouldIgnoreDiagnostic(TypeMoq.It.isValue(DiagnosticCodes.LSNotSupportedDiagnostic))) + .setup((f) => f.shouldIgnoreDiagnostic(TypeMoq.It.isValue(DiagnosticCodes.LSNotSupportedDiagnostic))) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.LSNotSupportedDiagnostic) .verifiable(TypeMoq.Times.atLeastOnce()); commandFactory - .setup(f => f.createCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((f) => f.createCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .verifiable(TypeMoq.Times.never()); - messageHandler.setup(m => m.handle(TypeMoq.It.isAny(), TypeMoq.It.isAny())).verifiable(TypeMoq.Times.never()); + messageHandler.setup((m) => m.handle(TypeMoq.It.isAny(), TypeMoq.It.isAny())).verifiable(TypeMoq.Times.never()); await diagnosticService.handle([diagnostic.object]); @@ -139,7 +139,7 @@ suite('Application Diagnostics - Checks LS not supported', () => { test('LSNotSupportedDiagnosticService can handle LSNotSupported diagnostics', async () => { const diagnostic = TypeMoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.LSNotSupportedDiagnostic) .verifiable(TypeMoq.Times.atLeastOnce()); const canHandle = await diagnosticService.canHandle(diagnostic.object); @@ -149,7 +149,7 @@ suite('Application Diagnostics - Checks LS not supported', () => { test('LSNotSupportedDiagnosticService can not handle non-LSNotSupported diagnostics', async () => { const diagnostic = TypeMoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => 'Something Else' as any) .verifiable(TypeMoq.Times.atLeastOnce()); const canHandle = await diagnosticService.canHandle(diagnostic.object); diff --git a/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts b/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts index 8f10ed12565b..49523ed18fe0 100644 --- a/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts +++ b/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts @@ -51,7 +51,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { serviceContainer = typemoq.Mock.ofType(); messageHandler = typemoq.Mock.ofType>(); serviceContainer - .setup(s => + .setup((s) => s.get( typemoq.It.isValue(IDiagnosticHandlerService), typemoq.It.isValue(DiagnosticCommandPromptHandlerServiceId) @@ -60,31 +60,33 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { .returns(() => messageHandler.object); commandFactory = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IDiagnosticsCommandFactory))) + .setup((s) => s.get(typemoq.It.isValue(IDiagnosticsCommandFactory))) .returns(() => commandFactory.object); settings = typemoq.Mock.ofType(); - settings.setup(s => s.pythonPath).returns(() => pythonPath); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); const configService = typemoq.Mock.ofType(); - configService.setup(c => c.getSettings(typemoq.It.isAny())).returns(() => settings.object); + configService.setup((c) => c.getSettings(typemoq.It.isAny())).returns(() => settings.object); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IConfigurationService))) + .setup((s) => s.get(typemoq.It.isValue(IConfigurationService))) .returns(() => configService.object); interpreterService = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IInterpreterService))) + .setup((s) => s.get(typemoq.It.isValue(IInterpreterService))) .returns(() => interpreterService.object); platformService = typemoq.Mock.ofType(); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IPlatformService))).returns(() => platformService.object); + serviceContainer + .setup((s) => s.get(typemoq.It.isValue(IPlatformService))) + .returns(() => platformService.object); helper = typemoq.Mock.ofType(); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IInterpreterHelper))).returns(() => helper.object); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IDisposableRegistry))).returns(() => []); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IInterpreterHelper))).returns(() => helper.object); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IDisposableRegistry))).returns(() => []); filterService = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IDiagnosticFilterService))) + .setup((s) => s.get(typemoq.It.isValue(IDiagnosticFilterService))) .returns(() => filterService.object); platformService - .setup(p => p.isMac) + .setup((p) => p.isMac) .returns(() => true) .verifiable(typemoq.Times.once()); return serviceContainer.object; @@ -111,7 +113,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { ]) { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => code) .verifiable(typemoq.Times.atLeastOnce()); @@ -123,7 +125,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { test('Can not handle non-InvalidPythonPathInterpreter diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => 'Something Else' as any) .verifiable(typemoq.Times.atLeastOnce()); @@ -134,7 +136,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { test('Should return empty diagnostics if not a Mac', async () => { platformService.reset(); platformService - .setup(p => p.isMac) + .setup((p) => p.isMac) .returns(() => true) .verifiable(typemoq.Times.once()); @@ -144,7 +146,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { }); test('Should return empty diagnostics if installer check is disabled', async () => { settings - .setup(s => s.disableInstallationChecks) + .setup((s) => s.disableInstallationChecks) .returns(() => true) .verifiable(typemoq.Times.once()); @@ -155,25 +157,25 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { }); test('Should return empty diagnostics if there are interpreters, one is selected, and platform is not mac', async () => { settings - .setup(s => s.disableInstallationChecks) + .setup((s) => s.disableInstallationChecks) .returns(() => false) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.hasInterpreters) + .setup((i) => i.hasInterpreters) .returns(() => Promise.resolve(true)) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.getInterpreters(typemoq.It.isAny())) + .setup((i) => i.getInterpreters(typemoq.It.isAny())) .returns(() => Promise.resolve([{} as any])) .verifiable(typemoq.Times.never()); interpreterService - .setup(i => i.getActiveInterpreter(typemoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(typemoq.It.isAny())) .returns(() => { return Promise.resolve({ type: InterpreterType.Unknown } as any); }) .verifiable(typemoq.Times.once()); platformService - .setup(i => i.isMac) + .setup((i) => i.isMac) .returns(() => false) .verifiable(typemoq.Times.once()); @@ -185,29 +187,29 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { }); test('Should return empty diagnostics if there are interpreters, platform is mac and selected interpreter is not default mac interpreter', async () => { settings - .setup(s => s.disableInstallationChecks) + .setup((s) => s.disableInstallationChecks) .returns(() => false) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.hasInterpreters) + .setup((i) => i.hasInterpreters) .returns(() => Promise.resolve(true)) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.getInterpreters(typemoq.It.isAny())) + .setup((i) => i.getInterpreters(typemoq.It.isAny())) .returns(() => Promise.resolve([{} as any])) .verifiable(typemoq.Times.never()); interpreterService - .setup(i => i.getActiveInterpreter(typemoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(typemoq.It.isAny())) .returns(() => { return Promise.resolve({ type: InterpreterType.Unknown } as any); }) .verifiable(typemoq.Times.once()); platformService - .setup(i => i.isMac) + .setup((i) => i.isMac) .returns(() => true) .verifiable(typemoq.Times.once()); helper - .setup(i => i.isMacDefaultPythonPath(typemoq.It.isAny())) + .setup((i) => i.isMacDefaultPythonPath(typemoq.It.isAny())) .returns(() => false) .verifiable(typemoq.Times.once()); @@ -220,25 +222,25 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { }); test('Should return diagnostic if there are no other interpreters, platform is mac and selected interpreter is default mac interpreter', async () => { settings - .setup(s => s.disableInstallationChecks) + .setup((s) => s.disableInstallationChecks) .returns(() => false) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.getInterpreters(typemoq.It.isAny())) + .setup((i) => i.getInterpreters(typemoq.It.isAny())) .returns(() => Promise.resolve([{ path: pythonPath } as any, { path: pythonPath } as any])) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.getActiveInterpreter(typemoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(typemoq.It.isAny())) .returns(() => { return Promise.resolve({ type: InterpreterType.Unknown } as any); }) .verifiable(typemoq.Times.once()); platformService - .setup(i => i.isMac) + .setup((i) => i.isMac) .returns(() => true) .verifiable(typemoq.Times.once()); helper - .setup(i => i.isMacDefaultPythonPath(typemoq.It.isValue(pythonPath))) + .setup((i) => i.isMacDefaultPythonPath(typemoq.It.isValue(pythonPath))) .returns(() => true) .verifiable(typemoq.Times.atLeastOnce()); @@ -260,11 +262,11 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { test('Should return diagnostic if there are other interpreters, platform is mac and selected interpreter is default mac interpreter', async () => { const nonMacStandardInterpreter = 'Non Mac Std Interpreter'; settings - .setup(s => s.disableInstallationChecks) + .setup((s) => s.disableInstallationChecks) .returns(() => false) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.getInterpreters(typemoq.It.isAny())) + .setup((i) => i.getInterpreters(typemoq.It.isAny())) .returns(() => Promise.resolve([ { path: pythonPath } as any, @@ -274,19 +276,19 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { ) .verifiable(typemoq.Times.once()); platformService - .setup(i => i.isMac) + .setup((i) => i.isMac) .returns(() => true) .verifiable(typemoq.Times.once()); helper - .setup(i => i.isMacDefaultPythonPath(typemoq.It.isValue(pythonPath))) + .setup((i) => i.isMacDefaultPythonPath(typemoq.It.isValue(pythonPath))) .returns(() => true) .verifiable(typemoq.Times.atLeastOnce()); helper - .setup(i => i.isMacDefaultPythonPath(typemoq.It.isValue(nonMacStandardInterpreter))) + .setup((i) => i.isMacDefaultPythonPath(typemoq.It.isValue(nonMacStandardInterpreter))) .returns(() => false) .verifiable(typemoq.Times.atLeastOnce()); interpreterService - .setup(i => i.getActiveInterpreter(typemoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(typemoq.It.isAny())) .returns(() => { return Promise.resolve({ type: InterpreterType.Unknown } as any); }) @@ -316,12 +318,12 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { const cmdIgnore = ({} as any) as IDiagnosticCommand; let messagePrompt: MessageCommandPrompt | undefined; messageHandler - .setup(i => i.handle(typemoq.It.isValue(diagnostic), typemoq.It.isAny())) + .setup((i) => i.handle(typemoq.It.isValue(diagnostic), typemoq.It.isAny())) .callback((_d, p: MessageCommandPrompt) => (messagePrompt = p)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -332,7 +334,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { .returns(() => cmd) .verifiable(typemoq.Times.once()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -364,12 +366,12 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { const cmdIgnore = ({} as any) as IDiagnosticCommand; let messagePrompt: MessageCommandPrompt | undefined; messageHandler - .setup(i => i.handle(typemoq.It.isValue(diagnostic), typemoq.It.isAny())) + .setup((i) => i.handle(typemoq.It.isValue(diagnostic), typemoq.It.isAny())) .callback((_d, p: MessageCommandPrompt) => (messagePrompt = p)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -381,7 +383,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { .returns(() => cmdLearn) .verifiable(typemoq.Times.once()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -393,7 +395,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { .returns(() => cmdDownload) .verifiable(typemoq.Times.once()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -423,7 +425,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { ); filterService - .setup(f => + .setup((f) => f.shouldIgnoreDiagnostic( typemoq.It.isValue(DiagnosticCodes.MacInterpreterSelectedAndNoOtherInterpretersDiagnostic) ) @@ -431,10 +433,10 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { .returns(() => Promise.resolve(true)) .verifiable(typemoq.Times.once()); commandFactory - .setup(f => f.createCommand(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((f) => f.createCommand(typemoq.It.isAny(), typemoq.It.isAny())) .verifiable(typemoq.Times.never()); messageHandler - .setup(f => f.handle(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((f) => f.handle(typemoq.It.isAny(), typemoq.It.isAny())) .verifiable(typemoq.Times.never()); await diagnosticService.handle([diagnostic]); @@ -463,7 +465,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { onDidChangeConfiguration: (cb: (e: ConfigurationChangeEvent) => Promise) => (callbackHandler = cb) } as any; const serviceContainerObject = createContainer(); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IWorkspaceService))).returns(() => workspaceService); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))).returns(() => workspaceService); diagnosticService = new (class extends InvalidMacPythonInterpreterService { protected async onDidChangeConfiguration(_event: ConfigurationChangeEvent) { invoked = true; @@ -477,7 +479,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { let invoked = false; const workspaceService = { onDidChangeConfiguration: noop } as any; const serviceContainerObject = createContainer(); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IWorkspaceService))).returns(() => workspaceService); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))).returns(() => workspaceService); diagnosticService = new (class extends InvalidMacPythonInterpreterService { protected async onDidChangeConfiguration(_event: ConfigurationChangeEvent) { invoked = true; @@ -492,15 +494,15 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { const serviceContainerObject = createContainer(); let diagnoseInvocationCount = 0; workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(typemoq.Times.once()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [{ uri: '' }] as any) .verifiable(typemoq.Times.once()); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IWorkspaceService))) + .setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); const diagnosticSvc = new (class extends InvalidMacPythonInterpreterService { constructor( @@ -525,7 +527,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { ); event - .setup(e => e.affectsConfiguration(typemoq.It.isValue('python.pythonPath'), typemoq.It.isAny())) + .setup((e) => e.affectsConfiguration(typemoq.It.isValue('python.pythonPath'), typemoq.It.isAny())) .returns(() => true) .verifiable(typemoq.Times.atLeastOnce()); @@ -544,15 +546,15 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { const serviceContainerObject = createContainer(); let diagnoseInvocationCount = 0; workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(typemoq.Times.once()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [{ uri: '' }] as any) .verifiable(typemoq.Times.once()); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IWorkspaceService))) + .setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); const diagnosticSvc = new (class extends InvalidMacPythonInterpreterService { constructor( @@ -577,7 +579,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { ); event - .setup(e => e.affectsConfiguration(typemoq.It.isValue('python.pythonPath'), typemoq.It.isAny())) + .setup((e) => e.affectsConfiguration(typemoq.It.isValue('python.pythonPath'), typemoq.It.isAny())) .returns(() => true) .verifiable(typemoq.Times.atLeastOnce()); diff --git a/src/test/application/diagnostics/checks/powerShellActivation.unit.test.ts b/src/test/application/diagnostics/checks/powerShellActivation.unit.test.ts index 8ccddb802042..fc5a1bb6a35e 100644 --- a/src/test/application/diagnostics/checks/powerShellActivation.unit.test.ts +++ b/src/test/application/diagnostics/checks/powerShellActivation.unit.test.ts @@ -42,12 +42,14 @@ suite('Application Diagnostics - PowerShell Activation', () => { setup(() => { const serviceContainer = typemoq.Mock.ofType(); platformService = typemoq.Mock.ofType(); - platformService.setup(p => p.pathVariableName).returns(() => pathVariableName); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IPlatformService))).returns(() => platformService.object); + platformService.setup((p) => p.pathVariableName).returns(() => pathVariableName); + serviceContainer + .setup((s) => s.get(typemoq.It.isValue(IPlatformService))) + .returns(() => platformService.object); messageHandler = typemoq.Mock.ofType>(); serviceContainer - .setup(s => + .setup((s) => s.get( typemoq.It.isValue(IDiagnosticHandlerService), typemoq.It.isValue(DiagnosticCommandPromptHandlerServiceId) @@ -56,33 +58,33 @@ suite('Application Diagnostics - PowerShell Activation', () => { .returns(() => messageHandler.object); appEnv = typemoq.Mock.ofType(); - appEnv.setup(a => a.extensionName).returns(() => extensionName); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IApplicationEnvironment))).returns(() => appEnv.object); + appEnv.setup((a) => a.extensionName).returns(() => extensionName); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IApplicationEnvironment))).returns(() => appEnv.object); filterService = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IDiagnosticFilterService))) + .setup((s) => s.get(typemoq.It.isValue(IDiagnosticFilterService))) .returns(() => filterService.object); commandFactory = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IDiagnosticsCommandFactory))) + .setup((s) => s.get(typemoq.It.isValue(IDiagnosticsCommandFactory))) .returns(() => commandFactory.object); const currentProc = typemoq.Mock.ofType(); procEnv = typemoq.Mock.ofType(); - currentProc.setup(p => p.env).returns(() => procEnv.object); - serviceContainer.setup(s => s.get(typemoq.It.isValue(ICurrentProcess))).returns(() => currentProc.object); + currentProc.setup((p) => p.env).returns(() => procEnv.object); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(ICurrentProcess))).returns(() => currentProc.object); const pathUtils = typemoq.Mock.ofType(); - pathUtils.setup(p => p.delimiter).returns(() => pathDelimiter); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IPathUtils))).returns(() => pathUtils.object); + pathUtils.setup((p) => p.delimiter).returns(() => pathDelimiter); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IPathUtils))).returns(() => pathUtils.object); const workspaceService = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IWorkspaceService))) + .setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); - workspaceService.setup(w => w.getWorkspaceFolder(typemoq.It.isAny())).returns(() => undefined); + workspaceService.setup((w) => w.getWorkspaceFolder(typemoq.It.isAny())).returns(() => undefined); diagnosticService = new (class extends PowerShellActivationHackDiagnosticsService { public _clear() { @@ -97,7 +99,7 @@ suite('Application Diagnostics - PowerShell Activation', () => { test('Can handle PowerShell diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.EnvironmentActivationInPowerShellWithBatchFilesNotSupportedDiagnostic) .verifiable(typemoq.Times.atLeastOnce()); @@ -108,7 +110,7 @@ suite('Application Diagnostics - PowerShell Activation', () => { test('Can not handle non-EnvPathVariable diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => 'Something Else' as any) .verifiable(typemoq.Times.atLeastOnce()); @@ -124,12 +126,12 @@ suite('Application Diagnostics - PowerShell Activation', () => { const diagnostic = typemoq.Mock.ofType(); let options: MessageCommandPrompt | undefined; diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => DiagnosticCodes.EnvironmentActivationInPowerShellWithBatchFilesNotSupportedDiagnostic) .verifiable(typemoq.Times.atLeastOnce()); const alwaysIgnoreCommand = typemoq.Mock.ofType(); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -142,7 +144,7 @@ suite('Application Diagnostics - PowerShell Activation', () => { .verifiable(typemoq.Times.once()); const launchBrowserCommand = typemoq.Mock.ofType(); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ type: 'launch' }) @@ -151,7 +153,7 @@ suite('Application Diagnostics - PowerShell Activation', () => { .returns(() => launchBrowserCommand.object) .verifiable(typemoq.Times.once()); messageHandler - .setup(m => m.handle(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((m) => m.handle(typemoq.It.isAny(), typemoq.It.isAny())) .callback((_, opts: MessageCommandPrompt) => (options = opts)) .verifiable(typemoq.Times.once()); diff --git a/src/test/application/diagnostics/checks/pythonInterpreter.unit.test.ts b/src/test/application/diagnostics/checks/pythonInterpreter.unit.test.ts index a6d04568da27..579b4b9d6994 100644 --- a/src/test/application/diagnostics/checks/pythonInterpreter.unit.test.ts +++ b/src/test/application/diagnostics/checks/pythonInterpreter.unit.test.ts @@ -46,7 +46,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { serviceContainer = typemoq.Mock.ofType(); messageHandler = typemoq.Mock.ofType>(); serviceContainer - .setup(s => + .setup((s) => s.get( typemoq.It.isValue(IDiagnosticHandlerService), typemoq.It.isValue(DiagnosticCommandPromptHandlerServiceId) @@ -55,24 +55,26 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { .returns(() => messageHandler.object); commandFactory = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IDiagnosticsCommandFactory))) + .setup((s) => s.get(typemoq.It.isValue(IDiagnosticsCommandFactory))) .returns(() => commandFactory.object); settings = typemoq.Mock.ofType(); - settings.setup(s => s.pythonPath).returns(() => pythonPath); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); const configService = typemoq.Mock.ofType(); - configService.setup(c => c.getSettings(typemoq.It.isAny())).returns(() => settings.object); + configService.setup((c) => c.getSettings(typemoq.It.isAny())).returns(() => settings.object); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IConfigurationService))) + .setup((s) => s.get(typemoq.It.isValue(IConfigurationService))) .returns(() => configService.object); interpreterService = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IInterpreterService))) + .setup((s) => s.get(typemoq.It.isValue(IInterpreterService))) .returns(() => interpreterService.object); platformService = typemoq.Mock.ofType(); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IPlatformService))).returns(() => platformService.object); + serviceContainer + .setup((s) => s.get(typemoq.It.isValue(IPlatformService))) + .returns(() => platformService.object); helper = typemoq.Mock.ofType(); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IInterpreterHelper))).returns(() => helper.object); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IDisposableRegistry))).returns(() => []); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IInterpreterHelper))).returns(() => helper.object); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IDisposableRegistry))).returns(() => []); return serviceContainer.object; } suite('Diagnostics', () => { @@ -97,7 +99,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { ]) { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => code) .verifiable(typemoq.Times.atLeastOnce()); @@ -109,7 +111,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { test('Can not handle non-InvalidPythonPathInterpreter diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => 'Something Else' as any) .verifiable(typemoq.Times.atLeastOnce()); @@ -119,7 +121,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { }); test('Should return empty diagnostics if installer check is disabled', async () => { settings - .setup(s => s.disableInstallationChecks) + .setup((s) => s.disableInstallationChecks) .returns(() => true) .verifiable(typemoq.Times.once()); @@ -129,11 +131,11 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { }); test('Should return diagnostics if there are no interpreters', async () => { settings - .setup(s => s.disableInstallationChecks) + .setup((s) => s.disableInstallationChecks) .returns(() => false) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.hasInterpreters) + .setup((i) => i.hasInterpreters) .returns(() => Promise.resolve(false)) .verifiable(typemoq.Times.once()); @@ -147,15 +149,15 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { }); test('Should return invalid diagnostics if there are interpreters but no current interpreter', async () => { settings - .setup(s => s.disableInstallationChecks) + .setup((s) => s.disableInstallationChecks) .returns(() => false) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.hasInterpreters) + .setup((i) => i.hasInterpreters) .returns(() => Promise.resolve(true)) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.getActiveInterpreter(typemoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(typemoq.It.isAny())) .returns(() => { return Promise.resolve(undefined); }) @@ -176,15 +178,15 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { }); test('Should return empty diagnostics if there are interpreters and a current interpreter', async () => { settings - .setup(s => s.disableInstallationChecks) + .setup((s) => s.disableInstallationChecks) .returns(() => false) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.hasInterpreters) + .setup((i) => i.hasInterpreters) .returns(() => Promise.resolve(true)) .verifiable(typemoq.Times.once()); interpreterService - .setup(i => i.getActiveInterpreter(typemoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(typemoq.It.isAny())) .returns(() => { return Promise.resolve({ type: InterpreterType.Unknown } as any); }) @@ -203,12 +205,12 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { const cmd = ({} as any) as IDiagnosticCommand; let messagePrompt: MessageCommandPrompt | undefined; messageHandler - .setup(i => i.handle(typemoq.It.isValue(diagnostic), typemoq.It.isAny())) + .setup((i) => i.handle(typemoq.It.isValue(diagnostic), typemoq.It.isAny())) .callback((_d, p: MessageCommandPrompt) => (messagePrompt = p)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ type: 'launch' }) @@ -232,12 +234,12 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { const cmd = ({} as any) as IDiagnosticCommand; let messagePrompt: MessageCommandPrompt | undefined; messageHandler - .setup(i => i.handle(typemoq.It.isValue(diagnostic), typemoq.It.isAny())) + .setup((i) => i.handle(typemoq.It.isValue(diagnostic), typemoq.It.isAny())) .callback((_d, p: MessageCommandPrompt) => (messagePrompt = p)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -265,12 +267,12 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { const cmd = ({} as any) as IDiagnosticCommand; let messagePrompt: MessageCommandPrompt | undefined; messageHandler - .setup(i => i.handle(typemoq.It.isValue(diagnostic), typemoq.It.isAny())) + .setup((i) => i.handle(typemoq.It.isValue(diagnostic), typemoq.It.isAny())) .callback((_d, p: MessageCommandPrompt) => (messagePrompt = p)) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -295,12 +297,12 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { const cmd = ({} as any) as IDiagnosticCommand; messageHandler - .setup(i => i.handle(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((i) => i.handle(typemoq.It.isAny(), typemoq.It.isAny())) .callback((_d, p: MessageCommandPrompt) => p) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.never()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -326,14 +328,14 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { InvalidPythonInterpreterService >; - diagnosticServiceMock.setup(f => f.canHandle(typemoq.It.isAny())).returns(() => Promise.resolve(false)); + diagnosticServiceMock.setup((f) => f.canHandle(typemoq.It.isAny())).returns(() => Promise.resolve(false)); messageHandler - .setup(i => i.handle(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((i) => i.handle(typemoq.It.isAny(), typemoq.It.isAny())) .callback((_d, p: MessageCommandPrompt) => p) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.never()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ @@ -354,12 +356,12 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { const cmd = ({} as any) as IDiagnosticCommand; messageHandler - .setup(i => i.handle(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((i) => i.handle(typemoq.It.isAny(), typemoq.It.isAny())) .callback((_d, p: MessageCommandPrompt) => p) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.never()); commandFactory - .setup(f => + .setup((f) => f.createCommand( typemoq.It.isAny(), typemoq.It.isObjectWith>({ diff --git a/src/test/application/diagnostics/checks/updateTestSettings.unit.test.ts b/src/test/application/diagnostics/checks/updateTestSettings.unit.test.ts index 50b2832d5567..8699426016bc 100644 --- a/src/test/application/diagnostics/checks/updateTestSettings.unit.test.ts +++ b/src/test/application/diagnostics/checks/updateTestSettings.unit.test.ts @@ -42,7 +42,7 @@ suite('Application Diagnostics - Check Test Settings', () => { teardown(() => { sandbox.restore(); }); - [Uri.file(__filename), undefined].forEach(resource => { + [Uri.file(__filename), undefined].forEach((resource) => { const resourceTitle = resource ? '(with a resource)' : '(without a resource)'; test(`activate method invokes UpdateTestSettings ${resourceTitle}`, async () => { @@ -83,7 +83,7 @@ suite('Application Diagnostics - Check Test Settings', () => { assert.deepEqual(files, []); verify(workspace.getWorkspaceFolder(resource)).once(); }); - test(`When there is a workspace folder, then return the user settings file & workspace file ${resourceTitle}`, async function() { + test(`When there is a workspace folder, then return the user settings file & workspace file ${resourceTitle}`, async function () { if (!resource) { return this.skip(); } @@ -95,7 +95,7 @@ suite('Application Diagnostics - Check Test Settings', () => { assert.deepEqual(files, ['user.json', path.join(Uri.file('folder1').fsPath, '.vscode', 'settings.json')]); verify(workspace.getWorkspaceFolder(resource)).once(); }); - test(`When there is a workspace folder & no user file, then workspace file ${resourceTitle}`, async function() { + test(`When there is a workspace folder & no user file, then workspace file ${resourceTitle}`, async function () { if (!resource) { return this.skip(); } @@ -119,7 +119,7 @@ suite('Application Diagnostics - Check Test Settings', () => { test(`Filter files based on whether they need to be fixed ${resourceTitle}`, async () => { const getSettingsFiles = sandbox.stub(UpdateTestSettingService.prototype, 'getSettingsFiles'); const filterFiles = sandbox.stub(UpdateTestSettingService.prototype, 'doesFileNeedToBeFixed'); - filterFiles.callsFake(file => Promise.resolve(file === 'file_a' || file === 'file_c')); + filterFiles.callsFake((file) => Promise.resolve(file === 'file_a' || file === 'file_c')); getSettingsFiles.returns(['file_a', 'file_b', 'file_c', 'file_d']); diagnosticService = new UpdateTestSettingService(instance(fs), instance(appEnv), instance(workspace)); @@ -150,7 +150,7 @@ suite('Application Diagnostics - Check Test Settings', () => { expectedValue: false, contents: '{"python.pythonPath":"1234", "python.unittest.unitTestArgs":[]}' } - ].forEach(item => { + ].forEach((item) => { test(item.testTitle, async () => { when(fs.readFile(__filename)).thenResolve(item.contents); @@ -206,7 +206,7 @@ suite('Application Diagnostics - Check Test Settings', () => { expectedContents: '{"python.jediEnabled": true}', contents: '{"python.languageServer": "jedi"}' } - ].forEach(item => { + ].forEach((item) => { test(item.testTitle, async () => { when(fs.readFile(__filename)).thenResolve(item.contents); when(fs.writeFile(__filename, anything())).thenResolve(); diff --git a/src/test/application/diagnostics/commands/execVSCCommands.unit.test.ts b/src/test/application/diagnostics/commands/execVSCCommands.unit.test.ts index ad7ff7533050..56f9e51e0051 100644 --- a/src/test/application/diagnostics/commands/execVSCCommands.unit.test.ts +++ b/src/test/application/diagnostics/commands/execVSCCommands.unit.test.ts @@ -19,7 +19,7 @@ suite('Application Diagnostics - Exec VSC Commands', () => { const serviceContainer = typemoq.Mock.ofType(); commandManager = typemoq.Mock.ofType(); serviceContainer - .setup(svc => svc.get(typemoq.It.isValue(ICommandManager), typemoq.It.isAny())) + .setup((svc) => svc.get(typemoq.It.isValue(ICommandManager), typemoq.It.isAny())) .returns(() => commandManager.object); commandFactory = new DiagnosticsCommandFactory(serviceContainer.object); }); @@ -37,7 +37,7 @@ suite('Application Diagnostics - Exec VSC Commands', () => { test('Test execution of VSC Command', async () => { const diagnostic = typemoq.Mock.ofType(); commandManager - .setup(cmd => cmd.executeCommand('editor.action.formatDocument')) + .setup((cmd) => cmd.executeCommand('editor.action.formatDocument')) .returns(() => Promise.resolve(undefined)) .verifiable(typemoq.Times.once()); diff --git a/src/test/application/diagnostics/commands/ignore.unit.test.ts b/src/test/application/diagnostics/commands/ignore.unit.test.ts index 876fde872fe2..65eb73c16d12 100644 --- a/src/test/application/diagnostics/commands/ignore.unit.test.ts +++ b/src/test/application/diagnostics/commands/ignore.unit.test.ts @@ -29,15 +29,15 @@ suite('Application Diagnostics - Commands Ignore', () => { test('Invoking Command should invoke the filter Service', async () => { const filterService = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IDiagnosticFilterService))) + .setup((s) => s.get(typemoq.It.isValue(IDiagnosticFilterService))) .returns(() => filterService.object) .verifiable(typemoq.Times.once()); diagnostic - .setup(d => d.code) + .setup((d) => d.code) .returns(() => 'xyz' as any) .verifiable(typemoq.Times.once()); filterService - .setup(s => s.ignoreDiagnostic(typemoq.It.isValue('xyz'), typemoq.It.isValue(DiagnosticScope.Global))) + .setup((s) => s.ignoreDiagnostic(typemoq.It.isValue('xyz'), typemoq.It.isValue(DiagnosticScope.Global))) .verifiable(typemoq.Times.once()); await ignoreCommand.invoke(); diff --git a/src/test/application/diagnostics/commands/launchBrowser.unit.test.ts b/src/test/application/diagnostics/commands/launchBrowser.unit.test.ts index 0c9264483baf..5b85621971fc 100644 --- a/src/test/application/diagnostics/commands/launchBrowser.unit.test.ts +++ b/src/test/application/diagnostics/commands/launchBrowser.unit.test.ts @@ -23,10 +23,10 @@ suite('Application Diagnostics - Commands Launch Browser', () => { test('Invoking Command should launch the browser', async () => { const browser = typemoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IBrowserService))) + .setup((s) => s.get(typemoq.It.isValue(IBrowserService))) .returns(() => browser.object) .verifiable(typemoq.Times.once()); - browser.setup(s => s.launch(typemoq.It.isValue(url))).verifiable(typemoq.Times.once()); + browser.setup((s) => s.launch(typemoq.It.isValue(url))).verifiable(typemoq.Times.once()); await cmd.invoke(); serviceContainer.verifyAll(); diff --git a/src/test/application/diagnostics/filter.unit.test.ts b/src/test/application/diagnostics/filter.unit.test.ts index 2615061667b0..fbd23e7390da 100644 --- a/src/test/application/diagnostics/filter.unit.test.ts +++ b/src/test/application/diagnostics/filter.unit.test.ts @@ -24,7 +24,7 @@ suite('Application Diagnostics - Filter', () => { state: () => workspaceState, otherState: () => globalState } - ].forEach(item => { + ].forEach((item) => { let serviceContainer: typemoq.IMock; let filterService: IDiagnosticFilterService; @@ -36,7 +36,7 @@ suite('Application Diagnostics - Filter', () => { const stateFactory = typemoq.Mock.ofType(); stateFactory - .setup(f => + .setup((f) => f.createGlobalPersistentState( typemoq.It.isValue(FilterKeys.GlobalDiagnosticFilter), typemoq.It.isValue([]) @@ -44,7 +44,7 @@ suite('Application Diagnostics - Filter', () => { ) .returns(() => globalState.object); stateFactory - .setup(f => + .setup((f) => f.createWorkspacePersistentState( typemoq.It.isValue(FilterKeys.WorkspaceDiagnosticFilter), typemoq.It.isValue([]) @@ -52,7 +52,7 @@ suite('Application Diagnostics - Filter', () => { ) .returns(() => workspaceState.object); serviceContainer - .setup(s => s.get(typemoq.It.isValue(IPersistentStateFactory))) + .setup((s) => s.get(typemoq.It.isValue(IPersistentStateFactory))) .returns(() => stateFactory.object); filterService = new DiagnosticFilterService(serviceContainer.object); @@ -61,18 +61,18 @@ suite('Application Diagnostics - Filter', () => { test(`ignoreDiagnostic must save codes in ${item.name} Persistent State`, async () => { const code = 'xyz'; item.state() - .setup(g => g.value) + .setup((g) => g.value) .returns(() => []) .verifiable(typemoq.Times.once()); item.state() - .setup(g => g.updateValue(typemoq.It.isValue([code]))) + .setup((g) => g.updateValue(typemoq.It.isValue([code]))) .verifiable(typemoq.Times.once()); item.otherState() - .setup(g => g.value) + .setup((g) => g.value) .verifiable(typemoq.Times.never()); item.otherState() - .setup(g => g.updateValue(typemoq.It.isAny())) + .setup((g) => g.updateValue(typemoq.It.isAny())) .verifiable(typemoq.Times.never()); await filterService.ignoreDiagnostic(code, item.scope); @@ -82,11 +82,11 @@ suite('Application Diagnostics - Filter', () => { test("shouldIgnoreDiagnostic should return 'false' when code does not exist in any State", async () => { const code = 'xyz'; item.state() - .setup(g => g.value) + .setup((g) => g.value) .returns(() => []) .verifiable(typemoq.Times.once()); item.otherState() - .setup(g => g.value) + .setup((g) => g.value) .returns(() => []) .verifiable(typemoq.Times.once()); @@ -98,11 +98,11 @@ suite('Application Diagnostics - Filter', () => { test(`shouldIgnoreDiagnostic should return \'true\' when code exist in ${item.name} State`, async () => { const code = 'xyz'; item.state() - .setup(g => g.value) + .setup((g) => g.value) .returns(() => ['a', 'b', 'c', code]) .verifiable(typemoq.Times.once()); item.otherState() - .setup(g => g.value) + .setup((g) => g.value) .returns(() => []) .verifiable(typemoq.Times.once()); @@ -115,11 +115,11 @@ suite('Application Diagnostics - Filter', () => { test("shouldIgnoreDiagnostic should return 'true' when code exist in any State", async () => { const code = 'xyz'; item.state() - .setup(g => g.value) + .setup((g) => g.value) .returns(() => []) .verifiable(typemoq.Times.atLeast(0)); item.otherState() - .setup(g => g.value) + .setup((g) => g.value) .returns(() => ['a', 'b', 'c', code]) .verifiable(typemoq.Times.atLeast(0)); @@ -133,21 +133,21 @@ suite('Application Diagnostics - Filter', () => { const code = 'xyz'; const currentState = ['a', 'b', 'c']; item.state() - .setup(g => g.value) + .setup((g) => g.value) .returns(() => currentState) .verifiable(typemoq.Times.atLeastOnce()); item.state() - .setup(g => g.updateValue(typemoq.It.isAny())) - .callback(value => { + .setup((g) => g.updateValue(typemoq.It.isAny())) + .callback((value) => { expect(value).to.deep.equal(currentState.concat([code])); }) .verifiable(typemoq.Times.atLeastOnce()); item.otherState() - .setup(g => g.value) + .setup((g) => g.value) .verifiable(typemoq.Times.never()); item.otherState() - .setup(g => g.updateValue(typemoq.It.isAny())) + .setup((g) => g.updateValue(typemoq.It.isAny())) .verifiable(typemoq.Times.never()); await filterService.ignoreDiagnostic(code, item.scope); diff --git a/src/test/application/diagnostics/promptHandler.unit.test.ts b/src/test/application/diagnostics/promptHandler.unit.test.ts index cf0bc2cfe5ac..6c190e7b909b 100644 --- a/src/test/application/diagnostics/promptHandler.unit.test.ts +++ b/src/test/application/diagnostics/promptHandler.unit.test.ts @@ -30,12 +30,12 @@ suite('Application Diagnostics - PromptHandler', () => { serviceContainer = typemoq.Mock.ofType(); appShell = typemoq.Mock.ofType(); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IApplicationShell))).returns(() => appShell.object); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IApplicationShell))).returns(() => appShell.object); promptHandler = new DiagnosticCommandPromptHandlerService(serviceContainer.object); }); - getNamesAndValues(DiagnosticSeverity).forEach(severity => { + getNamesAndValues(DiagnosticSeverity).forEach((severity) => { test(`Handling a diagnositic of severity '${severity.name}' should display a message without any buttons`, async () => { const diagnostic: IDiagnostic = { code: '1' as any, @@ -48,19 +48,19 @@ suite('Application Diagnostics - PromptHandler', () => { switch (severity.value) { case DiagnosticSeverity.Error: { appShell - .setup(a => a.showErrorMessage(typemoq.It.isValue(diagnostic.message))) + .setup((a) => a.showErrorMessage(typemoq.It.isValue(diagnostic.message))) .verifiable(typemoq.Times.once()); break; } case DiagnosticSeverity.Warning: { appShell - .setup(a => a.showWarningMessage(typemoq.It.isValue(diagnostic.message))) + .setup((a) => a.showWarningMessage(typemoq.It.isValue(diagnostic.message))) .verifiable(typemoq.Times.once()); break; } default: { appShell - .setup(a => a.showInformationMessage(typemoq.It.isValue(diagnostic.message))) + .setup((a) => a.showInformationMessage(typemoq.It.isValue(diagnostic.message))) .verifiable(typemoq.Times.once()); break; } @@ -86,7 +86,7 @@ suite('Application Diagnostics - PromptHandler', () => { switch (severity.value) { case DiagnosticSeverity.Error: { appShell - .setup(a => + .setup((a) => a.showErrorMessage( typemoq.It.isValue(options.message!), typemoq.It.isValue('Yes'), @@ -98,7 +98,7 @@ suite('Application Diagnostics - PromptHandler', () => { } case DiagnosticSeverity.Warning: { appShell - .setup(a => + .setup((a) => a.showWarningMessage( typemoq.It.isValue(options.message!), typemoq.It.isValue('Yes'), @@ -110,7 +110,7 @@ suite('Application Diagnostics - PromptHandler', () => { } default: { appShell - .setup(a => + .setup((a) => a.showInformationMessage( typemoq.It.isValue(options.message!), typemoq.It.isValue('Yes'), @@ -142,12 +142,12 @@ suite('Application Diagnostics - PromptHandler', () => { ], message: 'Custom Message' }; - command.setup(c => c.invoke()).verifiable(typemoq.Times.once()); + command.setup((c) => c.invoke()).verifiable(typemoq.Times.once()); switch (severity.value) { case DiagnosticSeverity.Error: { appShell - .setup(a => + .setup((a) => a.showErrorMessage( typemoq.It.isValue(options.message!), typemoq.It.isValue('Yes'), @@ -160,7 +160,7 @@ suite('Application Diagnostics - PromptHandler', () => { } case DiagnosticSeverity.Warning: { appShell - .setup(a => + .setup((a) => a.showWarningMessage( typemoq.It.isValue(options.message!), typemoq.It.isValue('Yes'), @@ -173,7 +173,7 @@ suite('Application Diagnostics - PromptHandler', () => { } default: { appShell - .setup(a => + .setup((a) => a.showInformationMessage( typemoq.It.isValue(options.message!), typemoq.It.isValue('Yes'), diff --git a/src/test/common.ts b/src/test/common.ts index f0428e8468ed..82526b9c6590 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -245,7 +245,7 @@ export async function deleteFiles(globPattern: string) { glob(globPattern, (ex, files) => (ex ? reject(ex) : resolve(files))); }); - return Promise.all(items.map(item => fs.remove(item).catch(noop))); + return Promise.all(items.map((item) => fs.remove(item).catch(noop))); } function getPythonPath(): string { if (process.env.CI_PYTHON_PATH && fs.existsSync(process.env.CI_PYTHON_PATH)) { @@ -315,8 +315,8 @@ export async function getPythonSemVer(procService?: IProcessService): Promise new SemVer(strVersion.stdout.trim())) - .catch(err => { + .then((strVersion) => new SemVer(strVersion.stdout.trim())) + .catch((err) => { // if the call fails this should make it loudly apparent. console.error('Failed to get Python Version in getPythonSemVer', err); return undefined; @@ -343,7 +343,7 @@ export async function getPythonSemVer(procService?: IProcessService): Promise { + const isPresent = searchVersions.findIndex((ver) => { const semverChecker = coerce(ver); if (semverChecker) { if (semverChecker.compare(version) === 0) { @@ -581,7 +581,7 @@ export class FakeClock { while (this.clock.countTimers() === 0) { // Relinquish control to event loop, so other timer code will run. // We want to wait for `setTimeout` to kick in. - await new Promise(resolve => process.nextTick(resolve)); + await new Promise((resolve) => process.nextTick(resolve)); } } /** diff --git a/src/test/common/asyncDump.ts b/src/test/common/asyncDump.ts index a974c5846a55..b92b0231ced9 100644 --- a/src/test/common/asyncDump.ts +++ b/src/test/common/asyncDump.ts @@ -1,9 +1,9 @@ -'use strict'; - -//tslint:disable:no-require-imports no-var-requires -const log = require('why-is-node-running'); - -// Call this function to debug async hangs. It should print out stack traces of still running promises. -export function asyncDump() { - log(); -} +'use strict'; + +//tslint:disable:no-require-imports no-var-requires +const log = require('why-is-node-running'); + +// Call this function to debug async hangs. It should print out stack traces of still running promises. +export function asyncDump() { + log(); +} diff --git a/src/test/common/configSettings.test.ts b/src/test/common/configSettings.test.ts index 35c298aa17c8..44844516ed2d 100644 --- a/src/test/common/configSettings.test.ts +++ b/src/test/common/configSettings.test.ts @@ -13,12 +13,12 @@ const workspaceRoot = path.join(__dirname, '..', '..', '..', 'src', 'test'); suite('Configuration Settings', () => { setup(initialize); - test('Check Values', done => { + test('Check Values', (done) => { const systemVariables: SystemVariables = new SystemVariables(undefined, workspaceRoot); // tslint:disable-next-line:no-any const pythonConfig = vscode.workspace.getConfiguration('python', (null as any) as vscode.Uri); const pythonSettings = getExtensionSettings(vscode.Uri.file(workspaceRoot)); - Object.keys(pythonSettings).forEach(key => { + Object.keys(pythonSettings).forEach((key) => { let settingValue = pythonConfig.get(key, 'Not a config'); if (settingValue === 'Not a config') { return; diff --git a/src/test/common/configSettings/configSettings.pythonPath.unit.test.ts b/src/test/common/configSettings/configSettings.pythonPath.unit.test.ts index 84b3a28186fa..2e6d821e9c3d 100644 --- a/src/test/common/configSettings/configSettings.pythonPath.unit.test.ts +++ b/src/test/common/configSettings/configSettings.pythonPath.unit.test.ts @@ -42,7 +42,7 @@ suite('Python Settings - pythonPath', () => { configSettings = new CustomPythonSettings(undefined, new MockAutoSelectionService()); const pythonPath = 'This is the python Path'; pythonSettings - .setup(p => p.get(typemoq.It.isValue('pythonPath'))) + .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) .returns(() => pythonPath) .verifiable(typemoq.Times.atLeast(1)); configSettings.update(pythonSettings.object); @@ -53,7 +53,7 @@ suite('Python Settings - pythonPath', () => { configSettings = new CustomPythonSettings(undefined, new MockAutoSelectionService()); const pythonPath = `~${path.sep}This is the python Path`; pythonSettings - .setup(p => p.get(typemoq.It.isValue('pythonPath'))) + .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) .returns(() => pythonPath) .verifiable(typemoq.Times.atLeast(1)); configSettings.update(pythonSettings.object); @@ -65,7 +65,7 @@ suite('Python Settings - pythonPath', () => { configSettings = new CustomPythonSettings(workspaceFolderUri, new MockAutoSelectionService()); const pythonPath = `.${path.sep}This is the python Path`; pythonSettings - .setup(p => p.get(typemoq.It.isValue('pythonPath'))) + .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) .returns(() => pythonPath) .verifiable(typemoq.Times.atLeast(1)); @@ -79,7 +79,7 @@ suite('Python Settings - pythonPath', () => { const workspaceFolderToken = '${workspaceFolder}'; const pythonPath = `${workspaceFolderToken}${path.sep}This is the python Path`; pythonSettings - .setup(p => p.get(typemoq.It.isValue('pythonPath'))) + .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) .returns(() => pythonPath) .verifiable(typemoq.Times.atLeast(1)); configSettings.update(pythonSettings.object); @@ -92,7 +92,7 @@ suite('Python Settings - pythonPath', () => { configSettings = new CustomPythonSettings(workspaceFolderUri, instance(selectionService)); const pythonPath = 'python'; pythonSettings - .setup(p => p.get(typemoq.It.isValue('pythonPath'))) + .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) .returns(() => pythonPath) .verifiable(typemoq.Times.atLeast(1)); configSettings.update(pythonSettings.object); @@ -108,7 +108,7 @@ suite('Python Settings - pythonPath', () => { when(selectionService.setWorkspaceInterpreter(workspaceFolderUri, anything())).thenResolve(); configSettings = new CustomPythonSettings(workspaceFolderUri, instance(selectionService)); pythonSettings - .setup(p => p.get(typemoq.It.isValue('pythonPath'))) + .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) .returns(() => 'python') .verifiable(typemoq.Times.atLeast(1)); configSettings.update(pythonSettings.object); diff --git a/src/test/common/configSettings/configSettings.unit.test.ts b/src/test/common/configSettings/configSettings.unit.test.ts index 4b668f37bcd6..8d037a31f450 100644 --- a/src/test/common/configSettings/configSettings.unit.test.ts +++ b/src/test/common/configSettings/configSettings.unit.test.ts @@ -60,16 +60,16 @@ suite('Python Settings', async () => { 'insidersChannel' ]) { config - .setup(c => c.get(name)) + .setup((c) => c.get(name)) // tslint:disable-next-line:no-any .returns(() => (sourceSettings as any)[name]); } if (sourceSettings.jediEnabled) { - config.setup(c => c.get('jediPath')).returns(() => sourceSettings.jediPath); + config.setup((c) => c.get('jediPath')).returns(() => sourceSettings.jediPath); } for (const name of ['venvFolders']) { config - .setup(c => c.get(name)) + .setup((c) => c.get(name)) // tslint:disable-next-line:no-any .returns(() => (sourceSettings as any)[name]); } @@ -77,42 +77,42 @@ suite('Python Settings', async () => { // boolean settings for (const name of ['downloadLanguageServer', 'jediEnabled', 'autoUpdateLanguageServer']) { config - .setup(c => c.get(name, true)) + .setup((c) => c.get(name, true)) // tslint:disable-next-line:no-any .returns(() => (sourceSettings as any)[name]); } for (const name of ['disableInstallationCheck', 'globalModuleInstallation']) { config - .setup(c => c.get(name)) + .setup((c) => c.get(name)) // tslint:disable-next-line:no-any .returns(() => (sourceSettings as any)[name]); } // number settings if (sourceSettings.jediEnabled) { - config.setup(c => c.get('jediMemoryLimit')).returns(() => sourceSettings.jediMemoryLimit); + config.setup((c) => c.get('jediMemoryLimit')).returns(() => sourceSettings.jediMemoryLimit); } // Language server type settings - config.setup(c => c.get('languageServer')).returns(() => sourceSettings.languageServer); + config.setup((c) => c.get('languageServer')).returns(() => sourceSettings.languageServer); // "any" settings // tslint:disable-next-line:no-any - config.setup(c => c.get('devOptions')).returns(() => sourceSettings.devOptions); + config.setup((c) => c.get('devOptions')).returns(() => sourceSettings.devOptions); // complex settings - config.setup(c => c.get('linting')).returns(() => sourceSettings.linting); - config.setup(c => c.get('analysis')).returns(() => sourceSettings.analysis); - config.setup(c => c.get('sortImports')).returns(() => sourceSettings.sortImports); - config.setup(c => c.get('formatting')).returns(() => sourceSettings.formatting); - config.setup(c => c.get('autoComplete')).returns(() => sourceSettings.autoComplete); + config.setup((c) => c.get('linting')).returns(() => sourceSettings.linting); + config.setup((c) => c.get('analysis')).returns(() => sourceSettings.analysis); + config.setup((c) => c.get('sortImports')).returns(() => sourceSettings.sortImports); + config.setup((c) => c.get('formatting')).returns(() => sourceSettings.formatting); + config.setup((c) => c.get('autoComplete')).returns(() => sourceSettings.autoComplete); config - .setup(c => c.get('workspaceSymbols')) + .setup((c) => c.get('workspaceSymbols')) .returns(() => sourceSettings.workspaceSymbols); - config.setup(c => c.get('testing')).returns(() => sourceSettings.testing); - config.setup(c => c.get('terminal')).returns(() => sourceSettings.terminal); - config.setup(c => c.get('dataScience')).returns(() => sourceSettings.datascience); - config.setup(c => c.get('experiments')).returns(() => sourceSettings.experiments); + config.setup((c) => c.get('testing')).returns(() => sourceSettings.testing); + config.setup((c) => c.get('terminal')).returns(() => sourceSettings.terminal); + config.setup((c) => c.get('dataScience')).returns(() => sourceSettings.datascience); + config.setup((c) => c.get('experiments')).returns(() => sourceSettings.experiments); } function testIfValueIsUpdated(settingName: string, value: any) { @@ -130,7 +130,7 @@ suite('Python Settings', async () => { suite('String settings', async () => { ['pythonPath', 'venvPath', 'condaPath', 'pipenvPath', 'envFile', 'poetryPath', 'insidersChannel'].forEach( - async settingName => { + async (settingName) => { testIfValueIsUpdated(settingName, 'stringValue'); } ); @@ -138,14 +138,14 @@ suite('Python Settings', async () => { suite('Boolean settings', async () => { ['downloadLanguageServer', 'jediEnabled', 'autoUpdateLanguageServer', 'globalModuleInstallation'].forEach( - async settingName => { + async (settingName) => { testIfValueIsUpdated(settingName, true); } ); }); suite('Number settings', async () => { - ['jediMemoryLimit'].forEach(async settingName => { + ['jediMemoryLimit'].forEach(async (settingName) => { testIfValueIsUpdated(settingName, 1001); }); }); @@ -155,7 +155,7 @@ suite('Python Settings', async () => { expected.condaPath = 'spam'; initializeConfig(expected); config - .setup(c => c.get('condaPath')) + .setup((c) => c.get('condaPath')) .returns(() => expected.condaPath) .verifiable(TypeMoq.Times.once()); @@ -170,7 +170,7 @@ suite('Python Settings', async () => { expected.condaPath = path.join('~', 'anaconda3', 'bin', 'conda'); initializeConfig(expected); config - .setup(c => c.get('condaPath')) + .setup((c) => c.get('condaPath')) .returns(() => expected.condaPath) .verifiable(TypeMoq.Times.once()); @@ -190,7 +190,7 @@ suite('Python Settings', async () => { }; initializeConfig(expected); config - .setup(c => c.get('experiments')) + .setup((c) => c.get('experiments')) .returns(() => expected.experiments) .verifiable(TypeMoq.Times.once()); @@ -221,7 +221,7 @@ suite('Python Settings', async () => { expected.formatting.blackPath = 'spam'; initializeConfig(expected); config - .setup(c => c.get('formatting')) + .setup((c) => c.get('formatting')) .returns(() => expected.formatting) .verifiable(TypeMoq.Times.once()); @@ -248,7 +248,7 @@ suite('Python Settings', async () => { expected.formatting.blackPath = 'spam'; initializeConfig(expected); config - .setup(c => c.get('formatting')) + .setup((c) => c.get('formatting')) .returns(() => expected.formatting) .verifiable(TypeMoq.Times.once()); @@ -302,7 +302,7 @@ suite('Python Settings', async () => { }; initializeConfig(expected); config - .setup(c => c.get('experiments')) + .setup((c) => c.get('experiments')) .returns(() => expected.experiments) .verifiable(TypeMoq.Times.once()); diff --git a/src/test/common/crypto.unit.test.ts b/src/test/common/crypto.unit.test.ts index 175987c275bf..878e2b1ff0d4 100644 --- a/src/test/common/crypto.unit.test.ts +++ b/src/test/common/crypto.unit.test.ts @@ -1,144 +1,144 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { assert, expect } from 'chai'; -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { CryptoUtils } from '../../client/common/crypto'; -import { EXTENSION_ROOT_DIR_FOR_TESTS } from '../constants'; - -const RANDOM_WORDS = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', 'common', 'randomWords.txt'); - -// tslint:disable-next-line: max-func-body-length -suite('Crypto Utils', async () => { - let crypto: CryptoUtils; - let wordsText: string; - suiteSetup(async () => { - wordsText = await fs.readFile(RANDOM_WORDS, 'utf8'); - }); - setup(() => { - crypto = new CryptoUtils(); - }); - async function getRandomWords(): Promise { - return wordsText.split('\n'); - } - - test('If hashFormat equals `number`, method createHash() returns a number', async () => { - const hash = crypto.createHash('blabla', 'number'); - assert.typeOf(hash, 'number', 'Type should be a number'); - }); - test('If hashFormat equals `string`, method createHash() returns a string', async () => { - const hash = crypto.createHash('blabla', 'string'); - assert.typeOf(hash, 'string', 'Type should be a string'); - }); - test('Hashes must be same for same strings (sha256)', async () => { - const hash1 = crypto.createHash('blabla', 'string', 'SHA256'); - const hash2 = crypto.createHash('blabla', 'string', 'SHA256'); - assert.equal(hash1, hash2); - }); - test('Hashes must be different for different strings (sha256)', async () => { - const hash1 = crypto.createHash('Hello', 'string', 'SHA256'); - const hash2 = crypto.createHash('World', 'string', 'SHA256'); - assert.notEqual(hash1, hash2); - }); - test('If hashFormat equals `number`, the hash should not be NaN', async () => { - let hash = crypto.createHash('test', 'number'); - assert.isNotNaN(hash, 'Number hash should not be NaN'); - hash = crypto.createHash('hash', 'number'); - assert.isNotNaN(hash, 'Number hash should not be NaN'); - hash = crypto.createHash('HASH1', 'number'); - assert.isNotNaN(hash, 'Number hash should not be NaN'); - }); - test('If hashFormat equals `string`, the hash should not be undefined', async () => { - let hash = crypto.createHash('test', 'string'); - assert.isDefined(hash, 'String hash should not be undefined'); - hash = crypto.createHash('hash', 'string'); - assert.isDefined(hash, 'String hash should not be undefined'); - hash = crypto.createHash('HASH1', 'string'); - assert.isDefined(hash, 'String hash should not be undefined'); - }); - test('If hashFormat equals `number`, hashes with different data should return different number hashes', async () => { - const hash1 = crypto.createHash('hash1', 'number'); - const hash2 = crypto.createHash('hash2', 'number'); - assert.notEqual(hash1, hash2, 'Hashes should be different numbers'); - }); - test('If hashFormat equals `string`, hashes with different data should return different string hashes', async () => { - const hash1 = crypto.createHash('hash1', 'string'); - const hash2 = crypto.createHash('hash2', 'string'); - assert.notEqual(hash1, hash2, 'Hashes should be different strings'); - }); - test('If hashFormat equals `number`, ensure numbers are uniformly distributed on scale from 0 to 100', async () => { - const wordList = await getRandomWords(); - const buckets: number[] = Array(100).fill(0); - const hashes = Array(10).fill(0); - for (const w of wordList) { - for (let i = 0; i < 10; i += 1) { - const word = `${w}${i}`; - const hash = crypto.createHash(word, 'number'); - buckets[hash % 100] += 1; - hashes[i] = hash % 100; - } - } - // Total number of words = wordList.length * 10, because we added ten variants of each word above. - const expectedHitsPerBucket = (wordList.length * 10) / 100; - for (const hit of buckets) { - expect(hit).to.be.lessThan(1.25 * expectedHitsPerBucket); - expect(hit).to.be.greaterThan(0.75 * expectedHitsPerBucket); - } - }); - test('If hashFormat equals `number`, on a scale of 0 to 100, small difference in the input on average produce large differences (about 33) in the output ', async () => { - const wordList = await getRandomWords(); - const buckets: number[] = Array(100).fill(0); - let hashes: number[] = []; - let totalDifference = 0; - // We are only iterating over the first 10 words for purposes of this test - for (const w of wordList.slice(0, 10)) { - hashes = []; - totalDifference = 0; - if (w.length === 0) { - continue; - } - for (let i = 0; i < 10; i += 1) { - const word = `${w}${i}`; - const hash = crypto.createHash(word, 'number'); - buckets[hash % 100] += 1; - hashes.push(hash % 100); - } - for (let i = 0; i < 10; i += 1) { - const word = `${i}${w}`; - const hash = crypto.createHash(word, 'number'); - buckets[hash % 100] += 1; - hashes.push(hash % 100); - } - // Iterating over ASCII alphabets 'a' to 'z' and appending to the word - for (let i = 0; i < 26; i += 1) { - const word = `${String.fromCharCode(97 + i)}${w}`; - const hash = crypto.createHash(word, 'number'); - buckets[hash % 100] += 1; - hashes.push(hash % 100); - } - // Iterating over ASCII alphabets 'a' to 'z' and prepending to the word - for (let i = 0; i < 26; i += 1) { - const word = `${w}${String.fromCharCode(97 + i)}`; - const hash = crypto.createHash(word, 'number'); - buckets[hash % 100] += 1; - hashes.push(hash % 100); - } - // tslint:disable: prefer-for-of - for (let i = 0; i < hashes.length; i += 1) { - for (let j = 0; j < hashes.length; j += 1) { - if (hashes[i] > hashes[j]) { - totalDifference += hashes[i] - hashes[j]; - } else { - totalDifference += hashes[j] - hashes[i]; - } - } - } - const averageDifference = totalDifference / hashes.length / hashes.length; - expect(averageDifference).to.be.lessThan(1.25 * 33); - expect(averageDifference).to.be.greaterThan(0.75 * 33); - } - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { assert, expect } from 'chai'; +import * as fs from 'fs-extra'; +import * as path from 'path'; +import { CryptoUtils } from '../../client/common/crypto'; +import { EXTENSION_ROOT_DIR_FOR_TESTS } from '../constants'; + +const RANDOM_WORDS = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', 'common', 'randomWords.txt'); + +// tslint:disable-next-line: max-func-body-length +suite('Crypto Utils', async () => { + let crypto: CryptoUtils; + let wordsText: string; + suiteSetup(async () => { + wordsText = await fs.readFile(RANDOM_WORDS, 'utf8'); + }); + setup(() => { + crypto = new CryptoUtils(); + }); + async function getRandomWords(): Promise { + return wordsText.split('\n'); + } + + test('If hashFormat equals `number`, method createHash() returns a number', async () => { + const hash = crypto.createHash('blabla', 'number'); + assert.typeOf(hash, 'number', 'Type should be a number'); + }); + test('If hashFormat equals `string`, method createHash() returns a string', async () => { + const hash = crypto.createHash('blabla', 'string'); + assert.typeOf(hash, 'string', 'Type should be a string'); + }); + test('Hashes must be same for same strings (sha256)', async () => { + const hash1 = crypto.createHash('blabla', 'string', 'SHA256'); + const hash2 = crypto.createHash('blabla', 'string', 'SHA256'); + assert.equal(hash1, hash2); + }); + test('Hashes must be different for different strings (sha256)', async () => { + const hash1 = crypto.createHash('Hello', 'string', 'SHA256'); + const hash2 = crypto.createHash('World', 'string', 'SHA256'); + assert.notEqual(hash1, hash2); + }); + test('If hashFormat equals `number`, the hash should not be NaN', async () => { + let hash = crypto.createHash('test', 'number'); + assert.isNotNaN(hash, 'Number hash should not be NaN'); + hash = crypto.createHash('hash', 'number'); + assert.isNotNaN(hash, 'Number hash should not be NaN'); + hash = crypto.createHash('HASH1', 'number'); + assert.isNotNaN(hash, 'Number hash should not be NaN'); + }); + test('If hashFormat equals `string`, the hash should not be undefined', async () => { + let hash = crypto.createHash('test', 'string'); + assert.isDefined(hash, 'String hash should not be undefined'); + hash = crypto.createHash('hash', 'string'); + assert.isDefined(hash, 'String hash should not be undefined'); + hash = crypto.createHash('HASH1', 'string'); + assert.isDefined(hash, 'String hash should not be undefined'); + }); + test('If hashFormat equals `number`, hashes with different data should return different number hashes', async () => { + const hash1 = crypto.createHash('hash1', 'number'); + const hash2 = crypto.createHash('hash2', 'number'); + assert.notEqual(hash1, hash2, 'Hashes should be different numbers'); + }); + test('If hashFormat equals `string`, hashes with different data should return different string hashes', async () => { + const hash1 = crypto.createHash('hash1', 'string'); + const hash2 = crypto.createHash('hash2', 'string'); + assert.notEqual(hash1, hash2, 'Hashes should be different strings'); + }); + test('If hashFormat equals `number`, ensure numbers are uniformly distributed on scale from 0 to 100', async () => { + const wordList = await getRandomWords(); + const buckets: number[] = Array(100).fill(0); + const hashes = Array(10).fill(0); + for (const w of wordList) { + for (let i = 0; i < 10; i += 1) { + const word = `${w}${i}`; + const hash = crypto.createHash(word, 'number'); + buckets[hash % 100] += 1; + hashes[i] = hash % 100; + } + } + // Total number of words = wordList.length * 10, because we added ten variants of each word above. + const expectedHitsPerBucket = (wordList.length * 10) / 100; + for (const hit of buckets) { + expect(hit).to.be.lessThan(1.25 * expectedHitsPerBucket); + expect(hit).to.be.greaterThan(0.75 * expectedHitsPerBucket); + } + }); + test('If hashFormat equals `number`, on a scale of 0 to 100, small difference in the input on average produce large differences (about 33) in the output ', async () => { + const wordList = await getRandomWords(); + const buckets: number[] = Array(100).fill(0); + let hashes: number[] = []; + let totalDifference = 0; + // We are only iterating over the first 10 words for purposes of this test + for (const w of wordList.slice(0, 10)) { + hashes = []; + totalDifference = 0; + if (w.length === 0) { + continue; + } + for (let i = 0; i < 10; i += 1) { + const word = `${w}${i}`; + const hash = crypto.createHash(word, 'number'); + buckets[hash % 100] += 1; + hashes.push(hash % 100); + } + for (let i = 0; i < 10; i += 1) { + const word = `${i}${w}`; + const hash = crypto.createHash(word, 'number'); + buckets[hash % 100] += 1; + hashes.push(hash % 100); + } + // Iterating over ASCII alphabets 'a' to 'z' and appending to the word + for (let i = 0; i < 26; i += 1) { + const word = `${String.fromCharCode(97 + i)}${w}`; + const hash = crypto.createHash(word, 'number'); + buckets[hash % 100] += 1; + hashes.push(hash % 100); + } + // Iterating over ASCII alphabets 'a' to 'z' and prepending to the word + for (let i = 0; i < 26; i += 1) { + const word = `${w}${String.fromCharCode(97 + i)}`; + const hash = crypto.createHash(word, 'number'); + buckets[hash % 100] += 1; + hashes.push(hash % 100); + } + // tslint:disable: prefer-for-of + for (let i = 0; i < hashes.length; i += 1) { + for (let j = 0; j < hashes.length; j += 1) { + if (hashes[i] > hashes[j]) { + totalDifference += hashes[i] - hashes[j]; + } else { + totalDifference += hashes[j] - hashes[i]; + } + } + } + const averageDifference = totalDifference / hashes.length / hashes.length; + expect(averageDifference).to.be.lessThan(1.25 * 33); + expect(averageDifference).to.be.greaterThan(0.75 * 33); + } + }); +}); diff --git a/src/test/common/dotnet/compatibilityService.unit.test.ts b/src/test/common/dotnet/compatibilityService.unit.test.ts index e36c45d474dd..eb6289ec41c5 100644 --- a/src/test/common/dotnet/compatibilityService.unit.test.ts +++ b/src/test/common/dotnet/compatibilityService.unit.test.ts @@ -13,8 +13,8 @@ import { getNamesAndValues } from '../../../client/common/utils/enum'; import { OSType } from '../../../client/common/utils/platform'; suite('DOT.NET', () => { - getNamesAndValues(OSType).forEach(osType => { - [true, false].forEach(supported => { + getNamesAndValues(OSType).forEach((osType) => { + [true, false].forEach((supported) => { test(`Test ${osType.name} support = ${supported}`, async () => { const unknownService = mock(UnknownOSDotNetCompatibilityService); const macService = mock(UnknownOSDotNetCompatibilityService); diff --git a/src/test/common/dotnet/serviceRegistry.unit.test.ts b/src/test/common/dotnet/serviceRegistry.unit.test.ts index 1eee5c14c74c..d03d6113d7cc 100644 --- a/src/test/common/dotnet/serviceRegistry.unit.test.ts +++ b/src/test/common/dotnet/serviceRegistry.unit.test.ts @@ -1,62 +1,62 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { instance, mock, verify } from 'ts-mockito'; -import { DotNetCompatibilityService } from '../../../client/common/dotnet/compatibilityService'; -import { registerTypes } from '../../../client/common/dotnet/serviceRegistry'; -import { LinuxDotNetCompatibilityService } from '../../../client/common/dotnet/services/linuxCompatibilityService'; -import { MacDotNetCompatibilityService } from '../../../client/common/dotnet/services/macCompatibilityService'; -import { UnknownOSDotNetCompatibilityService } from '../../../client/common/dotnet/services/unknownOsCompatibilityService'; -import { WindowsDotNetCompatibilityService } from '../../../client/common/dotnet/services/windowsCompatibilityService'; -import { IDotNetCompatibilityService, IOSDotNetCompatibilityService } from '../../../client/common/dotnet/types'; -import { OSType } from '../../../client/common/utils/platform'; -import { ServiceManager } from '../../../client/ioc/serviceManager'; -import { IServiceManager } from '../../../client/ioc/types'; - -suite('Common Dotnet Service Registry', () => { - let serviceManager: IServiceManager; - - setup(() => { - serviceManager = mock(ServiceManager); - }); - - test('Ensure services are registered', async () => { - registerTypes(instance(serviceManager)); - verify( - serviceManager.addSingleton( - IDotNetCompatibilityService, - DotNetCompatibilityService - ) - ).once(); - verify( - serviceManager.addSingleton( - IOSDotNetCompatibilityService, - MacDotNetCompatibilityService, - OSType.OSX - ) - ).once(); - verify( - serviceManager.addSingleton( - IOSDotNetCompatibilityService, - WindowsDotNetCompatibilityService, - OSType.Windows - ) - ).once(); - verify( - serviceManager.addSingleton( - IOSDotNetCompatibilityService, - LinuxDotNetCompatibilityService, - OSType.Linux - ) - ).once(); - verify( - serviceManager.addSingleton( - IOSDotNetCompatibilityService, - UnknownOSDotNetCompatibilityService, - OSType.Unknown - ) - ).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { instance, mock, verify } from 'ts-mockito'; +import { DotNetCompatibilityService } from '../../../client/common/dotnet/compatibilityService'; +import { registerTypes } from '../../../client/common/dotnet/serviceRegistry'; +import { LinuxDotNetCompatibilityService } from '../../../client/common/dotnet/services/linuxCompatibilityService'; +import { MacDotNetCompatibilityService } from '../../../client/common/dotnet/services/macCompatibilityService'; +import { UnknownOSDotNetCompatibilityService } from '../../../client/common/dotnet/services/unknownOsCompatibilityService'; +import { WindowsDotNetCompatibilityService } from '../../../client/common/dotnet/services/windowsCompatibilityService'; +import { IDotNetCompatibilityService, IOSDotNetCompatibilityService } from '../../../client/common/dotnet/types'; +import { OSType } from '../../../client/common/utils/platform'; +import { ServiceManager } from '../../../client/ioc/serviceManager'; +import { IServiceManager } from '../../../client/ioc/types'; + +suite('Common Dotnet Service Registry', () => { + let serviceManager: IServiceManager; + + setup(() => { + serviceManager = mock(ServiceManager); + }); + + test('Ensure services are registered', async () => { + registerTypes(instance(serviceManager)); + verify( + serviceManager.addSingleton( + IDotNetCompatibilityService, + DotNetCompatibilityService + ) + ).once(); + verify( + serviceManager.addSingleton( + IOSDotNetCompatibilityService, + MacDotNetCompatibilityService, + OSType.OSX + ) + ).once(); + verify( + serviceManager.addSingleton( + IOSDotNetCompatibilityService, + WindowsDotNetCompatibilityService, + OSType.Windows + ) + ).once(); + verify( + serviceManager.addSingleton( + IOSDotNetCompatibilityService, + LinuxDotNetCompatibilityService, + OSType.Linux + ) + ).once(); + verify( + serviceManager.addSingleton( + IOSDotNetCompatibilityService, + UnknownOSDotNetCompatibilityService, + OSType.Unknown + ) + ).once(); + }); +}); diff --git a/src/test/common/exitCIAfterTestReporter.ts b/src/test/common/exitCIAfterTestReporter.ts index fa844b43a856..602d6eefd1ed 100644 --- a/src/test/common/exitCIAfterTestReporter.ts +++ b/src/test/common/exitCIAfterTestReporter.ts @@ -25,7 +25,7 @@ async function connectToServer() { } const port = parseInt(await fs.readFile(portFile, 'utf-8'), 10); console.log(`Need to connect to port ${port}`); - return new Promise(resolve => { + return new Promise((resolve) => { try { client = new net.Socket(); client.connect({ port }, () => { diff --git a/src/test/common/experiments.unit.test.ts b/src/test/common/experiments.unit.test.ts index d3b1b5994b28..cae02e8a2d70 100644 --- a/src/test/common/experiments.unit.test.ts +++ b/src/test/common/experiments.unit.test.ts @@ -70,8 +70,8 @@ suite('A/B experiments', () => { experiments = TypeMoq.Mock.ofType(); const settings = mock(PythonSettings); when(settings.experiments).thenReturn(experiments.object); - experiments.setup(e => e.optInto).returns(() => []); - experiments.setup(e => e.optOutFrom).returns(() => []); + experiments.setup((e) => e.optInto).returns(() => []); + experiments.setup((e) => e.optOutFrom).returns(() => []); when(configurationService.getSettings(undefined)).thenReturn(instance(settings)); fs = mock(FileSystem); when( @@ -119,7 +119,7 @@ suite('A/B experiments', () => { test('Initializing experiments does not download experiments if storage is valid and contains experiments', async () => { isDownloadedStorageValid - .setup(n => n.value) + .setup((n) => n.value) .returns(() => true) .verifiable(TypeMoq.Times.once()); @@ -131,15 +131,15 @@ suite('A/B experiments', () => { test('If storage has expired, initializing experiments downloads the experiments, but does not store them if they are invalid or incomplete', async () => { const abExperiments = [{ name: 'experiment1', salt: 'salt', max: 100 }]; isDownloadedStorageValid - .setup(n => n.value) + .setup((n) => n.value) .returns(() => false) .verifiable(TypeMoq.Times.once()); isDownloadedStorageValid - .setup(n => n.updateValue(true)) + .setup((n) => n.updateValue(true)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); downloadedExperimentsStorage - .setup(n => n.updateValue(abExperiments)) + .setup((n) => n.updateValue(abExperiments)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -151,15 +151,15 @@ suite('A/B experiments', () => { test('If storage has expired, initializing experiments downloads the experiments, and stores them if they are valid', async () => { isDownloadedStorageValid - .setup(n => n.value) + .setup((n) => n.value) .returns(() => false) .verifiable(TypeMoq.Times.once()); isDownloadedStorageValid - .setup(n => n.updateValue(true)) + .setup((n) => n.updateValue(true)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); downloadedExperimentsStorage - .setup(n => n.updateValue([{ name: 'experiment1', salt: 'salt', min: 90, max: 100 }])) + .setup((n) => n.updateValue([{ name: 'experiment1', salt: 'salt', min: 90, max: 100 }])) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); @@ -170,15 +170,15 @@ suite('A/B experiments', () => { test('If downloading experiments fails with error, the storage is left as it is', async () => { isDownloadedStorageValid - .setup(n => n.value) + .setup((n) => n.value) .returns(() => false) .verifiable(TypeMoq.Times.once()); isDownloadedStorageValid - .setup(n => n.updateValue(true)) + .setup((n) => n.updateValue(true)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); downloadedExperimentsStorage - .setup(n => n.updateValue(anything())) + .setup((n) => n.updateValue(anything())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -194,11 +194,11 @@ suite('A/B experiments', () => { when(workspaceService.getConfiguration('telemetry')).thenReturn(workspaceConfig.object); workspaceConfig - .setup(c => c.inspect('enableTelemetry')) + .setup((c) => c.inspect('enableTelemetry')) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); downloadedExperimentsStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.never()); @@ -219,13 +219,13 @@ suite('A/B experiments', () => { const workspaceConfig = TypeMoq.Mock.ofType(); const settings = {}; experiments - .setup(e => e.enabled) + .setup((e) => e.enabled) .returns(() => enabled) .verifiable(TypeMoq.Times.atLeastOnce()); when(workspaceService.getConfiguration('telemetry')).thenReturn(workspaceConfig.object); workspaceConfig - .setup(c => c.inspect('enableTelemetry')) + .setup((c) => c.inspect('enableTelemetry')) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); @@ -257,7 +257,7 @@ suite('A/B experiments', () => { const sendTelemetry = sinon.stub(ExperimentsManager.prototype, 'sendTelemetryIfInExperiment'); sendTelemetry.callsFake((_: string) => noop()); experiments - .setup(e => e.enabled) + .setup((e) => e.enabled) .returns(() => enabled) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -309,7 +309,7 @@ suite('A/B experiments', () => { when(workspaceService.getConfiguration('telemetry')).thenReturn(workspaceConfig.object); workspaceConfig - .setup(c => c.inspect('enableTelemetry')) + .setup((c) => c.inspect('enableTelemetry')) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); @@ -349,7 +349,7 @@ suite('A/B experiments', () => { when(workspaceService.getConfiguration('telemetry')).thenReturn(workspaceConfig.object); workspaceConfig - .setup(c => c.inspect('enableTelemetry')) + .setup((c) => c.inspect('enableTelemetry')) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); @@ -370,15 +370,15 @@ suite('A/B experiments', () => { test('Ensure experiment storage is updated to contain the latest downloaded experiments', async () => { downloadedExperimentsStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => [{ name: 'experiment1', salt: 'salt', min: 90, max: 100 }]) .verifiable(TypeMoq.Times.atLeastOnce()); downloadedExperimentsStorage - .setup(n => n.updateValue(undefined)) + .setup((n) => n.updateValue(undefined)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); experimentStorage - .setup(n => n.updateValue([{ name: 'experiment1', salt: 'salt', min: 90, max: 100 }])) + .setup((n) => n.updateValue([{ name: 'experiment1', salt: 'salt', min: 90, max: 100 }])) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); @@ -406,20 +406,20 @@ suite('A/B experiments', () => { ); downloadedExperimentsStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); downloadedExperimentsStorage - .setup(n => n.updateValue(undefined)) + .setup((n) => n.updateValue(undefined)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); experimentStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => [{ name: 'experiment1', salt: 'salt', min: 90, max: 100 }]) .verifiable(TypeMoq.Times.once()); experimentStorage - .setup(n => n.updateValue(TypeMoq.It.isAny())) + .setup((n) => n.updateValue(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -448,16 +448,16 @@ suite('A/B experiments', () => { ); downloadedExperimentsStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); downloadedExperimentsStorage - .setup(n => n.updateValue(undefined)) + .setup((n) => n.updateValue(undefined)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); experimentStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); @@ -485,11 +485,11 @@ suite('A/B experiments', () => { instance(configurationService) ); downloadedExperimentsStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); downloadedExperimentsStorage - .setup(n => n.updateValue(undefined)) + .setup((n) => n.updateValue(undefined)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -504,11 +504,11 @@ suite('A/B experiments', () => { when(fs.readFile(anything())).thenResolve(fileContent); experimentStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); experimentStorage - .setup(n => n.updateValue(TypeMoq.It.isAny())) + .setup((n) => n.updateValue(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -537,11 +537,11 @@ suite('A/B experiments', () => { instance(configurationService) ); downloadedExperimentsStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); downloadedExperimentsStorage - .setup(n => n.updateValue(undefined)) + .setup((n) => n.updateValue(undefined)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -556,11 +556,11 @@ suite('A/B experiments', () => { when(fs.readFile(anything())).thenResolve(fileContent); experimentStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); experimentStorage - .setup(n => n.updateValue([{ name: 'experiment1', salt: 'salt', min: 90, max: 100 }])) + .setup((n) => n.updateValue([{ name: 'experiment1', salt: 'salt', min: 90, max: 100 }])) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); @@ -594,11 +594,11 @@ suite('A/B experiments', () => { }); test('If checking the existence of config file fails', async () => { downloadedExperimentsStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); downloadedExperimentsStorage - .setup(n => n.updateValue(undefined)) + .setup((n) => n.updateValue(undefined)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -607,11 +607,11 @@ suite('A/B experiments', () => { when(fs.readFile(anything())).thenResolve('fileContent'); experimentStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); experimentStorage - .setup(n => n.updateValue(TypeMoq.It.isAny())) + .setup((n) => n.updateValue(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -625,11 +625,11 @@ suite('A/B experiments', () => { test('If reading config file fails', async () => { downloadedExperimentsStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); downloadedExperimentsStorage - .setup(n => n.updateValue(undefined)) + .setup((n) => n.updateValue(undefined)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -638,11 +638,11 @@ suite('A/B experiments', () => { when(fs.readFile(anything())).thenThrow(error); experimentStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); experimentStorage - .setup(n => n.updateValue(TypeMoq.It.isAny())) + .setup((n) => n.updateValue(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -656,11 +656,11 @@ suite('A/B experiments', () => { test('If config file does not exist', async () => { downloadedExperimentsStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); downloadedExperimentsStorage - .setup(n => n.updateValue(undefined)) + .setup((n) => n.updateValue(undefined)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -668,11 +668,11 @@ suite('A/B experiments', () => { when(fs.readFile(anything())).thenResolve('fileContent'); experimentStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); experimentStorage - .setup(n => n.updateValue(TypeMoq.It.isAny())) + .setup((n) => n.updateValue(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -686,11 +686,11 @@ suite('A/B experiments', () => { test('If parsing file or updating storage fails', async () => { downloadedExperimentsStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); downloadedExperimentsStorage - .setup(n => n.updateValue(undefined)) + .setup((n) => n.updateValue(undefined)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -705,11 +705,11 @@ suite('A/B experiments', () => { when(fs.readFile(anything())).thenResolve(fileContent); experimentStorage - .setup(n => n.value) + .setup((n) => n.value) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); experimentStorage - .setup(n => n.updateValue(TypeMoq.It.isAny())) + .setup((n) => n.updateValue(TypeMoq.It.isAny())) .returns(() => Promise.reject(error)) .verifiable(TypeMoq.Times.once()); @@ -745,7 +745,7 @@ suite('A/B experiments', () => { } ]; - testsForInExperiment.forEach(testParams => { + testsForInExperiment.forEach((testParams) => { test(testParams.testName, async () => { expManager.userExperiments = testParams.userExperiments; expect(expManager.inExperiment(testParams.experimentName)).to.equal( @@ -787,7 +787,7 @@ suite('A/B experiments', () => { ]; suite('Function IsUserInRange()', () => { - testsForIsUserInRange.forEach(testParams => { + testsForIsUserInRange.forEach((testParams) => { test(testParams.testName, async () => { when(appEnvironment.machineId).thenReturn('101'); if (testParams.machineIdError) { @@ -935,9 +935,9 @@ suite('A/B experiments', () => { ]; suite('Function populateUserExperiments', async () => { - testsForPopulateUserExperiments.forEach(testParams => { + testsForPopulateUserExperiments.forEach((testParams) => { test(testParams.testName, async () => { - experimentStorage.setup(n => n.value).returns(() => testParams.experimentStorageValue); + experimentStorage.setup((n) => n.value).returns(() => testParams.experimentStorageValue); when(appEnvironment.machineId).thenReturn('101'); if (testParams.hash) { when(crypto.createHash(anything(), 'number', anything())).thenReturn(testParams.hash); @@ -1003,7 +1003,7 @@ suite('A/B experiments', () => { ]; suite('Function areExperimentsValid()', () => { - testsForAreExperimentsValid.forEach(testParams => { + testsForAreExperimentsValid.forEach((testParams) => { test(testParams.testName, () => { expect(expManager.areExperimentsValid(testParams.experiments as any)).to.equal( testParams.expectedResult @@ -1087,15 +1087,15 @@ suite('A/B experiments', () => { test('If storage as parameter is passed in as argument to function downloadAndStoreExperiments(), download experiments into that storage', async () => { downloadedExperimentsStorage - .setup(n => n.updateValue(TypeMoq.It.isAny())) + .setup((n) => n.updateValue(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); experimentStorage - .setup(n => n.updateValue(TypeMoq.It.isAny())) + .setup((n) => n.updateValue(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); isDownloadedStorageValid - .setup(n => n.updateValue(true)) + .setup((n) => n.updateValue(true)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); when(httpClient.getJSON(configUri, false)).thenResolve([ diff --git a/src/test/common/featureDeprecationManager.unit.test.ts b/src/test/common/featureDeprecationManager.unit.test.ts index e672e68b2eef..2f3b48a61eb2 100644 --- a/src/test/common/featureDeprecationManager.unit.test.ts +++ b/src/test/common/featureDeprecationManager.unit.test.ts @@ -18,10 +18,10 @@ suite('Feature Deprecation Manager Tests', () => { const persistentBool: TypeMoq.IMock> = TypeMoq.Mock.ofType< IPersistentState >(); - persistentBool.setup(a => a.value).returns(() => true); - persistentBool.setup(a => a.updateValue(TypeMoq.It.isValue(false))).returns(() => Promise.resolve()); + persistentBool.setup((a) => a.value).returns(() => true); + persistentBool.setup((a) => a.updateValue(TypeMoq.It.isValue(false))).returns(() => Promise.resolve()); persistentState - .setup(a => + .setup((a) => a.createGlobalPersistentState( TypeMoq.It.isValue('SHOW_DEPRECATED_FEATURE_PROMPT_BUILD_WORKSPACE_SYMBOLS'), TypeMoq.It.isValue(true) @@ -31,11 +31,11 @@ suite('Feature Deprecation Manager Tests', () => { .verifiable(TypeMoq.Times.once()); const popupMgr: TypeMoq.IMock = TypeMoq.Mock.ofType(); popupMgr - .setup(p => + .setup((p) => p.showInformationMessage(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString()) ) .returns( - _val => + (_val) => new Promise((resolve, _reject) => { resolve('Learn More'); }) @@ -43,7 +43,7 @@ suite('Feature Deprecation Manager Tests', () => { const cmdDisposable: TypeMoq.IMock = TypeMoq.Mock.ofType(); const cmdManager: TypeMoq.IMock = TypeMoq.Mock.ofType(); cmdManager - .setup(c => + .setup((c) => c.registerCommand( TypeMoq.It.isValue('python.buildWorkspaceSymbols'), TypeMoq.It.isAny(), @@ -54,12 +54,12 @@ suite('Feature Deprecation Manager Tests', () => { .verifiable(TypeMoq.Times.atLeastOnce()); const workspaceConfig: TypeMoq.IMock = TypeMoq.Mock.ofType(); workspaceConfig - .setup(ws => ws.has(TypeMoq.It.isAnyString())) + .setup((ws) => ws.has(TypeMoq.It.isAnyString())) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); const workspace: TypeMoq.IMock = TypeMoq.Mock.ofType(); workspace - .setup(w => w.getConfiguration(TypeMoq.It.isValue('python'), TypeMoq.It.isAny())) + .setup((w) => w.getConfiguration(TypeMoq.It.isValue('python'), TypeMoq.It.isAny())) .returns(() => workspaceConfig.object); const featureDepMgr: FeatureDeprecationManager = new FeatureDeprecationManager( persistentState.object, @@ -78,7 +78,7 @@ suite('Feature Deprecation Manager Tests', () => { const featureDepMgr = new FeatureDeprecationManager(_, _, _, _); pythonConfig - .setup(p => p.has(TypeMoq.It.isValue(deprecatedSetting.setting))) + .setup((p) => p.has(TypeMoq.It.isValue(deprecatedSetting.setting))) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -97,11 +97,11 @@ suite('Feature Deprecation Manager Tests', () => { for (const config of testConfigs) { pythonConfig.reset(); pythonConfig - .setup(p => p.has(TypeMoq.It.isValue(deprecatedSetting.setting))) + .setup((p) => p.has(TypeMoq.It.isValue(deprecatedSetting.setting))) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); pythonConfig - .setup(p => p.get(TypeMoq.It.isValue(deprecatedSetting.setting))) + .setup((p) => p.get(TypeMoq.It.isValue(deprecatedSetting.setting))) .returns(() => config.valueInSetting); isUsed = featureDepMgr.isDeprecatedSettingAndValueUsed(pythonConfig.object, deprecatedSetting); @@ -120,11 +120,11 @@ suite('Feature Deprecation Manager Tests', () => { for (const config of testConfigs) { pythonConfig.reset(); pythonConfig - .setup(p => p.has(TypeMoq.It.isValue(deprecatedSetting.setting))) + .setup((p) => p.has(TypeMoq.It.isValue(deprecatedSetting.setting))) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); pythonConfig - .setup(p => p.get(TypeMoq.It.isValue(deprecatedSetting.setting))) + .setup((p) => p.get(TypeMoq.It.isValue(deprecatedSetting.setting))) .returns(() => config.valueInSetting); deprecatedSetting.values = config.valuesToLookFor; diff --git a/src/test/common/helpers.test.ts b/src/test/common/helpers.test.ts index 2be773f275fe..45e14d9146a1 100644 --- a/src/test/common/helpers.test.ts +++ b/src/test/common/helpers.test.ts @@ -6,7 +6,7 @@ import { isNotInstalledError } from '../../client/common/helpers'; // Defines a Mocha test suite to group tests of similar kind together suite('helpers', () => { - test('isNotInstalledError', done => { + test('isNotInstalledError', (done) => { const error = new Error('something is not installed'); assert.equal(isNotInstalledError(error), false, 'Standard error'); diff --git a/src/test/common/insidersBuild/downloadChannelRules.unit.test.ts b/src/test/common/insidersBuild/downloadChannelRules.unit.test.ts index 5e25f1e6cb13..285e0df9d422 100644 --- a/src/test/common/insidersBuild/downloadChannelRules.unit.test.ts +++ b/src/test/common/insidersBuild/downloadChannelRules.unit.test.ts @@ -46,7 +46,7 @@ suite('Download channel rules - ExtensionInsidersDailyChannelRule', () => { test('If insiders channel rule is new, update look up time and return installer for insiders build', async () => { lastLookUpTime - .setup(l => l.updateValue(TypeMoq.It.isAnyNumber())) + .setup((l) => l.updateValue(TypeMoq.It.isAnyNumber())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); const result = await insidersDailyChannelRule.shouldLookForInsidersBuild(true); @@ -56,11 +56,11 @@ suite('Download channel rules - ExtensionInsidersDailyChannelRule', () => { suite('If insiders channel rule is not new', async () => { test('Update look up time and return installer for insiders build if looking for insiders the first time', async () => { lastLookUpTime - .setup(l => l.updateValue(TypeMoq.It.isAnyNumber())) + .setup((l) => l.updateValue(TypeMoq.It.isAnyNumber())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); lastLookUpTime - .setup(l => l.value) + .setup((l) => l.value) .returns(() => -1) .verifiable(TypeMoq.Times.atLeastOnce()); const result = await insidersDailyChannelRule.shouldLookForInsidersBuild(false); @@ -69,11 +69,11 @@ suite('Download channel rules - ExtensionInsidersDailyChannelRule', () => { }); test('Update look up time and return installer for insiders build if looking for insiders after 24 hrs of last lookup time', async () => { lastLookUpTime - .setup(l => l.updateValue(TypeMoq.It.isAnyNumber())) + .setup((l) => l.updateValue(TypeMoq.It.isAnyNumber())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); lastLookUpTime - .setup(l => l.value) + .setup((l) => l.value) .returns(() => Date.now() - 2 * frequencyForDailyInsidersCheck) // Looking after 2 days .verifiable(TypeMoq.Times.atLeastOnce()); const result = await insidersDailyChannelRule.shouldLookForInsidersBuild(false); @@ -82,11 +82,11 @@ suite('Download channel rules - ExtensionInsidersDailyChannelRule', () => { }); test('Do not update look up time or return any installer if looking for insiders within 24 hrs of last lookup time', async () => { lastLookUpTime - .setup(l => l.updateValue(TypeMoq.It.isAnyNumber())) + .setup((l) => l.updateValue(TypeMoq.It.isAnyNumber())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); lastLookUpTime - .setup(l => l.value) + .setup((l) => l.value) .returns(() => Date.now() - frequencyForDailyInsidersCheck / 2) // Looking after half a day .verifiable(TypeMoq.Times.atLeastOnce()); const result = await insidersDailyChannelRule.shouldLookForInsidersBuild(false); @@ -111,7 +111,7 @@ suite('Download channel rules - ExtensionInsidersWeeklyChannelRule', () => { test('If insiders channel rule is new, update look up time and return installer for insiders build', async () => { lastLookUpTime - .setup(l => l.updateValue(TypeMoq.It.isAnyNumber())) + .setup((l) => l.updateValue(TypeMoq.It.isAnyNumber())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); const result = await insidersWeeklyChannelRule.shouldLookForInsidersBuild(true); @@ -121,11 +121,11 @@ suite('Download channel rules - ExtensionInsidersWeeklyChannelRule', () => { suite('If insiders channel rule is not new', async () => { test('Update look up time and return installer for insiders build if looking for insiders the first time', async () => { lastLookUpTime - .setup(l => l.updateValue(TypeMoq.It.isAnyNumber())) + .setup((l) => l.updateValue(TypeMoq.It.isAnyNumber())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); lastLookUpTime - .setup(l => l.value) + .setup((l) => l.value) .returns(() => -1) .verifiable(TypeMoq.Times.atLeastOnce()); const result = await insidersWeeklyChannelRule.shouldLookForInsidersBuild(false); @@ -134,11 +134,11 @@ suite('Download channel rules - ExtensionInsidersWeeklyChannelRule', () => { }); test('Update look up time and return installer for insiders build if looking for insiders after a week of last lookup time', async () => { lastLookUpTime - .setup(l => l.updateValue(TypeMoq.It.isAnyNumber())) + .setup((l) => l.updateValue(TypeMoq.It.isAnyNumber())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); lastLookUpTime - .setup(l => l.value) + .setup((l) => l.value) .returns(() => Date.now() - 2 * frequencyForWeeklyInsidersCheck) // Looking after 2 weeks .verifiable(TypeMoq.Times.atLeastOnce()); const result = await insidersWeeklyChannelRule.shouldLookForInsidersBuild(false); @@ -147,11 +147,11 @@ suite('Download channel rules - ExtensionInsidersWeeklyChannelRule', () => { }); test('Do not update look up time or return any installer if looking for insiders within one week of last lookup time', async () => { lastLookUpTime - .setup(l => l.updateValue(TypeMoq.It.isAnyNumber())) + .setup((l) => l.updateValue(TypeMoq.It.isAnyNumber())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); lastLookUpTime - .setup(l => l.value) + .setup((l) => l.value) .returns(() => Date.now() - frequencyForWeeklyInsidersCheck / 2) // Looking after half a week .verifiable(TypeMoq.Times.atLeastOnce()); const result = await insidersWeeklyChannelRule.shouldLookForInsidersBuild(false); diff --git a/src/test/common/insidersBuild/downloadChannelService.unit.test.ts b/src/test/common/insidersBuild/downloadChannelService.unit.test.ts index d27fb61009b8..f3cce07d5c56 100644 --- a/src/test/common/insidersBuild/downloadChannelService.unit.test.ts +++ b/src/test/common/insidersBuild/downloadChannelService.unit.test.ts @@ -55,7 +55,7 @@ suite('Download channel service', () => { settings: 'daily', expectedResult: 'daily' } - ].forEach(testParams => { + ].forEach((testParams) => { test(testParams.testName, async () => { when(configService.getSettings()).thenReturn({ insidersChannel: testParams.settings as ExtensionChannels @@ -72,7 +72,7 @@ suite('Download channel service', () => { when(workspaceService.getConfiguration('python')).thenReturn(workspaceConfig.object); workspaceConfig - .setup(c => c.inspect(insidersChannelSetting)) + .setup((c) => c.inspect(insidersChannelSetting)) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); expect(channelService.isChannelUsingDefaultConfiguration).to.equal(false, 'Incorrect value'); @@ -85,7 +85,7 @@ suite('Download channel service', () => { when(workspaceService.getConfiguration('python')).thenReturn(workspaceConfig.object); workspaceConfig - .setup(c => c.inspect(insidersChannelSetting)) + .setup((c) => c.inspect(insidersChannelSetting)) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); expect(channelService.isChannelUsingDefaultConfiguration).to.equal(true, 'Incorrect value'); @@ -98,7 +98,7 @@ suite('Download channel service', () => { when(workspaceService.getConfiguration('python')).thenReturn(workspaceConfig.object); workspaceConfig - .setup(c => c.inspect(insidersChannelSetting)) + .setup((c) => c.inspect(insidersChannelSetting)) .returns(() => settings as any) .verifiable(TypeMoq.Times.once()); expect(() => channelService.isChannelUsingDefaultConfiguration).to.throw(); @@ -130,13 +130,13 @@ suite('Download channel service', () => { const event = TypeMoq.Mock.ofType(); const settings = { insidersChannel: 'off' }; event - .setup(e => e.affectsConfiguration(`python.${insidersChannelSetting}`)) + .setup((e) => e.affectsConfiguration(`python.${insidersChannelSetting}`)) .returns(() => true) .verifiable(TypeMoq.Times.once()); when(configService.getSettings()).thenReturn(settings as any); channelService._onDidChannelChange = _onDidChannelChange.object; _onDidChannelChange - .setup(emitter => emitter.fire(TypeMoq.It.isValue(settings.insidersChannel as any))) + .setup((emitter) => emitter.fire(TypeMoq.It.isValue(settings.insidersChannel as any))) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); await channelService.onDidChangeConfiguration(event.object); @@ -150,13 +150,13 @@ suite('Download channel service', () => { const event = TypeMoq.Mock.ofType(); const settings = { insidersChannel: 'off' }; event - .setup(e => e.affectsConfiguration(`python.${insidersChannelSetting}`)) + .setup((e) => e.affectsConfiguration(`python.${insidersChannelSetting}`)) .returns(() => false) .verifiable(TypeMoq.Times.once()); when(configService.getSettings()).thenReturn(settings as any); channelService._onDidChannelChange = _onDidChannelChange.object; _onDidChannelChange - .setup(emitter => emitter.fire(TypeMoq.It.isValue(settings.insidersChannel as any))) + .setup((emitter) => emitter.fire(TypeMoq.It.isValue(settings.insidersChannel as any))) .returns(() => undefined) .verifiable(TypeMoq.Times.never()); await channelService.onDidChangeConfiguration(event.object); @@ -168,7 +168,7 @@ suite('Download channel service', () => { test('Ensure on channel change captures the fired event with the correct arguments', async () => { const deferred = createDeferred(); const settings = { insidersChannel: 'off' }; - channelService.onDidChannelChange(channel => { + channelService.onDidChannelChange((channel) => { expect(channel).to.equal(settings.insidersChannel); deferred.resolve(true); }); diff --git a/src/test/common/insidersBuild/insidersExtensionPrompt.unit.test.ts b/src/test/common/insidersBuild/insidersExtensionPrompt.unit.test.ts index 2bcabd27237b..df7fcf45f332 100644 --- a/src/test/common/insidersBuild/insidersExtensionPrompt.unit.test.ts +++ b/src/test/common/insidersBuild/insidersExtensionPrompt.unit.test.ts @@ -1,155 +1,155 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -// tslint:disable:no-any - -import { anything, instance, mock, verify, when } from 'ts-mockito'; -import * as TypeMoq from 'typemoq'; -import { ApplicationShell } from '../../../client/common/application/applicationShell'; -import { CommandManager } from '../../../client/common/application/commandManager'; -import { IApplicationShell, ICommandManager } from '../../../client/common/application/types'; -import { ExtensionChannelService } from '../../../client/common/insidersBuild/downloadChannelService'; -import { - InsidersExtensionPrompt, - insidersPromptStateKey -} from '../../../client/common/insidersBuild/insidersExtensionPrompt'; -import { ExtensionChannel, IExtensionChannelService } from '../../../client/common/insidersBuild/types'; -import { PersistentStateFactory } from '../../../client/common/persistentState'; -import { IPersistentState, IPersistentStateFactory } from '../../../client/common/types'; -import { Common, DataScienceSurveyBanner, ExtensionChannels } from '../../../client/common/utils/localize'; - -// tslint:disable-next-line: max-func-body-length -suite('Insiders Extension prompt', () => { - let appShell: IApplicationShell; - let extensionChannelService: IExtensionChannelService; - let cmdManager: ICommandManager; - let persistentState: IPersistentStateFactory; - let hasUserBeenNotifiedState: TypeMoq.IMock>; - let insidersPrompt: InsidersExtensionPrompt; - setup(() => { - extensionChannelService = mock(ExtensionChannelService); - appShell = mock(ApplicationShell); - persistentState = mock(PersistentStateFactory); - cmdManager = mock(CommandManager); - hasUserBeenNotifiedState = TypeMoq.Mock.ofType>(); - when(persistentState.createGlobalPersistentState(insidersPromptStateKey, false)).thenReturn( - hasUserBeenNotifiedState.object - ); - insidersPrompt = new InsidersExtensionPrompt( - instance(appShell), - instance(extensionChannelService), - instance(cmdManager), - instance(persistentState) - ); - }); - - test("Channel is set to 'daily' if 'Yes, daily' option is selected", async () => { - const prompts = [ - ExtensionChannels.yesWeekly(), - ExtensionChannels.yesDaily(), - DataScienceSurveyBanner.bannerLabelNo() - ]; - when(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).thenResolve( - ExtensionChannels.yesDaily() as any - ); - when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); - when(extensionChannelService.updateChannel(ExtensionChannel.daily)).thenResolve(); - hasUserBeenNotifiedState - .setup(u => u.updateValue(true)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.once()); - await insidersPrompt.promptToInstallInsiders(); - verify(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).once(); - verify(extensionChannelService.updateChannel(ExtensionChannel.daily)).once(); - hasUserBeenNotifiedState.verifyAll(); - verify(cmdManager.executeCommand('workbench.action.reloadWindow')).never(); - }); - - test("Channel is set to 'weekly' if 'Yes, weekly' option is selected", async () => { - const prompts = [ - ExtensionChannels.yesWeekly(), - ExtensionChannels.yesDaily(), - DataScienceSurveyBanner.bannerLabelNo() - ]; - when(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).thenResolve( - ExtensionChannels.yesWeekly() as any - ); - when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); - when(extensionChannelService.updateChannel(ExtensionChannel.weekly)).thenResolve(); - hasUserBeenNotifiedState - .setup(u => u.updateValue(true)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.once()); - await insidersPrompt.promptToInstallInsiders(); - verify(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).once(); - verify(extensionChannelService.updateChannel(ExtensionChannel.weekly)).once(); - hasUserBeenNotifiedState.verifyAll(); - verify(cmdManager.executeCommand('workbench.action.reloadWindow')).never(); - }); - - test("No channel is set if 'No, thanks' option is selected", async () => { - const prompts = [ - ExtensionChannels.yesWeekly(), - ExtensionChannels.yesDaily(), - DataScienceSurveyBanner.bannerLabelNo() - ]; - when(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).thenResolve( - DataScienceSurveyBanner.bannerLabelNo() as any - ); - when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); - when(extensionChannelService.updateChannel(anything())).thenResolve(); - hasUserBeenNotifiedState - .setup(u => u.updateValue(true)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.once()); - await insidersPrompt.promptToInstallInsiders(); - verify(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).once(); - verify(extensionChannelService.updateChannel(anything())).never(); - hasUserBeenNotifiedState.verifyAll(); - verify(cmdManager.executeCommand('workbench.action.reloadWindow')).never(); - }); - - test('No channel is set if no option is selected', async () => { - const prompts = [ - ExtensionChannels.yesWeekly(), - ExtensionChannels.yesDaily(), - DataScienceSurveyBanner.bannerLabelNo() - ]; - when(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).thenResolve( - undefined as any - ); - when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); - when(extensionChannelService.updateChannel(anything())).thenResolve(); - hasUserBeenNotifiedState - .setup(u => u.updateValue(true)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.once()); - await insidersPrompt.promptToInstallInsiders(); - verify(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).once(); - verify(extensionChannelService.updateChannel(anything())).never(); - hasUserBeenNotifiedState.verifyAll(); - verify(cmdManager.executeCommand('workbench.action.reloadWindow')).never(); - }); - - test('Do not do anything if no option is selected in the reload prompt', async () => { - when( - appShell.showInformationMessage(ExtensionChannels.reloadToUseInsidersMessage(), Common.reload()) - ).thenResolve(undefined); - when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); - await insidersPrompt.promptToReload(); - verify(appShell.showInformationMessage(ExtensionChannels.reloadToUseInsidersMessage(), Common.reload())).once(); - verify(cmdManager.executeCommand('workbench.action.reloadWindow')).never(); - }); - - test("Reload windows if 'Reload' option is selected in the reload prompt", async () => { - when( - appShell.showInformationMessage(ExtensionChannels.reloadToUseInsidersMessage(), Common.reload()) - ).thenResolve(Common.reload() as any); - when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); - await insidersPrompt.promptToReload(); - verify(appShell.showInformationMessage(ExtensionChannels.reloadToUseInsidersMessage(), Common.reload())).once(); - verify(cmdManager.executeCommand('workbench.action.reloadWindow')).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// tslint:disable:no-any + +import { anything, instance, mock, verify, when } from 'ts-mockito'; +import * as TypeMoq from 'typemoq'; +import { ApplicationShell } from '../../../client/common/application/applicationShell'; +import { CommandManager } from '../../../client/common/application/commandManager'; +import { IApplicationShell, ICommandManager } from '../../../client/common/application/types'; +import { ExtensionChannelService } from '../../../client/common/insidersBuild/downloadChannelService'; +import { + InsidersExtensionPrompt, + insidersPromptStateKey +} from '../../../client/common/insidersBuild/insidersExtensionPrompt'; +import { ExtensionChannel, IExtensionChannelService } from '../../../client/common/insidersBuild/types'; +import { PersistentStateFactory } from '../../../client/common/persistentState'; +import { IPersistentState, IPersistentStateFactory } from '../../../client/common/types'; +import { Common, DataScienceSurveyBanner, ExtensionChannels } from '../../../client/common/utils/localize'; + +// tslint:disable-next-line: max-func-body-length +suite('Insiders Extension prompt', () => { + let appShell: IApplicationShell; + let extensionChannelService: IExtensionChannelService; + let cmdManager: ICommandManager; + let persistentState: IPersistentStateFactory; + let hasUserBeenNotifiedState: TypeMoq.IMock>; + let insidersPrompt: InsidersExtensionPrompt; + setup(() => { + extensionChannelService = mock(ExtensionChannelService); + appShell = mock(ApplicationShell); + persistentState = mock(PersistentStateFactory); + cmdManager = mock(CommandManager); + hasUserBeenNotifiedState = TypeMoq.Mock.ofType>(); + when(persistentState.createGlobalPersistentState(insidersPromptStateKey, false)).thenReturn( + hasUserBeenNotifiedState.object + ); + insidersPrompt = new InsidersExtensionPrompt( + instance(appShell), + instance(extensionChannelService), + instance(cmdManager), + instance(persistentState) + ); + }); + + test("Channel is set to 'daily' if 'Yes, daily' option is selected", async () => { + const prompts = [ + ExtensionChannels.yesWeekly(), + ExtensionChannels.yesDaily(), + DataScienceSurveyBanner.bannerLabelNo() + ]; + when(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).thenResolve( + ExtensionChannels.yesDaily() as any + ); + when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); + when(extensionChannelService.updateChannel(ExtensionChannel.daily)).thenResolve(); + hasUserBeenNotifiedState + .setup((u) => u.updateValue(true)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.once()); + await insidersPrompt.promptToInstallInsiders(); + verify(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).once(); + verify(extensionChannelService.updateChannel(ExtensionChannel.daily)).once(); + hasUserBeenNotifiedState.verifyAll(); + verify(cmdManager.executeCommand('workbench.action.reloadWindow')).never(); + }); + + test("Channel is set to 'weekly' if 'Yes, weekly' option is selected", async () => { + const prompts = [ + ExtensionChannels.yesWeekly(), + ExtensionChannels.yesDaily(), + DataScienceSurveyBanner.bannerLabelNo() + ]; + when(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).thenResolve( + ExtensionChannels.yesWeekly() as any + ); + when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); + when(extensionChannelService.updateChannel(ExtensionChannel.weekly)).thenResolve(); + hasUserBeenNotifiedState + .setup((u) => u.updateValue(true)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.once()); + await insidersPrompt.promptToInstallInsiders(); + verify(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).once(); + verify(extensionChannelService.updateChannel(ExtensionChannel.weekly)).once(); + hasUserBeenNotifiedState.verifyAll(); + verify(cmdManager.executeCommand('workbench.action.reloadWindow')).never(); + }); + + test("No channel is set if 'No, thanks' option is selected", async () => { + const prompts = [ + ExtensionChannels.yesWeekly(), + ExtensionChannels.yesDaily(), + DataScienceSurveyBanner.bannerLabelNo() + ]; + when(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).thenResolve( + DataScienceSurveyBanner.bannerLabelNo() as any + ); + when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); + when(extensionChannelService.updateChannel(anything())).thenResolve(); + hasUserBeenNotifiedState + .setup((u) => u.updateValue(true)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.once()); + await insidersPrompt.promptToInstallInsiders(); + verify(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).once(); + verify(extensionChannelService.updateChannel(anything())).never(); + hasUserBeenNotifiedState.verifyAll(); + verify(cmdManager.executeCommand('workbench.action.reloadWindow')).never(); + }); + + test('No channel is set if no option is selected', async () => { + const prompts = [ + ExtensionChannels.yesWeekly(), + ExtensionChannels.yesDaily(), + DataScienceSurveyBanner.bannerLabelNo() + ]; + when(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).thenResolve( + undefined as any + ); + when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); + when(extensionChannelService.updateChannel(anything())).thenResolve(); + hasUserBeenNotifiedState + .setup((u) => u.updateValue(true)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.once()); + await insidersPrompt.promptToInstallInsiders(); + verify(appShell.showInformationMessage(ExtensionChannels.promptMessage(), ...prompts)).once(); + verify(extensionChannelService.updateChannel(anything())).never(); + hasUserBeenNotifiedState.verifyAll(); + verify(cmdManager.executeCommand('workbench.action.reloadWindow')).never(); + }); + + test('Do not do anything if no option is selected in the reload prompt', async () => { + when( + appShell.showInformationMessage(ExtensionChannels.reloadToUseInsidersMessage(), Common.reload()) + ).thenResolve(undefined); + when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); + await insidersPrompt.promptToReload(); + verify(appShell.showInformationMessage(ExtensionChannels.reloadToUseInsidersMessage(), Common.reload())).once(); + verify(cmdManager.executeCommand('workbench.action.reloadWindow')).never(); + }); + + test("Reload windows if 'Reload' option is selected in the reload prompt", async () => { + when( + appShell.showInformationMessage(ExtensionChannels.reloadToUseInsidersMessage(), Common.reload()) + ).thenResolve(Common.reload() as any); + when(cmdManager.executeCommand('workbench.action.reloadWindow')).thenResolve(); + await insidersPrompt.promptToReload(); + verify(appShell.showInformationMessage(ExtensionChannels.reloadToUseInsidersMessage(), Common.reload())).once(); + verify(cmdManager.executeCommand('workbench.action.reloadWindow')).once(); + }); +}); diff --git a/src/test/common/insidersBuild/insidersExtensionService.unit.test.ts b/src/test/common/insidersBuild/insidersExtensionService.unit.test.ts index 9801cddb0825..a0d96984e168 100644 --- a/src/test/common/insidersBuild/insidersExtensionService.unit.test.ts +++ b/src/test/common/insidersBuild/insidersExtensionService.unit.test.ts @@ -1,567 +1,567 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -// tslint:disable:no-any - -import * as assert from 'assert'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { anything, instance, mock, verify, when } from 'ts-mockito'; -import * as TypeMoq from 'typemoq'; -import { EventEmitter } from 'vscode'; -import { ApplicationEnvironment } from '../../../client/common/application/applicationEnvironment'; -import { CommandManager } from '../../../client/common/application/commandManager'; -import { Channel, IApplicationEnvironment, ICommandManager } from '../../../client/common/application/types'; -import { Commands } from '../../../client/common/constants'; -import { ExtensionChannelService } from '../../../client/common/insidersBuild/downloadChannelService'; -import { InsidersExtensionPrompt } from '../../../client/common/insidersBuild/insidersExtensionPrompt'; -import { InsidersExtensionService } from '../../../client/common/insidersBuild/insidersExtensionService'; -import { - ExtensionChannels, - IExtensionChannelRule, - IExtensionChannelService, - IInsiderExtensionPrompt -} from '../../../client/common/insidersBuild/types'; -import { InsidersBuildInstaller } from '../../../client/common/installer/extensionBuildInstaller'; -import { IExtensionBuildInstaller } from '../../../client/common/installer/types'; -import { PersistentState } from '../../../client/common/persistentState'; -import { IDisposable, IPersistentState } from '../../../client/common/types'; -import { createDeferred, createDeferredFromPromise } from '../../../client/common/utils/async'; -import { ServiceContainer } from '../../../client/ioc/container'; -import { IServiceContainer } from '../../../client/ioc/types'; -import { sleep } from '../../../test/core'; - -suite('Insiders Extension Service - Handle channel', () => { - let appEnvironment: IApplicationEnvironment; - let serviceContainer: IServiceContainer; - let extensionChannelService: IExtensionChannelService; - let cmdManager: ICommandManager; - let insidersPrompt: IInsiderExtensionPrompt; - let insidersInstaller: IExtensionBuildInstaller; - let insidersExtensionService: InsidersExtensionService; - setup(() => { - extensionChannelService = mock(ExtensionChannelService); - appEnvironment = mock(ApplicationEnvironment); - cmdManager = mock(CommandManager); - serviceContainer = mock(ServiceContainer); - insidersPrompt = mock(InsidersExtensionPrompt); - insidersInstaller = mock(InsidersBuildInstaller); - insidersExtensionService = new InsidersExtensionService( - instance(extensionChannelService), - instance(insidersPrompt), - instance(appEnvironment), - instance(cmdManager), - instance(serviceContainer), - instance(insidersInstaller), - [] - ); - }); - - teardown(() => { - sinon.restore(); - }); - - test('If insiders is not be installed, handling channel does not do anything and simply returns', async () => { - const channelRule = TypeMoq.Mock.ofType(); - when(serviceContainer.get(IExtensionChannelRule, 'off')).thenReturn(channelRule.object); - channelRule - .setup(c => c.shouldLookForInsidersBuild(false)) - .returns(() => Promise.resolve(false)) - .verifiable(TypeMoq.Times.once()); - when(insidersInstaller.install()).thenResolve(undefined); - await insidersExtensionService.handleChannel('off'); - verify(insidersInstaller.install()).never(); - channelRule.verifyAll(); - }); - - test('If insiders is required to be installed, handling channel installs the build and prompts user', async () => { - const channelRule = TypeMoq.Mock.ofType(); - when(serviceContainer.get(IExtensionChannelRule, 'weekly')).thenReturn( - channelRule.object - ); - channelRule - .setup(c => c.shouldLookForInsidersBuild(false)) - .returns(() => Promise.resolve(true)) - .verifiable(TypeMoq.Times.once()); - when(insidersInstaller.install()).thenResolve(undefined); - when(insidersPrompt.promptToReload()).thenResolve(undefined); - await insidersExtensionService.handleChannel('weekly'); - verify(insidersInstaller.install()).once(); - verify(insidersPrompt.promptToReload()).once(); - channelRule.verifyAll(); - }); -}); - -// tslint:disable-next-line: max-func-body-length -suite('Insiders Extension Service - Activation', () => { - let appEnvironment: IApplicationEnvironment; - let serviceContainer: IServiceContainer; - let extensionChannelService: IExtensionChannelService; - let cmdManager: ICommandManager; - let insidersPrompt: IInsiderExtensionPrompt; - let registerCommandsAndHandlers: sinon.SinonStub; - let handleChannel: sinon.SinonStub; - let handleEdgeCases: sinon.SinonStub; - let insidersInstaller: IExtensionBuildInstaller; - let insidersExtensionService: InsidersExtensionService; - let envUITEST_DISABLE_INSIDERSExists = false; - setup(() => { - envUITEST_DISABLE_INSIDERSExists = process.env.UITEST_DISABLE_INSIDERS !== undefined; - delete process.env.UITEST_DISABLE_INSIDERS; - extensionChannelService = mock(ExtensionChannelService); - insidersInstaller = mock(InsidersBuildInstaller); - appEnvironment = mock(ApplicationEnvironment); - cmdManager = mock(CommandManager); - serviceContainer = mock(ServiceContainer); - insidersPrompt = mock(InsidersExtensionPrompt); - handleEdgeCases = sinon.stub(InsidersExtensionService.prototype, 'handleEdgeCases'); - registerCommandsAndHandlers = sinon.stub(InsidersExtensionService.prototype, 'registerCommandsAndHandlers'); - registerCommandsAndHandlers.callsFake(() => Promise.resolve()); - }); - - teardown(() => { - if (envUITEST_DISABLE_INSIDERSExists) { - process.env.UITEST_DISABLE_INSIDERS = '1'; - } - sinon.restore(); - }); - - test('If install channel is handled in the edge cases, do not handle it again using the general way', async () => { - handleChannel = sinon.stub(InsidersExtensionService.prototype, 'handleChannel'); - handleChannel.callsFake(() => Promise.resolve()); - handleEdgeCases.callsFake(() => Promise.resolve(true)); - insidersExtensionService = new InsidersExtensionService( - instance(extensionChannelService), - instance(insidersPrompt), - instance(appEnvironment), - instance(cmdManager), - instance(serviceContainer), - instance(insidersInstaller), - [] - ); - when(extensionChannelService.getChannel()).thenReturn('daily'); - when(extensionChannelService.isChannelUsingDefaultConfiguration).thenReturn(false); - - await insidersExtensionService.activate(); - - verify(extensionChannelService.getChannel()).once(); - verify(extensionChannelService.isChannelUsingDefaultConfiguration).once(); - assert.ok(registerCommandsAndHandlers.calledOnce); - assert.ok(handleEdgeCases.calledOnce); - assert.ok(handleEdgeCases.calledWith('daily', false)); - assert.ok(handleChannel.notCalled); - }); - - test('If install channel is not handled in the edge cases, handle it using the general way', async () => { - handleChannel = sinon.stub(InsidersExtensionService.prototype, 'handleChannel'); - handleChannel.callsFake(() => Promise.resolve()); - handleEdgeCases.callsFake(() => Promise.resolve(false)); - insidersExtensionService = new InsidersExtensionService( - instance(extensionChannelService), - instance(insidersPrompt), - instance(appEnvironment), - instance(cmdManager), - instance(serviceContainer), - instance(insidersInstaller), - [] - ); - when(extensionChannelService.getChannel()).thenReturn('daily'); - when(extensionChannelService.isChannelUsingDefaultConfiguration).thenReturn(false); - - await insidersExtensionService.activate(); - - verify(extensionChannelService.getChannel()).once(); - verify(extensionChannelService.isChannelUsingDefaultConfiguration).once(); - assert.ok(registerCommandsAndHandlers.calledOnce); - assert.ok(handleEdgeCases.calledOnce); - assert.ok(handleEdgeCases.calledWith('daily', false)); - assert.ok(handleChannel.calledOnce); - }); - - test('Ensure channels are reliably handled in the background', async () => { - const handleChannelsDeferred = createDeferred(); - handleChannel = sinon.stub(InsidersExtensionService.prototype, 'handleChannel'); - handleChannel.callsFake(() => handleChannelsDeferred.promise); - handleEdgeCases.callsFake(() => Promise.resolve(false)); - insidersExtensionService = new InsidersExtensionService( - instance(extensionChannelService), - instance(insidersPrompt), - instance(appEnvironment), - instance(cmdManager), - instance(serviceContainer), - instance(insidersInstaller), - [] - ); - when(extensionChannelService.getChannel()).thenReturn('daily'); - when(extensionChannelService.isChannelUsingDefaultConfiguration).thenReturn(false); - - const promise = insidersExtensionService.activate(); - const deferred = createDeferredFromPromise(promise); - await sleep(1); - - // Ensure activate() function has completed while handleChannel is still running - assert.equal(deferred.completed, true); - - handleChannelsDeferred.resolve(); - await sleep(1); - - assert.ok(registerCommandsAndHandlers.calledOnce); - assert.ok(handleEdgeCases.calledOnce); - assert.ok(handleChannel.calledOnce); - assert.ok(handleEdgeCases.calledWith('daily', false)); - }); -}); - -// tslint:disable-next-line: max-func-body-length -suite('Insiders Extension Service - Function handleEdgeCases()', () => { - let appEnvironment: TypeMoq.IMock; - let serviceContainer: TypeMoq.IMock; - let extensionChannelService: TypeMoq.IMock; - let cmdManager: TypeMoq.IMock; - let insidersPrompt: TypeMoq.IMock; - let hasUserBeenNotifiedState: IPersistentState; - let insidersInstaller: TypeMoq.IMock; - - let insidersExtensionService: InsidersExtensionService; - - function setupCommon() { - extensionChannelService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - insidersInstaller = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - appEnvironment = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - cmdManager = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - serviceContainer = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - insidersPrompt = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - hasUserBeenNotifiedState = mock(PersistentState); - - insidersExtensionService = new InsidersExtensionService( - extensionChannelService.object, - insidersPrompt.object, - appEnvironment.object, - cmdManager.object, - serviceContainer.object, - insidersInstaller.object, - [] - ); - - insidersPrompt - .setup(p => p.hasUserBeenNotified) - .returns(() => instance(hasUserBeenNotifiedState)) - // Basically means "we don't care" (necessary for strict mocks). - .verifiable(TypeMoq.Times.atLeast(0)); - } - - function verifyAll() { - // the most important ones: - insidersPrompt.verifyAll(); - insidersInstaller.verifyAll(); - extensionChannelService.verifyAll(); - // the other used interfaces: - appEnvironment.verifyAll(); - serviceContainer.verifyAll(); - cmdManager.verifyAll(); - } - - type TestInfo = { - vscodeChannel?: Channel; - extensionChannel?: Channel; - installChannel: ExtensionChannels; - isChannelUsingDefaultConfiguration?: boolean; - hasUserBeenNotified?: boolean; - }; - - function setState(info: TestInfo, checkPromptEnroll: boolean, checkDisable: boolean) { - if (info.vscodeChannel) { - appEnvironment.setup(e => e.channel).returns(() => info.vscodeChannel!); - } - if (info.extensionChannel) { - appEnvironment.setup(e => e.extensionChannel).returns(() => info.extensionChannel!); - } - - if (checkDisable) { - extensionChannelService.setup(ec => ec.updateChannel('off')).returns(() => Promise.resolve()); - } - if (info.hasUserBeenNotified !== undefined) { - when(hasUserBeenNotifiedState.value).thenReturn(info.hasUserBeenNotified!); - } - if (checkPromptEnroll) { - insidersPrompt.setup(p => p.promptToInstallInsiders()).returns(() => Promise.resolve()); - } - } - - suite('Case II - Verify Insiders Install Prompt is displayed when conditions are met', async () => { - const testsForHandleEdgeCaseII: TestInfo[] = [ - { - installChannel: 'daily', - // prompt to enroll - vscodeChannel: 'insiders', - hasUserBeenNotified: false, - isChannelUsingDefaultConfiguration: true - }, - { - installChannel: 'off', - // prompt to enroll - vscodeChannel: 'insiders', - hasUserBeenNotified: false, - isChannelUsingDefaultConfiguration: true - } - ]; - - setup(() => { - setupCommon(); - }); - - testsForHandleEdgeCaseII.forEach(testParams => { - const testName = `Insiders Install Prompt is displayed when vscode channel = '${ - testParams.vscodeChannel - }', extension channel = '${testParams.extensionChannel}', install channel = '${ - testParams.installChannel - }', ${ - !testParams.hasUserBeenNotified - ? 'user has not been notified to install insiders' - : 'user has already been notified to install insiders' - }, isChannelUsingDefaultConfiguration = ${testParams.isChannelUsingDefaultConfiguration}`; - test(testName, async () => { - setState(testParams, true, false); - - await insidersExtensionService.handleEdgeCases( - testParams.installChannel, - testParams.isChannelUsingDefaultConfiguration! - ); - - verifyAll(); - verify(hasUserBeenNotifiedState.value).once(); - }); - }); - }); - - suite('Case III - Verify Insiders channel is set to off when conditions are met', async () => { - const testsForHandleEdgeCaseIII: TestInfo[] = [ - { - installChannel: 'daily', - // skip enroll - vscodeChannel: 'stable', - // disable - // with installChannel from above - extensionChannel: 'stable' - }, - { - installChannel: 'weekly', - // skip enroll - vscodeChannel: 'stable', - // disable - // with installChannel from above - extensionChannel: 'stable' - } - ]; - - setup(() => { - setupCommon(); - }); - - testsForHandleEdgeCaseIII.forEach(testParams => { - const testName = `Insiders channel is set to off when vscode channel = '${ - testParams.vscodeChannel - }', extension channel = '${testParams.extensionChannel}', install channel = '${ - testParams.installChannel - }', ${ - !testParams.hasUserBeenNotified - ? 'user has not been notified to install insiders' - : 'user has already been notified to install insiders' - }, isChannelUsingDefaultConfiguration = ${testParams.isChannelUsingDefaultConfiguration}`; - test(testName, async () => { - setState(testParams, false, true); - - await insidersExtensionService.handleEdgeCases( - testParams.installChannel, - false // isDefault - ); - - verifyAll(); - verify(hasUserBeenNotifiedState.value).never(); - }); - }); - }); - - suite('Case IV - Verify no operation is performed if none of the case conditions are met', async () => { - const testsForHandleEdgeCaseIV: TestInfo[] = [ - { - installChannel: 'daily', - // skip enroll - vscodeChannel: 'insiders', - hasUserBeenNotified: true, - // skip disable - extensionChannel: 'insiders' - }, - { - installChannel: 'daily', - // skip enroll - vscodeChannel: 'insiders', - hasUserBeenNotified: false, - isChannelUsingDefaultConfiguration: false, - // skip disable - extensionChannel: 'insiders' - }, - { - installChannel: 'daily', - // skip enroll - vscodeChannel: 'stable', - // skip disable - extensionChannel: 'insiders' - }, - { - installChannel: 'off', - // skip enroll - vscodeChannel: 'insiders', - hasUserBeenNotified: true - }, - { - installChannel: 'off', - isChannelUsingDefaultConfiguration: true, - // skip enroll - vscodeChannel: 'insiders', - hasUserBeenNotified: true - }, - { - // skip re-enroll - installChannel: 'off', - isChannelUsingDefaultConfiguration: true, - // skip enroll - vscodeChannel: 'stable' - } - ]; - - setup(() => { - setupCommon(); - }); - - testsForHandleEdgeCaseIV.forEach(testParams => { - const testName = `No operation is performed when vscode channel = '${ - testParams.vscodeChannel - }', extension channel = '${testParams.extensionChannel}', install channel = '${ - testParams.installChannel - }', ${ - !testParams.hasUserBeenNotified - ? 'user has not been notified to install insiders' - : 'user has already been notified to install insiders' - }, isChannelUsingDefaultConfiguration = ${testParams.isChannelUsingDefaultConfiguration}`; - test(testName, async () => { - setState(testParams, false, false); - - await insidersExtensionService.handleEdgeCases( - testParams.installChannel, - testParams.isChannelUsingDefaultConfiguration || testParams.installChannel === 'off' - ); - - verifyAll(); - if (testParams.hasUserBeenNotified === undefined) { - verify(hasUserBeenNotifiedState.value).never(); - } else { - verify(hasUserBeenNotifiedState.value).once(); - } - }); - }); - }); -}); - -// tslint:disable-next-line: max-func-body-length -suite('Insiders Extension Service - Function registerCommandsAndHandlers()', () => { - let appEnvironment: IApplicationEnvironment; - let serviceContainer: IServiceContainer; - let extensionChannelService: IExtensionChannelService; - let cmdManager: ICommandManager; - let insidersPrompt: IInsiderExtensionPrompt; - let channelChangeEvent: EventEmitter; - let handleChannel: sinon.SinonStub; - let insidersExtensionService: InsidersExtensionService; - let insidersInstaller: IExtensionBuildInstaller; - setup(() => { - extensionChannelService = mock(ExtensionChannelService); - insidersInstaller = mock(InsidersBuildInstaller); - appEnvironment = mock(ApplicationEnvironment); - cmdManager = mock(CommandManager); - serviceContainer = mock(ServiceContainer); - insidersPrompt = mock(InsidersExtensionPrompt); - channelChangeEvent = new EventEmitter(); - handleChannel = sinon.stub(InsidersExtensionService.prototype, 'handleChannel'); - handleChannel.callsFake(() => Promise.resolve()); - insidersExtensionService = new InsidersExtensionService( - instance(extensionChannelService), - instance(insidersPrompt), - instance(appEnvironment), - instance(cmdManager), - instance(serviceContainer), - instance(insidersInstaller), - [] - ); - }); - - teardown(() => { - sinon.restore(); - channelChangeEvent.dispose(); - }); - - test('Ensure commands and handlers get registered, and disposables returned are in the disposable list', async () => { - const disposable1 = TypeMoq.Mock.ofType(); - const disposable2 = TypeMoq.Mock.ofType(); - const disposable3 = TypeMoq.Mock.ofType(); - const disposable4 = TypeMoq.Mock.ofType(); - when(extensionChannelService.onDidChannelChange).thenReturn(() => disposable1.object); - when(cmdManager.registerCommand(Commands.SwitchOffInsidersChannel, anything())).thenReturn(disposable2.object); - when(cmdManager.registerCommand(Commands.SwitchToInsidersDaily, anything())).thenReturn(disposable3.object); - when(cmdManager.registerCommand(Commands.SwitchToInsidersWeekly, anything())).thenReturn(disposable4.object); - - insidersExtensionService.registerCommandsAndHandlers(); - - expect(insidersExtensionService.disposables.length).to.equal(4); - verify(extensionChannelService.onDidChannelChange).once(); - verify(cmdManager.registerCommand(Commands.SwitchOffInsidersChannel, anything())).once(); - verify(cmdManager.registerCommand(Commands.SwitchToInsidersDaily, anything())).once(); - verify(cmdManager.registerCommand(Commands.SwitchToInsidersWeekly, anything())).once(); - }); - - test('Ensure commands and handlers get registered with the correct callback handlers', async () => { - const disposable1 = TypeMoq.Mock.ofType(); - const disposable2 = TypeMoq.Mock.ofType(); - const disposable3 = TypeMoq.Mock.ofType(); - const disposable4 = TypeMoq.Mock.ofType(); - let channelChangedHandler!: Function; - let switchTooffHandler!: Function; - let switchToInsidersDailyHandler!: Function; - let switchToweeklyHandler!: Function; - when(extensionChannelService.onDidChannelChange).thenReturn(cb => { - channelChangedHandler = cb; - return disposable1.object; - }); - when(cmdManager.registerCommand(Commands.SwitchOffInsidersChannel, anything())).thenCall((_, cb) => { - switchTooffHandler = cb; - return disposable2.object; - }); - when(cmdManager.registerCommand(Commands.SwitchToInsidersDaily, anything())).thenCall((_, cb) => { - switchToInsidersDailyHandler = cb; - return disposable3.object; - }); - when(cmdManager.registerCommand(Commands.SwitchToInsidersWeekly, anything())).thenCall((_, cb) => { - switchToweeklyHandler = cb; - return disposable4.object; - }); - - insidersExtensionService.registerCommandsAndHandlers(); - - channelChangedHandler('Some channel'); - assert.ok(handleChannel.calledOnce); - - when(extensionChannelService.updateChannel('off')).thenResolve(); - await switchTooffHandler(); - verify(extensionChannelService.updateChannel('off')).once(); - - when(extensionChannelService.updateChannel('daily')).thenResolve(); - await switchToInsidersDailyHandler(); - verify(extensionChannelService.updateChannel('daily')).once(); - - when(extensionChannelService.updateChannel('weekly')).thenResolve(); - await switchToweeklyHandler(); - verify(extensionChannelService.updateChannel('weekly')).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// tslint:disable:no-any + +import * as assert from 'assert'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { anything, instance, mock, verify, when } from 'ts-mockito'; +import * as TypeMoq from 'typemoq'; +import { EventEmitter } from 'vscode'; +import { ApplicationEnvironment } from '../../../client/common/application/applicationEnvironment'; +import { CommandManager } from '../../../client/common/application/commandManager'; +import { Channel, IApplicationEnvironment, ICommandManager } from '../../../client/common/application/types'; +import { Commands } from '../../../client/common/constants'; +import { ExtensionChannelService } from '../../../client/common/insidersBuild/downloadChannelService'; +import { InsidersExtensionPrompt } from '../../../client/common/insidersBuild/insidersExtensionPrompt'; +import { InsidersExtensionService } from '../../../client/common/insidersBuild/insidersExtensionService'; +import { + ExtensionChannels, + IExtensionChannelRule, + IExtensionChannelService, + IInsiderExtensionPrompt +} from '../../../client/common/insidersBuild/types'; +import { InsidersBuildInstaller } from '../../../client/common/installer/extensionBuildInstaller'; +import { IExtensionBuildInstaller } from '../../../client/common/installer/types'; +import { PersistentState } from '../../../client/common/persistentState'; +import { IDisposable, IPersistentState } from '../../../client/common/types'; +import { createDeferred, createDeferredFromPromise } from '../../../client/common/utils/async'; +import { ServiceContainer } from '../../../client/ioc/container'; +import { IServiceContainer } from '../../../client/ioc/types'; +import { sleep } from '../../../test/core'; + +suite('Insiders Extension Service - Handle channel', () => { + let appEnvironment: IApplicationEnvironment; + let serviceContainer: IServiceContainer; + let extensionChannelService: IExtensionChannelService; + let cmdManager: ICommandManager; + let insidersPrompt: IInsiderExtensionPrompt; + let insidersInstaller: IExtensionBuildInstaller; + let insidersExtensionService: InsidersExtensionService; + setup(() => { + extensionChannelService = mock(ExtensionChannelService); + appEnvironment = mock(ApplicationEnvironment); + cmdManager = mock(CommandManager); + serviceContainer = mock(ServiceContainer); + insidersPrompt = mock(InsidersExtensionPrompt); + insidersInstaller = mock(InsidersBuildInstaller); + insidersExtensionService = new InsidersExtensionService( + instance(extensionChannelService), + instance(insidersPrompt), + instance(appEnvironment), + instance(cmdManager), + instance(serviceContainer), + instance(insidersInstaller), + [] + ); + }); + + teardown(() => { + sinon.restore(); + }); + + test('If insiders is not be installed, handling channel does not do anything and simply returns', async () => { + const channelRule = TypeMoq.Mock.ofType(); + when(serviceContainer.get(IExtensionChannelRule, 'off')).thenReturn(channelRule.object); + channelRule + .setup((c) => c.shouldLookForInsidersBuild(false)) + .returns(() => Promise.resolve(false)) + .verifiable(TypeMoq.Times.once()); + when(insidersInstaller.install()).thenResolve(undefined); + await insidersExtensionService.handleChannel('off'); + verify(insidersInstaller.install()).never(); + channelRule.verifyAll(); + }); + + test('If insiders is required to be installed, handling channel installs the build and prompts user', async () => { + const channelRule = TypeMoq.Mock.ofType(); + when(serviceContainer.get(IExtensionChannelRule, 'weekly')).thenReturn( + channelRule.object + ); + channelRule + .setup((c) => c.shouldLookForInsidersBuild(false)) + .returns(() => Promise.resolve(true)) + .verifiable(TypeMoq.Times.once()); + when(insidersInstaller.install()).thenResolve(undefined); + when(insidersPrompt.promptToReload()).thenResolve(undefined); + await insidersExtensionService.handleChannel('weekly'); + verify(insidersInstaller.install()).once(); + verify(insidersPrompt.promptToReload()).once(); + channelRule.verifyAll(); + }); +}); + +// tslint:disable-next-line: max-func-body-length +suite('Insiders Extension Service - Activation', () => { + let appEnvironment: IApplicationEnvironment; + let serviceContainer: IServiceContainer; + let extensionChannelService: IExtensionChannelService; + let cmdManager: ICommandManager; + let insidersPrompt: IInsiderExtensionPrompt; + let registerCommandsAndHandlers: sinon.SinonStub; + let handleChannel: sinon.SinonStub; + let handleEdgeCases: sinon.SinonStub; + let insidersInstaller: IExtensionBuildInstaller; + let insidersExtensionService: InsidersExtensionService; + let envUITEST_DISABLE_INSIDERSExists = false; + setup(() => { + envUITEST_DISABLE_INSIDERSExists = process.env.UITEST_DISABLE_INSIDERS !== undefined; + delete process.env.UITEST_DISABLE_INSIDERS; + extensionChannelService = mock(ExtensionChannelService); + insidersInstaller = mock(InsidersBuildInstaller); + appEnvironment = mock(ApplicationEnvironment); + cmdManager = mock(CommandManager); + serviceContainer = mock(ServiceContainer); + insidersPrompt = mock(InsidersExtensionPrompt); + handleEdgeCases = sinon.stub(InsidersExtensionService.prototype, 'handleEdgeCases'); + registerCommandsAndHandlers = sinon.stub(InsidersExtensionService.prototype, 'registerCommandsAndHandlers'); + registerCommandsAndHandlers.callsFake(() => Promise.resolve()); + }); + + teardown(() => { + if (envUITEST_DISABLE_INSIDERSExists) { + process.env.UITEST_DISABLE_INSIDERS = '1'; + } + sinon.restore(); + }); + + test('If install channel is handled in the edge cases, do not handle it again using the general way', async () => { + handleChannel = sinon.stub(InsidersExtensionService.prototype, 'handleChannel'); + handleChannel.callsFake(() => Promise.resolve()); + handleEdgeCases.callsFake(() => Promise.resolve(true)); + insidersExtensionService = new InsidersExtensionService( + instance(extensionChannelService), + instance(insidersPrompt), + instance(appEnvironment), + instance(cmdManager), + instance(serviceContainer), + instance(insidersInstaller), + [] + ); + when(extensionChannelService.getChannel()).thenReturn('daily'); + when(extensionChannelService.isChannelUsingDefaultConfiguration).thenReturn(false); + + await insidersExtensionService.activate(); + + verify(extensionChannelService.getChannel()).once(); + verify(extensionChannelService.isChannelUsingDefaultConfiguration).once(); + assert.ok(registerCommandsAndHandlers.calledOnce); + assert.ok(handleEdgeCases.calledOnce); + assert.ok(handleEdgeCases.calledWith('daily', false)); + assert.ok(handleChannel.notCalled); + }); + + test('If install channel is not handled in the edge cases, handle it using the general way', async () => { + handleChannel = sinon.stub(InsidersExtensionService.prototype, 'handleChannel'); + handleChannel.callsFake(() => Promise.resolve()); + handleEdgeCases.callsFake(() => Promise.resolve(false)); + insidersExtensionService = new InsidersExtensionService( + instance(extensionChannelService), + instance(insidersPrompt), + instance(appEnvironment), + instance(cmdManager), + instance(serviceContainer), + instance(insidersInstaller), + [] + ); + when(extensionChannelService.getChannel()).thenReturn('daily'); + when(extensionChannelService.isChannelUsingDefaultConfiguration).thenReturn(false); + + await insidersExtensionService.activate(); + + verify(extensionChannelService.getChannel()).once(); + verify(extensionChannelService.isChannelUsingDefaultConfiguration).once(); + assert.ok(registerCommandsAndHandlers.calledOnce); + assert.ok(handleEdgeCases.calledOnce); + assert.ok(handleEdgeCases.calledWith('daily', false)); + assert.ok(handleChannel.calledOnce); + }); + + test('Ensure channels are reliably handled in the background', async () => { + const handleChannelsDeferred = createDeferred(); + handleChannel = sinon.stub(InsidersExtensionService.prototype, 'handleChannel'); + handleChannel.callsFake(() => handleChannelsDeferred.promise); + handleEdgeCases.callsFake(() => Promise.resolve(false)); + insidersExtensionService = new InsidersExtensionService( + instance(extensionChannelService), + instance(insidersPrompt), + instance(appEnvironment), + instance(cmdManager), + instance(serviceContainer), + instance(insidersInstaller), + [] + ); + when(extensionChannelService.getChannel()).thenReturn('daily'); + when(extensionChannelService.isChannelUsingDefaultConfiguration).thenReturn(false); + + const promise = insidersExtensionService.activate(); + const deferred = createDeferredFromPromise(promise); + await sleep(1); + + // Ensure activate() function has completed while handleChannel is still running + assert.equal(deferred.completed, true); + + handleChannelsDeferred.resolve(); + await sleep(1); + + assert.ok(registerCommandsAndHandlers.calledOnce); + assert.ok(handleEdgeCases.calledOnce); + assert.ok(handleChannel.calledOnce); + assert.ok(handleEdgeCases.calledWith('daily', false)); + }); +}); + +// tslint:disable-next-line: max-func-body-length +suite('Insiders Extension Service - Function handleEdgeCases()', () => { + let appEnvironment: TypeMoq.IMock; + let serviceContainer: TypeMoq.IMock; + let extensionChannelService: TypeMoq.IMock; + let cmdManager: TypeMoq.IMock; + let insidersPrompt: TypeMoq.IMock; + let hasUserBeenNotifiedState: IPersistentState; + let insidersInstaller: TypeMoq.IMock; + + let insidersExtensionService: InsidersExtensionService; + + function setupCommon() { + extensionChannelService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); + insidersInstaller = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); + appEnvironment = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); + cmdManager = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); + serviceContainer = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); + insidersPrompt = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); + hasUserBeenNotifiedState = mock(PersistentState); + + insidersExtensionService = new InsidersExtensionService( + extensionChannelService.object, + insidersPrompt.object, + appEnvironment.object, + cmdManager.object, + serviceContainer.object, + insidersInstaller.object, + [] + ); + + insidersPrompt + .setup((p) => p.hasUserBeenNotified) + .returns(() => instance(hasUserBeenNotifiedState)) + // Basically means "we don't care" (necessary for strict mocks). + .verifiable(TypeMoq.Times.atLeast(0)); + } + + function verifyAll() { + // the most important ones: + insidersPrompt.verifyAll(); + insidersInstaller.verifyAll(); + extensionChannelService.verifyAll(); + // the other used interfaces: + appEnvironment.verifyAll(); + serviceContainer.verifyAll(); + cmdManager.verifyAll(); + } + + type TestInfo = { + vscodeChannel?: Channel; + extensionChannel?: Channel; + installChannel: ExtensionChannels; + isChannelUsingDefaultConfiguration?: boolean; + hasUserBeenNotified?: boolean; + }; + + function setState(info: TestInfo, checkPromptEnroll: boolean, checkDisable: boolean) { + if (info.vscodeChannel) { + appEnvironment.setup((e) => e.channel).returns(() => info.vscodeChannel!); + } + if (info.extensionChannel) { + appEnvironment.setup((e) => e.extensionChannel).returns(() => info.extensionChannel!); + } + + if (checkDisable) { + extensionChannelService.setup((ec) => ec.updateChannel('off')).returns(() => Promise.resolve()); + } + if (info.hasUserBeenNotified !== undefined) { + when(hasUserBeenNotifiedState.value).thenReturn(info.hasUserBeenNotified!); + } + if (checkPromptEnroll) { + insidersPrompt.setup((p) => p.promptToInstallInsiders()).returns(() => Promise.resolve()); + } + } + + suite('Case II - Verify Insiders Install Prompt is displayed when conditions are met', async () => { + const testsForHandleEdgeCaseII: TestInfo[] = [ + { + installChannel: 'daily', + // prompt to enroll + vscodeChannel: 'insiders', + hasUserBeenNotified: false, + isChannelUsingDefaultConfiguration: true + }, + { + installChannel: 'off', + // prompt to enroll + vscodeChannel: 'insiders', + hasUserBeenNotified: false, + isChannelUsingDefaultConfiguration: true + } + ]; + + setup(() => { + setupCommon(); + }); + + testsForHandleEdgeCaseII.forEach((testParams) => { + const testName = `Insiders Install Prompt is displayed when vscode channel = '${ + testParams.vscodeChannel + }', extension channel = '${testParams.extensionChannel}', install channel = '${ + testParams.installChannel + }', ${ + !testParams.hasUserBeenNotified + ? 'user has not been notified to install insiders' + : 'user has already been notified to install insiders' + }, isChannelUsingDefaultConfiguration = ${testParams.isChannelUsingDefaultConfiguration}`; + test(testName, async () => { + setState(testParams, true, false); + + await insidersExtensionService.handleEdgeCases( + testParams.installChannel, + testParams.isChannelUsingDefaultConfiguration! + ); + + verifyAll(); + verify(hasUserBeenNotifiedState.value).once(); + }); + }); + }); + + suite('Case III - Verify Insiders channel is set to off when conditions are met', async () => { + const testsForHandleEdgeCaseIII: TestInfo[] = [ + { + installChannel: 'daily', + // skip enroll + vscodeChannel: 'stable', + // disable + // with installChannel from above + extensionChannel: 'stable' + }, + { + installChannel: 'weekly', + // skip enroll + vscodeChannel: 'stable', + // disable + // with installChannel from above + extensionChannel: 'stable' + } + ]; + + setup(() => { + setupCommon(); + }); + + testsForHandleEdgeCaseIII.forEach((testParams) => { + const testName = `Insiders channel is set to off when vscode channel = '${ + testParams.vscodeChannel + }', extension channel = '${testParams.extensionChannel}', install channel = '${ + testParams.installChannel + }', ${ + !testParams.hasUserBeenNotified + ? 'user has not been notified to install insiders' + : 'user has already been notified to install insiders' + }, isChannelUsingDefaultConfiguration = ${testParams.isChannelUsingDefaultConfiguration}`; + test(testName, async () => { + setState(testParams, false, true); + + await insidersExtensionService.handleEdgeCases( + testParams.installChannel, + false // isDefault + ); + + verifyAll(); + verify(hasUserBeenNotifiedState.value).never(); + }); + }); + }); + + suite('Case IV - Verify no operation is performed if none of the case conditions are met', async () => { + const testsForHandleEdgeCaseIV: TestInfo[] = [ + { + installChannel: 'daily', + // skip enroll + vscodeChannel: 'insiders', + hasUserBeenNotified: true, + // skip disable + extensionChannel: 'insiders' + }, + { + installChannel: 'daily', + // skip enroll + vscodeChannel: 'insiders', + hasUserBeenNotified: false, + isChannelUsingDefaultConfiguration: false, + // skip disable + extensionChannel: 'insiders' + }, + { + installChannel: 'daily', + // skip enroll + vscodeChannel: 'stable', + // skip disable + extensionChannel: 'insiders' + }, + { + installChannel: 'off', + // skip enroll + vscodeChannel: 'insiders', + hasUserBeenNotified: true + }, + { + installChannel: 'off', + isChannelUsingDefaultConfiguration: true, + // skip enroll + vscodeChannel: 'insiders', + hasUserBeenNotified: true + }, + { + // skip re-enroll + installChannel: 'off', + isChannelUsingDefaultConfiguration: true, + // skip enroll + vscodeChannel: 'stable' + } + ]; + + setup(() => { + setupCommon(); + }); + + testsForHandleEdgeCaseIV.forEach((testParams) => { + const testName = `No operation is performed when vscode channel = '${ + testParams.vscodeChannel + }', extension channel = '${testParams.extensionChannel}', install channel = '${ + testParams.installChannel + }', ${ + !testParams.hasUserBeenNotified + ? 'user has not been notified to install insiders' + : 'user has already been notified to install insiders' + }, isChannelUsingDefaultConfiguration = ${testParams.isChannelUsingDefaultConfiguration}`; + test(testName, async () => { + setState(testParams, false, false); + + await insidersExtensionService.handleEdgeCases( + testParams.installChannel, + testParams.isChannelUsingDefaultConfiguration || testParams.installChannel === 'off' + ); + + verifyAll(); + if (testParams.hasUserBeenNotified === undefined) { + verify(hasUserBeenNotifiedState.value).never(); + } else { + verify(hasUserBeenNotifiedState.value).once(); + } + }); + }); + }); +}); + +// tslint:disable-next-line: max-func-body-length +suite('Insiders Extension Service - Function registerCommandsAndHandlers()', () => { + let appEnvironment: IApplicationEnvironment; + let serviceContainer: IServiceContainer; + let extensionChannelService: IExtensionChannelService; + let cmdManager: ICommandManager; + let insidersPrompt: IInsiderExtensionPrompt; + let channelChangeEvent: EventEmitter; + let handleChannel: sinon.SinonStub; + let insidersExtensionService: InsidersExtensionService; + let insidersInstaller: IExtensionBuildInstaller; + setup(() => { + extensionChannelService = mock(ExtensionChannelService); + insidersInstaller = mock(InsidersBuildInstaller); + appEnvironment = mock(ApplicationEnvironment); + cmdManager = mock(CommandManager); + serviceContainer = mock(ServiceContainer); + insidersPrompt = mock(InsidersExtensionPrompt); + channelChangeEvent = new EventEmitter(); + handleChannel = sinon.stub(InsidersExtensionService.prototype, 'handleChannel'); + handleChannel.callsFake(() => Promise.resolve()); + insidersExtensionService = new InsidersExtensionService( + instance(extensionChannelService), + instance(insidersPrompt), + instance(appEnvironment), + instance(cmdManager), + instance(serviceContainer), + instance(insidersInstaller), + [] + ); + }); + + teardown(() => { + sinon.restore(); + channelChangeEvent.dispose(); + }); + + test('Ensure commands and handlers get registered, and disposables returned are in the disposable list', async () => { + const disposable1 = TypeMoq.Mock.ofType(); + const disposable2 = TypeMoq.Mock.ofType(); + const disposable3 = TypeMoq.Mock.ofType(); + const disposable4 = TypeMoq.Mock.ofType(); + when(extensionChannelService.onDidChannelChange).thenReturn(() => disposable1.object); + when(cmdManager.registerCommand(Commands.SwitchOffInsidersChannel, anything())).thenReturn(disposable2.object); + when(cmdManager.registerCommand(Commands.SwitchToInsidersDaily, anything())).thenReturn(disposable3.object); + when(cmdManager.registerCommand(Commands.SwitchToInsidersWeekly, anything())).thenReturn(disposable4.object); + + insidersExtensionService.registerCommandsAndHandlers(); + + expect(insidersExtensionService.disposables.length).to.equal(4); + verify(extensionChannelService.onDidChannelChange).once(); + verify(cmdManager.registerCommand(Commands.SwitchOffInsidersChannel, anything())).once(); + verify(cmdManager.registerCommand(Commands.SwitchToInsidersDaily, anything())).once(); + verify(cmdManager.registerCommand(Commands.SwitchToInsidersWeekly, anything())).once(); + }); + + test('Ensure commands and handlers get registered with the correct callback handlers', async () => { + const disposable1 = TypeMoq.Mock.ofType(); + const disposable2 = TypeMoq.Mock.ofType(); + const disposable3 = TypeMoq.Mock.ofType(); + const disposable4 = TypeMoq.Mock.ofType(); + let channelChangedHandler!: Function; + let switchTooffHandler!: Function; + let switchToInsidersDailyHandler!: Function; + let switchToweeklyHandler!: Function; + when(extensionChannelService.onDidChannelChange).thenReturn((cb) => { + channelChangedHandler = cb; + return disposable1.object; + }); + when(cmdManager.registerCommand(Commands.SwitchOffInsidersChannel, anything())).thenCall((_, cb) => { + switchTooffHandler = cb; + return disposable2.object; + }); + when(cmdManager.registerCommand(Commands.SwitchToInsidersDaily, anything())).thenCall((_, cb) => { + switchToInsidersDailyHandler = cb; + return disposable3.object; + }); + when(cmdManager.registerCommand(Commands.SwitchToInsidersWeekly, anything())).thenCall((_, cb) => { + switchToweeklyHandler = cb; + return disposable4.object; + }); + + insidersExtensionService.registerCommandsAndHandlers(); + + channelChangedHandler('Some channel'); + assert.ok(handleChannel.calledOnce); + + when(extensionChannelService.updateChannel('off')).thenResolve(); + await switchTooffHandler(); + verify(extensionChannelService.updateChannel('off')).once(); + + when(extensionChannelService.updateChannel('daily')).thenResolve(); + await switchToInsidersDailyHandler(); + verify(extensionChannelService.updateChannel('daily')).once(); + + when(extensionChannelService.updateChannel('weekly')).thenResolve(); + await switchToweeklyHandler(); + verify(extensionChannelService.updateChannel('weekly')).once(); + }); +}); diff --git a/src/test/common/installer.test.ts b/src/test/common/installer.test.ts index dc52ebc5ebfb..84789ca472fd 100644 --- a/src/test/common/installer.test.ts +++ b/src/test/common/installer.test.ts @@ -167,7 +167,7 @@ suite('Installer', () => { await installer.isInstalled(product, resource); await checkInstalledDef.promise; } - getNamesAndValues(Product).forEach(prod => { + getNamesAndValues(Product).forEach((prod) => { test(`Ensure isInstalled for Product: '${prod.name}' executes the right command`, async () => { ioc.serviceManager.addSingletonInstance( IModuleInstaller, @@ -199,9 +199,9 @@ suite('Installer', () => { const installer = ioc.serviceContainer.get(IInstaller); const checkInstalledDef = createDeferred(); const moduleInstallers = ioc.serviceContainer.getAll(IModuleInstaller); - const moduleInstallerOne = moduleInstallers.find(item => item.displayName === 'two')!; + const moduleInstallerOne = moduleInstallers.find((item) => item.displayName === 'two')!; - moduleInstallerOne.on('installModule', moduleName => { + moduleInstallerOne.on('installModule', (moduleName) => { const installName = installer.translateProductToModuleName(product, ModuleNamePurpose.install); if (installName === moduleName) { checkInstalledDef.resolve(); @@ -210,7 +210,7 @@ suite('Installer', () => { await installer.install(product); await checkInstalledDef.promise; } - getNamesAndValues(Product).forEach(prod => { + getNamesAndValues(Product).forEach((prod) => { test(`Ensure install for Product: '${prod.name}' executes the right command in IModuleInstaller`, async () => { ioc.serviceManager.addSingletonInstance( IModuleInstaller, diff --git a/src/test/common/installer/channelManager.unit.test.ts b/src/test/common/installer/channelManager.unit.test.ts index aa9b72cf1bf3..5517fed9134d 100644 --- a/src/test/common/installer/channelManager.unit.test.ts +++ b/src/test/common/installer/channelManager.unit.test.ts @@ -28,7 +28,7 @@ suite('InstallationChannelManager - getInstallationChannel()', () => { setup(() => { serviceContainer = TypeMoq.Mock.ofType(); appShell = TypeMoq.Mock.ofType(); - serviceContainer.setup(s => s.get(IApplicationShell)).returns(() => appShell.object); + serviceContainer.setup((s) => s.get(IApplicationShell)).returns(() => appShell.object); }); teardown(() => { @@ -37,10 +37,10 @@ suite('InstallationChannelManager - getInstallationChannel()', () => { test('If there is exactly one installation channel, return it', async () => { const moduleInstaller = TypeMoq.Mock.ofType(); - moduleInstaller.setup(m => m.name).returns(() => 'singleChannel'); + moduleInstaller.setup((m) => m.name).returns(() => 'singleChannel'); moduleInstaller // tslint:disable-next-line:no-any - .setup(m => (m as any).then) + .setup((m) => (m as any).then) .returns(() => undefined); getInstallationChannels = sinon.stub(InstallationChannelManager.prototype, 'getInstallationChannels'); getInstallationChannels.resolves([moduleInstaller.object]); @@ -67,19 +67,19 @@ suite('InstallationChannelManager - getInstallationChannel()', () => { test('If no channel is selected in the quickpick, return undefined', async () => { const moduleInstaller1 = TypeMoq.Mock.ofType(); - moduleInstaller1.setup(m => m.displayName).returns(() => 'moduleInstaller1'); + moduleInstaller1.setup((m) => m.displayName).returns(() => 'moduleInstaller1'); moduleInstaller1 // tslint:disable-next-line:no-any - .setup(m => (m as any).then) + .setup((m) => (m as any).then) .returns(() => undefined); const moduleInstaller2 = TypeMoq.Mock.ofType(); - moduleInstaller2.setup(m => m.displayName).returns(() => 'moduleInstaller2'); + moduleInstaller2.setup((m) => m.displayName).returns(() => 'moduleInstaller2'); moduleInstaller2 // tslint:disable-next-line:no-any - .setup(m => (m as any).then) + .setup((m) => (m as any).then) .returns(() => undefined); appShell - .setup(a => a.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((a) => a.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); getInstallationChannels = sinon.stub(InstallationChannelManager.prototype, 'getInstallationChannels'); @@ -96,16 +96,16 @@ suite('InstallationChannelManager - getInstallationChannel()', () => { test('If multiple channels are returned by the resource, show quick pick of the channel names and return the selected channel installer', async () => { const moduleInstaller1 = TypeMoq.Mock.ofType(); - moduleInstaller1.setup(m => m.displayName).returns(() => 'moduleInstaller1'); + moduleInstaller1.setup((m) => m.displayName).returns(() => 'moduleInstaller1'); moduleInstaller1 // tslint:disable-next-line:no-any - .setup(m => (m as any).then) + .setup((m) => (m as any).then) .returns(() => undefined); const moduleInstaller2 = TypeMoq.Mock.ofType(); - moduleInstaller2.setup(m => m.displayName).returns(() => 'moduleInstaller2'); + moduleInstaller2.setup((m) => m.displayName).returns(() => 'moduleInstaller2'); moduleInstaller2 // tslint:disable-next-line:no-any - .setup(m => (m as any).then) + .setup((m) => (m as any).then) .returns(() => undefined); const selection = { label: 'some label', @@ -113,7 +113,7 @@ suite('InstallationChannelManager - getInstallationChannel()', () => { installer: moduleInstaller2.object }; appShell - .setup(a => a.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((a) => a.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(selection)) .verifiable(TypeMoq.Times.once()); getInstallationChannels = sinon.stub(InstallationChannelManager.prototype, 'getInstallationChannels'); @@ -140,7 +140,7 @@ suite('InstallationChannelManager - getInstallationChannels()', () => { }); test('If no installers are returned by serviceContainer, return an empty list', async () => { - serviceContainer.setup(s => s.getAll(IModuleInstaller)).returns(() => []); + serviceContainer.setup((s) => s.getAll(IModuleInstaller)).returns(() => []); installChannelManager = new InstallationChannelManager(serviceContainer.object); const channel = await installChannelManager.getInstallationChannels(resource); assert.deepEqual(channel, []); @@ -153,10 +153,10 @@ suite('InstallationChannelManager - getInstallationChannels()', () => { const moduleInstaller = TypeMoq.Mock.ofType(); moduleInstaller // tslint:disable-next-line:no-any - .setup(m => (m as any).then) + .setup((m) => (m as any).then) .returns(() => undefined); - moduleInstaller.setup(m => m.priority).returns(() => 1); - moduleInstaller.setup(m => m.isSupported(resource)).returns(() => Promise.resolve(i % 2 === 0)); + moduleInstaller.setup((m) => m.priority).returns(() => 1); + moduleInstaller.setup((m) => m.isSupported(resource)).returns(() => Promise.resolve(i % 2 === 0)); moduleInstallers.push(moduleInstaller.object); } // Setup 3 installers with priority 2, where two are supported and other is not @@ -164,10 +164,10 @@ suite('InstallationChannelManager - getInstallationChannels()', () => { const moduleInstaller = TypeMoq.Mock.ofType(); moduleInstaller // tslint:disable-next-line:no-any - .setup(m => (m as any).then) + .setup((m) => (m as any).then) .returns(() => undefined); - moduleInstaller.setup(m => m.priority).returns(() => 2); - moduleInstaller.setup(m => m.isSupported(resource)).returns(() => Promise.resolve(i % 2 === 0)); + moduleInstaller.setup((m) => m.priority).returns(() => 2); + moduleInstaller.setup((m) => m.isSupported(resource)).returns(() => Promise.resolve(i % 2 === 0)); moduleInstallers.push(moduleInstaller.object); } // Setup 2 installers with priority 3, but none are supported @@ -175,13 +175,13 @@ suite('InstallationChannelManager - getInstallationChannels()', () => { const moduleInstaller = TypeMoq.Mock.ofType(); moduleInstaller // tslint:disable-next-line:no-any - .setup(m => (m as any).then) + .setup((m) => (m as any).then) .returns(() => undefined); - moduleInstaller.setup(m => m.priority).returns(() => 3); - moduleInstaller.setup(m => m.isSupported(resource)).returns(() => Promise.resolve(false)); + moduleInstaller.setup((m) => m.priority).returns(() => 3); + moduleInstaller.setup((m) => m.isSupported(resource)).returns(() => Promise.resolve(false)); moduleInstallers.push(moduleInstaller.object); } - serviceContainer.setup(s => s.getAll(IModuleInstaller)).returns(() => moduleInstallers); + serviceContainer.setup((s) => s.getAll(IModuleInstaller)).returns(() => moduleInstallers); installChannelManager = new InstallationChannelManager(serviceContainer.object); const channels = await installChannelManager.getInstallationChannels(resource); // Verify that highest supported priority is 2, so number of installers supported with that priority is 2 @@ -206,10 +206,10 @@ suite('InstallationChannelManager - showNoInstallersMessage()', () => { test('If no active interpreter is returned, simply return', async () => { serviceContainer - .setup(s => s.get(IInterpreterService)) + .setup((s) => s.get(IInterpreterService)) .returns(() => interpreterService.object); - serviceContainer.setup(s => s.get(IApplicationShell)).verifiable(TypeMoq.Times.never()); - interpreterService.setup(i => i.getActiveInterpreter(resource)).returns(() => Promise.resolve(undefined)); + serviceContainer.setup((s) => s.get(IApplicationShell)).verifiable(TypeMoq.Times.never()); + interpreterService.setup((i) => i.getActiveInterpreter(resource)).returns(() => Promise.resolve(undefined)); installChannelManager = new InstallationChannelManager(serviceContainer.object); await installChannelManager.showNoInstallersMessage(resource); serviceContainer.verifyAll(); @@ -221,18 +221,18 @@ suite('InstallationChannelManager - showNoInstallersMessage()', () => { }; const appShell = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(IInterpreterService)) + .setup((s) => s.get(IInterpreterService)) .returns(() => interpreterService.object); serviceContainer - .setup(s => s.get(IApplicationShell)) + .setup((s) => s.get(IApplicationShell)) .returns(() => appShell.object) .verifiable(TypeMoq.Times.once()); interpreterService - .setup(i => i.getActiveInterpreter(resource)) + .setup((i) => i.getActiveInterpreter(resource)) // tslint:disable-next-line: no-any .returns(() => Promise.resolve(activeInterpreter as any)); appShell - .setup(a => a.showErrorMessage(Installer.noCondaOrPipInstaller(), Installer.searchForHelp())) + .setup((a) => a.showErrorMessage(Installer.noCondaOrPipInstaller(), Installer.searchForHelp())) .verifiable(TypeMoq.Times.once()); installChannelManager = new InstallationChannelManager(serviceContainer.object); await installChannelManager.showNoInstallersMessage(resource); @@ -246,18 +246,18 @@ suite('InstallationChannelManager - showNoInstallersMessage()', () => { }; const appShell = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(IInterpreterService)) + .setup((s) => s.get(IInterpreterService)) .returns(() => interpreterService.object); serviceContainer - .setup(s => s.get(IApplicationShell)) + .setup((s) => s.get(IApplicationShell)) .returns(() => appShell.object) .verifiable(TypeMoq.Times.once()); interpreterService - .setup(i => i.getActiveInterpreter(resource)) + .setup((i) => i.getActiveInterpreter(resource)) // tslint:disable-next-line: no-any .returns(() => Promise.resolve(activeInterpreter as any)); appShell - .setup(a => a.showErrorMessage(Installer.noPipInstaller(), Installer.searchForHelp())) + .setup((a) => a.showErrorMessage(Installer.noPipInstaller(), Installer.searchForHelp())) .verifiable(TypeMoq.Times.once()); installChannelManager = new InstallationChannelManager(serviceContainer.object); await installChannelManager.showNoInstallersMessage(resource); @@ -265,7 +265,7 @@ suite('InstallationChannelManager - showNoInstallersMessage()', () => { appShell.verifyAll(); }); - [InterpreterType.Conda, InterpreterType.Pipenv].forEach(interpreterType => { + [InterpreterType.Conda, InterpreterType.Pipenv].forEach((interpreterType) => { [ { osName: 'Windows', @@ -282,7 +282,7 @@ suite('InstallationChannelManager - showNoInstallersMessage()', () => { isWindows: false, isMac: true } - ].forEach(testParams => { + ].forEach((testParams) => { const expectedURL = `https://www.bing.com/search?q=Install Pip ${testParams.osName} ${ interpreterType === InterpreterType.Conda ? 'Conda' : '' }`; @@ -297,28 +297,28 @@ suite('InstallationChannelManager - showNoInstallersMessage()', () => { const appShell = TypeMoq.Mock.ofType(); const platformService = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(IInterpreterService)) + .setup((s) => s.get(IInterpreterService)) .returns(() => interpreterService.object); serviceContainer - .setup(s => s.get(IApplicationShell)) + .setup((s) => s.get(IApplicationShell)) .returns(() => appShell.object) .verifiable(TypeMoq.Times.once()); serviceContainer - .setup(s => s.get(IPlatformService)) + .setup((s) => s.get(IPlatformService)) .returns(() => platformService.object) .verifiable(TypeMoq.Times.once()); interpreterService - .setup(i => i.getActiveInterpreter(resource)) + .setup((i) => i.getActiveInterpreter(resource)) // tslint:disable-next-line: no-any .returns(() => Promise.resolve(activeInterpreter as any)); - platformService.setup(p => p.isWindows).returns(() => testParams.isWindows); - platformService.setup(p => p.isMac).returns(() => testParams.isMac); + platformService.setup((p) => p.isWindows).returns(() => testParams.isWindows); + platformService.setup((p) => p.isMac).returns(() => testParams.isMac); appShell - .setup(a => a.showErrorMessage(TypeMoq.It.isAny(), Installer.searchForHelp())) + .setup((a) => a.showErrorMessage(TypeMoq.It.isAny(), Installer.searchForHelp())) .returns(() => Promise.resolve(Installer.searchForHelp())) .verifiable(TypeMoq.Times.once()); appShell - .setup(a => a.openUrl(expectedURL)) + .setup((a) => a.openUrl(expectedURL)) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); installChannelManager = new InstallationChannelManager(serviceContainer.object); @@ -335,27 +335,27 @@ suite('InstallationChannelManager - showNoInstallersMessage()', () => { const appShell = TypeMoq.Mock.ofType(); const platformService = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(IInterpreterService)) + .setup((s) => s.get(IInterpreterService)) .returns(() => interpreterService.object); serviceContainer - .setup(s => s.get(IApplicationShell)) + .setup((s) => s.get(IApplicationShell)) .returns(() => appShell.object) .verifiable(TypeMoq.Times.once()); serviceContainer - .setup(s => s.get(IPlatformService)) + .setup((s) => s.get(IPlatformService)) .returns(() => platformService.object) .verifiable(TypeMoq.Times.never()); interpreterService - .setup(i => i.getActiveInterpreter(resource)) + .setup((i) => i.getActiveInterpreter(resource)) // tslint:disable-next-line: no-any .returns(() => Promise.resolve(activeInterpreter as any)); - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); appShell - .setup(a => a.showErrorMessage(TypeMoq.It.isAnyString(), Installer.searchForHelp())) + .setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString(), Installer.searchForHelp())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); appShell - .setup(a => a.openUrl(TypeMoq.It.isAny())) + .setup((a) => a.openUrl(TypeMoq.It.isAny())) .returns(() => undefined) .verifiable(TypeMoq.Times.never()); installChannelManager = new InstallationChannelManager(serviceContainer.object); diff --git a/src/test/common/installer/extensionBuildInstaller.unit.test.ts b/src/test/common/installer/extensionBuildInstaller.unit.test.ts index 13cbd8e8ef88..c56e69038f21 100644 --- a/src/test/common/installer/extensionBuildInstaller.unit.test.ts +++ b/src/test/common/installer/extensionBuildInstaller.unit.test.ts @@ -1,100 +1,100 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -// tslint:disable:no-any max-func-body-length no-invalid-this - -import * as assert from 'assert'; -import { expect } from 'chai'; -import { anything, instance, mock, verify, when } from 'ts-mockito'; -import { Uri } from 'vscode'; -import { CommandManager } from '../../../client/common/application/commandManager'; -import { ICommandManager } from '../../../client/common/application/types'; -import { PVSC_EXTENSION_ID } from '../../../client/common/constants'; -import { - developmentBuildUri, - InsidersBuildInstaller, - StableBuildInstaller, - vsixFileExtension -} from '../../../client/common/installer/extensionBuildInstaller'; -import { FileDownloader } from '../../../client/common/net/fileDownloader'; -import { FileSystem } from '../../../client/common/platform/fileSystem'; -import { IFileSystem } from '../../../client/common/platform/types'; -import { DownloadOptions, IFileDownloader, IOutputChannel } from '../../../client/common/types'; -import { ExtensionChannels } from '../../../client/common/utils/localize'; -import { MockOutputChannel } from '../../../test/mockClasses'; - -suite('Extension build installer - Stable build installer', async () => { - let output: IOutputChannel; - let cmdManager: ICommandManager; - let stableBuildInstaller: StableBuildInstaller; - setup(() => { - output = mock(MockOutputChannel); - cmdManager = mock(CommandManager); - stableBuildInstaller = new StableBuildInstaller(instance(output), instance(cmdManager)); - }); - test('Installing stable build logs progress and installs stable', async () => { - when(output.append(ExtensionChannels.installingStableMessage())).thenReturn(); - when(output.appendLine(ExtensionChannels.installationCompleteMessage())).thenReturn(); - when(cmdManager.executeCommand('workbench.extensions.installExtension', PVSC_EXTENSION_ID)).thenResolve( - undefined - ); - await stableBuildInstaller.install(); - verify(output.append(ExtensionChannels.installingStableMessage())).once(); - verify(output.appendLine(ExtensionChannels.installationCompleteMessage())).once(); - verify(cmdManager.executeCommand('workbench.extensions.installExtension', PVSC_EXTENSION_ID)).once(); - }); -}); - -suite('Extension build installer - Insiders build installer', async () => { - let output: IOutputChannel; - let cmdManager: ICommandManager; - let fileDownloader: IFileDownloader; - let fs: IFileSystem; - let insidersBuildInstaller: InsidersBuildInstaller; - setup(() => { - output = mock(MockOutputChannel); - fileDownloader = mock(FileDownloader); - fs = mock(FileSystem); - cmdManager = mock(CommandManager); - insidersBuildInstaller = new InsidersBuildInstaller( - instance(output), - instance(fileDownloader), - instance(fs), - instance(cmdManager) - ); - }); - test('Installing Insiders build downloads and installs Insiders', async () => { - const vsixFilePath = 'path/to/vsix'; - const options = { - extension: vsixFileExtension, - outputChannel: output, - progressMessagePrefix: ExtensionChannels.downloadingInsidersMessage() - }; - when(output.append(ExtensionChannels.installingInsidersMessage())).thenReturn(); - when(output.appendLine(ExtensionChannels.startingDownloadOutputMessage())).thenReturn(); - when(output.appendLine(ExtensionChannels.downloadCompletedOutputMessage())).thenReturn(); - when(output.appendLine(ExtensionChannels.installationCompleteMessage())).thenReturn(); - when(fileDownloader.downloadFile(developmentBuildUri, anything())).thenCall( - (_, downloadOptions: DownloadOptions) => { - expect(downloadOptions.extension).to.equal(options.extension, 'Incorrect file extension'); - expect(downloadOptions.progressMessagePrefix).to.equal(options.progressMessagePrefix); - return Promise.resolve(vsixFilePath); - } - ); - when(cmdManager.executeCommand('workbench.extensions.installExtension', anything())).thenCall((_, cb) => { - assert.deepEqual(cb, Uri.file(vsixFilePath), 'Wrong VSIX installed'); - }); - when(fs.deleteFile(vsixFilePath)).thenResolve(); - - await insidersBuildInstaller.install(); - - verify(output.append(ExtensionChannels.installingInsidersMessage())).once(); - verify(output.appendLine(ExtensionChannels.startingDownloadOutputMessage())).once(); - verify(output.appendLine(ExtensionChannels.downloadCompletedOutputMessage())).once(); - verify(output.appendLine(ExtensionChannels.installationCompleteMessage())).once(); - verify(cmdManager.executeCommand('workbench.extensions.installExtension', anything())).once(); - verify(fs.deleteFile(vsixFilePath)).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// tslint:disable:no-any max-func-body-length no-invalid-this + +import * as assert from 'assert'; +import { expect } from 'chai'; +import { anything, instance, mock, verify, when } from 'ts-mockito'; +import { Uri } from 'vscode'; +import { CommandManager } from '../../../client/common/application/commandManager'; +import { ICommandManager } from '../../../client/common/application/types'; +import { PVSC_EXTENSION_ID } from '../../../client/common/constants'; +import { + developmentBuildUri, + InsidersBuildInstaller, + StableBuildInstaller, + vsixFileExtension +} from '../../../client/common/installer/extensionBuildInstaller'; +import { FileDownloader } from '../../../client/common/net/fileDownloader'; +import { FileSystem } from '../../../client/common/platform/fileSystem'; +import { IFileSystem } from '../../../client/common/platform/types'; +import { DownloadOptions, IFileDownloader, IOutputChannel } from '../../../client/common/types'; +import { ExtensionChannels } from '../../../client/common/utils/localize'; +import { MockOutputChannel } from '../../../test/mockClasses'; + +suite('Extension build installer - Stable build installer', async () => { + let output: IOutputChannel; + let cmdManager: ICommandManager; + let stableBuildInstaller: StableBuildInstaller; + setup(() => { + output = mock(MockOutputChannel); + cmdManager = mock(CommandManager); + stableBuildInstaller = new StableBuildInstaller(instance(output), instance(cmdManager)); + }); + test('Installing stable build logs progress and installs stable', async () => { + when(output.append(ExtensionChannels.installingStableMessage())).thenReturn(); + when(output.appendLine(ExtensionChannels.installationCompleteMessage())).thenReturn(); + when(cmdManager.executeCommand('workbench.extensions.installExtension', PVSC_EXTENSION_ID)).thenResolve( + undefined + ); + await stableBuildInstaller.install(); + verify(output.append(ExtensionChannels.installingStableMessage())).once(); + verify(output.appendLine(ExtensionChannels.installationCompleteMessage())).once(); + verify(cmdManager.executeCommand('workbench.extensions.installExtension', PVSC_EXTENSION_ID)).once(); + }); +}); + +suite('Extension build installer - Insiders build installer', async () => { + let output: IOutputChannel; + let cmdManager: ICommandManager; + let fileDownloader: IFileDownloader; + let fs: IFileSystem; + let insidersBuildInstaller: InsidersBuildInstaller; + setup(() => { + output = mock(MockOutputChannel); + fileDownloader = mock(FileDownloader); + fs = mock(FileSystem); + cmdManager = mock(CommandManager); + insidersBuildInstaller = new InsidersBuildInstaller( + instance(output), + instance(fileDownloader), + instance(fs), + instance(cmdManager) + ); + }); + test('Installing Insiders build downloads and installs Insiders', async () => { + const vsixFilePath = 'path/to/vsix'; + const options = { + extension: vsixFileExtension, + outputChannel: output, + progressMessagePrefix: ExtensionChannels.downloadingInsidersMessage() + }; + when(output.append(ExtensionChannels.installingInsidersMessage())).thenReturn(); + when(output.appendLine(ExtensionChannels.startingDownloadOutputMessage())).thenReturn(); + when(output.appendLine(ExtensionChannels.downloadCompletedOutputMessage())).thenReturn(); + when(output.appendLine(ExtensionChannels.installationCompleteMessage())).thenReturn(); + when(fileDownloader.downloadFile(developmentBuildUri, anything())).thenCall( + (_, downloadOptions: DownloadOptions) => { + expect(downloadOptions.extension).to.equal(options.extension, 'Incorrect file extension'); + expect(downloadOptions.progressMessagePrefix).to.equal(options.progressMessagePrefix); + return Promise.resolve(vsixFilePath); + } + ); + when(cmdManager.executeCommand('workbench.extensions.installExtension', anything())).thenCall((_, cb) => { + assert.deepEqual(cb, Uri.file(vsixFilePath), 'Wrong VSIX installed'); + }); + when(fs.deleteFile(vsixFilePath)).thenResolve(); + + await insidersBuildInstaller.install(); + + verify(output.append(ExtensionChannels.installingInsidersMessage())).once(); + verify(output.appendLine(ExtensionChannels.startingDownloadOutputMessage())).once(); + verify(output.appendLine(ExtensionChannels.downloadCompletedOutputMessage())).once(); + verify(output.appendLine(ExtensionChannels.installationCompleteMessage())).once(); + verify(cmdManager.executeCommand('workbench.extensions.installExtension', anything())).once(); + verify(fs.deleteFile(vsixFilePath)).once(); + }); +}); diff --git a/src/test/common/installer/installer.invalidPath.unit.test.ts b/src/test/common/installer/installer.invalidPath.unit.test.ts index ba06dc297c10..42f196e7e4d2 100644 --- a/src/test/common/installer/installer.invalidPath.unit.test.ts +++ b/src/test/common/installer/installer.invalidPath.unit.test.ts @@ -22,11 +22,11 @@ use(chaiAsPromised); // tslint:disable: max-func-body-length suite('Module Installer - Invalid Paths', () => { - [undefined, Uri.file('resource')].forEach(resource => { - ['moduleName', path.join('users', 'dev', 'tool', 'executable')].forEach(pathToExecutable => { + [undefined, Uri.file('resource')].forEach((resource) => { + ['moduleName', path.join('users', 'dev', 'tool', 'executable')].forEach((pathToExecutable) => { const isExecutableAModule = path.basename(pathToExecutable) === pathToExecutable; - getNamesAndValues(Product).forEach(product => { + getNamesAndValues(Product).forEach((product) => { let installer: ProductInstaller; let serviceContainer: TypeMoq.IMock; let app: TypeMoq.IMock; @@ -39,37 +39,37 @@ suite('Module Installer - Invalid Paths', () => { const outputChannel = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProductService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IProductService), TypeMoq.It.isAny())) .returns(() => new ProductService()); app = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) .returns(() => app.object); workspaceService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) .returns(() => workspaceService.object); productPathService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProductPathService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IProductPathService), TypeMoq.It.isAny())) .returns(() => productPathService.object); const interpreterService = TypeMoq.Mock.ofType(); const pythonInterpreter = TypeMoq.Mock.ofType(); // tslint:disable-next-line:no-any - pythonInterpreter.setup(i => (i as any).then).returns(() => undefined); + pythonInterpreter.setup((i) => (i as any).then).returns(() => undefined); interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(pythonInterpreter.object)); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) .returns(() => interpreterService.object); persistentState = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPersistentStateFactory), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPersistentStateFactory), TypeMoq.It.isAny())) .returns(() => persistentState.object); installer = new ProductInstaller(serviceContainer.object, outputChannel.object); @@ -95,18 +95,18 @@ suite('Module Installer - Invalid Paths', () => { // If the path to executable is a module, then we won't display error message indicating path is invalid. productPathService - .setup(p => + .setup((p) => p.getExecutableNameFromSettings(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource)) ) .returns(() => pathToExecutable) .verifiable(TypeMoq.Times.atLeast(isExecutableAModule ? 0 : 1)); productPathService - .setup(p => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) + .setup((p) => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) .returns(() => isExecutableAModule) .verifiable(TypeMoq.Times.atLeastOnce()); const anyParams = [0, 1, 2, 3, 4, 5].map(() => TypeMoq.It.isAny()); - app.setup(a => a.showErrorMessage(TypeMoq.It.isAny(), ...anyParams)) - .callback(message => { + app.setup((a) => a.showErrorMessage(TypeMoq.It.isAny(), ...anyParams)) + .callback((message) => { if (!isExecutableAModule) { expect(message).contains(pathToExecutable); } @@ -114,10 +114,10 @@ suite('Module Installer - Invalid Paths', () => { .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.exactly(1)); const persistValue = TypeMoq.Mock.ofType>(); - persistValue.setup(pv => pv.value).returns(() => false); - persistValue.setup(pv => pv.updateValue(TypeMoq.It.isValue(true))); + persistValue.setup((pv) => pv.value).returns(() => false); + persistValue.setup((pv) => pv.updateValue(TypeMoq.It.isValue(true))); persistentState - .setup(ps => + .setup((ps) => ps.createGlobalPersistentState( TypeMoq.It.isAnyString(), TypeMoq.It.isValue(undefined) diff --git a/src/test/common/installer/installer.unit.test.ts b/src/test/common/installer/installer.unit.test.ts index 51ae107da050..5e78b88968d5 100644 --- a/src/test/common/installer/installer.unit.test.ts +++ b/src/test/common/installer/installer.unit.test.ts @@ -61,12 +61,12 @@ import { sleep } from '../../common'; use(chaiAsPromised); suite('Module Installer only', () => { - [undefined, Uri.file('resource')].forEach(resource => { + [undefined, Uri.file('resource')].forEach((resource) => { // tslint:disable-next-line: cyclomatic-complexity getNamesAndValues(Product) .concat([{ name: 'Unkown product', value: 404 }]) // tslint:disable-next-line: cyclomatic-complexity - .forEach(product => { + .forEach((product) => { let disposables: Disposable[] = []; let installer: ProductInstaller; let installationChannel: TypeMoq.IMock; @@ -88,64 +88,64 @@ suite('Module Installer only', () => { disposables = []; serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) .returns(() => disposables); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProductService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IProductService), TypeMoq.It.isAny())) .returns(() => productService); installationChannel = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInstallationChannelManager), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IInstallationChannelManager), TypeMoq.It.isAny())) .returns(() => installationChannel.object); app = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) .returns(() => app.object); workspaceService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) .returns(() => workspaceService.object); persistentStore = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPersistentStateFactory), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPersistentStateFactory), TypeMoq.It.isAny())) .returns(() => persistentStore.object); moduleInstaller = TypeMoq.Mock.ofType(); // tslint:disable-next-line:no-any moduleInstaller.setup((x: any) => x.then).returns(() => undefined); installationChannel - .setup(i => i.getInstallationChannel(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((i) => i.getInstallationChannel(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(moduleInstaller.object)); installationChannel - .setup(i => i.getInstallationChannel(TypeMoq.It.isAny())) + .setup((i) => i.getInstallationChannel(TypeMoq.It.isAny())) .returns(() => Promise.resolve(moduleInstaller.object)); productPathService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProductPathService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IProductPathService), TypeMoq.It.isAny())) .returns(() => productPathService.object); productPathService - .setup(p => p.getExecutableNameFromSettings(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) + .setup((p) => p.getExecutableNameFromSettings(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) .returns(() => 'xyz'); productPathService - .setup(p => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) + .setup((p) => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) .returns(() => true); interpreterService = TypeMoq.Mock.ofType(); const pythonInterpreter = TypeMoq.Mock.ofType(); // tslint:disable-next-line:no-any - pythonInterpreter.setup(i => (i as any).then).returns(() => undefined); + pythonInterpreter.setup((i) => (i as any).then).returns(() => undefined); interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(pythonInterpreter.object)); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) .returns(() => interpreterService.object); installer = new ProductInstaller(serviceContainer.object, outputChannel.object); }); teardown(() => { // This must be resolved, else all subsequent tests will fail (as this same promise will be used for other tests). promptDeferred.resolve(); - disposables.forEach(disposable => { + disposables.forEach((disposable) => { if (disposable) { disposable.dispose(); } @@ -158,7 +158,7 @@ suite('Module Installer only', () => { test(`If product type is not recognized, throw error (${ resource ? 'With a resource' : 'without a resource' })`, async () => { - app.setup(a => + app.setup((a) => a.showErrorMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ).verifiable(TypeMoq.Times.never()); const getProductType = sinon.stub(ProductService.prototype, 'getProductType'); @@ -180,12 +180,12 @@ suite('Module Installer only', () => { }), print the instructions to install Ctags into the output channel`, async () => { const platformService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService))) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) .returns(() => platformService.object); - platformService.setup(p => p.isWindows).returns(() => true); - outputChannel.setup(o => o.appendLine(TypeMoq.It.isAny())).returns(() => undefined); + platformService.setup((p) => p.isWindows).returns(() => true); + outputChannel.setup((o) => o.appendLine(TypeMoq.It.isAny())).returns(() => undefined); outputChannel - .setup(o => + .setup((o) => o.appendLine( 'Install Universal Ctags Win32 to enable support for Workspace Symbols' ) @@ -193,7 +193,7 @@ suite('Module Installer only', () => { .returns(() => undefined) .verifiable(TypeMoq.Times.once()); outputChannel - .setup(o => o.show()) + .setup((o) => o.show()) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); const response = await installer.install(product.value, resource); @@ -205,19 +205,19 @@ suite('Module Installer only', () => { }), install Ctags using the corresponding script`, async () => { const platformService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService))) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) .returns(() => platformService.object); - platformService.setup(p => p.isWindows).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => false); const termianlService = TypeMoq.Mock.ofType(); const terminalServiceFactory = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ITerminalServiceFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(ITerminalServiceFactory))) .returns(() => terminalServiceFactory.object); terminalServiceFactory - .setup(p => p.getTerminalService(resource)) + .setup((p) => p.getTerminalService(resource)) .returns(() => termianlService.object); termianlService - .setup(t => t.sendCommand(CTagsInsllationScript, [])) + .setup((t) => t.sendCommand(CTagsInsllationScript, [])) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); const response = await installer.install(product.value, resource); @@ -229,19 +229,19 @@ suite('Module Installer only', () => { }), but installing Ctags fails with Error, log error and return`, async () => { const platformService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService))) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) .returns(() => platformService.object); - platformService.setup(p => p.isWindows).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => false); const termianlService = TypeMoq.Mock.ofType(); const terminalServiceFactory = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ITerminalServiceFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(ITerminalServiceFactory))) .returns(() => terminalServiceFactory.object); terminalServiceFactory - .setup(p => p.getTerminalService(resource)) + .setup((p) => p.getTerminalService(resource)) .returns(() => termianlService.object); termianlService - .setup(t => t.sendCommand(CTagsInsllationScript, [])) + .setup((t) => t.sendCommand(CTagsInsllationScript, [])) .returns(() => Promise.reject('Kaboom')) .verifiable(TypeMoq.Times.once()); const response = await installer.install(product.value, resource); @@ -253,7 +253,7 @@ suite('Module Installer only', () => { } (${ resource ? 'With a resource' : 'without a resource' }), install module and return response`, async () => { - app.setup(a => + app.setup((a) => a.showErrorMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve('Yes')) @@ -268,7 +268,7 @@ suite('Module Installer only', () => { test(`If 'No' is selected on the install prompt for the module installer ${product.name} (${ resource ? 'With a resource' : 'without a resource' }), return ignore response`, async () => { - app.setup(a => + app.setup((a) => a.showErrorMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve('No')) @@ -303,10 +303,10 @@ suite('Module Installer only', () => { product.name } (${resource ? 'With a resource' : 'without a resource'})`, async () => { workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) .returns(() => TypeMoq.Mock.ofType().object) .verifiable(TypeMoq.Times.exactly(resource ? 5 : 0)); - app.setup(a => + app.setup((a) => a.showErrorMessage( TypeMoq.It.isAny(), TypeMoq.It.isAny(), @@ -323,10 +323,10 @@ suite('Module Installer only', () => { }) .verifiable(TypeMoq.Times.once()); const persistVal = TypeMoq.Mock.ofType>(); - persistVal.setup(p => p.value).returns(() => false); - persistVal.setup(p => p.updateValue(TypeMoq.It.isValue(true))); + persistVal.setup((p) => p.value).returns(() => false); + persistVal.setup((p) => p.updateValue(TypeMoq.It.isValue(true))); persistentStore - .setup(ps => + .setup((ps) => ps.createGlobalPersistentState( TypeMoq.It.isAnyString(), TypeMoq.It.isValue(undefined) @@ -355,10 +355,10 @@ suite('Module Installer only', () => { product.name } (${resource ? 'With a resource' : 'without a resource'})`, async () => { workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) .returns(() => TypeMoq.Mock.ofType().object) .verifiable(TypeMoq.Times.exactly(resource ? 3 : 0)); - app.setup(a => + app.setup((a) => a.showErrorMessage( TypeMoq.It.isAny(), TypeMoq.It.isAny(), @@ -373,10 +373,10 @@ suite('Module Installer only', () => { .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.exactly(3)); const persistVal = TypeMoq.Mock.ofType>(); - persistVal.setup(p => p.value).returns(() => false); - persistVal.setup(p => p.updateValue(TypeMoq.It.isValue(true))); + persistVal.setup((p) => p.value).returns(() => false); + persistVal.setup((p) => p.updateValue(TypeMoq.It.isValue(true))); persistentStore - .setup(ps => + .setup((ps) => ps.createGlobalPersistentState( TypeMoq.It.isAnyString(), TypeMoq.It.isValue(undefined) @@ -397,10 +397,10 @@ suite('Module Installer only', () => { product.name } (${resource ? 'With a resource' : 'without a resource'})`, async () => { workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) .returns(() => TypeMoq.Mock.ofType().object) .verifiable(TypeMoq.Times.exactly(resource ? 2 : 0)); - app.setup(a => + app.setup((a) => a.showErrorMessage( TypeMoq.It.isAnyString(), TypeMoq.It.isValue('Install'), @@ -415,19 +415,19 @@ suite('Module Installer only', () => { const persistVal = TypeMoq.Mock.ofType>(); let mockPersistVal = false; persistVal - .setup(p => p.value) + .setup((p) => p.value) .returns(() => { return mockPersistVal; }); persistVal - .setup(p => p.updateValue(TypeMoq.It.isValue(true))) + .setup((p) => p.updateValue(TypeMoq.It.isValue(true))) .returns(() => { mockPersistVal = true; return Promise.resolve(); }) .verifiable(TypeMoq.Times.once()); persistentStore - .setup(ps => + .setup((ps) => ps.createGlobalPersistentState( TypeMoq.It.isAnyString(), TypeMoq.It.isValue(undefined) @@ -457,9 +457,9 @@ suite('Module Installer only', () => { product.name } (${resource ? 'With a resource' : 'without a resource'})`, async () => { workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) .returns(() => TypeMoq.Mock.ofType().object); - app.setup(a => + app.setup((a) => a.showErrorMessage( TypeMoq.It.isAnyString(), TypeMoq.It.isValue('Install'), @@ -470,7 +470,7 @@ suite('Module Installer only', () => { return undefined; }) .verifiable(TypeMoq.Times.once()); - app.setup(a => + app.setup((a) => a.showErrorMessage( TypeMoq.It.isAnyString(), TypeMoq.It.isValue('Install'), @@ -485,18 +485,18 @@ suite('Module Installer only', () => { const persistVal = TypeMoq.Mock.ofType>(); let mockPersistVal = false; persistVal - .setup(p => p.value) + .setup((p) => p.value) .returns(() => { return mockPersistVal; }); persistVal - .setup(p => p.updateValue(TypeMoq.It.isValue(true))) + .setup((p) => p.updateValue(TypeMoq.It.isValue(true))) .returns(() => { mockPersistVal = true; return Promise.resolve(); }); persistentStore - .setup(ps => + .setup((ps) => ps.createGlobalPersistentState( TypeMoq.It.isAnyString(), TypeMoq.It.isValue(undefined) @@ -524,7 +524,7 @@ suite('Module Installer only', () => { ); moduleInstaller - .setup(m => + .setup((m) => m.installModule( TypeMoq.It.isValue(moduleName), TypeMoq.It.isValue(resource), @@ -537,7 +537,7 @@ suite('Module Installer only', () => { await installer.install(product.value, resource); } catch (ex) { moduleInstaller.verify( - m => + (m) => m.installModule( TypeMoq.It.isValue(moduleName), TypeMoq.It.isValue(resource), @@ -556,7 +556,7 @@ suite('Module Installer only', () => { ); moduleInstaller - .setup(m => + .setup((m) => m.installModule( TypeMoq.It.isValue(moduleName), TypeMoq.It.isValue(resource), @@ -566,7 +566,7 @@ suite('Module Installer only', () => { .returns(() => Promise.reject(new Error('UnitTesting'))); installationChannel.reset(); installationChannel - .setup(i => i.getInstallationChannel(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((i) => i.getInstallationChannel(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); try { const response = await installer.install(product.value, resource); @@ -584,7 +584,7 @@ suite('Module Installer only', () => { ); moduleInstaller - .setup(m => + .setup((m) => m.installModule( TypeMoq.It.isValue(moduleName), TypeMoq.It.isValue(resource), @@ -597,7 +597,7 @@ suite('Module Installer only', () => { await installer.install(product.value, resource); } catch (ex) { moduleInstaller.verify( - m => + (m) => m.installModule( TypeMoq.It.isValue(moduleName), TypeMoq.It.isValue(resource), @@ -623,17 +623,17 @@ suite('Module Installer only', () => { const pythonExecutionFactory = TypeMoq.Mock.ofType(); const pythonExecutionService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPythonExecutionFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(IPythonExecutionFactory))) .returns(() => pythonExecutionFactory.object); pythonExecutionFactory - .setup(p => p.createActivatedEnvironment(TypeMoq.It.isAny())) + .setup((p) => p.createActivatedEnvironment(TypeMoq.It.isAny())) .returns(() => Promise.resolve(pythonExecutionService.object)); pythonExecutionService // tslint:disable-next-line: no-any - .setup(p => (p as any).then) + .setup((p) => (p as any).then) .returns(() => undefined); pythonExecutionService - .setup(p => p.isModuleInstalled(TypeMoq.It.isAny())) + .setup((p) => p.isModuleInstalled(TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); @@ -647,17 +647,17 @@ suite('Module Installer only', () => { const pythonExecutionFactory = TypeMoq.Mock.ofType(); const pythonExecutionService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPythonExecutionFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(IPythonExecutionFactory))) .returns(() => pythonExecutionFactory.object); pythonExecutionFactory - .setup(p => p.createActivatedEnvironment(TypeMoq.It.isAny())) + .setup((p) => p.createActivatedEnvironment(TypeMoq.It.isAny())) .returns(() => Promise.resolve(pythonExecutionService.object)); pythonExecutionService // tslint:disable-next-line: no-any - .setup(p => (p as any).then) + .setup((p) => (p as any).then) .returns(() => undefined); pythonExecutionService - .setup(p => p.isModuleInstalled(TypeMoq.It.isAny())) + .setup((p) => p.isModuleInstalled(TypeMoq.It.isAny())) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); @@ -672,26 +672,26 @@ suite('Module Installer only', () => { const processServiceFactory = TypeMoq.Mock.ofType(); const processService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(IProcessServiceFactory)) + .setup((c) => c.get(IProcessServiceFactory)) .returns(() => processServiceFactory.object); processServiceFactory - .setup(p => p.create(TypeMoq.It.isAny())) + .setup((p) => p.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(processService.object)); processService // tslint:disable-next-line: no-any - .setup(p => (p as any).then) + .setup((p) => (p as any).then) .returns(() => undefined); const executionResult: ExecutionResult = { stdout: 'output' }; processService - .setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(executionResult)) .verifiable(TypeMoq.Times.once()); productPathService.reset(); productPathService - .setup(p => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) + .setup((p) => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) .returns(() => false); const response = await installer.isInstalled(product.value, resource); @@ -705,23 +705,23 @@ suite('Module Installer only', () => { const processServiceFactory = TypeMoq.Mock.ofType(); const processService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(IProcessServiceFactory)) + .setup((c) => c.get(IProcessServiceFactory)) .returns(() => processServiceFactory.object); processServiceFactory - .setup(p => p.create(TypeMoq.It.isAny())) + .setup((p) => p.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(processService.object)); processService // tslint:disable-next-line: no-any - .setup(p => (p as any).then) + .setup((p) => (p as any).then) .returns(() => undefined); processService - .setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.reject('Kaboom')) .verifiable(TypeMoq.Times.once()); productPathService.reset(); productPathService - .setup(p => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) + .setup((p) => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) .returns(() => false); const response = await installer.isInstalled(product.value, resource); @@ -736,10 +736,10 @@ suite('Module Installer only', () => { resource ? 'With a resource' : 'without a resource' })`, async () => { workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) .returns(() => TypeMoq.Mock.ofType().object) .verifiable(TypeMoq.Times.never()); - app.setup(a => + app.setup((a) => a.showErrorMessage( TypeMoq.It.isAny(), TypeMoq.It.isAny(), @@ -754,10 +754,10 @@ suite('Module Installer only', () => { .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); const persistVal = TypeMoq.Mock.ofType>(); - persistVal.setup(p => p.value).returns(() => false); - persistVal.setup(p => p.updateValue(TypeMoq.It.isValue(true))); + persistVal.setup((p) => p.value).returns(() => false); + persistVal.setup((p) => p.updateValue(TypeMoq.It.isValue(true))); persistentStore - .setup(ps => + .setup((ps) => ps.createGlobalPersistentState( TypeMoq.It.isAnyString(), TypeMoq.It.isValue(undefined) @@ -767,7 +767,7 @@ suite('Module Installer only', () => { interpreterService.reset(); interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); await installer.promptToInstall(product.value, resource); diff --git a/src/test/common/installer/moduleInstaller.unit.test.ts b/src/test/common/installer/moduleInstaller.unit.test.ts index db78793b99ae..618d097d2274 100644 --- a/src/test/common/installer/moduleInstaller.unit.test.ts +++ b/src/test/common/installer/moduleInstaller.unit.test.ts @@ -99,10 +99,10 @@ suite('Module Installer', () => { serviceContainer = TypeMoq.Mock.ofType(); outputChannel = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isValue(STANDARD_OUTPUT_CHANNEL))) + .setup((c) => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isValue(STANDARD_OUTPUT_CHANNEL))) .returns(() => outputChannel.object); appShell = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); installer = new TestModuleInstaller(serviceContainer.object); }); teardown(() => { @@ -117,12 +117,12 @@ suite('Module Installer', () => { rewiremock.enable(); rewiremock('sudo-prompt').with(sudoPromptMock); appShell - .setup(a => a.showErrorMessage(error)) + .setup((a) => a.showErrorMessage(error)) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); outputChannel // tslint:disable-next-line: messages-must-be-localized - .setup(o => o.appendLine(`[Elevated] ${command}`)) + .setup((o) => o.appendLine(`[Elevated] ${command}`)) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); installer.elevatedInstall(execPath, args); @@ -138,16 +138,16 @@ suite('Module Installer', () => { rewiremock.enable(); rewiremock('sudo-prompt').with(sudoPromptMock); outputChannel - .setup(o => o.show()) + .setup((o) => o.show()) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); outputChannel // tslint:disable-next-line: messages-must-be-localized - .setup(o => o.appendLine(`[Elevated] ${command}`)) + .setup((o) => o.appendLine(`[Elevated] ${command}`)) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); outputChannel - .setup(o => o.append(stdout)) + .setup((o) => o.append(stdout)) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); installer.elevatedInstall(execPath, args); @@ -163,16 +163,16 @@ suite('Module Installer', () => { rewiremock('sudo-prompt').with(sudoPromptMock); outputChannel // tslint:disable-next-line: messages-must-be-localized - .setup(o => o.appendLine(`[Elevated] ${command}`)) + .setup((o) => o.appendLine(`[Elevated] ${command}`)) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); outputChannel - .setup(o => o.show()) + .setup((o) => o.show()) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); outputChannel // tslint:disable-next-line: messages-must-be-localized - .setup(o => o.append(`Warning: ${stderr}`)) + .setup((o) => o.append(`Warning: ${stderr}`)) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); installer.elevatedInstall(execPath, args); @@ -180,11 +180,11 @@ suite('Module Installer', () => { }); }); - [CondaInstaller, PipInstaller, PipEnvInstaller, TestModuleInstaller].forEach(installerClass => { + [CondaInstaller, PipInstaller, PipEnvInstaller, TestModuleInstaller].forEach((installerClass) => { // Proxy info is relevant only for PipInstaller. const proxyServers = installerClass === PipInstaller ? ['', 'proxy:1234'] : ['']; - proxyServers.forEach(proxyServer => { - [undefined, Uri.file('/users/dev/xyz')].forEach(resource => { + proxyServers.forEach((proxyServer) => { + [undefined, Uri.file('/users/dev/xyz')].forEach((resource) => { // Conda info is relevant only for CondaInstaller. const condaEnvs = installerClass === CondaInstaller @@ -195,14 +195,14 @@ suite('Module Installer', () => { { name: '', path: path.join('conda with spaces', 'path') } ] : []; - [undefined, ...condaEnvs].forEach(condaEnvInfo => { + [undefined, ...condaEnvs].forEach((condaEnvInfo) => { const testProxySuffix = proxyServer.length === 0 ? 'without proxy info' : 'with proxy info'; const testCondaEnv = condaEnvInfo ? condaEnvInfo.name ? 'without conda name' : 'with conda path' : 'without conda'; - const testSuite = [testProxySuffix, testCondaEnv].filter(item => item.length > 0).join(', '); + const testSuite = [testProxySuffix, testCondaEnv].filter((item) => item.length > 0).join(', '); suite(`${installerClass.name} (${testSuite})`, () => { let disposables: Disposable[] = []; let installationChannel: TypeMoq.IMock; @@ -218,73 +218,75 @@ suite('Module Installer', () => { appShell = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IApplicationShell))) + .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell))) .returns(() => appShell.object); fs = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IFileSystem))) + .setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))) .returns(() => fs.object); disposables = []; serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) .returns(() => disposables); installationChannel = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInstallationChannelManager), TypeMoq.It.isAny())) + .setup((c) => + c.get(TypeMoq.It.isValue(IInstallationChannelManager), TypeMoq.It.isAny()) + ) .returns(() => installationChannel.object); const condaService = TypeMoq.Mock.ofType(); - condaService.setup(c => c.getCondaFile()).returns(() => Promise.resolve(condaExecutable)); + condaService.setup((c) => c.getCondaFile()).returns(() => Promise.resolve(condaExecutable)); condaService - .setup(c => c.getCondaEnvironment(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironment(TypeMoq.It.isAny())) .returns(() => Promise.resolve(condaEnvInfo)); configService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) .returns(() => configService.object); pythonSettings = TypeMoq.Mock.ofType(); - pythonSettings.setup(p => p.pythonPath).returns(() => pythonPath); + pythonSettings.setup((p) => p.pythonPath).returns(() => pythonPath); configService - .setup(c => c.getSettings(TypeMoq.It.isAny())) + .setup((c) => c.getSettings(TypeMoq.It.isAny())) .returns(() => pythonSettings.object); terminalService = TypeMoq.Mock.ofType(); const terminalServiceFactory = TypeMoq.Mock.ofType(); terminalServiceFactory - .setup(f => f.getTerminalService(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((f) => f.getTerminalService(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => terminalService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ITerminalServiceFactory), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ITerminalServiceFactory), TypeMoq.It.isAny())) .returns(() => terminalServiceFactory.object); interpreterService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) .returns(() => interpreterService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ICondaService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ICondaService), TypeMoq.It.isAny())) .returns(() => condaService.object); const workspaceService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) .returns(() => workspaceService.object); const http = TypeMoq.Mock.ofType(); - http.setup(h => h.get(TypeMoq.It.isValue('proxy'), TypeMoq.It.isAny())).returns( + http.setup((h) => h.get(TypeMoq.It.isValue('proxy'), TypeMoq.It.isAny())).returns( () => proxyServer ); workspaceService - .setup(w => w.getConfiguration(TypeMoq.It.isValue('http'))) + .setup((w) => w.getConfiguration(TypeMoq.It.isValue('http'))) .returns(() => http.object); installer = new installerClass(serviceContainer.object); }); teardown(() => { - disposables.forEach(disposable => { + disposables.forEach((disposable) => { if (disposable) { disposable.dispose(); } @@ -293,15 +295,15 @@ suite('Module Installer', () => { }); function setActiveInterpreter(activeInterpreter?: PythonInterpreter) { interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isValue(resource))) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isValue(resource))) .returns(() => Promise.resolve(activeInterpreter)) .verifiable(TypeMoq.Times.atLeastOnce()); } - getModuleNamesForTesting().forEach(product => { + getModuleNamesForTesting().forEach((product) => { const moduleName = product.moduleName; async function installModuleAndVerifyCommand(command: string, expectedArgs: string[]) { terminalService - .setup(t => + .setup((t) => t.sendCommand( TypeMoq.It.isValue(command), TypeMoq.It.isValue(expectedArgs), @@ -317,7 +319,7 @@ suite('Module Installer', () => { if (product.value === Product.pylint) { // tslint:disable-next-line:no-shadowed-variable - generatePythonInterpreterVersions().forEach(interpreterInfo => { + generatePythonInterpreterVersions().forEach((interpreterInfo) => { const majorVersion = interpreterInfo.version ? interpreterInfo.version.major : 0; if (majorVersion === 2) { const testTitle = `Ensure install arg is \'pylint<2.0.0\' in ${ @@ -415,16 +417,16 @@ suite('Module Installer', () => { test(`If 'python.globalModuleInstallation' is set to true and pythonPath directory is read only, do an elevated install`, async () => { const info = TypeMoq.Mock.ofType(); info.setup((t: any) => t.then).returns(() => undefined); - info.setup(t => t.type).returns(() => InterpreterType.Unknown); - info.setup(t => t.version).returns(() => new SemVer('3.5.0-final')); + info.setup((t) => t.type).returns(() => InterpreterType.Unknown); + info.setup((t) => t.version).returns(() => new SemVer('3.5.0-final')); setActiveInterpreter(info.object); - pythonSettings.setup(p => p.globalModuleInstallation).returns(() => true); + pythonSettings.setup((p) => p.globalModuleInstallation).returns(() => true); const elevatedInstall = sinon.stub( TestModuleInstaller.prototype, 'elevatedInstall' ); elevatedInstall.returns(); - fs.setup(f => f.isDirReadonly(path.dirname(pythonPath))).returns(() => + fs.setup((f) => f.isDirReadonly(path.dirname(pythonPath))).returns(() => Promise.resolve(true) ); try { @@ -438,15 +440,15 @@ suite('Module Installer', () => { test(`If 'python.globalModuleInstallation' is set to true and pythonPath directory is not read only, send command to terminal`, async () => { const info = TypeMoq.Mock.ofType(); info.setup((t: any) => t.then).returns(() => undefined); - info.setup(t => t.type).returns(() => InterpreterType.Unknown); - info.setup(t => t.version).returns(() => new SemVer('3.5.0-final')); + info.setup((t) => t.type).returns(() => InterpreterType.Unknown); + info.setup((t) => t.version).returns(() => new SemVer('3.5.0-final')); setActiveInterpreter(info.object); - pythonSettings.setup(p => p.globalModuleInstallation).returns(() => true); - fs.setup(f => f.isDirReadonly(path.dirname(pythonPath))).returns(() => + pythonSettings.setup((p) => p.globalModuleInstallation).returns(() => true); + fs.setup((f) => f.isDirReadonly(path.dirname(pythonPath))).returns(() => Promise.resolve(false) ); terminalService - .setup(t => t.sendCommand(pythonPath, ['-m', 'executionInfo'], undefined)) + .setup((t) => t.sendCommand(pythonPath, ['-m', 'executionInfo'], undefined)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); try { @@ -460,12 +462,12 @@ suite('Module Installer', () => { test(`If 'python.globalModuleInstallation' is not set to true, concanate arguments with '--user' flag and send command to terminal`, async () => { const info = TypeMoq.Mock.ofType(); info.setup((t: any) => t.then).returns(() => undefined); - info.setup(t => t.type).returns(() => InterpreterType.Unknown); - info.setup(t => t.version).returns(() => new SemVer('3.5.0-final')); + info.setup((t) => t.type).returns(() => InterpreterType.Unknown); + info.setup((t) => t.version).returns(() => new SemVer('3.5.0-final')); setActiveInterpreter(info.object); - pythonSettings.setup(p => p.globalModuleInstallation).returns(() => false); + pythonSettings.setup((p) => p.globalModuleInstallation).returns(() => false); terminalService - .setup(t => + .setup((t) => t.sendCommand(pythonPath, ['-m', 'executionInfo', '--user'], undefined) ) .returns(() => Promise.resolve()) @@ -481,17 +483,17 @@ suite('Module Installer', () => { test(`ignores failures in IFileSystem.isDirReadonly()`, async () => { const info = TypeMoq.Mock.ofType(); info.setup((t: any) => t.then).returns(() => undefined); - info.setup(t => t.type).returns(() => InterpreterType.Unknown); - info.setup(t => t.version).returns(() => new SemVer('3.5.0-final')); + info.setup((t) => t.type).returns(() => InterpreterType.Unknown); + info.setup((t) => t.version).returns(() => new SemVer('3.5.0-final')); setActiveInterpreter(info.object); - pythonSettings.setup(p => p.globalModuleInstallation).returns(() => true); + pythonSettings.setup((p) => p.globalModuleInstallation).returns(() => true); const elevatedInstall = sinon.stub( TestModuleInstaller.prototype, 'elevatedInstall' ); elevatedInstall.returns(); const err = new Error('oops!'); - fs.setup(f => f.isDirReadonly(path.dirname(pythonPath))).returns(() => + fs.setup((f) => f.isDirReadonly(path.dirname(pythonPath))).returns(() => Promise.reject(err) ); try { @@ -509,7 +511,7 @@ suite('Module Installer', () => { title: Products.installingModule().format(product.name) }; appShell - .setup(a => a.withProgress(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((a) => a.withProgress(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .callback((expected, _) => assert.deepEqual(expected, options)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); @@ -584,20 +586,20 @@ suite('Module Installer', () => { function generatePythonInterpreterVersions() { const versions: SemVer[] = ['2.7.0-final', '3.4.0-final', '3.5.0-final', '3.6.0-final', '3.7.0-final'].map( - ver => new SemVer(ver) + (ver) => new SemVer(ver) ); - return versions.map(version => { + return versions.map((version) => { const info = TypeMoq.Mock.ofType(); info.setup((t: any) => t.then).returns(() => undefined); - info.setup(t => t.type).returns(() => InterpreterType.VirtualEnv); - info.setup(t => t.version).returns(() => version); + info.setup((t) => t.type).returns(() => InterpreterType.VirtualEnv); + info.setup((t) => t.version).returns(() => version); return info.object; }); } function getModuleNamesForTesting(): { name: string; value: Product; moduleName: string }[] { return getNamesAndValues(Product) - .map(product => { + .map((product) => { let moduleName = ''; const mockSvc = TypeMoq.Mock.ofType().object; const mockOutChnl = TypeMoq.Mock.ofType().object; @@ -609,5 +611,5 @@ function getModuleNamesForTesting(): { name: string; value: Product; moduleName: return; } }) - .filter(item => item !== undefined) as { name: string; value: Product; moduleName: string }[]; + .filter((item) => item !== undefined) as { name: string; value: Product; moduleName: string }[]; } diff --git a/src/test/common/installer/pipEnvInstaller.unit.test.ts b/src/test/common/installer/pipEnvInstaller.unit.test.ts index fd34cb591573..e2e1c17ad678 100644 --- a/src/test/common/installer/pipEnvInstaller.unit.test.ts +++ b/src/test/common/installer/pipEnvInstaller.unit.test.ts @@ -1,78 +1,78 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect } from 'chai'; -import * as TypeMoq from 'typemoq'; -import { Uri } from 'vscode'; -import { PipEnvInstaller } from '../../../client/common/installer/pipEnvInstaller'; -import { - IInterpreterLocatorService, - InterpreterType, - PIPENV_SERVICE, - PythonInterpreter -} from '../../../client/interpreter/contracts'; -import { IServiceContainer } from '../../../client/ioc/types'; - -// tslint:disable-next-line: max-func-body-length -suite('PipEnv installer', async () => { - let serviceContainer: TypeMoq.IMock; - let locatorService: TypeMoq.IMock; - let pipEnvInstaller: PipEnvInstaller; - setup(() => { - serviceContainer = TypeMoq.Mock.ofType(); - locatorService = TypeMoq.Mock.ofType(); - serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterLocatorService), TypeMoq.It.isValue(PIPENV_SERVICE))) - .returns(() => locatorService.object); - pipEnvInstaller = new PipEnvInstaller(serviceContainer.object); - }); - - test('Installer name is pipenv', () => { - expect(pipEnvInstaller.name).to.equal('pipenv'); - }); - - test('Installer priority is 10', () => { - expect(pipEnvInstaller.priority).to.equal(10); - }); - - test('If InterpreterUri is Pipenv interpreter, method isSupported() returns true', async () => { - const interpreter = { - type: InterpreterType.Pipenv - }; - // tslint:disable-next-line: no-any - const result = await pipEnvInstaller.isSupported(interpreter as any); - expect(result).to.equal(true, 'Should be true'); - }); - - test('If InterpreterUri is Python interpreter but not of type Pipenv, method isSupported() returns false', async () => { - const interpreter = { - type: InterpreterType.Conda - }; - // tslint:disable-next-line: no-any - const result = await pipEnvInstaller.isSupported(interpreter as any); - expect(result).to.equal(false, 'Should be false'); - }); - - test('If InterpreterUri is Resource, and if resource contains pipEnv interpreters, return true', async () => { - const resource = Uri.parse('a'); - locatorService - .setup(p => p.getInterpreters(resource)) - .returns(() => - Promise.resolve([ - TypeMoq.Mock.ofType().object, - TypeMoq.Mock.ofType().object - ]) - ); - const result = await pipEnvInstaller.isSupported(resource); - expect(result).to.equal(true, 'Should be true'); - }); - - test('If InterpreterUri is Resource, and if resource does not contain pipEnv interpreters, return false', async () => { - const resource = Uri.parse('a'); - locatorService.setup(p => p.getInterpreters(resource)).returns(() => Promise.resolve([])); - const result = await pipEnvInstaller.isSupported(resource); - expect(result).to.equal(false, 'Should be false'); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { expect } from 'chai'; +import * as TypeMoq from 'typemoq'; +import { Uri } from 'vscode'; +import { PipEnvInstaller } from '../../../client/common/installer/pipEnvInstaller'; +import { + IInterpreterLocatorService, + InterpreterType, + PIPENV_SERVICE, + PythonInterpreter +} from '../../../client/interpreter/contracts'; +import { IServiceContainer } from '../../../client/ioc/types'; + +// tslint:disable-next-line: max-func-body-length +suite('PipEnv installer', async () => { + let serviceContainer: TypeMoq.IMock; + let locatorService: TypeMoq.IMock; + let pipEnvInstaller: PipEnvInstaller; + setup(() => { + serviceContainer = TypeMoq.Mock.ofType(); + locatorService = TypeMoq.Mock.ofType(); + serviceContainer + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterLocatorService), TypeMoq.It.isValue(PIPENV_SERVICE))) + .returns(() => locatorService.object); + pipEnvInstaller = new PipEnvInstaller(serviceContainer.object); + }); + + test('Installer name is pipenv', () => { + expect(pipEnvInstaller.name).to.equal('pipenv'); + }); + + test('Installer priority is 10', () => { + expect(pipEnvInstaller.priority).to.equal(10); + }); + + test('If InterpreterUri is Pipenv interpreter, method isSupported() returns true', async () => { + const interpreter = { + type: InterpreterType.Pipenv + }; + // tslint:disable-next-line: no-any + const result = await pipEnvInstaller.isSupported(interpreter as any); + expect(result).to.equal(true, 'Should be true'); + }); + + test('If InterpreterUri is Python interpreter but not of type Pipenv, method isSupported() returns false', async () => { + const interpreter = { + type: InterpreterType.Conda + }; + // tslint:disable-next-line: no-any + const result = await pipEnvInstaller.isSupported(interpreter as any); + expect(result).to.equal(false, 'Should be false'); + }); + + test('If InterpreterUri is Resource, and if resource contains pipEnv interpreters, return true', async () => { + const resource = Uri.parse('a'); + locatorService + .setup((p) => p.getInterpreters(resource)) + .returns(() => + Promise.resolve([ + TypeMoq.Mock.ofType().object, + TypeMoq.Mock.ofType().object + ]) + ); + const result = await pipEnvInstaller.isSupported(resource); + expect(result).to.equal(true, 'Should be true'); + }); + + test('If InterpreterUri is Resource, and if resource does not contain pipEnv interpreters, return false', async () => { + const resource = Uri.parse('a'); + locatorService.setup((p) => p.getInterpreters(resource)).returns(() => Promise.resolve([])); + const result = await pipEnvInstaller.isSupported(resource); + expect(result).to.equal(false, 'Should be false'); + }); +}); diff --git a/src/test/common/installer/pipInstaller.unit.test.ts b/src/test/common/installer/pipInstaller.unit.test.ts index e36bcc912392..de27c0b4e938 100644 --- a/src/test/common/installer/pipInstaller.unit.test.ts +++ b/src/test/common/installer/pipInstaller.unit.test.ts @@ -1,130 +1,130 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { assert, expect } from 'chai'; -import * as TypeMoq from 'typemoq'; -import { Uri } from 'vscode'; -import { PipInstaller } from '../../../client/common/installer/pipInstaller'; -import { IPythonExecutionFactory, IPythonExecutionService } from '../../../client/common/process/types'; -import { IServiceContainer } from '../../../client/ioc/types'; - -// tslint:disable-next-line: max-func-body-length -suite('Pip installer', async () => { - let serviceContainer: TypeMoq.IMock; - let pythonExecutionFactory: TypeMoq.IMock; - let pipInstaller: PipInstaller; - setup(() => { - serviceContainer = TypeMoq.Mock.ofType(); - pythonExecutionFactory = TypeMoq.Mock.ofType(); - serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPythonExecutionFactory))) - .returns(() => pythonExecutionFactory.object); - pipInstaller = new PipInstaller(serviceContainer.object); - }); - - test('Installer name is Pip', () => { - expect(pipInstaller.name).to.equal('Pip'); - }); - - test('Installer priority is 0', () => { - expect(pipInstaller.priority).to.equal(0); - }); - - test('If InterpreterUri is Python interpreter, Python execution factory is called with the correct arguments', async () => { - const pythonExecutionService = TypeMoq.Mock.ofType(); - const interpreter = { - path: 'pythonPath' - }; - pythonExecutionFactory - .setup(p => p.create(TypeMoq.It.isAny())) - .callback(options => { - assert.deepEqual(options, { resource: undefined, pythonPath: interpreter.path }); - }) - .returns(() => Promise.resolve(pythonExecutionService.object)) - .verifiable(TypeMoq.Times.once()); - pythonExecutionService - // tslint:disable-next-line: no-any - .setup(p => (p as any).then) - .returns(() => undefined); - - // tslint:disable-next-line: no-any - await pipInstaller.isSupported(interpreter as any); - - pythonExecutionFactory.verifyAll(); - }); - - test('If InterpreterUri is Resource, Python execution factory is called with the correct arguments', async () => { - const pythonExecutionService = TypeMoq.Mock.ofType(); - const resource = Uri.parse('a'); - pythonExecutionFactory - .setup(p => p.create(TypeMoq.It.isAny())) - .callback(options => { - assert.deepEqual(options, { resource, pythonPath: undefined }); - }) - .returns(() => Promise.resolve(pythonExecutionService.object)) - .verifiable(TypeMoq.Times.once()); - pythonExecutionService - // tslint:disable-next-line: no-any - .setup(p => (p as any).then) - .returns(() => undefined); - - await pipInstaller.isSupported(resource); - - pythonExecutionFactory.verifyAll(); - }); - - test('Method isSupported() returns true if pip module is installed', async () => { - const pythonExecutionService = TypeMoq.Mock.ofType(); - const resource = Uri.parse('a'); - pythonExecutionFactory - .setup(p => p.create(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(pythonExecutionService.object)); - pythonExecutionService - // tslint:disable-next-line: no-any - .setup(p => (p as any).then) - .returns(() => undefined); - pythonExecutionService.setup(p => p.isModuleInstalled('pip')).returns(() => Promise.resolve(true)); - - const expected = await pipInstaller.isSupported(resource); - - expect(expected).to.equal(true, 'Should be true'); - }); - - test('Method isSupported() returns false if pip module is not installed', async () => { - const pythonExecutionService = TypeMoq.Mock.ofType(); - const resource = Uri.parse('a'); - pythonExecutionFactory - .setup(p => p.create(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(pythonExecutionService.object)); - pythonExecutionService - // tslint:disable-next-line: no-any - .setup(p => (p as any).then) - .returns(() => undefined); - pythonExecutionService.setup(p => p.isModuleInstalled('pip')).returns(() => Promise.resolve(false)); - - const expected = await pipInstaller.isSupported(resource); - - expect(expected).to.equal(false, 'Should be false'); - }); - - test('Method isSupported() returns false if checking if pip module is installed fails with error', async () => { - const pythonExecutionService = TypeMoq.Mock.ofType(); - const resource = Uri.parse('a'); - pythonExecutionFactory - .setup(p => p.create(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(pythonExecutionService.object)); - pythonExecutionService - // tslint:disable-next-line: no-any - .setup(p => (p as any).then) - .returns(() => undefined); - pythonExecutionService - .setup(p => p.isModuleInstalled('pip')) - .returns(() => Promise.reject('Unable to check if module is installed')); - - const expected = await pipInstaller.isSupported(resource); - - expect(expected).to.equal(false, 'Should be false'); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { assert, expect } from 'chai'; +import * as TypeMoq from 'typemoq'; +import { Uri } from 'vscode'; +import { PipInstaller } from '../../../client/common/installer/pipInstaller'; +import { IPythonExecutionFactory, IPythonExecutionService } from '../../../client/common/process/types'; +import { IServiceContainer } from '../../../client/ioc/types'; + +// tslint:disable-next-line: max-func-body-length +suite('Pip installer', async () => { + let serviceContainer: TypeMoq.IMock; + let pythonExecutionFactory: TypeMoq.IMock; + let pipInstaller: PipInstaller; + setup(() => { + serviceContainer = TypeMoq.Mock.ofType(); + pythonExecutionFactory = TypeMoq.Mock.ofType(); + serviceContainer + .setup((c) => c.get(TypeMoq.It.isValue(IPythonExecutionFactory))) + .returns(() => pythonExecutionFactory.object); + pipInstaller = new PipInstaller(serviceContainer.object); + }); + + test('Installer name is Pip', () => { + expect(pipInstaller.name).to.equal('Pip'); + }); + + test('Installer priority is 0', () => { + expect(pipInstaller.priority).to.equal(0); + }); + + test('If InterpreterUri is Python interpreter, Python execution factory is called with the correct arguments', async () => { + const pythonExecutionService = TypeMoq.Mock.ofType(); + const interpreter = { + path: 'pythonPath' + }; + pythonExecutionFactory + .setup((p) => p.create(TypeMoq.It.isAny())) + .callback((options) => { + assert.deepEqual(options, { resource: undefined, pythonPath: interpreter.path }); + }) + .returns(() => Promise.resolve(pythonExecutionService.object)) + .verifiable(TypeMoq.Times.once()); + pythonExecutionService + // tslint:disable-next-line: no-any + .setup((p) => (p as any).then) + .returns(() => undefined); + + // tslint:disable-next-line: no-any + await pipInstaller.isSupported(interpreter as any); + + pythonExecutionFactory.verifyAll(); + }); + + test('If InterpreterUri is Resource, Python execution factory is called with the correct arguments', async () => { + const pythonExecutionService = TypeMoq.Mock.ofType(); + const resource = Uri.parse('a'); + pythonExecutionFactory + .setup((p) => p.create(TypeMoq.It.isAny())) + .callback((options) => { + assert.deepEqual(options, { resource, pythonPath: undefined }); + }) + .returns(() => Promise.resolve(pythonExecutionService.object)) + .verifiable(TypeMoq.Times.once()); + pythonExecutionService + // tslint:disable-next-line: no-any + .setup((p) => (p as any).then) + .returns(() => undefined); + + await pipInstaller.isSupported(resource); + + pythonExecutionFactory.verifyAll(); + }); + + test('Method isSupported() returns true if pip module is installed', async () => { + const pythonExecutionService = TypeMoq.Mock.ofType(); + const resource = Uri.parse('a'); + pythonExecutionFactory + .setup((p) => p.create(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(pythonExecutionService.object)); + pythonExecutionService + // tslint:disable-next-line: no-any + .setup((p) => (p as any).then) + .returns(() => undefined); + pythonExecutionService.setup((p) => p.isModuleInstalled('pip')).returns(() => Promise.resolve(true)); + + const expected = await pipInstaller.isSupported(resource); + + expect(expected).to.equal(true, 'Should be true'); + }); + + test('Method isSupported() returns false if pip module is not installed', async () => { + const pythonExecutionService = TypeMoq.Mock.ofType(); + const resource = Uri.parse('a'); + pythonExecutionFactory + .setup((p) => p.create(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(pythonExecutionService.object)); + pythonExecutionService + // tslint:disable-next-line: no-any + .setup((p) => (p as any).then) + .returns(() => undefined); + pythonExecutionService.setup((p) => p.isModuleInstalled('pip')).returns(() => Promise.resolve(false)); + + const expected = await pipInstaller.isSupported(resource); + + expect(expected).to.equal(false, 'Should be false'); + }); + + test('Method isSupported() returns false if checking if pip module is installed fails with error', async () => { + const pythonExecutionService = TypeMoq.Mock.ofType(); + const resource = Uri.parse('a'); + pythonExecutionFactory + .setup((p) => p.create(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(pythonExecutionService.object)); + pythonExecutionService + // tslint:disable-next-line: no-any + .setup((p) => (p as any).then) + .returns(() => undefined); + pythonExecutionService + .setup((p) => p.isModuleInstalled('pip')) + .returns(() => Promise.reject('Unable to check if module is installed')); + + const expected = await pipInstaller.isSupported(resource); + + expect(expected).to.equal(false, 'Should be false'); + }); +}); diff --git a/src/test/common/installer/productPath.unit.test.ts b/src/test/common/installer/productPath.unit.test.ts index 82c4d0401159..fc2d8c7e2b7b 100644 --- a/src/test/common/installer/productPath.unit.test.ts +++ b/src/test/common/installer/productPath.unit.test.ts @@ -43,8 +43,8 @@ import { ITestsHelper } from '../../../client/testing/common/types'; use(chaiAsPromised); suite('Product Path', () => { - [undefined, Uri.file('resource')].forEach(resource => { - getNamesAndValues(Product).forEach(product => { + [undefined, Uri.file('resource')].forEach((resource) => { + getNamesAndValues(Product).forEach((product) => { class TestBaseProductPathsService extends BaseProductPathsService { public getExecutableNameFromSettings(_: Product, _resource?: Uri): string { return ''; @@ -68,21 +68,21 @@ suite('Product Path', () => { TypeMoq.Mock.ofType().object ); const pythonSettings = TypeMoq.Mock.ofType(); - pythonSettings.setup(p => p.formatting).returns(() => formattingSettings.object); - pythonSettings.setup(p => p.testing).returns(() => unitTestSettings.object); - pythonSettings.setup(p => p.workspaceSymbols).returns(() => workspaceSymnbolSettings.object); + pythonSettings.setup((p) => p.formatting).returns(() => formattingSettings.object); + pythonSettings.setup((p) => p.testing).returns(() => unitTestSettings.object); + pythonSettings.setup((p) => p.workspaceSymbols).returns(() => workspaceSymnbolSettings.object); configService - .setup(s => s.getSettings(TypeMoq.It.isValue(resource))) + .setup((s) => s.getSettings(TypeMoq.It.isValue(resource))) .returns(() => pythonSettings.object); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) + .setup((s) => s.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) .returns(() => configService.object); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IInstaller), TypeMoq.It.isAny())) + .setup((s) => s.get(TypeMoq.It.isValue(IInstaller), TypeMoq.It.isAny())) .returns(() => productInstaller); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProductService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IProductService), TypeMoq.It.isAny())) .returns(() => new ProductService()); }); @@ -148,14 +148,14 @@ suite('Product Path', () => { const formatterHelper = TypeMoq.Mock.ofType(); const expectedPath = 'Some Path'; serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IFormatterHelper), TypeMoq.It.isAny())) + .setup((s) => s.get(TypeMoq.It.isValue(IFormatterHelper), TypeMoq.It.isAny())) .returns(() => formatterHelper.object); formattingSettings - .setup(f => f.autopep8Path) + .setup((f) => f.autopep8Path) .returns(() => expectedPath) .verifiable(TypeMoq.Times.atLeastOnce()); formatterHelper - .setup(f => f.getSettingsPropertyNames(TypeMoq.It.isValue(product.value))) + .setup((f) => f.getSettingsPropertyNames(TypeMoq.It.isValue(product.value))) .returns(() => { return { pathName: 'autopep8Path', @@ -180,14 +180,14 @@ suite('Product Path', () => { const linterInfo = TypeMoq.Mock.ofType(); const expectedPath = 'Some Path'; serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(ILinterManager), TypeMoq.It.isAny())) + .setup((s) => s.get(TypeMoq.It.isValue(ILinterManager), TypeMoq.It.isAny())) .returns(() => linterManager.object); linterInfo - .setup(l => l.pathName(TypeMoq.It.isValue(resource))) + .setup((l) => l.pathName(TypeMoq.It.isValue(resource))) .returns(() => expectedPath) .verifiable(TypeMoq.Times.once()); linterManager - .setup(l => l.getLinterInfo(TypeMoq.It.isValue(product.value))) + .setup((l) => l.getLinterInfo(TypeMoq.It.isValue(product.value))) .returns(() => linterInfo.object) .verifiable(TypeMoq.Times.once()); @@ -220,7 +220,7 @@ suite('Product Path', () => { const productPathService = new CTagsProductPathService(serviceContainer.object); const expectedPath = 'Some Path'; workspaceSymnbolSettings - .setup(w => w.ctagsPath) + .setup((w) => w.ctagsPath) .returns(() => expectedPath) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -238,10 +238,10 @@ suite('Product Path', () => { const testHelper = TypeMoq.Mock.ofType(); const expectedPath = 'Some Path'; serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(ITestsHelper), TypeMoq.It.isAny())) + .setup((s) => s.get(TypeMoq.It.isValue(ITestsHelper), TypeMoq.It.isAny())) .returns(() => testHelper.object); testHelper - .setup(t => t.getSettingsPropertyNames(TypeMoq.It.isValue(product.value))) + .setup((t) => t.getSettingsPropertyNames(TypeMoq.It.isValue(product.value))) .returns(() => { return { argsName: 'autoTestDiscoverOnSaveEnabled', @@ -251,7 +251,7 @@ suite('Product Path', () => { }) .verifiable(TypeMoq.Times.once()); unitTestSettings - .setup(u => u.nosetestPath) + .setup((u) => u.nosetestPath) .returns(() => expectedPath) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -266,10 +266,10 @@ suite('Product Path', () => { const productPathService = new TestFrameworkProductPathService(serviceContainer.object); const testHelper = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(ITestsHelper), TypeMoq.It.isAny())) + .setup((s) => s.get(TypeMoq.It.isValue(ITestsHelper), TypeMoq.It.isAny())) .returns(() => testHelper.object); testHelper - .setup(t => t.getSettingsPropertyNames(TypeMoq.It.isValue(product.value))) + .setup((t) => t.getSettingsPropertyNames(TypeMoq.It.isValue(product.value))) .returns(() => { return { argsName: 'autoTestDiscoverOnSaveEnabled', diff --git a/src/test/common/installer/serviceRegistry.unit.test.ts b/src/test/common/installer/serviceRegistry.unit.test.ts index 6e33520a8bbf..c355515fa024 100644 --- a/src/test/common/installer/serviceRegistry.unit.test.ts +++ b/src/test/common/installer/serviceRegistry.unit.test.ts @@ -1,117 +1,117 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { instance, mock, verify } from 'ts-mockito'; -import { IWebPanelProvider } from '../../../client/common/application/types'; -import { WebPanelProvider } from '../../../client/common/application/webPanels/webPanelProvider'; -import { InstallationChannelManager } from '../../../client/common/installer/channelManager'; -import { CondaInstaller } from '../../../client/common/installer/condaInstaller'; -import { InsidersBuildInstaller, StableBuildInstaller } from '../../../client/common/installer/extensionBuildInstaller'; -import { PipEnvInstaller } from '../../../client/common/installer/pipEnvInstaller'; -import { PipInstaller } from '../../../client/common/installer/pipInstaller'; -import { PoetryInstaller } from '../../../client/common/installer/poetryInstaller'; -import { - CTagsProductPathService, - DataScienceProductPathService, - FormatterProductPathService, - LinterProductPathService, - RefactoringLibraryProductPathService, - TestFrameworkProductPathService -} from '../../../client/common/installer/productPath'; -import { ProductService } from '../../../client/common/installer/productService'; -import { registerTypes } from '../../../client/common/installer/serviceRegistry'; -import { - IExtensionBuildInstaller, - IInstallationChannelManager, - IModuleInstaller, - INSIDERS_INSTALLER, - IProductPathService, - IProductService, - STABLE_INSTALLER -} from '../../../client/common/installer/types'; -import { ProductType } from '../../../client/common/types'; -import { ServiceManager } from '../../../client/ioc/serviceManager'; -import { IServiceManager } from '../../../client/ioc/types'; - -suite('Common installer Service Registry', () => { - let serviceManager: IServiceManager; - - setup(() => { - serviceManager = mock(ServiceManager); - }); - - test('Ensure services are registered', async () => { - registerTypes(instance(serviceManager)); - verify(serviceManager.addSingleton(IModuleInstaller, CondaInstaller)).once(); - verify(serviceManager.addSingleton(IModuleInstaller, PipInstaller)).once(); - verify(serviceManager.addSingleton(IModuleInstaller, PipEnvInstaller)).once(); - verify(serviceManager.addSingleton(IModuleInstaller, PoetryInstaller)).once(); - verify( - serviceManager.addSingleton( - IInstallationChannelManager, - InstallationChannelManager - ) - ).once(); - verify( - serviceManager.addSingleton( - IExtensionBuildInstaller, - StableBuildInstaller, - STABLE_INSTALLER - ) - ).once(); - verify( - serviceManager.addSingleton( - IExtensionBuildInstaller, - InsidersBuildInstaller, - INSIDERS_INSTALLER - ) - ).once(); - - verify(serviceManager.addSingleton(IProductService, ProductService)).once(); - verify( - serviceManager.addSingleton( - IProductPathService, - CTagsProductPathService, - ProductType.WorkspaceSymbols - ) - ).once(); - verify( - serviceManager.addSingleton( - IProductPathService, - FormatterProductPathService, - ProductType.Formatter - ) - ).once(); - verify( - serviceManager.addSingleton( - IProductPathService, - LinterProductPathService, - ProductType.Linter - ) - ).once(); - verify( - serviceManager.addSingleton( - IProductPathService, - TestFrameworkProductPathService, - ProductType.TestFramework - ) - ).once(); - verify( - serviceManager.addSingleton( - IProductPathService, - RefactoringLibraryProductPathService, - ProductType.RefactoringLibrary - ) - ).once(); - verify( - serviceManager.addSingleton( - IProductPathService, - DataScienceProductPathService, - ProductType.DataScience - ) - ).once(); - verify(serviceManager.addSingleton(IWebPanelProvider, WebPanelProvider)).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { instance, mock, verify } from 'ts-mockito'; +import { IWebPanelProvider } from '../../../client/common/application/types'; +import { WebPanelProvider } from '../../../client/common/application/webPanels/webPanelProvider'; +import { InstallationChannelManager } from '../../../client/common/installer/channelManager'; +import { CondaInstaller } from '../../../client/common/installer/condaInstaller'; +import { InsidersBuildInstaller, StableBuildInstaller } from '../../../client/common/installer/extensionBuildInstaller'; +import { PipEnvInstaller } from '../../../client/common/installer/pipEnvInstaller'; +import { PipInstaller } from '../../../client/common/installer/pipInstaller'; +import { PoetryInstaller } from '../../../client/common/installer/poetryInstaller'; +import { + CTagsProductPathService, + DataScienceProductPathService, + FormatterProductPathService, + LinterProductPathService, + RefactoringLibraryProductPathService, + TestFrameworkProductPathService +} from '../../../client/common/installer/productPath'; +import { ProductService } from '../../../client/common/installer/productService'; +import { registerTypes } from '../../../client/common/installer/serviceRegistry'; +import { + IExtensionBuildInstaller, + IInstallationChannelManager, + IModuleInstaller, + INSIDERS_INSTALLER, + IProductPathService, + IProductService, + STABLE_INSTALLER +} from '../../../client/common/installer/types'; +import { ProductType } from '../../../client/common/types'; +import { ServiceManager } from '../../../client/ioc/serviceManager'; +import { IServiceManager } from '../../../client/ioc/types'; + +suite('Common installer Service Registry', () => { + let serviceManager: IServiceManager; + + setup(() => { + serviceManager = mock(ServiceManager); + }); + + test('Ensure services are registered', async () => { + registerTypes(instance(serviceManager)); + verify(serviceManager.addSingleton(IModuleInstaller, CondaInstaller)).once(); + verify(serviceManager.addSingleton(IModuleInstaller, PipInstaller)).once(); + verify(serviceManager.addSingleton(IModuleInstaller, PipEnvInstaller)).once(); + verify(serviceManager.addSingleton(IModuleInstaller, PoetryInstaller)).once(); + verify( + serviceManager.addSingleton( + IInstallationChannelManager, + InstallationChannelManager + ) + ).once(); + verify( + serviceManager.addSingleton( + IExtensionBuildInstaller, + StableBuildInstaller, + STABLE_INSTALLER + ) + ).once(); + verify( + serviceManager.addSingleton( + IExtensionBuildInstaller, + InsidersBuildInstaller, + INSIDERS_INSTALLER + ) + ).once(); + + verify(serviceManager.addSingleton(IProductService, ProductService)).once(); + verify( + serviceManager.addSingleton( + IProductPathService, + CTagsProductPathService, + ProductType.WorkspaceSymbols + ) + ).once(); + verify( + serviceManager.addSingleton( + IProductPathService, + FormatterProductPathService, + ProductType.Formatter + ) + ).once(); + verify( + serviceManager.addSingleton( + IProductPathService, + LinterProductPathService, + ProductType.Linter + ) + ).once(); + verify( + serviceManager.addSingleton( + IProductPathService, + TestFrameworkProductPathService, + ProductType.TestFramework + ) + ).once(); + verify( + serviceManager.addSingleton( + IProductPathService, + RefactoringLibraryProductPathService, + ProductType.RefactoringLibrary + ) + ).once(); + verify( + serviceManager.addSingleton( + IProductPathService, + DataScienceProductPathService, + ProductType.DataScience + ) + ).once(); + verify(serviceManager.addSingleton(IWebPanelProvider, WebPanelProvider)).once(); + }); +}); diff --git a/src/test/common/moduleInstaller.test.ts b/src/test/common/moduleInstaller.test.ts index 89f951e3863b..e0f46c007e32 100644 --- a/src/test/common/moduleInstaller.test.ts +++ b/src/test/common/moduleInstaller.test.ts @@ -69,7 +69,7 @@ const info: PythonInterpreter = { }; suite('Module Installer', () => { - [undefined, Uri.file(__filename)].forEach(resource => { + [undefined, Uri.file(__filename)].forEach((resource) => { let ioc: UnitTestIocContainer; let mockTerminalService: TypeMoq.IMock; let condaService: TypeMoq.IMock; @@ -106,13 +106,13 @@ suite('Module Installer', () => { mockTerminalService = TypeMoq.Mock.ofType(); mockTerminalFactory = TypeMoq.Mock.ofType(); mockTerminalFactory - .setup(t => t.getTerminalService(TypeMoq.It.isValue(resource))) + .setup((t) => t.getTerminalService(TypeMoq.It.isValue(resource))) .returns(() => mockTerminalService.object) .verifiable(TypeMoq.Times.atLeastOnce()); // If resource is provided, then ensure we do not invoke without the resource. mockTerminalFactory - .setup(t => t.getTerminalService(TypeMoq.It.isAny())) - .callback(passedInResource => expect(passedInResource).to.be.equal(resource)) + .setup((t) => t.getTerminalService(TypeMoq.It.isAny())) + .callback((passedInResource) => expect(passedInResource).to.be.equal(resource)) .returns(() => mockTerminalService.object); ioc.serviceManager.addSingletonInstance( ITerminalServiceFactory, @@ -140,8 +140,8 @@ suite('Module Installer', () => { const workspaceService = TypeMoq.Mock.ofType(); ioc.serviceManager.addSingletonInstance(IWorkspaceService, workspaceService.object); const http = TypeMoq.Mock.ofType(); - http.setup(h => h.get(TypeMoq.It.isValue('proxy'), TypeMoq.It.isAny())).returns(() => ''); - workspaceService.setup(w => w.getConfiguration(TypeMoq.It.isValue('http'))).returns(() => http.object); + http.setup((h) => h.get(TypeMoq.It.isValue('proxy'), TypeMoq.It.isAny())).returns(() => ''); + workspaceService.setup((w) => w.getConfiguration(TypeMoq.It.isValue('http'))).returns(() => http.object); ioc.registerMockProcessTypes(); ioc.serviceManager.addSingletonInstance(IsWindows, false); @@ -180,7 +180,9 @@ suite('Module Installer', () => { new MockModuleInstaller('mock', true) ); const mockInterpreterLocator = TypeMoq.Mock.ofType(); - mockInterpreterLocator.setup(p => p.getInterpreters(TypeMoq.It.isAny())).returns(() => Promise.resolve([])); + mockInterpreterLocator + .setup((p) => p.getInterpreters(TypeMoq.It.isAny())) + .returns(() => Promise.resolve([])); ioc.serviceManager.addSingletonInstance( IInterpreterLocatorService, mockInterpreterLocator.object, @@ -207,15 +209,15 @@ suite('Module Installer', () => { const moduleInstallers = ioc.serviceContainer.getAll(IModuleInstaller); expect(moduleInstallers).length(4, 'Incorrect number of installers'); - const pipInstaller = moduleInstallers.find(item => item.displayName === 'Pip')!; + const pipInstaller = moduleInstallers.find((item) => item.displayName === 'Pip')!; expect(pipInstaller).not.to.be.an('undefined', 'Pip installer not found'); await expect(pipInstaller.isSupported()).to.eventually.equal(true, 'Pip is not supported'); - const condaInstaller = moduleInstallers.find(item => item.displayName === 'Conda')!; + const condaInstaller = moduleInstallers.find((item) => item.displayName === 'Conda')!; expect(condaInstaller).not.to.be.an('undefined', 'Conda installer not found'); await expect(condaInstaller.isSupported()).to.eventually.equal(false, 'Conda is supported'); - const mockInstaller = moduleInstallers.find(item => item.displayName === 'mock')!; + const mockInstaller = moduleInstallers.find((item) => item.displayName === 'mock')!; expect(mockInstaller).not.to.be.an('undefined', 'mock installer not found'); await expect(mockInstaller.isSupported()).to.eventually.equal(true, 'mock is not supported'); }); @@ -228,7 +230,7 @@ suite('Module Installer', () => { const pythonPath = await getCurrentPythonPath(); const mockInterpreterLocator = TypeMoq.Mock.ofType(); mockInterpreterLocator - .setup(p => p.getInterpreters(TypeMoq.It.isAny())) + .setup((p) => p.getInterpreters(TypeMoq.It.isAny())) .returns(() => Promise.resolve([ { @@ -269,7 +271,7 @@ suite('Module Installer', () => { const moduleInstallers = ioc.serviceContainer.getAll(IModuleInstaller); expect(moduleInstallers).length(4, 'Incorrect number of installers'); - const pipInstaller = moduleInstallers.find(item => item.displayName === 'Pip')!; + const pipInstaller = moduleInstallers.find((item) => item.displayName === 'Pip')!; expect(pipInstaller).not.to.be.an('undefined', 'Pip installer not found'); await expect(pipInstaller.isSupported()).to.eventually.equal(true, 'Pip is not supported'); }); @@ -278,16 +280,16 @@ suite('Module Installer', () => { const configService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); const settings = TypeMoq.Mock.ofType(); const pythonPath = 'pythonABC'; - settings.setup(s => s.pythonPath).returns(() => pythonPath); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICondaService))).returns(() => condaService.object); - condaService.setup(c => c.isCondaAvailable()).returns(() => Promise.resolve(true)); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICondaService))).returns(() => condaService.object); + condaService.setup((c) => c.isCondaAvailable()).returns(() => Promise.resolve(true)); condaService - .setup(c => c.isCondaEnvironment(TypeMoq.It.isValue(pythonPath))) + .setup((c) => c.isCondaEnvironment(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(true)); const condaInstaller = new CondaInstaller(serviceContainer.object); @@ -298,16 +300,16 @@ suite('Module Installer', () => { const configService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); const settings = TypeMoq.Mock.ofType(); const pythonPath = 'pythonABC'; - settings.setup(s => s.pythonPath).returns(() => pythonPath); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICondaService))).returns(() => condaService.object); - condaService.setup(c => c.isCondaAvailable()).returns(() => Promise.resolve(true)); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICondaService))).returns(() => condaService.object); + condaService.setup((c) => c.isCondaAvailable()).returns(() => Promise.resolve(true)); condaService - .setup(c => c.isCondaEnvironment(TypeMoq.It.isValue(pythonPath))) + .setup((c) => c.isCondaEnvironment(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(false)); const condaInstaller = new CondaInstaller(serviceContainer.object); @@ -319,7 +321,7 @@ suite('Module Installer', () => { const interpreterPath = await getCurrentPythonPath(); const mockInterpreterLocator = TypeMoq.Mock.ofType(); mockInterpreterLocator - .setup(p => p.getInterpreters(TypeMoq.It.isAny())) + .setup((p) => p.getInterpreters(TypeMoq.It.isAny())) .returns(() => Promise.resolve([{ ...info, path: interpreterPath, type: InterpreterType.Unknown }])); ioc.serviceManager.addSingletonInstance( IInterpreterLocatorService, @@ -338,25 +340,25 @@ suite('Module Installer', () => { path: PYTHON_PATH }; interpreterService - .setup(x => x.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((x) => x.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(interpreter)); const moduleName = 'xyz'; const moduleInstallers = ioc.serviceContainer.getAll(IModuleInstaller); - const pipInstaller = moduleInstallers.find(item => item.displayName === 'Pip')!; + const pipInstaller = moduleInstallers.find((item) => item.displayName === 'Pip')!; expect(pipInstaller).not.to.be.an('undefined', 'Pip installer not found'); let argsSent: string[] = []; mockTerminalService - .setup(t => t.sendCommand(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((t) => t.sendCommand(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((_cmd: string, args: string[]) => { argsSent = args; return Promise.resolve(void 0); }); interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) // tslint:disable-next-line:no-any .returns(() => Promise.resolve({ type: InterpreterType.Unknown } as any)); @@ -373,7 +375,7 @@ suite('Module Installer', () => { const interpreterPath = await getCurrentPythonPath(); const mockInterpreterLocator = TypeMoq.Mock.ofType(); mockInterpreterLocator - .setup(p => p.getInterpreters(TypeMoq.It.isAny())) + .setup((p) => p.getInterpreters(TypeMoq.It.isAny())) .returns(() => Promise.resolve([{ ...info, path: interpreterPath, type: InterpreterType.Conda }])); ioc.serviceManager.addSingletonInstance( IInterpreterLocatorService, @@ -389,13 +391,13 @@ suite('Module Installer', () => { const moduleName = 'xyz'; const moduleInstallers = ioc.serviceContainer.getAll(IModuleInstaller); - const pipInstaller = moduleInstallers.find(item => item.displayName === 'Pip')!; + const pipInstaller = moduleInstallers.find((item) => item.displayName === 'Pip')!; expect(pipInstaller).not.to.be.an('undefined', 'Pip installer not found'); let argsSent: string[] = []; mockTerminalService - .setup(t => t.sendCommand(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((t) => t.sendCommand(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((_cmd: string, args: string[]) => { argsSent = args; return Promise.resolve(void 0); @@ -413,7 +415,7 @@ suite('Module Installer', () => { test(`Validate pipenv install arguments ${resourceTestNameSuffix}`, async () => { const mockInterpreterLocator = TypeMoq.Mock.ofType(); mockInterpreterLocator - .setup(p => p.getInterpreters(TypeMoq.It.isAny())) + .setup((p) => p.getInterpreters(TypeMoq.It.isAny())) .returns(() => Promise.resolve([{ ...info, path: 'interpreterPath', type: InterpreterType.VirtualEnv }]) ); @@ -425,14 +427,14 @@ suite('Module Installer', () => { const moduleName = 'xyz'; const moduleInstallers = ioc.serviceContainer.getAll(IModuleInstaller); - const pipInstaller = moduleInstallers.find(item => item.displayName === 'pipenv')!; + const pipInstaller = moduleInstallers.find((item) => item.displayName === 'pipenv')!; expect(pipInstaller).not.to.be.an('undefined', 'pipenv installer not found'); let argsSent: string[] = []; let command: string | undefined; mockTerminalService - .setup(t => t.sendCommand(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((t) => t.sendCommand(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((cmd: string, args: string[]) => { argsSent = args; command = cmd; diff --git a/src/test/common/net/fileDownloader.unit.test.ts b/src/test/common/net/fileDownloader.unit.test.ts index a55d5f13efca..55ca3883755d 100644 --- a/src/test/common/net/fileDownloader.unit.test.ts +++ b/src/test/common/net/fileDownloader.unit.test.ts @@ -122,9 +122,7 @@ suite('File Downloader', () => { }); test('Error is throw for http Status !== 200', async () => { // When downloading a uri, throw status 500 error. - nock('https://python.extension') - .get('/package.json') - .reply(500); + nock('https://python.extension').get('/package.json').reply(500); const progressReportStub = sinon.stub(); const progressReporter: Progress = { report: progressReportStub }; when(appShell.withProgress(anything(), anything())).thenCall((_, cb) => cb(progressReporter)); diff --git a/src/test/common/net/httpClient.unit.test.ts b/src/test/common/net/httpClient.unit.test.ts index 9972dcf94f93..33f30c4a1176 100644 --- a/src/test/common/net/httpClient.unit.test.ts +++ b/src/test/common/net/httpClient.unit.test.ts @@ -27,14 +27,14 @@ suite('Http Client', () => { workSpaceService = TypeMoq.Mock.ofType(); config = TypeMoq.Mock.ofType(); config - .setup(c => c.get(TypeMoq.It.isValue('proxy'), TypeMoq.It.isValue(''))) + .setup((c) => c.get(TypeMoq.It.isValue('proxy'), TypeMoq.It.isValue(''))) .returns(() => proxy) .verifiable(TypeMoq.Times.once()); workSpaceService - .setup(w => w.getConfiguration(TypeMoq.It.isValue('http'))) + .setup((w) => w.getConfiguration(TypeMoq.It.isValue('http'))) .returns(() => config.object) .verifiable(TypeMoq.Times.once()); - container.setup(a => a.get(TypeMoq.It.isValue(IWorkspaceService))).returns(() => workSpaceService.object); + container.setup((a) => a.get(TypeMoq.It.isValue(IWorkspaceService))).returns(() => workSpaceService.object); httpClient = new HttpClient(container.object); }); @@ -63,7 +63,7 @@ suite('Http Client', () => { returnedArgs: [undefined, { statusCode: 200 }, '[{ "strictJSON" : true,, }]'], strict: true } - ].forEach(async testParams => { + ].forEach(async (testParams) => { test(testParams.name, async () => { const requestMock = (_uri: any, _requestOptions: any, callBackFn: Function) => callBackFn(...testParams.returnedArgs); @@ -109,7 +109,7 @@ suite('Http Client', () => { strict: false, expectedJSON: [{ strictJSON: false }] } - ].forEach(async testParams => { + ].forEach(async (testParams) => { test(testParams.name, async () => { const requestMock = (_uri: any, _requestOptions: any, callBackFn: Function) => callBackFn(...testParams.returnedArgs); diff --git a/src/test/common/nuget/azureBobStoreRepository.functional.test.ts b/src/test/common/nuget/azureBobStoreRepository.functional.test.ts index ff0f783a42c7..48a58c988e43 100644 --- a/src/test/common/nuget/azureBobStoreRepository.functional.test.ts +++ b/src/test/common/nuget/azureBobStoreRepository.functional.test.ts @@ -1,67 +1,69 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect } from 'chai'; -import { SemVer } from 'semver'; -import * as typeMoq from 'typemoq'; -import { WorkspaceConfiguration } from 'vscode'; -import { DotNetLanguageServerPackageService } from '../../../client/activation/languageServer/languageServerPackageService'; -import { IApplicationEnvironment, IWorkspaceService } from '../../../client/common/application/types'; -import { AzureBlobStoreNugetRepository } from '../../../client/common/nuget/azureBlobStoreNugetRepository'; -import { INugetService } from '../../../client/common/nuget/types'; -import { PlatformService } from '../../../client/common/platform/platformService'; -import { IHttpClient } from '../../../client/common/types'; -import { IServiceContainer } from '../../../client/ioc/types'; - -const azureBlobStorageAccount = 'https://pvsc.blob.core.windows.net'; -const azureCDNBlobStorageAccount = 'https://pvsc.azureedge.net'; - -suite('Nuget Azure Storage Repository', () => { - let serviceContainer: typeMoq.IMock; - let httpClient: typeMoq.IMock; - let workspace: typeMoq.IMock; - let cfg: typeMoq.IMock; - let repo: AzureBlobStoreNugetRepository; - setup(() => { - serviceContainer = typeMoq.Mock.ofType(); - httpClient = typeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(IHttpClient))).returns(() => httpClient.object); - cfg = typeMoq.Mock.ofType(); - cfg.setup(c => c.get('proxyStrictSSL', true)).returns(() => true); - workspace = typeMoq.Mock.ofType(); - workspace.setup(w => w.getConfiguration('http', undefined)).returns(() => cfg.object); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(IWorkspaceService))).returns(() => workspace.object); - - const nugetService = typeMoq.Mock.ofType(); - nugetService.setup(n => n.getVersionFromPackageFileName(typeMoq.It.isAny())).returns(() => new SemVer('1.1.1')); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(INugetService))).returns(() => nugetService.object); - const defaultStorageChannel = 'python-language-server-stable'; - - repo = new AzureBlobStoreNugetRepository( - serviceContainer.object, - azureBlobStorageAccount, - defaultStorageChannel, - azureCDNBlobStorageAccount - ); - }); - - test('Get all packages', async function() { - // tslint:disable-next-line:no-invalid-this - this.timeout(15000); - const platformService = new PlatformService(); - const packageJson = { languageServerVersion: '0.0.1' }; - const appEnv = typeMoq.Mock.ofType(); - appEnv.setup(e => e.packageJson).returns(() => packageJson); - const lsPackageService = new DotNetLanguageServerPackageService( - serviceContainer.object, - appEnv.object, - platformService - ); - const packageName = lsPackageService.getNugetPackageName(); - const packages = await repo.getPackages(packageName, undefined); - - expect(packages).to.be.length.greaterThan(0); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { expect } from 'chai'; +import { SemVer } from 'semver'; +import * as typeMoq from 'typemoq'; +import { WorkspaceConfiguration } from 'vscode'; +import { DotNetLanguageServerPackageService } from '../../../client/activation/languageServer/languageServerPackageService'; +import { IApplicationEnvironment, IWorkspaceService } from '../../../client/common/application/types'; +import { AzureBlobStoreNugetRepository } from '../../../client/common/nuget/azureBlobStoreNugetRepository'; +import { INugetService } from '../../../client/common/nuget/types'; +import { PlatformService } from '../../../client/common/platform/platformService'; +import { IHttpClient } from '../../../client/common/types'; +import { IServiceContainer } from '../../../client/ioc/types'; + +const azureBlobStorageAccount = 'https://pvsc.blob.core.windows.net'; +const azureCDNBlobStorageAccount = 'https://pvsc.azureedge.net'; + +suite('Nuget Azure Storage Repository', () => { + let serviceContainer: typeMoq.IMock; + let httpClient: typeMoq.IMock; + let workspace: typeMoq.IMock; + let cfg: typeMoq.IMock; + let repo: AzureBlobStoreNugetRepository; + setup(() => { + serviceContainer = typeMoq.Mock.ofType(); + httpClient = typeMoq.Mock.ofType(); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(IHttpClient))).returns(() => httpClient.object); + cfg = typeMoq.Mock.ofType(); + cfg.setup((c) => c.get('proxyStrictSSL', true)).returns(() => true); + workspace = typeMoq.Mock.ofType(); + workspace.setup((w) => w.getConfiguration('http', undefined)).returns(() => cfg.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(IWorkspaceService))).returns(() => workspace.object); + + const nugetService = typeMoq.Mock.ofType(); + nugetService + .setup((n) => n.getVersionFromPackageFileName(typeMoq.It.isAny())) + .returns(() => new SemVer('1.1.1')); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(INugetService))).returns(() => nugetService.object); + const defaultStorageChannel = 'python-language-server-stable'; + + repo = new AzureBlobStoreNugetRepository( + serviceContainer.object, + azureBlobStorageAccount, + defaultStorageChannel, + azureCDNBlobStorageAccount + ); + }); + + test('Get all packages', async function () { + // tslint:disable-next-line:no-invalid-this + this.timeout(15000); + const platformService = new PlatformService(); + const packageJson = { languageServerVersion: '0.0.1' }; + const appEnv = typeMoq.Mock.ofType(); + appEnv.setup((e) => e.packageJson).returns(() => packageJson); + const lsPackageService = new DotNetLanguageServerPackageService( + serviceContainer.object, + appEnv.object, + platformService + ); + const packageName = lsPackageService.getNugetPackageName(); + const packages = await repo.getPackages(packageName, undefined); + + expect(packages).to.be.length.greaterThan(0); + }); +}); diff --git a/src/test/common/nuget/azureBobStoreRepository.unit.test.ts b/src/test/common/nuget/azureBobStoreRepository.unit.test.ts index e9dc6745453e..f29e12e42dcc 100644 --- a/src/test/common/nuget/azureBobStoreRepository.unit.test.ts +++ b/src/test/common/nuget/azureBobStoreRepository.unit.test.ts @@ -29,7 +29,7 @@ suite('Nuget Azure Storage Repository', () => { nugetService = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); cfg = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(INugetService))).returns(() => nugetService.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(INugetService))).returns(() => nugetService.object); }); class FakeBlobStore { @@ -64,10 +64,10 @@ suite('Nuget Azure Storage Repository', () => { test(`Get all packages ("${uri}" / ${setting})`, async () => { if (uri.startsWith('https://')) { serviceContainer - .setup(c => c.get(typeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(typeMoq.It.isValue(IWorkspaceService))) .returns(() => workspace.object); - workspace.setup(w => w.getConfiguration('http', undefined)).returns(() => cfg.object); - cfg.setup(c => c.get('proxyStrictSSL', true)).returns(() => setting); + workspace.setup((w) => w.getConfiguration('http', undefined)).returns(() => cfg.object); + cfg.setup((c) => c.get('proxyStrictSSL', true)).returns(() => setting); } const blobstore = new FakeBlobStore(); // tslint:disable:no-object-literal-type-assertion @@ -78,8 +78,8 @@ suite('Nuget Azure Storage Repository', () => { ]; // tslint:enable:no-object-literal-type-assertion const version = new SemVer('1.1.1'); - blobstore.results.forEach(r => { - nugetService.setup(n => n.getVersionFromPackageFileName(r.name)).returns(() => version); + blobstore.results.forEach((r) => { + nugetService.setup((n) => n.getVersionFromPackageFileName(r.name)).returns(() => version); }); let actualURI = ''; const repo = new AzureBlobStoreNugetRepository( @@ -87,7 +87,7 @@ suite('Nuget Azure Storage Repository', () => { uri, 'spam', 'eggs', - async uriArg => { + async (uriArg) => { actualURI = uriArg; return blobstore; } diff --git a/src/test/common/nuget/nugetRepository.unit.test.ts b/src/test/common/nuget/nugetRepository.unit.test.ts index 6e9214293771..1370e824b2da 100644 --- a/src/test/common/nuget/nugetRepository.unit.test.ts +++ b/src/test/common/nuget/nugetRepository.unit.test.ts @@ -1,67 +1,67 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect } from 'chai'; -import { SemVer } from 'semver'; -import * as typeMoq from 'typemoq'; -import { NugetRepository } from '../../../client/common/nuget/nugetRepository'; -import { IHttpClient } from '../../../client/common/types'; -import { IServiceContainer } from '../../../client/ioc/types'; - -suite('Nuget on Nuget Repo', () => { - let serviceContainer: typeMoq.IMock; - let httpClient: typeMoq.IMock; - let nugetRepo: NugetRepository; - setup(() => { - serviceContainer = typeMoq.Mock.ofType(); - httpClient = typeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(IHttpClient))).returns(() => httpClient.object); - - nugetRepo = new NugetRepository(serviceContainer.object); - }); - - test('Get all package versions', async () => { - const packageBaseAddress = 'a'; - const packageName = 'b'; - const resp = { versions: ['1.1.1', '1.2.1'] }; - const expectedUri = `${packageBaseAddress}/${packageName.toLowerCase().trim()}/index.json`; - - httpClient - .setup(h => h.getJSON(typeMoq.It.isValue(expectedUri))) - .returns(() => Promise.resolve(resp)) - .verifiable(typeMoq.Times.once()); - - const versions = await nugetRepo.getVersions(packageBaseAddress, packageName); - - httpClient.verifyAll(); - expect(versions).to.be.lengthOf(2); - expect(versions.map(item => item.raw)).to.deep.equal(resp.versions); - }); - - test('Get package uri', async () => { - const packageBaseAddress = 'a'; - const packageName = 'b'; - const version = '1.1.3'; - const expectedUri = `${packageBaseAddress}/${packageName}/${version}/${packageName}.${version}.nupkg`; - - const packageUri = nugetRepo.getNugetPackageUri(packageBaseAddress, packageName, new SemVer(version)); - - httpClient.verifyAll(); - expect(packageUri).to.equal(expectedUri); - }); - - test('Get packages', async () => { - const versions = ['1.1.1', '1.2.1', '2.2.2', '2.5.4', '2.9.5-release', '2.7.4-beta', '2.0.2', '3.5.4']; - nugetRepo.getVersions = () => Promise.resolve(versions.map(v => new SemVer(v))); - nugetRepo.getNugetPackageUri = () => 'uri'; - - const packages = await nugetRepo.getPackages('packageName'); - - expect(packages).to.be.lengthOf(versions.length); - expect(packages.map(item => item.version.raw)).to.be.deep.equal(versions); - expect(packages.map(item => item.uri)).to.be.deep.equal(versions.map(() => 'uri')); - expect(packages.map(item => item.package)).to.be.deep.equal(versions.map(() => 'packageName')); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { expect } from 'chai'; +import { SemVer } from 'semver'; +import * as typeMoq from 'typemoq'; +import { NugetRepository } from '../../../client/common/nuget/nugetRepository'; +import { IHttpClient } from '../../../client/common/types'; +import { IServiceContainer } from '../../../client/ioc/types'; + +suite('Nuget on Nuget Repo', () => { + let serviceContainer: typeMoq.IMock; + let httpClient: typeMoq.IMock; + let nugetRepo: NugetRepository; + setup(() => { + serviceContainer = typeMoq.Mock.ofType(); + httpClient = typeMoq.Mock.ofType(); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(IHttpClient))).returns(() => httpClient.object); + + nugetRepo = new NugetRepository(serviceContainer.object); + }); + + test('Get all package versions', async () => { + const packageBaseAddress = 'a'; + const packageName = 'b'; + const resp = { versions: ['1.1.1', '1.2.1'] }; + const expectedUri = `${packageBaseAddress}/${packageName.toLowerCase().trim()}/index.json`; + + httpClient + .setup((h) => h.getJSON(typeMoq.It.isValue(expectedUri))) + .returns(() => Promise.resolve(resp)) + .verifiable(typeMoq.Times.once()); + + const versions = await nugetRepo.getVersions(packageBaseAddress, packageName); + + httpClient.verifyAll(); + expect(versions).to.be.lengthOf(2); + expect(versions.map((item) => item.raw)).to.deep.equal(resp.versions); + }); + + test('Get package uri', async () => { + const packageBaseAddress = 'a'; + const packageName = 'b'; + const version = '1.1.3'; + const expectedUri = `${packageBaseAddress}/${packageName}/${version}/${packageName}.${version}.nupkg`; + + const packageUri = nugetRepo.getNugetPackageUri(packageBaseAddress, packageName, new SemVer(version)); + + httpClient.verifyAll(); + expect(packageUri).to.equal(expectedUri); + }); + + test('Get packages', async () => { + const versions = ['1.1.1', '1.2.1', '2.2.2', '2.5.4', '2.9.5-release', '2.7.4-beta', '2.0.2', '3.5.4']; + nugetRepo.getVersions = () => Promise.resolve(versions.map((v) => new SemVer(v))); + nugetRepo.getNugetPackageUri = () => 'uri'; + + const packages = await nugetRepo.getPackages('packageName'); + + expect(packages).to.be.lengthOf(versions.length); + expect(packages.map((item) => item.version.raw)).to.be.deep.equal(versions); + expect(packages.map((item) => item.uri)).to.be.deep.equal(versions.map(() => 'uri')); + expect(packages.map((item) => item.package)).to.be.deep.equal(versions.map(() => 'packageName')); + }); +}); diff --git a/src/test/common/platform/filesystem.functional.test.ts b/src/test/common/platform/filesystem.functional.test.ts index 1d2a9a8f95ec..3ceecb584faf 100644 --- a/src/test/common/platform/filesystem.functional.test.ts +++ b/src/test/common/platform/filesystem.functional.test.ts @@ -42,7 +42,7 @@ suite('FileSystem - raw', () => { }); suite('lstat', () => { - test('for symlinks, gives the link info', async function() { + test('for symlinks, gives the link info', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -78,7 +78,7 @@ suite('FileSystem - raw', () => { }); suite('chmod (non-Windows)', () => { - suiteSetup(function() { + suiteSetup(function () { // On Windows, chmod won't have any effect on the file itself. if (WINDOWS) { // tslint:disable-next-line:no-invalid-this @@ -190,7 +190,7 @@ suite('FileSystem - raw', () => { expect(stat).to.deep.equal(expected); }); - test('for symlinks, gives the linked info', async function() { + test('for symlinks, gives the linked info', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -257,7 +257,7 @@ suite('FileSystem - raw', () => { }); suite('createWriteStream', () => { - setup(function() { + setup(function () { if (OSX) { // tslint:disable-next-line:no-suspicious-comment // TODO(GH-10031) This appears to be producing @@ -295,7 +295,7 @@ suite('FileSystem - raw', () => { await assertDoesNotExist(filename); const data = 'line1\nline2\n'; - await writeToStream(filename, s => s.write(data)); + await writeToStream(filename, (s) => s.write(data)); await assertFileText(filename, data); }); @@ -304,7 +304,7 @@ suite('FileSystem - raw', () => { const filename = await fix.resolve('x/y/z/spam.py'); const data = '... 😁 ...'; - await writeToStream(filename, s => s.write(data)); + await writeToStream(filename, (s) => s.write(data)); await assertFileText(filename, data); }); @@ -313,7 +313,7 @@ suite('FileSystem - raw', () => { const filename = await fix.createFile('x/y/z/spam.py', '...'); const data = 'line1\nline2\n'; - await writeToStream(filename, s => s.write(data)); + await writeToStream(filename, (s) => s.write(data)); await assertFileText(filename, data); }); @@ -449,7 +449,7 @@ suite('FileSystem - utils', () => { expect(exists).to.equal(true); }); - test('symlink', async function() { + test('symlink', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -466,7 +466,7 @@ suite('FileSystem - utils', () => { expect(exists).to.equal(true); }); - test('unknown', async function() { + test('unknown', async function () { if (!SUPPORTS_SOCKETS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -544,7 +544,7 @@ suite('FileSystem', () => { }); suite('chmod (non-Windows)', () => { - suiteSetup(function() { + suiteSetup(function () { // On Windows, chmod won't have any effect on the file itself. if (WINDOWS) { // tslint:disable-next-line:no-invalid-this @@ -723,7 +723,7 @@ suite('FileSystem', () => { expect(exists).to.equal(true); }); - test('symlink', async function() { + test('symlink', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -740,7 +740,7 @@ suite('FileSystem', () => { expect(exists).to.equal(true); }); - test('unknown', async function() { + test('unknown', async function () { if (!SUPPORTS_SOCKETS) { // tslint:disable-next-line:no-invalid-this this.skip(); diff --git a/src/test/common/platform/filesystem.test.ts b/src/test/common/platform/filesystem.test.ts index 99cbc5f9a1fe..9600465216e1 100644 --- a/src/test/common/platform/filesystem.test.ts +++ b/src/test/common/platform/filesystem.test.ts @@ -57,7 +57,7 @@ suite('FileSystem - raw', () => { expect(stat).to.deep.equal(expected); }); - test('for symlinks, gets the info for the linked file', async function() { + test('for symlinks, gets the info for the linked file', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -72,7 +72,7 @@ suite('FileSystem - raw', () => { expect(stat).to.deep.equal(expected); }); - test('gets the info for a socket', async function() { + test('gets the info for a socket', async function () { if (!SUPPORTS_SOCKETS) { // tslint:disable-next-line:no-invalid-this return this.skip(); @@ -122,7 +122,7 @@ suite('FileSystem - raw', () => { await assertDoesNotExist(source); }); - test('rename symlink', async function() { + test('rename symlink', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -170,7 +170,7 @@ suite('FileSystem - raw', () => { await assertDoesNotExist(source); }); - test('move symlink', async function() { + test('move symlink', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -469,7 +469,7 @@ suite('FileSystem - raw', () => { }); suite('listdir', () => { - test('mixed', async function() { + test('mixed', async function () { // https://github.com/microsoft/vscode-python/issues/10240 // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -675,7 +675,7 @@ suite('FileSystem - utils', () => { expect(exists).to.equal(false); }); - test('symlinks are followed', async function() { + test('symlinks are followed', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -700,7 +700,7 @@ suite('FileSystem - utils', () => { expect(exists).to.equal(false); }); - test('matches (type: unknown)', async function() { + test('matches (type: unknown)', async function () { if (!SUPPORTS_SOCKETS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -738,7 +738,7 @@ suite('FileSystem - utils', () => { expect(exists).to.equal(false); }); - test('symlink', async function() { + test('symlink', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -752,7 +752,7 @@ suite('FileSystem - utils', () => { expect(exists).to.equal(true); }); - test('unknown', async function() { + test('unknown', async function () { if (!SUPPORTS_SOCKETS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -764,7 +764,7 @@ suite('FileSystem - utils', () => { expect(exists).to.equal(false); }); - test('failure in stat()', async function() { + test('failure in stat()', async function () { if (WINDOWS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -801,7 +801,7 @@ suite('FileSystem - utils', () => { expect(exists).to.equal(false); }); - test('symlink', async function() { + test('symlink', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -815,7 +815,7 @@ suite('FileSystem - utils', () => { expect(exists).to.equal(true); }); - test('unknown', async function() { + test('unknown', async function () { if (!SUPPORTS_SOCKETS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -827,7 +827,7 @@ suite('FileSystem - utils', () => { expect(exists).to.equal(false); }); - test('failure in stat()', async function() { + test('failure in stat()', async function () { if (WINDOWS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -883,7 +883,7 @@ suite('FileSystem - utils', () => { suite('isDirReadonly', () => { suite('non-Windows', () => { - suiteSetup(function() { + suiteSetup(function () { if (WINDOWS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -952,7 +952,7 @@ suite('FileSystem', () => { expect(stat).to.deep.equal(expected); }); - test('for symlinks, gets the info for the linked file', async function() { + test('for symlinks, gets the info for the linked file', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -967,7 +967,7 @@ suite('FileSystem', () => { expect(stat).to.deep.equal(expected); }); - test('gets the info for a socket', async function() { + test('gets the info for a socket', async function () { if (!SUPPORTS_SOCKETS) { // tslint:disable-next-line:no-invalid-this return this.skip(); @@ -1112,7 +1112,7 @@ suite('FileSystem', () => { expect(exists).to.equal(false); }); - test('symlink', async function() { + test('symlink', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -1126,7 +1126,7 @@ suite('FileSystem', () => { expect(exists).to.equal(true); }); - test('unknown', async function() { + test('unknown', async function () { if (!SUPPORTS_SOCKETS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -1156,7 +1156,7 @@ suite('FileSystem', () => { expect(exists).to.equal(false); }); - test('symlink', async function() { + test('symlink', async function () { if (!SUPPORTS_SYMLINKS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -1170,7 +1170,7 @@ suite('FileSystem', () => { expect(exists).to.equal(true); }); - test('unknown', async function() { + test('unknown', async function () { if (!SUPPORTS_SOCKETS) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -1262,7 +1262,7 @@ suite('FileSystem', () => { suite('isDirReadonly', () => { suite('non-Windows', () => { - suiteSetup(function() { + suiteSetup(function () { if (WINDOWS) { // tslint:disable-next-line:no-invalid-this this.skip(); diff --git a/src/test/common/platform/filesystem.unit.test.ts b/src/test/common/platform/filesystem.unit.test.ts index ed7fba870a7f..00e0ca55c9e3 100644 --- a/src/test/common/platform/filesystem.unit.test.ts +++ b/src/test/common/platform/filesystem.unit.test.ts @@ -31,11 +31,11 @@ function createDummyStat(filetype: FileType): FileStat { } function copyStat(stat: FileStat, old: TypeMoq.IMock) { - old.setup(s => s.size) // plug in the original value + old.setup((s) => s.size) // plug in the original value .returns(() => stat.size); - old.setup(s => s.ctimeMs) // plug in the original value + old.setup((s) => s.ctimeMs) // plug in the original value .returns(() => stat.ctime); - old.setup(s => s.mtimeMs) // plug in the original value + old.setup((s) => s.mtimeMs) // plug in the original value .returns(() => stat.mtime); } @@ -84,7 +84,7 @@ suite('Raw FileSystem', () => { }); function verifyAll() { raw.verifyAll(); - oldStats.forEach(stat => { + oldStats.forEach((stat) => { stat.verifyAll(); }); } @@ -102,34 +102,34 @@ suite('Raw FileSystem', () => { function setupStatFileType(stat: TypeMoq.IMock, filetype: FileType) { // This mirrors the logic in convertFileType(). if (filetype === FileType.File) { - stat.setup(s => s.isFile()) + stat.setup((s) => s.isFile()) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); } else if (filetype === FileType.Directory) { - stat.setup(s => s.isFile()) + stat.setup((s) => s.isFile()) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); - stat.setup(s => s.isDirectory()) + stat.setup((s) => s.isDirectory()) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); } else if ((filetype & FileType.SymbolicLink) > 0) { - stat.setup(s => s.isFile()) + stat.setup((s) => s.isFile()) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); - stat.setup(s => s.isDirectory()) + stat.setup((s) => s.isDirectory()) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); - stat.setup(s => s.isSymbolicLink()) + stat.setup((s) => s.isSymbolicLink()) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); } else if (filetype === FileType.Unknown) { - stat.setup(s => s.isFile()) + stat.setup((s) => s.isFile()) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); - stat.setup(s => s.isDirectory()) + stat.setup((s) => s.isDirectory()) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); - stat.setup(s => s.isSymbolicLink()) + stat.setup((s) => s.isSymbolicLink()) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); } else { @@ -141,7 +141,7 @@ suite('Raw FileSystem', () => { test('wraps the low-level function', async () => { const filename = 'x/y/z/spam.py'; const expected = createDummyStat(FileType.File); - raw.setup(r => r.stat(Uri(filename))) // expect the specific filename + raw.setup((r) => r.stat(Uri(filename))) // expect the specific filename .returns(() => Promise.resolve(expected)); const stat = await filesystem.stat(filename); @@ -151,7 +151,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.stat(TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.stat(TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.stat('spam.py'); @@ -167,7 +167,7 @@ suite('Raw FileSystem', () => { { kind: 'dir', filetype: FileType.Directory }, { kind: 'symlink', filetype: FileType.SymbolicLink }, { kind: 'unknown', filetype: FileType.Unknown } - ].forEach(testData => { + ].forEach((testData) => { test(`wraps the low-level function (filetype: ${testData.kind}`, async () => { const filename = 'x/y/z/spam.py'; const expected: FileStat = { @@ -180,7 +180,7 @@ suite('Raw FileSystem', () => { const old = createMockLegacyStat(); setupStatFileType(old, testData.filetype); copyStat(expected, old); - raw.setup(r => r.lstat(filename)) // expect the specific filename + raw.setup((r) => r.lstat(filename)) // expect the specific filename .returns(() => Promise.resolve(old.object)); const stat = await filesystem.lstat(filename); @@ -191,7 +191,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.lstat(TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.lstat(TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.lstat('spam.py'); @@ -205,7 +205,7 @@ suite('Raw FileSystem', () => { test('passes through a string mode', async () => { const filename = 'x/y/z/spam.py'; const mode = '755'; - raw.setup(r => r.chmod(filename, mode)) // expect the specific filename + raw.setup((r) => r.chmod(filename, mode)) // expect the specific filename .returns(() => Promise.resolve()); await filesystem.chmod(filename, mode); @@ -216,7 +216,7 @@ suite('Raw FileSystem', () => { test('passes through an int mode', async () => { const filename = 'x/y/z/spam.py'; const mode = 0o755; - raw.setup(r => r.chmod(filename, mode)) // expect the specific filename + raw.setup((r) => r.chmod(filename, mode)) // expect the specific filename .returns(() => Promise.resolve()); await filesystem.chmod(filename, mode); @@ -225,7 +225,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.chmod(TypeMoq.It.isAny(), TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.chmod(TypeMoq.It.isAny(), TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.chmod('spam.py', 755); @@ -239,11 +239,11 @@ suite('Raw FileSystem', () => { test('move a file (target does not exist)', async () => { const src = 'x/y/z/spam.py'; const tgt = 'x/y/spam.py'; - raw.setup(r => r.dirname(tgt)) // Provide the target's parent. + raw.setup((r) => r.dirname(tgt)) // Provide the target's parent. .returns(() => 'x/y'); - raw.setup(r => r.stat(Uri('x/y'))) // The parent dir exists. + raw.setup((r) => r.stat(Uri('x/y'))) // The parent dir exists. .returns(() => Promise.resolve((undefined as unknown) as FileStat)); - raw.setup(r => r.rename(Uri(src), Uri(tgt), { overwrite: false })) // expect the specific filename + raw.setup((r) => r.rename(Uri(src), Uri(tgt), { overwrite: false })) // expect the specific filename .returns(() => Promise.resolve()); await filesystem.move(src, tgt); @@ -254,16 +254,16 @@ suite('Raw FileSystem', () => { test('move a file (target exists)', async () => { const src = 'x/y/z/spam.py'; const tgt = 'x/y/spam.py'; - raw.setup(r => r.dirname(tgt)) // Provide the target's parent. + raw.setup((r) => r.dirname(tgt)) // Provide the target's parent. .returns(() => 'x/y'); - raw.setup(r => r.stat(Uri('x/y'))) // The parent dir exists. + raw.setup((r) => r.stat(Uri('x/y'))) // The parent dir exists. .returns(() => Promise.resolve((undefined as unknown) as FileStat)); const err = vscode.FileSystemError.FileExists('...'); - raw.setup(r => r.rename(Uri(src), Uri(tgt), { overwrite: false })) // expect the specific filename + raw.setup((r) => r.rename(Uri(src), Uri(tgt), { overwrite: false })) // expect the specific filename .returns(() => Promise.reject(err)); - raw.setup(r => r.stat(Uri(tgt))) // It's a file. + raw.setup((r) => r.stat(Uri(tgt))) // It's a file. .returns(() => Promise.resolve(({ type: FileType.File } as unknown) as FileStat)); - raw.setup(r => r.rename(Uri(src), Uri(tgt), { overwrite: true })) // expect the specific filename + raw.setup((r) => r.rename(Uri(src), Uri(tgt), { overwrite: true })) // expect the specific filename .returns(() => Promise.resolve()); await filesystem.move(src, tgt); @@ -274,11 +274,11 @@ suite('Raw FileSystem', () => { test('move a directory (target does not exist)', async () => { const src = 'x/y/z/spam'; const tgt = 'x/y/spam'; - raw.setup(r => r.dirname(tgt)) // Provide the target's parent. + raw.setup((r) => r.dirname(tgt)) // Provide the target's parent. .returns(() => 'x/y'); - raw.setup(r => r.stat(Uri('x/y'))) // The parent dir exists. + raw.setup((r) => r.stat(Uri('x/y'))) // The parent dir exists. .returns(() => Promise.resolve((undefined as unknown) as FileStat)); - raw.setup(r => r.rename(Uri(src), Uri(tgt), { overwrite: false })) // expect the specific filename + raw.setup((r) => r.rename(Uri(src), Uri(tgt), { overwrite: false })) // expect the specific filename .returns(() => Promise.resolve()); await filesystem.move(src, tgt); @@ -289,14 +289,14 @@ suite('Raw FileSystem', () => { test('moving a directory fails if target exists', async () => { const src = 'x/y/z/spam.py'; const tgt = 'x/y/spam.py'; - raw.setup(r => r.dirname(tgt)) // Provide the target's parent. + raw.setup((r) => r.dirname(tgt)) // Provide the target's parent. .returns(() => 'x/y'); - raw.setup(r => r.stat(Uri('x/y'))) // The parent dir exists. + raw.setup((r) => r.stat(Uri('x/y'))) // The parent dir exists. .returns(() => Promise.resolve((undefined as unknown) as FileStat)); const err = vscode.FileSystemError.FileExists('...'); - raw.setup(r => r.rename(Uri(src), Uri(tgt), { overwrite: false })) // expect the specific filename + raw.setup((r) => r.rename(Uri(src), Uri(tgt), { overwrite: false })) // expect the specific filename .returns(() => Promise.reject(err)); - raw.setup(r => r.stat(Uri(tgt))) // It's a directory. + raw.setup((r) => r.stat(Uri(tgt))) // It's a directory. .returns(() => Promise.resolve(({ type: FileType.Directory } as unknown) as FileStat)); const promise = filesystem.move(src, tgt); @@ -308,18 +308,18 @@ suite('Raw FileSystem', () => { test('move a symlink to a directory (target exists)', async () => { const src = 'x/y/z/spam'; const tgt = 'x/y/spam.lnk'; - raw.setup(r => r.dirname(tgt)) // Provide the target's parent. + raw.setup((r) => r.dirname(tgt)) // Provide the target's parent. .returns(() => 'x/y'); - raw.setup(r => r.stat(Uri('x/y'))) // The parent dir exists. + raw.setup((r) => r.stat(Uri('x/y'))) // The parent dir exists. .returns(() => Promise.resolve((undefined as unknown) as FileStat)); const err = vscode.FileSystemError.FileExists('...'); - raw.setup(r => r.rename(Uri(src), Uri(tgt), { overwrite: false })) // expect the specific filename + raw.setup((r) => r.rename(Uri(src), Uri(tgt), { overwrite: false })) // expect the specific filename .returns(() => Promise.reject(err)); - raw.setup(r => r.stat(Uri(tgt))) // It's a symlink. + raw.setup((r) => r.stat(Uri(tgt))) // It's a symlink. .returns(() => Promise.resolve(({ type: FileType.SymbolicLink | FileType.Directory } as unknown) as FileStat) ); - raw.setup(r => r.rename(Uri(src), Uri(tgt), { overwrite: true })) // expect the specific filename + raw.setup((r) => r.rename(Uri(src), Uri(tgt), { overwrite: true })) // expect the specific filename .returns(() => Promise.resolve()); await filesystem.move(src, tgt); @@ -328,10 +328,10 @@ suite('Raw FileSystem', () => { }); test('fails if the target parent dir does not exist', async () => { - raw.setup(r => r.dirname(TypeMoq.It.isAny())) // Provide the target's parent. + raw.setup((r) => r.dirname(TypeMoq.It.isAny())) // Provide the target's parent. .returns(() => ''); const err = vscode.FileSystemError.FileNotFound('...'); - raw.setup(r => r.stat(TypeMoq.It.isAny())) // The parent dir does not exist. + raw.setup((r) => r.stat(TypeMoq.It.isAny())) // The parent dir does not exist. .returns(() => Promise.reject(err)); const promise = filesystem.move('spam', 'eggs'); @@ -341,12 +341,12 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.dirname(TypeMoq.It.isAny())) // Provide the target's parent. + raw.setup((r) => r.dirname(TypeMoq.It.isAny())) // Provide the target's parent. .returns(() => ''); - raw.setup(r => r.stat(TypeMoq.It.isAny())) // The parent dir exists. + raw.setup((r) => r.stat(TypeMoq.It.isAny())) // The parent dir exists. .returns(() => Promise.resolve((undefined as unknown) as FileStat)); const err = new Error('oops!'); - raw.setup(r => r.rename(TypeMoq.It.isAny(), TypeMoq.It.isAny(), { overwrite: false })) // We don't care about the filename. + raw.setup((r) => r.rename(TypeMoq.It.isAny(), TypeMoq.It.isAny(), { overwrite: false })) // We don't care about the filename. .throws(err); const promise = filesystem.move('spam', 'eggs'); @@ -360,7 +360,7 @@ suite('Raw FileSystem', () => { test('wraps the low-level function', async () => { const filename = 'x/y/z/spam.py'; const expected = Buffer.from(''); - raw.setup(r => r.readFile(Uri(filename))) // expect the specific filename + raw.setup((r) => r.readFile(Uri(filename))) // expect the specific filename .returns(() => Promise.resolve(expected)); const data = await filesystem.readData(filename); @@ -370,7 +370,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.readFile(TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.readFile(TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.readData('spam.py'); @@ -385,7 +385,7 @@ suite('Raw FileSystem', () => { const filename = 'x/y/z/spam.py'; const expected = ''; const data = Buffer.from(expected); - raw.setup(r => r.readFile(Uri(filename))) // expect the specific filename + raw.setup((r) => r.readFile(Uri(filename))) // expect the specific filename .returns(() => Promise.resolve(data)); const text = await filesystem.readText(filename); @@ -395,7 +395,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.readFile(TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.readFile(TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.readText('spam.py'); @@ -410,7 +410,7 @@ suite('Raw FileSystem', () => { const filename = 'x/y/z/spam.py'; const text = ''; const data = Buffer.from(text); - raw.setup(r => r.writeFile(Uri(filename), data)) // expect the specific filename + raw.setup((r) => r.writeFile(Uri(filename), data)) // expect the specific filename .returns(() => Promise.resolve()); await filesystem.writeText(filename, text); @@ -419,7 +419,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.writeFile(TypeMoq.It.isAny(), TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.writeFile(TypeMoq.It.isAny(), TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.writeText('spam.py', ''); @@ -433,7 +433,7 @@ suite('Raw FileSystem', () => { test('wraps the low-level function', async () => { const filename = 'x/y/z/spam.py'; const text = ''; - raw.setup(r => r.appendFile(filename, text)) // expect the specific filename + raw.setup((r) => r.appendFile(filename, text)) // expect the specific filename .returns(() => Promise.resolve()); await filesystem.appendText(filename, text); @@ -442,7 +442,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.appendFile(TypeMoq.It.isAny(), TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.appendFile(TypeMoq.It.isAny(), TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.appendText('spam.py', ''); @@ -456,11 +456,11 @@ suite('Raw FileSystem', () => { test('wraps the low-level function', async () => { const src = 'x/y/z/spam.py'; const tgt = 'x/y/z/eggs.py'; - raw.setup(r => r.dirname(tgt)) // Provide the target's parent. + raw.setup((r) => r.dirname(tgt)) // Provide the target's parent. .returns(() => 'x/y/z'); - raw.setup(r => r.stat(Uri('x/y/z'))) // The parent dir exists. + raw.setup((r) => r.stat(Uri('x/y/z'))) // The parent dir exists. .returns(() => Promise.resolve((undefined as unknown) as FileStat)); - raw.setup(r => r.copy(Uri(src), Uri(tgt), { overwrite: true })) // Expect the specific args. + raw.setup((r) => r.copy(Uri(src), Uri(tgt), { overwrite: true })) // Expect the specific args. .returns(() => Promise.resolve()); await filesystem.copyFile(src, tgt); @@ -469,10 +469,10 @@ suite('Raw FileSystem', () => { }); test('fails if target parent does not exist', async () => { - raw.setup(r => r.dirname(TypeMoq.It.isAny())) // Provide the target's parent. + raw.setup((r) => r.dirname(TypeMoq.It.isAny())) // Provide the target's parent. .returns(() => ''); const err = vscode.FileSystemError.FileNotFound('...'); - raw.setup(r => r.stat(TypeMoq.It.isAny())) // The parent dir exists. + raw.setup((r) => r.stat(TypeMoq.It.isAny())) // The parent dir exists. .returns(() => Promise.reject(err)); const promise = filesystem.copyFile('spam', 'eggs'); @@ -482,11 +482,11 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.dirname(TypeMoq.It.isAny())) // Provide the target's parent. + raw.setup((r) => r.dirname(TypeMoq.It.isAny())) // Provide the target's parent. .returns(() => ''); - raw.setup(r => r.stat(TypeMoq.It.isAny())) // The parent dir exists. + raw.setup((r) => r.stat(TypeMoq.It.isAny())) // The parent dir exists. .returns(() => Promise.resolve((undefined as unknown) as FileStat)); - raw.setup(r => r.copy(TypeMoq.It.isAny(), TypeMoq.It.isAny(), { overwrite: true })) // We don't care about the filename. + raw.setup((r) => r.copy(TypeMoq.It.isAny(), TypeMoq.It.isAny(), { overwrite: true })) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.copyFile('spam', 'eggs'); @@ -504,7 +504,7 @@ suite('Raw FileSystem', () => { test('wraps the low-level function', async () => { const filename = 'x/y/z/spam.py'; - raw.setup(r => r.delete(Uri(filename), opts)) // expect the specific filename + raw.setup((r) => r.delete(Uri(filename), opts)) // expect the specific filename .returns(() => Promise.resolve()); await filesystem.rmfile(filename); @@ -513,7 +513,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.delete(TypeMoq.It.isAny(), opts)) // We don't care about the filename. + raw.setup((r) => r.delete(TypeMoq.It.isAny(), opts)) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.rmfile('spam.py'); @@ -526,7 +526,7 @@ suite('Raw FileSystem', () => { suite('mkdirp', () => { test('wraps the low-level function', async () => { const dirname = 'x/y/z/spam'; - raw.setup(r => r.createDirectory(Uri(dirname))) // expect the specific filename + raw.setup((r) => r.createDirectory(Uri(dirname))) // expect the specific filename .returns(() => Promise.resolve()); await filesystem.mkdirp(dirname); @@ -535,7 +535,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.createDirectory(TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.createDirectory(TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.mkdirp('spam'); @@ -553,9 +553,9 @@ suite('Raw FileSystem', () => { test('directory is empty', async () => { const dirname = 'x/y/z/spam'; - raw.setup(r => r.readDirectory(Uri(dirname))) // The dir is empty. + raw.setup((r) => r.readDirectory(Uri(dirname))) // The dir is empty. .returns(() => Promise.resolve([])); - raw.setup(r => r.delete(Uri(dirname), opts)) // Expect the specific args. + raw.setup((r) => r.delete(Uri(dirname), opts)) // Expect the specific args. .returns(() => Promise.resolve()); await filesystem.rmdir(dirname); @@ -564,7 +564,7 @@ suite('Raw FileSystem', () => { }); test('fails if readDirectory() fails (e.g. is a file)', async () => { - raw.setup(r => r.readDirectory(TypeMoq.It.isAny())) // It's not a directory. + raw.setup((r) => r.readDirectory(TypeMoq.It.isAny())) // It's not a directory. .throws(new Error('is a file')); const promise = filesystem.rmdir('spam'); @@ -580,7 +580,7 @@ suite('Raw FileSystem', () => { ['spam.py', FileType.File], ['other', FileType.SymbolicLink | FileType.File] ]; - raw.setup(r => r.readDirectory(TypeMoq.It.isAny())) // The dir is not empty. + raw.setup((r) => r.readDirectory(TypeMoq.It.isAny())) // The dir is not empty. .returns(() => Promise.resolve(entries)); const promise = filesystem.rmdir('spam'); @@ -590,9 +590,9 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.readDirectory(TypeMoq.It.isAny())) // The "file" exists. + raw.setup((r) => r.readDirectory(TypeMoq.It.isAny())) // The "file" exists. .returns(() => Promise.resolve([])); - raw.setup(r => r.delete(TypeMoq.It.isAny(), opts)) // We don't care about the filename. + raw.setup((r) => r.delete(TypeMoq.It.isAny(), opts)) // We don't care about the filename. .throws(new Error('oops!')); const promise = filesystem.rmdir('spam'); @@ -610,9 +610,9 @@ suite('Raw FileSystem', () => { test('wraps the low-level function', async () => { const dirname = 'x/y/z/spam'; - raw.setup(r => r.stat(Uri(dirname))) // The dir exists. + raw.setup((r) => r.stat(Uri(dirname))) // The dir exists. .returns(() => Promise.resolve((undefined as unknown) as FileStat)); - raw.setup(r => r.delete(Uri(dirname), opts)) // Expect the specific dirname. + raw.setup((r) => r.delete(Uri(dirname), opts)) // Expect the specific dirname. .returns(() => Promise.resolve()); await filesystem.rmtree(dirname); @@ -621,9 +621,9 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.stat(TypeMoq.It.isAny())) // The "file" exists. + raw.setup((r) => r.stat(TypeMoq.It.isAny())) // The "file" exists. .returns(() => Promise.resolve((undefined as unknown) as FileStat)); - raw.setup(r => r.delete(TypeMoq.It.isAny(), opts)) // We don't care about the filename. + raw.setup((r) => r.delete(TypeMoq.It.isAny(), opts)) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.rmtree('spam'); @@ -644,11 +644,11 @@ suite('Raw FileSystem', () => { ]; const expected = actual.map(([basename, filetype]) => { const filename = `x/y/z/spam/${basename}`; - raw.setup(r => r.join(dirname, basename)) // Expect the specific basename. + raw.setup((r) => r.join(dirname, basename)) // Expect the specific basename. .returns(() => filename); return [filename, filetype] as [string, FileType]; }); - raw.setup(r => r.readDirectory(Uri(dirname))) // Expect the specific filename. + raw.setup((r) => r.readDirectory(Uri(dirname))) // Expect the specific filename. .returns(() => Promise.resolve(actual)); const entries = await filesystem.listdir(dirname); @@ -660,7 +660,7 @@ suite('Raw FileSystem', () => { test('empty', async () => { const dirname = 'x/y/z/spam'; const expected: [string, FileType][] = []; - raw.setup(r => r.readDirectory(Uri(dirname))) // expect the specific filename + raw.setup((r) => r.readDirectory(Uri(dirname))) // expect the specific filename .returns(() => Promise.resolve([])); const entries = await filesystem.listdir(dirname); @@ -670,7 +670,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.readDirectory(TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.readDirectory(TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); const promise = filesystem.listdir('spam'); @@ -693,7 +693,7 @@ suite('Raw FileSystem', () => { const lstat = createMockLegacyStat(); setupStatFileType(lstat, FileType.Unknown); copyStat(expected, lstat); - raw.setup(r => r.lstatSync(filename)) // expect the specific filename + raw.setup((r) => r.lstatSync(filename)) // expect the specific filename .returns(() => lstat.object); const stat = filesystem.statSync(filename); @@ -705,7 +705,7 @@ suite('Raw FileSystem', () => { [ { kind: 'file', filetype: FileType.File }, { kind: 'dir', filetype: FileType.Directory } - ].forEach(testData => { + ].forEach((testData) => { test(`wraps the low-level function (filetype: ${testData.kind})`, async () => { const filename = 'x/y/z/spam.py'; const expected: FileStat = { @@ -717,11 +717,11 @@ suite('Raw FileSystem', () => { } as any; const lstat = createMockLegacyStat(); lstat - .setup(s => s.isSymbolicLink()) // not a symlink + .setup((s) => s.isSymbolicLink()) // not a symlink .returns(() => false); setupStatFileType(lstat, testData.filetype); copyStat(expected, lstat); - raw.setup(r => r.lstatSync(filename)) // expect the specific filename + raw.setup((r) => r.lstatSync(filename)) // expect the specific filename .returns(() => lstat.object); const stat = filesystem.statSync(filename); @@ -735,7 +735,7 @@ suite('Raw FileSystem', () => { { kind: 'file', filetype: FileType.File }, { kind: 'dir', filetype: FileType.Directory }, { kind: 'unknown', filetype: FileType.Unknown } - ].forEach(testData => { + ].forEach((testData) => { test(`wraps the low-level function (filetype: ${testData.kind} symlink)`, async () => { const filename = 'x/y/z/spam.py'; const expected: FileStat = { @@ -747,14 +747,14 @@ suite('Raw FileSystem', () => { } as any; const lstat = createMockLegacyStat(); lstat - .setup(s => s.isSymbolicLink()) // not a symlink + .setup((s) => s.isSymbolicLink()) // not a symlink .returns(() => true); - raw.setup(r => r.lstatSync(filename)) // expect the specific filename + raw.setup((r) => r.lstatSync(filename)) // expect the specific filename .returns(() => lstat.object); const old = createMockLegacyStat(); setupStatFileType(old, testData.filetype); copyStat(expected, old); - raw.setup(r => r.statSync(filename)) // expect the specific filename + raw.setup((r) => r.statSync(filename)) // expect the specific filename .returns(() => old.object); const stat = filesystem.statSync(filename); @@ -765,7 +765,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.lstatSync(TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.lstatSync(TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); expect(() => { @@ -779,7 +779,7 @@ suite('Raw FileSystem', () => { test('wraps the low-level function', () => { const filename = 'x/y/z/spam.py'; const expected = ''; - raw.setup(r => r.readFileSync(filename, 'utf8')) // expect the specific filename + raw.setup((r) => r.readFileSync(filename, 'utf8')) // expect the specific filename .returns(() => expected); const text = filesystem.readTextSync(filename); @@ -789,7 +789,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.readFileSync(TypeMoq.It.isAny(), TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.readFileSync(TypeMoq.It.isAny(), TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); expect(() => filesystem.readTextSync('spam.py')).to.throw(); @@ -803,7 +803,7 @@ suite('Raw FileSystem', () => { const filename = 'x/y/z/spam.py'; //tslint:disable-next-line:no-any const expected = {} as any; - raw.setup(r => r.createReadStream(filename)) // expect the specific filename + raw.setup((r) => r.createReadStream(filename)) // expect the specific filename .returns(() => expected); const stream = filesystem.createReadStream(filename); @@ -813,7 +813,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.createReadStream(TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.createReadStream(TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); expect(() => filesystem.createReadStream('spam.py')).to.throw(); @@ -827,7 +827,7 @@ suite('Raw FileSystem', () => { const filename = 'x/y/z/spam.py'; //tslint:disable-next-line:no-any const expected = {} as any; - raw.setup(r => r.createWriteStream(filename)) // expect the specific filename + raw.setup((r) => r.createWriteStream(filename)) // expect the specific filename .returns(() => expected); const stream = filesystem.createWriteStream(filename); @@ -837,7 +837,7 @@ suite('Raw FileSystem', () => { }); test('fails if the low-level call fails', async () => { - raw.setup(r => r.createWriteStream(TypeMoq.It.isAny())) // We don't care about the filename. + raw.setup((r) => r.createWriteStream(TypeMoq.It.isAny())) // We don't care about the filename. .throws(new Error('file not found')); expect(() => filesystem.createWriteStream('spam.py')).to.throw(); @@ -873,7 +873,7 @@ suite('FileSystemUtils', () => { }); function verifyAll() { deps.verifyAll(); - stats.forEach(stat => { + stats.forEach((stat) => { stat.verifyAll(); }); } @@ -892,7 +892,7 @@ suite('FileSystemUtils', () => { suite('createDirectory', () => { test('wraps the low-level function', async () => { const dirname = 'x/y/z/spam'; - deps.setup(d => d.mkdirp(dirname)) // expect the specific filename + deps.setup((d) => d.mkdirp(dirname)) // expect the specific filename .returns(() => Promise.resolve()); await utils.createDirectory(dirname); @@ -904,7 +904,7 @@ suite('FileSystemUtils', () => { suite('deleteDirectory', () => { test('wraps the low-level function', async () => { const dirname = 'x/y/z/spam'; - deps.setup(d => d.rmdir(dirname)) // expect the specific filename + deps.setup((d) => d.rmdir(dirname)) // expect the specific filename .returns(() => Promise.resolve()); await utils.deleteDirectory(dirname); @@ -916,7 +916,7 @@ suite('FileSystemUtils', () => { suite('deleteFile', () => { test('wraps the low-level function', async () => { const filename = 'x/y/z/spam.py'; - deps.setup(d => d.rmfile(filename)) // expect the specific filename + deps.setup((d) => d.rmfile(filename)) // expect the specific filename .returns(() => Promise.resolve()); await utils.deleteFile(filename); @@ -929,7 +929,7 @@ suite('FileSystemUtils', () => { test('exists (without type)', async () => { const filename = 'x/y/z/spam.py'; const stat = createMockStat(); - deps.setup(d => d.stat(filename)) // The "file" exists. + deps.setup((d) => d.stat(filename)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.pathExists(filename); @@ -941,7 +941,7 @@ suite('FileSystemUtils', () => { test('does not exist', async () => { const filename = 'x/y/z/spam.py'; const err = vscode.FileSystemError.FileNotFound(filename); - deps.setup(d => d.stat(filename)) // The file does not exist. + deps.setup((d) => d.stat(filename)) // The file does not exist. .throws(err); const exists = await utils.pathExists(filename); @@ -952,7 +952,7 @@ suite('FileSystemUtils', () => { test('ignores errors from stat()', async () => { const filename = 'x/y/z/spam.py'; - deps.setup(d => d.stat(filename)) // It's broken. + deps.setup((d) => d.stat(filename)) // It's broken. .returns(() => Promise.reject(new Error('oops!'))); const exists = await utils.pathExists(filename); @@ -964,7 +964,7 @@ suite('FileSystemUtils', () => { test('matches (type: undefined)', async () => { const filename = 'x/y/z/spam.py'; const stat = createMockStat(); - deps.setup(d => d.stat(filename)) // The "file" exists. + deps.setup((d) => d.stat(filename)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.pathExists(filename); @@ -976,9 +976,9 @@ suite('FileSystemUtils', () => { test('matches (type: file)', async () => { const filename = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a file. + stat.setup((s) => s.type) // It's a file. .returns(() => FileType.File); - deps.setup(d => d.stat(filename)) // The "file" exists. + deps.setup((d) => d.stat(filename)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.pathExists(filename, FileType.File); @@ -990,9 +990,9 @@ suite('FileSystemUtils', () => { test('mismatch (type: file)', async () => { const filename = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a directory. + stat.setup((s) => s.type) // It's a directory. .returns(() => FileType.Directory); - deps.setup(d => d.stat(filename)) // The "file" exists. + deps.setup((d) => d.stat(filename)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.pathExists(filename, FileType.File); @@ -1004,9 +1004,9 @@ suite('FileSystemUtils', () => { test('matches (type: directory)', async () => { const dirname = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a directory. + stat.setup((s) => s.type) // It's a directory. .returns(() => FileType.Directory); - deps.setup(d => d.stat(dirname)) // The "file" exists. + deps.setup((d) => d.stat(dirname)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.pathExists(dirname, FileType.Directory); @@ -1018,9 +1018,9 @@ suite('FileSystemUtils', () => { test('mismatch (type: directory)', async () => { const dirname = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a file. + stat.setup((s) => s.type) // It's a file. .returns(() => FileType.File); - deps.setup(d => d.stat(dirname)) // The "file" exists. + deps.setup((d) => d.stat(dirname)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.pathExists(dirname, FileType.Directory); @@ -1032,10 +1032,10 @@ suite('FileSystemUtils', () => { test('symlinks are followed', async () => { const symlink = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a symlink to a file. + stat.setup((s) => s.type) // It's a symlink to a file. .returns(() => FileType.File | FileType.SymbolicLink) .verifiable(TypeMoq.Times.exactly(3)); - deps.setup(d => d.stat(symlink)) // The "file" exists. + deps.setup((d) => d.stat(symlink)) // The "file" exists. .returns(() => Promise.resolve(stat.object)) .verifiable(TypeMoq.Times.exactly(3)); @@ -1052,9 +1052,9 @@ suite('FileSystemUtils', () => { test('mismatch (type: symlink)', async () => { const filename = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a file. + stat.setup((s) => s.type) // It's a file. .returns(() => FileType.File); - deps.setup(d => d.stat(filename)) // The "file" exists. + deps.setup((d) => d.stat(filename)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.pathExists(filename, FileType.SymbolicLink); @@ -1066,9 +1066,9 @@ suite('FileSystemUtils', () => { test('matches (type: unknown)', async () => { const sockFile = 'x/y/z/ipc.sock'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a socket. + stat.setup((s) => s.type) // It's a socket. .returns(() => FileType.Unknown); - deps.setup(d => d.stat(sockFile)) // The "file" exists. + deps.setup((d) => d.stat(sockFile)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.pathExists(sockFile, FileType.Unknown); @@ -1080,9 +1080,9 @@ suite('FileSystemUtils', () => { test('mismatch (type: unknown)', async () => { const filename = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a file. + stat.setup((s) => s.type) // It's a file. .returns(() => FileType.File); - deps.setup(d => d.stat(filename)) // The "file" exists. + deps.setup((d) => d.stat(filename)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.pathExists(filename, FileType.Unknown); @@ -1096,9 +1096,9 @@ suite('FileSystemUtils', () => { test('want file, got file', async () => { const filename = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a File. + stat.setup((s) => s.type) // It's a File. .returns(() => FileType.File); - deps.setup(d => d.stat(filename)) // The "file" exists. + deps.setup((d) => d.stat(filename)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.fileExists(filename); @@ -1110,9 +1110,9 @@ suite('FileSystemUtils', () => { test('want file, not file', async () => { const filename = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a directory. + stat.setup((s) => s.type) // It's a directory. .returns(() => FileType.Directory); - deps.setup(d => d.stat(filename)) // The "file" exists. + deps.setup((d) => d.stat(filename)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.fileExists(filename); @@ -1124,9 +1124,9 @@ suite('FileSystemUtils', () => { test('symlink', async () => { const symlink = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a symlink to a File. + stat.setup((s) => s.type) // It's a symlink to a File. .returns(() => FileType.File | FileType.SymbolicLink); - deps.setup(d => d.stat(symlink)) // The "file" exists. + deps.setup((d) => d.stat(symlink)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.fileExists(symlink); @@ -1139,9 +1139,9 @@ suite('FileSystemUtils', () => { test('unknown', async () => { const sockFile = 'x/y/z/ipc.sock'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a socket. + stat.setup((s) => s.type) // It's a socket. .returns(() => FileType.Unknown); - deps.setup(d => d.stat(sockFile)) // The "file" exists. + deps.setup((d) => d.stat(sockFile)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.fileExists(sockFile); @@ -1155,9 +1155,9 @@ suite('FileSystemUtils', () => { test('want directory, got directory', async () => { const dirname = 'x/y/z/spam'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a directory. + stat.setup((s) => s.type) // It's a directory. .returns(() => FileType.Directory); - deps.setup(d => d.stat(dirname)) // The "file" exists. + deps.setup((d) => d.stat(dirname)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.directoryExists(dirname); @@ -1169,9 +1169,9 @@ suite('FileSystemUtils', () => { test('want directory, not directory', async () => { const dirname = 'x/y/z/spam'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a file. + stat.setup((s) => s.type) // It's a file. .returns(() => FileType.File); - deps.setup(d => d.stat(dirname)) // The "file" exists. + deps.setup((d) => d.stat(dirname)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.directoryExists(dirname); @@ -1183,9 +1183,9 @@ suite('FileSystemUtils', () => { test('symlink', async () => { const symlink = 'x/y/z/spam'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a symlink to a directory. + stat.setup((s) => s.type) // It's a symlink to a directory. .returns(() => FileType.Directory | FileType.SymbolicLink); - deps.setup(d => d.stat(symlink)) // The "file" exists. + deps.setup((d) => d.stat(symlink)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.directoryExists(symlink); @@ -1198,9 +1198,9 @@ suite('FileSystemUtils', () => { test('unknown', async () => { const sockFile = 'x/y/z/ipc.sock'; const stat = createMockStat(); - stat.setup(s => s.type) // It's a socket. + stat.setup((s) => s.type) // It's a socket. .returns(() => FileType.Unknown); - deps.setup(d => d.stat(sockFile)) // The "file" exists. + deps.setup((d) => d.stat(sockFile)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const exists = await utils.directoryExists(sockFile); @@ -1219,7 +1219,7 @@ suite('FileSystemUtils', () => { ['x/y/z/spam/spam.py', FileType.File], ['x/y/z/spam/other', FileType.SymbolicLink | FileType.File] ]; - deps.setup(d => d.listdir(dirname)) // Full results get returned from RawFileSystem.listdir(). + deps.setup((d) => d.listdir(dirname)) // Full results get returned from RawFileSystem.listdir(). .returns(() => Promise.resolve(expected)); const entries = await utils.listdir(dirname); @@ -1231,9 +1231,9 @@ suite('FileSystemUtils', () => { test('returns [] if the directory does not exist', async () => { const dirname = 'x/y/z/spam'; const err = vscode.FileSystemError.FileNotFound(dirname); - deps.setup(d => d.listdir(dirname)) // The "file" does not exist. + deps.setup((d) => d.listdir(dirname)) // The "file" does not exist. .returns(() => Promise.reject(err)); - deps.setup(d => d.stat(dirname)) // The "file" does not exist. + deps.setup((d) => d.stat(dirname)) // The "file" does not exist. .returns(() => Promise.reject(err)); const entries = await utils.listdir(dirname); @@ -1245,10 +1245,10 @@ suite('FileSystemUtils', () => { test('fails if not a directory', async () => { const dirname = 'x/y/z/spam'; const err = vscode.FileSystemError.FileNotADirectory(dirname); - deps.setup(d => d.listdir(dirname)) // Fail (async) with not-a-directory. + deps.setup((d) => d.listdir(dirname)) // Fail (async) with not-a-directory. .returns(() => Promise.reject(err)); const stat = createMockStat(); - deps.setup(d => d.stat(dirname)) // The "file" exists. + deps.setup((d) => d.stat(dirname)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const promise = utils.listdir(dirname); @@ -1260,9 +1260,9 @@ suite('FileSystemUtils', () => { test('fails if the raw call promise fails', async () => { const dirname = 'x/y/z/spam'; const err = new Error('oops!'); - deps.setup(d => d.listdir(dirname)) // Fail (async) with an arbitrary error. + deps.setup((d) => d.listdir(dirname)) // Fail (async) with an arbitrary error. .returns(() => Promise.reject(err)); - deps.setup(d => d.stat(dirname)) // Fail with file-not-found. + deps.setup((d) => d.stat(dirname)) // Fail with file-not-found. .throws(vscode.FileSystemError.FileNotFound(dirname)); const entries = await utils.listdir(dirname); @@ -1274,10 +1274,10 @@ suite('FileSystemUtils', () => { test('fails if the raw call fails', async () => { const dirname = 'x/y/z/spam'; const err = new Error('oops!'); - deps.setup(d => d.listdir(dirname)) // Fail with an arbirary error. + deps.setup((d) => d.listdir(dirname)) // Fail with an arbirary error. .throws(err); const stat = createMockStat(); - deps.setup(d => d.stat(dirname)) // The "file" exists. + deps.setup((d) => d.stat(dirname)) // The "file" exists. .returns(() => Promise.resolve(stat.object)); const promise = utils.listdir(dirname); @@ -1305,7 +1305,7 @@ suite('FileSystemUtils', () => { 'x/y/z/spam/v', 'x/y/z/spam/other2' ]; - deps.setup(d => d.listdir(dirname)) // Full results get returned from RawFileSystem.listdir(). + deps.setup((d) => d.listdir(dirname)) // Full results get returned from RawFileSystem.listdir(). .returns(() => Promise.resolve(entries)); const filtered = await utils.getSubDirectories(dirname); @@ -1333,7 +1333,7 @@ suite('FileSystemUtils', () => { 'x/y/z/spam/eggs.py', 'x/y/z/spam/other1' ]; - deps.setup(d => d.listdir(filename)) // Full results get returned from RawFileSystem.listdir(). + deps.setup((d) => d.listdir(filename)) // Full results get returned from RawFileSystem.listdir(). .returns(() => Promise.resolve(entries)); const filtered = await utils.getFiles(filename); @@ -1345,18 +1345,18 @@ suite('FileSystemUtils', () => { suite('isDirReadonly', () => { setup(() => { - deps.setup(d => d.sep) // The value really doesn't matter. + deps.setup((d) => d.sep) // The value really doesn't matter. .returns(() => '/'); }); test('is not readonly', async () => { const dirname = 'x/y/z/spam'; const filename = `${dirname}/___vscpTest___`; - deps.setup(d => d.stat(dirname)) // Success! + deps.setup((d) => d.stat(dirname)) // Success! .returns(() => Promise.resolve((undefined as unknown) as FileStat)); - deps.setup(d => d.writeText(filename, '')) // Success! + deps.setup((d) => d.writeText(filename, '')) // Success! .returns(() => Promise.resolve()); - deps.setup(d => d.rmfile(filename)) // Success! + deps.setup((d) => d.rmfile(filename)) // Success! .returns(() => Promise.resolve()); const isReadonly = await utils.isDirReadonly(dirname); @@ -1371,9 +1371,9 @@ suite('FileSystemUtils', () => { const err = new Error('not permitted'); // tslint:disable-next-line:no-any (err as any).code = 'EACCES'; // errno - deps.setup(d => d.stat(dirname)) // Success! + deps.setup((d) => d.stat(dirname)) // Success! .returns(() => Promise.resolve((undefined as unknown) as FileStat)); - deps.setup(d => d.writeText(filename, '')) // not permitted + deps.setup((d) => d.writeText(filename, '')) // not permitted .returns(() => Promise.reject(err)); const isReadonly = await utils.isDirReadonly(dirname); @@ -1387,7 +1387,7 @@ suite('FileSystemUtils', () => { const err = new Error('not found'); // tslint:disable-next-line:no-any (err as any).code = 'ENOENT'; // errno - deps.setup(d => d.stat(dirname)) // file-not-found + deps.setup((d) => d.stat(dirname)) // file-not-found .returns(() => Promise.reject(err)); const promise = utils.isDirReadonly(dirname); @@ -1401,13 +1401,13 @@ suite('FileSystemUtils', () => { test('Getting hash for a file should return non-empty string', async () => { const filename = 'x/y/z/spam.py'; const stat = createMockStat(); - stat.setup(s => s.ctime) // created + stat.setup((s) => s.ctime) // created .returns(() => 100); - stat.setup(s => s.mtime) // modified + stat.setup((s) => s.mtime) // modified .returns(() => 120); - deps.setup(d => d.lstat(filename)) // file exists + deps.setup((d) => d.lstat(filename)) // file exists .returns(() => Promise.resolve(stat.object)); - deps.setup(d => d.getHash('100-120')) // built from ctime and mtime + deps.setup((d) => d.getHash('100-120')) // built from ctime and mtime .returns(() => 'deadbeef'); const hash = await utils.getFileHash(filename); @@ -1419,7 +1419,7 @@ suite('FileSystemUtils', () => { test('Getting hash for non existent file should throw error', async () => { const filename = 'x/y/z/spam.py'; const err = vscode.FileSystemError.FileNotFound(filename); - deps.setup(d => d.lstat(filename)) // file-not-found + deps.setup((d) => d.lstat(filename)) // file-not-found .returns(() => Promise.reject(err)); const promise = utils.getFileHash(filename); @@ -1440,7 +1440,7 @@ suite('FileSystemUtils', () => { 'x/y/z/spam.so', 'x/y/z/spam.data' ]; - deps.setup(d => d.globFile(pattern, undefined)) // found some + deps.setup((d) => d.globFile(pattern, undefined)) // found some .returns(() => Promise.resolve(expected)); const files = await utils.search(pattern); @@ -1460,7 +1460,7 @@ suite('FileSystemUtils', () => { 'x/y/z/spam.so', 'x/y/z/spam.data' ]; - deps.setup(d => d.globFile(pattern, { cwd: cwd })) // found some + deps.setup((d) => d.globFile(pattern, { cwd: cwd })) // found some .returns(() => Promise.resolve(expected)); const files = await utils.search(pattern, cwd); @@ -1471,7 +1471,7 @@ suite('FileSystemUtils', () => { test('no matches (empty)', async () => { const pattern = `x/y/z/spam.*`; - deps.setup(d => d.globFile(pattern, undefined)) // found none + deps.setup((d) => d.globFile(pattern, undefined)) // found none .returns(() => Promise.resolve([])); const files = await utils.search(pattern); @@ -1482,7 +1482,7 @@ suite('FileSystemUtils', () => { test('no matches (undefined)', async () => { const pattern = `x/y/z/spam.*`; - deps.setup(d => d.globFile(pattern, undefined)) // found none + deps.setup((d) => d.globFile(pattern, undefined)) // found none .returns(() => Promise.resolve((undefined as unknown) as string[])); const files = await utils.search(pattern); @@ -1495,7 +1495,7 @@ suite('FileSystemUtils', () => { suite('fileExistsSync', () => { test('file exists', async () => { const filename = 'x/y/z/spam.py'; - deps.setup(d => d.statSync(filename)) // The file exists. + deps.setup((d) => d.statSync(filename)) // The file exists. .returns(() => (undefined as unknown) as FileStat); const exists = utils.fileExistsSync(filename); @@ -1507,7 +1507,7 @@ suite('FileSystemUtils', () => { test('file does not exist', async () => { const filename = 'x/y/z/spam.py'; const err = vscode.FileSystemError.FileNotFound('...'); - deps.setup(d => d.statSync(filename)) // The file does not exist. + deps.setup((d) => d.statSync(filename)) // The file does not exist. .throws(err); const exists = utils.fileExistsSync(filename); @@ -1519,7 +1519,7 @@ suite('FileSystemUtils', () => { test('fails if low-level call fails', async () => { const filename = 'x/y/z/spam.py'; const err = new Error('oops!'); - deps.setup(d => d.statSync(filename)) // big badda boom + deps.setup((d) => d.statSync(filename)) // big badda boom .throws(err); expect(() => utils.fileExistsSync(filename)).to.throw(err); diff --git a/src/test/common/platform/fs-paths.unit.test.ts b/src/test/common/platform/fs-paths.unit.test.ts index ee020f950aa4..bfd588efdd49 100644 --- a/src/test/common/platform/fs-paths.unit.test.ts +++ b/src/test/common/platform/fs-paths.unit.test.ts @@ -46,7 +46,7 @@ suite('FileSystem - Path Utils', () => { const caseInsensitive = [OSType.Windows]; suite('arePathsSame', () => { - getNamesAndValues(OSType).forEach(item => { + getNamesAndValues(OSType).forEach((item) => { const osType = item.value; function setNormCase(filename: string, numCalls = 1): string { @@ -54,7 +54,7 @@ suite('FileSystem - Path Utils', () => { if (osType === OSType.Windows) { norm = path.normalize(filename).toUpperCase(); } - deps.setup(d => d.normCase(filename)) + deps.setup((d) => d.normCase(filename)) .returns(() => norm) .verifiable(TypeMoq.Times.exactly(numCalls)); return filename; @@ -65,7 +65,7 @@ suite('FileSystem - Path Utils', () => { 'c:\\users\\peter smith\\my documents\\test.txt', // some upper-case 'c:\\USERS\\Peter Smith\\my documents\\test.TXT' - ].forEach(path1 => { + ].forEach((path1) => { test(`True if paths are identical (type: ${item.name}) - ${path1}`, () => { path1 = setNormCase(path1, 2); diff --git a/src/test/common/platform/fs-temp.functional.test.ts b/src/test/common/platform/fs-temp.functional.test.ts index bd1fa89b3f38..226f2f734814 100644 --- a/src/test/common/platform/fs-temp.functional.test.ts +++ b/src/test/common/platform/fs-temp.functional.test.ts @@ -54,7 +54,7 @@ suite('FileSystem - TemporaryFileSystem', () => { expect(filename1).to.not.equal(filename2); }); - test('Ensure writing to a temp file is supported via file stream', async function() { + test('Ensure writing to a temp file is supported via file stream', async function () { if (WINDOWS) { // tslint:disable-next-line:no-invalid-this this.skip(); diff --git a/src/test/common/platform/fs-temp.unit.test.ts b/src/test/common/platform/fs-temp.unit.test.ts index 636ee9cc6abe..1d3ea1cd308d 100644 --- a/src/test/common/platform/fs-temp.unit.test.ts +++ b/src/test/common/platform/fs-temp.unit.test.ts @@ -30,7 +30,7 @@ suite('FileSystem - temp files', () => { suite('createFile', () => { test(`fails if the raw call fails`, async () => { const failure = new Error('oops'); - deps.setup(d => d.file({ postfix: '.tmp' }, TypeMoq.It.isAny())) + deps.setup((d) => d.file({ postfix: '.tmp' }, TypeMoq.It.isAny())) // fail with an arbitrary error .throws(failure); @@ -42,7 +42,7 @@ suite('FileSystem - temp files', () => { test(`fails if the raw call "returns" an error`, async () => { const failure = new Error('oops'); - deps.setup(d => d.file({ postfix: '.tmp' }, TypeMoq.It.isAny())) + deps.setup((d) => d.file({ postfix: '.tmp' }, TypeMoq.It.isAny())) // tslint:disable-next-line:no-empty .callback((_cfg, cb) => cb(failure, '...', -1, () => {})); diff --git a/src/test/common/platform/platformService.test.ts b/src/test/common/platform/platformService.test.ts index aa9920ad4385..0f803e38095e 100644 --- a/src/test/common/platform/platformService.test.ts +++ b/src/test/common/platform/platformService.test.ts @@ -77,7 +77,7 @@ suite('PlatformService', () => { ); }); - test('getVersion on Mac/Windows', async function() { + test('getVersion on Mac/Windows', async function () { if (osType === OSType.Linux) { // tslint:disable-next-line:no-invalid-this return this.skip(); @@ -88,7 +88,7 @@ suite('PlatformService', () => { expect(result.compare(expectedVersion)).to.be.equal(0, 'invalid value'); }); - test('getVersion on Linux shoud throw an exception', async function() { + test('getVersion on Linux shoud throw an exception', async function () { if (osType !== OSType.Linux) { // tslint:disable-next-line:no-invalid-this return this.skip(); diff --git a/src/test/common/platform/serviceRegistry.unit.test.ts b/src/test/common/platform/serviceRegistry.unit.test.ts index 5faed42c08b6..109a633e0489 100644 --- a/src/test/common/platform/serviceRegistry.unit.test.ts +++ b/src/test/common/platform/serviceRegistry.unit.test.ts @@ -1,28 +1,28 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { instance, mock, verify } from 'ts-mockito'; -import { FileSystem } from '../../../client/common/platform/fileSystem'; -import { PlatformService } from '../../../client/common/platform/platformService'; -import { RegistryImplementation } from '../../../client/common/platform/registry'; -import { registerTypes } from '../../../client/common/platform/serviceRegistry'; -import { IFileSystem, IPlatformService, IRegistry } from '../../../client/common/platform/types'; -import { ServiceManager } from '../../../client/ioc/serviceManager'; -import { IServiceManager } from '../../../client/ioc/types'; - -suite('Common Platform Service Registry', () => { - let serviceManager: IServiceManager; - - setup(() => { - serviceManager = mock(ServiceManager); - }); - - test('Ensure services are registered', async () => { - registerTypes(instance(serviceManager)); - verify(serviceManager.addSingleton(IPlatformService, PlatformService)).once(); - verify(serviceManager.addSingleton(IFileSystem, FileSystem)).once(); - verify(serviceManager.addSingleton(IRegistry, RegistryImplementation)).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { instance, mock, verify } from 'ts-mockito'; +import { FileSystem } from '../../../client/common/platform/fileSystem'; +import { PlatformService } from '../../../client/common/platform/platformService'; +import { RegistryImplementation } from '../../../client/common/platform/registry'; +import { registerTypes } from '../../../client/common/platform/serviceRegistry'; +import { IFileSystem, IPlatformService, IRegistry } from '../../../client/common/platform/types'; +import { ServiceManager } from '../../../client/ioc/serviceManager'; +import { IServiceManager } from '../../../client/ioc/types'; + +suite('Common Platform Service Registry', () => { + let serviceManager: IServiceManager; + + setup(() => { + serviceManager = mock(ServiceManager); + }); + + test('Ensure services are registered', async () => { + registerTypes(instance(serviceManager)); + verify(serviceManager.addSingleton(IPlatformService, PlatformService)).once(); + verify(serviceManager.addSingleton(IFileSystem, FileSystem)).once(); + verify(serviceManager.addSingleton(IRegistry, RegistryImplementation)).once(); + }); +}); diff --git a/src/test/common/platform/utils.ts b/src/test/common/platform/utils.ts index 0d4fc4b7f0c3..0a3e28e2895b 100644 --- a/src/test/common/platform/utils.ts +++ b/src/test/common/platform/utils.ts @@ -169,7 +169,7 @@ export class FSFixture extends CleanupFixture { public async createSocket(relname: string): Promise { const srv = this.ensureSocketServer(); const filename = await this.resolve(relname); - await new Promise(resolve => srv!.listen(filename, 0, resolve)); + await new Promise((resolve) => srv!.listen(filename, 0, resolve)); return filename; } @@ -242,7 +242,7 @@ export class FSFixture extends CleanupFixture { this.sockServer = srv; this.addCleanup(async () => { try { - await new Promise(resolve => srv.close(resolve)); + await new Promise((resolve) => srv.close(resolve)); } catch (err) { console.log(`failure while closing socket server: ${err}`); } diff --git a/src/test/common/process/condaExecutionService.unit.test.ts b/src/test/common/process/condaExecutionService.unit.test.ts index 9a20b2cd027a..91d5e9f1c7cc 100644 --- a/src/test/common/process/condaExecutionService.unit.test.ts +++ b/src/test/common/process/condaExecutionService.unit.test.ts @@ -21,10 +21,10 @@ suite('CondaExecutionService', () => { serviceContainer = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); fileSystem = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - serviceContainer.setup(s => s.get(IFileSystem)).returns(() => fileSystem.object); + serviceContainer.setup((s) => s.get(IFileSystem)).returns(() => fileSystem.object); }); - test('getExecutionInfo with a named environment should return execution info using the environment name', function() { + test('getExecutionInfo with a named environment should return execution info using the environment name', function () { // tslint:disable-next-line:no-invalid-this return this.skip(); @@ -42,7 +42,7 @@ suite('CondaExecutionService', () => { expect(result).to.deep.equal({ command: condaFile, args: ['run', '-n', environment.name, 'python', ...args] }); }); - test('getExecutionInfo with a non-named environment should return execution info using the environment path', async function() { + test('getExecutionInfo with a non-named environment should return execution info using the environment path', async function () { // tslint:disable-next-line:no-invalid-this return this.skip(); diff --git a/src/test/common/process/decoder.test.ts b/src/test/common/process/decoder.test.ts index f1135dec6642..92d43f8aadf4 100644 --- a/src/test/common/process/decoder.test.ts +++ b/src/test/common/process/decoder.test.ts @@ -18,7 +18,7 @@ suite('Decoder', () => { expect(decodedValue).equal(value, 'Decoded string is incorrect'); }); - test('Test decoding cp932 strings', function() { + test('Test decoding cp932 strings', function () { if (!encodingExists('cp866')) { // tslint:disable-next-line:no-invalid-this this.skip(); diff --git a/src/test/common/process/execFactory.test.ts b/src/test/common/process/execFactory.test.ts index 3d925164c673..d1d0400c55c3 100644 --- a/src/test/common/process/execFactory.test.ts +++ b/src/test/common/process/execFactory.test.ts @@ -26,43 +26,47 @@ suite('PythonExecutableService', () => { procService = TypeMoq.Mock.ofType(); configService = TypeMoq.Mock.ofType(); const fileSystem = TypeMoq.Mock.ofType(); - fileSystem.setup(f => f.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); + fileSystem.setup((f) => f.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider))) + .setup((c) => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider))) .returns(() => envVarsProvider.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(IProcessServiceFactory))) .returns(() => procServiceFactory.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); procService.setup((x: any) => x.then).returns(() => undefined); - procServiceFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(procService.object)); - envVarsProvider.setup(v => v.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({})); + procServiceFactory + .setup((p) => p.create(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(procService.object)); + envVarsProvider.setup((v) => v.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({})); const envActivationService = TypeMoq.Mock.ofType(); envActivationService - .setup(e => e.getActivatedEnvironmentVariables(TypeMoq.It.isAny())) + .setup((e) => e.getActivatedEnvironmentVariables(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); envActivationService - .setup(e => e.getActivatedEnvironmentVariables(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((e) => e.getActivatedEnvironmentVariables(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); envActivationService - .setup(e => e.getActivatedEnvironmentVariables(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((e) => + e.getActivatedEnvironmentVariables(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) + ) .returns(() => Promise.resolve(undefined)); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IEnvironmentActivationService), TypeMoq.It.isAny())) + .setup((s) => s.get(TypeMoq.It.isValue(IEnvironmentActivationService), TypeMoq.It.isAny())) .returns(() => envActivationService.object); }); test('Ensure resource is used when getting configuration service settings (undefined resource)', async () => { const pythonPath = `Python_Path_${new Date().toString()}`; const pythonVersion = `Python_Version_${new Date().toString()}`; const pythonSettings = TypeMoq.Mock.ofType(); - pythonSettings.setup(p => p.pythonPath).returns(() => pythonPath); - configService.setup(c => c.getSettings(TypeMoq.It.isValue(undefined))).returns(() => pythonSettings.object); + pythonSettings.setup((p) => p.pythonPath).returns(() => pythonPath); + configService.setup((c) => c.getSettings(TypeMoq.It.isValue(undefined))).returns(() => pythonSettings.object); procService - .setup(p => p.exec(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: pythonVersion })); const versionService = new InterpreterVersionService(procServiceFactory.object); @@ -75,10 +79,10 @@ suite('PythonExecutableService', () => { const pythonPath = `Python_Path_${new Date().toString()}`; const pythonVersion = `Python_Version_${new Date().toString()}`; const pythonSettings = TypeMoq.Mock.ofType(); - pythonSettings.setup(p => p.pythonPath).returns(() => pythonPath); - configService.setup(c => c.getSettings(TypeMoq.It.isValue(resource))).returns(() => pythonSettings.object); + pythonSettings.setup((p) => p.pythonPath).returns(() => pythonPath); + configService.setup((c) => c.getSettings(TypeMoq.It.isValue(resource))).returns(() => pythonSettings.object); procService - .setup(p => p.exec(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: pythonVersion })); const versionService = new InterpreterVersionService(procServiceFactory.object); diff --git a/src/test/common/process/logger.unit.test.ts b/src/test/common/process/logger.unit.test.ts index 4d2e80e30532..d6c0706c57c7 100644 --- a/src/test/common/process/logger.unit.test.ts +++ b/src/test/common/process/logger.unit.test.ts @@ -29,7 +29,7 @@ suite('ProcessLogger suite', () => { setup(() => { outputResult = ''; outputChannel - .setup(o => o.appendLine(TypeMoq.It.isAnyString())) + .setup((o) => o.appendLine(TypeMoq.It.isAnyString())) .returns((s: string) => (outputResult += `${s}\n`)); }); @@ -45,7 +45,7 @@ suite('ProcessLogger suite', () => { const expectedResult = `> test --foo --bar\n${Logging.currentWorkingDirectory()} ${options.cwd}\n`; expect(outputResult).to.equal(expectedResult, 'Output string is incorrect - String built incorrectly'); - outputChannel.verify(o => o.appendLine(TypeMoq.It.isAnyString()), TypeMoq.Times.exactly(2)); + outputChannel.verify((o) => o.appendLine(TypeMoq.It.isAnyString()), TypeMoq.Times.exactly(2)); }); test('Logger adds quotes around arguments if they contain spaces', async () => { diff --git a/src/test/common/process/proc.exec.test.ts b/src/test/common/process/proc.exec.test.ts index 59a8948efd7a..816a6db55ac4 100644 --- a/src/test/common/process/proc.exec.test.ts +++ b/src/test/common/process/proc.exec.test.ts @@ -35,7 +35,7 @@ suite('ProcessService Observable', () => { expect(result.stderr).to.equal(undefined, 'stderr not undefined'); }); - test('exec should output print unicode characters', async function() { + test('exec should output print unicode characters', async function () { // This test has not been working for many months in Python 2.7 under // Windows. Tracked by #2546. (unicode under Py2.7 is tough!) if (isOs(OSType.Windows) && (await isPythonVersion('2.7'))) { @@ -52,7 +52,7 @@ suite('ProcessService Observable', () => { expect(result.stderr).to.equal(undefined, 'stderr not undefined'); }); - test('exec should wait for completion of program with new lines', async function() { + test('exec should wait for completion of program with new lines', async function () { // tslint:disable-next-line:no-invalid-this this.timeout(5000); const procService = new ProcessService(new BufferDecoder()); @@ -73,13 +73,13 @@ suite('ProcessService Observable', () => { expect(result).not.to.be.an('undefined', 'result is undefined'); const values = result.stdout .split(/\r?\n/g) - .map(line => line.trim()) - .filter(line => line.length > 0); + .map((line) => line.trim()) + .filter((line) => line.length > 0); expect(values).to.deep.equal(outputs, 'Output values are incorrect'); expect(result.stderr).to.equal(undefined, 'stderr not undefined'); }); - test('exec should wait for completion of program without new lines', async function() { + test('exec should wait for completion of program without new lines', async function () { // tslint:disable-next-line:no-invalid-this this.timeout(5000); const procService = new ProcessService(new BufferDecoder()); @@ -100,13 +100,13 @@ suite('ProcessService Observable', () => { expect(result).not.to.be.an('undefined', 'result is undefined'); const values = result.stdout .split(/\r?\n/g) - .map(line => line.trim()) - .filter(line => line.length > 0); + .map((line) => line.trim()) + .filter((line) => line.length > 0); expect(values).to.deep.equal(outputs, 'Output values are incorrect'); expect(result.stderr).to.equal(undefined, 'stderr not undefined'); }); - test('exec should end when cancellationToken is cancelled', async function() { + test('exec should end when cancellationToken is cancelled', async function () { // tslint:disable-next-line:no-invalid-this this.timeout(15000); const procService = new ProcessService(new BufferDecoder()); @@ -129,13 +129,13 @@ suite('ProcessService Observable', () => { expect(result).not.to.be.an('undefined', 'result is undefined'); const values = result.stdout .split(/\r?\n/g) - .map(line => line.trim()) - .filter(line => line.length > 0); + .map((line) => line.trim()) + .filter((line) => line.length > 0); expect(values).to.deep.equal(['1'], 'Output values are incorrect'); expect(result.stderr).to.equal(undefined, 'stderr not undefined'); }); - test('exec should stream stdout and stderr separately', async function() { + test('exec should stream stdout and stderr separately', async function () { // tslint:disable-next-line:no-invalid-this this.timeout(7000); const procService = new ProcessService(new BufferDecoder()); @@ -167,17 +167,17 @@ suite('ProcessService Observable', () => { expect(result).not.to.be.an('undefined', 'result is undefined'); const stdouts = result.stdout .split(/\r?\n/g) - .map(line => line.trim()) - .filter(line => line.length > 0); + .map((line) => line.trim()) + .filter((line) => line.length > 0); expect(stdouts).to.deep.equal(expectedStdout, 'stdout values are incorrect'); const stderrs = result .stderr!.split(/\r?\n/g) - .map(line => line.trim()) - .filter(line => line.length > 0); + .map((line) => line.trim()) + .filter((line) => line.length > 0); expect(stderrs).to.deep.equal(expectedStderr, 'stderr values are incorrect'); }); - test('exec should merge stdout and stderr streams', async function() { + test('exec should merge stdout and stderr streams', async function () { // tslint:disable-next-line:no-invalid-this this.timeout(7000); const procService = new ProcessService(new BufferDecoder()); @@ -208,8 +208,8 @@ suite('ProcessService Observable', () => { expect(result).not.to.be.an('undefined', 'result is undefined'); const outputs = result.stdout .split(/\r?\n/g) - .map(line => line.trim()) - .filter(line => line.length > 0); + .map((line) => line.trim()) + .filter((line) => line.length > 0); expect(outputs).to.deep.equal(expectedOutput, 'Output values are incorrect'); }); diff --git a/src/test/common/process/proc.observable.test.ts b/src/test/common/process/proc.observable.test.ts index acaf6a624cba..8ebfdec3148b 100644 --- a/src/test/common/process/proc.observable.test.ts +++ b/src/test/common/process/proc.observable.test.ts @@ -22,7 +22,7 @@ suite('ProcessService', () => { setup(initialize); teardown(initialize); - test('execObservable should stream output with new lines', function(done) { + test('execObservable should stream output with new lines', function (done) { // tslint:disable-next-line:no-invalid-this this.timeout(10000); const procService = new ProcessService(new BufferDecoder()); @@ -44,7 +44,7 @@ suite('ProcessService', () => { expect(result).not.to.be.an('undefined', 'result is undefined'); result.out.subscribe( - output => { + (output) => { // Ignore line breaks. if (output.out.trim().length === 0) { return; @@ -62,7 +62,7 @@ suite('ProcessService', () => { ); }); - test('execObservable should stream output without new lines', function(done) { + test('execObservable should stream output without new lines', function (done) { // Skipping to get nightly build to pass. Opened this issue: // https://github.com/microsoft/vscode-python/issues/7411 // tslint:disable-next-line: no-invalid-this @@ -89,7 +89,7 @@ suite('ProcessService', () => { expect(result).not.to.be.an('undefined', 'result is undefined'); result.out.subscribe( - output => { + (output) => { // Ignore line breaks. if (output.out.trim().length === 0) { return; @@ -107,7 +107,7 @@ suite('ProcessService', () => { ); }); - test('execObservable should end when cancellationToken is cancelled', function(done) { + test('execObservable should end when cancellationToken is cancelled', function (done) { // tslint:disable-next-line:no-invalid-this this.timeout(15000); const procService = new ProcessService(new BufferDecoder()); @@ -130,7 +130,7 @@ suite('ProcessService', () => { def.promise.then(done).catch(done); expect(result).not.to.be.an('undefined', 'result is undefined'); result.out.subscribe( - output => { + (output) => { const value = output.out.trim(); if (value === '1') { cancellationToken.cancel(); @@ -154,7 +154,7 @@ suite('ProcessService', () => { ); }); - test('execObservable should end when process is killed', function(done) { + test('execObservable should end when process is killed', function (done) { // tslint:disable-next-line:no-invalid-this this.timeout(15000); const procService = new ProcessService(new BufferDecoder()); @@ -176,7 +176,7 @@ suite('ProcessService', () => { expect(result).not.to.be.an('undefined', 'result is undefined'); result.out.subscribe( - output => { + (output) => { const value = output.out.trim(); // Ignore line breaks. if (value.length === 0) { @@ -199,7 +199,7 @@ suite('ProcessService', () => { ); }); - test('execObservable should stream stdout and stderr separately', function(done) { + test('execObservable should stream stdout and stderr separately', function (done) { // tslint:disable-next-line:no-invalid-this this.timeout(20000); const procService = new ProcessService(new BufferDecoder()); @@ -237,7 +237,7 @@ suite('ProcessService', () => { expect(result).not.to.be.an('undefined', 'result is undefined'); result.out.subscribe( - output => { + (output) => { const value = output.out.trim(); // Ignore line breaks. if (value.length === 0) { @@ -253,7 +253,7 @@ suite('ProcessService', () => { ); }); - test('execObservable should send stdout and stderr streams separately', async function() { + test('execObservable should send stdout and stderr streams separately', async function () { // This test is failing on Windows. Tracked by GH #4755. if (isOs(OSType.Windows)) { // tslint:disable-next-line:no-invalid-this @@ -261,14 +261,14 @@ suite('ProcessService', () => { } }); - test('execObservable should throw an error with stderr output', done => { + test('execObservable should throw an error with stderr output', (done) => { const procService = new ProcessService(new BufferDecoder()); const pythonCode = ['import sys', 'sys.stderr.write("a")', 'sys.stderr.flush()']; const result = procService.execObservable(pythonPath, ['-c', pythonCode.join(';')], { throwOnStdErr: true }); expect(result).not.to.be.an('undefined', 'result is undefined.'); result.out.subscribe( - _output => { + (_output) => { done("Output received, when we're expecting an error to be thrown."); }, (ex: Error) => { @@ -281,16 +281,16 @@ suite('ProcessService', () => { ); }); - test('execObservable should throw an error when spawn file not found', done => { + test('execObservable should throw an error when spawn file not found', (done) => { const procService = new ProcessService(new BufferDecoder()); const result = procService.execObservable(Date.now().toString(), []); expect(result).not.to.be.an('undefined', 'result is undefined.'); result.out.subscribe( - _output => { + (_output) => { done("Output received, when we're expecting an error to be thrown."); }, - ex => { + (ex) => { expect(ex).to.have.property('code', 'ENOENT', 'Invalid error code'); done(); }, @@ -300,13 +300,13 @@ suite('ProcessService', () => { ); }); - test('execObservable should exit without no output', done => { + test('execObservable should exit without no output', (done) => { const procService = new ProcessService(new BufferDecoder()); const result = procService.execObservable(pythonPath, ['-c', 'import sys', 'sys.exit()']); expect(result).not.to.be.an('undefined', 'result is undefined.'); result.out.subscribe( - output => { + (output) => { done(`Output received, when we\'re not expecting any, ${JSON.stringify(output)}`); }, done, diff --git a/src/test/common/process/proc.unit.test.ts b/src/test/common/process/proc.unit.test.ts index a71f797e85ac..0f1f8fa1dc3a 100644 --- a/src/test/common/process/proc.unit.test.ts +++ b/src/test/common/process/proc.unit.test.ts @@ -16,12 +16,12 @@ interface IProcData { exited: Deferred; } -suite('Process - Process Service', function() { +suite('Process - Process Service', function () { // tslint:disable-next-line:no-invalid-this this.timeout(5000); const procsToKill: IProcData[] = []; teardown(() => { - procsToKill.forEach(p => { + procsToKill.forEach((p) => { if (!p.exited.resolved) { p.proc.kill(); } diff --git a/src/test/common/process/processFactory.unit.test.ts b/src/test/common/process/processFactory.unit.test.ts index c0140ecb781c..5c124ebab808 100644 --- a/src/test/common/process/processFactory.unit.test.ts +++ b/src/test/common/process/processFactory.unit.test.ts @@ -43,10 +43,10 @@ suite('Process - ProcessServiceFactory', () => { }); teardown(() => { - (disposableRegistry as Disposable[]).forEach(d => d.dispose()); + (disposableRegistry as Disposable[]).forEach((d) => d.dispose()); }); - [Uri.parse('test'), undefined].forEach(resource => { + [Uri.parse('test'), undefined].forEach((resource) => { test(`Ensure ProcessService is created with an ${resource ? 'existing' : 'undefined'} resource`, async () => { when(envVariablesProvider.getEnvironmentVariables(resource)).thenResolve({ x: 'test' }); diff --git a/src/test/common/process/pythonDaemon.functional.test.ts b/src/test/common/process/pythonDaemon.functional.test.ts index 8f81f83d7452..63761460f883 100644 --- a/src/test/common/process/pythonDaemon.functional.test.ts +++ b/src/test/common/process/pythonDaemon.functional.test.ts @@ -53,7 +53,7 @@ suite('Daemon', () => { .trim(); } }); - setup(async function() { + setup(async function () { if (isPythonVersion('2.7')) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -80,7 +80,7 @@ suite('Daemon', () => { connection.dispose(); } pythonDaemon.dispose(); - disposables.forEach(item => item.dispose()); + disposables.forEach((item) => item.dispose()); disposables = []; }); @@ -265,10 +265,10 @@ suite('Daemon', () => { const output = pythonDaemon.execObservable([fileToExecute], {}); const outputsReceived: string[] = []; await new Promise((resolve, reject) => { - output.out.subscribe(out => outputsReceived.push(out.out.trim()), reject, resolve); + output.out.subscribe((out) => outputsReceived.push(out.out.trim()), reject, resolve); }); assert.deepEqual( - outputsReceived.filter(item => item.length > 0), + outputsReceived.filter((item) => item.length > 0), ['0', '1', '2', '3', '4'] ); }).timeout(10_000); @@ -292,7 +292,7 @@ suite('Daemon', () => { const output = pythonDaemon.execObservable([fileToExecute], { throwOnStdErr: true }); const outputsReceived: string[] = []; const promise = new Promise((resolve, reject) => { - output.out.subscribe(out => outputsReceived.push(out.out.trim()), reject, resolve); + output.out.subscribe((out) => outputsReceived.push(out.out.trim()), reject, resolve); }); await expect(promise).to.eventually.be.rejectedWith('KABOOM'); }).timeout(3_000); diff --git a/src/test/common/process/pythonDaemonPool.functional.test.ts b/src/test/common/process/pythonDaemonPool.functional.test.ts index e8ad27f42a30..b9fab3746b18 100644 --- a/src/test/common/process/pythonDaemonPool.functional.test.ts +++ b/src/test/common/process/pythonDaemonPool.functional.test.ts @@ -67,7 +67,7 @@ suite('Daemon - Python Daemon Pool', () => { .trim(); } }); - setup(async function() { + setup(async function () { if (isPythonVersion('2.7')) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -101,14 +101,14 @@ suite('Daemon - Python Daemon Pool', () => { }); teardown(() => { sinon.restore(); - disposables.forEach(item => item.dispose()); + disposables.forEach((item) => item.dispose()); disposables = []; }); async function getStdOutFromObservable(output: ObservableExecutionResult) { return new Promise((resolve, reject) => { const data: string[] = []; output.out.subscribe( - out => data.push(out.out.trim()), + (out) => data.push(out.out.trim()), reject, () => resolve(data.join('')) ); @@ -282,10 +282,10 @@ suite('Daemon - Python Daemon Pool', () => { const output = pythonDaemonPool.execObservable([fileToExecute], {}); const outputsReceived: string[] = []; await new Promise((resolve, reject) => { - output.out.subscribe(out => outputsReceived.push(out.out.trim()), reject, resolve); + output.out.subscribe((out) => outputsReceived.push(out.out.trim()), reject, resolve); }); assert.deepEqual( - outputsReceived.filter(item => item.length > 0), + outputsReceived.filter((item) => item.length > 0), ['0', '1', '2', '3', '4'] ); }).timeout(5_000); @@ -309,7 +309,7 @@ suite('Daemon - Python Daemon Pool', () => { const output = pythonDaemonPool.execObservable([fileToExecute], { throwOnStdErr: true }); const outputsReceived: string[] = []; const promise = new Promise((resolve, reject) => { - output.out.subscribe(out => outputsReceived.push(out.out.trim()), reject, resolve); + output.out.subscribe((out) => outputsReceived.push(out.out.trim()), reject, resolve); }); await expect(promise).to.eventually.be.rejectedWith('KABOOM'); }).timeout(5_000); @@ -323,7 +323,7 @@ suite('Daemon - Python Daemon Pool', () => { const fileToExecute = await createPythonFile(source); // When using the python execution service, return a bogus value. when(pythonExecutionService.execObservable(deepEqual([fileToExecute]), anything())).thenCall(() => { - const observable = new Observable>(s => { + const observable = new Observable>((s) => { s.next({ out: 'mypid', source: 'stdout' }); s.complete(); }); @@ -399,8 +399,8 @@ suite('Daemon - Python Daemon Pool', () => { const fileToExecute1 = await createPythonFile(source1); let [pid1, pid2] = await Promise.all([ - pythonDaemonPool.exec([fileToExecute1], {}).then(out => out.stdout.trim()), - pythonDaemonPool.exec([fileToExecute1], {}).then(out => out.stdout.trim()) + pythonDaemonPool.exec([fileToExecute1], {}).then((out) => out.stdout.trim()), + pythonDaemonPool.exec([fileToExecute1], {}).then((out) => out.stdout.trim()) ]); const processesUsedToRunCode = new Set(); @@ -422,11 +422,11 @@ suite('Daemon - Python Daemon Pool', () => { [pid1, pid2] = await Promise.all([ pythonDaemonPool .exec([fileToExecute1], {}) - .then(out => out.stdout.trim()) + .then((out) => out.stdout.trim()) .catch(() => 'FAILED'), pythonDaemonPool .exec([fileToExecute2], {}) - .then(out => out.stdout.trim()) + .then((out) => out.stdout.trim()) .catch(() => 'FAILED') ]); @@ -447,8 +447,8 @@ suite('Daemon - Python Daemon Pool', () => { // Confirm we have two daemons by checking the Pids again. // One of them will be new. [pid1, pid2] = await Promise.all([ - pythonDaemonPool.exec([fileToExecute1], {}).then(out => out.stdout.trim()), - pythonDaemonPool.exec([fileToExecute1], {}).then(out => out.stdout.trim()) + pythonDaemonPool.exec([fileToExecute1], {}).then((out) => out.stdout.trim()), + pythonDaemonPool.exec([fileToExecute1], {}).then((out) => out.stdout.trim()) ]); // Keep track of the pids. diff --git a/src/test/common/process/pythonDaemonPool.unit.test.ts b/src/test/common/process/pythonDaemonPool.unit.test.ts index 4264220ed352..b7a5d0c8449d 100644 --- a/src/test/common/process/pythonDaemonPool.unit.test.ts +++ b/src/test/common/process/pythonDaemonPool.unit.test.ts @@ -206,7 +206,7 @@ suite('Daemon - Python Daemon Pool', () => { }).timeout(3_000); test('If executing python is fast, then use the daemon (for observables)', async () => { const execModuleObservable = sinon.stub(PythonDaemonExecutionService.prototype, 'execModuleObservable'); - const out = new Observable>(s => { + const out = new Observable>((s) => { s.next({ source: 'stdout', out: '' }); s.complete(); }); diff --git a/src/test/common/process/pythonExecutionFactory.unit.test.ts b/src/test/common/process/pythonExecutionFactory.unit.test.ts index 090d67cd0bfa..9c23db043f46 100644 --- a/src/test/common/process/pythonExecutionFactory.unit.test.ts +++ b/src/test/common/process/pythonExecutionFactory.unit.test.ts @@ -79,7 +79,7 @@ suite('Process - PythonExecutionFactory', () => { { resource: undefined, interpreter: pythonInterpreter }, { resource: Uri.parse('x'), interpreter: undefined }, { resource: Uri.parse('x'), interpreter: pythonInterpreter } - ].forEach(item => { + ].forEach((item) => { const resource = item.resource; const interpreter = item.interpreter; suite(title(resource, interpreter), () => { @@ -107,7 +107,7 @@ suite('Process - PythonExecutionFactory', () => { when(processLogger.logProcess('', [], {})).thenReturn(); processService = typemoq.Mock.ofType(); processService - .setup(p => + .setup((p) => p.on('exec', () => { return; }) @@ -227,7 +227,7 @@ suite('Process - PythonExecutionFactory', () => { expect(service).instanceOf(WindowsStorePythonProcess); }); - test('Ensure `create` returns a CondaExecutionService instance if createCondaExecutionService() returns a valid object', async function() { + test('Ensure `create` returns a CondaExecutionService instance if createCondaExecutionService() returns a valid object', async function () { // tslint:disable-next-line:no-invalid-this return this.skip(); @@ -255,7 +255,7 @@ suite('Process - PythonExecutionFactory', () => { expect(service).instanceOf(CondaExecutionService); }); - test('Ensure `create` returns a PythonExecutionService instance if createCondaExecutionService() returns undefined', async function() { + test('Ensure `create` returns a PythonExecutionService instance if createCondaExecutionService() returns undefined', async function () { // tslint:disable-next-line:no-invalid-this return this.skip(); @@ -277,7 +277,7 @@ suite('Process - PythonExecutionFactory', () => { expect(service).instanceOf(PythonExecutionService); }); - test('Ensure `createActivatedEnvironment` returns a CondaExecutionService instance if createCondaExecutionService() returns a valid object', async function() { + test('Ensure `createActivatedEnvironment` returns a CondaExecutionService instance if createCondaExecutionService() returns a valid object', async function () { // tslint:disable-next-line:no-invalid-this return this.skip(); @@ -311,7 +311,7 @@ suite('Process - PythonExecutionFactory', () => { expect(service).instanceOf(CondaExecutionService); }); - test('Ensure `createActivatedEnvironment` returns a PythonExecutionService instance if createCondaExecutionService() returns undefined', async function() { + test('Ensure `createActivatedEnvironment` returns a PythonExecutionService instance if createCondaExecutionService() returns undefined', async function () { // tslint:disable-next-line:no-invalid-this return this.skip(); diff --git a/src/test/common/process/pythonProc.simple.multiroot.test.ts b/src/test/common/process/pythonProc.simple.multiroot.test.ts index b26bffe5a575..64eb1ad60222 100644 --- a/src/test/common/process/pythonProc.simple.multiroot.test.ts +++ b/src/test/common/process/pythonProc.simple.multiroot.test.ts @@ -76,7 +76,7 @@ suite('PythonExecutableService', () => { let configService: IConfigurationService; let pythonExecFactory: IPythonExecutionFactory; - suiteSetup(async function() { + suiteSetup(async function () { if (!IS_MULTI_ROOT_TEST) { // tslint:disable-next-line:no-invalid-this this.skip(); @@ -179,7 +179,7 @@ suite('PythonExecutableService', () => { await expect(promise).to.eventually.be.rejectedWith(StdErrError); }); - test('Importing with a valid PYTHONPATH from .env file should succeed', async function() { + test('Importing with a valid PYTHONPATH from .env file should succeed', async function () { // This test has not been working for many months in Python 2.7 under // Windows. Tracked by #2547. if (isOs(OSType.Windows) && (await isPythonVersion('2.7'))) { @@ -221,7 +221,7 @@ suite('PythonExecutableService', () => { if (await fs.pathExists(pythonPath)) { expectedExecutablePath = pythonPath; } else { - expectedExecutablePath = await new Promise(resolve => { + expectedExecutablePath = await new Promise((resolve) => { execFile(pythonPath, ['-c', 'import sys;print(sys.executable)'], (_error, stdout, _stdErr) => { resolve(stdout.trim()); }); diff --git a/src/test/common/process/pythonProcess.unit.test.ts b/src/test/common/process/pythonProcess.unit.test.ts index 9ee29b1d3041..5752677fcf99 100644 --- a/src/test/common/process/pythonProcess.unit.test.ts +++ b/src/test/common/process/pythonProcess.unit.test.ts @@ -26,7 +26,7 @@ suite('PythonExecutionService', () => { const serviceContainer = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); fileSystem = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - serviceContainer.setup(s => s.get(IFileSystem)).returns(() => fileSystem.object); + serviceContainer.setup((s) => s.get(IFileSystem)).returns(() => fileSystem.object); executionService = new PythonExecutionService(serviceContainer.object, processService.object, pythonPath); }); @@ -40,7 +40,7 @@ suite('PythonExecutionService', () => { }; processService - .setup(p => p.shellExec(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.shellExec(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: JSON.stringify(json) })); const result = await executionService.getInterpreterInformation(); @@ -64,7 +64,7 @@ suite('PythonExecutionService', () => { }; processService - .setup(p => p.shellExec(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.shellExec(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: JSON.stringify(json) })); const result = await executionService.getInterpreterInformation(); @@ -91,7 +91,7 @@ suite('PythonExecutionService', () => { }; processService - .setup(p => p.shellExec(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.shellExec(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: JSON.stringify(json) })); const result = await executionService.getInterpreterInformation(); @@ -111,7 +111,7 @@ suite('PythonExecutionService', () => { test('getInterpreterInformation should error out if interpreterInfo.py times out', async () => { processService - .setup(p => p.shellExec(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.shellExec(TypeMoq.It.isAny(), TypeMoq.It.isAny())) // tslint:disable-next-line: no-any .returns(() => Promise.resolve(undefined as any)); @@ -125,7 +125,7 @@ suite('PythonExecutionService', () => { test('getInterpreterInformation should return undefined if the json value returned by interpreterInfo.py is not valid', async () => { processService - .setup(p => p.shellExec(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.shellExec(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'bad json' })); const result = await executionService.getInterpreterInformation(); @@ -134,7 +134,7 @@ suite('PythonExecutionService', () => { }); test('getExecutablePath should return pythonPath if pythonPath is a file', async () => { - fileSystem.setup(f => f.fileExists(pythonPath)).returns(() => Promise.resolve(true)); + fileSystem.setup((f) => f.fileExists(pythonPath)).returns(() => Promise.resolve(true)); const result = await executionService.getExecutablePath(); @@ -143,9 +143,9 @@ suite('PythonExecutionService', () => { test('getExecutablePath should not return pythonPath if pythonPath is not a file', async () => { const executablePath = 'path/to/dummy/executable'; - fileSystem.setup(f => f.fileExists(pythonPath)).returns(() => Promise.resolve(false)); + fileSystem.setup((f) => f.fileExists(pythonPath)).returns(() => Promise.resolve(false)); processService - .setup(p => p.exec(pythonPath, ['-c', 'import sys;print(sys.executable)'], { throwOnStdErr: true })) + .setup((p) => p.exec(pythonPath, ['-c', 'import sys;print(sys.executable)'], { throwOnStdErr: true })) .returns(() => Promise.resolve({ stdout: executablePath })); const result = await executionService.getExecutablePath(); @@ -155,9 +155,9 @@ suite('PythonExecutionService', () => { test('getExecutablePath should throw if the result of exec() writes to stderr', async () => { const stderr = 'bar'; - fileSystem.setup(f => f.fileExists(pythonPath)).returns(() => Promise.resolve(false)); + fileSystem.setup((f) => f.fileExists(pythonPath)).returns(() => Promise.resolve(false)); processService - .setup(p => p.exec(pythonPath, ['-c', 'import sys;print(sys.executable)'], { throwOnStdErr: true })) + .setup((p) => p.exec(pythonPath, ['-c', 'import sys;print(sys.executable)'], { throwOnStdErr: true })) .returns(() => Promise.reject(new StdErrError(stderr))); const result = executionService.getExecutablePath(); @@ -168,13 +168,13 @@ suite('PythonExecutionService', () => { test('isModuleInstalled should call processService.exec()', async () => { const moduleName = 'foo'; processService - .setup(p => p.exec(pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true })) + .setup((p) => p.exec(pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true })) .returns(() => Promise.resolve({ stdout: '' })); await executionService.isModuleInstalled(moduleName); processService.verify( - async p => p.exec(pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true }), + async (p) => p.exec(pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true }), TypeMoq.Times.once() ); }); @@ -182,7 +182,7 @@ suite('PythonExecutionService', () => { test('isModuleInstalled should return true when processService.exec() succeeds', async () => { const moduleName = 'foo'; processService - .setup(p => p.exec(pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true })) + .setup((p) => p.exec(pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true })) .returns(() => Promise.resolve({ stdout: '' })); const result = await executionService.isModuleInstalled(moduleName); @@ -193,7 +193,7 @@ suite('PythonExecutionService', () => { test('isModuleInstalled should return false when processService.exec() throws', async () => { const moduleName = 'foo'; processService - .setup(p => p.exec(pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true })) + .setup((p) => p.exec(pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true })) .returns(() => Promise.reject(new StdErrError('bar'))); const result = await executionService.isModuleInstalled(moduleName); @@ -212,11 +212,11 @@ suite('PythonExecutionService', () => { noop(); } }; - processService.setup(p => p.execObservable(pythonPath, args, options)).returns(() => observable); + processService.setup((p) => p.execObservable(pythonPath, args, options)).returns(() => observable); const result = executionService.execObservable(args, options); - processService.verify(p => p.execObservable(pythonPath, args, options), TypeMoq.Times.once()); + processService.verify((p) => p.execObservable(pythonPath, args, options), TypeMoq.Times.once()); expect(result).to.be.equal(observable, 'execObservable should return an observable'); }); @@ -233,11 +233,11 @@ suite('PythonExecutionService', () => { noop(); } }; - processService.setup(p => p.execObservable(pythonPath, expectedArgs, options)).returns(() => observable); + processService.setup((p) => p.execObservable(pythonPath, expectedArgs, options)).returns(() => observable); const result = executionService.execModuleObservable(moduleName, args, options); - processService.verify(p => p.execObservable(pythonPath, expectedArgs, options), TypeMoq.Times.once()); + processService.verify((p) => p.execObservable(pythonPath, expectedArgs, options), TypeMoq.Times.once()); expect(result).to.be.equal(observable, 'execModuleObservable should return an observable'); }); @@ -245,11 +245,11 @@ suite('PythonExecutionService', () => { const args = ['-a', 'b', '-c']; const options = {}; const stdout = 'foo'; - processService.setup(p => p.exec(pythonPath, args, options)).returns(() => Promise.resolve({ stdout })); + processService.setup((p) => p.exec(pythonPath, args, options)).returns(() => Promise.resolve({ stdout })); const result = await executionService.exec(args, options); - processService.verify(p => p.exec(pythonPath, args, options), TypeMoq.Times.once()); + processService.verify((p) => p.exec(pythonPath, args, options), TypeMoq.Times.once()); expect(result.stdout).to.be.equal(stdout, 'exec should return the content of stdout'); }); @@ -259,11 +259,13 @@ suite('PythonExecutionService', () => { const expectedArgs = ['-m', moduleName, ...args]; const options = {}; const stdout = 'bar'; - processService.setup(p => p.exec(pythonPath, expectedArgs, options)).returns(() => Promise.resolve({ stdout })); + processService + .setup((p) => p.exec(pythonPath, expectedArgs, options)) + .returns(() => Promise.resolve({ stdout })); const result = await executionService.execModule(moduleName, args, options); - processService.verify(p => p.exec(pythonPath, expectedArgs, options), TypeMoq.Times.once()); + processService.verify((p) => p.exec(pythonPath, expectedArgs, options), TypeMoq.Times.once()); expect(result.stdout).to.be.equal(stdout, 'exec should return the content of stdout'); }); @@ -273,10 +275,10 @@ suite('PythonExecutionService', () => { const expectedArgs = ['-m', moduleName, ...args]; const options = {}; processService - .setup(p => p.exec(pythonPath, expectedArgs, options)) + .setup((p) => p.exec(pythonPath, expectedArgs, options)) .returns(() => Promise.resolve({ stdout: 'bar', stderr: `Error: No module named ${moduleName}` })); processService - .setup(p => p.exec(pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true })) + .setup((p) => p.exec(pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true })) .returns(() => Promise.reject(new StdErrError('not installed'))); const result = executionService.execModule(moduleName, args, options); diff --git a/src/test/common/process/serviceRegistry.unit.test.ts b/src/test/common/process/serviceRegistry.unit.test.ts index ac457ddfd143..6f8b3d63d902 100644 --- a/src/test/common/process/serviceRegistry.unit.test.ts +++ b/src/test/common/process/serviceRegistry.unit.test.ts @@ -1,44 +1,44 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { instance, mock, verify } from 'ts-mockito'; -import { BufferDecoder } from '../../../client/common/process/decoder'; -import { ProcessServiceFactory } from '../../../client/common/process/processFactory'; -import { PythonExecutionFactory } from '../../../client/common/process/pythonExecutionFactory'; -import { PythonToolExecutionService } from '../../../client/common/process/pythonToolService'; -import { registerTypes } from '../../../client/common/process/serviceRegistry'; -import { - IBufferDecoder, - IProcessServiceFactory, - IPythonExecutionFactory, - IPythonToolExecutionService -} from '../../../client/common/process/types'; -import { ServiceManager } from '../../../client/ioc/serviceManager'; -import { IServiceManager } from '../../../client/ioc/types'; - -suite('Common Process Service Registry', () => { - let serviceManager: IServiceManager; - - setup(() => { - serviceManager = mock(ServiceManager); - }); - - test('Ensure services are registered', async () => { - registerTypes(instance(serviceManager)); - verify(serviceManager.addSingleton(IBufferDecoder, BufferDecoder)).once(); - verify( - serviceManager.addSingleton(IProcessServiceFactory, ProcessServiceFactory) - ).once(); - verify( - serviceManager.addSingleton(IPythonExecutionFactory, PythonExecutionFactory) - ).once(); - verify( - serviceManager.addSingleton( - IPythonToolExecutionService, - PythonToolExecutionService - ) - ).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { instance, mock, verify } from 'ts-mockito'; +import { BufferDecoder } from '../../../client/common/process/decoder'; +import { ProcessServiceFactory } from '../../../client/common/process/processFactory'; +import { PythonExecutionFactory } from '../../../client/common/process/pythonExecutionFactory'; +import { PythonToolExecutionService } from '../../../client/common/process/pythonToolService'; +import { registerTypes } from '../../../client/common/process/serviceRegistry'; +import { + IBufferDecoder, + IProcessServiceFactory, + IPythonExecutionFactory, + IPythonToolExecutionService +} from '../../../client/common/process/types'; +import { ServiceManager } from '../../../client/ioc/serviceManager'; +import { IServiceManager } from '../../../client/ioc/types'; + +suite('Common Process Service Registry', () => { + let serviceManager: IServiceManager; + + setup(() => { + serviceManager = mock(ServiceManager); + }); + + test('Ensure services are registered', async () => { + registerTypes(instance(serviceManager)); + verify(serviceManager.addSingleton(IBufferDecoder, BufferDecoder)).once(); + verify( + serviceManager.addSingleton(IProcessServiceFactory, ProcessServiceFactory) + ).once(); + verify( + serviceManager.addSingleton(IPythonExecutionFactory, PythonExecutionFactory) + ).once(); + verify( + serviceManager.addSingleton( + IPythonToolExecutionService, + PythonToolExecutionService + ) + ).once(); + }); +}); diff --git a/src/test/common/serviceRegistry.unit.test.ts b/src/test/common/serviceRegistry.unit.test.ts index 1010c9d93839..85c79b22efc2 100644 --- a/src/test/common/serviceRegistry.unit.test.ts +++ b/src/test/common/serviceRegistry.unit.test.ts @@ -1,186 +1,186 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -// tslint:disable: no-any - -import { expect } from 'chai'; -import * as typemoq from 'typemoq'; -import { IExtensionSingleActivationService } from '../../client/activation/types'; -import { ApplicationEnvironment } from '../../client/common/application/applicationEnvironment'; -import { ApplicationShell } from '../../client/common/application/applicationShell'; -import { CommandManager } from '../../client/common/application/commandManager'; -import { DebugService } from '../../client/common/application/debugService'; -import { DocumentManager } from '../../client/common/application/documentManager'; -import { Extensions } from '../../client/common/application/extensions'; -import { LanguageService } from '../../client/common/application/languageService'; -import { TerminalManager } from '../../client/common/application/terminalManager'; -import { - IApplicationEnvironment, - IApplicationShell, - ICommandManager, - IDebugService, - IDocumentManager, - ILanguageService, - ILiveShareApi, - ITerminalManager, - IWorkspaceService -} from '../../client/common/application/types'; -import { WorkspaceService } from '../../client/common/application/workspace'; -import { AsyncDisposableRegistry } from '../../client/common/asyncDisposableRegistry'; -import { ConfigurationService } from '../../client/common/configuration/service'; -import { CryptoUtils } from '../../client/common/crypto'; -import { EditorUtils } from '../../client/common/editor'; -import { ExperimentsManager } from '../../client/common/experiments'; -import { FeatureDeprecationManager } from '../../client/common/featureDeprecationManager'; -import { - ExtensionInsidersDailyChannelRule, - ExtensionInsidersOffChannelRule, - ExtensionInsidersWeeklyChannelRule -} from '../../client/common/insidersBuild/downloadChannelRules'; -import { ExtensionChannelService } from '../../client/common/insidersBuild/downloadChannelService'; -import { InsidersExtensionPrompt } from '../../client/common/insidersBuild/insidersExtensionPrompt'; -import { InsidersExtensionService } from '../../client/common/insidersBuild/insidersExtensionService'; -import { - ExtensionChannel, - IExtensionChannelRule, - IExtensionChannelService, - IInsiderExtensionPrompt -} from '../../client/common/insidersBuild/types'; -import { ProductInstaller } from '../../client/common/installer/productInstaller'; -import { BrowserService } from '../../client/common/net/browser'; -import { HttpClient } from '../../client/common/net/httpClient'; -import { NugetService } from '../../client/common/nuget/nugetService'; -import { INugetService } from '../../client/common/nuget/types'; -import { PersistentStateFactory } from '../../client/common/persistentState'; -import { PathUtils } from '../../client/common/platform/pathUtils'; -import { CurrentProcess } from '../../client/common/process/currentProcess'; -import { registerTypes } from '../../client/common/serviceRegistry'; -import { TerminalActivator } from '../../client/common/terminal/activator'; -import { PowershellTerminalActivationFailedHandler } from '../../client/common/terminal/activator/powershellFailedHandler'; -import { Bash } from '../../client/common/terminal/environmentActivationProviders/bash'; -import { CommandPromptAndPowerShell } from '../../client/common/terminal/environmentActivationProviders/commandPrompt'; -import { CondaActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/condaActivationProvider'; -import { PipEnvActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/pipEnvActivationProvider'; -import { PyEnvActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/pyenvActivationProvider'; -import { TerminalServiceFactory } from '../../client/common/terminal/factory'; -import { TerminalHelper } from '../../client/common/terminal/helper'; -import { SettingsShellDetector } from '../../client/common/terminal/shellDetectors/settingsShellDetector'; -import { TerminalNameShellDetector } from '../../client/common/terminal/shellDetectors/terminalNameShellDetector'; -import { UserEnvironmentShellDetector } from '../../client/common/terminal/shellDetectors/userEnvironmentShellDetector'; -import { VSCEnvironmentShellDetector } from '../../client/common/terminal/shellDetectors/vscEnvironmentShellDetector'; -import { - IShellDetector, - ITerminalActivationCommandProvider, - ITerminalActivationHandler, - ITerminalActivator, - ITerminalHelper, - ITerminalServiceFactory, - TerminalActivationProviders -} from '../../client/common/terminal/types'; -import { - IAsyncDisposableRegistry, - IBrowserService, - IConfigurationService, - ICryptoUtils, - ICurrentProcess, - IEditorUtils, - IExperimentsManager, - IExtensions, - IFeatureDeprecationManager, - IHttpClient, - IInstaller, - IPathUtils, - IPersistentStateFactory, - IRandom -} from '../../client/common/types'; -import { IMultiStepInputFactory, MultiStepInputFactory } from '../../client/common/utils/multiStepInput'; -import { Random } from '../../client/common/utils/random'; -import { LiveShareApi } from '../../client/datascience/liveshare/liveshare'; -import { IServiceManager } from '../../client/ioc/types'; -import { ImportTracker } from '../../client/telemetry/importTracker'; -import { IImportTracker } from '../../client/telemetry/types'; - -suite('Common - Service Registry', () => { - test('Registrations', () => { - const serviceManager = typemoq.Mock.ofType(); - - [ - [IExtensions, Extensions], - [IRandom, Random], - [IPersistentStateFactory, PersistentStateFactory], - [ITerminalServiceFactory, TerminalServiceFactory], - [IPathUtils, PathUtils], - [IApplicationShell, ApplicationShell], - [ICurrentProcess, CurrentProcess], - [IInstaller, ProductInstaller], - [ICommandManager, CommandManager], - [IConfigurationService, ConfigurationService], - [IWorkspaceService, WorkspaceService], - [IDocumentManager, DocumentManager], - [ITerminalManager, TerminalManager], - [IDebugService, DebugService], - [IApplicationEnvironment, ApplicationEnvironment], - [ILanguageService, LanguageService], - [IBrowserService, BrowserService], - [IHttpClient, HttpClient], - [IEditorUtils, EditorUtils], - [INugetService, NugetService], - [ITerminalActivator, TerminalActivator], - [ITerminalActivationHandler, PowershellTerminalActivationFailedHandler], - [ILiveShareApi, LiveShareApi], - [ICryptoUtils, CryptoUtils], - [IExperimentsManager, ExperimentsManager], - [ITerminalHelper, TerminalHelper], - [ITerminalActivationCommandProvider, PyEnvActivationCommandProvider, TerminalActivationProviders.pyenv], - [ITerminalActivationCommandProvider, Bash, TerminalActivationProviders.bashCShellFish], - [ - ITerminalActivationCommandProvider, - CommandPromptAndPowerShell, - TerminalActivationProviders.commandPromptAndPowerShell - ], - [ITerminalActivationCommandProvider, CondaActivationCommandProvider, TerminalActivationProviders.conda], - [ITerminalActivationCommandProvider, PipEnvActivationCommandProvider, TerminalActivationProviders.pipenv], - [IFeatureDeprecationManager, FeatureDeprecationManager], - [IAsyncDisposableRegistry, AsyncDisposableRegistry], - [IMultiStepInputFactory, MultiStepInputFactory], - [IImportTracker, ImportTracker], - [IShellDetector, TerminalNameShellDetector], - [IShellDetector, SettingsShellDetector], - [IShellDetector, UserEnvironmentShellDetector], - [IShellDetector, VSCEnvironmentShellDetector], - [IInsiderExtensionPrompt, InsidersExtensionPrompt], - [IExtensionSingleActivationService, InsidersExtensionService], - [IExtensionChannelService, ExtensionChannelService], - [IExtensionChannelRule, ExtensionInsidersOffChannelRule, ExtensionChannel.off], - [IExtensionChannelRule, ExtensionInsidersDailyChannelRule, ExtensionChannel.daily], - [IExtensionChannelRule, ExtensionInsidersWeeklyChannelRule, ExtensionChannel.weekly] - ].forEach(mapping => { - if (mapping.length === 2) { - serviceManager - .setup(s => - s.addSingleton( - typemoq.It.isValue(mapping[0] as any), - typemoq.It.is(value => mapping[1] === value) - ) - ) - .verifiable(typemoq.Times.atLeastOnce()); - } else { - serviceManager - .setup(s => - s.addSingleton( - typemoq.It.isValue(mapping[0] as any), - typemoq.It.isAny(), - typemoq.It.isValue(mapping[2] as any) - ) - ) - .callback((_, cls) => expect(cls).to.equal(mapping[1])) - .verifiable(typemoq.Times.once()); - } - }); - - registerTypes(serviceManager.object); - serviceManager.verifyAll(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// tslint:disable: no-any + +import { expect } from 'chai'; +import * as typemoq from 'typemoq'; +import { IExtensionSingleActivationService } from '../../client/activation/types'; +import { ApplicationEnvironment } from '../../client/common/application/applicationEnvironment'; +import { ApplicationShell } from '../../client/common/application/applicationShell'; +import { CommandManager } from '../../client/common/application/commandManager'; +import { DebugService } from '../../client/common/application/debugService'; +import { DocumentManager } from '../../client/common/application/documentManager'; +import { Extensions } from '../../client/common/application/extensions'; +import { LanguageService } from '../../client/common/application/languageService'; +import { TerminalManager } from '../../client/common/application/terminalManager'; +import { + IApplicationEnvironment, + IApplicationShell, + ICommandManager, + IDebugService, + IDocumentManager, + ILanguageService, + ILiveShareApi, + ITerminalManager, + IWorkspaceService +} from '../../client/common/application/types'; +import { WorkspaceService } from '../../client/common/application/workspace'; +import { AsyncDisposableRegistry } from '../../client/common/asyncDisposableRegistry'; +import { ConfigurationService } from '../../client/common/configuration/service'; +import { CryptoUtils } from '../../client/common/crypto'; +import { EditorUtils } from '../../client/common/editor'; +import { ExperimentsManager } from '../../client/common/experiments'; +import { FeatureDeprecationManager } from '../../client/common/featureDeprecationManager'; +import { + ExtensionInsidersDailyChannelRule, + ExtensionInsidersOffChannelRule, + ExtensionInsidersWeeklyChannelRule +} from '../../client/common/insidersBuild/downloadChannelRules'; +import { ExtensionChannelService } from '../../client/common/insidersBuild/downloadChannelService'; +import { InsidersExtensionPrompt } from '../../client/common/insidersBuild/insidersExtensionPrompt'; +import { InsidersExtensionService } from '../../client/common/insidersBuild/insidersExtensionService'; +import { + ExtensionChannel, + IExtensionChannelRule, + IExtensionChannelService, + IInsiderExtensionPrompt +} from '../../client/common/insidersBuild/types'; +import { ProductInstaller } from '../../client/common/installer/productInstaller'; +import { BrowserService } from '../../client/common/net/browser'; +import { HttpClient } from '../../client/common/net/httpClient'; +import { NugetService } from '../../client/common/nuget/nugetService'; +import { INugetService } from '../../client/common/nuget/types'; +import { PersistentStateFactory } from '../../client/common/persistentState'; +import { PathUtils } from '../../client/common/platform/pathUtils'; +import { CurrentProcess } from '../../client/common/process/currentProcess'; +import { registerTypes } from '../../client/common/serviceRegistry'; +import { TerminalActivator } from '../../client/common/terminal/activator'; +import { PowershellTerminalActivationFailedHandler } from '../../client/common/terminal/activator/powershellFailedHandler'; +import { Bash } from '../../client/common/terminal/environmentActivationProviders/bash'; +import { CommandPromptAndPowerShell } from '../../client/common/terminal/environmentActivationProviders/commandPrompt'; +import { CondaActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/condaActivationProvider'; +import { PipEnvActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/pipEnvActivationProvider'; +import { PyEnvActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/pyenvActivationProvider'; +import { TerminalServiceFactory } from '../../client/common/terminal/factory'; +import { TerminalHelper } from '../../client/common/terminal/helper'; +import { SettingsShellDetector } from '../../client/common/terminal/shellDetectors/settingsShellDetector'; +import { TerminalNameShellDetector } from '../../client/common/terminal/shellDetectors/terminalNameShellDetector'; +import { UserEnvironmentShellDetector } from '../../client/common/terminal/shellDetectors/userEnvironmentShellDetector'; +import { VSCEnvironmentShellDetector } from '../../client/common/terminal/shellDetectors/vscEnvironmentShellDetector'; +import { + IShellDetector, + ITerminalActivationCommandProvider, + ITerminalActivationHandler, + ITerminalActivator, + ITerminalHelper, + ITerminalServiceFactory, + TerminalActivationProviders +} from '../../client/common/terminal/types'; +import { + IAsyncDisposableRegistry, + IBrowserService, + IConfigurationService, + ICryptoUtils, + ICurrentProcess, + IEditorUtils, + IExperimentsManager, + IExtensions, + IFeatureDeprecationManager, + IHttpClient, + IInstaller, + IPathUtils, + IPersistentStateFactory, + IRandom +} from '../../client/common/types'; +import { IMultiStepInputFactory, MultiStepInputFactory } from '../../client/common/utils/multiStepInput'; +import { Random } from '../../client/common/utils/random'; +import { LiveShareApi } from '../../client/datascience/liveshare/liveshare'; +import { IServiceManager } from '../../client/ioc/types'; +import { ImportTracker } from '../../client/telemetry/importTracker'; +import { IImportTracker } from '../../client/telemetry/types'; + +suite('Common - Service Registry', () => { + test('Registrations', () => { + const serviceManager = typemoq.Mock.ofType(); + + [ + [IExtensions, Extensions], + [IRandom, Random], + [IPersistentStateFactory, PersistentStateFactory], + [ITerminalServiceFactory, TerminalServiceFactory], + [IPathUtils, PathUtils], + [IApplicationShell, ApplicationShell], + [ICurrentProcess, CurrentProcess], + [IInstaller, ProductInstaller], + [ICommandManager, CommandManager], + [IConfigurationService, ConfigurationService], + [IWorkspaceService, WorkspaceService], + [IDocumentManager, DocumentManager], + [ITerminalManager, TerminalManager], + [IDebugService, DebugService], + [IApplicationEnvironment, ApplicationEnvironment], + [ILanguageService, LanguageService], + [IBrowserService, BrowserService], + [IHttpClient, HttpClient], + [IEditorUtils, EditorUtils], + [INugetService, NugetService], + [ITerminalActivator, TerminalActivator], + [ITerminalActivationHandler, PowershellTerminalActivationFailedHandler], + [ILiveShareApi, LiveShareApi], + [ICryptoUtils, CryptoUtils], + [IExperimentsManager, ExperimentsManager], + [ITerminalHelper, TerminalHelper], + [ITerminalActivationCommandProvider, PyEnvActivationCommandProvider, TerminalActivationProviders.pyenv], + [ITerminalActivationCommandProvider, Bash, TerminalActivationProviders.bashCShellFish], + [ + ITerminalActivationCommandProvider, + CommandPromptAndPowerShell, + TerminalActivationProviders.commandPromptAndPowerShell + ], + [ITerminalActivationCommandProvider, CondaActivationCommandProvider, TerminalActivationProviders.conda], + [ITerminalActivationCommandProvider, PipEnvActivationCommandProvider, TerminalActivationProviders.pipenv], + [IFeatureDeprecationManager, FeatureDeprecationManager], + [IAsyncDisposableRegistry, AsyncDisposableRegistry], + [IMultiStepInputFactory, MultiStepInputFactory], + [IImportTracker, ImportTracker], + [IShellDetector, TerminalNameShellDetector], + [IShellDetector, SettingsShellDetector], + [IShellDetector, UserEnvironmentShellDetector], + [IShellDetector, VSCEnvironmentShellDetector], + [IInsiderExtensionPrompt, InsidersExtensionPrompt], + [IExtensionSingleActivationService, InsidersExtensionService], + [IExtensionChannelService, ExtensionChannelService], + [IExtensionChannelRule, ExtensionInsidersOffChannelRule, ExtensionChannel.off], + [IExtensionChannelRule, ExtensionInsidersDailyChannelRule, ExtensionChannel.daily], + [IExtensionChannelRule, ExtensionInsidersWeeklyChannelRule, ExtensionChannel.weekly] + ].forEach((mapping) => { + if (mapping.length === 2) { + serviceManager + .setup((s) => + s.addSingleton( + typemoq.It.isValue(mapping[0] as any), + typemoq.It.is((value) => mapping[1] === value) + ) + ) + .verifiable(typemoq.Times.atLeastOnce()); + } else { + serviceManager + .setup((s) => + s.addSingleton( + typemoq.It.isValue(mapping[0] as any), + typemoq.It.isAny(), + typemoq.It.isValue(mapping[2] as any) + ) + ) + .callback((_, cls) => expect(cls).to.equal(mapping[1])) + .verifiable(typemoq.Times.once()); + } + }); + + registerTypes(serviceManager.object); + serviceManager.verifyAll(); + }); +}); diff --git a/src/test/common/socketStream.test.ts b/src/test/common/socketStream.test.ts index 87a5c49772b0..13d3082dcd96 100644 --- a/src/test/common/socketStream.test.ts +++ b/src/test/common/socketStream.test.ts @@ -37,7 +37,7 @@ class MockSocket { // Defines a Mocha test suite to group tests of similar kind together // tslint:disable-next-line:max-func-body-length suite('SocketStream', () => { - test('Read Byte', done => { + test('Read Byte', (done) => { const buffer = new Buffer('X'); const byteValue = buffer[0]; const socket = new MockSocket(); @@ -47,7 +47,7 @@ suite('SocketStream', () => { assert.equal(stream.ReadByte(), byteValue); done(); }); - test('Read Int32', done => { + test('Read Int32', (done) => { const num = 1234; const socket = new MockSocket(); const buffer = uint64be.encode(num); @@ -57,7 +57,7 @@ suite('SocketStream', () => { assert.equal(stream.ReadInt32(), num); done(); }); - test('Read Int64', done => { + test('Read Int64', (done) => { const num = 9007199254740993; const socket = new MockSocket(); const buffer = uint64be.encode(num); @@ -67,7 +67,7 @@ suite('SocketStream', () => { assert.equal(stream.ReadInt64(), num); done(); }); - test('Read Ascii String', done => { + test('Read Ascii String', (done) => { const message = 'Hello World'; const socket = new MockSocket(); const buffer = Buffer.concat([new Buffer('A'), uint64be.encode(message.length), new Buffer(message)]); @@ -77,7 +77,7 @@ suite('SocketStream', () => { assert.equal(stream.ReadString(), message); done(); }); - test('Read Unicode String', done => { + test('Read Unicode String', (done) => { const message = 'Hello World - Функция проверки ИНН и КПП - 说明'; const socket = new MockSocket(); const stringBuffer = new Buffer(message); @@ -91,7 +91,7 @@ suite('SocketStream', () => { assert.equal(stream.ReadString(), message); done(); }); - test('Read RollBackTransaction', done => { + test('Read RollBackTransaction', (done) => { const message = 'Hello World'; const socket = new MockSocket(); let buffer = Buffer.concat([new Buffer('A'), uint64be.encode(message.length), new Buffer(message)]); @@ -110,7 +110,7 @@ suite('SocketStream', () => { assert.equal(stream.ReadString(), message, 'First message not read properly after rolling back transaction'); done(); }); - test('Read EndTransaction', done => { + test('Read EndTransaction', (done) => { const message = 'Hello World'; const socket = new MockSocket(); let buffer = Buffer.concat([new Buffer('A'), uint64be.encode(message.length), new Buffer(message)]); @@ -130,7 +130,7 @@ suite('SocketStream', () => { assert.notEqual(stream.ReadString(), message, 'First message cannot be read after commit transaction'); done(); }); - test('Write Buffer', done => { + test('Write Buffer', (done) => { const message = 'Hello World'; const buffer = new Buffer(''); const socket = new MockSocket(); @@ -141,7 +141,7 @@ suite('SocketStream', () => { assert.equal(socket.dataWritten, message); done(); }); - test('Write Int32', done => { + test('Write Int32', (done) => { const num = 1234; const buffer = new Buffer(''); const socket = new MockSocket(); @@ -152,7 +152,7 @@ suite('SocketStream', () => { assert.equal(uint64be.decode(socket.rawDataWritten), num); done(); }); - test('Write Int64', done => { + test('Write Int64', (done) => { const num = 9007199254740993; const buffer = new Buffer(''); const socket = new MockSocket(); @@ -163,7 +163,7 @@ suite('SocketStream', () => { assert.equal(uint64be.decode(socket.rawDataWritten), num); done(); }); - test('Write Ascii String', done => { + test('Write Ascii String', (done) => { const message = 'Hello World'; const buffer = new Buffer(''); const socket = new MockSocket(); @@ -174,7 +174,7 @@ suite('SocketStream', () => { assert.equal(socket.dataWritten, message); done(); }); - test('Write Unicode String', done => { + test('Write Unicode String', (done) => { const message = 'Hello World - Функция проверки ИНН и КПП - 说明'; const buffer = new Buffer(''); const socket = new MockSocket(); diff --git a/src/test/common/terminals/activation.bash.unit.test.ts b/src/test/common/terminals/activation.bash.unit.test.ts index a7cb6494a14a..b14415e3aab2 100644 --- a/src/test/common/terminals/activation.bash.unit.test.ts +++ b/src/test/common/terminals/activation.bash.unit.test.ts @@ -19,32 +19,34 @@ suite('Terminal Environment Activation (bash)', () => { 'usr/bin/python', 'usr/bin/env with spaces/env more/python', 'c:\\users\\windows paths\\conda\\python.exe' - ].forEach(pythonPath => { + ].forEach((pythonPath) => { const hasSpaces = pythonPath.indexOf(' ') > 0; const suiteTitle = hasSpaces ? 'and there are spaces in the script file (pythonpath),' : 'and there are no spaces in the script file (pythonpath),'; suite(suiteTitle, () => { ['activate', 'activate.sh', 'activate.csh', 'activate.fish', 'activate.bat', 'Activate.ps1'].forEach( - scriptFileName => { + (scriptFileName) => { suite(`and script file is ${scriptFileName}`, () => { let serviceContainer: TypeMoq.IMock; let fileSystem: TypeMoq.IMock; setup(() => { serviceContainer = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(IFileSystem)).returns(() => fileSystem.object); + serviceContainer.setup((c) => c.get(IFileSystem)).returns(() => fileSystem.object); const configService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); const settings = TypeMoq.Mock.ofType(); - settings.setup(s => s.pythonPath).returns(() => pythonPath); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + configService + .setup((c) => c.getSettings(TypeMoq.It.isAny())) + .returns(() => settings.object); }); - getNamesAndValues(TerminalShellType).forEach(shellType => { + getNamesAndValues(TerminalShellType).forEach((shellType) => { let isScriptFileSupported = false; switch (shellType.value) { case TerminalShellType.zsh: @@ -103,7 +105,7 @@ suite('Terminal Environment Activation (bash)', () => { const pathToScriptFile = path.join(path.dirname(pythonPath), scriptFileName); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) .returns(() => Promise.resolve(true)); const command = await bash.getActivationCommands(undefined, shellType.value); diff --git a/src/test/common/terminals/activation.commandPrompt.unit.test.ts b/src/test/common/terminals/activation.commandPrompt.unit.test.ts index 248122d4b2d1..56d1239caf72 100644 --- a/src/test/common/terminals/activation.commandPrompt.unit.test.ts +++ b/src/test/common/terminals/activation.commandPrompt.unit.test.ts @@ -19,7 +19,7 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { 'c:/programfiles/python/python', 'c:/program files/python/python', 'c:\\users\\windows paths\\conda\\python.exe' - ].forEach(pythonPath => { + ].forEach((pythonPath) => { const hasSpaces = pythonPath.indexOf(' ') > 0; const resource = Uri.file('a'); @@ -28,25 +28,27 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { : 'and there are no spaces in the script file (pythonpath),'; suite(suiteTitle, () => { ['activate', 'activate.sh', 'activate.csh', 'activate.fish', 'activate.bat', 'Activate.ps1'].forEach( - scriptFileName => { + (scriptFileName) => { suite(`and script file is ${scriptFileName}`, () => { let serviceContainer: TypeMoq.IMock; let fileSystem: TypeMoq.IMock; setup(() => { serviceContainer = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(IFileSystem)).returns(() => fileSystem.object); + serviceContainer.setup((c) => c.get(IFileSystem)).returns(() => fileSystem.object); const configService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); const settings = TypeMoq.Mock.ofType(); - settings.setup(s => s.pythonPath).returns(() => pythonPath); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + configService + .setup((c) => c.getSettings(TypeMoq.It.isAny())) + .returns(() => settings.object); }); - getNamesAndValues(TerminalShellType).forEach(shellType => { + getNamesAndValues(TerminalShellType).forEach((shellType) => { const isScriptFileSupported = ['activate.bat', 'Activate.ps1'].indexOf(scriptFileName) >= 0; const titleTitle = isScriptFileSupported ? `Ensure terminal type is supported (Shell: ${shellType.name})` @@ -87,16 +89,16 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { serviceContainer = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); platform = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(IFileSystem)).returns(() => fileSystem.object); - serviceContainer.setup(c => c.get(IPlatformService)).returns(() => platform.object); + serviceContainer.setup((c) => c.get(IFileSystem)).returns(() => fileSystem.object); + serviceContainer.setup((c) => c.get(IPlatformService)).returns(() => platform.object); const configService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); const settings = TypeMoq.Mock.ofType(); - settings.setup(s => s.pythonPath).returns(() => pythonPath); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); }); test('Ensure batch files are supported by command prompt', async () => { @@ -104,7 +106,7 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { const pathToScriptFile = path.join(path.dirname(pythonPath), 'activate.bat'); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) .returns(() => Promise.resolve(true)); const commands = await bash.getActivationCommands(resource, TerminalShellType.commandPrompt); @@ -119,10 +121,10 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { test('Ensure batch files are not supported by powershell (on windows)', async () => { const batch = new CommandPromptAndPowerShell(serviceContainer.object); - platform.setup(p => p.isWindows).returns(() => true); + platform.setup((p) => p.isWindows).returns(() => true); const pathToScriptFile = path.join(path.dirname(pythonPath), 'activate.bat'); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) .returns(() => Promise.resolve(true)); const command = await batch.getActivationCommands(resource, TerminalShellType.powershell); @@ -132,10 +134,10 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { test('Ensure batch files are not supported by powershell core (on windows)', async () => { const bash = new CommandPromptAndPowerShell(serviceContainer.object); - platform.setup(p => p.isWindows).returns(() => true); + platform.setup((p) => p.isWindows).returns(() => true); const pathToScriptFile = path.join(path.dirname(pythonPath), 'activate.bat'); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) .returns(() => Promise.resolve(true)); const command = await bash.getActivationCommands(resource, TerminalShellType.powershellCore); @@ -145,10 +147,10 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { test('Ensure batch files are not supported by powershell (on non-windows)', async () => { const bash = new CommandPromptAndPowerShell(serviceContainer.object); - platform.setup(p => p.isWindows).returns(() => false); + platform.setup((p) => p.isWindows).returns(() => false); const pathToScriptFile = path.join(path.dirname(pythonPath), 'activate.bat'); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) .returns(() => Promise.resolve(true)); const command = await bash.getActivationCommands(resource, TerminalShellType.powershell); @@ -158,10 +160,10 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { test('Ensure batch files are not supported by powershell core (on non-windows)', async () => { const bash = new CommandPromptAndPowerShell(serviceContainer.object); - platform.setup(p => p.isWindows).returns(() => false); + platform.setup((p) => p.isWindows).returns(() => false); const pathToScriptFile = path.join(path.dirname(pythonPath), 'activate.bat'); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) .returns(() => Promise.resolve(true)); const command = await bash.getActivationCommands(resource, TerminalShellType.powershellCore); @@ -177,25 +179,25 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { serviceContainer = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); platform = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(IFileSystem)).returns(() => fileSystem.object); - serviceContainer.setup(c => c.get(IPlatformService)).returns(() => platform.object); + serviceContainer.setup((c) => c.get(IFileSystem)).returns(() => fileSystem.object); + serviceContainer.setup((c) => c.get(IPlatformService)).returns(() => platform.object); const configService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); const settings = TypeMoq.Mock.ofType(); - settings.setup(s => s.pythonPath).returns(() => pythonPath); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); }); test('Ensure powershell files are not supported by command prompt', async () => { const bash = new CommandPromptAndPowerShell(serviceContainer.object); - platform.setup(p => p.isWindows).returns(() => true); + platform.setup((p) => p.isWindows).returns(() => true); const pathToScriptFile = path.join(path.dirname(pythonPath), 'Activate.ps1'); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) .returns(() => Promise.resolve(true)); const command = await bash.getActivationCommands(resource, TerminalShellType.commandPrompt); @@ -208,10 +210,10 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { test('Ensure powershell files are supported by powershell', async () => { const bash = new CommandPromptAndPowerShell(serviceContainer.object); - platform.setup(p => p.isWindows).returns(() => true); + platform.setup((p) => p.isWindows).returns(() => true); const pathToScriptFile = path.join(path.dirname(pythonPath), 'Activate.ps1'); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) .returns(() => Promise.resolve(true)); const command = await bash.getActivationCommands(resource, TerminalShellType.powershell); @@ -224,10 +226,10 @@ suite('Terminal Environment Activation (cmd/powershell)', () => { test('Ensure powershell files are supported by powershell core', async () => { const bash = new CommandPromptAndPowerShell(serviceContainer.object); - platform.setup(p => p.isWindows).returns(() => true); + platform.setup((p) => p.isWindows).returns(() => true); const pathToScriptFile = path.join(path.dirname(pythonPath), 'Activate.ps1'); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pathToScriptFile))) .returns(() => Promise.resolve(true)); const command = await bash.getActivationCommands(resource, TerminalShellType.powershellCore); diff --git a/src/test/common/terminals/activation.conda.unit.test.ts b/src/test/common/terminals/activation.conda.unit.test.ts index 5b63e1694f66..bd3142cb5b4b 100644 --- a/src/test/common/terminals/activation.conda.unit.test.ts +++ b/src/test/common/terminals/activation.conda.unit.test.ts @@ -51,44 +51,44 @@ suite('Terminal Environment Activation conda', () => { serviceContainer = TypeMoq.Mock.ofType(); disposables = []; serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) .returns(() => disposables); fileSystem = TypeMoq.Mock.ofType(); platformService = TypeMoq.Mock.ofType(); processService = TypeMoq.Mock.ofType(); condaService = TypeMoq.Mock.ofType(); - condaService.setup(c => c.getCondaFile()).returns(() => Promise.resolve(conda)); + condaService.setup((c) => c.getCondaFile()).returns(() => Promise.resolve(conda)); bash = mock(Bash); processService.setup((x: any) => x.then).returns(() => undefined); procServiceFactory = TypeMoq.Mock.ofType(); procServiceFactory - .setup(p => p.create(TypeMoq.It.isAny())) + .setup((p) => p.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(processService.object)); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) .returns(() => platformService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) .returns(() => fileSystem.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())) .returns(() => procServiceFactory.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ICondaService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ICondaService), TypeMoq.It.isAny())) .returns(() => condaService.object); configService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); pythonSettings = TypeMoq.Mock.ofType(); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); terminalSettings = TypeMoq.Mock.ofType(); - pythonSettings.setup(s => s.terminal).returns(() => terminalSettings.object); + pythonSettings.setup((s) => s.terminal).returns(() => terminalSettings.object); terminalHelper = new TerminalHelper( platformService.object, @@ -105,7 +105,7 @@ suite('Terminal Environment Activation conda', () => { ); }); teardown(() => { - disposables.forEach(disposable => { + disposables.forEach((disposable) => { if (disposable) { disposable.dispose(); } @@ -113,7 +113,7 @@ suite('Terminal Environment Activation conda', () => { }); test('Ensure no activation commands are returned if the feature is disabled', async () => { - terminalSettings.setup(t => t.activateEnvironment).returns(() => false); + terminalSettings.setup((t) => t.activateEnvironment).returns(() => false); const activationCommands = await terminalHelper.getEnvironmentActivationCommands( TerminalShellType.bash, @@ -126,9 +126,9 @@ suite('Terminal Environment Activation conda', () => { conda = 'path to conda'; const envName = 'EnvA'; const pythonPath = 'python3'; - platformService.setup(p => p.isWindows).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => false); condaService - .setup(c => c.getCondaEnvironment(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironment(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ name: envName, path: path.dirname(pythonPath) })); const expected = ['"path to conda" activate EnvA']; @@ -146,18 +146,18 @@ suite('Terminal Environment Activation conda', () => { const envName = 'EnvA'; const pythonPath = 'python3'; const condaPath = path.join('a', 'b', 'c', 'conda'); - platformService.setup(p => p.isWindows).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => false); condaService.reset(); condaService - .setup(c => c.getCondaEnvironment(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironment(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ name: envName, path: path.dirname(pythonPath) }) ); - condaService.setup(c => c.getCondaFile()).returns(() => Promise.resolve(condaPath)); - condaService.setup(c => c.getCondaVersion()).returns(() => Promise.resolve(parse('4.3.1', true)!)); + condaService.setup((c) => c.getCondaFile()).returns(() => Promise.resolve(condaPath)); + condaService.setup((c) => c.getCondaVersion()).returns(() => Promise.resolve(parse('4.3.1', true)!)); const expected = [`source ${path.join(path.dirname(condaPath), 'activate').fileToCommandArgument()} EnvA`]; const provider = new CondaActivationCommandProvider( @@ -174,18 +174,18 @@ suite('Terminal Environment Activation conda', () => { const envName = 'EnvA'; const pythonPath = 'python3'; const condaPath = path.join('a', 'b', 'c', 'conda'); - platformService.setup(p => p.isWindows).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => false); condaService.reset(); condaService - .setup(c => c.getCondaEnvironment(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironment(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ name: envName, path: path.dirname(pythonPath) }) ); - condaService.setup(c => c.getCondaFile()).returns(() => Promise.resolve(condaPath)); - condaService.setup(c => c.getCondaVersion()).returns(() => Promise.resolve(parse('4.4.0', true)!)); + condaService.setup((c) => c.getCondaFile()).returns(() => Promise.resolve(condaPath)); + condaService.setup((c) => c.getCondaVersion()).returns(() => Promise.resolve(parse('4.4.0', true)!)); const expected = [`source ${path.join(path.dirname(condaPath), 'activate').fileToCommandArgument()} EnvA`]; const provider = new CondaActivationCommandProvider( @@ -246,22 +246,22 @@ suite('Terminal Environment Activation conda', () => { } ]; - testsForActivationUsingInterpreterPath.forEach(testParams => { + testsForActivationUsingInterpreterPath.forEach((testParams) => { test(testParams.testName, async () => { const pythonPath = 'python3'; - platformService.setup(p => p.isWindows).returns(() => testParams.isWindows); + platformService.setup((p) => p.isWindows).returns(() => testParams.isWindows); condaService.reset(); condaService - .setup(c => c.getCondaEnvironment(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironment(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ name: testParams.envName, path: path.dirname(pythonPath) }) ); - condaService.setup(c => c.getCondaVersion()).returns(() => Promise.resolve(parse('4.4.0', true)!)); + condaService.setup((c) => c.getCondaVersion()).returns(() => Promise.resolve(parse('4.4.0', true)!)); condaService - .setup(c => c.getCondaFileFromInterpreter(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((c) => c.getCondaFileFromInterpreter(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(interpreterPath)); const provider = new CondaActivationCommandProvider( @@ -283,14 +283,14 @@ suite('Terminal Environment Activation conda', () => { shellType: TerminalShellType, envName: string ) { - terminalSettings.setup(t => t.activateEnvironment).returns(() => true); - platformService.setup(p => p.isLinux).returns(() => isLinux); - platformService.setup(p => p.isWindows).returns(() => isWindows); - platformService.setup(p => p.isMac).returns(() => isOsx); - condaService.setup(c => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); - pythonSettings.setup(s => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.activateEnvironment).returns(() => true); + platformService.setup((p) => p.isLinux).returns(() => isLinux); + platformService.setup((p) => p.isWindows).returns(() => isWindows); + platformService.setup((p) => p.isMac).returns(() => isOsx); + condaService.setup((c) => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + pythonSettings.setup((s) => s.pythonPath).returns(() => pythonPath); condaService - .setup(c => c.getCondaEnvironment(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironment(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ name: envName, path: path.dirname(pythonPath) })); const activationCommands = await new CondaActivationCommandProvider( @@ -330,7 +330,7 @@ suite('Terminal Environment Activation conda', () => { expect(activationCommands).to.equal(undefined, 'Incorrect Activation command'); } } - getNamesAndValues(TerminalShellType).forEach(shellType => { + getNamesAndValues(TerminalShellType).forEach((shellType) => { test(`Conda activation command for shell ${shellType.name} on (windows)`, async () => { const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'enva', 'python.exe'); await testCondaActivationCommands(true, false, false, pythonPath, shellType.value, 'Env'); @@ -346,7 +346,7 @@ suite('Terminal Environment Activation conda', () => { await testCondaActivationCommands(false, true, false, pythonPath, shellType.value, 'Env'); }); }); - getNamesAndValues(TerminalShellType).forEach(shellType => { + getNamesAndValues(TerminalShellType).forEach((shellType) => { test(`Conda activation command for shell ${shellType.name} on (windows), containing spaces in environment name`, async () => { const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'enva', 'python.exe'); await testCondaActivationCommands(true, false, false, pythonPath, shellType.value, 'Env A'); @@ -362,7 +362,7 @@ suite('Terminal Environment Activation conda', () => { await testCondaActivationCommands(false, true, false, pythonPath, shellType.value, 'Env A'); }); }); - getNamesAndValues(TerminalShellType).forEach(shellType => { + getNamesAndValues(TerminalShellType).forEach((shellType) => { test(`Conda activation command for shell ${shellType.name} on (windows), containing no environment name`, async () => { const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'enva', 'python.exe'); await testCondaActivationCommands(true, false, false, pythonPath, shellType.value, ''); @@ -384,14 +384,14 @@ suite('Terminal Environment Activation conda', () => { isLinux: boolean, pythonPath: string ) { - terminalSettings.setup(t => t.activateEnvironment).returns(() => true); - platformService.setup(p => p.isLinux).returns(() => isLinux); - platformService.setup(p => p.isWindows).returns(() => isWindows); - platformService.setup(p => p.isMac).returns(() => isOsx); - condaService.setup(c => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); - pythonSettings.setup(s => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.activateEnvironment).returns(() => true); + platformService.setup((p) => p.isLinux).returns(() => isLinux); + platformService.setup((p) => p.isWindows).returns(() => isWindows); + platformService.setup((p) => p.isMac).returns(() => isOsx); + condaService.setup((c) => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + pythonSettings.setup((s) => s.pythonPath).returns(() => pythonPath); condaService - .setup(c => c.getCondaEnvironment(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironment(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ name: 'EnvA', path: path.dirname(pythonPath) })); const expectedActivationCommand = isWindows ? ['activate EnvA'] : ['source activate EnvA']; @@ -405,7 +405,7 @@ suite('Terminal Environment Activation conda', () => { test('If environment is a conda environment, ensure conda activation command is sent (windows)', async () => { const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'enva', 'python.exe'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(true)); await expectCondaActivationCommand(true, false, false, pythonPath); }); @@ -413,7 +413,9 @@ suite('Terminal Environment Activation conda', () => { test('If environment is a conda environment, ensure conda activation command is sent (linux)', async () => { const pythonPath = path.join('users', 'xyz', '.conda', 'envs', 'enva', 'bin', 'python'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(true)); await expectCondaActivationCommand(false, false, true, pythonPath); }); @@ -421,24 +423,26 @@ suite('Terminal Environment Activation conda', () => { test('If environment is a conda environment, ensure conda activation command is sent (osx)', async () => { const pythonPath = path.join('users', 'xyz', '.conda', 'envs', 'enva', 'bin', 'python'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(true)); await expectCondaActivationCommand(false, true, false, pythonPath); }); test('Get activation script command if environment is not a conda environment', async () => { const pythonPath = path.join('users', 'xyz', '.conda', 'envs', 'enva', 'bin', 'python'); - terminalSettings.setup(t => t.activateEnvironment).returns(() => true); - condaService.setup(c => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); - pythonSettings.setup(s => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.activateEnvironment).returns(() => true); + condaService.setup((c) => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); + pythonSettings.setup((s) => s.pythonPath).returns(() => pythonPath); const mockProvider = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.getAll(TypeMoq.It.isValue(ITerminalActivationCommandProvider), TypeMoq.It.isAny())) + .setup((c) => c.getAll(TypeMoq.It.isValue(ITerminalActivationCommandProvider), TypeMoq.It.isAny())) .returns(() => [mockProvider.object]); - mockProvider.setup(p => p.isShellSupported(TypeMoq.It.isAny())).returns(() => true); + mockProvider.setup((p) => p.isShellSupported(TypeMoq.It.isAny())).returns(() => true); mockProvider - .setup(p => p.getActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.getActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(['mock command'])); const expectedActivationCommand = ['mock command']; @@ -458,13 +462,13 @@ suite('Terminal Environment Activation conda', () => { isLinux: boolean, pythonPath: string ) { - terminalSettings.setup(t => t.activateEnvironment).returns(() => true); - platformService.setup(p => p.isLinux).returns(() => isLinux); - platformService.setup(p => p.isWindows).returns(() => isWindows); - platformService.setup(p => p.isMac).returns(() => isOsx); - condaService.setup(c => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); - condaService.setup(c => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); - pythonSettings.setup(s => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.activateEnvironment).returns(() => true); + platformService.setup((p) => p.isLinux).returns(() => isLinux); + platformService.setup((p) => p.isWindows).returns(() => isWindows); + platformService.setup((p) => p.isMac).returns(() => isOsx); + condaService.setup((c) => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + condaService.setup((c) => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); + pythonSettings.setup((s) => s.pythonPath).returns(() => pythonPath); when(bash.isShellSupported(anything())).thenReturn(true); when(bash.getActivationCommands(anything(), TerminalShellType.bash)).thenResolve(['mock command']); @@ -480,7 +484,7 @@ suite('Terminal Environment Activation conda', () => { test('If environment is a conda environment and environment detection fails, ensure activatino of script is sent (windows)', async () => { const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'enva', 'python.exe'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(true)); await expectActivationCommandIfCondaDetectionFails(true, false, false, pythonPath); }); @@ -488,7 +492,9 @@ suite('Terminal Environment Activation conda', () => { test('If environment is a conda environment and environment detection fails, ensure activatino of script is sent (osx)', async () => { const pythonPath = path.join('users', 'xyz', '.conda', 'envs', 'enva', 'python'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(true)); await expectActivationCommandIfCondaDetectionFails(false, true, false, pythonPath); }); @@ -496,7 +502,9 @@ suite('Terminal Environment Activation conda', () => { test('If environment is a conda environment and environment detection fails, ensure activatino of script is sent (linux)', async () => { const pythonPath = path.join('users', 'xyz', '.conda', 'envs', 'enva', 'python'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(true)); await expectActivationCommandIfCondaDetectionFails(false, false, true, pythonPath); }); @@ -504,18 +512,18 @@ suite('Terminal Environment Activation conda', () => { test('Return undefined if unable to get activation command', async () => { const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'enva', 'python.exe'); - terminalSettings.setup(t => t.activateEnvironment).returns(() => true); - condaService.setup(c => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); + terminalSettings.setup((t) => t.activateEnvironment).returns(() => true); + condaService.setup((c) => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); - pythonSettings.setup(s => s.pythonPath).returns(() => pythonPath); + pythonSettings.setup((s) => s.pythonPath).returns(() => pythonPath); const mockProvider = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.getAll(TypeMoq.It.isValue(ITerminalActivationCommandProvider), TypeMoq.It.isAny())) + .setup((c) => c.getAll(TypeMoq.It.isValue(ITerminalActivationCommandProvider), TypeMoq.It.isAny())) .returns(() => [mockProvider.object]); - mockProvider.setup(p => p.isShellSupported(TypeMoq.It.isAny())).returns(() => true); + mockProvider.setup((p) => p.isShellSupported(TypeMoq.It.isAny())).returns(() => true); mockProvider - .setup(p => p.getActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.getActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); const activationCommands = await terminalHelper.getEnvironmentActivationCommands( @@ -619,12 +627,12 @@ suite('Terminal Environment Activation conda', () => { const servCnt = TypeMoq.Mock.ofType(); const condaSrv = TypeMoq.Mock.ofType(); condaSrv - .setup(c => c.getCondaFile()) + .setup((c) => c.getCondaFile()) .returns(async () => { return path.join(testParams.basePath, 'conda.exe'); }); servCnt - .setup(s => s.get(TypeMoq.It.isValue(ICondaService), TypeMoq.It.isAny())) + .setup((s) => s.get(TypeMoq.It.isValue(ICondaService), TypeMoq.It.isAny())) .returns(() => condaSrv.object); const tstCmdProvider = new CondaActivationCommandProvider( diff --git a/src/test/common/terminals/activator/base.unit.test.ts b/src/test/common/terminals/activator/base.unit.test.ts index 4e8fa04d03a5..79f1c121cdb4 100644 --- a/src/test/common/terminals/activator/base.unit.test.ts +++ b/src/test/common/terminals/activator/base.unit.test.ts @@ -29,24 +29,24 @@ suite('Terminal Base Activator', () => { { commandCount: 2, preserveFocus: false }, { commandCount: 1, preserveFocus: true }, { commandCount: 1, preserveFocus: true } - ].forEach(item => { + ].forEach((item) => { const titleSuffix = `(${item.commandCount} activation command, and preserve focus in terminal is ${item.preserveFocus})`; const activationCommands = item.commandCount === 1 ? ['CMD1'] : ['CMD1', 'CMD2']; test(`Terminal is activated ${titleSuffix}`, async () => { helper - .setup(h => + .setup((h) => h.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve(activationCommands)); const terminal = TypeMoq.Mock.ofType(); terminal - .setup(t => t.show(TypeMoq.It.isValue(item.preserveFocus))) + .setup((t) => t.show(TypeMoq.It.isValue(item.preserveFocus))) .returns(() => undefined) .verifiable(TypeMoq.Times.exactly(activationCommands.length)); - activationCommands.forEach(cmd => { + activationCommands.forEach((cmd) => { terminal - .setup(t => t.sendText(TypeMoq.It.isValue(cmd))) + .setup((t) => t.sendText(TypeMoq.It.isValue(cmd))) .returns(() => undefined) .verifiable(TypeMoq.Times.exactly(1)); }); @@ -57,19 +57,19 @@ suite('Terminal Base Activator', () => { }); test(`Terminal is activated only once ${titleSuffix}`, async () => { helper - .setup(h => + .setup((h) => h.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve(activationCommands)); const terminal = TypeMoq.Mock.ofType(); terminal - .setup(t => t.show(TypeMoq.It.isValue(item.preserveFocus))) + .setup((t) => t.show(TypeMoq.It.isValue(item.preserveFocus))) .returns(() => undefined) .verifiable(TypeMoq.Times.exactly(activationCommands.length)); - activationCommands.forEach(cmd => { + activationCommands.forEach((cmd) => { terminal - .setup(t => t.sendText(TypeMoq.It.isValue(cmd))) + .setup((t) => t.sendText(TypeMoq.It.isValue(cmd))) .returns(() => undefined) .verifiable(TypeMoq.Times.exactly(1)); }); @@ -82,19 +82,19 @@ suite('Terminal Base Activator', () => { }); test(`Terminal is activated only once ${titleSuffix} (even when not waiting)`, async () => { helper - .setup(h => + .setup((h) => h.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve(activationCommands)); const terminal = TypeMoq.Mock.ofType(); terminal - .setup(t => t.show(TypeMoq.It.isValue(item.preserveFocus))) + .setup((t) => t.show(TypeMoq.It.isValue(item.preserveFocus))) .returns(() => undefined) .verifiable(TypeMoq.Times.exactly(activationCommands.length)); - activationCommands.forEach(cmd => { + activationCommands.forEach((cmd) => { terminal - .setup(t => t.sendText(TypeMoq.It.isValue(cmd))) + .setup((t) => t.sendText(TypeMoq.It.isValue(cmd))) .returns(() => undefined) .verifiable(TypeMoq.Times.exactly(1)); }); diff --git a/src/test/common/terminals/activator/index.unit.test.ts b/src/test/common/terminals/activator/index.unit.test.ts index dd38e8594a06..9ee528b8a25e 100644 --- a/src/test/common/terminals/activator/index.unit.test.ts +++ b/src/test/common/terminals/activator/index.unit.test.ts @@ -31,11 +31,11 @@ suite('Terminal Activator', () => { }); async function testActivationAndHandlers(activationSuccessful: boolean) { baseActivator - .setup(b => b.activateEnvironmentInTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((b) => b.activateEnvironmentInTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(activationSuccessful)) .verifiable(TypeMoq.Times.once()); handler1 - .setup(h => + .setup((h) => h.handleActivation( TypeMoq.It.isAny(), TypeMoq.It.isAny(), @@ -46,7 +46,7 @@ suite('Terminal Activator', () => { .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); handler2 - .setup(h => + .setup((h) => h.handleActivation( TypeMoq.It.isAny(), TypeMoq.It.isAny(), diff --git a/src/test/common/terminals/activator/powerShellFailedHandler.unit.test.ts b/src/test/common/terminals/activator/powerShellFailedHandler.unit.test.ts index 5453fabb3113..0ca229c07dee 100644 --- a/src/test/common/terminals/activator/powerShellFailedHandler.unit.test.ts +++ b/src/test/common/terminals/activator/powerShellFailedHandler.unit.test.ts @@ -29,11 +29,11 @@ suite('Terminal Activation Powershell Failed Handler', () => { shellType: TerminalShellType, cmdPromptHasActivationCommands: boolean ) { - platform.setup(p => p.isWindows).returns(() => isWindows); - helper.setup(p => p.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => shellType); + platform.setup((p) => p.isWindows).returns(() => isWindows); + helper.setup((p) => p.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => shellType); const cmdPromptCommands = cmdPromptHasActivationCommands ? ['a'] : []; helper - .setup(h => + .setup((h) => h.getEnvironmentActivationCommands( TypeMoq.It.isValue(TerminalShellType.commandPrompt), TypeMoq.It.isAny() @@ -42,7 +42,7 @@ suite('Terminal Activation Powershell Failed Handler', () => { .returns(() => Promise.resolve(cmdPromptCommands)); diagnosticService - .setup(d => d.handle(TypeMoq.It.isAny())) + .setup((d) => d.handle(TypeMoq.It.isAny())) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.exactly(mustHandleDiagnostics ? 1 : 0)); await psHandler.handleActivation( @@ -53,11 +53,11 @@ suite('Terminal Activation Powershell Failed Handler', () => { ); } - [true, false].forEach(isWindows => { + [true, false].forEach((isWindows) => { suite(`OS is ${isWindows ? 'Windows' : 'Non-Widows'}`, () => { - getNamesAndValues(TerminalShellType).forEach(shell => { + getNamesAndValues(TerminalShellType).forEach((shell) => { suite(`Shell is ${shell.name}`, () => { - [true, false].forEach(hasCommandPromptActivations => { + [true, false].forEach((hasCommandPromptActivations) => { hasCommandPromptActivations = isWindows && hasCommandPromptActivations && shell.value !== TerminalShellType.commandPrompt; suite( @@ -67,7 +67,7 @@ suite('Terminal Activation Powershell Failed Handler', () => { : "Can't activate with Command Prompt" }`, () => { - [true, false].forEach(activatedSuccessfully => { + [true, false].forEach((activatedSuccessfully) => { suite( `Terminal Activation is ${activatedSuccessfully ? 'successful' : 'has failed'}`, () => { diff --git a/src/test/common/terminals/commandPrompt.unit.test.ts b/src/test/common/terminals/commandPrompt.unit.test.ts index c12189b802d7..2a240967d2b8 100644 --- a/src/test/common/terminals/commandPrompt.unit.test.ts +++ b/src/test/common/terminals/commandPrompt.unit.test.ts @@ -25,7 +25,7 @@ suite('Terminal Command Prompt', () => { test('Getting Path Command Prompt executable (32 on 64Win)', async () => { const env = { windir: 'windir' }; currentProc - .setup(p => p.env) + .setup((p) => p.env) .returns(() => env) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -37,7 +37,7 @@ suite('Terminal Command Prompt', () => { test('Getting Path Command Prompt executable (not 32 on 64Win)', async () => { const env = { PROCESSOR_ARCHITEW6432: 'x', windir: 'windir' }; currentProc - .setup(p => p.env) + .setup((p) => p.env) .returns(() => env) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -49,12 +49,12 @@ suite('Terminal Command Prompt', () => { test('Use command prompt as default shell', async () => { const env = { windir: 'windir' }; currentProc - .setup(p => p.env) + .setup((p) => p.env) .returns(() => env) .verifiable(TypeMoq.Times.atLeastOnce()); const cmdPromptPath = path.join('windir', 'System32', 'cmd.exe'); configService - .setup(c => + .setup((c) => c.updateSectionSetting( TypeMoq.It.isValue('terminal'), TypeMoq.It.isValue('integrated.shell.windows'), diff --git a/src/test/common/terminals/environmentActivationProviders/pipEnvActivationProvider.unit.test.ts b/src/test/common/terminals/environmentActivationProviders/pipEnvActivationProvider.unit.test.ts index cd68674092e4..c320a0fc9f40 100644 --- a/src/test/common/terminals/environmentActivationProviders/pipEnvActivationProvider.unit.test.ts +++ b/src/test/common/terminals/environmentActivationProviders/pipEnvActivationProvider.unit.test.ts @@ -20,7 +20,7 @@ import { InterpreterService } from '../../../../client/interpreter/interpreterSe // tslint:disable:no-any suite('Terminals Activation - Pipenv', () => { - [undefined, Uri.parse('x')].forEach(resource => { + [undefined, Uri.parse('x')].forEach((resource) => { suite(resource ? 'With a resource' : 'Without a resource', () => { let pipenvExecFile = 'pipenv'; let activationProvider: ITerminalActivationCommandProvider; @@ -41,7 +41,7 @@ suite('Terminals Activation - Pipenv', () => { instance(fs) ); - pipenvService.setup(p => p.executable).returns(() => pipenvExecFile); + pipenvService.setup((p) => p.executable).returns(() => pipenvExecFile); }); test('No commands for no interpreter', async () => { @@ -55,7 +55,7 @@ suite('Terminals Activation - Pipenv', () => { }); test('No commands for an interpreter that is not Pipenv', async () => { const nonPipInterpreterTypes = getNamesAndValues(InterpreterType).filter( - t => t.value !== InterpreterType.Pipenv + (t) => t.value !== InterpreterType.Pipenv ); for (const interpreterType of nonPipInterpreterTypes) { when(interpreterService.getActiveInterpreter(resource)).thenResolve({ diff --git a/src/test/common/terminals/factory.unit.test.ts b/src/test/common/terminals/factory.unit.test.ts index a21791da2ff8..1aeee9263f53 100644 --- a/src/test/common/terminals/factory.unit.test.ts +++ b/src/test/common/terminals/factory.unit.test.ts @@ -25,29 +25,29 @@ suite('Terminal Service Factory', () => { const interpreterService = TypeMoq.Mock.ofType(); fs = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) .returns(() => interpreterService.object); disposables = []; serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) .returns(() => disposables); const terminalHelper = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ITerminalHelper), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ITerminalHelper), TypeMoq.It.isAny())) .returns(() => terminalHelper.object); const terminalManager = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ITerminalManager), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ITerminalManager), TypeMoq.It.isAny())) .returns(() => terminalManager.object); factory = new TerminalServiceFactory(serviceContainer.object, fs.object, interpreterService.object); workspaceService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) .returns(() => workspaceService.object); }); teardown(() => { - disposables.forEach(disposable => { + disposables.forEach((disposable) => { if (disposable) { disposable.dispose(); } @@ -109,18 +109,18 @@ suite('Terminal Service Factory', () => { const workspaceUriA = Uri.file('A'); const workspaceUriB = Uri.file('B'); const workspaceFolderA = TypeMoq.Mock.ofType(); - workspaceFolderA.setup(w => w.uri).returns(() => workspaceUriA); + workspaceFolderA.setup((w) => w.uri).returns(() => workspaceUriA); const workspaceFolderB = TypeMoq.Mock.ofType(); - workspaceFolderB.setup(w => w.uri).returns(() => workspaceUriB); + workspaceFolderB.setup((w) => w.uri).returns(() => workspaceUriB); workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(file1A))) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(file1A))) .returns(() => workspaceFolderA.object); workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(file2A))) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(file2A))) .returns(() => workspaceFolderA.object); workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(fileB))) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(fileB))) .returns(() => workspaceFolderB.object); const terminalForFile1A = factory.getTerminalService(file1A) as SynchronousTerminalService; diff --git a/src/test/common/terminals/helper.unit.test.ts b/src/test/common/terminals/helper.unit.test.ts index 2a6e5f1f6797..c5b9dadebb8e 100644 --- a/src/test/common/terminals/helper.unit.test.ts +++ b/src/test/common/terminals/helper.unit.test.ts @@ -111,7 +111,7 @@ suite('Terminal Service helpers', () => { expect(args.name).to.be.deep.equal(theTitle); }); test('Ensure spaces in command is quoted', async () => { - getNamesAndValues(TerminalShellType).forEach(item => { + getNamesAndValues(TerminalShellType).forEach((item) => { const command = 'c:\\python 3.7.exe'; const args = ['1', '2']; const commandPrefix = @@ -126,7 +126,7 @@ suite('Terminal Service helpers', () => { }); test('Ensure empty args are ignored', async () => { - getNamesAndValues(TerminalShellType).forEach(item => { + getNamesAndValues(TerminalShellType).forEach((item) => { const command = 'python3.7.exe'; const args: string[] = []; const commandPrefix = @@ -141,7 +141,7 @@ suite('Terminal Service helpers', () => { }); test('Ensure empty args are ignored with s in command', async () => { - getNamesAndValues(TerminalShellType).forEach(item => { + getNamesAndValues(TerminalShellType).forEach((item) => { const command = 'c:\\python 3.7.exe'; const args: string[] = []; const commandPrefix = @@ -161,7 +161,7 @@ suite('Terminal Service helpers', () => { } suite('Activation', () => { - [undefined, Uri.parse('a')].forEach(resource => { + [undefined, Uri.parse('a')].forEach((resource) => { suite(title(resource), () => { setup(() => { doSetup(); @@ -257,7 +257,7 @@ suite('Terminal Service helpers', () => { ); when(pipenvActivationProvider.isShellSupported(anything())).thenReturn(true); - [bashActivationProvider, cmdActivationProvider, pyenvActivationProvider].forEach(provider => { + [bashActivationProvider, cmdActivationProvider, pyenvActivationProvider].forEach((provider) => { when(provider.getActivationCommands(resource, anything())).thenResolve(['Something']); when(provider.isShellSupported(anything())).thenReturn(true); }); @@ -325,7 +325,7 @@ suite('Terminal Service helpers', () => { verify(pipenvActivationProvider.isShellSupported(anything())).atLeast(1); verify(cmdActivationProvider.isShellSupported(anything())).atLeast(1); }); - [undefined, pythonInterpreter].forEach(interpreter => { + [undefined, pythonInterpreter].forEach((interpreter) => { test('Activation command for Shell must be empty for unknown os', async () => { when(platformService.osType).thenReturn(OSType.Unknown); @@ -339,8 +339,8 @@ suite('Terminal Service helpers', () => { } }); }); - [undefined, pythonInterpreter].forEach(interpreter => { - [OSType.Linux, OSType.OSX, OSType.Windows].forEach(osType => { + [undefined, pythonInterpreter].forEach((interpreter) => { + [OSType.Linux, OSType.OSX, OSType.Windows].forEach((osType) => { test(`Activation command for Shell must never use pipenv nor pyenv (${osType})`, async () => { const pythonPath = 'some python Path value'; const shellToExpect = diff --git a/src/test/common/terminals/pyenvActivationProvider.unit.test.ts b/src/test/common/terminals/pyenvActivationProvider.unit.test.ts index 2cbb3a5c6ec0..505fa95c4723 100644 --- a/src/test/common/terminals/pyenvActivationProvider.unit.test.ts +++ b/src/test/common/terminals/pyenvActivationProvider.unit.test.ts @@ -23,7 +23,7 @@ suite('Terminal Environment Activation pyenv', () => { serviceContainer = TypeMoq.Mock.ofType(); interpreterService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) .returns(() => interpreterService.object); activationProvider = new PyEnvActivationCommandProvider(serviceContainer.object); @@ -37,7 +37,7 @@ suite('Terminal Environment Activation pyenv', () => { test('Ensure no activation commands are returned if intrepreter info is not found', async () => { interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); @@ -55,7 +55,7 @@ suite('Terminal Environment Activation pyenv', () => { type: InterpreterType.Unknown }; interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(intepreterInfo)) .verifiable(TypeMoq.Times.once()); @@ -73,7 +73,7 @@ suite('Terminal Environment Activation pyenv', () => { type: InterpreterType.Pyenv }; interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(intepreterInfo)) .verifiable(TypeMoq.Times.once()); @@ -92,7 +92,7 @@ suite('Terminal Environment Activation pyenv', () => { envName: 'my env name' }; interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(intepreterInfo)) .verifiable(TypeMoq.Times.once()); diff --git a/src/test/common/terminals/service.unit.test.ts b/src/test/common/terminals/service.unit.test.ts index 27615879a909..9a2fc9a19b29 100644 --- a/src/test/common/terminals/service.unit.test.ts +++ b/src/test/common/terminals/service.unit.test.ts @@ -32,124 +32,124 @@ suite('Terminal Service', () => { disposables = []; mockServiceContainer = TypeMoq.Mock.ofType(); - mockServiceContainer.setup(c => c.get(ITerminalManager)).returns(() => terminalManager.object); - mockServiceContainer.setup(c => c.get(ITerminalHelper)).returns(() => terminalHelper.object); - mockServiceContainer.setup(c => c.get(IPlatformService)).returns(() => platformService.object); - mockServiceContainer.setup(c => c.get(IDisposableRegistry)).returns(() => disposables); - mockServiceContainer.setup(c => c.get(IWorkspaceService)).returns(() => workspaceService.object); - mockServiceContainer.setup(c => c.get(ITerminalActivator)).returns(() => terminalActivator.object); + mockServiceContainer.setup((c) => c.get(ITerminalManager)).returns(() => terminalManager.object); + mockServiceContainer.setup((c) => c.get(ITerminalHelper)).returns(() => terminalHelper.object); + mockServiceContainer.setup((c) => c.get(IPlatformService)).returns(() => platformService.object); + mockServiceContainer.setup((c) => c.get(IDisposableRegistry)).returns(() => disposables); + mockServiceContainer.setup((c) => c.get(IWorkspaceService)).returns(() => workspaceService.object); + mockServiceContainer.setup((c) => c.get(ITerminalActivator)).returns(() => terminalActivator.object); }); teardown(() => { if (service) { // tslint:disable-next-line:no-any service.dispose(); } - disposables.filter(item => !!item).forEach(item => item.dispose()); + disposables.filter((item) => !!item).forEach((item) => item.dispose()); }); test('Ensure terminal is disposed', async () => { terminalHelper - .setup(helper => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((helper) => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); const os: string = 'windows'; service = new TerminalService(mockServiceContainer.object); const shellPath = 'powershell.exe'; workspaceService - .setup(w => w.getConfiguration(TypeMoq.It.isValue('terminal.integrated.shell'))) + .setup((w) => w.getConfiguration(TypeMoq.It.isValue('terminal.integrated.shell'))) .returns(() => { const workspaceConfig = TypeMoq.Mock.ofType(); - workspaceConfig.setup(c => c.get(os)).returns(() => shellPath); + workspaceConfig.setup((c) => c.get(os)).returns(() => shellPath); return workspaceConfig.object; }); - platformService.setup(p => p.isWindows).returns(() => os === 'windows'); - platformService.setup(p => p.isLinux).returns(() => os === 'linux'); - platformService.setup(p => p.isMac).returns(() => os === 'osx'); - terminalManager.setup(t => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); + platformService.setup((p) => p.isWindows).returns(() => os === 'windows'); + platformService.setup((p) => p.isLinux).returns(() => os === 'linux'); + platformService.setup((p) => p.isMac).returns(() => os === 'osx'); + terminalManager.setup((t) => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); terminalHelper - .setup(h => h.buildCommandForTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((h) => h.buildCommandForTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => 'dummy text'); // Sending a command will cause the terminal to be created await service.sendCommand('', []); - terminal.verify(t => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.exactly(2)); + terminal.verify((t) => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.exactly(2)); service.dispose(); - terminal.verify(t => t.dispose(), TypeMoq.Times.exactly(1)); + terminal.verify((t) => t.dispose(), TypeMoq.Times.exactly(1)); }); test('Ensure command is sent to terminal and it is shown', async () => { terminalHelper - .setup(helper => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((helper) => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); service = new TerminalService(mockServiceContainer.object); const commandToSend = 'SomeCommand'; const args = ['1', '2']; const commandToExpect = [commandToSend].concat(args).join(' '); terminalHelper - .setup(h => h.buildCommandForTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((h) => h.buildCommandForTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => commandToExpect); - terminalHelper.setup(h => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); - terminalManager.setup(t => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); + terminalHelper.setup((h) => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); + terminalManager.setup((t) => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); await service.sendCommand(commandToSend, args); - terminal.verify(t => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.exactly(2)); + terminal.verify((t) => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.exactly(2)); terminal.verify( - t => t.sendText(TypeMoq.It.isValue(commandToExpect), TypeMoq.It.isValue(true)), + (t) => t.sendText(TypeMoq.It.isValue(commandToExpect), TypeMoq.It.isValue(true)), TypeMoq.Times.exactly(1) ); }); test('Ensure text is sent to terminal and it is shown', async () => { terminalHelper - .setup(helper => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((helper) => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); service = new TerminalService(mockServiceContainer.object); const textToSend = 'Some Text'; - terminalHelper.setup(h => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); - terminalManager.setup(t => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); + terminalHelper.setup((h) => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); + terminalManager.setup((t) => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); await service.sendText(textToSend); - terminal.verify(t => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.exactly(2)); - terminal.verify(t => t.sendText(TypeMoq.It.isValue(textToSend)), TypeMoq.Times.exactly(1)); + terminal.verify((t) => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.exactly(2)); + terminal.verify((t) => t.sendText(TypeMoq.It.isValue(textToSend)), TypeMoq.Times.exactly(1)); }); test('Ensure terminal shown', async () => { terminalHelper - .setup(helper => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((helper) => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); service = new TerminalService(mockServiceContainer.object); - terminalHelper.setup(h => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); - terminalManager.setup(t => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); + terminalHelper.setup((h) => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); + terminalManager.setup((t) => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); await service.show(); - terminal.verify(t => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.exactly(2)); + terminal.verify((t) => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.exactly(2)); }); test('Ensure terminal shown and focus is set to the Terminal', async () => { terminalHelper - .setup(helper => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((helper) => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); service = new TerminalService(mockServiceContainer.object); - terminalHelper.setup(h => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); - terminalManager.setup(t => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); + terminalHelper.setup((h) => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); + terminalManager.setup((t) => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); await service.show(false); - terminal.verify(t => t.show(TypeMoq.It.isValue(false)), TypeMoq.Times.exactly(2)); + terminal.verify((t) => t.show(TypeMoq.It.isValue(false)), TypeMoq.Times.exactly(2)); }); test('Ensure terminal is activated once after creation', async () => { service = new TerminalService(mockServiceContainer.object); terminalActivator - .setup(h => h.activateEnvironmentInTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((h) => h.activateEnvironmentInTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); terminalManager - .setup(t => t.createTerminal(TypeMoq.It.isAny())) + .setup((t) => t.createTerminal(TypeMoq.It.isAny())) .returns(() => terminal.object) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -160,18 +160,18 @@ suite('Terminal Service', () => { terminalHelper.verifyAll(); terminalActivator.verifyAll(); - terminal.verify(t => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.atLeastOnce()); + terminal.verify((t) => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.atLeastOnce()); }); test('Ensure terminal is activated once before sending text', async () => { service = new TerminalService(mockServiceContainer.object); const textToSend = 'Some Text'; terminalActivator - .setup(h => h.activateEnvironmentInTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((h) => h.activateEnvironmentInTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); terminalManager - .setup(t => t.createTerminal(TypeMoq.It.isAny())) + .setup((t) => t.createTerminal(TypeMoq.It.isAny())) .returns(() => terminal.object) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -182,26 +182,26 @@ suite('Terminal Service', () => { terminalHelper.verifyAll(); terminalActivator.verifyAll(); - terminal.verify(t => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.atLeastOnce()); + terminal.verify((t) => t.show(TypeMoq.It.isValue(true)), TypeMoq.Times.atLeastOnce()); }); test('Ensure close event is not fired when another terminal is closed', async () => { terminalHelper - .setup(helper => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((helper) => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); let eventFired = false; let eventHandler: undefined | (() => void); terminalManager - .setup(m => m.onDidCloseTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(handler => { + .setup((m) => m.onDidCloseTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((handler) => { eventHandler = handler; // tslint:disable-next-line:no-empty return { dispose: () => {} }; }); service = new TerminalService(mockServiceContainer.object); service.onDidCloseTerminal(() => (eventFired = true), service); - terminalHelper.setup(h => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); - terminalManager.setup(t => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); + terminalHelper.setup((h) => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); + terminalManager.setup((t) => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); // This will create the terminal. await service.sendText('blah'); @@ -213,13 +213,13 @@ suite('Terminal Service', () => { test('Ensure close event is not fired when terminal is closed', async () => { terminalHelper - .setup(helper => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((helper) => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); let eventFired = false; let eventHandler: undefined | ((t: VSCodeTerminal) => void); terminalManager - .setup(m => m.onDidCloseTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(handler => { + .setup((m) => m.onDidCloseTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((handler) => { eventHandler = handler; // tslint:disable-next-line:no-empty return { dispose: () => {} }; @@ -227,8 +227,8 @@ suite('Terminal Service', () => { service = new TerminalService(mockServiceContainer.object); service.onDidCloseTerminal(() => (eventFired = true)); - terminalHelper.setup(h => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); - terminalManager.setup(t => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); + terminalHelper.setup((h) => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); + terminalManager.setup((t) => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); // This will create the terminal. await service.sendText('blah'); diff --git a/src/test/common/terminals/serviceRegistry.unit.test.ts b/src/test/common/terminals/serviceRegistry.unit.test.ts index 2abc41b61bb1..8bc7e1670016 100644 --- a/src/test/common/terminals/serviceRegistry.unit.test.ts +++ b/src/test/common/terminals/serviceRegistry.unit.test.ts @@ -1,64 +1,64 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { instance, mock, verify } from 'ts-mockito'; -import { IExtensionSingleActivationService } from '../../../client/activation/types'; -import { ServiceManager } from '../../../client/ioc/serviceManager'; -import { IServiceManager } from '../../../client/ioc/types'; -import { ExtensionActivationForTerminalActivation, TerminalAutoActivation } from '../../../client/terminals/activation'; -import { CodeExecutionManager } from '../../../client/terminals/codeExecution/codeExecutionManager'; -import { DjangoShellCodeExecutionProvider } from '../../../client/terminals/codeExecution/djangoShellCodeExecution'; -import { CodeExecutionHelper } from '../../../client/terminals/codeExecution/helper'; -import { ReplProvider } from '../../../client/terminals/codeExecution/repl'; -import { TerminalCodeExecutionProvider } from '../../../client/terminals/codeExecution/terminalCodeExecution'; -import { registerTypes } from '../../../client/terminals/serviceRegistry'; -import { - ICodeExecutionHelper, - ICodeExecutionManager, - ICodeExecutionService, - ITerminalAutoActivation -} from '../../../client/terminals/types'; - -suite('Common Terminal Service Registry', () => { - let serviceManager: IServiceManager; - - setup(() => { - serviceManager = mock(ServiceManager); - }); - - test('Ensure services are registered', async () => { - registerTypes(instance(serviceManager)); - verify(serviceManager.addSingleton(ICodeExecutionHelper, CodeExecutionHelper)).once(); - - verify(serviceManager.addSingleton(ICodeExecutionManager, CodeExecutionManager)).once(); - - verify( - serviceManager.addSingleton( - ICodeExecutionService, - DjangoShellCodeExecutionProvider, - 'djangoShell' - ) - ).once(); - verify( - serviceManager.addSingleton( - ICodeExecutionService, - TerminalCodeExecutionProvider, - 'standard' - ) - ).once(); - verify(serviceManager.addSingleton(ICodeExecutionService, ReplProvider, 'repl')).once(); - - verify( - serviceManager.addSingleton(ITerminalAutoActivation, TerminalAutoActivation) - ).once(); - - verify( - serviceManager.addSingleton( - IExtensionSingleActivationService, - ExtensionActivationForTerminalActivation - ) - ).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { instance, mock, verify } from 'ts-mockito'; +import { IExtensionSingleActivationService } from '../../../client/activation/types'; +import { ServiceManager } from '../../../client/ioc/serviceManager'; +import { IServiceManager } from '../../../client/ioc/types'; +import { ExtensionActivationForTerminalActivation, TerminalAutoActivation } from '../../../client/terminals/activation'; +import { CodeExecutionManager } from '../../../client/terminals/codeExecution/codeExecutionManager'; +import { DjangoShellCodeExecutionProvider } from '../../../client/terminals/codeExecution/djangoShellCodeExecution'; +import { CodeExecutionHelper } from '../../../client/terminals/codeExecution/helper'; +import { ReplProvider } from '../../../client/terminals/codeExecution/repl'; +import { TerminalCodeExecutionProvider } from '../../../client/terminals/codeExecution/terminalCodeExecution'; +import { registerTypes } from '../../../client/terminals/serviceRegistry'; +import { + ICodeExecutionHelper, + ICodeExecutionManager, + ICodeExecutionService, + ITerminalAutoActivation +} from '../../../client/terminals/types'; + +suite('Common Terminal Service Registry', () => { + let serviceManager: IServiceManager; + + setup(() => { + serviceManager = mock(ServiceManager); + }); + + test('Ensure services are registered', async () => { + registerTypes(instance(serviceManager)); + verify(serviceManager.addSingleton(ICodeExecutionHelper, CodeExecutionHelper)).once(); + + verify(serviceManager.addSingleton(ICodeExecutionManager, CodeExecutionManager)).once(); + + verify( + serviceManager.addSingleton( + ICodeExecutionService, + DjangoShellCodeExecutionProvider, + 'djangoShell' + ) + ).once(); + verify( + serviceManager.addSingleton( + ICodeExecutionService, + TerminalCodeExecutionProvider, + 'standard' + ) + ).once(); + verify(serviceManager.addSingleton(ICodeExecutionService, ReplProvider, 'repl')).once(); + + verify( + serviceManager.addSingleton(ITerminalAutoActivation, TerminalAutoActivation) + ).once(); + + verify( + serviceManager.addSingleton( + IExtensionSingleActivationService, + ExtensionActivationForTerminalActivation + ) + ).once(); + }); +}); diff --git a/src/test/common/terminals/shellDetector.unit.test.ts b/src/test/common/terminals/shellDetector.unit.test.ts index 88998c4d6033..8afa2b18ba4a 100644 --- a/src/test/common/terminals/shellDetector.unit.test.ts +++ b/src/test/common/terminals/shellDetector.unit.test.ts @@ -34,7 +34,7 @@ suite('Shell Detector', () => { setup(() => (platformService = mock(PlatformService))); teardown(() => sandbox.restore()); - getNamesAndValues(OSType).forEach(os => { + getNamesAndValues(OSType).forEach((os) => { const testSuffix = `(OS ${os.name})`; test('Test identification of Terminal Shells in order of priority', async () => { const callOrder: string[] = []; diff --git a/src/test/common/terminals/shellDetectors/shellDetectors.unit.test.ts b/src/test/common/terminals/shellDetectors/shellDetectors.unit.test.ts index 0c476e9777c7..7abd0c28d74e 100644 --- a/src/test/common/terminals/shellDetectors/shellDetectors.unit.test.ts +++ b/src/test/common/terminals/shellDetectors/shellDetectors.unit.test.ts @@ -120,7 +120,7 @@ suite('Shell Detectors', () => { ); }); }); - getNamesAndValues(OSType).forEach(os => { + getNamesAndValues(OSType).forEach((os) => { test(`Get shell path from settings (OS ${os.name})`, async () => { const shellPathInSettings = 'some value'; const shellDetector = new SettingsShellDetector(instance(workspaceService), instance(platformService)); @@ -190,7 +190,7 @@ suite('Shell Detectors', () => { expect(shellPath).to.equal('hello.exe'); }); - [OSType.OSX, OSType.Linux].forEach(osType => { + [OSType.OSX, OSType.Linux].forEach((osType) => { test(`Default shell on ${osType} is /bin/bash`, () => { const shellDetector = new UserEnvironmentShellDetector(instance(currentProcess), instance(platformService)); when(platformService.osType).thenReturn(OSType.OSX); diff --git a/src/test/common/terminals/synchronousTerminalService.unit.test.ts b/src/test/common/terminals/synchronousTerminalService.unit.test.ts index 90dccc1d0653..8fe19073f779 100644 --- a/src/test/common/terminals/synchronousTerminalService.unit.test.ts +++ b/src/test/common/terminals/synchronousTerminalService.unit.test.ts @@ -83,7 +83,7 @@ suite('Terminal Service (synchronous)', () => { when(fs.readFile(anything())).thenResolve(''); // Send the necessary commands to the terminal. - const promise = service.sendCommand('cmd', ['1', '2'], cancel.token).catch(ex => Promise.reject(ex)); + const promise = service.sendCommand('cmd', ['1', '2'], cancel.token).catch((ex) => Promise.reject(ex)); const deferred = createDeferredFrom(promise); // required to shutup node (we must handled exceptions). @@ -125,7 +125,7 @@ suite('Terminal Service (synchronous)', () => { when(fs.readFile(anything())).thenResolve(''); // Send the necessary commands to the terminal. - const promise = service.sendCommand('cmd', ['1', '2'], cancel.token).catch(ex => Promise.reject(ex)); + const promise = service.sendCommand('cmd', ['1', '2'], cancel.token).catch((ex) => Promise.reject(ex)); const deferred = createDeferredFrom(promise); // required to shutup node (we must handled exceptions). diff --git a/src/test/common/utils/async.unit.test.ts b/src/test/common/utils/async.unit.test.ts index aedefc790bfb..e3e03223fca0 100644 --- a/src/test/common/utils/async.unit.test.ts +++ b/src/test/common/utils/async.unit.test.ts @@ -7,11 +7,11 @@ import * as assert from 'assert'; import { createDeferred } from '../../../client/common/utils/async'; suite('Deferred', () => { - test('Resolve', done => { + test('Resolve', (done) => { const valueToSent = new Date().getTime(); const def = createDeferred(); def.promise - .then(value => { + .then((value) => { assert.equal(value, valueToSent); assert.equal(def.resolved, true, 'resolved property value is not `true`'); }) @@ -28,15 +28,15 @@ suite('Deferred', () => { assert.equal(def.rejected, false, 'Promise is rejected even when it should not be'); assert.equal(def.completed, true, 'Promise is not completed even when it should not be'); }); - test('Reject', done => { + test('Reject', (done) => { const errorToSend = new Error('Something'); const def = createDeferred(); def.promise - .then(value => { + .then((value) => { assert.fail(value, 'Error', 'Was expecting promise to get rejected, however it was resolved', ''); done(); }) - .catch(reason => { + .catch((reason) => { assert.equal(reason, errorToSend, 'Error received is not the same'); done(); }) diff --git a/src/test/common/utils/cacheUtils.unit.test.ts b/src/test/common/utils/cacheUtils.unit.test.ts index 6c9c6a79417a..b68e3164c7b2 100644 --- a/src/test/common/utils/cacheUtils.unit.test.ts +++ b/src/test/common/utils/cacheUtils.unit.test.ts @@ -260,7 +260,7 @@ suite('Common Utils - CacheUtils', () => { { index: 0, name: '1', uri: Uri.parse('wkfolder1') }, { index: 1, name: '2', uri: Uri.parse('wkfolder2') } ]; - vsc.workspace.getWorkspaceFolder = res => { + vsc.workspace.getWorkspaceFolder = (res) => { const index = res.fsPath === resource.fsPath ? 0 : 1; return vsc.workspace.workspaceFolders![index]; }; diff --git a/src/test/common/utils/decorators.unit.test.ts b/src/test/common/utils/decorators.unit.test.ts index 7ca60bdeac34..f4a2a60cb199 100644 --- a/src/test/common/utils/decorators.unit.test.ts +++ b/src/test/common/utils/decorators.unit.test.ts @@ -18,7 +18,7 @@ import { sleep } from '../../core'; use(chaiPromise); // tslint:disable:no-any max-func-body-length no-unnecessary-class -suite('Common Utils - Decorators', function() { +suite('Common Utils - Decorators', function () { // For some reason, sometimes we have timeouts on CI. // Note: setTimeout and similar functions are not guaranteed to execute // at the precise time prescribed. @@ -306,7 +306,7 @@ suite('Common Utils - Decorators', function() { const start = getHighPrecisionTime(); let capturedEx: Error | undefined; - await one.run().catch(ex => (capturedEx = ex)); + await one.run().catch((ex) => (capturedEx = ex)); await waitForCalls(one.timestamps, 1); const delay = one.timestamps[0] - start; @@ -340,7 +340,7 @@ suite('Common Utils - Decorators', function() { expect(one.timestamps).to.have.lengthOf(one.calls.length); expect(errored).to.be.equal(false, "Exception raised when there shouldn't have been any"); }); - test('Debounce: multiple async calls when awaiting on all', async function() { + test('Debounce: multiple async calls when awaiting on all', async function () { const wait = 100; // tslint:disable-next-line:max-classes-per-file class One extends Base { diff --git a/src/test/common/utils/localize.functional.test.ts b/src/test/common/utils/localize.functional.test.ts index 70d13f784cef..064c2cc68987 100644 --- a/src/test/common/utils/localize.functional.test.ts +++ b/src/test/common/utils/localize.functional.test.ts @@ -1,227 +1,227 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -// tslint:disable:max-func-body-length - -import * as assert from 'assert'; -import * as fs from 'fs'; -import * as path from 'path'; -import { EXTENSION_ROOT_DIR } from '../../../client/common/constants'; -import * as localize from '../../../client/common/utils/localize'; - -const defaultNLSFile = path.join(EXTENSION_ROOT_DIR, 'package.nls.json'); - -// Defines a Mocha test suite to group tests of similar kind together -suite('Localization', () => { - // Note: We use package.nls.json by default for tests. Use the - // setLocale() helper to switch to a different locale. - - let localeFiles: string[]; - let nls_orig: string | undefined; - - setup(() => { - localeFiles = []; - - nls_orig = process.env.VSCODE_NLS_CONFIG; - setLocale('en-us'); - - // Ensure each test starts fresh. - localize._resetCollections(); - }); - - teardown(() => { - if (nls_orig) { - process.env.VSCODE_NLS_CONFIG = nls_orig; - } else { - delete process.env.VSCODE_NLS_CONFIG; - } - - const filenames = localeFiles; - localeFiles = []; - for (const filename of filenames) { - fs.unlinkSync(filename); - } - }); - - function addLocale(locale: string, nls: Record) { - const filename = addLocaleFile(locale, nls); - localeFiles.push(filename); - } - - test('keys', done => { - const val = localize.LanguageService.bannerMessage(); - assert.equal( - val, - 'Can you please take 2 minutes to tell us how the Python Language Server is working for you?', - 'LanguageService string doesnt match' - ); - done(); - }); - - test('keys italian', done => { - // Force a config change - setLocale('it'); - - const val = localize.LanguageService.bannerLabelYes(); - assert.equal(val, 'Sì, prenderò il sondaggio ora', 'bannerLabelYes is not being translated'); - done(); - }); - - test('key found for locale', done => { - addLocale('spam', { - 'debug.selectConfigurationTitle': '???', - 'Common.gotIt': '!!!' - }); - setLocale('spam'); - - const title = localize.DebugConfigStrings.selectConfiguration.title(); - const gotIt = localize.Common.gotIt(); - - assert.equal(title, '???', 'not used'); - assert.equal(gotIt, '!!!', 'not used'); - done(); - }); - - test('key not found for locale (default used)', done => { - addLocale('spam', { - 'debug.selectConfigurationTitle': '???' - }); - setLocale('spam'); - - const gotIt = localize.Common.gotIt(); - - assert.equal(gotIt, 'Got it!', `default not used (got ${gotIt})`); - done(); - }); - - test('keys exist', done => { - // Read in the JSON object for the package.nls.json - const nlsCollection = getDefaultCollection(); - - // Now match all of our namespace entries to our nls entries - useEveryLocalization(localize); - - // Now verify all of the asked for keys exist - const askedFor = localize._getAskedForCollection(); - const missing: Record = {}; - Object.keys(askedFor).forEach((key: string) => { - // Now check that this key exists somewhere in the nls collection - if (!nlsCollection[key]) { - missing[key] = askedFor[key]; - } - }); - - // If any missing keys, output an error - const missingKeys = Object.keys(missing); - if (missingKeys && missingKeys.length > 0) { - let message = 'Missing keys. Add the following to package.nls.json:\n'; - missingKeys.forEach((k: string) => { - message = message.concat(`\t"${k}" : "${missing[k]}",\n`); - }); - assert.fail(message); - } - - done(); - }); - - test('all keys used', function(done) { - // tslint:disable-next-line:no-suspicious-comment - // TODO: Unused keys need to be cleaned up. - // tslint:disable-next-line:no-invalid-this - this.skip(); - //test('all keys used', done => { - const nlsCollection = getDefaultCollection(); - useEveryLocalization(localize); - - // Now verify all of the asked for keys exist - const askedFor = localize._getAskedForCollection(); - const extra: Record = {}; - Object.keys(nlsCollection).forEach((key: string) => { - // Now check that this key exists somewhere in the nls collection - if (askedFor[key]) { - return; - } - if (key.toLowerCase().indexOf('datascience') >= 0) { - return; - } - extra[key] = nlsCollection[key]; - }); - - // If any missing keys, output an error - const extraKeys = Object.keys(extra); - if (extraKeys && extraKeys.length > 0) { - let message = 'Unused keys. Remove the following from package.nls.json:\n'; - extraKeys.forEach((k: string) => { - message = message.concat(`\t"${k}" : "${extra[k]}",\n`); - }); - assert.fail(message); - } - - done(); - }); -}); - -function addLocaleFile(locale: string, nls: Record) { - const filename = path.join(EXTENSION_ROOT_DIR, `package.nls.${locale}.json`); - if (fs.existsSync(filename)) { - throw Error(`NLS file ${filename} already exists`); - } - const contents = JSON.stringify(nls); - fs.writeFileSync(filename, contents); - return filename; -} - -function setLocale(locale: string) { - let nls: Record; - if (process.env.VSCODE_NLS_CONFIG) { - nls = JSON.parse(process.env.VSCODE_NLS_CONFIG); - nls.locale = locale; - } else { - nls = { locale: locale }; - } - process.env.VSCODE_NLS_CONFIG = JSON.stringify(nls); -} - -function getDefaultCollection() { - if (!fs.existsSync(defaultNLSFile)) { - throw Error('package.nls.json is missing'); - } - const contents = fs.readFileSync(defaultNLSFile, 'utf8'); - return JSON.parse(contents); -} - -// tslint:disable-next-line:no-any -function useEveryLocalization(topns: any) { - // Read all of the namespaces from the localize import. - const entries = Object.keys(topns); - - // Now match all of our namespace entries to our nls entries. - entries.forEach((e: string) => { - // @ts-ignore - if (typeof topns[e] === 'function') { - return; - } - // It must be a namespace. - useEveryLocalizationInNS(topns[e]); - }); -} - -// tslint:disable-next-line:no-any -function useEveryLocalizationInNS(ns: any) { - // The namespace should have functions inside of it. - // @ts-ignore - const props = Object.keys(ns); - - // Run every function and cover every sub-namespace. - // This should fill up our "asked-for keys" collection. - props.forEach((key: string) => { - if (typeof ns[key] === 'function') { - const func = ns[key]; - func(); - } else { - useEveryLocalizationInNS(ns[key]); - } - }); -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// tslint:disable:max-func-body-length + +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as path from 'path'; +import { EXTENSION_ROOT_DIR } from '../../../client/common/constants'; +import * as localize from '../../../client/common/utils/localize'; + +const defaultNLSFile = path.join(EXTENSION_ROOT_DIR, 'package.nls.json'); + +// Defines a Mocha test suite to group tests of similar kind together +suite('Localization', () => { + // Note: We use package.nls.json by default for tests. Use the + // setLocale() helper to switch to a different locale. + + let localeFiles: string[]; + let nls_orig: string | undefined; + + setup(() => { + localeFiles = []; + + nls_orig = process.env.VSCODE_NLS_CONFIG; + setLocale('en-us'); + + // Ensure each test starts fresh. + localize._resetCollections(); + }); + + teardown(() => { + if (nls_orig) { + process.env.VSCODE_NLS_CONFIG = nls_orig; + } else { + delete process.env.VSCODE_NLS_CONFIG; + } + + const filenames = localeFiles; + localeFiles = []; + for (const filename of filenames) { + fs.unlinkSync(filename); + } + }); + + function addLocale(locale: string, nls: Record) { + const filename = addLocaleFile(locale, nls); + localeFiles.push(filename); + } + + test('keys', (done) => { + const val = localize.LanguageService.bannerMessage(); + assert.equal( + val, + 'Can you please take 2 minutes to tell us how the Python Language Server is working for you?', + 'LanguageService string doesnt match' + ); + done(); + }); + + test('keys italian', (done) => { + // Force a config change + setLocale('it'); + + const val = localize.LanguageService.bannerLabelYes(); + assert.equal(val, 'Sì, prenderò il sondaggio ora', 'bannerLabelYes is not being translated'); + done(); + }); + + test('key found for locale', (done) => { + addLocale('spam', { + 'debug.selectConfigurationTitle': '???', + 'Common.gotIt': '!!!' + }); + setLocale('spam'); + + const title = localize.DebugConfigStrings.selectConfiguration.title(); + const gotIt = localize.Common.gotIt(); + + assert.equal(title, '???', 'not used'); + assert.equal(gotIt, '!!!', 'not used'); + done(); + }); + + test('key not found for locale (default used)', (done) => { + addLocale('spam', { + 'debug.selectConfigurationTitle': '???' + }); + setLocale('spam'); + + const gotIt = localize.Common.gotIt(); + + assert.equal(gotIt, 'Got it!', `default not used (got ${gotIt})`); + done(); + }); + + test('keys exist', (done) => { + // Read in the JSON object for the package.nls.json + const nlsCollection = getDefaultCollection(); + + // Now match all of our namespace entries to our nls entries + useEveryLocalization(localize); + + // Now verify all of the asked for keys exist + const askedFor = localize._getAskedForCollection(); + const missing: Record = {}; + Object.keys(askedFor).forEach((key: string) => { + // Now check that this key exists somewhere in the nls collection + if (!nlsCollection[key]) { + missing[key] = askedFor[key]; + } + }); + + // If any missing keys, output an error + const missingKeys = Object.keys(missing); + if (missingKeys && missingKeys.length > 0) { + let message = 'Missing keys. Add the following to package.nls.json:\n'; + missingKeys.forEach((k: string) => { + message = message.concat(`\t"${k}" : "${missing[k]}",\n`); + }); + assert.fail(message); + } + + done(); + }); + + test('all keys used', function (done) { + // tslint:disable-next-line:no-suspicious-comment + // TODO: Unused keys need to be cleaned up. + // tslint:disable-next-line:no-invalid-this + this.skip(); + //test('all keys used', done => { + const nlsCollection = getDefaultCollection(); + useEveryLocalization(localize); + + // Now verify all of the asked for keys exist + const askedFor = localize._getAskedForCollection(); + const extra: Record = {}; + Object.keys(nlsCollection).forEach((key: string) => { + // Now check that this key exists somewhere in the nls collection + if (askedFor[key]) { + return; + } + if (key.toLowerCase().indexOf('datascience') >= 0) { + return; + } + extra[key] = nlsCollection[key]; + }); + + // If any missing keys, output an error + const extraKeys = Object.keys(extra); + if (extraKeys && extraKeys.length > 0) { + let message = 'Unused keys. Remove the following from package.nls.json:\n'; + extraKeys.forEach((k: string) => { + message = message.concat(`\t"${k}" : "${extra[k]}",\n`); + }); + assert.fail(message); + } + + done(); + }); +}); + +function addLocaleFile(locale: string, nls: Record) { + const filename = path.join(EXTENSION_ROOT_DIR, `package.nls.${locale}.json`); + if (fs.existsSync(filename)) { + throw Error(`NLS file ${filename} already exists`); + } + const contents = JSON.stringify(nls); + fs.writeFileSync(filename, contents); + return filename; +} + +function setLocale(locale: string) { + let nls: Record; + if (process.env.VSCODE_NLS_CONFIG) { + nls = JSON.parse(process.env.VSCODE_NLS_CONFIG); + nls.locale = locale; + } else { + nls = { locale: locale }; + } + process.env.VSCODE_NLS_CONFIG = JSON.stringify(nls); +} + +function getDefaultCollection() { + if (!fs.existsSync(defaultNLSFile)) { + throw Error('package.nls.json is missing'); + } + const contents = fs.readFileSync(defaultNLSFile, 'utf8'); + return JSON.parse(contents); +} + +// tslint:disable-next-line:no-any +function useEveryLocalization(topns: any) { + // Read all of the namespaces from the localize import. + const entries = Object.keys(topns); + + // Now match all of our namespace entries to our nls entries. + entries.forEach((e: string) => { + // @ts-ignore + if (typeof topns[e] === 'function') { + return; + } + // It must be a namespace. + useEveryLocalizationInNS(topns[e]); + }); +} + +// tslint:disable-next-line:no-any +function useEveryLocalizationInNS(ns: any) { + // The namespace should have functions inside of it. + // @ts-ignore + const props = Object.keys(ns); + + // Run every function and cover every sub-namespace. + // This should fill up our "asked-for keys" collection. + props.forEach((key: string) => { + if (typeof ns[key] === 'function') { + const func = ns[key]; + func(); + } else { + useEveryLocalizationInNS(ns[key]); + } + }); +} diff --git a/src/test/common/variables/envVarsProvider.multiroot.test.ts b/src/test/common/variables/envVarsProvider.multiroot.test.ts index babe6507adb9..362285a9cd36 100644 --- a/src/test/common/variables/envVarsProvider.multiroot.test.ts +++ b/src/test/common/variables/envVarsProvider.multiroot.test.ts @@ -39,7 +39,7 @@ const workspace4PyFile = Uri.file(path.join(workspace4Path.fsPath, 'one.py')); suite('Multiroot Environment Variables Provider', () => { let ioc: UnitTestIocContainer; const pathVariableName = IS_WINDOWS ? WINDOWS_PATH_VARIABLE_NAME : NON_WINDOWS_PATH_VARIABLE_NAME; - suiteSetup(async function() { + suiteSetup(async function () { if (!IS_MULTI_ROOT_TEST) { // tslint:disable-next-line:no-invalid-this return this.skip(); @@ -131,7 +131,7 @@ suite('Multiroot Environment Variables Provider', () => { expect(vars).to.have.property('X1234PYEXTUNITTESTVAR', '1234', 'X1234PYEXTUNITTESTVAR value is invalid'); expect(vars).to.have.property('PYTHONPATH', '../workspace5', 'PYTHONPATH value is invalid'); - Object.keys(processVariables).forEach(variable => { + Object.keys(processVariables).forEach((variable) => { expect(vars).to.have.property(variable); // On CI, it was seen that processVariable[variable] can contain spaces at the end, which causes tests to fail. So trim the strings before comparing. expect(vars[variable]?.trim()).to.equal( @@ -189,7 +189,7 @@ suite('Multiroot Environment Variables Provider', () => { expect(vars).to.have.property(pathVariableName, processVariables[pathVariableName], 'PATH value is invalid'); }); - test('PATH from process variables should be included in in variables returned', async function() { + test('PATH from process variables should be included in in variables returned', async function () { // this test is flaky on windows (likely the value of the path property // has incorrect path separator chars). Tracked by GH #4756 if (isOs(OSType.Windows)) { @@ -336,7 +336,7 @@ suite('Multiroot Environment Variables Provider', () => { await settings.update('envFile', '${workspaceRoot}/.env2', ConfigurationTarget.WorkspaceFolder); // Wait for settings to get refreshed. - await new Promise(resolve => setTimeout(resolve, 5000)); + await new Promise((resolve) => setTimeout(resolve, 5000)); const newVars = await envProvider.getEnvironmentVariables(workspace4PyFile); expect(newVars).to.not.equal(undefined, 'Variables is is undefiend'); diff --git a/src/test/common/variables/envVarsService.unit.test.ts b/src/test/common/variables/envVarsService.unit.test.ts index fdf346ef4aa6..0e1ae863478d 100644 --- a/src/test/common/variables/envVarsService.unit.test.ts +++ b/src/test/common/variables/envVarsService.unit.test.ts @@ -40,9 +40,9 @@ suite('Environment Variables Service', () => { fs.verifyAll(); } function setFile(fileName: string, text: string) { - fs.setup(f => f.fileExists(fileName)) // Handle the specific file. + fs.setup((f) => f.fileExists(fileName)) // Handle the specific file. .returns(() => Promise.resolve(true)); // The file exists. - fs.setup(f => f.readFile(fileName)) // Handle the specific file. + fs.setup((f) => f.readFile(fileName)) // Handle the specific file. .returns(() => Promise.resolve(text)); // Pretend to read from the file. } @@ -55,7 +55,7 @@ suite('Environment Variables Service', () => { }); test('Custom variables should be undefined with non-existent files', async () => { - fs.setup(f => f.fileExists(filename)) // Handle the specific file. + fs.setup((f) => f.fileExists(filename)) // Handle the specific file. .returns(() => Promise.resolve(false)); // The file is missing. const vars = await variablesService.parseFile(filename); @@ -66,7 +66,7 @@ suite('Environment Variables Service', () => { test('Custom variables should be undefined when folder name is passed instead of a file name', async () => { const dirname = 'x/y/z'; - fs.setup(f => f.fileExists(dirname)) // Handle the specific "file". + fs.setup((f) => f.fileExists(dirname)) // Handle the specific "file". .returns(() => Promise.resolve(false)); // It isn't a "regular" file. const vars = await variablesService.parseFile(dirname); @@ -159,11 +159,11 @@ PYTHON=${BINDIR}/python3\n\ }); }); - PATHS.map(pathVariable => { + PATHS.map((pathVariable) => { suite(`mergeVariables() (path var: ${pathVariable})`, () => { setup(() => { pathUtils - .setup(pu => pu.getPathVariableName()) // This always gets called. + .setup((pu) => pu.getPathVariableName()) // This always gets called. .returns(() => pathVariable as PathVar); // Pretend we're on a specific platform. }); @@ -217,11 +217,11 @@ PYTHON=${BINDIR}/python3\n\ }); }); - PATHS.map(pathVariable => { + PATHS.map((pathVariable) => { suite(`appendPath() (path var: ${pathVariable})`, () => { setup(() => { pathUtils - .setup(pu => pu.getPathVariableName()) // This always gets called. + .setup((pu) => pu.getPathVariableName()) // This always gets called. .returns(() => pathVariable as PathVar); // Pretend we're on a specific platform. }); diff --git a/src/test/common/variables/environmentVariablesProvider.unit.test.ts b/src/test/common/variables/environmentVariablesProvider.unit.test.ts index b603a9999b87..4b2e32756e5e 100644 --- a/src/test/common/variables/environmentVariablesProvider.unit.test.ts +++ b/src/test/common/variables/environmentVariablesProvider.unit.test.ts @@ -61,7 +61,7 @@ suite('Multiroot Environment Variables Provider', () => { provider.trackedWorkspaceFolders.add(workspaceFolder1Uri.fsPath); provider.trackedWorkspaceFolders.add(workspaceFolder2Uri.fsPath); - provider.onDidEnvironmentVariablesChange(uri => (affectedWorkspace = uri)); + provider.onDidEnvironmentVariablesChange((uri) => (affectedWorkspace = uri)); const changedEvent: ConfigurationChangeEvent = { affectsConfiguration(setting: string, uri?: Uri) { return setting === 'python.envFile' && uri!.fsPath === workspaceFolder1Uri.fsPath; @@ -78,7 +78,7 @@ suite('Multiroot Environment Variables Provider', () => { const workspaceFolderUri = Uri.file('workspace1'); provider.trackedWorkspaceFolders.add(workspaceFolderUri.fsPath); - provider.onDidEnvironmentVariablesChange(uri => (affectedWorkspace = uri)); + provider.onDidEnvironmentVariablesChange((uri) => (affectedWorkspace = uri)); const changedEvent: ConfigurationChangeEvent = { affectsConfiguration(_setting: string, _uri?: Uri) { return false; @@ -91,7 +91,7 @@ suite('Multiroot Environment Variables Provider', () => { }); test('Event is not fired when workspace is not tracked', () => { let affectedWorkspace: Uri | undefined; - provider.onDidEnvironmentVariablesChange(uri => (affectedWorkspace = uri)); + provider.onDidEnvironmentVariablesChange((uri) => (affectedWorkspace = uri)); const changedEvent: ConfigurationChangeEvent = { affectsConfiguration(_setting: string, _uri?: Uri) { return true; @@ -102,7 +102,7 @@ suite('Multiroot Environment Variables Provider', () => { assert.equal(affectedWorkspace, undefined); }); - [undefined, Uri.file('workspace')].forEach(workspaceUri => { + [undefined, Uri.file('workspace')].forEach((workspaceUri) => { const workspaceTitle = workspaceUri ? '(with a workspace)' : '(without a workspace)'; test(`Event is fired when the environment file is modified ${workspaceTitle}`, () => { let affectedWorkspace: Uri | undefined = Uri.file('dummy value'); @@ -112,11 +112,11 @@ suite('Multiroot Environment Variables Provider', () => { let onChangeHandler: undefined | ((resource?: Uri) => Function); fileSystemWatcher - .setup(fs => fs.onDidChange(typemoq.It.isAny())) - .callback(cb => (onChangeHandler = cb)) + .setup((fs) => fs.onDidChange(typemoq.It.isAny())) + .callback((cb) => (onChangeHandler = cb)) .verifiable(typemoq.Times.once()); when(workspace.createFileSystemWatcher(envFile)).thenReturn(fileSystemWatcher.object); - provider.onDidEnvironmentVariablesChange(uri => (affectedWorkspace = uri)); + provider.onDidEnvironmentVariablesChange((uri) => (affectedWorkspace = uri)); provider.createFileWatcher(envFile, workspaceUri); @@ -135,11 +135,11 @@ suite('Multiroot Environment Variables Provider', () => { let onDeleted: undefined | ((resource?: Uri) => Function); fileSystemWatcher - .setup(fs => fs.onDidDelete(typemoq.It.isAny())) - .callback(cb => (onDeleted = cb)) + .setup((fs) => fs.onDidDelete(typemoq.It.isAny())) + .callback((cb) => (onDeleted = cb)) .verifiable(typemoq.Times.once()); when(workspace.createFileSystemWatcher(envFile)).thenReturn(fileSystemWatcher.object); - provider.onDidEnvironmentVariablesChange(uri => (affectedWorkspace = uri)); + provider.onDidEnvironmentVariablesChange((uri) => (affectedWorkspace = uri)); provider.createFileWatcher(envFile, workspaceUri); @@ -158,11 +158,11 @@ suite('Multiroot Environment Variables Provider', () => { let onCreated: undefined | ((resource?: Uri) => Function); fileSystemWatcher - .setup(fs => fs.onDidCreate(typemoq.It.isAny())) - .callback(cb => (onCreated = cb)) + .setup((fs) => fs.onDidCreate(typemoq.It.isAny())) + .callback((cb) => (onCreated = cb)) .verifiable(typemoq.Times.once()); when(workspace.createFileSystemWatcher(envFile)).thenReturn(fileSystemWatcher.object); - provider.onDidEnvironmentVariablesChange(uri => (affectedWorkspace = uri)); + provider.onDidEnvironmentVariablesChange((uri) => (affectedWorkspace = uri)); provider.createFileWatcher(envFile, workspaceUri); @@ -177,9 +177,9 @@ suite('Multiroot Environment Variables Provider', () => { const envFile = path.join('a', 'b', 'env.file'); const fileSystemWatcher = typemoq.Mock.ofType(); - fileSystemWatcher.setup(fs => fs.onDidChange(typemoq.It.isAny())).verifiable(typemoq.Times.once()); - fileSystemWatcher.setup(fs => fs.onDidCreate(typemoq.It.isAny())).verifiable(typemoq.Times.once()); - fileSystemWatcher.setup(fs => fs.onDidDelete(typemoq.It.isAny())).verifiable(typemoq.Times.once()); + fileSystemWatcher.setup((fs) => fs.onDidChange(typemoq.It.isAny())).verifiable(typemoq.Times.once()); + fileSystemWatcher.setup((fs) => fs.onDidCreate(typemoq.It.isAny())).verifiable(typemoq.Times.once()); + fileSystemWatcher.setup((fs) => fs.onDidDelete(typemoq.It.isAny())).verifiable(typemoq.Times.once()); when(workspace.createFileSystemWatcher(envFile)).thenReturn(fileSystemWatcher.object); provider.createFileWatcher(envFile); diff --git a/src/test/common/variables/serviceRegistry.unit.test.ts b/src/test/common/variables/serviceRegistry.unit.test.ts index 3ab43dfa854d..55a3bbc2f2a4 100644 --- a/src/test/common/variables/serviceRegistry.unit.test.ts +++ b/src/test/common/variables/serviceRegistry.unit.test.ts @@ -1,36 +1,36 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { instance, mock, verify } from 'ts-mockito'; -import { EnvironmentVariablesService } from '../../../client/common/variables/environment'; -import { EnvironmentVariablesProvider } from '../../../client/common/variables/environmentVariablesProvider'; -import { registerTypes } from '../../../client/common/variables/serviceRegistry'; -import { IEnvironmentVariablesProvider, IEnvironmentVariablesService } from '../../../client/common/variables/types'; -import { ServiceManager } from '../../../client/ioc/serviceManager'; -import { IServiceManager } from '../../../client/ioc/types'; - -suite('Common variables Service Registry', () => { - let serviceManager: IServiceManager; - - setup(() => { - serviceManager = mock(ServiceManager); - }); - - test('Ensure services are registered', async () => { - registerTypes(instance(serviceManager)); - verify( - serviceManager.addSingleton( - IEnvironmentVariablesService, - EnvironmentVariablesService - ) - ).once(); - verify( - serviceManager.addSingleton( - IEnvironmentVariablesProvider, - EnvironmentVariablesProvider - ) - ).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { instance, mock, verify } from 'ts-mockito'; +import { EnvironmentVariablesService } from '../../../client/common/variables/environment'; +import { EnvironmentVariablesProvider } from '../../../client/common/variables/environmentVariablesProvider'; +import { registerTypes } from '../../../client/common/variables/serviceRegistry'; +import { IEnvironmentVariablesProvider, IEnvironmentVariablesService } from '../../../client/common/variables/types'; +import { ServiceManager } from '../../../client/ioc/serviceManager'; +import { IServiceManager } from '../../../client/ioc/types'; + +suite('Common variables Service Registry', () => { + let serviceManager: IServiceManager; + + setup(() => { + serviceManager = mock(ServiceManager); + }); + + test('Ensure services are registered', async () => { + registerTypes(instance(serviceManager)); + verify( + serviceManager.addSingleton( + IEnvironmentVariablesService, + EnvironmentVariablesService + ) + ).once(); + verify( + serviceManager.addSingleton( + IEnvironmentVariablesProvider, + EnvironmentVariablesProvider + ) + ).once(); + }); +}); diff --git a/src/test/common/webPanel/webPanel.unit.test.ts b/src/test/common/webPanel/webPanel.unit.test.ts index 963fcbb5e7a8..549ad1061cff 100644 --- a/src/test/common/webPanel/webPanel.unit.test.ts +++ b/src/test/common/webPanel/webPanel.unit.test.ts @@ -32,7 +32,7 @@ suite('WebPanelServer', () => { host?.dispose(); }); - test('Server responds with html when given valid input', done => { + test('Server responds with html when given valid input', (done) => { chai.request(server) .get( `/${uuid()}?token=${token}&scripts=${encodeURIComponent( @@ -48,7 +48,7 @@ suite('WebPanelServer', () => { }); }); - test('Server responds with 404 when given invalid input', done => { + test('Server responds with 404 when given invalid input', (done) => { chai.request(server) .get( `/${uuid()}?scripts=${encodeURIComponent(path.basename(historyBundle))}&rootPath=${encodeURIComponent( @@ -62,7 +62,7 @@ suite('WebPanelServer', () => { }); }); - test('Server responds with 404 when given file not found', done => { + test('Server responds with 404 when given file not found', (done) => { const agent = chai.request(server).keepOpen(); agent .get( @@ -81,7 +81,7 @@ suite('WebPanelServer', () => { }); }); - test('Server can find the index_bundle', done => { + test('Server can find the index_bundle', (done) => { // See here for where the code for this comes from (you might think keepOpen is required, but instead request.agent is used for multiple requests) // https://www.chaijs.com/plugins/chai-http/ and search 'Retaining cookies with each request' const agent = chai.request.agent(server); @@ -103,7 +103,7 @@ suite('WebPanelServer', () => { }); }); - test('Server can find the a file in a cwd', done => { + test('Server can find the a file in a cwd', (done) => { const agent = chai.request.agent(server); agent .get( @@ -125,7 +125,7 @@ suite('WebPanelServer', () => { }); }); - test('Server will skip a file not in the cwd', done => { + test('Server will skip a file not in the cwd', (done) => { const agent = chai.request.agent(server); agent .get( diff --git a/src/test/configuration/interpreterSelector.unit.test.ts b/src/test/configuration/interpreterSelector.unit.test.ts index 11ec13445465..ec9a7ca4e3ea 100644 --- a/src/test/configuration/interpreterSelector.unit.test.ts +++ b/src/test/configuration/interpreterSelector.unit.test.ts @@ -98,14 +98,14 @@ suite('Interpreters - selector', () => { workspace = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); fileSystem - .setup(x => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) + .setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) .returns((a: string, b: string) => a === b); - configurationService.setup(x => x.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + configurationService.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); - comparer.setup(c => c.compare(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => 0); + comparer.setup((c) => c.compare(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => 0); }); - [true, false].forEach(isWindows => { + [true, false].forEach((isWindows) => { test(`Suggestions (${isWindows ? 'Windows' : 'Non-Windows'})`, async () => { const selector = new InterpreterSelector( interpreterService.object, @@ -127,12 +127,12 @@ suite('Interpreters - selector', () => { { displayName: '2 (virtualenv)', path: 'c:/path2/path2', type: InterpreterType.VirtualEnv }, { displayName: '3', path: 'c:/path2/path2', type: InterpreterType.Unknown }, { displayName: '4', path: 'c:/path4/path4', type: InterpreterType.Conda } - ].map(item => { + ].map((item) => { return { ...info, ...item }; }); interpreterService - .setup(x => x.getInterpreters(TypeMoq.It.isAny())) - .returns(() => new Promise(resolve => resolve(initial))); + .setup((x) => x.getInterpreters(TypeMoq.It.isAny())) + .returns(() => new Promise((resolve) => resolve(initial))); const actual = await selector.getSuggestions(undefined); @@ -174,7 +174,7 @@ suite('Interpreters - selector', () => { configurationService.object, commandManager.object ); - pythonSettings.setup(p => p.pythonPath).returns(() => 'python'); + pythonSettings.setup((p) => p.pythonPath).returns(() => 'python'); const selectedItem: IInterpreterQuickPickItem = { description: '', detail: '', @@ -184,15 +184,15 @@ suite('Interpreters - selector', () => { interpreter: {} as any }; - workspace.setup(w => w.workspaceFolders).returns(() => undefined); + workspace.setup((w) => w.workspaceFolders).returns(() => undefined); selector.getSuggestions = () => Promise.resolve([]); appShell - .setup(s => s.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((s) => s.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(selectedItem)) .verifiable(TypeMoq.Times.once()); pythonPathUpdater - .setup(p => + .setup((p) => p.updatePythonPath( TypeMoq.It.isValue(selectedItem.path), TypeMoq.It.isValue(ConfigurationTarget.Global), @@ -222,7 +222,7 @@ suite('Interpreters - selector', () => { configurationService.object, commandManager.object ); - pythonSettings.setup(p => p.pythonPath).returns(() => 'python'); + pythonSettings.setup((p) => p.pythonPath).returns(() => 'python'); const selectedItem: IInterpreterQuickPickItem = { description: '', detail: '', @@ -233,15 +233,15 @@ suite('Interpreters - selector', () => { }; const folder = { name: 'one', uri: Uri.parse('one'), index: 0 }; - workspace.setup(w => w.workspaceFolders).returns(() => [folder]); + workspace.setup((w) => w.workspaceFolders).returns(() => [folder]); selector.getSuggestions = () => Promise.resolve([]); appShell - .setup(s => s.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((s) => s.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(selectedItem)) .verifiable(TypeMoq.Times.once()); pythonPathUpdater - .setup(p => + .setup((p) => p.updatePythonPath( TypeMoq.It.isValue(selectedItem.path), TypeMoq.It.isValue(ConfigurationTarget.WorkspaceFolder), @@ -271,7 +271,7 @@ suite('Interpreters - selector', () => { configurationService.object, commandManager.object ); - pythonSettings.setup(p => p.pythonPath).returns(() => 'python'); + pythonSettings.setup((p) => p.pythonPath).returns(() => 'python'); const selectedItem: IInterpreterQuickPickItem = { description: '', detail: '', @@ -283,19 +283,19 @@ suite('Interpreters - selector', () => { const folder1 = { name: 'one', uri: Uri.parse('one'), index: 1 }; const folder2 = { name: 'two', uri: Uri.parse('two'), index: 2 }; - workspace.setup(w => w.workspaceFolders).returns(() => [folder1, folder2]); + workspace.setup((w) => w.workspaceFolders).returns(() => [folder1, folder2]); selector.getSuggestions = () => Promise.resolve([]); appShell - .setup(s => s.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((s) => s.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(selectedItem)) .verifiable(TypeMoq.Times.once()); appShell - .setup(s => s.showWorkspaceFolderPick(TypeMoq.It.isAny())) + .setup((s) => s.showWorkspaceFolderPick(TypeMoq.It.isAny())) .returns(() => Promise.resolve(folder2)) .verifiable(TypeMoq.Times.once()); pythonPathUpdater - .setup(p => + .setup((p) => p.updatePythonPath( TypeMoq.It.isValue(selectedItem.path), TypeMoq.It.isValue(ConfigurationTarget.WorkspaceFolder), @@ -337,19 +337,19 @@ suite('Interpreters - selector', () => { const folder1 = { name: 'one', uri: Uri.parse('one'), index: 1 }; const folder2 = { name: 'two', uri: Uri.parse('two'), index: 2 }; - workspace.setup(w => w.workspaceFolders).returns(() => [folder1, folder2]); + workspace.setup((w) => w.workspaceFolders).returns(() => [folder1, folder2]); selector.getSuggestions = () => Promise.resolve([]); appShell - .setup(s => s.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((s) => s.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(selectedItem)) .verifiable(TypeMoq.Times.never()); appShell - .setup(s => s.showWorkspaceFolderPick(TypeMoq.It.isAny())) + .setup((s) => s.showWorkspaceFolderPick(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); pythonPathUpdater - .setup(p => + .setup((p) => p.updatePythonPath(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve()) diff --git a/src/test/constants.ts b/src/test/constants.ts index 66ff67c9e21b..2e255ecec9d5 100644 --- a/src/test/constants.ts +++ b/src/test/constants.ts @@ -1,42 +1,42 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import * as path from 'path'; -import { IS_CI_SERVER, IS_CI_SERVER_TEST_DEBUGGER } from './ciConstants'; - -// Activating extension for Multiroot and Debugger CI tests for Windows takes just over 2 minutes sometimes, so 3 minutes seems like a safe margin -export const MAX_EXTENSION_ACTIVATION_TIME = 180_000; -export const TEST_TIMEOUT = 25000; -export const TEST_RETRYCOUNT = 3; -export const IS_SMOKE_TEST = process.env.VSC_PYTHON_SMOKE_TEST === '1'; -export const IS_PERF_TEST = process.env.VSC_PYTHON_PERF_TEST === '1'; -export const IS_MULTI_ROOT_TEST = isMultitrootTest(); - -// If running on CI server, then run debugger tests ONLY if the corresponding flag is enabled. -export const TEST_DEBUGGER = IS_CI_SERVER ? IS_CI_SERVER_TEST_DEBUGGER : true; - -function isMultitrootTest() { - // No need to run smoke nor perf tests in a multi-root environment. - if (IS_SMOKE_TEST || IS_PERF_TEST) { - return false; - } - try { - // tslint:disable-next-line:no-require-imports - const vscode = require('vscode'); - const workspace = vscode.workspace; - return Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 1; - } catch { - // being accessed, when VS Code hasn't been launched. - return false; - } -} - -export const EXTENSION_ROOT_DIR_FOR_TESTS = path.join(__dirname, '..', '..'); -export const PVSC_EXTENSION_ID_FOR_TESTS = 'ms-python.python'; - -export const SMOKE_TEST_EXTENSIONS_DIR = path.join( - EXTENSION_ROOT_DIR_FOR_TESTS, - 'tmp', - 'ext', - 'smokeTestExtensionsFolder' -); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import * as path from 'path'; +import { IS_CI_SERVER, IS_CI_SERVER_TEST_DEBUGGER } from './ciConstants'; + +// Activating extension for Multiroot and Debugger CI tests for Windows takes just over 2 minutes sometimes, so 3 minutes seems like a safe margin +export const MAX_EXTENSION_ACTIVATION_TIME = 180_000; +export const TEST_TIMEOUT = 25000; +export const TEST_RETRYCOUNT = 3; +export const IS_SMOKE_TEST = process.env.VSC_PYTHON_SMOKE_TEST === '1'; +export const IS_PERF_TEST = process.env.VSC_PYTHON_PERF_TEST === '1'; +export const IS_MULTI_ROOT_TEST = isMultitrootTest(); + +// If running on CI server, then run debugger tests ONLY if the corresponding flag is enabled. +export const TEST_DEBUGGER = IS_CI_SERVER ? IS_CI_SERVER_TEST_DEBUGGER : true; + +function isMultitrootTest() { + // No need to run smoke nor perf tests in a multi-root environment. + if (IS_SMOKE_TEST || IS_PERF_TEST) { + return false; + } + try { + // tslint:disable-next-line:no-require-imports + const vscode = require('vscode'); + const workspace = vscode.workspace; + return Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 1; + } catch { + // being accessed, when VS Code hasn't been launched. + return false; + } +} + +export const EXTENSION_ROOT_DIR_FOR_TESTS = path.join(__dirname, '..', '..'); +export const PVSC_EXTENSION_ID_FOR_TESTS = 'ms-python.python'; + +export const SMOKE_TEST_EXTENSIONS_DIR = path.join( + EXTENSION_ROOT_DIR_FOR_TESTS, + 'tmp', + 'ext', + 'smokeTestExtensionsFolder' +); diff --git a/src/test/core.ts b/src/test/core.ts index c9e07735f397..8db9d06665ec 100644 --- a/src/test/core.ts +++ b/src/test/core.ts @@ -6,7 +6,7 @@ // File without any dependencies on VS Code. export async function sleep(milliseconds: number) { - return new Promise(resolve => setTimeout(resolve, milliseconds)); + return new Promise((resolve) => setTimeout(resolve, milliseconds)); } // tslint:disable-next-line:no-empty diff --git a/src/test/datascience/color.test.ts b/src/test/datascience/color.test.ts index 8df97088ebe8..74603f558f19 100644 --- a/src/test/datascience/color.test.ts +++ b/src/test/datascience/color.test.ts @@ -1,177 +1,177 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { assert } from 'chai'; -import * as TypeMoq from 'typemoq'; -import { WorkspaceConfiguration } from 'vscode'; - -import { Extensions } from '../../client/common/application/extensions'; -import { IWorkspaceService } from '../../client/common/application/types'; -import { PythonSettings } from '../../client/common/configSettings'; -import { FileSystem } from '../../client/common/platform/fileSystem'; -import { CurrentProcess } from '../../client/common/process/currentProcess'; -import { IConfigurationService } from '../../client/common/types'; -import { CodeCssGenerator } from '../../client/datascience/codeCssGenerator'; -import { ThemeFinder } from '../../client/datascience/themeFinder'; -import { IThemeFinder } from '../../client/datascience/types'; -import { MockAutoSelectionService } from '../mocks/autoSelector'; - -// tslint:disable:max-func-body-length -suite('Theme colors', () => { - let themeFinder: ThemeFinder; - let extensions: Extensions; - let currentProcess: CurrentProcess; - let workspaceService: TypeMoq.IMock; - let workspaceConfig: TypeMoq.IMock; - let cssGenerator: CodeCssGenerator; - let configService: TypeMoq.IMock; - const settings: PythonSettings = new PythonSettings(undefined, new MockAutoSelectionService()); - - setup(() => { - extensions = new Extensions(); - currentProcess = new CurrentProcess(); - const fs = new FileSystem(); - themeFinder = new ThemeFinder(extensions, currentProcess, fs); - - workspaceConfig = TypeMoq.Mock.ofType(); - workspaceConfig - .setup(ws => ws.has(TypeMoq.It.isAnyString())) - .returns(() => { - return false; - }); - workspaceConfig - .setup(ws => ws.get(TypeMoq.It.isAnyString())) - .returns(() => { - return undefined; - }); - workspaceConfig - .setup(ws => ws.get(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())) - .returns((_s, d) => { - return d; - }); - - settings.datascience = { - allowImportFromNotebook: true, - jupyterLaunchTimeout: 20000, - jupyterLaunchRetries: 3, - enabled: true, - jupyterServerURI: 'local', - notebookFileRoot: 'WORKSPACE', - changeDirOnImportExport: true, - useDefaultConfigForJupyter: true, - jupyterInterruptTimeout: 10000, - searchForJupyter: true, - showCellInputCode: true, - collapseCellInputCodeByDefault: true, - allowInput: true, - maxOutputSize: 400, - errorBackgroundColor: '#FFFFFF', - sendSelectionToInteractiveWindow: false, - variableExplorerExclude: 'module;function;builtin_function_or_method', - codeRegularExpression: '^(#\\s*%%|#\\s*\\|#\\s*In\\[\\d*?\\]|#\\s*In\\[ \\])', - markdownRegularExpression: '^(#\\s*%%\\s*\\[markdown\\]|#\\s*\\)', - enablePlotViewer: true, - runStartupCommands: '', - debugJustMyCode: true, - variableQueries: [], - jupyterCommandLineArguments: [] - }; - configService = TypeMoq.Mock.ofType(); - configService.setup(x => x.getSettings(TypeMoq.It.isAny())).returns(() => settings); - - workspaceService = TypeMoq.Mock.ofType(); - workspaceService.setup(c => c.getConfiguration(TypeMoq.It.isAny())).returns(() => workspaceConfig.object); - workspaceService - .setup(c => c.getConfiguration(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => workspaceConfig.object); - - cssGenerator = new CodeCssGenerator(workspaceService.object, themeFinder, configService.object, fs); - }); - - function runTest(themeName: string, isDark: boolean, shouldExist: boolean) { - test(themeName, async () => { - const json = await themeFinder.findThemeRootJson(themeName); - if (shouldExist) { - assert.ok(json, `Cannot find theme ${themeName}`); - const actuallyDark = await themeFinder.isThemeDark(themeName); - assert.equal(actuallyDark, isDark, `Theme ${themeName} darkness is not ${isDark}`); - workspaceConfig.reset(); - workspaceConfig - .setup(ws => ws.get(TypeMoq.It.isValue('colorTheme'))) - .returns(() => { - return themeName; - }); - workspaceConfig - .setup(ws => ws.get(TypeMoq.It.isValue('fontFamily'))) - .returns(() => { - return 'Arial'; - }); - workspaceConfig - .setup(ws => ws.get(TypeMoq.It.isValue('fontSize'))) - .returns(() => { - return 16; - }); - workspaceConfig - .setup(ws => ws.get(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())) - .returns((_s, d) => { - return d; - }); - const theme = await cssGenerator.generateMonacoTheme(undefined, isDark, themeName); - assert.ok(theme, `Cannot find monaco theme for ${themeName}`); - const colors = await cssGenerator.generateThemeCss(undefined, isDark, themeName); - assert.ok(colors, 'Cannot find theme colors for Kimbie Dark'); - - // Make sure we have a string value that is not set to a variable - // (that would be the default and all themes have a string color) - assert.ok(theme.rules, 'No rules found in monaco theme'); - // tslint:disable-next-line: no-any - const commentPunctuation = (theme.rules as any[]).findIndex( - r => r.token === 'punctuation.definition.comment' - ); - assert.ok(commentPunctuation >= 0, 'No punctuation.comment found'); - } else { - assert.notOk(json, `Found ${themeName} when not expected`); - } - }); - } - - // One test per known theme - runTest('Light (Visual Studio)', false, true); - runTest('Light+ (default light)', false, true); - runTest('Quiet Light', false, true); - runTest('Solarized Light', false, true); - runTest('Abyss', true, true); - runTest('Dark (Visual Studio)', true, true); - runTest('Dark+ (default dark)', true, true); - runTest('Kimbie Dark', true, true); - runTest('Monokai', true, true); - runTest('Monokai Dimmed', true, true); - runTest('Red', true, true); - runTest('Solarized Dark', true, true); - runTest('Tomorrow Night Blue', true, true); - - // One test to make sure unknown themes don't return a value. - runTest('Knight Rider', true, false); - - // Test for when theme's json can't be found. - test('Missing json theme', async () => { - const mockThemeFinder = TypeMoq.Mock.ofType(); - mockThemeFinder.setup(m => m.isThemeDark(TypeMoq.It.isAnyString())).returns(() => Promise.resolve(false)); - mockThemeFinder - .setup(m => m.findThemeRootJson(TypeMoq.It.isAnyString())) - .returns(() => Promise.resolve(undefined)); - - const fs = new FileSystem(); - cssGenerator = new CodeCssGenerator(workspaceService.object, mockThemeFinder.object, configService.object, fs); - - const colors = await cssGenerator.generateThemeCss(undefined, false, 'Kimbie Dark'); - assert.ok(colors, 'Cannot find theme colors for Kimbie Dark'); - - // Make sure we have a string value that is not set to a variable - // (that would be the default and all themes have a string color) - const matches = /--code-string-color\:\s(.*?);/gm.exec(colors); - assert.ok(matches, 'No matches found for string color'); - assert.equal(matches!.length, 2, 'Wrong number of matches for for string color'); - assert.ok(matches![1].includes('#'), 'String color not found'); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { assert } from 'chai'; +import * as TypeMoq from 'typemoq'; +import { WorkspaceConfiguration } from 'vscode'; + +import { Extensions } from '../../client/common/application/extensions'; +import { IWorkspaceService } from '../../client/common/application/types'; +import { PythonSettings } from '../../client/common/configSettings'; +import { FileSystem } from '../../client/common/platform/fileSystem'; +import { CurrentProcess } from '../../client/common/process/currentProcess'; +import { IConfigurationService } from '../../client/common/types'; +import { CodeCssGenerator } from '../../client/datascience/codeCssGenerator'; +import { ThemeFinder } from '../../client/datascience/themeFinder'; +import { IThemeFinder } from '../../client/datascience/types'; +import { MockAutoSelectionService } from '../mocks/autoSelector'; + +// tslint:disable:max-func-body-length +suite('Theme colors', () => { + let themeFinder: ThemeFinder; + let extensions: Extensions; + let currentProcess: CurrentProcess; + let workspaceService: TypeMoq.IMock; + let workspaceConfig: TypeMoq.IMock; + let cssGenerator: CodeCssGenerator; + let configService: TypeMoq.IMock; + const settings: PythonSettings = new PythonSettings(undefined, new MockAutoSelectionService()); + + setup(() => { + extensions = new Extensions(); + currentProcess = new CurrentProcess(); + const fs = new FileSystem(); + themeFinder = new ThemeFinder(extensions, currentProcess, fs); + + workspaceConfig = TypeMoq.Mock.ofType(); + workspaceConfig + .setup((ws) => ws.has(TypeMoq.It.isAnyString())) + .returns(() => { + return false; + }); + workspaceConfig + .setup((ws) => ws.get(TypeMoq.It.isAnyString())) + .returns(() => { + return undefined; + }); + workspaceConfig + .setup((ws) => ws.get(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())) + .returns((_s, d) => { + return d; + }); + + settings.datascience = { + allowImportFromNotebook: true, + jupyterLaunchTimeout: 20000, + jupyterLaunchRetries: 3, + enabled: true, + jupyterServerURI: 'local', + notebookFileRoot: 'WORKSPACE', + changeDirOnImportExport: true, + useDefaultConfigForJupyter: true, + jupyterInterruptTimeout: 10000, + searchForJupyter: true, + showCellInputCode: true, + collapseCellInputCodeByDefault: true, + allowInput: true, + maxOutputSize: 400, + errorBackgroundColor: '#FFFFFF', + sendSelectionToInteractiveWindow: false, + variableExplorerExclude: 'module;function;builtin_function_or_method', + codeRegularExpression: '^(#\\s*%%|#\\s*\\|#\\s*In\\[\\d*?\\]|#\\s*In\\[ \\])', + markdownRegularExpression: '^(#\\s*%%\\s*\\[markdown\\]|#\\s*\\)', + enablePlotViewer: true, + runStartupCommands: '', + debugJustMyCode: true, + variableQueries: [], + jupyterCommandLineArguments: [] + }; + configService = TypeMoq.Mock.ofType(); + configService.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings); + + workspaceService = TypeMoq.Mock.ofType(); + workspaceService.setup((c) => c.getConfiguration(TypeMoq.It.isAny())).returns(() => workspaceConfig.object); + workspaceService + .setup((c) => c.getConfiguration(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(() => workspaceConfig.object); + + cssGenerator = new CodeCssGenerator(workspaceService.object, themeFinder, configService.object, fs); + }); + + function runTest(themeName: string, isDark: boolean, shouldExist: boolean) { + test(themeName, async () => { + const json = await themeFinder.findThemeRootJson(themeName); + if (shouldExist) { + assert.ok(json, `Cannot find theme ${themeName}`); + const actuallyDark = await themeFinder.isThemeDark(themeName); + assert.equal(actuallyDark, isDark, `Theme ${themeName} darkness is not ${isDark}`); + workspaceConfig.reset(); + workspaceConfig + .setup((ws) => ws.get(TypeMoq.It.isValue('colorTheme'))) + .returns(() => { + return themeName; + }); + workspaceConfig + .setup((ws) => ws.get(TypeMoq.It.isValue('fontFamily'))) + .returns(() => { + return 'Arial'; + }); + workspaceConfig + .setup((ws) => ws.get(TypeMoq.It.isValue('fontSize'))) + .returns(() => { + return 16; + }); + workspaceConfig + .setup((ws) => ws.get(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())) + .returns((_s, d) => { + return d; + }); + const theme = await cssGenerator.generateMonacoTheme(undefined, isDark, themeName); + assert.ok(theme, `Cannot find monaco theme for ${themeName}`); + const colors = await cssGenerator.generateThemeCss(undefined, isDark, themeName); + assert.ok(colors, 'Cannot find theme colors for Kimbie Dark'); + + // Make sure we have a string value that is not set to a variable + // (that would be the default and all themes have a string color) + assert.ok(theme.rules, 'No rules found in monaco theme'); + // tslint:disable-next-line: no-any + const commentPunctuation = (theme.rules as any[]).findIndex( + (r) => r.token === 'punctuation.definition.comment' + ); + assert.ok(commentPunctuation >= 0, 'No punctuation.comment found'); + } else { + assert.notOk(json, `Found ${themeName} when not expected`); + } + }); + } + + // One test per known theme + runTest('Light (Visual Studio)', false, true); + runTest('Light+ (default light)', false, true); + runTest('Quiet Light', false, true); + runTest('Solarized Light', false, true); + runTest('Abyss', true, true); + runTest('Dark (Visual Studio)', true, true); + runTest('Dark+ (default dark)', true, true); + runTest('Kimbie Dark', true, true); + runTest('Monokai', true, true); + runTest('Monokai Dimmed', true, true); + runTest('Red', true, true); + runTest('Solarized Dark', true, true); + runTest('Tomorrow Night Blue', true, true); + + // One test to make sure unknown themes don't return a value. + runTest('Knight Rider', true, false); + + // Test for when theme's json can't be found. + test('Missing json theme', async () => { + const mockThemeFinder = TypeMoq.Mock.ofType(); + mockThemeFinder.setup((m) => m.isThemeDark(TypeMoq.It.isAnyString())).returns(() => Promise.resolve(false)); + mockThemeFinder + .setup((m) => m.findThemeRootJson(TypeMoq.It.isAnyString())) + .returns(() => Promise.resolve(undefined)); + + const fs = new FileSystem(); + cssGenerator = new CodeCssGenerator(workspaceService.object, mockThemeFinder.object, configService.object, fs); + + const colors = await cssGenerator.generateThemeCss(undefined, false, 'Kimbie Dark'); + assert.ok(colors, 'Cannot find theme colors for Kimbie Dark'); + + // Make sure we have a string value that is not set to a variable + // (that would be the default and all themes have a string color) + const matches = /--code-string-color\:\s(.*?);/gm.exec(colors); + assert.ok(matches, 'No matches found for string color'); + assert.equal(matches!.length, 2, 'Wrong number of matches for for string color'); + assert.ok(matches![1].includes('#'), 'String color not found'); + }); +}); diff --git a/src/test/datascience/commands/commandRegistry.unit.test.ts b/src/test/datascience/commands/commandRegistry.unit.test.ts index 47de210fe270..ad42d2e87cd2 100644 --- a/src/test/datascience/commands/commandRegistry.unit.test.ts +++ b/src/test/datascience/commands/commandRegistry.unit.test.ts @@ -80,7 +80,7 @@ suite('Data Science - Commands', () => { Commands.DebugCurrentCellPalette, Commands.CreateNewNotebook, Commands.ViewJupyterOutput - ].forEach(command => { + ].forEach((command) => { test(`Should register Command ${command}`, () => { // tslint:disable-next-line: no-any verify(commandManager.registerCommand(command as any, anything(), commandRegistry)).once(); diff --git a/src/test/datascience/dataScienceIocContainer.ts b/src/test/datascience/dataScienceIocContainer.ts index 849715420fd7..0e483e4f41e1 100644 --- a/src/test/datascience/dataScienceIocContainer.ts +++ b/src/test/datascience/dataScienceIocContainer.ts @@ -1,1497 +1,1497 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -//tslint:disable:trailing-comma no-any -import * as child_process from 'child_process'; -import { ReactWrapper } from 'enzyme'; -import { interfaces } from 'inversify'; -import * as os from 'os'; -import * as path from 'path'; -import { SemVer } from 'semver'; -import { anything, instance, mock, reset, when } from 'ts-mockito'; -import * as TypeMoq from 'typemoq'; -import { - CancellationTokenSource, - ConfigurationChangeEvent, - Disposable, - Event, - EventEmitter, - FileSystemWatcher, - Memento, - Uri, - WorkspaceFolder, - WorkspaceFoldersChangeEvent -} from 'vscode'; -import * as vsls from 'vsls/vscode'; - -import { LanguageServerExtensionActivationService } from '../../client/activation/activationService'; -import { LanguageServerDownloader } from '../../client/activation/common/downloader'; -import { JediExtensionActivator } from '../../client/activation/jedi'; -import { DotNetLanguageServerActivator } from '../../client/activation/languageServer/activator'; -import { LanguageServerCompatibilityService } from '../../client/activation/languageServer/languageServerCompatibilityService'; -import { LanguageServerExtension } from '../../client/activation/languageServer/languageServerExtension'; -import { DotNetLanguageServerFolderService } from '../../client/activation/languageServer/languageServerFolderService'; -import { DotNetLanguageServerPackageService } from '../../client/activation/languageServer/languageServerPackageService'; -import { DotNetLanguageServerManager } from '../../client/activation/languageServer/manager'; -import { NodeLanguageServerActivator } from '../../client/activation/node/activator'; -import { NodeLanguageServerManager } from '../../client/activation/node/manager'; -import { - IExtensionSingleActivationService, - ILanguageServerActivator, - ILanguageServerAnalysisOptions, - ILanguageServerCache, - ILanguageServerCompatibilityService, - ILanguageServerDownloader, - ILanguageServerExtension, - ILanguageServerFolderService, - ILanguageServerManager, - ILanguageServerPackageService, - ILanguageServerProxy, - LanguageServerType -} from '../../client/activation/types'; -import { - LSNotSupportedDiagnosticService, - LSNotSupportedDiagnosticServiceId -} from '../../client/application/diagnostics/checks/lsNotSupported'; -import { DiagnosticFilterService } from '../../client/application/diagnostics/filter'; -import { - DiagnosticCommandPromptHandlerService, - DiagnosticCommandPromptHandlerServiceId, - MessageCommandPrompt -} from '../../client/application/diagnostics/promptHandler'; -import { - IDiagnosticFilterService, - IDiagnosticHandlerService, - IDiagnosticsService -} from '../../client/application/diagnostics/types'; -import { ClipboardService } from '../../client/common/application/clipboard'; -import { TerminalManager } from '../../client/common/application/terminalManager'; -import { - IApplicationShell, - IClipboard, - ICommandManager, - ICustomEditorService, - IDebugService, - IDocumentManager, - ILiveShareApi, - ILiveShareTestingApi, - ITerminalManager, - IWebPanel, - IWebPanelMessageListener, - IWebPanelOptions, - IWebPanelProvider, - IWorkspaceService -} from '../../client/common/application/types'; -import { WebPanel } from '../../client/common/application/webPanels/webPanel'; -import { WebPanelProvider } from '../../client/common/application/webPanels/webPanelProvider'; -import { WorkspaceService } from '../../client/common/application/workspace'; -import { AsyncDisposableRegistry } from '../../client/common/asyncDisposableRegistry'; -import { PythonSettings } from '../../client/common/configSettings'; -import { EXTENSION_ROOT_DIR, UseCustomEditorApi } from '../../client/common/constants'; -import { CryptoUtils } from '../../client/common/crypto'; -import { DotNetCompatibilityService } from '../../client/common/dotnet/compatibilityService'; -import { IDotNetCompatibilityService } from '../../client/common/dotnet/types'; -import { ExperimentsManager } from '../../client/common/experiments'; -import { InstallationChannelManager } from '../../client/common/installer/channelManager'; -import { ProductInstaller } from '../../client/common/installer/productInstaller'; -import { - CTagsProductPathService, - DataScienceProductPathService, - FormatterProductPathService, - LinterProductPathService, - RefactoringLibraryProductPathService, - TestFrameworkProductPathService -} from '../../client/common/installer/productPath'; -import { ProductService } from '../../client/common/installer/productService'; -import { IInstallationChannelManager, IProductPathService, IProductService } from '../../client/common/installer/types'; -import { IS_WINDOWS } from '../../client/common/platform/constants'; -import { PathUtils } from '../../client/common/platform/pathUtils'; -import { RegistryImplementation } from '../../client/common/platform/registry'; -import { IFileSystem, IRegistry } from '../../client/common/platform/types'; -import { CurrentProcess } from '../../client/common/process/currentProcess'; -import { BufferDecoder } from '../../client/common/process/decoder'; -import { ProcessLogger } from '../../client/common/process/logger'; -import { ProcessServiceFactory } from '../../client/common/process/processFactory'; -import { PythonExecutionFactory } from '../../client/common/process/pythonExecutionFactory'; -import { - IBufferDecoder, - IProcessLogger, - IProcessServiceFactory, - IPythonExecutionFactory -} from '../../client/common/process/types'; -import { Bash } from '../../client/common/terminal/environmentActivationProviders/bash'; -import { CommandPromptAndPowerShell } from '../../client/common/terminal/environmentActivationProviders/commandPrompt'; -import { CondaActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/condaActivationProvider'; -import { PipEnvActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/pipEnvActivationProvider'; -import { PyEnvActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/pyenvActivationProvider'; -import { TerminalHelper } from '../../client/common/terminal/helper'; -import { TerminalNameShellDetector } from '../../client/common/terminal/shellDetectors/terminalNameShellDetector'; -import { - IShellDetector, - ITerminalActivationCommandProvider, - ITerminalHelper, - TerminalActivationProviders -} from '../../client/common/terminal/types'; -import { - BANNER_NAME_LS_SURVEY, - GLOBAL_MEMENTO, - IAsyncDisposableRegistry, - IConfigurationService, - ICryptoUtils, - ICurrentProcess, - IDataScienceSettings, - IExperimentsManager, - IExtensionContext, - IExtensions, - IInstaller, - IMemento, - IOutputChannel, - IPathUtils, - IPersistentStateFactory, - IPythonExtensionBanner, - IsWindows, - ProductType, - Resource, - WORKSPACE_MEMENTO -} from '../../client/common/types'; -import { Deferred, sleep } from '../../client/common/utils/async'; -import { noop } from '../../client/common/utils/misc'; -import { IMultiStepInputFactory, MultiStepInputFactory } from '../../client/common/utils/multiStepInput'; -import { Architecture } from '../../client/common/utils/platform'; -import { EnvironmentVariablesService } from '../../client/common/variables/environment'; -import { EnvironmentVariablesProvider } from '../../client/common/variables/environmentVariablesProvider'; -import { IEnvironmentVariablesProvider, IEnvironmentVariablesService } from '../../client/common/variables/types'; -import { CodeCssGenerator } from '../../client/datascience/codeCssGenerator'; -import { JUPYTER_OUTPUT_CHANNEL } from '../../client/datascience/constants'; -import { ActiveEditorContextService } from '../../client/datascience/context/activeEditorContext'; -import { DataViewer } from '../../client/datascience/data-viewing/dataViewer'; -import { DataViewerDependencyService } from '../../client/datascience/data-viewing/dataViewerDependencyService'; -import { DataViewerProvider } from '../../client/datascience/data-viewing/dataViewerProvider'; -import { DebugLocationTrackerFactory } from '../../client/datascience/debugLocationTrackerFactory'; -import { CellHashLogger } from '../../client/datascience/editor-integration/cellhashLogger'; -import { CellHashProvider } from '../../client/datascience/editor-integration/cellhashprovider'; -import { CodeLensFactory } from '../../client/datascience/editor-integration/codeLensFactory'; -import { DataScienceCodeLensProvider } from '../../client/datascience/editor-integration/codelensprovider'; -import { CodeWatcher } from '../../client/datascience/editor-integration/codewatcher'; -import { DataScienceErrorHandler } from '../../client/datascience/errorHandler/errorHandler'; -import { GatherProvider } from '../../client/datascience/gather/gather'; -import { GatherListener } from '../../client/datascience/gather/gatherListener'; -import { GatherLogger } from '../../client/datascience/gather/gatherLogger'; -import { IntellisenseProvider } from '../../client/datascience/interactive-common/intellisense/intellisenseProvider'; -import { NotebookProvider } from '../../client/datascience/interactive-common/notebookProvider'; -import { AutoSaveService } from '../../client/datascience/interactive-ipynb/autoSaveService'; -import { NativeEditor } from '../../client/datascience/interactive-ipynb/nativeEditor'; -import { NativeEditorCommandListener } from '../../client/datascience/interactive-ipynb/nativeEditorCommandListener'; -import { NativeEditorOldWebView } from '../../client/datascience/interactive-ipynb/nativeEditorOldWebView'; -import { NativeEditorStorage } from '../../client/datascience/interactive-ipynb/nativeEditorStorage'; -import { NativeEditorSynchronizer } from '../../client/datascience/interactive-ipynb/nativeEditorSynchronizer'; -import { InteractiveWindow } from '../../client/datascience/interactive-window/interactiveWindow'; -import { InteractiveWindowCommandListener } from '../../client/datascience/interactive-window/interactiveWindowCommandListener'; -import { IPyWidgetHandler } from '../../client/datascience/ipywidgets/ipywidgetHandler'; -import { IPyWidgetMessageDispatcherFactory } from '../../client/datascience/ipywidgets/ipyWidgetMessageDispatcherFactory'; -import { JupyterCommandFactory } from '../../client/datascience/jupyter/interpreter/jupyterCommand'; -import { JupyterCommandFinder } from '../../client/datascience/jupyter/interpreter/jupyterCommandFinder'; -import { JupyterCommandInterpreterDependencyService } from '../../client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService'; -import { JupyterCommandFinderInterpreterExecutionService } from '../../client/datascience/jupyter/interpreter/jupyterCommandInterpreterExecutionService'; -import { JupyterInterpreterDependencyService } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService'; -import { JupyterInterpreterOldCacheStateStore } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterOldCacheStateStore'; -import { JupyterInterpreterSelectionCommand } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterSelectionCommand'; -import { JupyterInterpreterSelector } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterSelector'; -import { JupyterInterpreterService } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterService'; -import { JupyterInterpreterStateStore } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterStateStore'; -import { JupyterInterpreterSubCommandExecutionService } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterSubCommandExecutionService'; -import { JupyterDebugger } from '../../client/datascience/jupyter/jupyterDebugger'; -import { JupyterExecutionFactory } from '../../client/datascience/jupyter/jupyterExecutionFactory'; -import { JupyterExporter } from '../../client/datascience/jupyter/jupyterExporter'; -import { JupyterImporter } from '../../client/datascience/jupyter/jupyterImporter'; -import { JupyterPasswordConnect } from '../../client/datascience/jupyter/jupyterPasswordConnect'; -import { JupyterServerWrapper } from '../../client/datascience/jupyter/jupyterServerWrapper'; -import { JupyterSessionManagerFactory } from '../../client/datascience/jupyter/jupyterSessionManagerFactory'; -import { JupyterVariables } from '../../client/datascience/jupyter/jupyterVariables'; -import { KernelSelectionProvider } from '../../client/datascience/jupyter/kernels/kernelSelections'; -import { KernelSelector } from '../../client/datascience/jupyter/kernels/kernelSelector'; -import { KernelService } from '../../client/datascience/jupyter/kernels/kernelService'; -import { KernelSwitcher } from '../../client/datascience/jupyter/kernels/kernelSwitcher'; -import { NotebookStarter } from '../../client/datascience/jupyter/notebookStarter'; -import { ServerPreload } from '../../client/datascience/jupyter/serverPreload'; -import { JupyterServerSelector } from '../../client/datascience/jupyter/serverSelector'; -import { PlotViewer } from '../../client/datascience/plotting/plotViewer'; -import { PlotViewerProvider } from '../../client/datascience/plotting/plotViewerProvider'; -import { ProgressReporter } from '../../client/datascience/progress/progressReporter'; -import { StatusProvider } from '../../client/datascience/statusProvider'; -import { ThemeFinder } from '../../client/datascience/themeFinder'; -import { - ICellHashListener, - ICellHashLogger, - ICellHashProvider, - ICodeCssGenerator, - ICodeLensFactory, - ICodeWatcher, - IDataScience, - IDataScienceCodeLensProvider, - IDataScienceCommandListener, - IDataScienceErrorHandler, - IDataViewer, - IDataViewerProvider, - IDebugLocationTracker, - IGatherLogger, - IGatherProvider, - IInteractiveWindow, - IInteractiveWindowListener, - IInteractiveWindowProvider, - IJupyterCommandFactory, - IJupyterDebugger, - IJupyterExecution, - IJupyterInterpreterDependencyManager, - IJupyterPasswordConnect, - IJupyterSessionManagerFactory, - IJupyterSubCommandExecutionService, - IJupyterVariables, - INotebookEditor, - INotebookEditorProvider, - INotebookExecutionLogger, - INotebookExporter, - INotebookImporter, - INotebookProvider, - INotebookServer, - INotebookStorage, - IPlotViewer, - IPlotViewerProvider, - IStatusProvider, - IThemeFinder -} from '../../client/datascience/types'; -import { ProtocolParser } from '../../client/debugger/debugAdapter/Common/protocolParser'; -import { IProtocolParser } from '../../client/debugger/debugAdapter/types'; -import { - EnvironmentActivationService, - EnvironmentActivationServiceCache -} from '../../client/interpreter/activation/service'; -import { IEnvironmentActivationService } from '../../client/interpreter/activation/types'; -import { InterpreterComparer } from '../../client/interpreter/configuration/interpreterComparer'; -import { InterpreterSelector } from '../../client/interpreter/configuration/interpreterSelector'; -import { PythonPathUpdaterService } from '../../client/interpreter/configuration/pythonPathUpdaterService'; -import { PythonPathUpdaterServiceFactory } from '../../client/interpreter/configuration/pythonPathUpdaterServiceFactory'; -import { - IInterpreterComparer, - IInterpreterSelector, - IPythonPathUpdaterServiceFactory, - IPythonPathUpdaterServiceManager -} from '../../client/interpreter/configuration/types'; -import { - CONDA_ENV_FILE_SERVICE, - CONDA_ENV_SERVICE, - CURRENT_PATH_SERVICE, - GLOBAL_VIRTUAL_ENV_SERVICE, - ICondaService, - IInterpreterDisplay, - IInterpreterHelper, - IInterpreterLocatorHelper, - IInterpreterLocatorService, - IInterpreterService, - IInterpreterVersionService, - IInterpreterWatcher, - IInterpreterWatcherBuilder, - IKnownSearchPathsForInterpreters, - INTERPRETER_LOCATOR_SERVICE, - InterpreterType, - IPipEnvService, - IShebangCodeLensProvider, - IVirtualEnvironmentsSearchPathProvider, - KNOWN_PATH_SERVICE, - PIPENV_SERVICE, - PythonInterpreter, - WINDOWS_REGISTRY_SERVICE, - WORKSPACE_VIRTUAL_ENV_SERVICE -} from '../../client/interpreter/contracts'; -import { ShebangCodeLensProvider } from '../../client/interpreter/display/shebangCodeLensProvider'; -import { InterpreterHelper } from '../../client/interpreter/helpers'; -import { InterpreterVersionService } from '../../client/interpreter/interpreterVersion'; -import { PythonInterpreterLocatorService } from '../../client/interpreter/locators'; -import { InterpreterLocatorHelper } from '../../client/interpreter/locators/helpers'; -import { CacheableLocatorPromiseCache } from '../../client/interpreter/locators/services/cacheableLocatorService'; -import { CondaEnvFileService } from '../../client/interpreter/locators/services/condaEnvFileService'; -import { CondaEnvService } from '../../client/interpreter/locators/services/condaEnvService'; -import { - CurrentPathService, - PythonInPathCommandProvider -} from '../../client/interpreter/locators/services/currentPathService'; -import { - GlobalVirtualEnvironmentsSearchPathProvider, - GlobalVirtualEnvService -} from '../../client/interpreter/locators/services/globalVirtualEnvService'; -import { InterpreterHashProvider } from '../../client/interpreter/locators/services/hashProvider'; -import { InterpeterHashProviderFactory } from '../../client/interpreter/locators/services/hashProviderFactory'; -import { InterpreterFilter } from '../../client/interpreter/locators/services/interpreterFilter'; -import { InterpreterWatcherBuilder } from '../../client/interpreter/locators/services/interpreterWatcherBuilder'; -import { - KnownPathsService, - KnownSearchPathsForInterpreters -} from '../../client/interpreter/locators/services/KnownPathsService'; -import { PipEnvService } from '../../client/interpreter/locators/services/pipEnvService'; -import { PipEnvServiceHelper } from '../../client/interpreter/locators/services/pipEnvServiceHelper'; -import { WindowsRegistryService } from '../../client/interpreter/locators/services/windowsRegistryService'; -import { WindowsStoreInterpreter } from '../../client/interpreter/locators/services/windowsStoreInterpreter'; -import { - WorkspaceVirtualEnvironmentsSearchPathProvider, - WorkspaceVirtualEnvService -} from '../../client/interpreter/locators/services/workspaceVirtualEnvService'; -import { WorkspaceVirtualEnvWatcherService } from '../../client/interpreter/locators/services/workspaceVirtualEnvWatcherService'; -import { IPipEnvServiceHelper, IPythonInPathCommandProvider } from '../../client/interpreter/locators/types'; -import { registerInterpreterTypes } from '../../client/interpreter/serviceRegistry'; -import { VirtualEnvironmentManager } from '../../client/interpreter/virtualEnvs'; -import { IVirtualEnvironmentManager } from '../../client/interpreter/virtualEnvs/types'; -import { LanguageServerSurveyBanner } from '../../client/languageServices/languageServerSurveyBanner'; -import { CodeExecutionHelper } from '../../client/terminals/codeExecution/helper'; -import { ICodeExecutionHelper } from '../../client/terminals/types'; -import { IVsCodeApi } from '../../datascience-ui/react-common/postOffice'; -import { MockOutputChannel } from '../mockClasses'; -import { MockAutoSelectionService } from '../mocks/autoSelector'; -import { UnitTestIocContainer } from '../testing/serviceRegistry'; -import { MockCommandManager } from './mockCommandManager'; -import { MockCustomEditorService } from './mockCustomEditorService'; -import { MockDebuggerService } from './mockDebugService'; -import { MockDocumentManager } from './mockDocumentManager'; -import { MockExtensions } from './mockExtensions'; -import { MockFileSystem } from './mockFileSystem'; -import { MockJupyterManager, SupportedCommands } from './mockJupyterManager'; -import { MockJupyterManagerFactory } from './mockJupyterManagerFactory'; -import { MockLanguageServerAnalysisOptions } from './mockLanguageServerAnalysisOptions'; -import { MockLanguageServerProxy } from './mockLanguageServerProxy'; -import { MockLiveShareApi } from './mockLiveShare'; -import { MockWorkspaceConfiguration } from './mockWorkspaceConfig'; -import { MockWorkspaceFolder } from './mockWorkspaceFolder'; -import { TestInteractiveWindowProvider } from './testInteractiveWindowProvider'; -import { TestNativeEditorProvider } from './testNativeEditorProvider'; -import { TestPersistentStateFactory } from './testPersistentStateFactory'; -import { WebBrowserPanelProvider } from './uiTests/webBrowserPanelProvider'; - -export class DataScienceIocContainer extends UnitTestIocContainer { - public get workingInterpreter() { - return this.workingPython; - } - - public get workingInterpreter2() { - return this.workingPython2; - } - - public get onContextSet(): Event<{ name: string; value: boolean }> { - return this.contextSetEvent.event; - } - - public get mockJupyter(): MockJupyterManager | undefined { - return this.jupyterMock ? this.jupyterMock.getManager() : undefined; - } - private static jupyterInterpreters: PythonInterpreter[] = []; - public webPanelListener: IWebPanelMessageListener | undefined; - public readonly useCommandFinderForJupyterServer = false; - public wrapper: ReactWrapper, React.Component> | undefined; - public wrapperCreatedPromise: Deferred | undefined; - public postMessage: ((ev: MessageEvent) => void) | undefined; - public applicationShell!: TypeMoq.IMock; - // tslint:disable-next-line:no-any - public datascience!: TypeMoq.IMock; - private missedMessages: any[] = []; - private commandManager: MockCommandManager = new MockCommandManager(); - private setContexts: Record = {}; - private contextSetEvent: EventEmitter<{ name: string; value: boolean }> = new EventEmitter<{ - name: string; - value: boolean; - }>(); - private jupyterMock: MockJupyterManagerFactory | undefined; - private shouldMockJupyter: boolean; - private asyncRegistry: AsyncDisposableRegistry; - private configChangeEvent = new EventEmitter(); - private worksaceFoldersChangedEvent = new EventEmitter(); - private documentManager = new MockDocumentManager(); - private workingPython: PythonInterpreter = { - path: '/foo/bar/python.exe', - version: new SemVer('3.6.6-final'), - sysVersion: '1.0.0.0', - sysPrefix: 'Python', - displayName: 'Python', - type: InterpreterType.Unknown, - architecture: Architecture.x64 - }; - private workingPython2: PythonInterpreter = { - path: '/foo/baz/python.exe', - version: new SemVer('3.6.7-final'), - sysVersion: '1.0.0.0', - sysPrefix: 'Python', - displayName: 'Python', - type: InterpreterType.Unknown, - architecture: Architecture.x64 - }; - private extraListeners: ((m: string, p: any) => void)[] = []; - - private webPanelProvider = mock(WebPanelProvider); - private settingsMap = new Map(); - private configMap = new Map(); - private emptyConfig = new MockWorkspaceConfiguration(); - private workspaceFolders: MockWorkspaceFolder[] = []; - private defaultPythonPath: string | undefined; - private kernelServiceMock = mock(KernelService); - - constructor(private readonly uiTest: boolean = false) { - super(); - this.useVSCodeAPI = false; - const isRollingBuild = process.env ? process.env.VSCODE_PYTHON_ROLLING !== undefined : false; - this.shouldMockJupyter = !isRollingBuild; - this.asyncRegistry = new AsyncDisposableRegistry(); - } - - public async dispose(): Promise { - await this.asyncRegistry.dispose(); - await super.dispose(); - - if (!this.uiTest) { - // Blur window focus so we don't have editors polling - // tslint:disable-next-line: no-require-imports - const reactHelpers = require('./reactHelpers') as typeof import('./reactHelpers'); - reactHelpers.blurWindow(); - } - - if (this.wrapper && this.wrapper.length) { - this.wrapper.unmount(); - this.wrapper = undefined; - } - - // Bounce this so that our editor has time to shutdown - await sleep(150); - - if (!this.uiTest) { - // Clear out the monaco global services. Some of these services are preventing shutdown. - // tslint:disable: no-require-imports - const services = require('monaco-editor/esm/vs/editor/standalone/browser/standaloneServices') as any; - if (services.StaticServices) { - const keys = Object.keys(services.StaticServices); - keys.forEach(k => { - const service = services.StaticServices[k] as any; - if (service && service._value && service._value.dispose) { - if (typeof service._value.dispose === 'function') { - service._value.dispose(); - } - } - }); - } - // This file doesn't have an export so we can't force a dispose. Instead it has a 5 second timeout - const config = require('monaco-editor/esm/vs/editor/browser/config/configuration') as any; - if (config.getCSSBasedConfiguration) { - config.getCSSBasedConfiguration().dispose(); - } - } - - // Because there are outstanding promises holding onto this object, clear out everything we can - this.workspaceFolders = []; - this.settingsMap.clear(); - this.configMap.clear(); - this.setContexts = {}; - this.extraListeners = []; - this.webPanelListener = undefined; - reset(this.webPanelProvider); - - // Turn off the static maps for the environment and conda services. Otherwise this - // can mess up tests that don't depend upon them - CacheableLocatorPromiseCache.forceUseNormal(); - EnvironmentActivationServiceCache.forceUseNormal(); - } - - //tslint:disable:max-func-body-length - public registerDataScienceTypes(useCustomEditor: boolean = false) { - // Inform the cacheable locator service to use a static map so that it stays in memory in between tests - CacheableLocatorPromiseCache.forceUseStatic(); - - // Do the same thing for the environment variable activation service. - EnvironmentActivationServiceCache.forceUseStatic(); - - // Make sure the default python path is set. - this.defaultPythonPath = this.findPythonPath(); - - // Create the workspace service first as it's used to set config values. - this.createWorkspaceService(); - - // Setup our webpanel provider to create our dummy web panel - when(this.webPanelProvider.create(anything())).thenCall(this.onCreateWebPanel.bind(this)); - if (this.uiTest) { - this.serviceManager.addSingleton(IWebPanelProvider, WebBrowserPanelProvider); - } else { - this.serviceManager.addSingletonInstance( - IWebPanelProvider, - instance(this.webPanelProvider) - ); - } - - this.registerFileSystemTypes(); - this.serviceManager.rebindInstance(IFileSystem, new MockFileSystem()); - this.serviceManager.addSingleton(IJupyterExecution, JupyterExecutionFactory); - this.serviceManager.addSingleton( - IInteractiveWindowProvider, - TestInteractiveWindowProvider - ); - this.serviceManager.addSingletonInstance(UseCustomEditorApi, useCustomEditor); - this.serviceManager.addSingleton(IDataViewerProvider, DataViewerProvider); - this.serviceManager.addSingleton(IPlotViewerProvider, PlotViewerProvider); - this.serviceManager.add(IInteractiveWindow, InteractiveWindow); - this.serviceManager.add(IDataViewer, DataViewer); - this.serviceManager.add(IPlotViewer, PlotViewer); - this.serviceManager.add(INotebookImporter, JupyterImporter); - this.serviceManager.add(INotebookExporter, JupyterExporter); - this.serviceManager.addSingleton(ILiveShareApi, MockLiveShareApi); - this.serviceManager.addSingleton(IExtensions, MockExtensions); - this.serviceManager.add(INotebookServer, JupyterServerWrapper); - this.serviceManager.add(IJupyterCommandFactory, JupyterCommandFactory); - this.serviceManager.addSingleton(IThemeFinder, ThemeFinder); - this.serviceManager.addSingleton(ICodeCssGenerator, CodeCssGenerator); - this.serviceManager.addSingleton(IStatusProvider, StatusProvider); - this.serviceManager.addSingletonInstance( - IAsyncDisposableRegistry, - this.asyncRegistry - ); - this.serviceManager.addSingleton( - IEnvironmentActivationService, - EnvironmentActivationService - ); - this.serviceManager.add(ICodeWatcher, CodeWatcher); - this.serviceManager.add( - IDataScienceCodeLensProvider, - DataScienceCodeLensProvider - ); - this.serviceManager.add(ICodeExecutionHelper, CodeExecutionHelper); - this.serviceManager.add( - IDataScienceCommandListener, - InteractiveWindowCommandListener - ); - this.serviceManager.addSingleton(IDataScienceErrorHandler, DataScienceErrorHandler); - this.serviceManager.add(IInstallationChannelManager, InstallationChannelManager); - this.serviceManager.addSingleton(IJupyterVariables, JupyterVariables); - this.serviceManager.addSingleton(IJupyterDebugger, JupyterDebugger, undefined, [ - ICellHashListener - ]); - this.serviceManager.addSingleton(IDebugLocationTracker, DebugLocationTrackerFactory); - this.serviceManager.addSingleton(INotebookEditorProvider, TestNativeEditorProvider); - this.serviceManager.addSingleton( - DataViewerDependencyService, - DataViewerDependencyService - ); - this.serviceManager.add( - INotebookEditor, - useCustomEditor ? NativeEditor : NativeEditorOldWebView - ); - - this.serviceManager.add(INotebookStorage, NativeEditorStorage); - this.serviceManager.addSingletonInstance( - ICustomEditorService, - new MockCustomEditorService(this.asyncRegistry, this.commandManager) - ); - this.serviceManager.addSingleton( - IDataScienceCommandListener, - NativeEditorCommandListener - ); - this.serviceManager.addSingletonInstance( - IOutputChannel, - mock(MockOutputChannel), - JUPYTER_OUTPUT_CHANNEL - ); - this.serviceManager.addSingleton(ICryptoUtils, CryptoUtils); - this.serviceManager.addSingleton( - IExtensionSingleActivationService, - ServerPreload - ); - const mockExtensionContext = TypeMoq.Mock.ofType(); - mockExtensionContext.setup(m => m.globalStoragePath).returns(() => os.tmpdir()); - this.serviceManager.addSingletonInstance(IExtensionContext, mockExtensionContext.object); - - const mockServerSelector = mock(JupyterServerSelector); - this.serviceManager.addSingletonInstance( - JupyterServerSelector, - instance(mockServerSelector) - ); - - this.serviceManager.addSingleton(ITerminalHelper, TerminalHelper); - this.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - Bash, - TerminalActivationProviders.bashCShellFish - ); - this.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - CommandPromptAndPowerShell, - TerminalActivationProviders.commandPromptAndPowerShell - ); - this.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - PyEnvActivationCommandProvider, - TerminalActivationProviders.pyenv - ); - this.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - CondaActivationCommandProvider, - TerminalActivationProviders.conda - ); - this.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - PipEnvActivationCommandProvider, - TerminalActivationProviders.pipenv - ); - this.serviceManager.addSingleton(ITerminalManager, TerminalManager); - - //const configuration = this.serviceManager.get(IConfigurationService); - //const pythonSettings = configuration.getSettings(); - const languageServerType = LanguageServerType.Microsoft; // pythonSettings.languageServer; - - this.serviceManager.addSingleton(ILanguageServerProxy, MockLanguageServerProxy); - this.serviceManager.addSingleton( - ILanguageServerCache, - LanguageServerExtensionActivationService - ); - this.serviceManager.addSingleton(ILanguageServerExtension, LanguageServerExtension); - - this.serviceManager.add( - ILanguageServerActivator, - JediExtensionActivator, - LanguageServerType.Jedi - ); - if (languageServerType === LanguageServerType.Microsoft) { - this.serviceManager.add( - ILanguageServerActivator, - DotNetLanguageServerActivator, - LanguageServerType.Microsoft - ); - this.serviceManager.add(ILanguageServerManager, DotNetLanguageServerManager); - this.serviceManager.addSingleton( - ILanguageServerAnalysisOptions, - MockLanguageServerAnalysisOptions - ); - } else if (languageServerType === LanguageServerType.Node) { - this.serviceManager.add( - ILanguageServerActivator, - NodeLanguageServerActivator, - LanguageServerType.Node - ); - this.serviceManager.add(ILanguageServerManager, NodeLanguageServerManager); - } - - this.serviceManager.addSingleton(INotebookProvider, NotebookProvider); - - this.serviceManager.add(IInteractiveWindowListener, IntellisenseProvider); - this.serviceManager.add(IInteractiveWindowListener, AutoSaveService); - this.serviceManager.add(IInteractiveWindowListener, GatherListener); - this.serviceManager.addSingleton( - IPyWidgetMessageDispatcherFactory, - IPyWidgetMessageDispatcherFactory - ); - if (this.uiTest) { - this.serviceManager.add(IInteractiveWindowListener, IPyWidgetHandler); - } - this.serviceManager.add(IProtocolParser, ProtocolParser); - this.serviceManager.addSingleton(IDebugService, MockDebuggerService); - this.serviceManager.add(ICellHashProvider, CellHashProvider); - this.serviceManager.add(ICellHashLogger, CellHashLogger, undefined, [ - INotebookExecutionLogger - ]); - this.serviceManager.add(IGatherProvider, GatherProvider); - this.serviceManager.add(IGatherLogger, GatherLogger, undefined, [INotebookExecutionLogger]); - this.serviceManager.addSingleton(ICodeLensFactory, CodeLensFactory, undefined, [ - IInteractiveWindowListener - ]); - this.serviceManager.addSingleton(IShellDetector, TerminalNameShellDetector); - this.serviceManager.addSingleton(JupyterCommandFinder, JupyterCommandFinder); - this.serviceManager.addSingleton( - IDiagnosticsService, - LSNotSupportedDiagnosticService, - LSNotSupportedDiagnosticServiceId - ); - this.serviceManager.addSingleton( - ILanguageServerCompatibilityService, - LanguageServerCompatibilityService - ); - this.serviceManager.addSingleton>( - IDiagnosticHandlerService, - DiagnosticCommandPromptHandlerService, - DiagnosticCommandPromptHandlerServiceId - ); - this.serviceManager.addSingleton(IDiagnosticFilterService, DiagnosticFilterService); - this.serviceManager.addSingleton(NotebookStarter, NotebookStarter); - this.serviceManager.addSingleton(KernelSelector, KernelSelector); - this.serviceManager.addSingleton(KernelSelectionProvider, KernelSelectionProvider); - this.serviceManager.addSingleton(KernelSwitcher, KernelSwitcher); - this.serviceManager.addSingleton(IProductService, ProductService); - this.serviceManager.addSingleton( - IProductPathService, - CTagsProductPathService, - ProductType.WorkspaceSymbols - ); - this.serviceManager.addSingleton( - IProductPathService, - FormatterProductPathService, - ProductType.Formatter - ); - this.serviceManager.addSingleton( - IProductPathService, - LinterProductPathService, - ProductType.Linter - ); - this.serviceManager.addSingleton( - IProductPathService, - TestFrameworkProductPathService, - ProductType.TestFramework - ); - this.serviceManager.addSingleton( - IProductPathService, - RefactoringLibraryProductPathService, - ProductType.RefactoringLibrary - ); - this.serviceManager.addSingleton( - IProductPathService, - DataScienceProductPathService, - ProductType.DataScience - ); - this.serviceManager.addSingleton(IMultiStepInputFactory, MultiStepInputFactory); - - // No need of reporting progress. - const progressReporter = mock(ProgressReporter); - when(progressReporter.createProgressIndicator(anything())).thenReturn({ - dispose: noop, - token: new CancellationTokenSource().token - }); - this.serviceManager.addSingletonInstance(ProgressReporter, instance(progressReporter)); - - // Don't check for dot net compatibility - const dotNetCompability = mock(DotNetCompatibilityService); - when(dotNetCompability.isSupported()).thenResolve(true); - this.serviceManager.addSingletonInstance( - IDotNetCompatibilityService, - instance(dotNetCompability) - ); - - // Don't allow a banner to show up - const extensionBanner = mock(LanguageServerSurveyBanner); - this.serviceManager.addSingletonInstance( - IPythonExtensionBanner, - instance(extensionBanner), - BANNER_NAME_LS_SURVEY - ); - - // Don't allow the download to happen - const downloader = mock(LanguageServerDownloader); - this.serviceManager.addSingletonInstance( - ILanguageServerDownloader, - instance(downloader) - ); - - const folderService = mock(DotNetLanguageServerFolderService); - const packageService = mock(DotNetLanguageServerPackageService); - this.serviceManager.addSingletonInstance( - ILanguageServerFolderService, - instance(folderService) - ); - this.serviceManager.addSingletonInstance( - ILanguageServerPackageService, - instance(packageService) - ); - - // Disable experiments. - const experimentManager = mock(ExperimentsManager); - when(experimentManager.inExperiment(anything())).thenReturn(false); - when(experimentManager.activate()).thenResolve(); - this.serviceManager.addSingletonInstance(IExperimentsManager, instance(experimentManager)); - - // Setup our command list - this.commandManager.registerCommand('setContext', (name: string, value: boolean) => { - this.setContexts[name] = value; - this.contextSetEvent.fire({ name: name, value: value }); - }); - this.serviceManager.addSingletonInstance(ICommandManager, this.commandManager); - - // Mock the app shell - const appShell = (this.applicationShell = TypeMoq.Mock.ofType()); - const configurationService = TypeMoq.Mock.ofType(); - this.datascience = TypeMoq.Mock.ofType(); - - configurationService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(this.getSettings.bind(this)); - - const startTime = Date.now(); - this.datascience.setup(d => d.activationStartTime).returns(() => startTime); - - this.serviceManager.addSingleton( - IEnvironmentVariablesProvider, - EnvironmentVariablesProvider - ); - - this.serviceManager.addSingletonInstance(IApplicationShell, appShell.object); - this.serviceManager.addSingleton(IClipboard, ClipboardService); - this.serviceManager.addSingletonInstance(IDocumentManager, this.documentManager); - this.serviceManager.addSingletonInstance( - IConfigurationService, - configurationService.object - ); - this.serviceManager.addSingletonInstance(IDataScience, this.datascience.object); - this.serviceManager.addSingleton(IBufferDecoder, BufferDecoder); - this.serviceManager.addSingleton( - IEnvironmentVariablesService, - EnvironmentVariablesService - ); - this.serviceManager.addSingleton(IPathUtils, PathUtils); - this.serviceManager.addSingletonInstance(IsWindows, IS_WINDOWS); - - const globalStorage = this.serviceManager.get(IMemento, GLOBAL_MEMENTO); - const localStorage = this.serviceManager.get(IMemento, WORKSPACE_MEMENTO); - - // Create a custom persistent state factory that remembers specific things between tests - this.serviceManager.addSingletonInstance( - IPersistentStateFactory, - new TestPersistentStateFactory(globalStorage, localStorage) - ); - - const currentProcess = new CurrentProcess(); - this.serviceManager.addSingletonInstance(ICurrentProcess, currentProcess); - this.serviceManager.addSingleton(IRegistry, RegistryImplementation); - - this.serviceManager.addSingleton( - JupyterInterpreterStateStore, - JupyterInterpreterStateStore - ); - this.serviceManager.addSingleton( - IExtensionSingleActivationService, - JupyterInterpreterSelectionCommand - ); - this.serviceManager.addSingleton( - JupyterInterpreterSelector, - JupyterInterpreterSelector - ); - this.serviceManager.addSingleton( - JupyterInterpreterDependencyService, - JupyterInterpreterDependencyService - ); - this.serviceManager.addSingleton( - JupyterInterpreterService, - JupyterInterpreterService - ); - this.serviceManager.addSingleton( - JupyterInterpreterOldCacheStateStore, - JupyterInterpreterOldCacheStateStore - ); - this.serviceManager.addSingleton( - ActiveEditorContextService, - ActiveEditorContextService - ); - - if (this.useCommandFinderForJupyterServer) { - this.serviceManager.addSingleton( - IJupyterSubCommandExecutionService, - JupyterCommandFinderInterpreterExecutionService - ); - this.serviceManager.addSingleton( - IJupyterInterpreterDependencyManager, - JupyterCommandInterpreterDependencyService - ); - } else { - this.serviceManager.addSingleton( - IJupyterSubCommandExecutionService, - JupyterInterpreterSubCommandExecutionService - ); - this.serviceManager.addSingleton( - IJupyterInterpreterDependencyManager, - JupyterInterpreterSubCommandExecutionService - ); - } - - const interpreterDisplay = TypeMoq.Mock.ofType(); - interpreterDisplay.setup(i => i.refresh(TypeMoq.It.isAny())).returns(() => Promise.resolve()); - - // Create our jupyter mock if necessary - if (this.shouldMockJupyter) { - this.jupyterMock = new MockJupyterManagerFactory(this.serviceManager); - // When using mocked Jupyter, default to using default kernel. - when(this.kernelServiceMock.searchAndRegisterKernel(anything(), anything())).thenResolve(undefined); - this.serviceManager.addSingletonInstance(KernelService, instance(this.kernelServiceMock)); - - this.serviceManager.addSingleton( - InterpeterHashProviderFactory, - InterpeterHashProviderFactory - ); - this.serviceManager.addSingleton(WindowsStoreInterpreter, WindowsStoreInterpreter); - this.serviceManager.addSingleton(InterpreterHashProvider, InterpreterHashProvider); - this.serviceManager.addSingleton(InterpreterFilter, InterpreterFilter); - this.serviceManager.add( - IInterpreterWatcher, - WorkspaceVirtualEnvWatcherService, - WORKSPACE_VIRTUAL_ENV_SERVICE - ); - this.serviceManager.addSingleton( - IInterpreterWatcherBuilder, - InterpreterWatcherBuilder - ); - this.serviceManager.add( - IInterpreterWatcher, - WorkspaceVirtualEnvWatcherService, - WORKSPACE_VIRTUAL_ENV_SERVICE - ); - this.serviceManager.addSingleton( - IInterpreterWatcherBuilder, - InterpreterWatcherBuilder - ); - - this.serviceManager.addSingleton( - IInterpreterLocatorService, - PythonInterpreterLocatorService, - INTERPRETER_LOCATOR_SERVICE - ); - this.serviceManager.addSingleton( - IInterpreterLocatorService, - CondaEnvFileService, - CONDA_ENV_FILE_SERVICE - ); - this.serviceManager.addSingleton( - IInterpreterLocatorService, - CondaEnvService, - CONDA_ENV_SERVICE - ); - this.serviceManager.addSingleton( - IInterpreterLocatorService, - CurrentPathService, - CURRENT_PATH_SERVICE - ); - this.serviceManager.addSingleton( - IInterpreterLocatorService, - GlobalVirtualEnvService, - GLOBAL_VIRTUAL_ENV_SERVICE - ); - this.serviceManager.addSingleton( - IInterpreterLocatorService, - WorkspaceVirtualEnvService, - WORKSPACE_VIRTUAL_ENV_SERVICE - ); - this.serviceManager.addSingleton( - IInterpreterLocatorService, - PipEnvService, - PIPENV_SERVICE - ); - this.serviceManager.addSingleton(IPipEnvService, PipEnvService); - this.serviceManager.addSingleton( - IInterpreterLocatorService, - WindowsRegistryService, - WINDOWS_REGISTRY_SERVICE - ); - - this.serviceManager.addSingleton( - IInterpreterLocatorService, - KnownPathsService, - KNOWN_PATH_SERVICE - ); - - this.serviceManager.addSingleton(IInterpreterHelper, InterpreterHelper); - this.serviceManager.addSingleton( - IInterpreterLocatorHelper, - InterpreterLocatorHelper - ); - this.serviceManager.addSingleton(IInterpreterComparer, InterpreterComparer); - this.serviceManager.addSingleton( - IInterpreterVersionService, - InterpreterVersionService - ); - this.serviceManager.addSingleton( - IPythonInPathCommandProvider, - PythonInPathCommandProvider - ); - - this.serviceManager.addSingleton(IPipEnvServiceHelper, PipEnvServiceHelper); - this.serviceManager.addSingleton(IInterpreterSelector, InterpreterSelector); - this.serviceManager.addSingleton( - IShebangCodeLensProvider, - ShebangCodeLensProvider - ); - this.serviceManager.addSingleton( - IPythonPathUpdaterServiceFactory, - PythonPathUpdaterServiceFactory - ); - this.serviceManager.addSingleton( - IPythonPathUpdaterServiceManager, - PythonPathUpdaterService - ); - - // Don't use conda at all when mocking - const condaService = TypeMoq.Mock.ofType(); - this.serviceManager.addSingletonInstance(ICondaService, condaService.object); - condaService.setup(c => c.isCondaAvailable()).returns(() => Promise.resolve(false)); - condaService.setup(c => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); - condaService.setup(c => c.condaEnvironmentsFile).returns(() => undefined); - - this.serviceManager.addSingleton( - IVirtualEnvironmentsSearchPathProvider, - GlobalVirtualEnvironmentsSearchPathProvider, - 'global' - ); - this.serviceManager.addSingleton( - IVirtualEnvironmentsSearchPathProvider, - WorkspaceVirtualEnvironmentsSearchPathProvider, - 'workspace' - ); - this.serviceManager.addSingleton( - IVirtualEnvironmentManager, - VirtualEnvironmentManager - ); - this.serviceManager.add( - IKnownSearchPathsForInterpreters, - KnownSearchPathsForInterpreters - ); - this.serviceManager.addSingleton( - IPythonInPathCommandProvider, - PythonInPathCommandProvider - ); - this.serviceManager.addSingletonInstance( - IInterpreterDisplay, - interpreterDisplay.object - ); - } else { - this.serviceManager.addSingleton(IInstaller, ProductInstaller); - this.serviceManager.addSingleton(KernelService, KernelService); - this.serviceManager.addSingleton(IProcessServiceFactory, ProcessServiceFactory); - this.serviceManager.addSingleton(IPythonExecutionFactory, PythonExecutionFactory); - - // Make sure full interpreter services are available. - registerInterpreterTypes(this.serviceManager); - - // Rebind the interpreter display as we don't want to use the real one - this.serviceManager.rebindInstance(IInterpreterDisplay, interpreterDisplay.object); - - this.serviceManager.addSingleton( - IJupyterSessionManagerFactory, - JupyterSessionManagerFactory - ); - this.serviceManager.addSingleton(IJupyterPasswordConnect, JupyterPasswordConnect); - this.serviceManager.addSingleton(IProcessLogger, ProcessLogger); - } - this.serviceManager.addSingleton(NativeEditorSynchronizer, NativeEditorSynchronizer); - // Disable syncrhonizing edits - this.serviceContainer.get(NativeEditorSynchronizer).disable(); - const dummyDisposable = { - dispose: () => { - return; - } - }; - - appShell.setup(a => a.showErrorMessage(TypeMoq.It.isAnyString())).returns(() => Promise.resolve('')); - appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve('')); - appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns((_a1: string, a2: string, _a3: string) => Promise.resolve(a2)); - appShell - .setup(a => - a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) - ) - .returns((_a1: string, a2: string, _a3: string, _a4: string) => Promise.resolve(a2)); - appShell - .setup(a => a.showSaveDialog(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(Uri.file('test.ipynb'))); - appShell.setup(a => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); - appShell.setup(a => a.showInputBox(TypeMoq.It.isAny())).returns(() => Promise.resolve('Python')); - - const interpreterManager = this.serviceContainer.get(IInterpreterService); - interpreterManager.initialize(); - - if (this.mockJupyter) { - this.addInterpreter(this.workingPython2, SupportedCommands.all); - this.addInterpreter(this.workingPython, SupportedCommands.all); - } - } - public setFileContents(uri: Uri, contents: string) { - const fileSystem = this.serviceManager.get(IFileSystem) as MockFileSystem; - fileSystem.addFileContents(uri.fsPath, contents); - } - - public async activate(): Promise { - // Activate all of the extension activation services - const activationServices = this.serviceManager.getAll( - IExtensionSingleActivationService - ); - - await Promise.all(activationServices.map(a => a.activate())); - - // Then force our interpreter to be one that supports jupyter (unless in a mock state when we don't have to) - if (!this.mockJupyter) { - const interpreterService = this.serviceManager.get(IInterpreterService); - const activeInterpreter = await interpreterService.getActiveInterpreter(); - if (!activeInterpreter || !(await this.hasJupyter(activeInterpreter))) { - const list = await this.getJupyterInterpreters(); - this.forceSettingsChanged(undefined, list[0].path); - - // Also set this as the interpreter to use for jupyter - await this.serviceManager - .get(JupyterInterpreterService) - .setAsSelectedInterpreter(list[0]); - } - } - } - - public get kernelService() { - return this.kernelServiceMock; - } - - // tslint:disable:any - public createWebView( - mount: () => ReactWrapper, React.Component>, - role: vsls.Role = vsls.Role.None - ) { - // Force the container to mock actual live share if necessary - if (role !== vsls.Role.None) { - const liveShareTest = this.get(ILiveShareApi) as ILiveShareTestingApi; - liveShareTest.forceRole(role); - } - - // We need to mount the react control before we even create an interactive window object. Otherwise the mount will miss rendering some parts - this.mountReactControl(mount); - } - - public getContext(name: string): boolean { - if (this.setContexts.hasOwnProperty(name)) { - return this.setContexts[name]; - } - - return false; - } - - public getSettings(resource?: Uri) { - const key = this.getResourceKey(resource); - let setting = this.settingsMap.get(key); - if (!setting) { - // Make sure we have the default config for this resource first. - this.getWorkspaceConfig('python', resource); - setting = new (class extends PythonSettings { - public fireChangeEvent() { - this.changed.fire(); - } - })(resource, new MockAutoSelectionService(), this.serviceManager.get(IWorkspaceService)); - this.settingsMap.set(key, setting); - } - return setting; - } - - public forceSettingsChanged(resource: Resource, newPath: string, datascienceSettings?: IDataScienceSettings) { - const settings = this.getSettings(resource); - settings.pythonPath = newPath; - settings.datascience = datascienceSettings ? datascienceSettings : settings.datascience; - - // The workspace config must be updated too as a config change event will cause the data to be reread from - // the config. - const config = this.getWorkspaceConfig('python', resource); - config.update('pythonPath', newPath).ignoreErrors(); - config.update('dataScience', settings.datascience).ignoreErrors(); - settings.fireChangeEvent(); - this.configChangeEvent.fire({ - affectsConfiguration(_s: string, _r?: Uri): boolean { - return true; - } - }); - } - - public async getJupyterCapableInterpreter(): Promise { - const list = await this.getJupyterInterpreters(); - return list ? list[0] : undefined; - } - - public async getJupyterInterpreters(): Promise { - // This should be cacheable as we don't install new interpreters during tests - if (DataScienceIocContainer.jupyterInterpreters.length > 0) { - return DataScienceIocContainer.jupyterInterpreters; - } - const list = await this.get(IInterpreterService).getInterpreters(undefined); - const promises = list.map(f => this.hasJupyter(f).then(b => (b ? f : undefined))); - const resolved = await Promise.all(promises); - DataScienceIocContainer.jupyterInterpreters = resolved.filter(r => r) as PythonInterpreter[]; - return DataScienceIocContainer.jupyterInterpreters; - } - - public addWorkspaceFolder(folderPath: string) { - const workspaceFolder = new MockWorkspaceFolder(folderPath, this.workspaceFolders.length); - this.workspaceFolders.push(workspaceFolder); - return workspaceFolder; - } - - public addResourceToFolder(resource: Uri, folderPath: string) { - let folder = this.workspaceFolders.find(f => f.uri.fsPath === folderPath); - if (!folder) { - folder = this.addWorkspaceFolder(folderPath); - } - folder.ownedResources.add(resource.toString()); - } - - public get(serviceIdentifier: interfaces.ServiceIdentifier, name?: string | number | symbol): T { - return this.serviceManager.get(serviceIdentifier, name); - } - - public getAll(serviceIdentifier: interfaces.ServiceIdentifier, name?: string | number | symbol): T[] { - return this.serviceManager.getAll(serviceIdentifier, name); - } - - public addDocument(code: string, file: string) { - this.documentManager.addDocument(code, file); - } - - public addMessageListener(callback: (m: string, p: any) => void) { - this.extraListeners.push(callback); - } - - public removeMessageListener(callback: (m: string, p: any) => void) { - const index = this.extraListeners.indexOf(callback); - if (index >= 0) { - this.extraListeners.splice(index, 1); - } - } - - public addInterpreter(newInterpreter: PythonInterpreter, commands: SupportedCommands) { - if (this.mockJupyter) { - this.mockJupyter.addInterpreter(newInterpreter, commands); - } - } - - public postMessageToWebPanel(msg: any) { - if (this.webPanelListener) { - this.webPanelListener.onMessage(msg.type, msg.payload); - } else { - this.missedMessages.push(msg); - } - - if (this.extraListeners.length) { - this.extraListeners.forEach(e => e(msg.type, msg.payload)); - } - if (this.wrapperCreatedPromise && !this.wrapperCreatedPromise.resolved) { - this.wrapperCreatedPromise.resolve(); - } - - // Clear out msg payload - delete msg.payload; - } - - public getWorkspaceConfig(section: string | undefined, resource?: Resource): MockWorkspaceConfiguration { - if (!section || section !== 'python') { - return this.emptyConfig; - } - const key = this.getResourceKey(resource); - let result = this.configMap.get(key); - if (!result) { - result = this.generatePythonWorkspaceConfig(); - this.configMap.set(key, result); - } - return result; - } - - private createWebPanel(): IWebPanel { - const webPanel = mock(WebPanel); - when(webPanel.postMessage(anything())).thenCall(m => { - // tslint:disable-next-line: no-require-imports - const reactHelpers = require('./reactHelpers') as typeof import('./reactHelpers'); - const message = reactHelpers.createMessageEvent(m); - if (this.postMessage) { - this.postMessage(message); - } - if (m.payload) { - delete m.payload; - } - }); - when((webPanel as any).then).thenReturn(undefined); - return instance(webPanel); - } - - private async onCreateWebPanel(options: IWebPanelOptions) { - // Keep track of the current listener. It listens to messages through the vscode api - this.webPanelListener = options.listener; - - // Send messages that were already posted but were missed. - // During normal operation, the react control will not be created before - // the webPanelListener - if (this.missedMessages.length && this.webPanelListener) { - // This needs to be async because we are being called in the ctor of the webpanel. It can't - // handle some messages during the ctor. - setTimeout(() => { - this.missedMessages.forEach(m => - this.webPanelListener ? this.webPanelListener.onMessage(m.type, m.payload) : noop() - ); - }, 0); - - // Note, you might think we should clean up the messages. However since the mount only occurs once, we might - // create multiple webpanels with the same mount. We need to resend these messages to - // other webpanels that get created with the same mount. - } - - // Return our dummy web panel - return this.createWebPanel(); - } - - private generatePythonWorkspaceConfig(): MockWorkspaceConfiguration { - // Create a dummy settings just to setup the workspace config - const pythonSettings = new PythonSettings(undefined, new MockAutoSelectionService()); - pythonSettings.pythonPath = this.defaultPythonPath!; - pythonSettings.datascience = { - allowImportFromNotebook: true, - jupyterLaunchTimeout: 20000, - jupyterLaunchRetries: 3, - enabled: true, - jupyterServerURI: 'local', - // tslint:disable-next-line: no-invalid-template-strings - notebookFileRoot: '${fileDirname}', - changeDirOnImportExport: false, - useDefaultConfigForJupyter: true, - jupyterInterruptTimeout: 10000, - searchForJupyter: true, - showCellInputCode: true, - collapseCellInputCodeByDefault: true, - allowInput: true, - maxOutputSize: 400, - errorBackgroundColor: '#FFFFFF', - sendSelectionToInteractiveWindow: false, - codeRegularExpression: '^(#\\s*%%|#\\s*\\|#\\s*In\\[\\d*?\\]|#\\s*In\\[ \\])', - markdownRegularExpression: '^(#\\s*%%\\s*\\[markdown\\]|#\\s*\\)', - variableExplorerExclude: 'module;function;builtin_function_or_method', - liveShareConnectionTimeout: 100, - enablePlotViewer: true, - stopOnFirstLineWhileDebugging: true, - stopOnError: true, - addGotoCodeLenses: true, - enableCellCodeLens: true, - runStartupCommands: '', - debugJustMyCode: true, - variableQueries: [], - jupyterCommandLineArguments: [], - disableJupyterAutoStart: true - }; - pythonSettings.jediEnabled = false; - pythonSettings.downloadLanguageServer = false; - const folders = ['Envs', '.virtualenvs']; - pythonSettings.venvFolders = folders; - pythonSettings.venvPath = path.join('~', 'foo'); - pythonSettings.terminal = { - executeInFileDir: false, - launchArgs: [], - activateEnvironment: true, - activateEnvInCurrentTerminal: false - }; - - // Use these settings to default all of the settings in a python configuration - return new MockWorkspaceConfiguration(pythonSettings); - } - - private createWorkspaceService() { - class MockFileSystemWatcher implements FileSystemWatcher { - public ignoreCreateEvents: boolean = false; - public ignoreChangeEvents: boolean = false; - public ignoreDeleteEvents: boolean = false; - //tslint:disable-next-line:no-any - public onDidChange(_listener: (e: Uri) => any, _thisArgs?: any, _disposables?: Disposable[]): Disposable { - return { dispose: noop }; - } - //tslint:disable-next-line:no-any - public onDidDelete(_listener: (e: Uri) => any, _thisArgs?: any, _disposables?: Disposable[]): Disposable { - return { dispose: noop }; - } - //tslint:disable-next-line:no-any - public onDidCreate(_listener: (e: Uri) => any, _thisArgs?: any, _disposables?: Disposable[]): Disposable { - return { dispose: noop }; - } - public dispose() { - noop(); - } - } - - const workspaceService = mock(WorkspaceService); - this.serviceManager.addSingletonInstance(IWorkspaceService, instance(workspaceService)); - when(workspaceService.onDidChangeConfiguration).thenReturn(this.configChangeEvent.event); - when(workspaceService.onDidChangeWorkspaceFolders).thenReturn(this.worksaceFoldersChangedEvent.event); - - // Create another config for other parts of the workspace config. - when(workspaceService.getConfiguration(anything())).thenCall(this.getWorkspaceConfig.bind(this)); - when(workspaceService.getConfiguration(anything(), anything())).thenCall(this.getWorkspaceConfig.bind(this)); - const testWorkspaceFolder = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience'); - - when(workspaceService.createFileSystemWatcher(anything(), anything(), anything(), anything())).thenReturn( - new MockFileSystemWatcher() - ); - when(workspaceService.createFileSystemWatcher(anything())).thenReturn(new MockFileSystemWatcher()); - when(workspaceService.hasWorkspaceFolders).thenReturn(true); - when(workspaceService.workspaceFolders).thenReturn(this.workspaceFolders); - when(workspaceService.rootPath).thenReturn(testWorkspaceFolder); - when(workspaceService.getWorkspaceFolder(anything())).thenCall(this.getWorkspaceFolder.bind(this)); - this.addWorkspaceFolder(testWorkspaceFolder); - return workspaceService; - } - - private getWorkspaceFolder(uri: Resource): WorkspaceFolder | undefined { - if (uri) { - return this.workspaceFolders.find(w => w.ownedResources.has(uri.toString())); - } - return undefined; - } - - private getResourceKey(resource: Resource): string { - const workspace = this.serviceManager.get(IWorkspaceService); - const workspaceFolderUri = PythonSettings.getSettingsUriAndTarget(resource, workspace).uri; - return workspaceFolderUri ? workspaceFolderUri.fsPath : ''; - } - - private async hasJupyter(interpreter: PythonInterpreter): Promise { - try { - const dependencyChecker = this.serviceManager.get( - JupyterInterpreterDependencyService - ); - return dependencyChecker.areDependenciesInstalled(interpreter); - } catch (ex) { - return false; - } - } - - private findPythonPath(): string { - try { - // Give preference to the CI test python (could also be set in launch.json for debugging). - const output = child_process.execFileSync( - process.env.CI_PYTHON_PATH || 'python', - ['-c', 'import sys;print(sys.executable)'], - { encoding: 'utf8' } - ); - return output.replace(/\r?\n/g, ''); - } catch (ex) { - return 'python'; - } - } - - private mountReactControl(mount: () => ReactWrapper, React.Component>) { - // This is a remount (or first time). Clear out messages that were sent - // by the last mount - this.missedMessages = []; - this.webPanelListener = undefined; - this.extraListeners = []; - this.wrapperCreatedPromise = undefined; - - // Setup the acquireVsCodeApi. The react control will cache this value when it's mounted. - const globalAcquireVsCodeApi = (): IVsCodeApi => { - return { - // tslint:disable-next-line:no-any - postMessage: (msg: any) => { - this.postMessageToWebPanel(msg); - }, - // tslint:disable-next-line:no-any no-empty - setState: (_msg: any) => {}, - // tslint:disable-next-line:no-any no-empty - getState: () => { - return {}; - } - }; - }; - // tslint:disable-next-line:no-string-literal - (global as any)['acquireVsCodeApi'] = globalAcquireVsCodeApi; - - // Remap event handlers to point to the container. - const oldListener = window.addEventListener; - window.addEventListener = (event: string, cb: any) => { - if (event === 'message') { - this.postMessage = cb; - } - }; - - // Mount our main panel. This will make the global api be cached and have the event handler registered - this.wrapper = mount(); - - // We can remove the global api and event listener now. - delete (global as any).acquireVsCodeApi; - window.addEventListener = oldListener; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +//tslint:disable:trailing-comma no-any +import * as child_process from 'child_process'; +import { ReactWrapper } from 'enzyme'; +import { interfaces } from 'inversify'; +import * as os from 'os'; +import * as path from 'path'; +import { SemVer } from 'semver'; +import { anything, instance, mock, reset, when } from 'ts-mockito'; +import * as TypeMoq from 'typemoq'; +import { + CancellationTokenSource, + ConfigurationChangeEvent, + Disposable, + Event, + EventEmitter, + FileSystemWatcher, + Memento, + Uri, + WorkspaceFolder, + WorkspaceFoldersChangeEvent +} from 'vscode'; +import * as vsls from 'vsls/vscode'; + +import { LanguageServerExtensionActivationService } from '../../client/activation/activationService'; +import { LanguageServerDownloader } from '../../client/activation/common/downloader'; +import { JediExtensionActivator } from '../../client/activation/jedi'; +import { DotNetLanguageServerActivator } from '../../client/activation/languageServer/activator'; +import { LanguageServerCompatibilityService } from '../../client/activation/languageServer/languageServerCompatibilityService'; +import { LanguageServerExtension } from '../../client/activation/languageServer/languageServerExtension'; +import { DotNetLanguageServerFolderService } from '../../client/activation/languageServer/languageServerFolderService'; +import { DotNetLanguageServerPackageService } from '../../client/activation/languageServer/languageServerPackageService'; +import { DotNetLanguageServerManager } from '../../client/activation/languageServer/manager'; +import { NodeLanguageServerActivator } from '../../client/activation/node/activator'; +import { NodeLanguageServerManager } from '../../client/activation/node/manager'; +import { + IExtensionSingleActivationService, + ILanguageServerActivator, + ILanguageServerAnalysisOptions, + ILanguageServerCache, + ILanguageServerCompatibilityService, + ILanguageServerDownloader, + ILanguageServerExtension, + ILanguageServerFolderService, + ILanguageServerManager, + ILanguageServerPackageService, + ILanguageServerProxy, + LanguageServerType +} from '../../client/activation/types'; +import { + LSNotSupportedDiagnosticService, + LSNotSupportedDiagnosticServiceId +} from '../../client/application/diagnostics/checks/lsNotSupported'; +import { DiagnosticFilterService } from '../../client/application/diagnostics/filter'; +import { + DiagnosticCommandPromptHandlerService, + DiagnosticCommandPromptHandlerServiceId, + MessageCommandPrompt +} from '../../client/application/diagnostics/promptHandler'; +import { + IDiagnosticFilterService, + IDiagnosticHandlerService, + IDiagnosticsService +} from '../../client/application/diagnostics/types'; +import { ClipboardService } from '../../client/common/application/clipboard'; +import { TerminalManager } from '../../client/common/application/terminalManager'; +import { + IApplicationShell, + IClipboard, + ICommandManager, + ICustomEditorService, + IDebugService, + IDocumentManager, + ILiveShareApi, + ILiveShareTestingApi, + ITerminalManager, + IWebPanel, + IWebPanelMessageListener, + IWebPanelOptions, + IWebPanelProvider, + IWorkspaceService +} from '../../client/common/application/types'; +import { WebPanel } from '../../client/common/application/webPanels/webPanel'; +import { WebPanelProvider } from '../../client/common/application/webPanels/webPanelProvider'; +import { WorkspaceService } from '../../client/common/application/workspace'; +import { AsyncDisposableRegistry } from '../../client/common/asyncDisposableRegistry'; +import { PythonSettings } from '../../client/common/configSettings'; +import { EXTENSION_ROOT_DIR, UseCustomEditorApi } from '../../client/common/constants'; +import { CryptoUtils } from '../../client/common/crypto'; +import { DotNetCompatibilityService } from '../../client/common/dotnet/compatibilityService'; +import { IDotNetCompatibilityService } from '../../client/common/dotnet/types'; +import { ExperimentsManager } from '../../client/common/experiments'; +import { InstallationChannelManager } from '../../client/common/installer/channelManager'; +import { ProductInstaller } from '../../client/common/installer/productInstaller'; +import { + CTagsProductPathService, + DataScienceProductPathService, + FormatterProductPathService, + LinterProductPathService, + RefactoringLibraryProductPathService, + TestFrameworkProductPathService +} from '../../client/common/installer/productPath'; +import { ProductService } from '../../client/common/installer/productService'; +import { IInstallationChannelManager, IProductPathService, IProductService } from '../../client/common/installer/types'; +import { IS_WINDOWS } from '../../client/common/platform/constants'; +import { PathUtils } from '../../client/common/platform/pathUtils'; +import { RegistryImplementation } from '../../client/common/platform/registry'; +import { IFileSystem, IRegistry } from '../../client/common/platform/types'; +import { CurrentProcess } from '../../client/common/process/currentProcess'; +import { BufferDecoder } from '../../client/common/process/decoder'; +import { ProcessLogger } from '../../client/common/process/logger'; +import { ProcessServiceFactory } from '../../client/common/process/processFactory'; +import { PythonExecutionFactory } from '../../client/common/process/pythonExecutionFactory'; +import { + IBufferDecoder, + IProcessLogger, + IProcessServiceFactory, + IPythonExecutionFactory +} from '../../client/common/process/types'; +import { Bash } from '../../client/common/terminal/environmentActivationProviders/bash'; +import { CommandPromptAndPowerShell } from '../../client/common/terminal/environmentActivationProviders/commandPrompt'; +import { CondaActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/condaActivationProvider'; +import { PipEnvActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/pipEnvActivationProvider'; +import { PyEnvActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/pyenvActivationProvider'; +import { TerminalHelper } from '../../client/common/terminal/helper'; +import { TerminalNameShellDetector } from '../../client/common/terminal/shellDetectors/terminalNameShellDetector'; +import { + IShellDetector, + ITerminalActivationCommandProvider, + ITerminalHelper, + TerminalActivationProviders +} from '../../client/common/terminal/types'; +import { + BANNER_NAME_LS_SURVEY, + GLOBAL_MEMENTO, + IAsyncDisposableRegistry, + IConfigurationService, + ICryptoUtils, + ICurrentProcess, + IDataScienceSettings, + IExperimentsManager, + IExtensionContext, + IExtensions, + IInstaller, + IMemento, + IOutputChannel, + IPathUtils, + IPersistentStateFactory, + IPythonExtensionBanner, + IsWindows, + ProductType, + Resource, + WORKSPACE_MEMENTO +} from '../../client/common/types'; +import { Deferred, sleep } from '../../client/common/utils/async'; +import { noop } from '../../client/common/utils/misc'; +import { IMultiStepInputFactory, MultiStepInputFactory } from '../../client/common/utils/multiStepInput'; +import { Architecture } from '../../client/common/utils/platform'; +import { EnvironmentVariablesService } from '../../client/common/variables/environment'; +import { EnvironmentVariablesProvider } from '../../client/common/variables/environmentVariablesProvider'; +import { IEnvironmentVariablesProvider, IEnvironmentVariablesService } from '../../client/common/variables/types'; +import { CodeCssGenerator } from '../../client/datascience/codeCssGenerator'; +import { JUPYTER_OUTPUT_CHANNEL } from '../../client/datascience/constants'; +import { ActiveEditorContextService } from '../../client/datascience/context/activeEditorContext'; +import { DataViewer } from '../../client/datascience/data-viewing/dataViewer'; +import { DataViewerDependencyService } from '../../client/datascience/data-viewing/dataViewerDependencyService'; +import { DataViewerProvider } from '../../client/datascience/data-viewing/dataViewerProvider'; +import { DebugLocationTrackerFactory } from '../../client/datascience/debugLocationTrackerFactory'; +import { CellHashLogger } from '../../client/datascience/editor-integration/cellhashLogger'; +import { CellHashProvider } from '../../client/datascience/editor-integration/cellhashprovider'; +import { CodeLensFactory } from '../../client/datascience/editor-integration/codeLensFactory'; +import { DataScienceCodeLensProvider } from '../../client/datascience/editor-integration/codelensprovider'; +import { CodeWatcher } from '../../client/datascience/editor-integration/codewatcher'; +import { DataScienceErrorHandler } from '../../client/datascience/errorHandler/errorHandler'; +import { GatherProvider } from '../../client/datascience/gather/gather'; +import { GatherListener } from '../../client/datascience/gather/gatherListener'; +import { GatherLogger } from '../../client/datascience/gather/gatherLogger'; +import { IntellisenseProvider } from '../../client/datascience/interactive-common/intellisense/intellisenseProvider'; +import { NotebookProvider } from '../../client/datascience/interactive-common/notebookProvider'; +import { AutoSaveService } from '../../client/datascience/interactive-ipynb/autoSaveService'; +import { NativeEditor } from '../../client/datascience/interactive-ipynb/nativeEditor'; +import { NativeEditorCommandListener } from '../../client/datascience/interactive-ipynb/nativeEditorCommandListener'; +import { NativeEditorOldWebView } from '../../client/datascience/interactive-ipynb/nativeEditorOldWebView'; +import { NativeEditorStorage } from '../../client/datascience/interactive-ipynb/nativeEditorStorage'; +import { NativeEditorSynchronizer } from '../../client/datascience/interactive-ipynb/nativeEditorSynchronizer'; +import { InteractiveWindow } from '../../client/datascience/interactive-window/interactiveWindow'; +import { InteractiveWindowCommandListener } from '../../client/datascience/interactive-window/interactiveWindowCommandListener'; +import { IPyWidgetHandler } from '../../client/datascience/ipywidgets/ipywidgetHandler'; +import { IPyWidgetMessageDispatcherFactory } from '../../client/datascience/ipywidgets/ipyWidgetMessageDispatcherFactory'; +import { JupyterCommandFactory } from '../../client/datascience/jupyter/interpreter/jupyterCommand'; +import { JupyterCommandFinder } from '../../client/datascience/jupyter/interpreter/jupyterCommandFinder'; +import { JupyterCommandInterpreterDependencyService } from '../../client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService'; +import { JupyterCommandFinderInterpreterExecutionService } from '../../client/datascience/jupyter/interpreter/jupyterCommandInterpreterExecutionService'; +import { JupyterInterpreterDependencyService } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService'; +import { JupyterInterpreterOldCacheStateStore } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterOldCacheStateStore'; +import { JupyterInterpreterSelectionCommand } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterSelectionCommand'; +import { JupyterInterpreterSelector } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterSelector'; +import { JupyterInterpreterService } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterService'; +import { JupyterInterpreterStateStore } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterStateStore'; +import { JupyterInterpreterSubCommandExecutionService } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterSubCommandExecutionService'; +import { JupyterDebugger } from '../../client/datascience/jupyter/jupyterDebugger'; +import { JupyterExecutionFactory } from '../../client/datascience/jupyter/jupyterExecutionFactory'; +import { JupyterExporter } from '../../client/datascience/jupyter/jupyterExporter'; +import { JupyterImporter } from '../../client/datascience/jupyter/jupyterImporter'; +import { JupyterPasswordConnect } from '../../client/datascience/jupyter/jupyterPasswordConnect'; +import { JupyterServerWrapper } from '../../client/datascience/jupyter/jupyterServerWrapper'; +import { JupyterSessionManagerFactory } from '../../client/datascience/jupyter/jupyterSessionManagerFactory'; +import { JupyterVariables } from '../../client/datascience/jupyter/jupyterVariables'; +import { KernelSelectionProvider } from '../../client/datascience/jupyter/kernels/kernelSelections'; +import { KernelSelector } from '../../client/datascience/jupyter/kernels/kernelSelector'; +import { KernelService } from '../../client/datascience/jupyter/kernels/kernelService'; +import { KernelSwitcher } from '../../client/datascience/jupyter/kernels/kernelSwitcher'; +import { NotebookStarter } from '../../client/datascience/jupyter/notebookStarter'; +import { ServerPreload } from '../../client/datascience/jupyter/serverPreload'; +import { JupyterServerSelector } from '../../client/datascience/jupyter/serverSelector'; +import { PlotViewer } from '../../client/datascience/plotting/plotViewer'; +import { PlotViewerProvider } from '../../client/datascience/plotting/plotViewerProvider'; +import { ProgressReporter } from '../../client/datascience/progress/progressReporter'; +import { StatusProvider } from '../../client/datascience/statusProvider'; +import { ThemeFinder } from '../../client/datascience/themeFinder'; +import { + ICellHashListener, + ICellHashLogger, + ICellHashProvider, + ICodeCssGenerator, + ICodeLensFactory, + ICodeWatcher, + IDataScience, + IDataScienceCodeLensProvider, + IDataScienceCommandListener, + IDataScienceErrorHandler, + IDataViewer, + IDataViewerProvider, + IDebugLocationTracker, + IGatherLogger, + IGatherProvider, + IInteractiveWindow, + IInteractiveWindowListener, + IInteractiveWindowProvider, + IJupyterCommandFactory, + IJupyterDebugger, + IJupyterExecution, + IJupyterInterpreterDependencyManager, + IJupyterPasswordConnect, + IJupyterSessionManagerFactory, + IJupyterSubCommandExecutionService, + IJupyterVariables, + INotebookEditor, + INotebookEditorProvider, + INotebookExecutionLogger, + INotebookExporter, + INotebookImporter, + INotebookProvider, + INotebookServer, + INotebookStorage, + IPlotViewer, + IPlotViewerProvider, + IStatusProvider, + IThemeFinder +} from '../../client/datascience/types'; +import { ProtocolParser } from '../../client/debugger/debugAdapter/Common/protocolParser'; +import { IProtocolParser } from '../../client/debugger/debugAdapter/types'; +import { + EnvironmentActivationService, + EnvironmentActivationServiceCache +} from '../../client/interpreter/activation/service'; +import { IEnvironmentActivationService } from '../../client/interpreter/activation/types'; +import { InterpreterComparer } from '../../client/interpreter/configuration/interpreterComparer'; +import { InterpreterSelector } from '../../client/interpreter/configuration/interpreterSelector'; +import { PythonPathUpdaterService } from '../../client/interpreter/configuration/pythonPathUpdaterService'; +import { PythonPathUpdaterServiceFactory } from '../../client/interpreter/configuration/pythonPathUpdaterServiceFactory'; +import { + IInterpreterComparer, + IInterpreterSelector, + IPythonPathUpdaterServiceFactory, + IPythonPathUpdaterServiceManager +} from '../../client/interpreter/configuration/types'; +import { + CONDA_ENV_FILE_SERVICE, + CONDA_ENV_SERVICE, + CURRENT_PATH_SERVICE, + GLOBAL_VIRTUAL_ENV_SERVICE, + ICondaService, + IInterpreterDisplay, + IInterpreterHelper, + IInterpreterLocatorHelper, + IInterpreterLocatorService, + IInterpreterService, + IInterpreterVersionService, + IInterpreterWatcher, + IInterpreterWatcherBuilder, + IKnownSearchPathsForInterpreters, + INTERPRETER_LOCATOR_SERVICE, + InterpreterType, + IPipEnvService, + IShebangCodeLensProvider, + IVirtualEnvironmentsSearchPathProvider, + KNOWN_PATH_SERVICE, + PIPENV_SERVICE, + PythonInterpreter, + WINDOWS_REGISTRY_SERVICE, + WORKSPACE_VIRTUAL_ENV_SERVICE +} from '../../client/interpreter/contracts'; +import { ShebangCodeLensProvider } from '../../client/interpreter/display/shebangCodeLensProvider'; +import { InterpreterHelper } from '../../client/interpreter/helpers'; +import { InterpreterVersionService } from '../../client/interpreter/interpreterVersion'; +import { PythonInterpreterLocatorService } from '../../client/interpreter/locators'; +import { InterpreterLocatorHelper } from '../../client/interpreter/locators/helpers'; +import { CacheableLocatorPromiseCache } from '../../client/interpreter/locators/services/cacheableLocatorService'; +import { CondaEnvFileService } from '../../client/interpreter/locators/services/condaEnvFileService'; +import { CondaEnvService } from '../../client/interpreter/locators/services/condaEnvService'; +import { + CurrentPathService, + PythonInPathCommandProvider +} from '../../client/interpreter/locators/services/currentPathService'; +import { + GlobalVirtualEnvironmentsSearchPathProvider, + GlobalVirtualEnvService +} from '../../client/interpreter/locators/services/globalVirtualEnvService'; +import { InterpreterHashProvider } from '../../client/interpreter/locators/services/hashProvider'; +import { InterpeterHashProviderFactory } from '../../client/interpreter/locators/services/hashProviderFactory'; +import { InterpreterFilter } from '../../client/interpreter/locators/services/interpreterFilter'; +import { InterpreterWatcherBuilder } from '../../client/interpreter/locators/services/interpreterWatcherBuilder'; +import { + KnownPathsService, + KnownSearchPathsForInterpreters +} from '../../client/interpreter/locators/services/KnownPathsService'; +import { PipEnvService } from '../../client/interpreter/locators/services/pipEnvService'; +import { PipEnvServiceHelper } from '../../client/interpreter/locators/services/pipEnvServiceHelper'; +import { WindowsRegistryService } from '../../client/interpreter/locators/services/windowsRegistryService'; +import { WindowsStoreInterpreter } from '../../client/interpreter/locators/services/windowsStoreInterpreter'; +import { + WorkspaceVirtualEnvironmentsSearchPathProvider, + WorkspaceVirtualEnvService +} from '../../client/interpreter/locators/services/workspaceVirtualEnvService'; +import { WorkspaceVirtualEnvWatcherService } from '../../client/interpreter/locators/services/workspaceVirtualEnvWatcherService'; +import { IPipEnvServiceHelper, IPythonInPathCommandProvider } from '../../client/interpreter/locators/types'; +import { registerInterpreterTypes } from '../../client/interpreter/serviceRegistry'; +import { VirtualEnvironmentManager } from '../../client/interpreter/virtualEnvs'; +import { IVirtualEnvironmentManager } from '../../client/interpreter/virtualEnvs/types'; +import { LanguageServerSurveyBanner } from '../../client/languageServices/languageServerSurveyBanner'; +import { CodeExecutionHelper } from '../../client/terminals/codeExecution/helper'; +import { ICodeExecutionHelper } from '../../client/terminals/types'; +import { IVsCodeApi } from '../../datascience-ui/react-common/postOffice'; +import { MockOutputChannel } from '../mockClasses'; +import { MockAutoSelectionService } from '../mocks/autoSelector'; +import { UnitTestIocContainer } from '../testing/serviceRegistry'; +import { MockCommandManager } from './mockCommandManager'; +import { MockCustomEditorService } from './mockCustomEditorService'; +import { MockDebuggerService } from './mockDebugService'; +import { MockDocumentManager } from './mockDocumentManager'; +import { MockExtensions } from './mockExtensions'; +import { MockFileSystem } from './mockFileSystem'; +import { MockJupyterManager, SupportedCommands } from './mockJupyterManager'; +import { MockJupyterManagerFactory } from './mockJupyterManagerFactory'; +import { MockLanguageServerAnalysisOptions } from './mockLanguageServerAnalysisOptions'; +import { MockLanguageServerProxy } from './mockLanguageServerProxy'; +import { MockLiveShareApi } from './mockLiveShare'; +import { MockWorkspaceConfiguration } from './mockWorkspaceConfig'; +import { MockWorkspaceFolder } from './mockWorkspaceFolder'; +import { TestInteractiveWindowProvider } from './testInteractiveWindowProvider'; +import { TestNativeEditorProvider } from './testNativeEditorProvider'; +import { TestPersistentStateFactory } from './testPersistentStateFactory'; +import { WebBrowserPanelProvider } from './uiTests/webBrowserPanelProvider'; + +export class DataScienceIocContainer extends UnitTestIocContainer { + public get workingInterpreter() { + return this.workingPython; + } + + public get workingInterpreter2() { + return this.workingPython2; + } + + public get onContextSet(): Event<{ name: string; value: boolean }> { + return this.contextSetEvent.event; + } + + public get mockJupyter(): MockJupyterManager | undefined { + return this.jupyterMock ? this.jupyterMock.getManager() : undefined; + } + private static jupyterInterpreters: PythonInterpreter[] = []; + public webPanelListener: IWebPanelMessageListener | undefined; + public readonly useCommandFinderForJupyterServer = false; + public wrapper: ReactWrapper, React.Component> | undefined; + public wrapperCreatedPromise: Deferred | undefined; + public postMessage: ((ev: MessageEvent) => void) | undefined; + public applicationShell!: TypeMoq.IMock; + // tslint:disable-next-line:no-any + public datascience!: TypeMoq.IMock; + private missedMessages: any[] = []; + private commandManager: MockCommandManager = new MockCommandManager(); + private setContexts: Record = {}; + private contextSetEvent: EventEmitter<{ name: string; value: boolean }> = new EventEmitter<{ + name: string; + value: boolean; + }>(); + private jupyterMock: MockJupyterManagerFactory | undefined; + private shouldMockJupyter: boolean; + private asyncRegistry: AsyncDisposableRegistry; + private configChangeEvent = new EventEmitter(); + private worksaceFoldersChangedEvent = new EventEmitter(); + private documentManager = new MockDocumentManager(); + private workingPython: PythonInterpreter = { + path: '/foo/bar/python.exe', + version: new SemVer('3.6.6-final'), + sysVersion: '1.0.0.0', + sysPrefix: 'Python', + displayName: 'Python', + type: InterpreterType.Unknown, + architecture: Architecture.x64 + }; + private workingPython2: PythonInterpreter = { + path: '/foo/baz/python.exe', + version: new SemVer('3.6.7-final'), + sysVersion: '1.0.0.0', + sysPrefix: 'Python', + displayName: 'Python', + type: InterpreterType.Unknown, + architecture: Architecture.x64 + }; + private extraListeners: ((m: string, p: any) => void)[] = []; + + private webPanelProvider = mock(WebPanelProvider); + private settingsMap = new Map(); + private configMap = new Map(); + private emptyConfig = new MockWorkspaceConfiguration(); + private workspaceFolders: MockWorkspaceFolder[] = []; + private defaultPythonPath: string | undefined; + private kernelServiceMock = mock(KernelService); + + constructor(private readonly uiTest: boolean = false) { + super(); + this.useVSCodeAPI = false; + const isRollingBuild = process.env ? process.env.VSCODE_PYTHON_ROLLING !== undefined : false; + this.shouldMockJupyter = !isRollingBuild; + this.asyncRegistry = new AsyncDisposableRegistry(); + } + + public async dispose(): Promise { + await this.asyncRegistry.dispose(); + await super.dispose(); + + if (!this.uiTest) { + // Blur window focus so we don't have editors polling + // tslint:disable-next-line: no-require-imports + const reactHelpers = require('./reactHelpers') as typeof import('./reactHelpers'); + reactHelpers.blurWindow(); + } + + if (this.wrapper && this.wrapper.length) { + this.wrapper.unmount(); + this.wrapper = undefined; + } + + // Bounce this so that our editor has time to shutdown + await sleep(150); + + if (!this.uiTest) { + // Clear out the monaco global services. Some of these services are preventing shutdown. + // tslint:disable: no-require-imports + const services = require('monaco-editor/esm/vs/editor/standalone/browser/standaloneServices') as any; + if (services.StaticServices) { + const keys = Object.keys(services.StaticServices); + keys.forEach((k) => { + const service = services.StaticServices[k] as any; + if (service && service._value && service._value.dispose) { + if (typeof service._value.dispose === 'function') { + service._value.dispose(); + } + } + }); + } + // This file doesn't have an export so we can't force a dispose. Instead it has a 5 second timeout + const config = require('monaco-editor/esm/vs/editor/browser/config/configuration') as any; + if (config.getCSSBasedConfiguration) { + config.getCSSBasedConfiguration().dispose(); + } + } + + // Because there are outstanding promises holding onto this object, clear out everything we can + this.workspaceFolders = []; + this.settingsMap.clear(); + this.configMap.clear(); + this.setContexts = {}; + this.extraListeners = []; + this.webPanelListener = undefined; + reset(this.webPanelProvider); + + // Turn off the static maps for the environment and conda services. Otherwise this + // can mess up tests that don't depend upon them + CacheableLocatorPromiseCache.forceUseNormal(); + EnvironmentActivationServiceCache.forceUseNormal(); + } + + //tslint:disable:max-func-body-length + public registerDataScienceTypes(useCustomEditor: boolean = false) { + // Inform the cacheable locator service to use a static map so that it stays in memory in between tests + CacheableLocatorPromiseCache.forceUseStatic(); + + // Do the same thing for the environment variable activation service. + EnvironmentActivationServiceCache.forceUseStatic(); + + // Make sure the default python path is set. + this.defaultPythonPath = this.findPythonPath(); + + // Create the workspace service first as it's used to set config values. + this.createWorkspaceService(); + + // Setup our webpanel provider to create our dummy web panel + when(this.webPanelProvider.create(anything())).thenCall(this.onCreateWebPanel.bind(this)); + if (this.uiTest) { + this.serviceManager.addSingleton(IWebPanelProvider, WebBrowserPanelProvider); + } else { + this.serviceManager.addSingletonInstance( + IWebPanelProvider, + instance(this.webPanelProvider) + ); + } + + this.registerFileSystemTypes(); + this.serviceManager.rebindInstance(IFileSystem, new MockFileSystem()); + this.serviceManager.addSingleton(IJupyterExecution, JupyterExecutionFactory); + this.serviceManager.addSingleton( + IInteractiveWindowProvider, + TestInteractiveWindowProvider + ); + this.serviceManager.addSingletonInstance(UseCustomEditorApi, useCustomEditor); + this.serviceManager.addSingleton(IDataViewerProvider, DataViewerProvider); + this.serviceManager.addSingleton(IPlotViewerProvider, PlotViewerProvider); + this.serviceManager.add(IInteractiveWindow, InteractiveWindow); + this.serviceManager.add(IDataViewer, DataViewer); + this.serviceManager.add(IPlotViewer, PlotViewer); + this.serviceManager.add(INotebookImporter, JupyterImporter); + this.serviceManager.add(INotebookExporter, JupyterExporter); + this.serviceManager.addSingleton(ILiveShareApi, MockLiveShareApi); + this.serviceManager.addSingleton(IExtensions, MockExtensions); + this.serviceManager.add(INotebookServer, JupyterServerWrapper); + this.serviceManager.add(IJupyterCommandFactory, JupyterCommandFactory); + this.serviceManager.addSingleton(IThemeFinder, ThemeFinder); + this.serviceManager.addSingleton(ICodeCssGenerator, CodeCssGenerator); + this.serviceManager.addSingleton(IStatusProvider, StatusProvider); + this.serviceManager.addSingletonInstance( + IAsyncDisposableRegistry, + this.asyncRegistry + ); + this.serviceManager.addSingleton( + IEnvironmentActivationService, + EnvironmentActivationService + ); + this.serviceManager.add(ICodeWatcher, CodeWatcher); + this.serviceManager.add( + IDataScienceCodeLensProvider, + DataScienceCodeLensProvider + ); + this.serviceManager.add(ICodeExecutionHelper, CodeExecutionHelper); + this.serviceManager.add( + IDataScienceCommandListener, + InteractiveWindowCommandListener + ); + this.serviceManager.addSingleton(IDataScienceErrorHandler, DataScienceErrorHandler); + this.serviceManager.add(IInstallationChannelManager, InstallationChannelManager); + this.serviceManager.addSingleton(IJupyterVariables, JupyterVariables); + this.serviceManager.addSingleton(IJupyterDebugger, JupyterDebugger, undefined, [ + ICellHashListener + ]); + this.serviceManager.addSingleton(IDebugLocationTracker, DebugLocationTrackerFactory); + this.serviceManager.addSingleton(INotebookEditorProvider, TestNativeEditorProvider); + this.serviceManager.addSingleton( + DataViewerDependencyService, + DataViewerDependencyService + ); + this.serviceManager.add( + INotebookEditor, + useCustomEditor ? NativeEditor : NativeEditorOldWebView + ); + + this.serviceManager.add(INotebookStorage, NativeEditorStorage); + this.serviceManager.addSingletonInstance( + ICustomEditorService, + new MockCustomEditorService(this.asyncRegistry, this.commandManager) + ); + this.serviceManager.addSingleton( + IDataScienceCommandListener, + NativeEditorCommandListener + ); + this.serviceManager.addSingletonInstance( + IOutputChannel, + mock(MockOutputChannel), + JUPYTER_OUTPUT_CHANNEL + ); + this.serviceManager.addSingleton(ICryptoUtils, CryptoUtils); + this.serviceManager.addSingleton( + IExtensionSingleActivationService, + ServerPreload + ); + const mockExtensionContext = TypeMoq.Mock.ofType(); + mockExtensionContext.setup((m) => m.globalStoragePath).returns(() => os.tmpdir()); + this.serviceManager.addSingletonInstance(IExtensionContext, mockExtensionContext.object); + + const mockServerSelector = mock(JupyterServerSelector); + this.serviceManager.addSingletonInstance( + JupyterServerSelector, + instance(mockServerSelector) + ); + + this.serviceManager.addSingleton(ITerminalHelper, TerminalHelper); + this.serviceManager.addSingleton( + ITerminalActivationCommandProvider, + Bash, + TerminalActivationProviders.bashCShellFish + ); + this.serviceManager.addSingleton( + ITerminalActivationCommandProvider, + CommandPromptAndPowerShell, + TerminalActivationProviders.commandPromptAndPowerShell + ); + this.serviceManager.addSingleton( + ITerminalActivationCommandProvider, + PyEnvActivationCommandProvider, + TerminalActivationProviders.pyenv + ); + this.serviceManager.addSingleton( + ITerminalActivationCommandProvider, + CondaActivationCommandProvider, + TerminalActivationProviders.conda + ); + this.serviceManager.addSingleton( + ITerminalActivationCommandProvider, + PipEnvActivationCommandProvider, + TerminalActivationProviders.pipenv + ); + this.serviceManager.addSingleton(ITerminalManager, TerminalManager); + + //const configuration = this.serviceManager.get(IConfigurationService); + //const pythonSettings = configuration.getSettings(); + const languageServerType = LanguageServerType.Microsoft; // pythonSettings.languageServer; + + this.serviceManager.addSingleton(ILanguageServerProxy, MockLanguageServerProxy); + this.serviceManager.addSingleton( + ILanguageServerCache, + LanguageServerExtensionActivationService + ); + this.serviceManager.addSingleton(ILanguageServerExtension, LanguageServerExtension); + + this.serviceManager.add( + ILanguageServerActivator, + JediExtensionActivator, + LanguageServerType.Jedi + ); + if (languageServerType === LanguageServerType.Microsoft) { + this.serviceManager.add( + ILanguageServerActivator, + DotNetLanguageServerActivator, + LanguageServerType.Microsoft + ); + this.serviceManager.add(ILanguageServerManager, DotNetLanguageServerManager); + this.serviceManager.addSingleton( + ILanguageServerAnalysisOptions, + MockLanguageServerAnalysisOptions + ); + } else if (languageServerType === LanguageServerType.Node) { + this.serviceManager.add( + ILanguageServerActivator, + NodeLanguageServerActivator, + LanguageServerType.Node + ); + this.serviceManager.add(ILanguageServerManager, NodeLanguageServerManager); + } + + this.serviceManager.addSingleton(INotebookProvider, NotebookProvider); + + this.serviceManager.add(IInteractiveWindowListener, IntellisenseProvider); + this.serviceManager.add(IInteractiveWindowListener, AutoSaveService); + this.serviceManager.add(IInteractiveWindowListener, GatherListener); + this.serviceManager.addSingleton( + IPyWidgetMessageDispatcherFactory, + IPyWidgetMessageDispatcherFactory + ); + if (this.uiTest) { + this.serviceManager.add(IInteractiveWindowListener, IPyWidgetHandler); + } + this.serviceManager.add(IProtocolParser, ProtocolParser); + this.serviceManager.addSingleton(IDebugService, MockDebuggerService); + this.serviceManager.add(ICellHashProvider, CellHashProvider); + this.serviceManager.add(ICellHashLogger, CellHashLogger, undefined, [ + INotebookExecutionLogger + ]); + this.serviceManager.add(IGatherProvider, GatherProvider); + this.serviceManager.add(IGatherLogger, GatherLogger, undefined, [INotebookExecutionLogger]); + this.serviceManager.addSingleton(ICodeLensFactory, CodeLensFactory, undefined, [ + IInteractiveWindowListener + ]); + this.serviceManager.addSingleton(IShellDetector, TerminalNameShellDetector); + this.serviceManager.addSingleton(JupyterCommandFinder, JupyterCommandFinder); + this.serviceManager.addSingleton( + IDiagnosticsService, + LSNotSupportedDiagnosticService, + LSNotSupportedDiagnosticServiceId + ); + this.serviceManager.addSingleton( + ILanguageServerCompatibilityService, + LanguageServerCompatibilityService + ); + this.serviceManager.addSingleton>( + IDiagnosticHandlerService, + DiagnosticCommandPromptHandlerService, + DiagnosticCommandPromptHandlerServiceId + ); + this.serviceManager.addSingleton(IDiagnosticFilterService, DiagnosticFilterService); + this.serviceManager.addSingleton(NotebookStarter, NotebookStarter); + this.serviceManager.addSingleton(KernelSelector, KernelSelector); + this.serviceManager.addSingleton(KernelSelectionProvider, KernelSelectionProvider); + this.serviceManager.addSingleton(KernelSwitcher, KernelSwitcher); + this.serviceManager.addSingleton(IProductService, ProductService); + this.serviceManager.addSingleton( + IProductPathService, + CTagsProductPathService, + ProductType.WorkspaceSymbols + ); + this.serviceManager.addSingleton( + IProductPathService, + FormatterProductPathService, + ProductType.Formatter + ); + this.serviceManager.addSingleton( + IProductPathService, + LinterProductPathService, + ProductType.Linter + ); + this.serviceManager.addSingleton( + IProductPathService, + TestFrameworkProductPathService, + ProductType.TestFramework + ); + this.serviceManager.addSingleton( + IProductPathService, + RefactoringLibraryProductPathService, + ProductType.RefactoringLibrary + ); + this.serviceManager.addSingleton( + IProductPathService, + DataScienceProductPathService, + ProductType.DataScience + ); + this.serviceManager.addSingleton(IMultiStepInputFactory, MultiStepInputFactory); + + // No need of reporting progress. + const progressReporter = mock(ProgressReporter); + when(progressReporter.createProgressIndicator(anything())).thenReturn({ + dispose: noop, + token: new CancellationTokenSource().token + }); + this.serviceManager.addSingletonInstance(ProgressReporter, instance(progressReporter)); + + // Don't check for dot net compatibility + const dotNetCompability = mock(DotNetCompatibilityService); + when(dotNetCompability.isSupported()).thenResolve(true); + this.serviceManager.addSingletonInstance( + IDotNetCompatibilityService, + instance(dotNetCompability) + ); + + // Don't allow a banner to show up + const extensionBanner = mock(LanguageServerSurveyBanner); + this.serviceManager.addSingletonInstance( + IPythonExtensionBanner, + instance(extensionBanner), + BANNER_NAME_LS_SURVEY + ); + + // Don't allow the download to happen + const downloader = mock(LanguageServerDownloader); + this.serviceManager.addSingletonInstance( + ILanguageServerDownloader, + instance(downloader) + ); + + const folderService = mock(DotNetLanguageServerFolderService); + const packageService = mock(DotNetLanguageServerPackageService); + this.serviceManager.addSingletonInstance( + ILanguageServerFolderService, + instance(folderService) + ); + this.serviceManager.addSingletonInstance( + ILanguageServerPackageService, + instance(packageService) + ); + + // Disable experiments. + const experimentManager = mock(ExperimentsManager); + when(experimentManager.inExperiment(anything())).thenReturn(false); + when(experimentManager.activate()).thenResolve(); + this.serviceManager.addSingletonInstance(IExperimentsManager, instance(experimentManager)); + + // Setup our command list + this.commandManager.registerCommand('setContext', (name: string, value: boolean) => { + this.setContexts[name] = value; + this.contextSetEvent.fire({ name: name, value: value }); + }); + this.serviceManager.addSingletonInstance(ICommandManager, this.commandManager); + + // Mock the app shell + const appShell = (this.applicationShell = TypeMoq.Mock.ofType()); + const configurationService = TypeMoq.Mock.ofType(); + this.datascience = TypeMoq.Mock.ofType(); + + configurationService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(this.getSettings.bind(this)); + + const startTime = Date.now(); + this.datascience.setup((d) => d.activationStartTime).returns(() => startTime); + + this.serviceManager.addSingleton( + IEnvironmentVariablesProvider, + EnvironmentVariablesProvider + ); + + this.serviceManager.addSingletonInstance(IApplicationShell, appShell.object); + this.serviceManager.addSingleton(IClipboard, ClipboardService); + this.serviceManager.addSingletonInstance(IDocumentManager, this.documentManager); + this.serviceManager.addSingletonInstance( + IConfigurationService, + configurationService.object + ); + this.serviceManager.addSingletonInstance(IDataScience, this.datascience.object); + this.serviceManager.addSingleton(IBufferDecoder, BufferDecoder); + this.serviceManager.addSingleton( + IEnvironmentVariablesService, + EnvironmentVariablesService + ); + this.serviceManager.addSingleton(IPathUtils, PathUtils); + this.serviceManager.addSingletonInstance(IsWindows, IS_WINDOWS); + + const globalStorage = this.serviceManager.get(IMemento, GLOBAL_MEMENTO); + const localStorage = this.serviceManager.get(IMemento, WORKSPACE_MEMENTO); + + // Create a custom persistent state factory that remembers specific things between tests + this.serviceManager.addSingletonInstance( + IPersistentStateFactory, + new TestPersistentStateFactory(globalStorage, localStorage) + ); + + const currentProcess = new CurrentProcess(); + this.serviceManager.addSingletonInstance(ICurrentProcess, currentProcess); + this.serviceManager.addSingleton(IRegistry, RegistryImplementation); + + this.serviceManager.addSingleton( + JupyterInterpreterStateStore, + JupyterInterpreterStateStore + ); + this.serviceManager.addSingleton( + IExtensionSingleActivationService, + JupyterInterpreterSelectionCommand + ); + this.serviceManager.addSingleton( + JupyterInterpreterSelector, + JupyterInterpreterSelector + ); + this.serviceManager.addSingleton( + JupyterInterpreterDependencyService, + JupyterInterpreterDependencyService + ); + this.serviceManager.addSingleton( + JupyterInterpreterService, + JupyterInterpreterService + ); + this.serviceManager.addSingleton( + JupyterInterpreterOldCacheStateStore, + JupyterInterpreterOldCacheStateStore + ); + this.serviceManager.addSingleton( + ActiveEditorContextService, + ActiveEditorContextService + ); + + if (this.useCommandFinderForJupyterServer) { + this.serviceManager.addSingleton( + IJupyterSubCommandExecutionService, + JupyterCommandFinderInterpreterExecutionService + ); + this.serviceManager.addSingleton( + IJupyterInterpreterDependencyManager, + JupyterCommandInterpreterDependencyService + ); + } else { + this.serviceManager.addSingleton( + IJupyterSubCommandExecutionService, + JupyterInterpreterSubCommandExecutionService + ); + this.serviceManager.addSingleton( + IJupyterInterpreterDependencyManager, + JupyterInterpreterSubCommandExecutionService + ); + } + + const interpreterDisplay = TypeMoq.Mock.ofType(); + interpreterDisplay.setup((i) => i.refresh(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + + // Create our jupyter mock if necessary + if (this.shouldMockJupyter) { + this.jupyterMock = new MockJupyterManagerFactory(this.serviceManager); + // When using mocked Jupyter, default to using default kernel. + when(this.kernelServiceMock.searchAndRegisterKernel(anything(), anything())).thenResolve(undefined); + this.serviceManager.addSingletonInstance(KernelService, instance(this.kernelServiceMock)); + + this.serviceManager.addSingleton( + InterpeterHashProviderFactory, + InterpeterHashProviderFactory + ); + this.serviceManager.addSingleton(WindowsStoreInterpreter, WindowsStoreInterpreter); + this.serviceManager.addSingleton(InterpreterHashProvider, InterpreterHashProvider); + this.serviceManager.addSingleton(InterpreterFilter, InterpreterFilter); + this.serviceManager.add( + IInterpreterWatcher, + WorkspaceVirtualEnvWatcherService, + WORKSPACE_VIRTUAL_ENV_SERVICE + ); + this.serviceManager.addSingleton( + IInterpreterWatcherBuilder, + InterpreterWatcherBuilder + ); + this.serviceManager.add( + IInterpreterWatcher, + WorkspaceVirtualEnvWatcherService, + WORKSPACE_VIRTUAL_ENV_SERVICE + ); + this.serviceManager.addSingleton( + IInterpreterWatcherBuilder, + InterpreterWatcherBuilder + ); + + this.serviceManager.addSingleton( + IInterpreterLocatorService, + PythonInterpreterLocatorService, + INTERPRETER_LOCATOR_SERVICE + ); + this.serviceManager.addSingleton( + IInterpreterLocatorService, + CondaEnvFileService, + CONDA_ENV_FILE_SERVICE + ); + this.serviceManager.addSingleton( + IInterpreterLocatorService, + CondaEnvService, + CONDA_ENV_SERVICE + ); + this.serviceManager.addSingleton( + IInterpreterLocatorService, + CurrentPathService, + CURRENT_PATH_SERVICE + ); + this.serviceManager.addSingleton( + IInterpreterLocatorService, + GlobalVirtualEnvService, + GLOBAL_VIRTUAL_ENV_SERVICE + ); + this.serviceManager.addSingleton( + IInterpreterLocatorService, + WorkspaceVirtualEnvService, + WORKSPACE_VIRTUAL_ENV_SERVICE + ); + this.serviceManager.addSingleton( + IInterpreterLocatorService, + PipEnvService, + PIPENV_SERVICE + ); + this.serviceManager.addSingleton(IPipEnvService, PipEnvService); + this.serviceManager.addSingleton( + IInterpreterLocatorService, + WindowsRegistryService, + WINDOWS_REGISTRY_SERVICE + ); + + this.serviceManager.addSingleton( + IInterpreterLocatorService, + KnownPathsService, + KNOWN_PATH_SERVICE + ); + + this.serviceManager.addSingleton(IInterpreterHelper, InterpreterHelper); + this.serviceManager.addSingleton( + IInterpreterLocatorHelper, + InterpreterLocatorHelper + ); + this.serviceManager.addSingleton(IInterpreterComparer, InterpreterComparer); + this.serviceManager.addSingleton( + IInterpreterVersionService, + InterpreterVersionService + ); + this.serviceManager.addSingleton( + IPythonInPathCommandProvider, + PythonInPathCommandProvider + ); + + this.serviceManager.addSingleton(IPipEnvServiceHelper, PipEnvServiceHelper); + this.serviceManager.addSingleton(IInterpreterSelector, InterpreterSelector); + this.serviceManager.addSingleton( + IShebangCodeLensProvider, + ShebangCodeLensProvider + ); + this.serviceManager.addSingleton( + IPythonPathUpdaterServiceFactory, + PythonPathUpdaterServiceFactory + ); + this.serviceManager.addSingleton( + IPythonPathUpdaterServiceManager, + PythonPathUpdaterService + ); + + // Don't use conda at all when mocking + const condaService = TypeMoq.Mock.ofType(); + this.serviceManager.addSingletonInstance(ICondaService, condaService.object); + condaService.setup((c) => c.isCondaAvailable()).returns(() => Promise.resolve(false)); + condaService.setup((c) => c.isCondaEnvironment(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); + condaService.setup((c) => c.condaEnvironmentsFile).returns(() => undefined); + + this.serviceManager.addSingleton( + IVirtualEnvironmentsSearchPathProvider, + GlobalVirtualEnvironmentsSearchPathProvider, + 'global' + ); + this.serviceManager.addSingleton( + IVirtualEnvironmentsSearchPathProvider, + WorkspaceVirtualEnvironmentsSearchPathProvider, + 'workspace' + ); + this.serviceManager.addSingleton( + IVirtualEnvironmentManager, + VirtualEnvironmentManager + ); + this.serviceManager.add( + IKnownSearchPathsForInterpreters, + KnownSearchPathsForInterpreters + ); + this.serviceManager.addSingleton( + IPythonInPathCommandProvider, + PythonInPathCommandProvider + ); + this.serviceManager.addSingletonInstance( + IInterpreterDisplay, + interpreterDisplay.object + ); + } else { + this.serviceManager.addSingleton(IInstaller, ProductInstaller); + this.serviceManager.addSingleton(KernelService, KernelService); + this.serviceManager.addSingleton(IProcessServiceFactory, ProcessServiceFactory); + this.serviceManager.addSingleton(IPythonExecutionFactory, PythonExecutionFactory); + + // Make sure full interpreter services are available. + registerInterpreterTypes(this.serviceManager); + + // Rebind the interpreter display as we don't want to use the real one + this.serviceManager.rebindInstance(IInterpreterDisplay, interpreterDisplay.object); + + this.serviceManager.addSingleton( + IJupyterSessionManagerFactory, + JupyterSessionManagerFactory + ); + this.serviceManager.addSingleton(IJupyterPasswordConnect, JupyterPasswordConnect); + this.serviceManager.addSingleton(IProcessLogger, ProcessLogger); + } + this.serviceManager.addSingleton(NativeEditorSynchronizer, NativeEditorSynchronizer); + // Disable syncrhonizing edits + this.serviceContainer.get(NativeEditorSynchronizer).disable(); + const dummyDisposable = { + dispose: () => { + return; + } + }; + + appShell.setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString())).returns(() => Promise.resolve('')); + appShell + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(() => Promise.resolve('')); + appShell + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((_a1: string, a2: string, _a3: string) => Promise.resolve(a2)); + appShell + .setup((a) => + a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) + ) + .returns((_a1: string, a2: string, _a3: string, _a4: string) => Promise.resolve(a2)); + appShell + .setup((a) => a.showSaveDialog(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(Uri.file('test.ipynb'))); + appShell.setup((a) => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); + appShell.setup((a) => a.showInputBox(TypeMoq.It.isAny())).returns(() => Promise.resolve('Python')); + + const interpreterManager = this.serviceContainer.get(IInterpreterService); + interpreterManager.initialize(); + + if (this.mockJupyter) { + this.addInterpreter(this.workingPython2, SupportedCommands.all); + this.addInterpreter(this.workingPython, SupportedCommands.all); + } + } + public setFileContents(uri: Uri, contents: string) { + const fileSystem = this.serviceManager.get(IFileSystem) as MockFileSystem; + fileSystem.addFileContents(uri.fsPath, contents); + } + + public async activate(): Promise { + // Activate all of the extension activation services + const activationServices = this.serviceManager.getAll( + IExtensionSingleActivationService + ); + + await Promise.all(activationServices.map((a) => a.activate())); + + // Then force our interpreter to be one that supports jupyter (unless in a mock state when we don't have to) + if (!this.mockJupyter) { + const interpreterService = this.serviceManager.get(IInterpreterService); + const activeInterpreter = await interpreterService.getActiveInterpreter(); + if (!activeInterpreter || !(await this.hasJupyter(activeInterpreter))) { + const list = await this.getJupyterInterpreters(); + this.forceSettingsChanged(undefined, list[0].path); + + // Also set this as the interpreter to use for jupyter + await this.serviceManager + .get(JupyterInterpreterService) + .setAsSelectedInterpreter(list[0]); + } + } + } + + public get kernelService() { + return this.kernelServiceMock; + } + + // tslint:disable:any + public createWebView( + mount: () => ReactWrapper, React.Component>, + role: vsls.Role = vsls.Role.None + ) { + // Force the container to mock actual live share if necessary + if (role !== vsls.Role.None) { + const liveShareTest = this.get(ILiveShareApi) as ILiveShareTestingApi; + liveShareTest.forceRole(role); + } + + // We need to mount the react control before we even create an interactive window object. Otherwise the mount will miss rendering some parts + this.mountReactControl(mount); + } + + public getContext(name: string): boolean { + if (this.setContexts.hasOwnProperty(name)) { + return this.setContexts[name]; + } + + return false; + } + + public getSettings(resource?: Uri) { + const key = this.getResourceKey(resource); + let setting = this.settingsMap.get(key); + if (!setting) { + // Make sure we have the default config for this resource first. + this.getWorkspaceConfig('python', resource); + setting = new (class extends PythonSettings { + public fireChangeEvent() { + this.changed.fire(); + } + })(resource, new MockAutoSelectionService(), this.serviceManager.get(IWorkspaceService)); + this.settingsMap.set(key, setting); + } + return setting; + } + + public forceSettingsChanged(resource: Resource, newPath: string, datascienceSettings?: IDataScienceSettings) { + const settings = this.getSettings(resource); + settings.pythonPath = newPath; + settings.datascience = datascienceSettings ? datascienceSettings : settings.datascience; + + // The workspace config must be updated too as a config change event will cause the data to be reread from + // the config. + const config = this.getWorkspaceConfig('python', resource); + config.update('pythonPath', newPath).ignoreErrors(); + config.update('dataScience', settings.datascience).ignoreErrors(); + settings.fireChangeEvent(); + this.configChangeEvent.fire({ + affectsConfiguration(_s: string, _r?: Uri): boolean { + return true; + } + }); + } + + public async getJupyterCapableInterpreter(): Promise { + const list = await this.getJupyterInterpreters(); + return list ? list[0] : undefined; + } + + public async getJupyterInterpreters(): Promise { + // This should be cacheable as we don't install new interpreters during tests + if (DataScienceIocContainer.jupyterInterpreters.length > 0) { + return DataScienceIocContainer.jupyterInterpreters; + } + const list = await this.get(IInterpreterService).getInterpreters(undefined); + const promises = list.map((f) => this.hasJupyter(f).then((b) => (b ? f : undefined))); + const resolved = await Promise.all(promises); + DataScienceIocContainer.jupyterInterpreters = resolved.filter((r) => r) as PythonInterpreter[]; + return DataScienceIocContainer.jupyterInterpreters; + } + + public addWorkspaceFolder(folderPath: string) { + const workspaceFolder = new MockWorkspaceFolder(folderPath, this.workspaceFolders.length); + this.workspaceFolders.push(workspaceFolder); + return workspaceFolder; + } + + public addResourceToFolder(resource: Uri, folderPath: string) { + let folder = this.workspaceFolders.find((f) => f.uri.fsPath === folderPath); + if (!folder) { + folder = this.addWorkspaceFolder(folderPath); + } + folder.ownedResources.add(resource.toString()); + } + + public get(serviceIdentifier: interfaces.ServiceIdentifier, name?: string | number | symbol): T { + return this.serviceManager.get(serviceIdentifier, name); + } + + public getAll(serviceIdentifier: interfaces.ServiceIdentifier, name?: string | number | symbol): T[] { + return this.serviceManager.getAll(serviceIdentifier, name); + } + + public addDocument(code: string, file: string) { + this.documentManager.addDocument(code, file); + } + + public addMessageListener(callback: (m: string, p: any) => void) { + this.extraListeners.push(callback); + } + + public removeMessageListener(callback: (m: string, p: any) => void) { + const index = this.extraListeners.indexOf(callback); + if (index >= 0) { + this.extraListeners.splice(index, 1); + } + } + + public addInterpreter(newInterpreter: PythonInterpreter, commands: SupportedCommands) { + if (this.mockJupyter) { + this.mockJupyter.addInterpreter(newInterpreter, commands); + } + } + + public postMessageToWebPanel(msg: any) { + if (this.webPanelListener) { + this.webPanelListener.onMessage(msg.type, msg.payload); + } else { + this.missedMessages.push(msg); + } + + if (this.extraListeners.length) { + this.extraListeners.forEach((e) => e(msg.type, msg.payload)); + } + if (this.wrapperCreatedPromise && !this.wrapperCreatedPromise.resolved) { + this.wrapperCreatedPromise.resolve(); + } + + // Clear out msg payload + delete msg.payload; + } + + public getWorkspaceConfig(section: string | undefined, resource?: Resource): MockWorkspaceConfiguration { + if (!section || section !== 'python') { + return this.emptyConfig; + } + const key = this.getResourceKey(resource); + let result = this.configMap.get(key); + if (!result) { + result = this.generatePythonWorkspaceConfig(); + this.configMap.set(key, result); + } + return result; + } + + private createWebPanel(): IWebPanel { + const webPanel = mock(WebPanel); + when(webPanel.postMessage(anything())).thenCall((m) => { + // tslint:disable-next-line: no-require-imports + const reactHelpers = require('./reactHelpers') as typeof import('./reactHelpers'); + const message = reactHelpers.createMessageEvent(m); + if (this.postMessage) { + this.postMessage(message); + } + if (m.payload) { + delete m.payload; + } + }); + when((webPanel as any).then).thenReturn(undefined); + return instance(webPanel); + } + + private async onCreateWebPanel(options: IWebPanelOptions) { + // Keep track of the current listener. It listens to messages through the vscode api + this.webPanelListener = options.listener; + + // Send messages that were already posted but were missed. + // During normal operation, the react control will not be created before + // the webPanelListener + if (this.missedMessages.length && this.webPanelListener) { + // This needs to be async because we are being called in the ctor of the webpanel. It can't + // handle some messages during the ctor. + setTimeout(() => { + this.missedMessages.forEach((m) => + this.webPanelListener ? this.webPanelListener.onMessage(m.type, m.payload) : noop() + ); + }, 0); + + // Note, you might think we should clean up the messages. However since the mount only occurs once, we might + // create multiple webpanels with the same mount. We need to resend these messages to + // other webpanels that get created with the same mount. + } + + // Return our dummy web panel + return this.createWebPanel(); + } + + private generatePythonWorkspaceConfig(): MockWorkspaceConfiguration { + // Create a dummy settings just to setup the workspace config + const pythonSettings = new PythonSettings(undefined, new MockAutoSelectionService()); + pythonSettings.pythonPath = this.defaultPythonPath!; + pythonSettings.datascience = { + allowImportFromNotebook: true, + jupyterLaunchTimeout: 20000, + jupyterLaunchRetries: 3, + enabled: true, + jupyterServerURI: 'local', + // tslint:disable-next-line: no-invalid-template-strings + notebookFileRoot: '${fileDirname}', + changeDirOnImportExport: false, + useDefaultConfigForJupyter: true, + jupyterInterruptTimeout: 10000, + searchForJupyter: true, + showCellInputCode: true, + collapseCellInputCodeByDefault: true, + allowInput: true, + maxOutputSize: 400, + errorBackgroundColor: '#FFFFFF', + sendSelectionToInteractiveWindow: false, + codeRegularExpression: '^(#\\s*%%|#\\s*\\|#\\s*In\\[\\d*?\\]|#\\s*In\\[ \\])', + markdownRegularExpression: '^(#\\s*%%\\s*\\[markdown\\]|#\\s*\\)', + variableExplorerExclude: 'module;function;builtin_function_or_method', + liveShareConnectionTimeout: 100, + enablePlotViewer: true, + stopOnFirstLineWhileDebugging: true, + stopOnError: true, + addGotoCodeLenses: true, + enableCellCodeLens: true, + runStartupCommands: '', + debugJustMyCode: true, + variableQueries: [], + jupyterCommandLineArguments: [], + disableJupyterAutoStart: true + }; + pythonSettings.jediEnabled = false; + pythonSettings.downloadLanguageServer = false; + const folders = ['Envs', '.virtualenvs']; + pythonSettings.venvFolders = folders; + pythonSettings.venvPath = path.join('~', 'foo'); + pythonSettings.terminal = { + executeInFileDir: false, + launchArgs: [], + activateEnvironment: true, + activateEnvInCurrentTerminal: false + }; + + // Use these settings to default all of the settings in a python configuration + return new MockWorkspaceConfiguration(pythonSettings); + } + + private createWorkspaceService() { + class MockFileSystemWatcher implements FileSystemWatcher { + public ignoreCreateEvents: boolean = false; + public ignoreChangeEvents: boolean = false; + public ignoreDeleteEvents: boolean = false; + //tslint:disable-next-line:no-any + public onDidChange(_listener: (e: Uri) => any, _thisArgs?: any, _disposables?: Disposable[]): Disposable { + return { dispose: noop }; + } + //tslint:disable-next-line:no-any + public onDidDelete(_listener: (e: Uri) => any, _thisArgs?: any, _disposables?: Disposable[]): Disposable { + return { dispose: noop }; + } + //tslint:disable-next-line:no-any + public onDidCreate(_listener: (e: Uri) => any, _thisArgs?: any, _disposables?: Disposable[]): Disposable { + return { dispose: noop }; + } + public dispose() { + noop(); + } + } + + const workspaceService = mock(WorkspaceService); + this.serviceManager.addSingletonInstance(IWorkspaceService, instance(workspaceService)); + when(workspaceService.onDidChangeConfiguration).thenReturn(this.configChangeEvent.event); + when(workspaceService.onDidChangeWorkspaceFolders).thenReturn(this.worksaceFoldersChangedEvent.event); + + // Create another config for other parts of the workspace config. + when(workspaceService.getConfiguration(anything())).thenCall(this.getWorkspaceConfig.bind(this)); + when(workspaceService.getConfiguration(anything(), anything())).thenCall(this.getWorkspaceConfig.bind(this)); + const testWorkspaceFolder = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience'); + + when(workspaceService.createFileSystemWatcher(anything(), anything(), anything(), anything())).thenReturn( + new MockFileSystemWatcher() + ); + when(workspaceService.createFileSystemWatcher(anything())).thenReturn(new MockFileSystemWatcher()); + when(workspaceService.hasWorkspaceFolders).thenReturn(true); + when(workspaceService.workspaceFolders).thenReturn(this.workspaceFolders); + when(workspaceService.rootPath).thenReturn(testWorkspaceFolder); + when(workspaceService.getWorkspaceFolder(anything())).thenCall(this.getWorkspaceFolder.bind(this)); + this.addWorkspaceFolder(testWorkspaceFolder); + return workspaceService; + } + + private getWorkspaceFolder(uri: Resource): WorkspaceFolder | undefined { + if (uri) { + return this.workspaceFolders.find((w) => w.ownedResources.has(uri.toString())); + } + return undefined; + } + + private getResourceKey(resource: Resource): string { + const workspace = this.serviceManager.get(IWorkspaceService); + const workspaceFolderUri = PythonSettings.getSettingsUriAndTarget(resource, workspace).uri; + return workspaceFolderUri ? workspaceFolderUri.fsPath : ''; + } + + private async hasJupyter(interpreter: PythonInterpreter): Promise { + try { + const dependencyChecker = this.serviceManager.get( + JupyterInterpreterDependencyService + ); + return dependencyChecker.areDependenciesInstalled(interpreter); + } catch (ex) { + return false; + } + } + + private findPythonPath(): string { + try { + // Give preference to the CI test python (could also be set in launch.json for debugging). + const output = child_process.execFileSync( + process.env.CI_PYTHON_PATH || 'python', + ['-c', 'import sys;print(sys.executable)'], + { encoding: 'utf8' } + ); + return output.replace(/\r?\n/g, ''); + } catch (ex) { + return 'python'; + } + } + + private mountReactControl(mount: () => ReactWrapper, React.Component>) { + // This is a remount (or first time). Clear out messages that were sent + // by the last mount + this.missedMessages = []; + this.webPanelListener = undefined; + this.extraListeners = []; + this.wrapperCreatedPromise = undefined; + + // Setup the acquireVsCodeApi. The react control will cache this value when it's mounted. + const globalAcquireVsCodeApi = (): IVsCodeApi => { + return { + // tslint:disable-next-line:no-any + postMessage: (msg: any) => { + this.postMessageToWebPanel(msg); + }, + // tslint:disable-next-line:no-any no-empty + setState: (_msg: any) => {}, + // tslint:disable-next-line:no-any no-empty + getState: () => { + return {}; + } + }; + }; + // tslint:disable-next-line:no-string-literal + (global as any)['acquireVsCodeApi'] = globalAcquireVsCodeApi; + + // Remap event handlers to point to the container. + const oldListener = window.addEventListener; + window.addEventListener = (event: string, cb: any) => { + if (event === 'message') { + this.postMessage = cb; + } + }; + + // Mount our main panel. This will make the global api be cached and have the event handler registered + this.wrapper = mount(); + + // We can remove the global api and event listener now. + delete (global as any).acquireVsCodeApi; + window.addEventListener = oldListener; + } +} diff --git a/src/test/datascience/datascienceSurveyBanner.unit.test.ts b/src/test/datascience/datascienceSurveyBanner.unit.test.ts index 617ba791ea8f..9c037b926277 100644 --- a/src/test/datascience/datascienceSurveyBanner.unit.test.ts +++ b/src/test/datascience/datascienceSurveyBanner.unit.test.ts @@ -43,7 +43,7 @@ suite('Data Science Survey Banner', () => { const expectedUri: string = targetUri; let receivedUri: string = ''; browser - .setup(b => + .setup((b) => b.launch( typemoq.It.is((a: string) => { receivedUri = a; @@ -75,7 +75,7 @@ suite('Data Science Survey Banner', () => { const expectedUri: string = targetUri; let receivedUri: string = ''; browser - .setup(b => + .setup((b) => b.launch( typemoq.It.is((a: string) => { receivedUri = a; @@ -96,7 +96,7 @@ suite('Data Science Survey Banner', () => { test('Do not show data science banner when it is disabled', () => { appShell - .setup(a => + .setup((a) => a.showInformationMessage(typemoq.It.isValue(message), typemoq.It.isValue(yes), typemoq.It.isValue(no)) ) .verifiable(typemoq.Times.never()); @@ -115,7 +115,7 @@ suite('Data Science Survey Banner', () => { }); test('Do not show data science banner if we have not hit our execution count or our notebook count', () => { appShell - .setup(a => + .setup((a) => a.showInformationMessage(typemoq.It.isValue(message), typemoq.It.isValue(yes), typemoq.It.isValue(no)) ) .verifiable(typemoq.Times.never()); @@ -152,51 +152,51 @@ function preparePopup( const openedEventEmitter = new EventEmitter(); when(provider.onDidOpenNotebookEditor).thenReturn(openedEventEmitter.event); enabledValState - .setup(a => a.updateValue(typemoq.It.isValue(true))) + .setup((a) => a.updateValue(typemoq.It.isValue(true))) .returns(() => { enabledValue = true; return Promise.resolve(); }); enabledValState - .setup(a => a.updateValue(typemoq.It.isValue(false))) + .setup((a) => a.updateValue(typemoq.It.isValue(false))) .returns(() => { enabledValue = false; return Promise.resolve(); }); executionCountState - .setup(a => a.updateValue(typemoq.It.isAnyNumber())) + .setup((a) => a.updateValue(typemoq.It.isAnyNumber())) .returns(() => { executionCount += 1; return Promise.resolve(); }); openCountState - .setup(a => a.updateValue(typemoq.It.isAnyNumber())) - .returns(v => { + .setup((a) => a.updateValue(typemoq.It.isAnyNumber())) + .returns((v) => { openCount = v; return Promise.resolve(); }); - enabledValState.setup(a => a.value).returns(() => enabledValue); - executionCountState.setup(a => a.value).returns(() => executionCount); - openCountState.setup(a => a.value).returns(() => openCount); + enabledValState.setup((a) => a.value).returns(() => enabledValue); + executionCountState.setup((a) => a.value).returns(() => executionCount); + openCountState.setup((a) => a.value).returns(() => openCount); myfactory - .setup(a => + .setup((a) => a.createGlobalPersistentState(typemoq.It.isValue(DSSurveyStateKeys.ShowBanner), typemoq.It.isValue(true)) ) .returns(() => { return enabledValState.object; }); myfactory - .setup(a => + .setup((a) => a.createGlobalPersistentState(typemoq.It.isValue(DSSurveyStateKeys.ShowBanner), typemoq.It.isValue(false)) ) .returns(() => { return enabledValState.object; }); myfactory - .setup(a => + .setup((a) => a.createGlobalPersistentState( typemoq.It.isValue(DSSurveyStateKeys.ExecutionCount), typemoq.It.isAnyNumber() @@ -206,7 +206,7 @@ function preparePopup( return executionCountState.object; }); myfactory - .setup(a => + .setup((a) => a.createGlobalPersistentState( typemoq.It.isValue(DSSurveyStateKeys.OpenNotebookCount), typemoq.It.isAnyNumber() diff --git a/src/test/datascience/dataviewer.functional.test.tsx b/src/test/datascience/dataviewer.functional.test.tsx index 9259ca14c0ef..a4b97925117c 100644 --- a/src/test/datascience/dataviewer.functional.test.tsx +++ b/src/test/datascience/dataviewer.functional.test.tsx @@ -1,241 +1,241 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -// tslint:disable:max-func-body-length trailing-comma no-any no-multiline-string -import '../../client/common/extensions'; - -import { nbformat } from '@jupyterlab/coreutils'; -import * as assert from 'assert'; -import { mount, ReactWrapper } from 'enzyme'; -import { parse } from 'node-html-parser'; -import * as React from 'react'; -import * as uuid from 'uuid/v4'; -import { Disposable, Uri } from 'vscode'; - -import { Identifiers } from '../../client/datascience/constants'; -import { DataViewerMessages } from '../../client/datascience/data-viewing/types'; -import { IDataViewer, IDataViewerProvider, INotebook, INotebookProvider } from '../../client/datascience/types'; -import { MainPanel } from '../../datascience-ui/data-explorer/mainPanel'; -import { ReactSlickGrid } from '../../datascience-ui/data-explorer/reactSlickGrid'; -import { noop } from '../core'; -import { DataScienceIocContainer } from './dataScienceIocContainer'; -import { waitForMessage } from './testHelpers'; - -// import { asyncDump } from '../common/asyncDump'; -suite('DataScience DataViewer tests', () => { - const disposables: Disposable[] = []; - let dataProvider: IDataViewerProvider; - let ioc: DataScienceIocContainer; - let notebook: INotebook | undefined; - - suiteSetup(function() { - // DataViewer tests require jupyter to run. Othewrise can't - // run any of our variable execution code - const isRollingBuild = process.env ? process.env.VSCODE_PYTHON_ROLLING !== undefined : false; - if (!isRollingBuild) { - // tslint:disable-next-line:no-console - console.log('Skipping DataViewer tests. Requires python environment'); - // tslint:disable-next-line:no-invalid-this - this.skip(); - } - }); - - setup(async () => { - ioc = new DataScienceIocContainer(); - ioc.registerDataScienceTypes(); - return ioc.activate(); - }); - - function mountWebView(): ReactWrapper, React.Component> { - // Setup our webview panel - ioc.createWebView(() => mount()); - - // Make sure the data explorer provider and execution factory in the container is created (the extension does this on startup in the extension) - dataProvider = ioc.get(IDataViewerProvider); - - return ioc.wrapper!; - } - - teardown(async () => { - for (const disposable of disposables) { - if (!disposable) { - continue; - } - // tslint:disable-next-line:no-any - const promise = disposable.dispose() as Promise; - if (promise) { - await promise; - } - } - await ioc.dispose(); - delete (global as any).ascquireVsCodeApi; - }); - - suiteTeardown(() => { - // asyncDump(); - }); - - async function createDataViewer(variable: string, type: string): Promise { - return dataProvider.create( - { - name: variable, - value: '', - supportsDataExplorer: true, - type, - size: 0, - truncated: true, - shape: '', - count: 0 - }, - notebook! - ); - } - - async function injectCode(code: string): Promise { - const notebookProvider = ioc.get(INotebookProvider); - notebook = await notebookProvider.getOrCreateNotebook({ - identity: Uri.parse(Identifiers.InteractiveWindowIdentity) - }); - if (notebook) { - const cells = await notebook.execute(code, Identifiers.EmptyFileName, 0, uuid()); - assert.equal(cells.length, 1, `Wrong number of cells returned`); - assert.equal(cells[0].data.cell_type, 'code', `Wrong type of cell returned`); - const cell = cells[0].data as nbformat.ICodeCell; - if (cell.outputs.length > 0) { - const error = cell.outputs[0].evalue; - if (error) { - assert.fail(`Unexpected error: ${error}`); - } - } - } - } - - function getCompletedPromise(): Promise { - return waitForMessage(ioc, DataViewerMessages.CompletedData); - } - - // tslint:disable-next-line:no-any - function runMountedTest( - name: string, - testFunc: (wrapper: ReactWrapper, React.Component>) => Promise - ) { - test(name, async () => { - const wrapper = mountWebView(); - try { - await testFunc(wrapper); - } finally { - // Make sure to unmount the wrapper or it will interfere with other tests - if (wrapper && wrapper.length) { - wrapper.unmount(); - } - } - }); - } - - function sortRows( - wrapper: ReactWrapper, React.Component>, - sortCol: string, - sortAsc: boolean - ): void { - // Cause our sort - const mainPanelWrapper = wrapper.find(MainPanel); - assert.ok(mainPanelWrapper && mainPanelWrapper.length > 0, 'Grid not found to sort on'); - const mainPanel = mainPanelWrapper.instance() as MainPanel; - assert.ok(mainPanel, 'Main panel instance not found'); - const reactGrid = (mainPanel as any).grid.current as ReactSlickGrid; - assert.ok(reactGrid, 'Grid control not found'); - if (reactGrid.state.grid) { - const cols = reactGrid.state.grid.getColumns(); - const col = cols.find(c => c.field === sortCol); - assert.ok(col, `${sortCol} is not a column of the grid`); - reactGrid.sort(new Slick.EventData(), { - sortCol: col, - sortAsc, - multiColumnSort: false, - grid: reactGrid.state.grid - }); - } - } - - function verifyRows(wrapper: ReactWrapper, React.Component>, rows: (string | number)[]) { - const mainPanel = wrapper.find('.main-panel'); - assert.ok(mainPanel.length >= 1, "Didn't find any cells being rendered"); - - // Force the main panel to actually render. - const html = mainPanel.html(); - const root = parse(html) as any; - const cells = root.querySelectorAll('.react-grid-cell') as HTMLElement[]; - assert.ok(cells, 'No cells found'); - assert.ok(cells.length >= rows.length, 'Not enough cells found'); - // Cells should be an array that matches up to the values we expect. - for (let i = 0; i < rows.length; i += 1) { - // Span should have our value (based on the CellFormatter's output) - const span = cells[i].querySelector('div.cell-formatter span') as HTMLSpanElement; - assert.ok(span, `Span ${i} not found`); - const val = rows[i].toString(); - assert.equal(val, span.innerHTML, `Row ${i} not matching. ${span.innerHTML} !== ${val}`); - } - } - - runMountedTest('Data Frame', async wrapper => { - await injectCode('import pandas as pd\r\ndf = pd.DataFrame([0, 1, 2, 3])'); - const gotAllRows = getCompletedPromise(); - const dv = await createDataViewer('df', 'DataFrame'); - assert.ok(dv, 'DataViewer not created'); - await gotAllRows; - - verifyRows(wrapper, [0, 0, 1, 1, 2, 2, 3, 3]); - }); - - runMountedTest('List', async wrapper => { - await injectCode('ls = [0, 1, 2, 3]'); - const gotAllRows = getCompletedPromise(); - const dv = await createDataViewer('ls', 'list'); - assert.ok(dv, 'DataViewer not created'); - await gotAllRows; - - verifyRows(wrapper, [0, 0, 1, 1, 2, 2, 3, 3]); - }); - - runMountedTest('Series', async wrapper => { - await injectCode('import pandas as pd\r\ns = pd.Series([0, 1, 2, 3])'); - const gotAllRows = getCompletedPromise(); - const dv = await createDataViewer('s', 'Series'); - assert.ok(dv, 'DataViewer not created'); - await gotAllRows; - - verifyRows(wrapper, [0, 0, 1, 1, 2, 2, 3, 3]); - }); - - runMountedTest('np.array', async wrapper => { - await injectCode('import numpy as np\r\nx = np.array([0, 1, 2, 3])'); - const gotAllRows = getCompletedPromise(); - const dv = await createDataViewer('x', 'ndarray'); - assert.ok(dv, 'DataViewer not created'); - await gotAllRows; - - verifyRows(wrapper, [0, 0, 1, 1, 2, 2, 3, 3]); - }); - - runMountedTest('Failure', async _wrapper => { - await injectCode('import numpy as np\r\nx = np.array([0, 1, 2, 3])'); - try { - await createDataViewer('unknown variable', 'ndarray'); - assert.fail('Exception should have been thrown'); - } catch { - noop(); - } - }); - - runMountedTest('Sorting', async wrapper => { - await injectCode('import numpy as np\r\nx = np.array([0, 1, 2, 3])'); - const gotAllRows = getCompletedPromise(); - const dv = await createDataViewer('x', 'ndarray'); - assert.ok(dv, 'DataViewer not created'); - await gotAllRows; - - verifyRows(wrapper, [0, 0, 1, 1, 2, 2, 3, 3]); - sortRows(wrapper, '0', false); - verifyRows(wrapper, [3, 3, 2, 2, 1, 1, 0, 0]); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +// tslint:disable:max-func-body-length trailing-comma no-any no-multiline-string +import '../../client/common/extensions'; + +import { nbformat } from '@jupyterlab/coreutils'; +import * as assert from 'assert'; +import { mount, ReactWrapper } from 'enzyme'; +import { parse } from 'node-html-parser'; +import * as React from 'react'; +import * as uuid from 'uuid/v4'; +import { Disposable, Uri } from 'vscode'; + +import { Identifiers } from '../../client/datascience/constants'; +import { DataViewerMessages } from '../../client/datascience/data-viewing/types'; +import { IDataViewer, IDataViewerProvider, INotebook, INotebookProvider } from '../../client/datascience/types'; +import { MainPanel } from '../../datascience-ui/data-explorer/mainPanel'; +import { ReactSlickGrid } from '../../datascience-ui/data-explorer/reactSlickGrid'; +import { noop } from '../core'; +import { DataScienceIocContainer } from './dataScienceIocContainer'; +import { waitForMessage } from './testHelpers'; + +// import { asyncDump } from '../common/asyncDump'; +suite('DataScience DataViewer tests', () => { + const disposables: Disposable[] = []; + let dataProvider: IDataViewerProvider; + let ioc: DataScienceIocContainer; + let notebook: INotebook | undefined; + + suiteSetup(function () { + // DataViewer tests require jupyter to run. Othewrise can't + // run any of our variable execution code + const isRollingBuild = process.env ? process.env.VSCODE_PYTHON_ROLLING !== undefined : false; + if (!isRollingBuild) { + // tslint:disable-next-line:no-console + console.log('Skipping DataViewer tests. Requires python environment'); + // tslint:disable-next-line:no-invalid-this + this.skip(); + } + }); + + setup(async () => { + ioc = new DataScienceIocContainer(); + ioc.registerDataScienceTypes(); + return ioc.activate(); + }); + + function mountWebView(): ReactWrapper, React.Component> { + // Setup our webview panel + ioc.createWebView(() => mount()); + + // Make sure the data explorer provider and execution factory in the container is created (the extension does this on startup in the extension) + dataProvider = ioc.get(IDataViewerProvider); + + return ioc.wrapper!; + } + + teardown(async () => { + for (const disposable of disposables) { + if (!disposable) { + continue; + } + // tslint:disable-next-line:no-any + const promise = disposable.dispose() as Promise; + if (promise) { + await promise; + } + } + await ioc.dispose(); + delete (global as any).ascquireVsCodeApi; + }); + + suiteTeardown(() => { + // asyncDump(); + }); + + async function createDataViewer(variable: string, type: string): Promise { + return dataProvider.create( + { + name: variable, + value: '', + supportsDataExplorer: true, + type, + size: 0, + truncated: true, + shape: '', + count: 0 + }, + notebook! + ); + } + + async function injectCode(code: string): Promise { + const notebookProvider = ioc.get(INotebookProvider); + notebook = await notebookProvider.getOrCreateNotebook({ + identity: Uri.parse(Identifiers.InteractiveWindowIdentity) + }); + if (notebook) { + const cells = await notebook.execute(code, Identifiers.EmptyFileName, 0, uuid()); + assert.equal(cells.length, 1, `Wrong number of cells returned`); + assert.equal(cells[0].data.cell_type, 'code', `Wrong type of cell returned`); + const cell = cells[0].data as nbformat.ICodeCell; + if (cell.outputs.length > 0) { + const error = cell.outputs[0].evalue; + if (error) { + assert.fail(`Unexpected error: ${error}`); + } + } + } + } + + function getCompletedPromise(): Promise { + return waitForMessage(ioc, DataViewerMessages.CompletedData); + } + + // tslint:disable-next-line:no-any + function runMountedTest( + name: string, + testFunc: (wrapper: ReactWrapper, React.Component>) => Promise + ) { + test(name, async () => { + const wrapper = mountWebView(); + try { + await testFunc(wrapper); + } finally { + // Make sure to unmount the wrapper or it will interfere with other tests + if (wrapper && wrapper.length) { + wrapper.unmount(); + } + } + }); + } + + function sortRows( + wrapper: ReactWrapper, React.Component>, + sortCol: string, + sortAsc: boolean + ): void { + // Cause our sort + const mainPanelWrapper = wrapper.find(MainPanel); + assert.ok(mainPanelWrapper && mainPanelWrapper.length > 0, 'Grid not found to sort on'); + const mainPanel = mainPanelWrapper.instance() as MainPanel; + assert.ok(mainPanel, 'Main panel instance not found'); + const reactGrid = (mainPanel as any).grid.current as ReactSlickGrid; + assert.ok(reactGrid, 'Grid control not found'); + if (reactGrid.state.grid) { + const cols = reactGrid.state.grid.getColumns(); + const col = cols.find((c) => c.field === sortCol); + assert.ok(col, `${sortCol} is not a column of the grid`); + reactGrid.sort(new Slick.EventData(), { + sortCol: col, + sortAsc, + multiColumnSort: false, + grid: reactGrid.state.grid + }); + } + } + + function verifyRows(wrapper: ReactWrapper, React.Component>, rows: (string | number)[]) { + const mainPanel = wrapper.find('.main-panel'); + assert.ok(mainPanel.length >= 1, "Didn't find any cells being rendered"); + + // Force the main panel to actually render. + const html = mainPanel.html(); + const root = parse(html) as any; + const cells = root.querySelectorAll('.react-grid-cell') as HTMLElement[]; + assert.ok(cells, 'No cells found'); + assert.ok(cells.length >= rows.length, 'Not enough cells found'); + // Cells should be an array that matches up to the values we expect. + for (let i = 0; i < rows.length; i += 1) { + // Span should have our value (based on the CellFormatter's output) + const span = cells[i].querySelector('div.cell-formatter span') as HTMLSpanElement; + assert.ok(span, `Span ${i} not found`); + const val = rows[i].toString(); + assert.equal(val, span.innerHTML, `Row ${i} not matching. ${span.innerHTML} !== ${val}`); + } + } + + runMountedTest('Data Frame', async (wrapper) => { + await injectCode('import pandas as pd\r\ndf = pd.DataFrame([0, 1, 2, 3])'); + const gotAllRows = getCompletedPromise(); + const dv = await createDataViewer('df', 'DataFrame'); + assert.ok(dv, 'DataViewer not created'); + await gotAllRows; + + verifyRows(wrapper, [0, 0, 1, 1, 2, 2, 3, 3]); + }); + + runMountedTest('List', async (wrapper) => { + await injectCode('ls = [0, 1, 2, 3]'); + const gotAllRows = getCompletedPromise(); + const dv = await createDataViewer('ls', 'list'); + assert.ok(dv, 'DataViewer not created'); + await gotAllRows; + + verifyRows(wrapper, [0, 0, 1, 1, 2, 2, 3, 3]); + }); + + runMountedTest('Series', async (wrapper) => { + await injectCode('import pandas as pd\r\ns = pd.Series([0, 1, 2, 3])'); + const gotAllRows = getCompletedPromise(); + const dv = await createDataViewer('s', 'Series'); + assert.ok(dv, 'DataViewer not created'); + await gotAllRows; + + verifyRows(wrapper, [0, 0, 1, 1, 2, 2, 3, 3]); + }); + + runMountedTest('np.array', async (wrapper) => { + await injectCode('import numpy as np\r\nx = np.array([0, 1, 2, 3])'); + const gotAllRows = getCompletedPromise(); + const dv = await createDataViewer('x', 'ndarray'); + assert.ok(dv, 'DataViewer not created'); + await gotAllRows; + + verifyRows(wrapper, [0, 0, 1, 1, 2, 2, 3, 3]); + }); + + runMountedTest('Failure', async (_wrapper) => { + await injectCode('import numpy as np\r\nx = np.array([0, 1, 2, 3])'); + try { + await createDataViewer('unknown variable', 'ndarray'); + assert.fail('Exception should have been thrown'); + } catch { + noop(); + } + }); + + runMountedTest('Sorting', async (wrapper) => { + await injectCode('import numpy as np\r\nx = np.array([0, 1, 2, 3])'); + const gotAllRows = getCompletedPromise(); + const dv = await createDataViewer('x', 'ndarray'); + assert.ok(dv, 'DataViewer not created'); + await gotAllRows; + + verifyRows(wrapper, [0, 0, 1, 1, 2, 2, 3, 3]); + sortRows(wrapper, '0', false); + verifyRows(wrapper, [3, 3, 2, 2, 1, 1, 0, 0]); + }); +}); diff --git a/src/test/datascience/debugger.functional.test.tsx b/src/test/datascience/debugger.functional.test.tsx index 6d1ae82079d4..8de5cd3e7e81 100644 --- a/src/test/datascience/debugger.functional.test.tsx +++ b/src/test/datascience/debugger.functional.test.tsx @@ -38,7 +38,7 @@ suite('DataScience Debugger tests', () => { let lastErrorMessage: string | undefined; let mockDebuggerService: MockDebuggerService | undefined; - suiteSetup(function() { + suiteSetup(function () { // Debugger tests require jupyter to run. Othewrise can't not really testing them const isRollingBuild = process.env ? process.env.VSCODE_PYTHON_ROLLING !== undefined : false; @@ -100,17 +100,17 @@ suite('DataScience Debugger tests', () => { } }; const appShell = TypeMoq.Mock.ofType(); - appShell.setup(a => a.showErrorMessage(TypeMoq.It.isAnyString())).returns(e => (lastErrorMessage = e)); + appShell.setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString())).returns((e) => (lastErrorMessage = e)); appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve('')); appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((_a1: string, a2: string, _a3: string) => Promise.resolve(a2)); appShell - .setup(a => a.showSaveDialog(TypeMoq.It.isAny())) + .setup((a) => a.showSaveDialog(TypeMoq.It.isAny())) .returns(() => Promise.resolve(Uri.file('test.ipynb'))); - appShell.setup(a => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); + appShell.setup((a) => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); result.serviceManager.rebindInstance(IApplicationShell, appShell.object); @@ -199,7 +199,7 @@ suite('DataScience Debugger tests', () => { if (expectedBreakLine) { assert.equal(codeLenses.length, 3, 'Incorrect number of debug code lenses stop'); - codeLenses.forEach(codeLens => { + codeLenses.forEach((codeLens) => { assert.ok(codeLens.range.contains(new Position(expectedBreakLine - 1, 0))); }); } else { diff --git a/src/test/datascience/editor-integration/cellhashprovider.unit.test.ts b/src/test/datascience/editor-integration/cellhashprovider.unit.test.ts index 6a8067da3b32..6b86569e4a13 100644 --- a/src/test/datascience/editor-integration/cellhashprovider.unit.test.ts +++ b/src/test/datascience/editor-integration/cellhashprovider.unit.test.ts @@ -42,11 +42,11 @@ suite('CellHashProvider Unit Tests', () => { dataScienceSettings = TypeMoq.Mock.ofType(); debugService = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); - dataScienceSettings.setup(d => d.enabled).returns(() => true); - pythonSettings.setup(p => p.datascience).returns(() => dataScienceSettings.object); - configurationService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); - debugService.setup(d => d.activeDebugSession).returns(() => undefined); - fileSystem.setup(d => d.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => true); + dataScienceSettings.setup((d) => d.enabled).returns(() => true); + pythonSettings.setup((p) => p.datascience).returns(() => dataScienceSettings.object); + configurationService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + debugService.setup((d) => d.activeDebugSession).returns(() => undefined); + fileSystem.setup((d) => d.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => true); documentManager = new MockDocumentManager(); hashProvider = new CellHashProvider( documentManager, @@ -287,8 +287,8 @@ suite('CellHashProvider Unit Tests', () => { // Execution count should go up, but still only have two cells. const hashes = hashProvider.getHashes(); assert.equal(hashes.length, 2, 'Wrong number of hashes'); - const fooHash = hashes.find(h => h.file === Uri.file('foo.py').fsPath); - const barHash = hashes.find(h => h.file === Uri.file('bar.py').fsPath); + const fooHash = hashes.find((h) => h.file === Uri.file('foo.py').fsPath); + const barHash = hashes.find((h) => h.file === Uri.file('bar.py').fsPath); assert.ok(fooHash, 'No hash for foo.py'); assert.ok(barHash, 'No hash for bar.py'); assert.equal(fooHash!.hashes.length, 2, 'Not enough hashes found'); diff --git a/src/test/datascience/editor-integration/codelensprovider.unit.test.ts b/src/test/datascience/editor-integration/codelensprovider.unit.test.ts index 929240035d2b..244d743c3b75 100644 --- a/src/test/datascience/editor-integration/codelensprovider.unit.test.ts +++ b/src/test/datascience/editor-integration/codelensprovider.unit.test.ts @@ -37,13 +37,13 @@ suite('DataScienceCodeLensProvider Unit Tests', () => { pythonSettings = TypeMoq.Mock.ofType(); dataScienceSettings = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); - dataScienceSettings.setup(d => d.enabled).returns(() => true); - pythonSettings.setup(p => p.datascience).returns(() => dataScienceSettings.object); - configurationService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + dataScienceSettings.setup((d) => d.enabled).returns(() => true); + pythonSettings.setup((p) => p.datascience).returns(() => dataScienceSettings.object); + configurationService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); commandManager - .setup(c => c.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((c) => c.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve()); - debugService.setup(d => d.activeDebugSession).returns(() => undefined); + debugService.setup((d) => d.activeDebugSession).returns(() => undefined); codeLensProvider = new DataScienceCodeLensProvider( serviceContainer.object, @@ -60,19 +60,19 @@ suite('DataScienceCodeLensProvider Unit Tests', () => { test('Initialize Code Lenses one document', () => { // Create our document const document = TypeMoq.Mock.ofType(); - document.setup(d => d.fileName).returns(() => 'test.py'); - document.setup(d => d.version).returns(() => 1); + document.setup((d) => d.fileName).returns(() => 'test.py'); + document.setup((d) => d.version).returns(() => 1); const targetCodeWatcher = TypeMoq.Mock.ofType(); targetCodeWatcher - .setup(tc => tc.getCodeLenses()) + .setup((tc) => tc.getCodeLenses()) .returns(() => []) .verifiable(TypeMoq.Times.once()); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ICodeWatcher))) + .setup((c) => c.get(TypeMoq.It.isValue(ICodeWatcher))) .returns(() => targetCodeWatcher.object) .verifiable(TypeMoq.Times.once()); - documentManager.setup(d => d.textDocuments).returns(() => [document.object]); + documentManager.setup((d) => d.textDocuments).returns(() => [document.object]); codeLensProvider.provideCodeLenses(document.object, tokenSource.token); @@ -83,21 +83,21 @@ suite('DataScienceCodeLensProvider Unit Tests', () => { test('Initialize Code Lenses same doc called', () => { // Create our document const document = TypeMoq.Mock.ofType(); - document.setup(d => d.fileName).returns(() => 'test.py'); - document.setup(d => d.version).returns(() => 1); + document.setup((d) => d.fileName).returns(() => 'test.py'); + document.setup((d) => d.version).returns(() => 1); const targetCodeWatcher = TypeMoq.Mock.ofType(); targetCodeWatcher - .setup(tc => tc.getCodeLenses()) + .setup((tc) => tc.getCodeLenses()) .returns(() => []) .verifiable(TypeMoq.Times.exactly(2)); - targetCodeWatcher.setup(tc => tc.getFileName()).returns(() => 'test.py'); - targetCodeWatcher.setup(tc => tc.getVersion()).returns(() => 1); + targetCodeWatcher.setup((tc) => tc.getFileName()).returns(() => 'test.py'); + targetCodeWatcher.setup((tc) => tc.getVersion()).returns(() => 1); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ICodeWatcher))) + .setup((c) => c.get(TypeMoq.It.isValue(ICodeWatcher))) .returns(() => targetCodeWatcher.object) .verifiable(TypeMoq.Times.once()); - documentManager.setup(d => d.textDocuments).returns(() => [document.object]); + documentManager.setup((d) => d.textDocuments).returns(() => [document.object]); codeLensProvider.provideCodeLenses(document.object, tokenSource.token); codeLensProvider.provideCodeLenses(document.object, tokenSource.token); @@ -110,30 +110,30 @@ suite('DataScienceCodeLensProvider Unit Tests', () => { test('Initialize Code Lenses new name / version', () => { // Create our document const document = TypeMoq.Mock.ofType(); - document.setup(d => d.fileName).returns(() => 'test.py'); - document.setup(d => d.version).returns(() => 1); + document.setup((d) => d.fileName).returns(() => 'test.py'); + document.setup((d) => d.version).returns(() => 1); const document2 = TypeMoq.Mock.ofType(); - document2.setup(d => d.fileName).returns(() => 'test2.py'); - document2.setup(d => d.version).returns(() => 1); + document2.setup((d) => d.fileName).returns(() => 'test2.py'); + document2.setup((d) => d.version).returns(() => 1); const document3 = TypeMoq.Mock.ofType(); - document3.setup(d => d.fileName).returns(() => 'test.py'); - document3.setup(d => d.version).returns(() => 2); + document3.setup((d) => d.fileName).returns(() => 'test.py'); + document3.setup((d) => d.version).returns(() => 2); const targetCodeWatcher = TypeMoq.Mock.ofType(); targetCodeWatcher - .setup(tc => tc.getCodeLenses()) + .setup((tc) => tc.getCodeLenses()) .returns(() => []) .verifiable(TypeMoq.Times.exactly(3)); - targetCodeWatcher.setup(tc => tc.getFileName()).returns(() => 'test.py'); - targetCodeWatcher.setup(tc => tc.getVersion()).returns(() => 1); + targetCodeWatcher.setup((tc) => tc.getFileName()).returns(() => 'test.py'); + targetCodeWatcher.setup((tc) => tc.getVersion()).returns(() => 1); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ICodeWatcher))) + .setup((c) => c.get(TypeMoq.It.isValue(ICodeWatcher))) .returns(() => targetCodeWatcher.object) .verifiable(TypeMoq.Times.exactly(3)); documentManager - .setup(d => d.textDocuments) + .setup((d) => d.textDocuments) .returns(() => [document.object, document2.object, document3.object]); codeLensProvider.provideCodeLenses(document.object, tokenSource.token); diff --git a/src/test/datascience/editor-integration/codewatcher.unit.test.ts b/src/test/datascience/editor-integration/codewatcher.unit.test.ts index 0e80309face1..e0738de79c2b 100644 --- a/src/test/datascience/editor-integration/codewatcher.unit.test.ts +++ b/src/test/datascience/editor-integration/codewatcher.unit.test.ts @@ -96,17 +96,17 @@ suite('DataScience Code Watcher Unit Tests', () => { variableQueries: [], jupyterCommandLineArguments: [] }; - debugService.setup(d => d.activeDebugSession).returns(() => undefined); + debugService.setup((d) => d.activeDebugSession).returns(() => undefined); // Setup the service container to return code watchers serviceContainer = TypeMoq.Mock.ofType(); // Setup the file system - fileSystem.setup(f => f.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => true); + fileSystem.setup((f) => f.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => true); const codeLensFactory = new CodeLensFactory(configService.object, notebookProvider.object, fileSystem.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ICodeWatcher))) + .setup((c) => c.get(TypeMoq.It.isValue(ICodeWatcher))) .returns( () => new CodeWatcher( @@ -125,17 +125,17 @@ suite('DataScience Code Watcher Unit Tests', () => { // Setup our active history instance interactiveWindowProvider - .setup(h => h.getOrCreateActive()) + .setup((h) => h.getOrCreateActive()) .returns(() => Promise.resolve(activeInteractiveWindow.object)); // Setup our active text editor - documentManager.setup(dm => dm.activeTextEditor).returns(() => textEditor.object); + documentManager.setup((dm) => dm.activeTextEditor).returns(() => textEditor.object); // Setup config service - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings); commandManager - .setup(c => c.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((c) => c.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((c, n, v) => { if (c === 'setContext') { contexts.set(n, v); @@ -396,7 +396,7 @@ fourth line // Set up our expected call to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue(testString), TypeMoq.It.isValue(fileName), @@ -428,7 +428,7 @@ testing2`; const document = createDocument(inputText, fileName, version, TypeMoq.Times.atLeastOnce()); document - .setup(doc => doc.getText()) + .setup((doc) => doc.getText()) .returns(() => inputText) .verifiable(TypeMoq.Times.exactly(1)); @@ -437,7 +437,7 @@ testing2`; // Set up our expected calls to add code // RunFileInteractive should run the entire file in one block, not cell by cell like RunAllCells activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue(inputText), TypeMoq.It.isValue(fileName), @@ -470,7 +470,7 @@ testing2`; // Set up our expected calls to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue('testing0\n#%%\ntesting1'), TypeMoq.It.isValue(fileName), @@ -483,7 +483,7 @@ testing2`; .verifiable(TypeMoq.Times.once()); activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue('#%%\ntesting2'), TypeMoq.It.isValue(fileName), @@ -515,7 +515,7 @@ testing2`; // Set up our expected calls to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue('#%%\ntesting2'), TypeMoq.It.isValue(fileName), @@ -530,7 +530,7 @@ testing2`; .verifiable(TypeMoq.Times.once()); // For this test we need to set up a document selection point - textEditor.setup(te => te.selection).returns(() => new Selection(2, 0, 2, 0)); + textEditor.setup((te) => te.selection).returns(() => new Selection(2, 0, 2, 0)); await codeWatcher.runCurrentCell(); @@ -560,7 +560,7 @@ testing3`; // Set up our expected calls to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue(targetText1), TypeMoq.It.isValue(fileName), @@ -573,7 +573,7 @@ testing3`; .verifiable(TypeMoq.Times.once()); activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue(targetText2), TypeMoq.It.isValue(fileName), @@ -615,7 +615,7 @@ testing2`; // Set up our expected calls to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue(targetText1), TypeMoq.It.isValue(fileName), @@ -628,7 +628,7 @@ testing2`; .verifiable(TypeMoq.Times.once()); activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue(targetText2), TypeMoq.It.isValue(fileName), @@ -665,7 +665,7 @@ testing1`; // Set up our expected calls to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue(targetText), TypeMoq.It.isValue(fileName), @@ -697,11 +697,11 @@ print('testing')`; // If adding empty lines nothing should be added and history should not be started interactiveWindowProvider - .setup(h => h.getOrCreateActive()) + .setup((h) => h.getOrCreateActive()) .returns(() => Promise.resolve(activeInteractiveWindow.object)) .verifiable(TypeMoq.Times.never()); activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isAny(), TypeMoq.It.isValue(fileName), @@ -741,7 +741,7 @@ testing3`; // Set up our expected calls to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue(targetText), TypeMoq.It.isValue(fileName), @@ -772,7 +772,7 @@ testing2`; codeWatcher.setDocument(document.object); helper - .setup(h => + .setup((h) => h.getSelectedTextToExecute( TypeMoq.It.is((ed: TextEditor) => { return textEditor.object === ed; @@ -780,11 +780,11 @@ testing2`; ) ) .returns(() => Promise.resolve('testing2')); - helper.setup(h => h.normalizeLines(TypeMoq.It.isAny())).returns(() => Promise.resolve('testing2')); + helper.setup((h) => h.normalizeLines(TypeMoq.It.isAny())).returns(() => Promise.resolve('testing2')); // Set up our expected calls to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue('testing2'), TypeMoq.It.isValue(fileName), @@ -799,8 +799,8 @@ testing2`; .verifiable(TypeMoq.Times.once()); // For this test we need to set up a document selection point - textEditor.setup(te => te.document).returns(() => document.object); - textEditor.setup(te => te.selection).returns(() => new Selection(3, 0, 3, 0)); + textEditor.setup((te) => te.document).returns(() => document.object); + textEditor.setup((te) => te.selection).returns(() => new Selection(3, 0, 3, 0)); // Try our RunCell command with the first selection point await codeWatcher.runSelectionOrLine(textEditor.object); @@ -823,7 +823,7 @@ testing2`; // Set up our expected calls to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue('#%%\ntesting1'), TypeMoq.It.isValue(fileName), @@ -839,7 +839,7 @@ testing2`; // For this test we need to set up a document selection point const selection = new Selection(0, 0, 0, 0); - textEditor.setup(te => te.selection).returns(() => selection); + textEditor.setup((te) => te.selection).returns(() => selection); //textEditor.setup(te => te.selection = TypeMoq.It.isAny()).verifiable(TypeMoq.Times.once()); //textEditor.setup(te => te.selection = TypeMoq.It.isAnyObject(Selection)); @@ -872,8 +872,8 @@ testing2`; const version = 1; const inputText = '#%% foobar'; const document = createDocument(inputText, fileName, version, TypeMoq.Times.atLeastOnce()); - document.setup(doc => doc.getText()).returns(() => inputText); - documentManager.setup(d => d.textDocuments).returns(() => [document.object]); + document.setup((doc) => doc.getText()).returns(() => inputText); + documentManager.setup((d) => d.textDocuments).returns(() => [document.object]); const codeLensProvider = new DataScienceCodeLensProvider( serviceContainer.object, debugLocationTracker.object, @@ -930,7 +930,7 @@ testing2`; // Set up our expected calls to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue(targetText1), TypeMoq.It.isValue(fileName), @@ -943,7 +943,7 @@ testing2`; .verifiable(TypeMoq.Times.once()); activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue(targetText2), TypeMoq.It.isValue(fileName), @@ -975,7 +975,7 @@ testing2`; // Command tests override getText, so just need the ranges here // Set up our expected calls to add code activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue('#%%\ntesting1'), TypeMoq.It.isValue(fileName), @@ -988,7 +988,7 @@ testing2`; // Command tests override getText, so just need the ranges here .verifiable(TypeMoq.Times.once()); activeInteractiveWindow - .setup(h => + .setup((h) => h.addCode( TypeMoq.It.isValue('#%%\ntesting2'), TypeMoq.It.isValue(fileName), diff --git a/src/test/datascience/editor-integration/gotocell.functional.test.ts b/src/test/datascience/editor-integration/gotocell.functional.test.ts index a35450f65323..53abd84d8d7b 100644 --- a/src/test/datascience/editor-integration/gotocell.functional.test.ts +++ b/src/test/datascience/editor-integration/gotocell.functional.test.ts @@ -130,7 +130,7 @@ suite('DataScience gotocell tests', () => { function addDocument(cells: { code: string; result: any; cellType?: string }[], filePath: string) { let docText = ''; - cells.forEach(c => { + cells.forEach((c) => { addMockData(c.code, c.result, c.cellType); docText = docText.concat(c.code, '\n'); }); @@ -158,7 +158,7 @@ suite('DataScience gotocell tests', () => { const codeLenses = getCodeLenses(); assert.ok(codeLenses && codeLenses.length > 0, 'No cell code lenses found'); if (codeLenses.length) { - const runLens = codeLenses.filter(c => c.command && c.command.command === Commands.RunCell); + const runLens = codeLenses.filter((c) => c.command && c.command.command === Commands.RunCell); assert.ok(runLens && runLens.length > pos, 'No run cell code lenses found'); const codeLens = runLens[pos]; const code = doc.getText(codeLens.range); @@ -179,7 +179,7 @@ suite('DataScience gotocell tests', () => { // There should be one with the ScrollTo command const scrollTo = codeLenses.find( - c => c.command && c.command.command === Commands.ScrollToCell && c.range.start.line === startLine + (c) => c.command && c.command.command === Commands.ScrollToCell && c.range.start.line === startLine ); assert.equal(scrollTo, undefined, 'Goto cell code lens should not be found'); } @@ -190,7 +190,7 @@ suite('DataScience gotocell tests', () => { // There should be one with the ScrollTo command const scrollTo = codeLenses.find( - c => c.command && c.command.command === Commands.ScrollToCell && c.range.start.line === startLine + (c) => c.command && c.command.command === Commands.ScrollToCell && c.range.start.line === startLine ); assert.ok(scrollTo, 'Goto cell code lens not found'); @@ -227,7 +227,7 @@ suite('DataScience gotocell tests', () => { // Verify we don't have a goto const codeLenses = getCodeLenses(); - const scrollTo = codeLenses.find(c => c.command && c.command.command === Commands.ScrollToCell); + const scrollTo = codeLenses.find((c) => c.command && c.command.command === Commands.ScrollToCell); assert.equal(scrollTo, undefined, 'Goto cell code lens should not be found'); // Execute the first cell diff --git a/src/test/datascience/editor-integration/helpers.ts b/src/test/datascience/editor-integration/helpers.ts index f71cd3c8a1bf..1d21c2dc49b4 100644 --- a/src/test/datascience/editor-integration/helpers.ts +++ b/src/test/datascience/editor-integration/helpers.ts @@ -1,72 +1,72 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import * as TypeMoq from 'typemoq'; -import { Range, TextDocument, TextLine, Uri } from 'vscode'; - -// tslint:disable:max-func-body-length no-trailing-whitespace no-multiline-string -// Disable whitespace / multiline as we use that to pass in our fake file strings - -// Helper function to create a document and get line count and lines -export function createDocument( - inputText: string, - fileName: string, - fileVersion: number, - times: TypeMoq.Times, - implementGetText?: boolean -): TypeMoq.IMock { - const document = TypeMoq.Mock.ofType(); - - // Split our string on newline chars - const inputLines = inputText.split(/\r?\n/); - - document.setup(d => d.languageId).returns(() => 'python'); - - // First set the metadata - document - .setup(d => d.fileName) - .returns(() => Uri.file(fileName).fsPath) - .verifiable(times); - document - .setup(d => d.version) - .returns(() => fileVersion) - .verifiable(times); - - // Next add the lines in - document.setup(d => d.lineCount).returns(() => inputLines.length); - - const textLines = inputLines.map((line, index) => { - const textLine = TypeMoq.Mock.ofType(); - const testRange = new Range(index, 0, index, line.length); - textLine.setup(l => l.text).returns(() => line); - textLine.setup(l => l.range).returns(() => testRange); - textLine.setup(l => l.isEmptyOrWhitespace).returns(() => line.trim().length === 0); - return textLine; - }); - document.setup(d => d.lineAt(TypeMoq.It.isAnyNumber())).returns((index: number) => textLines[index].object); - - // Get text is a bit trickier - if (implementGetText) { - document.setup(d => d.getText()).returns(() => inputText); - document - .setup(d => d.getText(TypeMoq.It.isAny())) - .returns((r: Range) => { - let results = ''; - if (r) { - for (let line = r.start.line; line <= r.end.line && line < inputLines.length; line += 1) { - const startIndex = line === r.start.line ? r.start.character : 0; - const endIndex = line === r.end.line ? r.end.character : inputLines[line].length - 1; - results += inputLines[line].slice(startIndex, endIndex + 1); - if (line !== r.end.line) { - results += '\n'; - } - } - } else { - results = inputText; - } - return results; - }); - } - - return document; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import * as TypeMoq from 'typemoq'; +import { Range, TextDocument, TextLine, Uri } from 'vscode'; + +// tslint:disable:max-func-body-length no-trailing-whitespace no-multiline-string +// Disable whitespace / multiline as we use that to pass in our fake file strings + +// Helper function to create a document and get line count and lines +export function createDocument( + inputText: string, + fileName: string, + fileVersion: number, + times: TypeMoq.Times, + implementGetText?: boolean +): TypeMoq.IMock { + const document = TypeMoq.Mock.ofType(); + + // Split our string on newline chars + const inputLines = inputText.split(/\r?\n/); + + document.setup((d) => d.languageId).returns(() => 'python'); + + // First set the metadata + document + .setup((d) => d.fileName) + .returns(() => Uri.file(fileName).fsPath) + .verifiable(times); + document + .setup((d) => d.version) + .returns(() => fileVersion) + .verifiable(times); + + // Next add the lines in + document.setup((d) => d.lineCount).returns(() => inputLines.length); + + const textLines = inputLines.map((line, index) => { + const textLine = TypeMoq.Mock.ofType(); + const testRange = new Range(index, 0, index, line.length); + textLine.setup((l) => l.text).returns(() => line); + textLine.setup((l) => l.range).returns(() => testRange); + textLine.setup((l) => l.isEmptyOrWhitespace).returns(() => line.trim().length === 0); + return textLine; + }); + document.setup((d) => d.lineAt(TypeMoq.It.isAnyNumber())).returns((index: number) => textLines[index].object); + + // Get text is a bit trickier + if (implementGetText) { + document.setup((d) => d.getText()).returns(() => inputText); + document + .setup((d) => d.getText(TypeMoq.It.isAny())) + .returns((r: Range) => { + let results = ''; + if (r) { + for (let line = r.start.line; line <= r.end.line && line < inputLines.length; line += 1) { + const startIndex = line === r.start.line ? r.start.character : 0; + const endIndex = line === r.end.line ? r.end.character : inputLines[line].length - 1; + results += inputLines[line].slice(startIndex, endIndex + 1); + if (line !== r.end.line) { + results += '\n'; + } + } + } else { + results = inputText; + } + return results; + }); + } + + return document; +} diff --git a/src/test/datascience/errorHandler.functional.test.tsx b/src/test/datascience/errorHandler.functional.test.tsx index 760a5cf17dfb..3e6144aa5bfe 100644 --- a/src/test/datascience/errorHandler.functional.test.tsx +++ b/src/test/datascience/errorHandler.functional.test.tsx @@ -37,7 +37,7 @@ suite('DataScience Error Handler Functional Tests', () => { function modifyContainer(): DataScienceIocContainer { const jupyterExecution = TypeMoq.Mock.ofType(); - jupyterExecution.setup(jup => jup.getUsableJupyterPython()).returns(() => Promise.resolve(undefined)); + jupyterExecution.setup((jup) => jup.getUsableJupyterPython()).returns(() => Promise.resolve(undefined)); ioc.serviceManager.rebindInstance(IJupyterExecution, jupyterExecution.object); ioc.createWebView(() => mountConnectedMainPanel('interactive'), vsls.Role.None); @@ -64,7 +64,7 @@ suite('DataScience Error Handler Functional Tests', () => { } ]; channels - .setup(ch => ch.getInstallationChannels()) + .setup((ch) => ch.getInstallationChannels()) .returns(() => Promise.resolve(installers)) .verifiable(TypeMoq.Times.once()); diff --git a/src/test/datascience/errorHandler.unit.test.ts b/src/test/datascience/errorHandler.unit.test.ts index e67697a7b2fd..b2cfe6911490 100644 --- a/src/test/datascience/errorHandler.unit.test.ts +++ b/src/test/datascience/errorHandler.unit.test.ts @@ -34,7 +34,7 @@ suite('DataScience Error Handler Unit Tests', () => { test('Default error', async () => { applicationShell - .setup(app => app.showErrorMessage(typemoq.It.isAny())) + .setup((app) => app.showErrorMessage(typemoq.It.isAny())) .returns(() => Promise.resolve(message)) .verifiable(typemoq.Times.once()); @@ -46,7 +46,7 @@ suite('DataScience Error Handler Unit Tests', () => { test('Jupyter Self Certificates Error', async () => { applicationShell - .setup(app => app.showErrorMessage(typemoq.It.isAny())) + .setup((app) => app.showErrorMessage(typemoq.It.isAny())) .returns(() => Promise.resolve(message)) .verifiable(typemoq.Times.never()); @@ -58,7 +58,7 @@ suite('DataScience Error Handler Unit Tests', () => { test('Jupyter Install Error', async () => { applicationShell - .setup(app => + .setup((app) => app.showInformationMessage( typemoq.It.isAny(), typemoq.It.isValue(localize.DataScience.jupyterInstall()), @@ -87,7 +87,7 @@ suite('DataScience Error Handler Unit Tests', () => { ]; channels - .setup(ch => ch.getInstallationChannels()) + .setup((ch) => ch.getInstallationChannels()) .returns(() => Promise.resolve(installers)) .verifiable(typemoq.Times.once()); @@ -100,7 +100,7 @@ suite('DataScience Error Handler Unit Tests', () => { test('ZMQ Install Error', async () => { applicationShell - .setup(app => + .setup((app) => app.showErrorMessage(typemoq.It.isAny(), typemoq.It.isValue(localize.DataScience.selectNewServer())) ) .returns(() => Promise.resolve(localize.DataScience.selectNewServer())) diff --git a/src/test/datascience/execution.unit.test.ts b/src/test/datascience/execution.unit.test.ts index 861d8b2efdff..1f941679d28b 100644 --- a/src/test/datascience/execution.unit.test.ts +++ b/src/test/datascience/execution.unit.test.ts @@ -1,1224 +1,1218 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { assert } from 'chai'; -import * as fs from 'fs-extra'; -import * as os from 'os'; -import * as path from 'path'; -import { Observable } from 'rxjs/Observable'; -import { SemVer } from 'semver'; -import { anyString, anything, instance, match, mock, reset, verify, when } from 'ts-mockito'; -import { Matcher } from 'ts-mockito/lib/matcher/type/Matcher'; -import * as TypeMoq from 'typemoq'; -import * as uuid from 'uuid/v4'; -import { CancellationTokenSource, ConfigurationChangeEvent, Disposable, EventEmitter } from 'vscode'; -import { ApplicationShell } from '../../client/common/application/applicationShell'; -import { IApplicationShell, IWorkspaceService } from '../../client/common/application/types'; -import { WorkspaceService } from '../../client/common/application/workspace'; -import { PythonSettings } from '../../client/common/configSettings'; -import { ConfigurationService } from '../../client/common/configuration/service'; -import { PYTHON_LANGUAGE } from '../../client/common/constants'; -import { PersistentState, PersistentStateFactory } from '../../client/common/persistentState'; -import { FileSystem } from '../../client/common/platform/fileSystem'; -import { IFileSystem } from '../../client/common/platform/types'; -import { ProcessServiceFactory } from '../../client/common/process/processFactory'; -import { PythonExecutionFactory } from '../../client/common/process/pythonExecutionFactory'; -import { - ExecutionResult, - IProcessService, - IProcessServiceFactory, - IPythonExecutionFactory, - IPythonExecutionService, - ObservableExecutionResult, - Output -} from '../../client/common/process/types'; -import { IAsyncDisposableRegistry, IConfigurationService, IDisposableRegistry } from '../../client/common/types'; -import { createDeferred } from '../../client/common/utils/async'; -import { Architecture } from '../../client/common/utils/platform'; -import { EXTENSION_ROOT_DIR } from '../../client/constants'; -import { JupyterCommandFactory } from '../../client/datascience/jupyter/interpreter/jupyterCommand'; -import { JupyterCommandFinder } from '../../client/datascience/jupyter/interpreter/jupyterCommandFinder'; -import { JupyterCommandFinderInterpreterExecutionService } from '../../client/datascience/jupyter/interpreter/jupyterCommandInterpreterExecutionService'; -import { JupyterExecutionFactory } from '../../client/datascience/jupyter/jupyterExecutionFactory'; -import { KernelSelector } from '../../client/datascience/jupyter/kernels/kernelSelector'; -import { NotebookStarter } from '../../client/datascience/jupyter/notebookStarter'; -import { LiveShareApi } from '../../client/datascience/liveshare/liveshare'; -import { - IJupyterKernelSpec, - IJupyterSubCommandExecutionService, - INotebookServer -} from '../../client/datascience/types'; -import { EnvironmentActivationService } from '../../client/interpreter/activation/service'; -import { IEnvironmentActivationService } from '../../client/interpreter/activation/types'; -import { IInterpreterService, InterpreterType, PythonInterpreter } from '../../client/interpreter/contracts'; -import { InterpreterService } from '../../client/interpreter/interpreterService'; -import { KnownSearchPathsForInterpreters } from '../../client/interpreter/locators/services/KnownPathsService'; -import { ServiceContainer } from '../../client/ioc/container'; -import { getOSType, OSType } from '../common'; -import { noop, sleep } from '../core'; -import { MockOutputChannel } from '../mockClasses'; -import { MockAutoSelectionService } from '../mocks/autoSelector'; -import { MockJupyterServer } from './mockJupyterServer'; - -// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length -class DisposableRegistry implements IDisposableRegistry, IAsyncDisposableRegistry { - private disposables: Disposable[] = []; - - public push = (disposable: Disposable): void => { - this.disposables.push(disposable); - }; - - public dispose = async (): Promise => { - for (const disposable of this.disposables) { - if (!disposable) { - continue; - } - const val = disposable.dispose(); - if (val instanceof Promise) { - const promise = val as Promise; - await promise; - } - } - this.disposables = []; - }; -} - -suite('Jupyter Execution', async () => { - const interpreterService = mock(InterpreterService); - const jupyterOutputChannel = new MockOutputChannel(''); - const executionFactory = mock(PythonExecutionFactory); - const liveShare = mock(LiveShareApi); - const configService = mock(ConfigurationService); - const application = mock(ApplicationShell); - const processServiceFactory = mock(ProcessServiceFactory); - const knownSearchPaths = mock(KnownSearchPathsForInterpreters); - const fileSystem = mock(FileSystem); - const activationHelper = mock(EnvironmentActivationService); - const serviceContainer = mock(ServiceContainer); - const workspaceService = mock(WorkspaceService); - const disposableRegistry = new DisposableRegistry(); - const dummyEvent = new EventEmitter(); - const configChangeEvent = new EventEmitter(); - const pythonSettings = new PythonSettings(undefined, new MockAutoSelectionService()); - const jupyterOnPath = getOSType() === OSType.Windows ? '/foo/bar/jupyter.exe' : '/foo/bar/jupyter'; - let ipykernelInstallCount = 0; - let kernelSelector: KernelSelector; - let notebookStarter: NotebookStarter; - const workingPython: PythonInterpreter = { - path: '/foo/bar/python.exe', - version: new SemVer('3.6.6-final'), - sysVersion: '1.0.0.0', - sysPrefix: 'Python', - type: InterpreterType.Unknown, - architecture: Architecture.x64 - }; - - const missingKernelPython: PythonInterpreter = { - path: '/foo/baz/python.exe', - version: new SemVer('3.1.1-final'), - sysVersion: '1.0.0.0', - sysPrefix: 'Python', - type: InterpreterType.Unknown, - architecture: Architecture.x64 - }; - - const missingNotebookPython: PythonInterpreter = { - path: '/bar/baz/python.exe', - version: new SemVer('2.1.1-final'), - sysVersion: '1.0.0.0', - sysPrefix: 'Python', - type: InterpreterType.Unknown, - architecture: Architecture.x64 - }; - - const missingNotebookPython2: PythonInterpreter = { - path: '/two/baz/python.exe', - version: new SemVer('2.1.1'), - sysVersion: '1.0.0.0', - sysPrefix: 'Python', - type: InterpreterType.Unknown, - architecture: Architecture.x64 - }; - - let workingKernelSpec: string; - - suiteSetup(() => { - noop(); - }); - suiteTeardown(() => { - noop(); - }); - - setup(() => { - workingKernelSpec = createTempSpec(workingPython.path); - ipykernelInstallCount = 0; - // tslint:disable-next-line:no-invalid-this - }); - - teardown(() => { - reset(fileSystem); - return cleanupDisposables(); - }); - - function cleanupDisposables(): Promise { - return disposableRegistry.dispose(); - } - - // tslint:disable-next-line: max-classes-per-file - class FunctionMatcher extends Matcher { - private func: (obj: any) => boolean; - constructor(func: (obj: any) => boolean) { - super(); - this.func = func; - } - public match(value: Object): boolean { - return this.func(value); - } - public toString(): string { - return 'FunctionMatcher'; - } - } - - function createTempSpec(pythonPath: string): string { - const tempDir = os.tmpdir(); - const subDir = uuid(); - const filePath = path.join(tempDir, subDir, 'kernel.json'); - fs.ensureDirSync(path.dirname(filePath)); - fs.writeJSONSync(filePath, { - display_name: 'Python 3', - language: 'python', - argv: [pythonPath, '-m', 'ipykernel_launcher', '-f', '{connection_file}'] - }); - return filePath; - } - - function argThat(func: (obj: any) => boolean): any { - return new FunctionMatcher(func); - } - - function createTypeMoq(tag: string): TypeMoq.IMock { - // Use typemoqs for those things that are resolved as promises. mockito doesn't allow nesting of mocks. ES6 Proxy class - // is the problem. We still need to make it thenable though. See this issue: https://github.com/florinn/typemoq/issues/67 - const result: TypeMoq.IMock = TypeMoq.Mock.ofType(); - (result as any).tag = tag; - result.setup((x: any) => x.then).returns(() => undefined); - return result; - } - - function argsMatch(matchers: (string | RegExp)[], args: string[]): boolean { - if (matchers.length === args.length) { - return args.every((s, i) => { - const r = matchers[i] as RegExp; - return r && r.test ? r.test(s) : s === matchers[i]; - }); - } - return false; - } - - function setupPythonService( - service: TypeMoq.IMock, - module: string | undefined, - args: (string | RegExp)[], - result: Promise> - ) { - if (module) { - service - .setup(x => - x.execModule( - TypeMoq.It.isValue(module), - TypeMoq.It.is(a => argsMatch(args, a)), - TypeMoq.It.isAny() - ) - ) - .returns(() => result); - const withModuleArgs = ['-m', module, ...args]; - service - .setup(x => - x.exec( - TypeMoq.It.is(a => argsMatch(withModuleArgs, a)), - TypeMoq.It.isAny() - ) - ) - .returns(() => result); - } else { - service - .setup(x => - x.exec( - TypeMoq.It.is(a => argsMatch(args, a)), - TypeMoq.It.isAny() - ) - ) - .returns(() => result); - } - } - - function setupPythonServiceWithFunc( - service: TypeMoq.IMock, - module: string, - args: (string | RegExp)[], - result: () => Promise> - ) { - service - .setup(x => - x.execModule( - TypeMoq.It.isValue(module), - TypeMoq.It.is(a => argsMatch(args, a)), - TypeMoq.It.isAny() - ) - ) - .returns(result); - const withModuleArgs = ['-m', module, ...args]; - service - .setup(x => - x.exec( - TypeMoq.It.is(a => argsMatch(withModuleArgs, a)), - TypeMoq.It.isAny() - ) - ) - .returns(result); - service - .setup(x => - x.execModule( - TypeMoq.It.isValue(module), - TypeMoq.It.is(a => argsMatch(args, a)), - TypeMoq.It.isAny() - ) - ) - .returns(result); - } - - function setupPythonServiceExecObservable( - service: TypeMoq.IMock, - module: string, - args: (string | RegExp)[], - stderr: string[], - stdout: string[] - ) { - const result: ObservableExecutionResult = { - proc: undefined, - out: new Observable>(subscriber => { - stderr.forEach(s => subscriber.next({ source: 'stderr', out: s })); - stdout.forEach(s => subscriber.next({ source: 'stderr', out: s })); - }), - dispose: () => { - noop(); - } - }; - - service - .setup(x => - x.execModuleObservable( - TypeMoq.It.isValue(module), - TypeMoq.It.is(a => argsMatch(args, a)), - TypeMoq.It.isAny() - ) - ) - .returns(() => result); - const withModuleArgs = ['-m', module, ...args]; - service - .setup(x => - x.execObservable( - TypeMoq.It.is(a => argsMatch(withModuleArgs, a)), - TypeMoq.It.isAny() - ) - ) - .returns(() => result); - } - - function setupProcessServiceExec( - service: TypeMoq.IMock, - file: string, - args: (string | RegExp)[], - result: Promise> - ) { - service - .setup(x => - x.exec( - TypeMoq.It.isValue(file), - TypeMoq.It.is(a => argsMatch(args, a)), - TypeMoq.It.isAny() - ) - ) - .returns(() => result); - } - - function setupProcessServiceExecWithFunc( - service: TypeMoq.IMock, - file: string, - args: (string | RegExp)[], - result: () => Promise> - ) { - service - .setup(x => - x.exec( - TypeMoq.It.isValue(file), - TypeMoq.It.is(a => argsMatch(args, a)), - TypeMoq.It.isAny() - ) - ) - .returns(result); - } - - function setupProcessServiceExecObservable( - service: TypeMoq.IMock, - file: string, - args: (string | RegExp)[], - stderr: string[], - stdout: string[] - ) { - const result: ObservableExecutionResult = { - proc: undefined, - out: new Observable>(subscriber => { - stderr.forEach(s => subscriber.next({ source: 'stderr', out: s })); - stdout.forEach(s => subscriber.next({ source: 'stderr', out: s })); - }), - dispose: () => { - noop(); - } - }; - - service - .setup(x => - x.execObservable( - TypeMoq.It.isValue(file), - TypeMoq.It.is(a => argsMatch(args, a)), - TypeMoq.It.isAny() - ) - ) - .returns(() => result); - } - - function createKernelSpecs(specs: { name: string; resourceDir: string }[]): Record { - const models: Record = {}; - specs.forEach(spec => { - models[spec.name] = { - resource_dir: spec.resourceDir, - spec: { - name: spec.name, - display_name: spec.name, - language: 'python' - } - }; - }); - return models; - } - function setupWorkingPythonService( - service: TypeMoq.IMock, - notebookStdErr?: string[], - runInDocker?: boolean - ) { - setupPythonService(service, 'ipykernel', ['--version'], Promise.resolve({ stdout: '1.1.1.1' })); - setupPythonService(service, 'jupyter', ['nbconvert', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); - setupPythonService(service, 'jupyter', ['notebook', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); - setupPythonService(service, 'jupyter', ['kernelspec', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); - service.setup(x => x.getInterpreterInformation()).returns(() => Promise.resolve(workingPython)); - - // Don't mind the goofy path here. It's supposed to not find the item. It's just testing the internal regex works - setupPythonServiceWithFunc(service, 'jupyter', ['kernelspec', 'list', '--json'], () => { - // Return different results after we install our kernel - if (ipykernelInstallCount > 0) { - const kernelSpecs = createKernelSpecs([ - { name: 'working', resourceDir: path.dirname(workingKernelSpec) }, - { - name: '0e8519db-0895-416c-96df-fa80131ecea0', - resourceDir: - 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' - } - ]); - return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); - } else { - const kernelSpecs = createKernelSpecs([ - { - name: '0e8519db-0895-416c-96df-fa80131ecea0', - resourceDir: - 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' - } - ]); - return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); - } - }); - const kernelSpecs2 = createKernelSpecs([ - { name: 'working', resourceDir: path.dirname(workingKernelSpec) }, - { - name: '0e8519db-0895-416c-96df-fa80131ecea0', - resourceDir: - 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' - } - ]); - setupPythonService( - service, - 'jupyter', - ['kernelspec', 'list', '--json'], - Promise.resolve({ stdout: JSON.stringify(kernelSpecs2) }) - ); - setupPythonServiceWithFunc( - service, - 'ipykernel', - ['install', '--user', '--name', /\w+-\w+-\w+-\w+-\w+/, '--display-name', `'Python Interactive'`], - () => { - ipykernelInstallCount += 1; - const kernelSpecs = createKernelSpecs([ - { name: 'somename', resourceDir: path.dirname(workingKernelSpec) } - ]); - return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); - } - ); - const getServerInfoPath = path.join( - EXTENSION_ROOT_DIR, - 'pythonFiles', - 'vscode_datascience_helpers', - 'getServerInfo.py' - ); - setupPythonService( - service, - undefined, - [getServerInfoPath], - Promise.resolve({ stdout: 'failure to get server infos' }) - ); - setupPythonServiceExecObservable(service, 'jupyter', ['kernelspec', 'list', '--json'], [], []); - const dockerArgs = runInDocker ? ['--ip', '127.0.0.1'] : []; - setupPythonServiceExecObservable( - service, - 'jupyter', - [ - 'notebook', - '--no-browser', - /--notebook-dir=.*/, - /--config=.*/, - '--NotebookApp.iopub_data_rate_limit=10000000000.0', - ...dockerArgs - ], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] - ); - } - - function setupMissingKernelPythonService( - service: TypeMoq.IMock, - notebookStdErr?: string[] - ) { - setupPythonService(service, 'jupyter', ['notebook', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); - setupPythonService(service, 'jupyter', ['kernelspec', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); - service.setup(x => x.getInterpreterInformation()).returns(() => Promise.resolve(missingKernelPython)); - const kernelSpecs = createKernelSpecs([{ name: 'working', resourceDir: path.dirname(workingKernelSpec) }]); - setupPythonService( - service, - 'jupyter', - ['kernelspec', 'list', '--json'], - Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }) - ); - const getServerInfoPath = path.join( - EXTENSION_ROOT_DIR, - 'pythonFiles', - 'vscode_datascience_helpers', - 'getServerInfo.py' - ); - setupPythonService( - service, - undefined, - [getServerInfoPath], - Promise.resolve({ stdout: 'failure to get server infos' }) - ); - setupPythonServiceExecObservable(service, 'jupyter', ['kernelspec', 'list', '--json'], [], []); - setupPythonServiceExecObservable( - service, - 'jupyter', - [ - 'notebook', - '--no-browser', - /--notebook-dir=.*/, - /--config=.*/, - '--NotebookApp.iopub_data_rate_limit=10000000000.0' - ], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] - ); - } - - function setupMissingNotebookPythonService(service: TypeMoq.IMock) { - service - .setup(x => x.execModule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(_v => { - return Promise.reject('cant exec'); - }); - service.setup(x => x.getInterpreterInformation()).returns(() => Promise.resolve(missingNotebookPython)); - } - - function setupWorkingProcessService(service: TypeMoq.IMock, notebookStdErr?: string[]) { - // Don't mind the goofy path here. It's supposed to not find the item. It's just testing the internal regex works - setupProcessServiceExecWithFunc( - service, - workingPython.path, - ['-m', 'jupyter', 'kernelspec', 'list', '--json'], - () => { - // Return different results after we install our kernel - if (ipykernelInstallCount > 0) { - const kernelSpecs = createKernelSpecs([ - { name: 'working', resourceDir: path.dirname(workingKernelSpec) }, - { - name: '0e8519db-0895-416c-96df-fa80131ecea0', - resourceDir: - 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' - } - ]); - return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); - } else { - const kernelSpecs = createKernelSpecs([ - { - name: '0e8519db-0895-416c-96df-fa80131ecea0', - resourceDir: - 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' - } - ]); - return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); - } - } - ); - const kernelSpecs2 = createKernelSpecs([ - { name: 'working', resourceDir: path.dirname(workingKernelSpec) }, - { - name: '0e8519db-0895-416c-96df-fa80131ecea0', - resourceDir: - 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' - } - ]); - setupProcessServiceExec( - service, - workingPython.path, - ['-m', 'jupyter', 'kernelspec', 'list', '--json'], - Promise.resolve({ stdout: JSON.stringify(kernelSpecs2) }) - ); - setupProcessServiceExecWithFunc( - service, - workingPython.path, - [ - '-m', - 'ipykernel', - 'install', - '--user', - '--name', - /\w+-\w+-\w+-\w+-\w+/, - '--display-name', - `'Python Interactive'` - ], - () => { - ipykernelInstallCount += 1; - const kernelSpecs = createKernelSpecs([ - { name: 'somename', resourceDir: path.dirname(workingKernelSpec) } - ]); - return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); - } - ); - const getServerInfoPath = path.join( - EXTENSION_ROOT_DIR, - 'pythonFiles', - 'vscode_datascience_helpers', - 'getServerInfo.py' - ); - setupProcessServiceExec( - service, - workingPython.path, - [getServerInfoPath], - Promise.resolve({ stdout: 'failure to get server infos' }) - ); - setupProcessServiceExecObservable( - service, - workingPython.path, - ['-m', 'jupyter', 'kernelspec', 'list', '--json'], - [], - [] - ); - setupProcessServiceExecObservable( - service, - workingPython.path, - [ - '-m', - 'jupyter', - 'notebook', - '--no-browser', - /--notebook-dir=.*/, - /--config=.*/, - '--NotebookApp.iopub_data_rate_limit=10000000000.0' - ], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] - ); - } - - function setupMissingKernelProcessService(service: TypeMoq.IMock, notebookStdErr?: string[]) { - const kernelSpecs = createKernelSpecs([{ name: 'working', resourceDir: path.dirname(workingKernelSpec) }]); - setupProcessServiceExec( - service, - missingKernelPython.path, - ['-m', 'jupyter', 'kernelspec', 'list', '--json'], - Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }) - ); - const getServerInfoPath = path.join( - EXTENSION_ROOT_DIR, - 'pythonFiles', - 'vscode_datascience_helpers', - 'getServerInfo.py' - ); - setupProcessServiceExec( - service, - missingKernelPython.path, - [getServerInfoPath], - Promise.resolve({ stdout: 'failure to get server infos' }) - ); - setupProcessServiceExecObservable( - service, - missingKernelPython.path, - ['-m', 'jupyter', 'kernelspec', 'list', '--json'], - [], - [] - ); - setupProcessServiceExecObservable( - service, - missingKernelPython.path, - [ - '-m', - 'jupyter', - 'notebook', - '--no-browser', - /--notebook-dir=.*/, - /--config=.*/, - '--NotebookApp.iopub_data_rate_limit=10000000000.0' - ], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] - ); - } - - function setupPathProcessService( - jupyterPath: string, - service: TypeMoq.IMock, - notebookStdErr?: string[] - ) { - const kernelSpecs = createKernelSpecs([{ name: 'working', resourceDir: path.dirname(workingKernelSpec) }]); - setupProcessServiceExec( - service, - jupyterPath, - ['kernelspec', 'list', '--json'], - Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }) - ); - setupProcessServiceExecObservable(service, jupyterPath, ['kernelspec', 'list', '--json'], [], []); - setupProcessServiceExec(service, jupyterPath, ['--version'], Promise.resolve({ stdout: '1.1.1.1' })); - setupProcessServiceExec( - service, - jupyterPath, - ['notebook', '--version'], - Promise.resolve({ stdout: '1.1.1.1' }) - ); - setupProcessServiceExec( - service, - jupyterPath, - ['kernelspec', '--version'], - Promise.resolve({ stdout: '1.1.1.1' }) - ); - setupProcessServiceExecObservable( - service, - jupyterPath, - [ - 'notebook', - '--no-browser', - /--notebook-dir=.*/, - /--config=.*/, - '--NotebookApp.iopub_data_rate_limit=10000000000.0' - ], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] - ); - - // WE also check for existence with just the key jupyter - setupProcessServiceExec(service, 'jupyter', ['--version'], Promise.resolve({ stdout: '1.1.1.1' })); - setupProcessServiceExec(service, 'jupyter', ['notebook', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); - setupProcessServiceExec( - service, - 'jupyter', - ['kernelspec', '--version'], - Promise.resolve({ stdout: '1.1.1.1' }) - ); - } - - function createExecution( - activeInterpreter: PythonInterpreter, - notebookStdErr?: string[], - skipSearch?: boolean - ): JupyterExecutionFactory { - return createExecutionAndReturnProcessService(activeInterpreter, notebookStdErr, skipSearch) - .jupyterExecutionFactory; - } - function createExecutionAndReturnProcessService( - activeInterpreter: PythonInterpreter, - notebookStdErr?: string[], - skipSearch?: boolean, - runInDocker?: boolean - ): { - executionService: IPythonExecutionService; - jupyterExecutionFactory: JupyterExecutionFactory; - } { - // Setup defaults - when(interpreterService.onDidChangeInterpreter).thenReturn(dummyEvent.event); - when(interpreterService.getActiveInterpreter(anything())).thenResolve(activeInterpreter); - when(interpreterService.getInterpreters(anything())).thenResolve([ - workingPython, - missingKernelPython, - missingNotebookPython - ]); - when(interpreterService.getInterpreterDetails(match('/foo/bar/python.exe'))).thenResolve(workingPython); // Mockito is stupid. Matchers have to use literals. - when(interpreterService.getInterpreterDetails(match('/foo/baz/python.exe'))).thenResolve(missingKernelPython); - when(interpreterService.getInterpreterDetails(match('/bar/baz/python.exe'))).thenResolve(missingNotebookPython); - when(interpreterService.getInterpreterDetails(argThat(o => !o.includes || !o.includes('python')))).thenReject( - ('Unknown interpreter' as any) as Error - ); - if (runInDocker) { - when(fileSystem.readFile('/proc/self/cgroup')).thenResolve('hello docker world'); - } - // Create our working python and process service. - const workingService = createTypeMoq('working'); - setupWorkingPythonService(workingService, notebookStdErr, runInDocker); - const missingKernelService = createTypeMoq('missingKernel'); - setupMissingKernelPythonService(missingKernelService, notebookStdErr); - const missingNotebookService = createTypeMoq('missingNotebook'); - setupMissingNotebookPythonService(missingNotebookService); - const missingNotebookService2 = createTypeMoq('missingNotebook2'); - setupMissingNotebookPythonService(missingNotebookService2); - const processService = createTypeMoq('working process'); - setupWorkingProcessService(processService, notebookStdErr); - setupMissingKernelProcessService(processService, notebookStdErr); - setupPathProcessService(jupyterOnPath, processService, notebookStdErr); - when(executionFactory.create(argThat(o => o.pythonPath && o.pythonPath === workingPython.path))).thenResolve( - workingService.object - ); - when( - executionFactory.create(argThat(o => o.pythonPath && o.pythonPath === missingKernelPython.path)) - ).thenResolve(missingKernelService.object); - when( - executionFactory.create(argThat(o => o.pythonPath && o.pythonPath === missingNotebookPython.path)) - ).thenResolve(missingNotebookService.object); - when( - executionFactory.create(argThat(o => o.pythonPath && o.pythonPath === missingNotebookPython2.path)) - ).thenResolve(missingNotebookService2.object); - - when( - executionFactory.createDaemon(argThat(o => o.pythonPath && o.pythonPath === workingPython.path)) - ).thenResolve(workingService.object); - - when( - executionFactory.createDaemon(argThat(o => o.pythonPath && o.pythonPath === missingKernelPython.path)) - ).thenResolve(missingKernelService.object); - - when( - executionFactory.createDaemon(argThat(o => o.pythonPath && o.pythonPath === missingNotebookPython.path)) - ).thenResolve(missingNotebookService.object); - - when( - executionFactory.createDaemon(argThat(o => o.pythonPath && o.pythonPath === missingNotebookPython2.path)) - ).thenResolve(missingNotebookService2.object); - - let activeService = workingService; - if (activeInterpreter === missingKernelPython) { - activeService = missingKernelService; - } else if (activeInterpreter === missingNotebookPython) { - activeService = missingNotebookService; - } else if (activeInterpreter === missingNotebookPython2) { - activeService = missingNotebookService2; - } - when(executionFactory.create(argThat(o => !o || !o.pythonPath))).thenResolve(activeService.object); - when( - executionFactory.createActivatedEnvironment(argThat(o => !o || o.interpreter === activeInterpreter)) - ).thenResolve(activeService.object); - when( - executionFactory.createActivatedEnvironment(argThat(o => o && o.interpreter.path === workingPython.path)) - ).thenResolve(workingService.object); - when( - executionFactory.createActivatedEnvironment( - argThat(o => o && o.interpreter.path === missingKernelPython.path) - ) - ).thenResolve(missingKernelService.object); - when( - executionFactory.createActivatedEnvironment( - argThat(o => o && o.interpreter.path === missingNotebookPython.path) - ) - ).thenResolve(missingNotebookService.object); - when( - executionFactory.createActivatedEnvironment( - argThat(o => o && o.interpreter.path === missingNotebookPython2.path) - ) - ).thenResolve(missingNotebookService2.object); - when(processServiceFactory.create()).thenResolve(processService.object); - - when(liveShare.getApi()).thenResolve(null); - - // Service container needs logger, file system, and config service - when(serviceContainer.get(IConfigurationService)).thenReturn(instance(configService)); - when(serviceContainer.get(IFileSystem)).thenReturn(instance(fileSystem)); - when(serviceContainer.get(IWorkspaceService)).thenReturn(instance(workspaceService)); - when(serviceContainer.get(IApplicationShell)).thenReturn(instance(application)); - when(configService.getSettings(anything())).thenReturn(pythonSettings); - when(workspaceService.onDidChangeConfiguration).thenReturn(configChangeEvent.event); - when(application.withProgress(anything(), anything())).thenCall( - (_, cb: (_: any, token: any) => Promise) => { - return new Promise((resolve, reject) => { - cb({ report: noop }, new CancellationTokenSource().token) - .then(resolve) - .catch(reject); - }); - } - ); - - // Setup default settings - pythonSettings.datascience = { - allowImportFromNotebook: true, - jupyterLaunchTimeout: 10, - jupyterLaunchRetries: 3, - enabled: true, - jupyterServerURI: 'local', - // tslint:disable-next-line: no-invalid-template-strings - notebookFileRoot: '${fileDirname}', - changeDirOnImportExport: true, - useDefaultConfigForJupyter: true, - jupyterInterruptTimeout: 10000, - searchForJupyter: !skipSearch, - showCellInputCode: true, - collapseCellInputCodeByDefault: true, - allowInput: true, - maxOutputSize: 400, - errorBackgroundColor: '#FFFFFF', - sendSelectionToInteractiveWindow: false, - variableExplorerExclude: 'module;function;builtin_function_or_method', - codeRegularExpression: '^(#\\s*%%|#\\s*\\|#\\s*In\\[\\d*?\\]|#\\s*In\\[ \\])', - markdownRegularExpression: '^(#\\s*%%\\s*\\[markdown\\]|#\\s*\\)', - allowLiveShare: false, - enablePlotViewer: true, - runStartupCommands: '', - debugJustMyCode: true, - variableQueries: [], - jupyterCommandLineArguments: [] - }; - - // Service container also needs to generate jupyter servers. However we can't use a mock as that messes up returning - // this object from a promise - when(serviceContainer.get(INotebookServer)).thenReturn(new MockJupyterServer()); - - when(knownSearchPaths.getSearchPaths()).thenReturn(['/foo/bar']); - - // We also need a file system - const tempFile = { - dispose: () => { - return undefined; - }, - filePath: '/foo/bar/baz.py' - }; - when(fileSystem.createTemporaryFile(anything())).thenResolve(tempFile); - when(fileSystem.createDirectory(anything())).thenResolve(); - when(fileSystem.deleteDirectory(anything())).thenResolve(); - when(fileSystem.fileExists(workingKernelSpec)).thenResolve(true); - when(fileSystem.readFile(workingKernelSpec)).thenResolve( - '{"display_name":"Python 3","language":"python","argv":["/foo/bar/python.exe","-m","ipykernel_launcher","-f","{connection_file}"]}' - ); - - const commandFactory = new JupyterCommandFactory( - instance(executionFactory), - instance(activationHelper), - instance(processServiceFactory), - instance(interpreterService) - ); - const persistentSateFactory = mock(PersistentStateFactory); - const persistentState = mock(PersistentState); - when(persistentState.updateValue(anything())).thenResolve(); - when(persistentSateFactory.createGlobalPersistentState(anything())).thenReturn(instance(persistentState)); - when(persistentSateFactory.createGlobalPersistentState(anything(), anything())).thenReturn( - instance(persistentState) - ); - when(persistentSateFactory.createWorkspacePersistentState(anything())).thenReturn(instance(persistentState)); - when(persistentSateFactory.createWorkspacePersistentState(anything(), anything())).thenReturn( - instance(persistentState) - ); - const commandFinder = new JupyterCommandFinder( - instance(interpreterService), - instance(executionFactory), - instance(configService), - instance(knownSearchPaths), - disposableRegistry, - instance(fileSystem), - instance(processServiceFactory), - commandFactory, - instance(workspaceService), - instance(application), - instance(persistentSateFactory) - ); - when(serviceContainer.get(JupyterCommandFinder)).thenReturn(commandFinder); - when(serviceContainer.get(IInterpreterService)).thenReturn(instance(interpreterService)); - when(serviceContainer.get(IProcessServiceFactory)).thenReturn( - instance(processServiceFactory) - ); - when(serviceContainer.get(IEnvironmentActivationService)).thenReturn( - instance(activationHelper) - ); - when(serviceContainer.get(IPythonExecutionFactory)).thenReturn( - instance(executionFactory) - ); - kernelSelector = mock(KernelSelector); - const kernelSpec: IJupyterKernelSpec = { - argv: [], - display_name: 'hello', - language: PYTHON_LANGUAGE, - name: 'hello', - path: '' - }; - when( - kernelSelector.getKernelForLocalConnection(anything(), anything(), anything(), anything(), anything()) - ).thenResolve({ - kernelSpec - }); - const jupyterCmdExecutionService = new JupyterCommandFinderInterpreterExecutionService( - commandFinder, - instance(interpreterService), - instance(fileSystem), - instance(executionFactory) - ); - when(serviceContainer.get(IJupyterSubCommandExecutionService)).thenReturn( - jupyterCmdExecutionService - ); - notebookStarter = new NotebookStarter( - jupyterCmdExecutionService, - instance(fileSystem), - instance(serviceContainer), - instance(jupyterOutputChannel) - ); - when(serviceContainer.get(KernelSelector)).thenReturn(instance(kernelSelector)); - when(serviceContainer.get(NotebookStarter)).thenReturn(notebookStarter); - return { - executionService: activeService.object, - jupyterExecutionFactory: new JupyterExecutionFactory( - instance(liveShare), - instance(interpreterService), - disposableRegistry, - disposableRegistry, - instance(fileSystem), - instance(workspaceService), - instance(configService), - instance(kernelSelector), - notebookStarter, - instance(application), - instance(jupyterOutputChannel), - instance(serviceContainer) - ) - }; - } - - test('Working notebook and commands found', async () => { - const jupyterExecutionFactory = createExecution(workingPython); - - await assert.eventually.equal(jupyterExecutionFactory.isNotebookSupported(), true, 'Notebook not supported'); - await assert.eventually.equal(jupyterExecutionFactory.isImportSupported(), true, 'Import not supported'); - const usableInterpreter = await jupyterExecutionFactory.getUsableJupyterPython(); - assert.isOk(usableInterpreter, 'Usable interpreter not found'); - await assert.isFulfilled(jupyterExecutionFactory.connectToNotebookServer(), 'Should be able to start a server'); - }).timeout(10000); - - test('Includes correct args for running in docker', async () => { - const { jupyterExecutionFactory } = createExecutionAndReturnProcessService( - workingPython, - undefined, - undefined, - true - ); - - await assert.eventually.equal(jupyterExecutionFactory.isNotebookSupported(), true, 'Notebook not supported'); - await assert.eventually.equal(jupyterExecutionFactory.isImportSupported(), true, 'Import not supported'); - const usableInterpreter = await jupyterExecutionFactory.getUsableJupyterPython(); - assert.isOk(usableInterpreter, 'Usable interpreter not found'); - await assert.isFulfilled(jupyterExecutionFactory.connectToNotebookServer(), 'Should be able to start a server'); - }).timeout(10000); - - test('Failing notebook throws exception', async () => { - const execution = createExecution(missingNotebookPython); - when(interpreterService.getInterpreters(anything())).thenResolve([missingNotebookPython]); - await assert.isRejected(execution.connectToNotebookServer(), 'cant exec'); - }).timeout(10000); - - test('Failing others throws exception', async () => { - const execution = createExecution(missingNotebookPython); - when(interpreterService.getInterpreters(anything())).thenResolve([ - missingNotebookPython, - missingNotebookPython2 - ]); - await assert.isRejected(execution.connectToNotebookServer(), 'cant exec'); - }).timeout(10000); - - test('Other than active works', async () => { - const execution = createExecution(missingNotebookPython); - await assert.eventually.equal(execution.isNotebookSupported(), true, 'Notebook not supported'); - await assert.eventually.equal(execution.isImportSupported(), true, 'Import not supported'); - const usableInterpreter = await execution.getUsableJupyterPython(); - assert.isOk(usableInterpreter, 'Usable interpreter not found'); - if (usableInterpreter) { - assert.notEqual(usableInterpreter.path, missingNotebookPython.path); - } - }).timeout(10000); - - test('Missing kernel python still finds interpreter', async () => { - const execution = createExecution(missingKernelPython); - when(interpreterService.getActiveInterpreter(anything())).thenResolve(missingKernelPython); - await assert.eventually.equal(execution.isNotebookSupported(), true, 'Notebook not supported'); - const usableInterpreter = await execution.getUsableJupyterPython(); - assert.isOk(usableInterpreter, 'Usable interpreter not found'); - if (usableInterpreter) { - // Linter - assert.equal(usableInterpreter.path, missingKernelPython.path); - assert.equal( - usableInterpreter.version!.major, - missingKernelPython.version!.major, - 'Found interpreter should match on major' - ); - assert.equal( - usableInterpreter.version!.minor, - missingKernelPython.version!.minor, - 'Found interpreter should match on minor' - ); - } - }).timeout(10000); - - test('Other than active finds closest match', async () => { - const execution = createExecution(missingNotebookPython); - when(interpreterService.getActiveInterpreter(anything())).thenResolve(missingNotebookPython); - await assert.eventually.equal(execution.isNotebookSupported(), true, 'Notebook not supported'); - const usableInterpreter = await execution.getUsableJupyterPython(); - assert.isOk(usableInterpreter, 'Usable interpreter not found'); - if (usableInterpreter) { - // Linter - assert.notEqual(usableInterpreter.path, missingNotebookPython.path); - assert.notEqual( - usableInterpreter.version!.major, - missingNotebookPython.version!.major, - 'Found interpreter should not match on major' - ); - } - // Force config change and ask again - pythonSettings.datascience.searchForJupyter = false; - const evt = { - affectsConfiguration(_m: string): boolean { - return true; - } - }; - configChangeEvent.fire(evt); - // Wait for cache to get cleared. - await sleep(100); - await assert.eventually.equal( - execution.isNotebookSupported(), - false, - 'Notebook should not be supported after config change' - ); - }).timeout(10000); - - test('Display progress message', async () => { - const execution = createExecution(missingNotebookPython); - await assert.eventually.equal(execution.isNotebookSupported(), true, 'Notebook not supported'); - const usableInterpreter = await execution.getUsableJupyterPython(); - assert.isOk(usableInterpreter, 'Usable interpreter not found'); - if (usableInterpreter) { - // Linter - assert.notEqual(usableInterpreter.path, missingNotebookPython.path); - assert.notEqual( - usableInterpreter.version!.major, - missingNotebookPython.version!.major, - 'Found interpreter should not match on major' - ); - } - // Force config change and ask again - pythonSettings.datascience.searchForJupyter = false; - const evt = { - affectsConfiguration(_m: string): boolean { - return true; - } - }; - configChangeEvent.fire(evt); - // Wait for cache to get cleared. - await sleep(100); - await assert.eventually.equal( - execution.isNotebookSupported(), - false, - 'Notebook should not be supported after config change' - ); - verify(application.withProgress(anything(), anything())).atLeast(1); - }).timeout(10000); - - test('Progress message should not be displayed for more than 1s when interpreter search completes quickly', async () => { - const execution = createExecution(missingNotebookPython); - const progressCancellation = new CancellationTokenSource(); - reset(application); - when(application.withProgress(anything(), anything())).thenCall( - (_, cb: (_: any, token: any) => Promise) => { - return new Promise((resolve, reject) => { - cb({ report: noop }, progressCancellation.token) - .then(resolve) - .catch(reject); - }); - } - ); - - // Now interpreters = fast discovery (less time for display of progress). - when(interpreterService.getInterpreters(anything())).thenReturn(Promise.resolve([])); - - // The call to isNotebookSupported should not timeout in 1 seconds. - const isNotebookSupported = execution.isNotebookSupported(); - await assert.eventually.notEqual( - Promise.race([isNotebookSupported, sleep(1000).then(() => 'timeout')]), - 'timeout' - ); - verify(application.withProgress(anything(), anything())).atLeast(1); - }).timeout(10_000); - - test('Cancel progress message if interpreter search takes too long', async () => { - const execution = createExecution(missingNotebookPython); - const progressCancellation = new CancellationTokenSource(); - reset(application); - when(application.withProgress(anything(), anything())).thenCall( - (_, cb: (_: any, token: any) => Promise) => { - return new Promise((resolve, reject) => { - cb({ report: noop }, progressCancellation.token) - .then(resolve) - .catch(reject); - }); - } - ); - - const slowInterpreterDiscovery = createDeferred(); - when(interpreterService.getInterpreters(anything())).thenReturn(slowInterpreterDiscovery.promise); - - // The call to interpreterService.getInterpreters shoud not complete, it is very slow. - const isNotebookSupported = execution.isNotebookSupported(); - await assert.eventually.equal( - Promise.race([isNotebookSupported, sleep(5000).then(() => 'timeout')]), - 'timeout' - ); - - // Once we cancel the progress message, the promise should resolve almost immediately. - progressCancellation.cancel(); - await assert.eventually.notEqual( - Promise.race([isNotebookSupported, sleep(500).then(() => 'timeout')]), - 'timeout' - ); - verify(application.withProgress(anything(), anything())).atLeast(1); - }).timeout(20_000); - - test('Jupyter found on the path', async () => { - // Make sure we can find jupyter on the path if we - // can't find it in a python module. - const execution = createExecution(missingNotebookPython); - when(interpreterService.getInterpreters(anything())).thenResolve([missingNotebookPython]); - when(fileSystem.getFiles(anyString())).thenResolve([jupyterOnPath]); - await assert.isFulfilled(execution.connectToNotebookServer(), 'Should be able to start a server'); - }).timeout(10000); - - test('Jupyter found on the path skipped', async () => { - // Make sure we can find jupyter on the path if we - // can't find it in a python module. - const execution = createExecution(missingNotebookPython, undefined, true); - when(interpreterService.getInterpreters(anything())).thenResolve([missingNotebookPython]); - when(fileSystem.getFiles(anyString())).thenResolve([jupyterOnPath]); - await assert.isRejected(execution.connectToNotebookServer(), 'cant exec'); - }).timeout(10000); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { assert } from 'chai'; +import * as fs from 'fs-extra'; +import * as os from 'os'; +import * as path from 'path'; +import { Observable } from 'rxjs/Observable'; +import { SemVer } from 'semver'; +import { anyString, anything, instance, match, mock, reset, verify, when } from 'ts-mockito'; +import { Matcher } from 'ts-mockito/lib/matcher/type/Matcher'; +import * as TypeMoq from 'typemoq'; +import * as uuid from 'uuid/v4'; +import { CancellationTokenSource, ConfigurationChangeEvent, Disposable, EventEmitter } from 'vscode'; +import { ApplicationShell } from '../../client/common/application/applicationShell'; +import { IApplicationShell, IWorkspaceService } from '../../client/common/application/types'; +import { WorkspaceService } from '../../client/common/application/workspace'; +import { PythonSettings } from '../../client/common/configSettings'; +import { ConfigurationService } from '../../client/common/configuration/service'; +import { PYTHON_LANGUAGE } from '../../client/common/constants'; +import { PersistentState, PersistentStateFactory } from '../../client/common/persistentState'; +import { FileSystem } from '../../client/common/platform/fileSystem'; +import { IFileSystem } from '../../client/common/platform/types'; +import { ProcessServiceFactory } from '../../client/common/process/processFactory'; +import { PythonExecutionFactory } from '../../client/common/process/pythonExecutionFactory'; +import { + ExecutionResult, + IProcessService, + IProcessServiceFactory, + IPythonExecutionFactory, + IPythonExecutionService, + ObservableExecutionResult, + Output +} from '../../client/common/process/types'; +import { IAsyncDisposableRegistry, IConfigurationService, IDisposableRegistry } from '../../client/common/types'; +import { createDeferred } from '../../client/common/utils/async'; +import { Architecture } from '../../client/common/utils/platform'; +import { EXTENSION_ROOT_DIR } from '../../client/constants'; +import { JupyterCommandFactory } from '../../client/datascience/jupyter/interpreter/jupyterCommand'; +import { JupyterCommandFinder } from '../../client/datascience/jupyter/interpreter/jupyterCommandFinder'; +import { JupyterCommandFinderInterpreterExecutionService } from '../../client/datascience/jupyter/interpreter/jupyterCommandInterpreterExecutionService'; +import { JupyterExecutionFactory } from '../../client/datascience/jupyter/jupyterExecutionFactory'; +import { KernelSelector } from '../../client/datascience/jupyter/kernels/kernelSelector'; +import { NotebookStarter } from '../../client/datascience/jupyter/notebookStarter'; +import { LiveShareApi } from '../../client/datascience/liveshare/liveshare'; +import { + IJupyterKernelSpec, + IJupyterSubCommandExecutionService, + INotebookServer +} from '../../client/datascience/types'; +import { EnvironmentActivationService } from '../../client/interpreter/activation/service'; +import { IEnvironmentActivationService } from '../../client/interpreter/activation/types'; +import { IInterpreterService, InterpreterType, PythonInterpreter } from '../../client/interpreter/contracts'; +import { InterpreterService } from '../../client/interpreter/interpreterService'; +import { KnownSearchPathsForInterpreters } from '../../client/interpreter/locators/services/KnownPathsService'; +import { ServiceContainer } from '../../client/ioc/container'; +import { getOSType, OSType } from '../common'; +import { noop, sleep } from '../core'; +import { MockOutputChannel } from '../mockClasses'; +import { MockAutoSelectionService } from '../mocks/autoSelector'; +import { MockJupyterServer } from './mockJupyterServer'; + +// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length +class DisposableRegistry implements IDisposableRegistry, IAsyncDisposableRegistry { + private disposables: Disposable[] = []; + + public push = (disposable: Disposable): void => { + this.disposables.push(disposable); + }; + + public dispose = async (): Promise => { + for (const disposable of this.disposables) { + if (!disposable) { + continue; + } + const val = disposable.dispose(); + if (val instanceof Promise) { + const promise = val as Promise; + await promise; + } + } + this.disposables = []; + }; +} + +suite('Jupyter Execution', async () => { + const interpreterService = mock(InterpreterService); + const jupyterOutputChannel = new MockOutputChannel(''); + const executionFactory = mock(PythonExecutionFactory); + const liveShare = mock(LiveShareApi); + const configService = mock(ConfigurationService); + const application = mock(ApplicationShell); + const processServiceFactory = mock(ProcessServiceFactory); + const knownSearchPaths = mock(KnownSearchPathsForInterpreters); + const fileSystem = mock(FileSystem); + const activationHelper = mock(EnvironmentActivationService); + const serviceContainer = mock(ServiceContainer); + const workspaceService = mock(WorkspaceService); + const disposableRegistry = new DisposableRegistry(); + const dummyEvent = new EventEmitter(); + const configChangeEvent = new EventEmitter(); + const pythonSettings = new PythonSettings(undefined, new MockAutoSelectionService()); + const jupyterOnPath = getOSType() === OSType.Windows ? '/foo/bar/jupyter.exe' : '/foo/bar/jupyter'; + let ipykernelInstallCount = 0; + let kernelSelector: KernelSelector; + let notebookStarter: NotebookStarter; + const workingPython: PythonInterpreter = { + path: '/foo/bar/python.exe', + version: new SemVer('3.6.6-final'), + sysVersion: '1.0.0.0', + sysPrefix: 'Python', + type: InterpreterType.Unknown, + architecture: Architecture.x64 + }; + + const missingKernelPython: PythonInterpreter = { + path: '/foo/baz/python.exe', + version: new SemVer('3.1.1-final'), + sysVersion: '1.0.0.0', + sysPrefix: 'Python', + type: InterpreterType.Unknown, + architecture: Architecture.x64 + }; + + const missingNotebookPython: PythonInterpreter = { + path: '/bar/baz/python.exe', + version: new SemVer('2.1.1-final'), + sysVersion: '1.0.0.0', + sysPrefix: 'Python', + type: InterpreterType.Unknown, + architecture: Architecture.x64 + }; + + const missingNotebookPython2: PythonInterpreter = { + path: '/two/baz/python.exe', + version: new SemVer('2.1.1'), + sysVersion: '1.0.0.0', + sysPrefix: 'Python', + type: InterpreterType.Unknown, + architecture: Architecture.x64 + }; + + let workingKernelSpec: string; + + suiteSetup(() => { + noop(); + }); + suiteTeardown(() => { + noop(); + }); + + setup(() => { + workingKernelSpec = createTempSpec(workingPython.path); + ipykernelInstallCount = 0; + // tslint:disable-next-line:no-invalid-this + }); + + teardown(() => { + reset(fileSystem); + return cleanupDisposables(); + }); + + function cleanupDisposables(): Promise { + return disposableRegistry.dispose(); + } + + // tslint:disable-next-line: max-classes-per-file + class FunctionMatcher extends Matcher { + private func: (obj: any) => boolean; + constructor(func: (obj: any) => boolean) { + super(); + this.func = func; + } + public match(value: Object): boolean { + return this.func(value); + } + public toString(): string { + return 'FunctionMatcher'; + } + } + + function createTempSpec(pythonPath: string): string { + const tempDir = os.tmpdir(); + const subDir = uuid(); + const filePath = path.join(tempDir, subDir, 'kernel.json'); + fs.ensureDirSync(path.dirname(filePath)); + fs.writeJSONSync(filePath, { + display_name: 'Python 3', + language: 'python', + argv: [pythonPath, '-m', 'ipykernel_launcher', '-f', '{connection_file}'] + }); + return filePath; + } + + function argThat(func: (obj: any) => boolean): any { + return new FunctionMatcher(func); + } + + function createTypeMoq(tag: string): TypeMoq.IMock { + // Use typemoqs for those things that are resolved as promises. mockito doesn't allow nesting of mocks. ES6 Proxy class + // is the problem. We still need to make it thenable though. See this issue: https://github.com/florinn/typemoq/issues/67 + const result: TypeMoq.IMock = TypeMoq.Mock.ofType(); + (result as any).tag = tag; + result.setup((x: any) => x.then).returns(() => undefined); + return result; + } + + function argsMatch(matchers: (string | RegExp)[], args: string[]): boolean { + if (matchers.length === args.length) { + return args.every((s, i) => { + const r = matchers[i] as RegExp; + return r && r.test ? r.test(s) : s === matchers[i]; + }); + } + return false; + } + + function setupPythonService( + service: TypeMoq.IMock, + module: string | undefined, + args: (string | RegExp)[], + result: Promise> + ) { + if (module) { + service + .setup((x) => + x.execModule( + TypeMoq.It.isValue(module), + TypeMoq.It.is((a) => argsMatch(args, a)), + TypeMoq.It.isAny() + ) + ) + .returns(() => result); + const withModuleArgs = ['-m', module, ...args]; + service + .setup((x) => + x.exec( + TypeMoq.It.is((a) => argsMatch(withModuleArgs, a)), + TypeMoq.It.isAny() + ) + ) + .returns(() => result); + } else { + service + .setup((x) => + x.exec( + TypeMoq.It.is((a) => argsMatch(args, a)), + TypeMoq.It.isAny() + ) + ) + .returns(() => result); + } + } + + function setupPythonServiceWithFunc( + service: TypeMoq.IMock, + module: string, + args: (string | RegExp)[], + result: () => Promise> + ) { + service + .setup((x) => + x.execModule( + TypeMoq.It.isValue(module), + TypeMoq.It.is((a) => argsMatch(args, a)), + TypeMoq.It.isAny() + ) + ) + .returns(result); + const withModuleArgs = ['-m', module, ...args]; + service + .setup((x) => + x.exec( + TypeMoq.It.is((a) => argsMatch(withModuleArgs, a)), + TypeMoq.It.isAny() + ) + ) + .returns(result); + service + .setup((x) => + x.execModule( + TypeMoq.It.isValue(module), + TypeMoq.It.is((a) => argsMatch(args, a)), + TypeMoq.It.isAny() + ) + ) + .returns(result); + } + + function setupPythonServiceExecObservable( + service: TypeMoq.IMock, + module: string, + args: (string | RegExp)[], + stderr: string[], + stdout: string[] + ) { + const result: ObservableExecutionResult = { + proc: undefined, + out: new Observable>((subscriber) => { + stderr.forEach((s) => subscriber.next({ source: 'stderr', out: s })); + stdout.forEach((s) => subscriber.next({ source: 'stderr', out: s })); + }), + dispose: () => { + noop(); + } + }; + + service + .setup((x) => + x.execModuleObservable( + TypeMoq.It.isValue(module), + TypeMoq.It.is((a) => argsMatch(args, a)), + TypeMoq.It.isAny() + ) + ) + .returns(() => result); + const withModuleArgs = ['-m', module, ...args]; + service + .setup((x) => + x.execObservable( + TypeMoq.It.is((a) => argsMatch(withModuleArgs, a)), + TypeMoq.It.isAny() + ) + ) + .returns(() => result); + } + + function setupProcessServiceExec( + service: TypeMoq.IMock, + file: string, + args: (string | RegExp)[], + result: Promise> + ) { + service + .setup((x) => + x.exec( + TypeMoq.It.isValue(file), + TypeMoq.It.is((a) => argsMatch(args, a)), + TypeMoq.It.isAny() + ) + ) + .returns(() => result); + } + + function setupProcessServiceExecWithFunc( + service: TypeMoq.IMock, + file: string, + args: (string | RegExp)[], + result: () => Promise> + ) { + service + .setup((x) => + x.exec( + TypeMoq.It.isValue(file), + TypeMoq.It.is((a) => argsMatch(args, a)), + TypeMoq.It.isAny() + ) + ) + .returns(result); + } + + function setupProcessServiceExecObservable( + service: TypeMoq.IMock, + file: string, + args: (string | RegExp)[], + stderr: string[], + stdout: string[] + ) { + const result: ObservableExecutionResult = { + proc: undefined, + out: new Observable>((subscriber) => { + stderr.forEach((s) => subscriber.next({ source: 'stderr', out: s })); + stdout.forEach((s) => subscriber.next({ source: 'stderr', out: s })); + }), + dispose: () => { + noop(); + } + }; + + service + .setup((x) => + x.execObservable( + TypeMoq.It.isValue(file), + TypeMoq.It.is((a) => argsMatch(args, a)), + TypeMoq.It.isAny() + ) + ) + .returns(() => result); + } + + function createKernelSpecs(specs: { name: string; resourceDir: string }[]): Record { + const models: Record = {}; + specs.forEach((spec) => { + models[spec.name] = { + resource_dir: spec.resourceDir, + spec: { + name: spec.name, + display_name: spec.name, + language: 'python' + } + }; + }); + return models; + } + function setupWorkingPythonService( + service: TypeMoq.IMock, + notebookStdErr?: string[], + runInDocker?: boolean + ) { + setupPythonService(service, 'ipykernel', ['--version'], Promise.resolve({ stdout: '1.1.1.1' })); + setupPythonService(service, 'jupyter', ['nbconvert', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); + setupPythonService(service, 'jupyter', ['notebook', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); + setupPythonService(service, 'jupyter', ['kernelspec', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); + service.setup((x) => x.getInterpreterInformation()).returns(() => Promise.resolve(workingPython)); + + // Don't mind the goofy path here. It's supposed to not find the item. It's just testing the internal regex works + setupPythonServiceWithFunc(service, 'jupyter', ['kernelspec', 'list', '--json'], () => { + // Return different results after we install our kernel + if (ipykernelInstallCount > 0) { + const kernelSpecs = createKernelSpecs([ + { name: 'working', resourceDir: path.dirname(workingKernelSpec) }, + { + name: '0e8519db-0895-416c-96df-fa80131ecea0', + resourceDir: + 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' + } + ]); + return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); + } else { + const kernelSpecs = createKernelSpecs([ + { + name: '0e8519db-0895-416c-96df-fa80131ecea0', + resourceDir: + 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' + } + ]); + return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); + } + }); + const kernelSpecs2 = createKernelSpecs([ + { name: 'working', resourceDir: path.dirname(workingKernelSpec) }, + { + name: '0e8519db-0895-416c-96df-fa80131ecea0', + resourceDir: + 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' + } + ]); + setupPythonService( + service, + 'jupyter', + ['kernelspec', 'list', '--json'], + Promise.resolve({ stdout: JSON.stringify(kernelSpecs2) }) + ); + setupPythonServiceWithFunc( + service, + 'ipykernel', + ['install', '--user', '--name', /\w+-\w+-\w+-\w+-\w+/, '--display-name', `'Python Interactive'`], + () => { + ipykernelInstallCount += 1; + const kernelSpecs = createKernelSpecs([ + { name: 'somename', resourceDir: path.dirname(workingKernelSpec) } + ]); + return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); + } + ); + const getServerInfoPath = path.join( + EXTENSION_ROOT_DIR, + 'pythonFiles', + 'vscode_datascience_helpers', + 'getServerInfo.py' + ); + setupPythonService( + service, + undefined, + [getServerInfoPath], + Promise.resolve({ stdout: 'failure to get server infos' }) + ); + setupPythonServiceExecObservable(service, 'jupyter', ['kernelspec', 'list', '--json'], [], []); + const dockerArgs = runInDocker ? ['--ip', '127.0.0.1'] : []; + setupPythonServiceExecObservable( + service, + 'jupyter', + [ + 'notebook', + '--no-browser', + /--notebook-dir=.*/, + /--config=.*/, + '--NotebookApp.iopub_data_rate_limit=10000000000.0', + ...dockerArgs + ], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] + ); + } + + function setupMissingKernelPythonService( + service: TypeMoq.IMock, + notebookStdErr?: string[] + ) { + setupPythonService(service, 'jupyter', ['notebook', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); + setupPythonService(service, 'jupyter', ['kernelspec', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); + service.setup((x) => x.getInterpreterInformation()).returns(() => Promise.resolve(missingKernelPython)); + const kernelSpecs = createKernelSpecs([{ name: 'working', resourceDir: path.dirname(workingKernelSpec) }]); + setupPythonService( + service, + 'jupyter', + ['kernelspec', 'list', '--json'], + Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }) + ); + const getServerInfoPath = path.join( + EXTENSION_ROOT_DIR, + 'pythonFiles', + 'vscode_datascience_helpers', + 'getServerInfo.py' + ); + setupPythonService( + service, + undefined, + [getServerInfoPath], + Promise.resolve({ stdout: 'failure to get server infos' }) + ); + setupPythonServiceExecObservable(service, 'jupyter', ['kernelspec', 'list', '--json'], [], []); + setupPythonServiceExecObservable( + service, + 'jupyter', + [ + 'notebook', + '--no-browser', + /--notebook-dir=.*/, + /--config=.*/, + '--NotebookApp.iopub_data_rate_limit=10000000000.0' + ], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] + ); + } + + function setupMissingNotebookPythonService(service: TypeMoq.IMock) { + service + .setup((x) => x.execModule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((_v) => { + return Promise.reject('cant exec'); + }); + service.setup((x) => x.getInterpreterInformation()).returns(() => Promise.resolve(missingNotebookPython)); + } + + function setupWorkingProcessService(service: TypeMoq.IMock, notebookStdErr?: string[]) { + // Don't mind the goofy path here. It's supposed to not find the item. It's just testing the internal regex works + setupProcessServiceExecWithFunc( + service, + workingPython.path, + ['-m', 'jupyter', 'kernelspec', 'list', '--json'], + () => { + // Return different results after we install our kernel + if (ipykernelInstallCount > 0) { + const kernelSpecs = createKernelSpecs([ + { name: 'working', resourceDir: path.dirname(workingKernelSpec) }, + { + name: '0e8519db-0895-416c-96df-fa80131ecea0', + resourceDir: + 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' + } + ]); + return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); + } else { + const kernelSpecs = createKernelSpecs([ + { + name: '0e8519db-0895-416c-96df-fa80131ecea0', + resourceDir: + 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' + } + ]); + return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); + } + } + ); + const kernelSpecs2 = createKernelSpecs([ + { name: 'working', resourceDir: path.dirname(workingKernelSpec) }, + { + name: '0e8519db-0895-416c-96df-fa80131ecea0', + resourceDir: + 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' + } + ]); + setupProcessServiceExec( + service, + workingPython.path, + ['-m', 'jupyter', 'kernelspec', 'list', '--json'], + Promise.resolve({ stdout: JSON.stringify(kernelSpecs2) }) + ); + setupProcessServiceExecWithFunc( + service, + workingPython.path, + [ + '-m', + 'ipykernel', + 'install', + '--user', + '--name', + /\w+-\w+-\w+-\w+-\w+/, + '--display-name', + `'Python Interactive'` + ], + () => { + ipykernelInstallCount += 1; + const kernelSpecs = createKernelSpecs([ + { name: 'somename', resourceDir: path.dirname(workingKernelSpec) } + ]); + return Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }); + } + ); + const getServerInfoPath = path.join( + EXTENSION_ROOT_DIR, + 'pythonFiles', + 'vscode_datascience_helpers', + 'getServerInfo.py' + ); + setupProcessServiceExec( + service, + workingPython.path, + [getServerInfoPath], + Promise.resolve({ stdout: 'failure to get server infos' }) + ); + setupProcessServiceExecObservable( + service, + workingPython.path, + ['-m', 'jupyter', 'kernelspec', 'list', '--json'], + [], + [] + ); + setupProcessServiceExecObservable( + service, + workingPython.path, + [ + '-m', + 'jupyter', + 'notebook', + '--no-browser', + /--notebook-dir=.*/, + /--config=.*/, + '--NotebookApp.iopub_data_rate_limit=10000000000.0' + ], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] + ); + } + + function setupMissingKernelProcessService(service: TypeMoq.IMock, notebookStdErr?: string[]) { + const kernelSpecs = createKernelSpecs([{ name: 'working', resourceDir: path.dirname(workingKernelSpec) }]); + setupProcessServiceExec( + service, + missingKernelPython.path, + ['-m', 'jupyter', 'kernelspec', 'list', '--json'], + Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }) + ); + const getServerInfoPath = path.join( + EXTENSION_ROOT_DIR, + 'pythonFiles', + 'vscode_datascience_helpers', + 'getServerInfo.py' + ); + setupProcessServiceExec( + service, + missingKernelPython.path, + [getServerInfoPath], + Promise.resolve({ stdout: 'failure to get server infos' }) + ); + setupProcessServiceExecObservable( + service, + missingKernelPython.path, + ['-m', 'jupyter', 'kernelspec', 'list', '--json'], + [], + [] + ); + setupProcessServiceExecObservable( + service, + missingKernelPython.path, + [ + '-m', + 'jupyter', + 'notebook', + '--no-browser', + /--notebook-dir=.*/, + /--config=.*/, + '--NotebookApp.iopub_data_rate_limit=10000000000.0' + ], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] + ); + } + + function setupPathProcessService( + jupyterPath: string, + service: TypeMoq.IMock, + notebookStdErr?: string[] + ) { + const kernelSpecs = createKernelSpecs([{ name: 'working', resourceDir: path.dirname(workingKernelSpec) }]); + setupProcessServiceExec( + service, + jupyterPath, + ['kernelspec', 'list', '--json'], + Promise.resolve({ stdout: JSON.stringify(kernelSpecs) }) + ); + setupProcessServiceExecObservable(service, jupyterPath, ['kernelspec', 'list', '--json'], [], []); + setupProcessServiceExec(service, jupyterPath, ['--version'], Promise.resolve({ stdout: '1.1.1.1' })); + setupProcessServiceExec( + service, + jupyterPath, + ['notebook', '--version'], + Promise.resolve({ stdout: '1.1.1.1' }) + ); + setupProcessServiceExec( + service, + jupyterPath, + ['kernelspec', '--version'], + Promise.resolve({ stdout: '1.1.1.1' }) + ); + setupProcessServiceExecObservable( + service, + jupyterPath, + [ + 'notebook', + '--no-browser', + /--notebook-dir=.*/, + /--config=.*/, + '--NotebookApp.iopub_data_rate_limit=10000000000.0' + ], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] + ); + + // WE also check for existence with just the key jupyter + setupProcessServiceExec(service, 'jupyter', ['--version'], Promise.resolve({ stdout: '1.1.1.1' })); + setupProcessServiceExec(service, 'jupyter', ['notebook', '--version'], Promise.resolve({ stdout: '1.1.1.1' })); + setupProcessServiceExec( + service, + 'jupyter', + ['kernelspec', '--version'], + Promise.resolve({ stdout: '1.1.1.1' }) + ); + } + + function createExecution( + activeInterpreter: PythonInterpreter, + notebookStdErr?: string[], + skipSearch?: boolean + ): JupyterExecutionFactory { + return createExecutionAndReturnProcessService(activeInterpreter, notebookStdErr, skipSearch) + .jupyterExecutionFactory; + } + function createExecutionAndReturnProcessService( + activeInterpreter: PythonInterpreter, + notebookStdErr?: string[], + skipSearch?: boolean, + runInDocker?: boolean + ): { + executionService: IPythonExecutionService; + jupyterExecutionFactory: JupyterExecutionFactory; + } { + // Setup defaults + when(interpreterService.onDidChangeInterpreter).thenReturn(dummyEvent.event); + when(interpreterService.getActiveInterpreter(anything())).thenResolve(activeInterpreter); + when(interpreterService.getInterpreters(anything())).thenResolve([ + workingPython, + missingKernelPython, + missingNotebookPython + ]); + when(interpreterService.getInterpreterDetails(match('/foo/bar/python.exe'))).thenResolve(workingPython); // Mockito is stupid. Matchers have to use literals. + when(interpreterService.getInterpreterDetails(match('/foo/baz/python.exe'))).thenResolve(missingKernelPython); + when(interpreterService.getInterpreterDetails(match('/bar/baz/python.exe'))).thenResolve(missingNotebookPython); + when(interpreterService.getInterpreterDetails(argThat((o) => !o.includes || !o.includes('python')))).thenReject( + ('Unknown interpreter' as any) as Error + ); + if (runInDocker) { + when(fileSystem.readFile('/proc/self/cgroup')).thenResolve('hello docker world'); + } + // Create our working python and process service. + const workingService = createTypeMoq('working'); + setupWorkingPythonService(workingService, notebookStdErr, runInDocker); + const missingKernelService = createTypeMoq('missingKernel'); + setupMissingKernelPythonService(missingKernelService, notebookStdErr); + const missingNotebookService = createTypeMoq('missingNotebook'); + setupMissingNotebookPythonService(missingNotebookService); + const missingNotebookService2 = createTypeMoq('missingNotebook2'); + setupMissingNotebookPythonService(missingNotebookService2); + const processService = createTypeMoq('working process'); + setupWorkingProcessService(processService, notebookStdErr); + setupMissingKernelProcessService(processService, notebookStdErr); + setupPathProcessService(jupyterOnPath, processService, notebookStdErr); + when(executionFactory.create(argThat((o) => o.pythonPath && o.pythonPath === workingPython.path))).thenResolve( + workingService.object + ); + when( + executionFactory.create(argThat((o) => o.pythonPath && o.pythonPath === missingKernelPython.path)) + ).thenResolve(missingKernelService.object); + when( + executionFactory.create(argThat((o) => o.pythonPath && o.pythonPath === missingNotebookPython.path)) + ).thenResolve(missingNotebookService.object); + when( + executionFactory.create(argThat((o) => o.pythonPath && o.pythonPath === missingNotebookPython2.path)) + ).thenResolve(missingNotebookService2.object); + + when( + executionFactory.createDaemon(argThat((o) => o.pythonPath && o.pythonPath === workingPython.path)) + ).thenResolve(workingService.object); + + when( + executionFactory.createDaemon(argThat((o) => o.pythonPath && o.pythonPath === missingKernelPython.path)) + ).thenResolve(missingKernelService.object); + + when( + executionFactory.createDaemon(argThat((o) => o.pythonPath && o.pythonPath === missingNotebookPython.path)) + ).thenResolve(missingNotebookService.object); + + when( + executionFactory.createDaemon(argThat((o) => o.pythonPath && o.pythonPath === missingNotebookPython2.path)) + ).thenResolve(missingNotebookService2.object); + + let activeService = workingService; + if (activeInterpreter === missingKernelPython) { + activeService = missingKernelService; + } else if (activeInterpreter === missingNotebookPython) { + activeService = missingNotebookService; + } else if (activeInterpreter === missingNotebookPython2) { + activeService = missingNotebookService2; + } + when(executionFactory.create(argThat((o) => !o || !o.pythonPath))).thenResolve(activeService.object); + when( + executionFactory.createActivatedEnvironment(argThat((o) => !o || o.interpreter === activeInterpreter)) + ).thenResolve(activeService.object); + when( + executionFactory.createActivatedEnvironment(argThat((o) => o && o.interpreter.path === workingPython.path)) + ).thenResolve(workingService.object); + when( + executionFactory.createActivatedEnvironment( + argThat((o) => o && o.interpreter.path === missingKernelPython.path) + ) + ).thenResolve(missingKernelService.object); + when( + executionFactory.createActivatedEnvironment( + argThat((o) => o && o.interpreter.path === missingNotebookPython.path) + ) + ).thenResolve(missingNotebookService.object); + when( + executionFactory.createActivatedEnvironment( + argThat((o) => o && o.interpreter.path === missingNotebookPython2.path) + ) + ).thenResolve(missingNotebookService2.object); + when(processServiceFactory.create()).thenResolve(processService.object); + + when(liveShare.getApi()).thenResolve(null); + + // Service container needs logger, file system, and config service + when(serviceContainer.get(IConfigurationService)).thenReturn(instance(configService)); + when(serviceContainer.get(IFileSystem)).thenReturn(instance(fileSystem)); + when(serviceContainer.get(IWorkspaceService)).thenReturn(instance(workspaceService)); + when(serviceContainer.get(IApplicationShell)).thenReturn(instance(application)); + when(configService.getSettings(anything())).thenReturn(pythonSettings); + when(workspaceService.onDidChangeConfiguration).thenReturn(configChangeEvent.event); + when(application.withProgress(anything(), anything())).thenCall( + (_, cb: (_: any, token: any) => Promise) => { + return new Promise((resolve, reject) => { + cb({ report: noop }, new CancellationTokenSource().token).then(resolve).catch(reject); + }); + } + ); + + // Setup default settings + pythonSettings.datascience = { + allowImportFromNotebook: true, + jupyterLaunchTimeout: 10, + jupyterLaunchRetries: 3, + enabled: true, + jupyterServerURI: 'local', + // tslint:disable-next-line: no-invalid-template-strings + notebookFileRoot: '${fileDirname}', + changeDirOnImportExport: true, + useDefaultConfigForJupyter: true, + jupyterInterruptTimeout: 10000, + searchForJupyter: !skipSearch, + showCellInputCode: true, + collapseCellInputCodeByDefault: true, + allowInput: true, + maxOutputSize: 400, + errorBackgroundColor: '#FFFFFF', + sendSelectionToInteractiveWindow: false, + variableExplorerExclude: 'module;function;builtin_function_or_method', + codeRegularExpression: '^(#\\s*%%|#\\s*\\|#\\s*In\\[\\d*?\\]|#\\s*In\\[ \\])', + markdownRegularExpression: '^(#\\s*%%\\s*\\[markdown\\]|#\\s*\\)', + allowLiveShare: false, + enablePlotViewer: true, + runStartupCommands: '', + debugJustMyCode: true, + variableQueries: [], + jupyterCommandLineArguments: [] + }; + + // Service container also needs to generate jupyter servers. However we can't use a mock as that messes up returning + // this object from a promise + when(serviceContainer.get(INotebookServer)).thenReturn(new MockJupyterServer()); + + when(knownSearchPaths.getSearchPaths()).thenReturn(['/foo/bar']); + + // We also need a file system + const tempFile = { + dispose: () => { + return undefined; + }, + filePath: '/foo/bar/baz.py' + }; + when(fileSystem.createTemporaryFile(anything())).thenResolve(tempFile); + when(fileSystem.createDirectory(anything())).thenResolve(); + when(fileSystem.deleteDirectory(anything())).thenResolve(); + when(fileSystem.fileExists(workingKernelSpec)).thenResolve(true); + when(fileSystem.readFile(workingKernelSpec)).thenResolve( + '{"display_name":"Python 3","language":"python","argv":["/foo/bar/python.exe","-m","ipykernel_launcher","-f","{connection_file}"]}' + ); + + const commandFactory = new JupyterCommandFactory( + instance(executionFactory), + instance(activationHelper), + instance(processServiceFactory), + instance(interpreterService) + ); + const persistentSateFactory = mock(PersistentStateFactory); + const persistentState = mock(PersistentState); + when(persistentState.updateValue(anything())).thenResolve(); + when(persistentSateFactory.createGlobalPersistentState(anything())).thenReturn(instance(persistentState)); + when(persistentSateFactory.createGlobalPersistentState(anything(), anything())).thenReturn( + instance(persistentState) + ); + when(persistentSateFactory.createWorkspacePersistentState(anything())).thenReturn(instance(persistentState)); + when(persistentSateFactory.createWorkspacePersistentState(anything(), anything())).thenReturn( + instance(persistentState) + ); + const commandFinder = new JupyterCommandFinder( + instance(interpreterService), + instance(executionFactory), + instance(configService), + instance(knownSearchPaths), + disposableRegistry, + instance(fileSystem), + instance(processServiceFactory), + commandFactory, + instance(workspaceService), + instance(application), + instance(persistentSateFactory) + ); + when(serviceContainer.get(JupyterCommandFinder)).thenReturn(commandFinder); + when(serviceContainer.get(IInterpreterService)).thenReturn(instance(interpreterService)); + when(serviceContainer.get(IProcessServiceFactory)).thenReturn( + instance(processServiceFactory) + ); + when(serviceContainer.get(IEnvironmentActivationService)).thenReturn( + instance(activationHelper) + ); + when(serviceContainer.get(IPythonExecutionFactory)).thenReturn( + instance(executionFactory) + ); + kernelSelector = mock(KernelSelector); + const kernelSpec: IJupyterKernelSpec = { + argv: [], + display_name: 'hello', + language: PYTHON_LANGUAGE, + name: 'hello', + path: '' + }; + when( + kernelSelector.getKernelForLocalConnection(anything(), anything(), anything(), anything(), anything()) + ).thenResolve({ + kernelSpec + }); + const jupyterCmdExecutionService = new JupyterCommandFinderInterpreterExecutionService( + commandFinder, + instance(interpreterService), + instance(fileSystem), + instance(executionFactory) + ); + when(serviceContainer.get(IJupyterSubCommandExecutionService)).thenReturn( + jupyterCmdExecutionService + ); + notebookStarter = new NotebookStarter( + jupyterCmdExecutionService, + instance(fileSystem), + instance(serviceContainer), + instance(jupyterOutputChannel) + ); + when(serviceContainer.get(KernelSelector)).thenReturn(instance(kernelSelector)); + when(serviceContainer.get(NotebookStarter)).thenReturn(notebookStarter); + return { + executionService: activeService.object, + jupyterExecutionFactory: new JupyterExecutionFactory( + instance(liveShare), + instance(interpreterService), + disposableRegistry, + disposableRegistry, + instance(fileSystem), + instance(workspaceService), + instance(configService), + instance(kernelSelector), + notebookStarter, + instance(application), + instance(jupyterOutputChannel), + instance(serviceContainer) + ) + }; + } + + test('Working notebook and commands found', async () => { + const jupyterExecutionFactory = createExecution(workingPython); + + await assert.eventually.equal(jupyterExecutionFactory.isNotebookSupported(), true, 'Notebook not supported'); + await assert.eventually.equal(jupyterExecutionFactory.isImportSupported(), true, 'Import not supported'); + const usableInterpreter = await jupyterExecutionFactory.getUsableJupyterPython(); + assert.isOk(usableInterpreter, 'Usable interpreter not found'); + await assert.isFulfilled(jupyterExecutionFactory.connectToNotebookServer(), 'Should be able to start a server'); + }).timeout(10000); + + test('Includes correct args for running in docker', async () => { + const { jupyterExecutionFactory } = createExecutionAndReturnProcessService( + workingPython, + undefined, + undefined, + true + ); + + await assert.eventually.equal(jupyterExecutionFactory.isNotebookSupported(), true, 'Notebook not supported'); + await assert.eventually.equal(jupyterExecutionFactory.isImportSupported(), true, 'Import not supported'); + const usableInterpreter = await jupyterExecutionFactory.getUsableJupyterPython(); + assert.isOk(usableInterpreter, 'Usable interpreter not found'); + await assert.isFulfilled(jupyterExecutionFactory.connectToNotebookServer(), 'Should be able to start a server'); + }).timeout(10000); + + test('Failing notebook throws exception', async () => { + const execution = createExecution(missingNotebookPython); + when(interpreterService.getInterpreters(anything())).thenResolve([missingNotebookPython]); + await assert.isRejected(execution.connectToNotebookServer(), 'cant exec'); + }).timeout(10000); + + test('Failing others throws exception', async () => { + const execution = createExecution(missingNotebookPython); + when(interpreterService.getInterpreters(anything())).thenResolve([ + missingNotebookPython, + missingNotebookPython2 + ]); + await assert.isRejected(execution.connectToNotebookServer(), 'cant exec'); + }).timeout(10000); + + test('Other than active works', async () => { + const execution = createExecution(missingNotebookPython); + await assert.eventually.equal(execution.isNotebookSupported(), true, 'Notebook not supported'); + await assert.eventually.equal(execution.isImportSupported(), true, 'Import not supported'); + const usableInterpreter = await execution.getUsableJupyterPython(); + assert.isOk(usableInterpreter, 'Usable interpreter not found'); + if (usableInterpreter) { + assert.notEqual(usableInterpreter.path, missingNotebookPython.path); + } + }).timeout(10000); + + test('Missing kernel python still finds interpreter', async () => { + const execution = createExecution(missingKernelPython); + when(interpreterService.getActiveInterpreter(anything())).thenResolve(missingKernelPython); + await assert.eventually.equal(execution.isNotebookSupported(), true, 'Notebook not supported'); + const usableInterpreter = await execution.getUsableJupyterPython(); + assert.isOk(usableInterpreter, 'Usable interpreter not found'); + if (usableInterpreter) { + // Linter + assert.equal(usableInterpreter.path, missingKernelPython.path); + assert.equal( + usableInterpreter.version!.major, + missingKernelPython.version!.major, + 'Found interpreter should match on major' + ); + assert.equal( + usableInterpreter.version!.minor, + missingKernelPython.version!.minor, + 'Found interpreter should match on minor' + ); + } + }).timeout(10000); + + test('Other than active finds closest match', async () => { + const execution = createExecution(missingNotebookPython); + when(interpreterService.getActiveInterpreter(anything())).thenResolve(missingNotebookPython); + await assert.eventually.equal(execution.isNotebookSupported(), true, 'Notebook not supported'); + const usableInterpreter = await execution.getUsableJupyterPython(); + assert.isOk(usableInterpreter, 'Usable interpreter not found'); + if (usableInterpreter) { + // Linter + assert.notEqual(usableInterpreter.path, missingNotebookPython.path); + assert.notEqual( + usableInterpreter.version!.major, + missingNotebookPython.version!.major, + 'Found interpreter should not match on major' + ); + } + // Force config change and ask again + pythonSettings.datascience.searchForJupyter = false; + const evt = { + affectsConfiguration(_m: string): boolean { + return true; + } + }; + configChangeEvent.fire(evt); + // Wait for cache to get cleared. + await sleep(100); + await assert.eventually.equal( + execution.isNotebookSupported(), + false, + 'Notebook should not be supported after config change' + ); + }).timeout(10000); + + test('Display progress message', async () => { + const execution = createExecution(missingNotebookPython); + await assert.eventually.equal(execution.isNotebookSupported(), true, 'Notebook not supported'); + const usableInterpreter = await execution.getUsableJupyterPython(); + assert.isOk(usableInterpreter, 'Usable interpreter not found'); + if (usableInterpreter) { + // Linter + assert.notEqual(usableInterpreter.path, missingNotebookPython.path); + assert.notEqual( + usableInterpreter.version!.major, + missingNotebookPython.version!.major, + 'Found interpreter should not match on major' + ); + } + // Force config change and ask again + pythonSettings.datascience.searchForJupyter = false; + const evt = { + affectsConfiguration(_m: string): boolean { + return true; + } + }; + configChangeEvent.fire(evt); + // Wait for cache to get cleared. + await sleep(100); + await assert.eventually.equal( + execution.isNotebookSupported(), + false, + 'Notebook should not be supported after config change' + ); + verify(application.withProgress(anything(), anything())).atLeast(1); + }).timeout(10000); + + test('Progress message should not be displayed for more than 1s when interpreter search completes quickly', async () => { + const execution = createExecution(missingNotebookPython); + const progressCancellation = new CancellationTokenSource(); + reset(application); + when(application.withProgress(anything(), anything())).thenCall( + (_, cb: (_: any, token: any) => Promise) => { + return new Promise((resolve, reject) => { + cb({ report: noop }, progressCancellation.token).then(resolve).catch(reject); + }); + } + ); + + // Now interpreters = fast discovery (less time for display of progress). + when(interpreterService.getInterpreters(anything())).thenReturn(Promise.resolve([])); + + // The call to isNotebookSupported should not timeout in 1 seconds. + const isNotebookSupported = execution.isNotebookSupported(); + await assert.eventually.notEqual( + Promise.race([isNotebookSupported, sleep(1000).then(() => 'timeout')]), + 'timeout' + ); + verify(application.withProgress(anything(), anything())).atLeast(1); + }).timeout(10_000); + + test('Cancel progress message if interpreter search takes too long', async () => { + const execution = createExecution(missingNotebookPython); + const progressCancellation = new CancellationTokenSource(); + reset(application); + when(application.withProgress(anything(), anything())).thenCall( + (_, cb: (_: any, token: any) => Promise) => { + return new Promise((resolve, reject) => { + cb({ report: noop }, progressCancellation.token).then(resolve).catch(reject); + }); + } + ); + + const slowInterpreterDiscovery = createDeferred(); + when(interpreterService.getInterpreters(anything())).thenReturn(slowInterpreterDiscovery.promise); + + // The call to interpreterService.getInterpreters shoud not complete, it is very slow. + const isNotebookSupported = execution.isNotebookSupported(); + await assert.eventually.equal( + Promise.race([isNotebookSupported, sleep(5000).then(() => 'timeout')]), + 'timeout' + ); + + // Once we cancel the progress message, the promise should resolve almost immediately. + progressCancellation.cancel(); + await assert.eventually.notEqual( + Promise.race([isNotebookSupported, sleep(500).then(() => 'timeout')]), + 'timeout' + ); + verify(application.withProgress(anything(), anything())).atLeast(1); + }).timeout(20_000); + + test('Jupyter found on the path', async () => { + // Make sure we can find jupyter on the path if we + // can't find it in a python module. + const execution = createExecution(missingNotebookPython); + when(interpreterService.getInterpreters(anything())).thenResolve([missingNotebookPython]); + when(fileSystem.getFiles(anyString())).thenResolve([jupyterOnPath]); + await assert.isFulfilled(execution.connectToNotebookServer(), 'Should be able to start a server'); + }).timeout(10000); + + test('Jupyter found on the path skipped', async () => { + // Make sure we can find jupyter on the path if we + // can't find it in a python module. + const execution = createExecution(missingNotebookPython, undefined, true); + when(interpreterService.getInterpreters(anything())).thenResolve([missingNotebookPython]); + when(fileSystem.getFiles(anyString())).thenResolve([jupyterOnPath]); + await assert.isRejected(execution.connectToNotebookServer(), 'cant exec'); + }).timeout(10000); +}); diff --git a/src/test/datascience/executionServiceMock.ts b/src/test/datascience/executionServiceMock.ts index 981933caa3b1..71fd0fac27c9 100644 --- a/src/test/datascience/executionServiceMock.ts +++ b/src/test/datascience/executionServiceMock.ts @@ -1,83 +1,83 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { SemVer } from 'semver'; - -import { ErrorUtils } from '../../client/common/errors/errorUtils'; -import { ModuleNotInstalledError } from '../../client/common/errors/moduleNotInstalledError'; -import { BufferDecoder } from '../../client/common/process/decoder'; -import { ProcessService } from '../../client/common/process/proc'; -import { - ExecutionResult, - InterpreterInfomation, - IPythonExecutionService, - ObservableExecutionResult, - SpawnOptions -} from '../../client/common/process/types'; -import { Architecture } from '../../client/common/utils/platform'; - -export class MockPythonExecutionService implements IPythonExecutionService { - private procService: ProcessService; - private pythonPath: string = 'python'; - - constructor() { - this.procService = new ProcessService(new BufferDecoder()); - } - - public getInterpreterInformation(): Promise { - return Promise.resolve({ - path: '', - version: new SemVer('3.6.0-beta'), - sysVersion: '1.0', - sysPrefix: '1.0', - architecture: Architecture.x64 - }); - } - - public getExecutablePath(): Promise { - return Promise.resolve(this.pythonPath); - } - public isModuleInstalled(moduleName: string): Promise { - return this.procService - .exec(this.pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true }) - .then(() => true) - .catch(() => false); - } - public execObservable(args: string[], options: SpawnOptions): ObservableExecutionResult { - const opts: SpawnOptions = { ...options }; - return this.procService.execObservable(this.pythonPath, args, opts); - } - public execModuleObservable( - moduleName: string, - args: string[], - options: SpawnOptions - ): ObservableExecutionResult { - const opts: SpawnOptions = { ...options }; - return this.procService.execObservable(this.pythonPath, ['-m', moduleName, ...args], opts); - } - public exec(args: string[], options: SpawnOptions): Promise> { - const opts: SpawnOptions = { ...options }; - return this.procService.exec(this.pythonPath, args, opts); - } - public async execModule( - moduleName: string, - args: string[], - options: SpawnOptions - ): Promise> { - const opts: SpawnOptions = { ...options }; - const result = await this.procService.exec(this.pythonPath, ['-m', moduleName, ...args], opts); - - // If a module is not installed we'll have something in stderr. - if (moduleName && ErrorUtils.outputHasModuleNotInstalledError(moduleName!, result.stderr)) { - const isInstalled = await this.isModuleInstalled(moduleName!); - if (!isInstalled) { - throw new ModuleNotInstalledError(moduleName!); - } - } - - return result; - } - public getExecutionInfo(args: string[]) { - return { command: this.pythonPath, args }; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { SemVer } from 'semver'; + +import { ErrorUtils } from '../../client/common/errors/errorUtils'; +import { ModuleNotInstalledError } from '../../client/common/errors/moduleNotInstalledError'; +import { BufferDecoder } from '../../client/common/process/decoder'; +import { ProcessService } from '../../client/common/process/proc'; +import { + ExecutionResult, + InterpreterInfomation, + IPythonExecutionService, + ObservableExecutionResult, + SpawnOptions +} from '../../client/common/process/types'; +import { Architecture } from '../../client/common/utils/platform'; + +export class MockPythonExecutionService implements IPythonExecutionService { + private procService: ProcessService; + private pythonPath: string = 'python'; + + constructor() { + this.procService = new ProcessService(new BufferDecoder()); + } + + public getInterpreterInformation(): Promise { + return Promise.resolve({ + path: '', + version: new SemVer('3.6.0-beta'), + sysVersion: '1.0', + sysPrefix: '1.0', + architecture: Architecture.x64 + }); + } + + public getExecutablePath(): Promise { + return Promise.resolve(this.pythonPath); + } + public isModuleInstalled(moduleName: string): Promise { + return this.procService + .exec(this.pythonPath, ['-c', `import ${moduleName}`], { throwOnStdErr: true }) + .then(() => true) + .catch(() => false); + } + public execObservable(args: string[], options: SpawnOptions): ObservableExecutionResult { + const opts: SpawnOptions = { ...options }; + return this.procService.execObservable(this.pythonPath, args, opts); + } + public execModuleObservable( + moduleName: string, + args: string[], + options: SpawnOptions + ): ObservableExecutionResult { + const opts: SpawnOptions = { ...options }; + return this.procService.execObservable(this.pythonPath, ['-m', moduleName, ...args], opts); + } + public exec(args: string[], options: SpawnOptions): Promise> { + const opts: SpawnOptions = { ...options }; + return this.procService.exec(this.pythonPath, args, opts); + } + public async execModule( + moduleName: string, + args: string[], + options: SpawnOptions + ): Promise> { + const opts: SpawnOptions = { ...options }; + const result = await this.procService.exec(this.pythonPath, ['-m', moduleName, ...args], opts); + + // If a module is not installed we'll have something in stderr. + if (moduleName && ErrorUtils.outputHasModuleNotInstalledError(moduleName!, result.stderr)) { + const isInstalled = await this.isModuleInstalled(moduleName!); + if (!isInstalled) { + throw new ModuleNotInstalledError(moduleName!); + } + } + + return result; + } + public getExecutionInfo(args: string[]) { + return { command: this.pythonPath, args }; + } +} diff --git a/src/test/datascience/gather/gather.unit.test.ts b/src/test/datascience/gather/gather.unit.test.ts index 7b8ba2576a95..0aad706cffca 100644 --- a/src/test/datascience/gather/gather.unit.test.ts +++ b/src/test/datascience/gather/gather.unit.test.ts @@ -132,13 +132,13 @@ suite('DataScience code gathering unit tests', () => { } ]; - dataScienceSettings.setup(d => d.gatherRules).returns(() => gatherRules); - dataScienceSettings.setup(d => d.enabled).returns(() => true); - dataScienceSettings.setup(d => d.defaultCellMarker).returns(() => '# %%'); - pythonSettings.setup(p => p.datascience).returns(() => dataScienceSettings.object); - configurationService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + dataScienceSettings.setup((d) => d.gatherRules).returns(() => gatherRules); + dataScienceSettings.setup((d) => d.enabled).returns(() => true); + dataScienceSettings.setup((d) => d.defaultCellMarker).returns(() => '# %%'); + pythonSettings.setup((p) => p.datascience).returns(() => dataScienceSettings.object); + configurationService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve('')); const gatherProvider = new GatherProvider( configurationService.object, diff --git a/src/test/datascience/intellisense.functional.test.tsx b/src/test/datascience/intellisense.functional.test.tsx index 859295e9151b..c5c296a7b4a4 100644 --- a/src/test/datascience/intellisense.functional.test.tsx +++ b/src/test/datascience/intellisense.functional.test.tsx @@ -116,7 +116,7 @@ suite('DataScience Intellisense tests', () => { runMountedTest( 'Simple autocomplete', - async wrapper => { + async (wrapper) => { // Create an interactive window so that it listens to the results. const interactiveWindow = await getOrCreateInteractiveWindow(ioc); await interactiveWindow.show(); @@ -139,7 +139,7 @@ suite('DataScience Intellisense tests', () => { runMountedTest( 'Multiple interpreters', - async wrapper => { + async (wrapper) => { // Create an interactive window so that it listens to the results. const interactiveWindow = await getOrCreateInteractiveWindow(ioc); await interactiveWindow.show(); @@ -161,7 +161,7 @@ suite('DataScience Intellisense tests', () => { const oldActive = await interpreterService.getActiveInterpreter(undefined); const interpreters = await interpreterService.getInterpreters(undefined); if (interpreters.length > 1 && oldActive) { - const firstOther = interpreters.filter(i => i.path !== oldActive.path); + const firstOther = interpreters.filter((i) => i.path !== oldActive.path); ioc.forceSettingsChanged(undefined, firstOther[0].path); const active = await interpreterService.getActiveInterpreter(undefined); assert.notDeepEqual(active, oldActive, 'Should have changed interpreter'); @@ -185,7 +185,7 @@ suite('DataScience Intellisense tests', () => { runMountedTest( 'Jupyter autocomplete', - async wrapper => { + async (wrapper) => { if (ioc.mockJupyter) { // This test only works when mocking. @@ -212,7 +212,7 @@ suite('DataScience Intellisense tests', () => { runMountedTest( 'Jupyter autocomplete not timeout', - async wrapper => { + async (wrapper) => { if (ioc.mockJupyter) { // This test only works when mocking. @@ -242,7 +242,7 @@ suite('DataScience Intellisense tests', () => { runMountedTest( 'Filtered Jupyter autocomplete, verify magic commands appear', - async wrapper => { + async (wrapper) => { if (ioc.mockJupyter) { // This test only works when mocking. @@ -270,7 +270,7 @@ suite('DataScience Intellisense tests', () => { runMountedTest( 'Filtered Jupyter autocomplete, verify magic commands are filtered', - async wrapper => { + async (wrapper) => { if (ioc.mockJupyter) { // This test only works when mocking. diff --git a/src/test/datascience/intellisense.unit.test.ts b/src/test/datascience/intellisense.unit.test.ts index ab2fcf8e56c5..627d4b3e7fba 100644 --- a/src/test/datascience/intellisense.unit.test.ts +++ b/src/test/datascience/intellisense.unit.test.ts @@ -65,8 +65,8 @@ suite('Data Science Intellisense Unit Tests', () => { notebookProvider = TypeMoq.Mock.ofType(); pythonSettings.jediEnabled = false; - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings); - workspaceService.setup(w => w.rootPath).returns(() => '/foo/bar'); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings); + workspaceService.setup((w) => w.rootPath).returns(() => '/foo/bar'); intellisenseProvider = new IntellisenseProvider( workspaceService.object, @@ -222,7 +222,7 @@ suite('Data Science Intellisense Unit Tests', () => { source: 'user' | 'undo' | 'redo' = 'user' ): Promise { if (cell) { - let index = cells.findIndex(c => c.id === cell.id); + let index = cells.findIndex((c) => c.id === cell.id); if (index < 0) { index = oldIndex; } else { @@ -272,9 +272,9 @@ suite('Data Science Intellisense Unit Tests', () => { ): Promise { const cell = createEmptyCell(id, null); cell.data.source = code; - const index = codeCellAbove ? cells.findIndex(c => c.id === codeCellAbove) : end ? cells.length : 0; + const index = codeCellAbove ? cells.findIndex((c) => c.id === codeCellAbove) : end ? cells.length : 0; if (source === 'undo') { - cells = cells.filter(c => c.id !== id); + cells = cells.filter((c) => c.id !== id); } else { cells.splice(index, 0, cell); } @@ -422,13 +422,13 @@ suite('Data Science Intellisense Unit Tests', () => { await addCode('y', 1, 2, 1); await addCode('s', 1, 3, 2); expect(getDocumentContents()).to.be.eq('import sys\nsys', 'Document not set after edit'); - await removeCell(cells.find(c => c.id === '1')); + await removeCell(cells.find((c) => c.id === '1')); expect(getDocumentContents()).to.be.eq('import sys\nsys', 'Removing a cell broken'); await addCell('import sys', '2'); expect(getDocumentContents()).to.be.eq('import sys\nimport sys\nsys', 'Adding a cell broken'); await addCell('import bar', '3'); expect(getDocumentContents()).to.be.eq('import sys\nimport sys\nimport bar\nsys', 'Adding a cell broken'); - await removeCell(cells.find(c => c.id === '1')); + await removeCell(cells.find((c) => c.id === '1')); expect(getDocumentContents()).to.be.eq('import sys\nimport sys\nimport bar\nsys', 'Removing a cell broken'); }); @@ -525,7 +525,7 @@ df df `; expect(getDocumentContents()).to.be.eq(afterInsert2, 'Insert2 cell failed'); - await removeCell(cells.find(c => c.id === '7')); + await removeCell(cells.find((c) => c.id === '7')); expect(getDocumentContents()).to.be.eq(afterInsert, 'Remove 2 cell failed'); await swapCells('0', '2'); const afterSwap = `foo @@ -591,7 +591,7 @@ c expect(getDocumentContents()).to.be.eq('', 'Remove all failed'); await removeAllCells('undo', oldCells); expect(getDocumentContents()).to.be.eq(startContent, 'Remove all undo failed'); - const cell = cells.find(c => c.id === '1'); + const cell = cells.find((c) => c.id === '1'); const oldIndex = await removeCell(cell); const afterRemove = `a=1 a diff --git a/src/test/datascience/interactive-ipynb/nativeEditorProvider.unit.test.ts b/src/test/datascience/interactive-ipynb/nativeEditorProvider.unit.test.ts index 49fbb8c939b2..acbb9160010b 100644 --- a/src/test/datascience/interactive-ipynb/nativeEditorProvider.unit.test.ts +++ b/src/test/datascience/interactive-ipynb/nativeEditorProvider.unit.test.ts @@ -40,48 +40,48 @@ suite('Data Science - Native Editor Provider', () => { storage = typemoq.Mock.ofType(); customEditorService = typemoq.Mock.ofType(); panel = typemoq.Mock.ofType(); - panel.setup(e => (e as any).then).returns(() => undefined); + panel.setup((e) => (e as any).then).returns(() => undefined); }); function createNotebookProvider() { editor = typemoq.Mock.ofType(); when(configService.getSettings(anything())).thenReturn({ datascience: { useNotebookEditor: true } } as any); - editor.setup(e => e.closed).returns(() => new EventEmitter().event); - editor.setup(e => e.executed).returns(() => new EventEmitter().event); - editor.setup(e => (e as any).then).returns(() => undefined); - storage.setup(e => (e as any).then).returns(() => undefined); + editor.setup((e) => e.closed).returns(() => new EventEmitter().event); + editor.setup((e) => e.executed).returns(() => new EventEmitter().event); + editor.setup((e) => (e as any).then).returns(() => undefined); + storage.setup((e) => (e as any).then).returns(() => undefined); storage - .setup(s => s.load(typemoq.It.isAny(), typemoq.It.isAny())) - .returns(f => { + .setup((s) => s.load(typemoq.It.isAny(), typemoq.It.isAny())) + .returns((f) => { storageFile = f; return Promise.resolve(storage.object); }); - storage.setup(s => s.file).returns(() => storageFile); + storage.setup((s) => s.file).returns(() => storageFile); when(svcContainer.get(INotebookEditor)).thenReturn(editor.object); when(svcContainer.get(INotebookStorage)).thenReturn(storage.object); - customEditorService.setup(e => (e as any).then).returns(() => undefined); + customEditorService.setup((e) => (e as any).then).returns(() => undefined); customEditorService - .setup(c => c.registerCustomEditorProvider(typemoq.It.isAny(), typemoq.It.isAny(), typemoq.It.isAny())) + .setup((c) => c.registerCustomEditorProvider(typemoq.It.isAny(), typemoq.It.isAny(), typemoq.It.isAny())) .returns((_a1, _a2, _a3) => { return { dispose: noop }; }); customEditorService - .setup(c => c.openEditor(typemoq.It.isAny())) - .returns(async f => { + .setup((c) => c.openEditor(typemoq.It.isAny())) + .returns(async (f) => { const doc = typemoq.Mock.ofType(); - doc.setup(d => d.uri).returns(() => f); + doc.setup((d) => d.uri).returns(() => f); return registeredProvider.resolveCustomEditor(doc.object, panel.object); }); editor - .setup(e => e.load(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((e) => e.load(typemoq.It.isAny(), typemoq.It.isAny())) .returns((s, _p) => { file = s.file; return Promise.resolve(); }); - editor.setup(e => e.show()).returns(() => Promise.resolve()); - editor.setup(e => e.file).returns(() => file); + editor.setup((e) => e.show()).returns(() => Promise.resolve()); + editor.setup((e) => e.file).returns(() => file); registeredProvider = new NativeEditorProvider( instance(svcContainer), diff --git a/src/test/datascience/interactive-ipynb/nativeEditorStorage.unit.test.ts b/src/test/datascience/interactive-ipynb/nativeEditorStorage.unit.test.ts index af8bb24ed675..c4c508af62a6 100644 --- a/src/test/datascience/interactive-ipynb/nativeEditorStorage.unit.test.ts +++ b/src/test/datascience/interactive-ipynb/nativeEditorStorage.unit.test.ts @@ -293,7 +293,7 @@ suite('Data Science - Native Editor Storage', () => { const settingsChangedEvent = new EventEmitter(); context - .setup(c => c.globalStoragePath) + .setup((c) => c.globalStoragePath) .returns(() => path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience', 'WorkspaceDir')); when(settings.onDidChange).thenReturn(settingsChangedEvent.event); @@ -338,7 +338,7 @@ suite('Data Science - Native Editor Storage', () => { lastWriteFileValue = baseFile; wroteToFileEvent = new EventEmitter(); fileSystem - .setup(f => f.writeFile(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((f) => f.writeFile(typemoq.It.isAny(), typemoq.It.isAny())) .returns((a1, a2) => { if (a1.includes(`${testIndex}.ipynb`)) { lastWriteFileValue = a2; @@ -347,8 +347,8 @@ suite('Data Science - Native Editor Storage', () => { return Promise.resolve(); }); fileSystem - .setup(f => f.readFile(typemoq.It.isAny())) - .returns(_a1 => { + .setup((f) => f.readFile(typemoq.It.isAny())) + .returns((_a1) => { return Promise.resolve(lastWriteFileValue); }); @@ -365,7 +365,7 @@ suite('Data Science - Native Editor Storage', () => { teardown(() => { globalMemento.clear(); sinon.reset(); - disposables.forEach(d => d.dispose()); + disposables.forEach((d) => d.dispose()); }); function insertCell(index: number, code: string) { @@ -501,7 +501,7 @@ suite('Data Science - Native Editor Storage', () => { test('Opening file with global storage but no global file will still open with old contents', async () => { // This test is really for making sure when a user upgrades to a new extension, we still have their old storage const file = Uri.parse('file:///foo.ipynb'); - fileSystem.setup(f => f.stat(typemoq.It.isAny())).returns(() => Promise.resolve({ mtime: 1 } as any)); + fileSystem.setup((f) => f.stat(typemoq.It.isAny())).returns(() => Promise.resolve({ mtime: 1 } as any)); // Initially nothing in memento expect(globalMemento.get(`notebook-storage-${file.toString()}`)).to.be.undefined; @@ -524,7 +524,7 @@ suite('Data Science - Native Editor Storage', () => { // This test is really for making sure when a user upgrades to a new extension, we still have their old storage const file = Uri.parse('file:///foo.ipynb'); - fileSystem.setup(f => f.stat(typemoq.It.isAny())).returns(() => Promise.resolve({ mtime: 1 } as any)); + fileSystem.setup((f) => f.stat(typemoq.It.isAny())).returns(() => Promise.resolve({ mtime: 1 } as any)); // Initially nothing in memento expect(globalMemento.get(`notebook-storage-${file.toString()}`)).to.be.undefined; diff --git a/src/test/datascience/interactiveWindow.functional.test.tsx b/src/test/datascience/interactiveWindow.functional.test.tsx index 83640bebe4eb..e5777c8a0a99 100644 --- a/src/test/datascience/interactiveWindow.functional.test.tsx +++ b/src/test/datascience/interactiveWindow.functional.test.tsx @@ -1,1089 +1,1080 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import * as assert from 'assert'; -import * as fs from 'fs-extra'; -import { parse } from 'node-html-parser'; -import * as os from 'os'; -import * as path from 'path'; -import * as TypeMoq from 'typemoq'; -import { Disposable, Selection, TextDocument, TextEditor, Uri } from 'vscode'; - -import { ReactWrapper } from 'enzyme'; -import { IApplicationShell, IDocumentManager } from '../../client/common/application/types'; -import { IDataScienceSettings } from '../../client/common/types'; -import { createDeferred, waitForPromise } from '../../client/common/utils/async'; -import { noop } from '../../client/common/utils/misc'; -import { EXTENSION_ROOT_DIR } from '../../client/constants'; -import { generateCellsFromDocument } from '../../client/datascience/cellFactory'; -import { EditorContexts } from '../../client/datascience/constants'; -import { InteractiveWindowMessages } from '../../client/datascience/interactive-common/interactiveWindowTypes'; -import { InteractiveWindow } from '../../client/datascience/interactive-window/interactiveWindow'; -import { IInteractiveWindowProvider } from '../../client/datascience/types'; -import { IInterpreterService } from '../../client/interpreter/contracts'; -import { concatMultilineStringInput } from '../../datascience-ui/common'; -import { InteractivePanel } from '../../datascience-ui/history-react/interactivePanel'; -import { IKeyboardEvent } from '../../datascience-ui/react-common/event'; -import { ImageButton } from '../../datascience-ui/react-common/imageButton'; -import { MonacoEditor } from '../../datascience-ui/react-common/monacoEditor'; -import { DataScienceIocContainer } from './dataScienceIocContainer'; -import { createDocument } from './editor-integration/helpers'; -import { defaultDataScienceSettings } from './helpers'; -import { - addCode, - closeInteractiveWindow, - getInteractiveCellResults, - getOrCreateInteractiveWindow, - runMountedTest -} from './interactiveWindowTestHelpers'; -import { MockDocumentManager } from './mockDocumentManager'; -import { MockEditor } from './mockTextEditor'; -import { createMessageEvent, waitForUpdate } from './reactHelpers'; -import { - addContinuousMockData, - addInputMockData, - addMockData, - CellInputState, - CellPosition, - enterEditorKey, - enterInput, - escapePath, - findButton, - getInteractiveEditor, - getLastOutputCell, - mountWebView, - srcDirectory, - submitInput, - toggleCellExpansion, - typeCode, - verifyHtmlOnCell, - verifyLastCellInputState, - waitForMessage, - waitForMessageResponse -} from './testHelpers'; - -// tslint:disable:max-func-body-length trailing-comma no-any no-multiline-string -suite('DataScience Interactive Window output tests', () => { - const disposables: Disposable[] = []; - let ioc: DataScienceIocContainer; - const defaultCellMarker = '# %%'; - - setup(async () => { - ioc = new DataScienceIocContainer(); - ioc.registerDataScienceTypes(); - return ioc.activate(); - }); - - teardown(async () => { - for (const disposable of disposables) { - if (!disposable) { - continue; - } - // tslint:disable-next-line:no-any - const promise = disposable.dispose() as Promise; - if (promise) { - await promise; - } - } - await ioc.dispose(); - }); - - async function forceSettingsChange(newSettings: IDataScienceSettings) { - const window = await getOrCreateInteractiveWindow(ioc); - await window.show(); - const update = waitForMessage(ioc, InteractiveWindowMessages.SettingsUpdated); - ioc.forceSettingsChanged(undefined, ioc.getSettings().pythonPath, newSettings); - return update; - } - - function simulateKeyPressOnEditor( - editorControl: ReactWrapper, React.Component> | undefined, - keyboardEvent: Partial & { code: string } - ) { - enterEditorKey(editorControl, keyboardEvent); - } - - // Uncomment this to debug hangs on exit - // suiteTeardown(() => { - // asyncDump(); - // }); - - runMountedTest( - 'Simple text', - async wrapper => { - await addCode(ioc, wrapper, 'a=1\na'); - - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Clear output', - async wrapper => { - const text = `from IPython.display import clear_output -for i in range(10): - clear_output() - print("Hello World {0}!".format(i)) -`; - addContinuousMockData(ioc, text, async _c => { - return { - result: 'Hello World 9!', - haveMore: false - }; - }); - await addCode(ioc, wrapper, text); - - verifyHtmlOnCell(wrapper, 'InteractiveCell', '
Hello World 9!', CellPosition.Last); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Hide inputs', - async wrapper => { - await forceSettingsChange({ ...defaultDataScienceSettings(), showCellInputCode: false }); - - await addCode(ioc, wrapper, 'a=1\na'); - - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Hidden); - - // Add a cell without output, this cell should not show up at all - addMockData(ioc, 'a=1', undefined, 'text/plain'); - await addCode(ioc, wrapper, 'a=1'); - - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.First); - verifyHtmlOnCell(wrapper, 'InteractiveCell', undefined, CellPosition.Last); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Show inputs', - async wrapper => { - await forceSettingsChange({ ...defaultDataScienceSettings() }); - - await addCode(ioc, wrapper, 'a=1\na'); - - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Collapsed); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Expand inputs', - async wrapper => { - await forceSettingsChange({ ...defaultDataScienceSettings(), collapseCellInputCodeByDefault: false }); - await addCode(ioc, wrapper, 'a=1\na'); - - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Expanded); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Ctrl + 1/Ctrl + 2', - async wrapper => { - // Create an interactive window so that it listens to the results. - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - await interactiveWindow.show(); - - // Type in the input box - const editor = getInteractiveEditor(wrapper); - typeCode(editor, 'a=1\na'); - - // Give focus to a random div - const reactDiv = wrapper - .find('div') - .first() - .getDOMNode(); - - const domDiv = reactDiv.querySelector('div'); - - if (domDiv && ioc.postMessage) { - domDiv.tabIndex = -1; - domDiv.focus(); - - // send the ctrl + 1/2 message, this should put focus back on the input box - const message = createMessageEvent({ type: InteractiveWindowMessages.Activate, payload: undefined }); - ioc.postMessage(message); - - // Then enter press shift + enter on the active element - const activeElement = document.activeElement; - if (activeElement) { - await submitInput(ioc, activeElement as HTMLTextAreaElement); - } - } - - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Escape/Ctrl+U', - async wrapper => { - // Create an interactive window so that it listens to the results. - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - await interactiveWindow.show(); - - // Type in the input box - const editor = getInteractiveEditor(wrapper); - typeCode(editor, 'a=1\na'); - - // Check code is what we think it is - const reactEditor = editor.instance() as MonacoEditor; - assert.equal(reactEditor.state.model?.getValue().replace(/\r/g, ''), 'a=1\na'); - - // Send escape - simulateKeyPressOnEditor(editor, { code: 'Escape' }); - assert.equal(reactEditor.state.model?.getValue().replace(/\r/g, ''), ''); - - typeCode(editor, 'a=1\na'); - assert.equal(reactEditor.state.model?.getValue().replace(/\r/g, ''), 'a=1\na'); - - simulateKeyPressOnEditor(editor, { code: 'KeyU', ctrlKey: true }); - assert.equal(reactEditor.state.model?.getValue().replace(/\r/g, ''), ''); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Click outside cells sets focus to input box', - async wrapper => { - // Create an interactive window so that it listens to the results. - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - await interactiveWindow.show(); - - // Type in the input box - const editor = getInteractiveEditor(wrapper); - typeCode(editor, 'a=1\na'); - - // Give focus to a random div - const reactDiv = wrapper - .find('div') - .first() - .getDOMNode(); - - const domDiv = reactDiv.querySelector('div'); - - if (domDiv && ioc.postMessage) { - domDiv.tabIndex = -1; - domDiv.focus(); - - wrapper - .find('section#main-panel-footer') - .first() - .simulate('click'); - - // Then enter press shift + enter on the active element - const activeElement = document.activeElement; - if (activeElement) { - await submitInput(ioc, activeElement as HTMLTextAreaElement); - } - } - - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Collapse / expand cell', - async wrapper => { - await forceSettingsChange({ ...defaultDataScienceSettings() }); - await addCode(ioc, wrapper, 'a=1\na'); - - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Collapsed); - - toggleCellExpansion(wrapper, 'InteractiveCell'); - - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Expanded); - - toggleCellExpansion(wrapper, 'InteractiveCell'); - - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Collapsed); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Hide / show cell', - async wrapper => { - await forceSettingsChange({ ...defaultDataScienceSettings() }); - await addCode(ioc, wrapper, 'a=1\na'); - - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Collapsed); - - // Hide the inputs and verify - await forceSettingsChange({ ...defaultDataScienceSettings(), showCellInputCode: false }); - - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Hidden); - - // Show the inputs and verify - await forceSettingsChange({ ...defaultDataScienceSettings(), showCellInputCode: true }); - - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); - verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Collapsed); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Mime Types', - async wrapper => { - const badPanda = `import pandas as pd -df = pd.read("${escapePath(path.join(srcDirectory(), 'DefaultSalesReport.csv'))}") -df.head()`; - const goodPanda = `import pandas as pd -df = pd.read_csv("${escapePath(path.join(srcDirectory(), 'DefaultSalesReport.csv'))}") -df.head()`; - const matPlotLib = - 'import matplotlib.pyplot as plt\r\nimport numpy as np\r\nx = np.linspace(0,20,100)\r\nplt.plot(x, np.sin(x))\r\nplt.show()'; - const matPlotLibResults = 'img'; - const spinningCursor = `import sys -import time - -def spinning_cursor(): - while True: - for cursor in '|/-\\\\': - yield cursor - -spinner = spinning_cursor() -for _ in range(50): - sys.stdout.write(next(spinner)) - sys.stdout.flush() - time.sleep(0.1) - sys.stdout.write('\\r')`; - - addMockData(ioc, badPanda, `pandas has no attribute 'read'`, 'text/html', 'error'); - addMockData(ioc, goodPanda, `A table`, 'text/html'); - addMockData(ioc, matPlotLib, matPlotLibResults, 'text/html'); - const cursors = ['|', '/', '-', '\\']; - let cursorPos = 0; - let loops = 3; - addContinuousMockData(ioc, spinningCursor, async _c => { - const result = `${cursors[cursorPos]}\r`; - cursorPos += 1; - if (cursorPos >= cursors.length) { - cursorPos = 0; - loops -= 1; - } - return Promise.resolve({ result: result, haveMore: loops > 0 }); - }); - - await addCode(ioc, wrapper, badPanda, true); - verifyHtmlOnCell(wrapper, 'InteractiveCell', `has no attribute 'read'`, CellPosition.Last); - - await addCode(ioc, wrapper, goodPanda); - verifyHtmlOnCell(wrapper, 'InteractiveCell', ``, CellPosition.Last); - - await addCode(ioc, wrapper, matPlotLib); - verifyHtmlOnCell(wrapper, 'InteractiveCell', /img|Figure/, CellPosition.Last); - - await addCode(ioc, wrapper, spinningCursor); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '
', CellPosition.Last); - - addContinuousMockData(ioc, 'len?', async _c => { - return Promise.resolve({ - result: `Signature: len(obj, /) -Docstring: Return the number of items in a container. -Type: builtin_function_or_method`, - haveMore: false - }); - }); - await addCode(ioc, wrapper, 'len?'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', 'len', CellPosition.Last); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Undo/redo commands', - async wrapper => { - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - - // Get a cell into the list - await addCode(ioc, wrapper, 'a=1\na'); - - // Now verify if we undo, we have no cells - let afterUndo = await getInteractiveCellResults(ioc, wrapper, () => { - interactiveWindow.undoCells(); - return Promise.resolve(); - }); - - assert.equal(afterUndo.length, 1, `Undo should remove cells + ${afterUndo.debug()}`); - - // Redo should put the cells back - const afterRedo = await getInteractiveCellResults(ioc, wrapper, () => { - interactiveWindow.redoCells(); - return Promise.resolve(); - }); - assert.equal(afterRedo.length, 2, 'Redo should put cells back'); - - // Get another cell into the list - const afterAdd = await addCode(ioc, wrapper, 'a=1\na'); - assert.equal(afterAdd.length, 3, 'Second cell did not get added'); - - // Clear everything - const afterClear = await getInteractiveCellResults(ioc, wrapper, () => { - interactiveWindow.removeAllCells(); - return Promise.resolve(); - }); - assert.equal(afterClear.length, 1, "Clear didn't work"); - - // Undo should put them back - afterUndo = await getInteractiveCellResults(ioc, wrapper, () => { - interactiveWindow.undoCells(); - return Promise.resolve(); - }); - - assert.equal(afterUndo.length, 3, `Undo should put cells back`); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Click buttons', - async wrapper => { - // Goto source should cause the visible editor to be picked as long as its filename matches - const showedEditor = createDeferred(); - const textEditors: TextEditor[] = []; - const docManager = TypeMoq.Mock.ofType(); - const visibleEditor = TypeMoq.Mock.ofType(); - const dummyDocument = TypeMoq.Mock.ofType(); - dummyDocument.setup(d => d.fileName).returns(() => Uri.file('foo.py').fsPath); - visibleEditor.setup(v => v.show()).returns(() => showedEditor.resolve()); - visibleEditor.setup(v => v.revealRange(TypeMoq.It.isAny())).returns(noop); - visibleEditor.setup(v => v.document).returns(() => dummyDocument.object); - textEditors.push(visibleEditor.object); - docManager.setup(a => a.visibleTextEditors).returns(() => textEditors); - ioc.serviceManager.rebindInstance(IDocumentManager, docManager.object); - - // Get a cell into the list - await addCode(ioc, wrapper, 'a=1\na'); - - // 'Click' the buttons in the react control - const undo = findButton(wrapper, InteractivePanel, 2); - const redo = findButton(wrapper, InteractivePanel, 1); - const clear = findButton(wrapper, InteractivePanel, 0); - - // Now verify if we undo, we have no cells - let afterUndo = await getInteractiveCellResults(ioc, wrapper, () => { - undo!.simulate('click'); - return Promise.resolve(); - }); - - assert.equal(afterUndo.length, 1, `Undo should remove cells`); - - // Redo should put the cells back - const afterRedo = await getInteractiveCellResults(ioc, wrapper, async () => { - redo!.simulate('click'); - return Promise.resolve(); - }); - assert.equal(afterRedo.length, 2, 'Redo should put cells back'); - - // Get another cell into the list - const afterAdd = await addCode(ioc, wrapper, 'a=1\na'); - assert.equal(afterAdd.length, 3, 'Second cell did not get added'); - - // Clear everything - const afterClear = await getInteractiveCellResults(ioc, wrapper, async () => { - clear!.simulate('click'); - return Promise.resolve(); - }); - assert.equal(afterClear.length, 1, "Clear didn't work"); - - // Undo should put them back - afterUndo = await getInteractiveCellResults(ioc, wrapper, async () => { - undo!.simulate('click'); - return Promise.resolve(); - }); - - assert.equal(afterUndo.length, 3, `Undo should put cells back`); - - // find the buttons on the cell itself - const ImageButtons = afterUndo.at(afterUndo.length - 2).find(ImageButton); - assert.equal(ImageButtons.length, 4, 'Cell buttons not found'); - - const goto = ImageButtons.at(1); - const deleteButton = ImageButtons.at(3); - - // Make sure goto works - await waitForMessageResponse(ioc, () => goto.simulate('click')); - await waitForPromise(showedEditor.promise, 1000); - assert.ok(showedEditor.resolved, 'Goto source is not jumping to editor'); - - // Make sure delete works - const afterDelete = await getInteractiveCellResults(ioc, wrapper, async () => { - deleteButton.simulate('click'); - return Promise.resolve(); - }); - assert.equal(afterDelete.length, 2, `Delete should remove a cell`); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Export', - async wrapper => { - // Export should cause the export dialog to come up. Remap appshell so we can check - const dummyDisposable = { - dispose: () => { - return; - } - }; - let exportCalled = false; - const appShell = TypeMoq.Mock.ofType(); - appShell - .setup(a => a.showErrorMessage(TypeMoq.It.isAnyString())) - .returns(e => { - throw e; - }); - appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve('')); - appShell - .setup(a => a.showSaveDialog(TypeMoq.It.isAny())) - .returns(() => { - exportCalled = true; - return Promise.resolve(undefined); - }); - appShell.setup(a => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); - ioc.serviceManager.rebindInstance(IApplicationShell, appShell.object); - - // Make sure to create the interactive window after the rebind or it gets the wrong application shell. - await addCode(ioc, wrapper, 'a=1\na'); - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - - // Export should cause exportCalled to change to true - await waitForMessageResponse(ioc, () => interactiveWindow.exportCells()); - assert.equal(exportCalled, true, 'Export is not being called during export'); - - // Remove the cell - const exportButton = findButton(wrapper, InteractivePanel, 6); - const undo = findButton(wrapper, InteractivePanel, 2); - - // Now verify if we undo, we have no cells - const afterUndo = await getInteractiveCellResults(ioc, wrapper, () => { - undo!.simulate('click'); - return Promise.resolve(); - }); - - assert.equal(afterUndo.length, 1, 'Undo should remove cells'); - - // Then verify we cannot click the button (it should be disabled) - exportCalled = false; - const response = waitForMessageResponse(ioc, () => exportButton!.simulate('click')); - await waitForPromise(response, 100); - assert.equal(exportCalled, false, 'Export should not be called when no cells visible'); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Multiple Interpreters', - async (wrapper, context) => { - if (!ioc.mockJupyter) { - const interactiveWindowProvider = ioc.get(IInteractiveWindowProvider); - const interpreterService = ioc.get(IInterpreterService); - const interpreters = await ioc.getJupyterInterpreters(); - if (interpreters.length < 2) { - // tslint:disable-next-line: no-console - console.log( - 'Multiple interpreters skipped because local machine does not have more than one jupyter environment' - ); - context.skip(); - return; - } - const window = (await interactiveWindowProvider.getOrCreateActive()) as InteractiveWindow; - await addCode(ioc, wrapper, 'a=1\na'); - const activeInterpreter = await interpreterService.getActiveInterpreter( - await window.getOwningResource() - ); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - assert.equal( - window.notebook!.getMatchingInterpreter()?.path, - activeInterpreter?.path, - 'Active intrepreter not used to launch notebook' - ); - await closeInteractiveWindow(window, wrapper); - - // Add another python path - const secondUri = Uri.file('bar.py'); - ioc.addResourceToFolder(secondUri, path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience2')); - ioc.forceSettingsChanged( - secondUri, - interpreters.filter(i => i.path !== activeInterpreter?.path)[0].path - ); - - // Then open a second time and make sure it uses this new path - const newWrapper = mountWebView(ioc, 'interactive'); - assert.ok(newWrapper, 'Could not mount a second time'); - const newWindow = (await interactiveWindowProvider.getOrCreateActive()) as InteractiveWindow; - await addCode(ioc, newWrapper, 'a=1\na', false, secondUri); - assert.notEqual( - newWindow.notebook!.getMatchingInterpreter()?.path, - activeInterpreter?.path, - 'Active intrepreter used to launch second notebook when it should not have' - ); - verifyHtmlOnCell(newWrapper, 'InteractiveCell', '1', CellPosition.Last); - } else { - context.skip(); - } - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Dispose test', - async () => { - // tslint:disable-next-line:no-any - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - await interactiveWindow.show(); // Have to wait for the load to finish - await interactiveWindow.dispose(); - // tslint:disable-next-line:no-any - const h2 = await getOrCreateInteractiveWindow(ioc); - // Check equal and then dispose so the test goes away - const equal = Object.is(interactiveWindow, h2); - await h2.show(); - assert.ok(!equal, 'Disposing is not removing the active interactive window'); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Editor Context', - async wrapper => { - // Before we have any cells, verify our contexts are not set - assert.equal( - ioc.getContext(EditorContexts.HaveInteractive), - false, - 'Should not have interactive before starting' - ); - assert.equal( - ioc.getContext(EditorContexts.HaveInteractiveCells), - false, - 'Should not have interactive cells before starting' - ); - assert.equal( - ioc.getContext(EditorContexts.HaveRedoableCells), - false, - 'Should not have redoable before starting' - ); - - // Verify we can send different commands to the UI and it will respond - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - - // Get an update promise so we can wait for the add code - const updatePromise = waitForUpdate(wrapper, InteractivePanel); - - // Send some code to the interactive window - await interactiveWindow.addCode('a=1\na', Uri.file('foo.py').fsPath, 2); - - // Wait for the render to go through - await updatePromise; - - // Now we should have the 3 editor contexts - assert.equal( - ioc.getContext(EditorContexts.HaveInteractive), - true, - 'Should have interactive after starting' - ); - assert.equal( - ioc.getContext(EditorContexts.HaveInteractiveCells), - true, - 'Should have interactive cells after starting' - ); - assert.equal( - ioc.getContext(EditorContexts.HaveRedoableCells), - false, - 'Should not have redoable after starting' - ); - - // Setup a listener for context change events. We have 3 separate contexts, so we have to wait for all 3. - let count = 0; - let deferred = createDeferred(); - const eventDispose = ioc.onContextSet(_a => { - count += 1; - if (count >= 3) { - deferred.resolve(); - } - }); - disposables.push(eventDispose); - - // Create a method that resets the waiting - const resetWaiting = () => { - count = 0; - deferred = createDeferred(); - }; - - // Now send an undo command. This should change the state, so use our waitForInfo promise instead - resetWaiting(); - interactiveWindow.undoCells(); - await waitForPromise(deferred.promise, 2000); - assert.ok(deferred.resolved, 'Never got update to state'); - assert.equal( - ioc.getContext(EditorContexts.HaveInteractiveCells), - false, - 'Should not have interactive cells after undo as sysinfo is ignored' - ); - assert.equal(ioc.getContext(EditorContexts.HaveRedoableCells), true, 'Should have redoable after undo'); - - resetWaiting(); - interactiveWindow.redoCells(); - await waitForPromise(deferred.promise, 2000); - assert.ok(deferred.resolved, 'Never got update to state'); - assert.equal( - ioc.getContext(EditorContexts.HaveInteractiveCells), - true, - 'Should have interactive cells after redo' - ); - assert.equal( - ioc.getContext(EditorContexts.HaveRedoableCells), - false, - 'Should not have redoable after redo' - ); - - resetWaiting(); - interactiveWindow.removeAllCells(); - await waitForPromise(deferred.promise, 2000); - assert.ok(deferred.resolved, 'Never got update to state'); - assert.equal( - ioc.getContext(EditorContexts.HaveInteractiveCells), - false, - 'Should not have interactive cells after delete' - ); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Simple input', - async wrapper => { - // Create an interactive window so that it listens to the results. - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - await interactiveWindow.show(); - - // Then enter some code. - await enterInput(wrapper, ioc, 'a=1\na', 'InteractiveCell'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Copy to source input', - async wrapper => { - const showedEditor = createDeferred(); - ioc.addDocument('# No cells here', 'foo.py'); - const docManager = ioc.get(IDocumentManager) as MockDocumentManager; - const editor = (await docManager.showTextDocument(docManager.textDocuments[0])) as MockEditor; - editor.setRevealCallback(() => showedEditor.resolve()); - - // Create an interactive window so that it listens to the results. - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - await interactiveWindow.show(); - - // Then enter some code. - await enterInput(wrapper, ioc, 'a=1\na', 'InteractiveCell'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - const ImageButtons = getLastOutputCell(wrapper, 'InteractiveCell').find(ImageButton); - assert.equal(ImageButtons.length, 4, 'Cell buttons not found'); - const copyToSource = ImageButtons.at(2); - - // Then click the copy to source button - await waitForMessageResponse(ioc, () => copyToSource.simulate('click')); - await waitForPromise(showedEditor.promise, 100); - assert.ok(showedEditor.resolved, 'Copy to source is not adding code to the editor'); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Multiple input', - async wrapper => { - // Create an interactive window so that it listens to the results. - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - await interactiveWindow.show(); - - // Then enter some code. - await enterInput(wrapper, ioc, 'a=1\na', 'InteractiveCell'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - - // Then delete the node - const lastCell = getLastOutputCell(wrapper, 'InteractiveCell'); - const ImageButtons = lastCell.find(ImageButton); - assert.equal(ImageButtons.length, 4, 'Cell buttons not found'); - const deleteButton = ImageButtons.at(3); - - // Make sure delete works - const afterDelete = await getInteractiveCellResults(ioc, wrapper, async () => { - deleteButton.simulate('click'); - return Promise.resolve(); - }); - assert.equal(afterDelete.length, 1, `Delete should remove a cell`); - - // Should be able to enter again - await enterInput(wrapper, ioc, 'a=1\na', 'InteractiveCell'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - - // Try a 3rd time with some new input - addMockData(ioc, 'print("hello")', 'hello'); - await enterInput(wrapper, ioc, 'print("hello', 'InteractiveCell'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', 'hello', CellPosition.Last); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Restart with session failure', - async wrapper => { - // Prime the pump - await addCode(ioc, wrapper, 'a=1\na'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - - // Then something that could possibly timeout - addContinuousMockData(ioc, 'import time\r\ntime.sleep(1000)', _c => { - return Promise.resolve({ result: '', haveMore: true }); - }); - - // Then get our mock session and force it to not restart ever. - if (ioc.mockJupyter) { - const currentSession = ioc.mockJupyter.getCurrentSession(); - if (currentSession) { - currentSession.prolongRestarts(); - } - } - - // Then try executing our long running cell and restarting in the middle - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - const executed = createDeferred(); - // We have to wait until the execute goes through before we reset. - interactiveWindow.onExecutedCode(() => executed.resolve()); - const added = interactiveWindow.addCode('import time\r\ntime.sleep(1000)', Uri.file('foo').fsPath, 0); - await executed.promise; - await interactiveWindow.restartKernel(); - await added; - - // Now see if our wrapper still works. Interactive window should have forced a restart - await interactiveWindow.addCode('a=1\na', Uri.file('foo').fsPath, 0); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'LiveLossPlot', - async wrapper => { - // Only run this test when not mocking. Too complicated to mimic otherwise - if (!ioc.mockJupyter) { - // Load all of our cells - const testFile = path.join(srcDirectory(), 'liveloss.py'); - const version = 1; - const inputText = await fs.readFile(testFile, 'utf-8'); - const document = createDocument(inputText, testFile, version, TypeMoq.Times.atLeastOnce(), true); - const cells = generateCellsFromDocument(document.object); - assert.ok(cells, 'No cells generated'); - assert.equal(cells.length, 2, 'Not enough cells generated'); - - // Run the first cell - await addCode(ioc, wrapper, concatMultilineStringInput(cells[0].data.source)); - - // Last cell should generate a series of updates. Verify we end up with a single image - await addCode(ioc, wrapper, concatMultilineStringInput(cells[1].data.source)); - const cell = getLastOutputCell(wrapper, 'InteractiveCell'); - - const output = cell!.find('div.cell-output'); - assert.ok(output.length > 0, 'No output cell found'); - const outHtml = output.html(); - - const root = parse(outHtml) as any; - const png = root.querySelectorAll('img') as HTMLElement[]; - assert.ok(png, 'No pngs found'); - assert.equal(png.length, 1, 'Wrong number of pngs'); - } - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Gather code run from text editor', - async wrapper => { - ioc.getSettings().datascience.enableGather = true; - ioc.getSettings().datascience.gatherToScript = true; - // Enter some code. - const code = `${defaultCellMarker}\na=1\na`; - await addCode(ioc, wrapper, code); - addMockData(ioc, code, undefined); - const ImageButtons = getLastOutputCell(wrapper, 'InteractiveCell').find(ImageButton); // This isn't rendering correctly - assert.equal(ImageButtons.length, 4, 'Cell buttons not found'); - const gatherCode = ImageButtons.at(0); - - // Then click the gather code button - await waitForMessageResponse(ioc, () => gatherCode.simulate('click')); - const docManager = ioc.get(IDocumentManager) as MockDocumentManager; - assert.notEqual(docManager.activeTextEditor, undefined); - if (docManager.activeTextEditor) { - // Ignore white space. - assert.equal( - docManager.activeTextEditor.document.getText().trim(), - `${defaultCellMarker} [markdown]\n## Gather not available` - ); - } - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Gather code run from input box', - async wrapper => { - ioc.getSettings().datascience.enableGather = true; - ioc.getSettings().datascience.gatherToScript = true; - // Create an interactive window so that it listens to the results. - const interactiveWindow = await getOrCreateInteractiveWindow(ioc); - await interactiveWindow.show(); - - // Then enter some code. - await enterInput(wrapper, ioc, 'a=1\na', 'InteractiveCell'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - const ImageButtons = getLastOutputCell(wrapper, 'InteractiveCell').find(ImageButton); - assert.equal(ImageButtons.length, 4, 'Cell buttons not found'); - const gatherCode = ImageButtons.at(0); - - // Then click the gather code button - await waitForMessageResponse(ioc, () => gatherCode.simulate('click')); - const docManager = ioc.get(IDocumentManager) as MockDocumentManager; - assert.notEqual(docManager.activeTextEditor, undefined); - if (docManager.activeTextEditor) { - // Ignore whitespace - assert.equal( - docManager.activeTextEditor.document.getText().trim(), - `${defaultCellMarker} [markdown]\n## Gather not available` - ); - } - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Copy back to source', - async _wrapper => { - ioc.addDocument(`${defaultCellMarker}${os.EOL}print("bar")`, 'foo.py'); - const docManager = ioc.get(IDocumentManager); - docManager.showTextDocument(docManager.textDocuments[0]); - const window = (await getOrCreateInteractiveWindow(ioc)) as InteractiveWindow; - await window.show(); - await window.copyCode({ source: 'print("baz")' }); - assert.equal( - docManager.textDocuments[0].getText(), - `${defaultCellMarker}${os.EOL}print("baz")${os.EOL}${defaultCellMarker}${os.EOL}print("bar")`, - 'Text not inserted' - ); - const activeEditor = docManager.activeTextEditor as MockEditor; - activeEditor.selection = new Selection(1, 2, 1, 2); - await window.copyCode({ source: 'print("baz")' }); - assert.equal( - docManager.textDocuments[0].getText(), - `${defaultCellMarker}${os.EOL}${defaultCellMarker}${os.EOL}print("baz")${os.EOL}${defaultCellMarker}${os.EOL}print("baz")${os.EOL}${defaultCellMarker}${os.EOL}print("bar")`, - 'Text not inserted' - ); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Limit text output', - async wrapper => { - ioc.getSettings().datascience.textOutputLimit = 8; - - // Output should be trimmed to just two lines of output - const code = `print("hello\\nworld\\nhow\\nare\\nyou")`; - addMockData(ioc, code, 'are\nyou\n'); - await addCode(ioc, wrapper, code); - - verifyHtmlOnCell(wrapper, 'InteractiveCell', '>are\nyou', CellPosition.Last); - }, - () => { - return ioc; - } - ); - - runMountedTest( - 'Type in input', - async wrapper => { - const appShell = TypeMoq.Mock.ofType(); - appShell - .setup(a => a.showInputBox(TypeMoq.It.isAny())) - .returns(() => { - return Promise.resolve('typed input'); - }); - ioc.serviceManager.rebindInstance(IApplicationShell, appShell.object); - - // Send in some special input - const code = `b = input('Test')\nb`; - addInputMockData(ioc, code, 'typed input'); - await addCode(ioc, wrapper, code); - - verifyHtmlOnCell(wrapper, 'InteractiveCell', 'typed input', CellPosition.Last); - }, - () => { - return ioc; - } - ); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import * as assert from 'assert'; +import * as fs from 'fs-extra'; +import { parse } from 'node-html-parser'; +import * as os from 'os'; +import * as path from 'path'; +import * as TypeMoq from 'typemoq'; +import { Disposable, Selection, TextDocument, TextEditor, Uri } from 'vscode'; + +import { ReactWrapper } from 'enzyme'; +import { IApplicationShell, IDocumentManager } from '../../client/common/application/types'; +import { IDataScienceSettings } from '../../client/common/types'; +import { createDeferred, waitForPromise } from '../../client/common/utils/async'; +import { noop } from '../../client/common/utils/misc'; +import { EXTENSION_ROOT_DIR } from '../../client/constants'; +import { generateCellsFromDocument } from '../../client/datascience/cellFactory'; +import { EditorContexts } from '../../client/datascience/constants'; +import { InteractiveWindowMessages } from '../../client/datascience/interactive-common/interactiveWindowTypes'; +import { InteractiveWindow } from '../../client/datascience/interactive-window/interactiveWindow'; +import { IInteractiveWindowProvider } from '../../client/datascience/types'; +import { IInterpreterService } from '../../client/interpreter/contracts'; +import { concatMultilineStringInput } from '../../datascience-ui/common'; +import { InteractivePanel } from '../../datascience-ui/history-react/interactivePanel'; +import { IKeyboardEvent } from '../../datascience-ui/react-common/event'; +import { ImageButton } from '../../datascience-ui/react-common/imageButton'; +import { MonacoEditor } from '../../datascience-ui/react-common/monacoEditor'; +import { DataScienceIocContainer } from './dataScienceIocContainer'; +import { createDocument } from './editor-integration/helpers'; +import { defaultDataScienceSettings } from './helpers'; +import { + addCode, + closeInteractiveWindow, + getInteractiveCellResults, + getOrCreateInteractiveWindow, + runMountedTest +} from './interactiveWindowTestHelpers'; +import { MockDocumentManager } from './mockDocumentManager'; +import { MockEditor } from './mockTextEditor'; +import { createMessageEvent, waitForUpdate } from './reactHelpers'; +import { + addContinuousMockData, + addInputMockData, + addMockData, + CellInputState, + CellPosition, + enterEditorKey, + enterInput, + escapePath, + findButton, + getInteractiveEditor, + getLastOutputCell, + mountWebView, + srcDirectory, + submitInput, + toggleCellExpansion, + typeCode, + verifyHtmlOnCell, + verifyLastCellInputState, + waitForMessage, + waitForMessageResponse +} from './testHelpers'; + +// tslint:disable:max-func-body-length trailing-comma no-any no-multiline-string +suite('DataScience Interactive Window output tests', () => { + const disposables: Disposable[] = []; + let ioc: DataScienceIocContainer; + const defaultCellMarker = '# %%'; + + setup(async () => { + ioc = new DataScienceIocContainer(); + ioc.registerDataScienceTypes(); + return ioc.activate(); + }); + + teardown(async () => { + for (const disposable of disposables) { + if (!disposable) { + continue; + } + // tslint:disable-next-line:no-any + const promise = disposable.dispose() as Promise; + if (promise) { + await promise; + } + } + await ioc.dispose(); + }); + + async function forceSettingsChange(newSettings: IDataScienceSettings) { + const window = await getOrCreateInteractiveWindow(ioc); + await window.show(); + const update = waitForMessage(ioc, InteractiveWindowMessages.SettingsUpdated); + ioc.forceSettingsChanged(undefined, ioc.getSettings().pythonPath, newSettings); + return update; + } + + function simulateKeyPressOnEditor( + editorControl: ReactWrapper, React.Component> | undefined, + keyboardEvent: Partial & { code: string } + ) { + enterEditorKey(editorControl, keyboardEvent); + } + + // Uncomment this to debug hangs on exit + // suiteTeardown(() => { + // asyncDump(); + // }); + + runMountedTest( + 'Simple text', + async (wrapper) => { + await addCode(ioc, wrapper, 'a=1\na'); + + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Clear output', + async (wrapper) => { + const text = `from IPython.display import clear_output +for i in range(10): + clear_output() + print("Hello World {0}!".format(i)) +`; + addContinuousMockData(ioc, text, async (_c) => { + return { + result: 'Hello World 9!', + haveMore: false + }; + }); + await addCode(ioc, wrapper, text); + + verifyHtmlOnCell(wrapper, 'InteractiveCell', '
Hello World 9!', CellPosition.Last); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Hide inputs', + async (wrapper) => { + await forceSettingsChange({ ...defaultDataScienceSettings(), showCellInputCode: false }); + + await addCode(ioc, wrapper, 'a=1\na'); + + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Hidden); + + // Add a cell without output, this cell should not show up at all + addMockData(ioc, 'a=1', undefined, 'text/plain'); + await addCode(ioc, wrapper, 'a=1'); + + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.First); + verifyHtmlOnCell(wrapper, 'InteractiveCell', undefined, CellPosition.Last); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Show inputs', + async (wrapper) => { + await forceSettingsChange({ ...defaultDataScienceSettings() }); + + await addCode(ioc, wrapper, 'a=1\na'); + + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Collapsed); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Expand inputs', + async (wrapper) => { + await forceSettingsChange({ ...defaultDataScienceSettings(), collapseCellInputCodeByDefault: false }); + await addCode(ioc, wrapper, 'a=1\na'); + + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Expanded); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Ctrl + 1/Ctrl + 2', + async (wrapper) => { + // Create an interactive window so that it listens to the results. + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + await interactiveWindow.show(); + + // Type in the input box + const editor = getInteractiveEditor(wrapper); + typeCode(editor, 'a=1\na'); + + // Give focus to a random div + const reactDiv = wrapper.find('div').first().getDOMNode(); + + const domDiv = reactDiv.querySelector('div'); + + if (domDiv && ioc.postMessage) { + domDiv.tabIndex = -1; + domDiv.focus(); + + // send the ctrl + 1/2 message, this should put focus back on the input box + const message = createMessageEvent({ type: InteractiveWindowMessages.Activate, payload: undefined }); + ioc.postMessage(message); + + // Then enter press shift + enter on the active element + const activeElement = document.activeElement; + if (activeElement) { + await submitInput(ioc, activeElement as HTMLTextAreaElement); + } + } + + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Escape/Ctrl+U', + async (wrapper) => { + // Create an interactive window so that it listens to the results. + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + await interactiveWindow.show(); + + // Type in the input box + const editor = getInteractiveEditor(wrapper); + typeCode(editor, 'a=1\na'); + + // Check code is what we think it is + const reactEditor = editor.instance() as MonacoEditor; + assert.equal(reactEditor.state.model?.getValue().replace(/\r/g, ''), 'a=1\na'); + + // Send escape + simulateKeyPressOnEditor(editor, { code: 'Escape' }); + assert.equal(reactEditor.state.model?.getValue().replace(/\r/g, ''), ''); + + typeCode(editor, 'a=1\na'); + assert.equal(reactEditor.state.model?.getValue().replace(/\r/g, ''), 'a=1\na'); + + simulateKeyPressOnEditor(editor, { code: 'KeyU', ctrlKey: true }); + assert.equal(reactEditor.state.model?.getValue().replace(/\r/g, ''), ''); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Click outside cells sets focus to input box', + async (wrapper) => { + // Create an interactive window so that it listens to the results. + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + await interactiveWindow.show(); + + // Type in the input box + const editor = getInteractiveEditor(wrapper); + typeCode(editor, 'a=1\na'); + + // Give focus to a random div + const reactDiv = wrapper.find('div').first().getDOMNode(); + + const domDiv = reactDiv.querySelector('div'); + + if (domDiv && ioc.postMessage) { + domDiv.tabIndex = -1; + domDiv.focus(); + + wrapper.find('section#main-panel-footer').first().simulate('click'); + + // Then enter press shift + enter on the active element + const activeElement = document.activeElement; + if (activeElement) { + await submitInput(ioc, activeElement as HTMLTextAreaElement); + } + } + + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Collapse / expand cell', + async (wrapper) => { + await forceSettingsChange({ ...defaultDataScienceSettings() }); + await addCode(ioc, wrapper, 'a=1\na'); + + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Collapsed); + + toggleCellExpansion(wrapper, 'InteractiveCell'); + + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Expanded); + + toggleCellExpansion(wrapper, 'InteractiveCell'); + + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Collapsed); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Hide / show cell', + async (wrapper) => { + await forceSettingsChange({ ...defaultDataScienceSettings() }); + await addCode(ioc, wrapper, 'a=1\na'); + + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Collapsed); + + // Hide the inputs and verify + await forceSettingsChange({ ...defaultDataScienceSettings(), showCellInputCode: false }); + + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Hidden); + + // Show the inputs and verify + await forceSettingsChange({ ...defaultDataScienceSettings(), showCellInputCode: true }); + + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Visible); + verifyLastCellInputState(wrapper, 'InteractiveCell', CellInputState.Collapsed); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Mime Types', + async (wrapper) => { + const badPanda = `import pandas as pd +df = pd.read("${escapePath(path.join(srcDirectory(), 'DefaultSalesReport.csv'))}") +df.head()`; + const goodPanda = `import pandas as pd +df = pd.read_csv("${escapePath(path.join(srcDirectory(), 'DefaultSalesReport.csv'))}") +df.head()`; + const matPlotLib = + 'import matplotlib.pyplot as plt\r\nimport numpy as np\r\nx = np.linspace(0,20,100)\r\nplt.plot(x, np.sin(x))\r\nplt.show()'; + const matPlotLibResults = 'img'; + const spinningCursor = `import sys +import time + +def spinning_cursor(): + while True: + for cursor in '|/-\\\\': + yield cursor + +spinner = spinning_cursor() +for _ in range(50): + sys.stdout.write(next(spinner)) + sys.stdout.flush() + time.sleep(0.1) + sys.stdout.write('\\r')`; + + addMockData(ioc, badPanda, `pandas has no attribute 'read'`, 'text/html', 'error'); + addMockData(ioc, goodPanda, `A table`, 'text/html'); + addMockData(ioc, matPlotLib, matPlotLibResults, 'text/html'); + const cursors = ['|', '/', '-', '\\']; + let cursorPos = 0; + let loops = 3; + addContinuousMockData(ioc, spinningCursor, async (_c) => { + const result = `${cursors[cursorPos]}\r`; + cursorPos += 1; + if (cursorPos >= cursors.length) { + cursorPos = 0; + loops -= 1; + } + return Promise.resolve({ result: result, haveMore: loops > 0 }); + }); + + await addCode(ioc, wrapper, badPanda, true); + verifyHtmlOnCell(wrapper, 'InteractiveCell', `has no attribute 'read'`, CellPosition.Last); + + await addCode(ioc, wrapper, goodPanda); + verifyHtmlOnCell(wrapper, 'InteractiveCell', ``, CellPosition.Last); + + await addCode(ioc, wrapper, matPlotLib); + verifyHtmlOnCell(wrapper, 'InteractiveCell', /img|Figure/, CellPosition.Last); + + await addCode(ioc, wrapper, spinningCursor); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '
', CellPosition.Last); + + addContinuousMockData(ioc, 'len?', async (_c) => { + return Promise.resolve({ + result: `Signature: len(obj, /) +Docstring: Return the number of items in a container. +Type: builtin_function_or_method`, + haveMore: false + }); + }); + await addCode(ioc, wrapper, 'len?'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', 'len', CellPosition.Last); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Undo/redo commands', + async (wrapper) => { + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + + // Get a cell into the list + await addCode(ioc, wrapper, 'a=1\na'); + + // Now verify if we undo, we have no cells + let afterUndo = await getInteractiveCellResults(ioc, wrapper, () => { + interactiveWindow.undoCells(); + return Promise.resolve(); + }); + + assert.equal(afterUndo.length, 1, `Undo should remove cells + ${afterUndo.debug()}`); + + // Redo should put the cells back + const afterRedo = await getInteractiveCellResults(ioc, wrapper, () => { + interactiveWindow.redoCells(); + return Promise.resolve(); + }); + assert.equal(afterRedo.length, 2, 'Redo should put cells back'); + + // Get another cell into the list + const afterAdd = await addCode(ioc, wrapper, 'a=1\na'); + assert.equal(afterAdd.length, 3, 'Second cell did not get added'); + + // Clear everything + const afterClear = await getInteractiveCellResults(ioc, wrapper, () => { + interactiveWindow.removeAllCells(); + return Promise.resolve(); + }); + assert.equal(afterClear.length, 1, "Clear didn't work"); + + // Undo should put them back + afterUndo = await getInteractiveCellResults(ioc, wrapper, () => { + interactiveWindow.undoCells(); + return Promise.resolve(); + }); + + assert.equal(afterUndo.length, 3, `Undo should put cells back`); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Click buttons', + async (wrapper) => { + // Goto source should cause the visible editor to be picked as long as its filename matches + const showedEditor = createDeferred(); + const textEditors: TextEditor[] = []; + const docManager = TypeMoq.Mock.ofType(); + const visibleEditor = TypeMoq.Mock.ofType(); + const dummyDocument = TypeMoq.Mock.ofType(); + dummyDocument.setup((d) => d.fileName).returns(() => Uri.file('foo.py').fsPath); + visibleEditor.setup((v) => v.show()).returns(() => showedEditor.resolve()); + visibleEditor.setup((v) => v.revealRange(TypeMoq.It.isAny())).returns(noop); + visibleEditor.setup((v) => v.document).returns(() => dummyDocument.object); + textEditors.push(visibleEditor.object); + docManager.setup((a) => a.visibleTextEditors).returns(() => textEditors); + ioc.serviceManager.rebindInstance(IDocumentManager, docManager.object); + + // Get a cell into the list + await addCode(ioc, wrapper, 'a=1\na'); + + // 'Click' the buttons in the react control + const undo = findButton(wrapper, InteractivePanel, 2); + const redo = findButton(wrapper, InteractivePanel, 1); + const clear = findButton(wrapper, InteractivePanel, 0); + + // Now verify if we undo, we have no cells + let afterUndo = await getInteractiveCellResults(ioc, wrapper, () => { + undo!.simulate('click'); + return Promise.resolve(); + }); + + assert.equal(afterUndo.length, 1, `Undo should remove cells`); + + // Redo should put the cells back + const afterRedo = await getInteractiveCellResults(ioc, wrapper, async () => { + redo!.simulate('click'); + return Promise.resolve(); + }); + assert.equal(afterRedo.length, 2, 'Redo should put cells back'); + + // Get another cell into the list + const afterAdd = await addCode(ioc, wrapper, 'a=1\na'); + assert.equal(afterAdd.length, 3, 'Second cell did not get added'); + + // Clear everything + const afterClear = await getInteractiveCellResults(ioc, wrapper, async () => { + clear!.simulate('click'); + return Promise.resolve(); + }); + assert.equal(afterClear.length, 1, "Clear didn't work"); + + // Undo should put them back + afterUndo = await getInteractiveCellResults(ioc, wrapper, async () => { + undo!.simulate('click'); + return Promise.resolve(); + }); + + assert.equal(afterUndo.length, 3, `Undo should put cells back`); + + // find the buttons on the cell itself + const ImageButtons = afterUndo.at(afterUndo.length - 2).find(ImageButton); + assert.equal(ImageButtons.length, 4, 'Cell buttons not found'); + + const goto = ImageButtons.at(1); + const deleteButton = ImageButtons.at(3); + + // Make sure goto works + await waitForMessageResponse(ioc, () => goto.simulate('click')); + await waitForPromise(showedEditor.promise, 1000); + assert.ok(showedEditor.resolved, 'Goto source is not jumping to editor'); + + // Make sure delete works + const afterDelete = await getInteractiveCellResults(ioc, wrapper, async () => { + deleteButton.simulate('click'); + return Promise.resolve(); + }); + assert.equal(afterDelete.length, 2, `Delete should remove a cell`); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Export', + async (wrapper) => { + // Export should cause the export dialog to come up. Remap appshell so we can check + const dummyDisposable = { + dispose: () => { + return; + } + }; + let exportCalled = false; + const appShell = TypeMoq.Mock.ofType(); + appShell + .setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString())) + .returns((e) => { + throw e; + }); + appShell + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(() => Promise.resolve('')); + appShell + .setup((a) => a.showSaveDialog(TypeMoq.It.isAny())) + .returns(() => { + exportCalled = true; + return Promise.resolve(undefined); + }); + appShell.setup((a) => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); + ioc.serviceManager.rebindInstance(IApplicationShell, appShell.object); + + // Make sure to create the interactive window after the rebind or it gets the wrong application shell. + await addCode(ioc, wrapper, 'a=1\na'); + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + + // Export should cause exportCalled to change to true + await waitForMessageResponse(ioc, () => interactiveWindow.exportCells()); + assert.equal(exportCalled, true, 'Export is not being called during export'); + + // Remove the cell + const exportButton = findButton(wrapper, InteractivePanel, 6); + const undo = findButton(wrapper, InteractivePanel, 2); + + // Now verify if we undo, we have no cells + const afterUndo = await getInteractiveCellResults(ioc, wrapper, () => { + undo!.simulate('click'); + return Promise.resolve(); + }); + + assert.equal(afterUndo.length, 1, 'Undo should remove cells'); + + // Then verify we cannot click the button (it should be disabled) + exportCalled = false; + const response = waitForMessageResponse(ioc, () => exportButton!.simulate('click')); + await waitForPromise(response, 100); + assert.equal(exportCalled, false, 'Export should not be called when no cells visible'); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Multiple Interpreters', + async (wrapper, context) => { + if (!ioc.mockJupyter) { + const interactiveWindowProvider = ioc.get(IInteractiveWindowProvider); + const interpreterService = ioc.get(IInterpreterService); + const interpreters = await ioc.getJupyterInterpreters(); + if (interpreters.length < 2) { + // tslint:disable-next-line: no-console + console.log( + 'Multiple interpreters skipped because local machine does not have more than one jupyter environment' + ); + context.skip(); + return; + } + const window = (await interactiveWindowProvider.getOrCreateActive()) as InteractiveWindow; + await addCode(ioc, wrapper, 'a=1\na'); + const activeInterpreter = await interpreterService.getActiveInterpreter( + await window.getOwningResource() + ); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + assert.equal( + window.notebook!.getMatchingInterpreter()?.path, + activeInterpreter?.path, + 'Active intrepreter not used to launch notebook' + ); + await closeInteractiveWindow(window, wrapper); + + // Add another python path + const secondUri = Uri.file('bar.py'); + ioc.addResourceToFolder(secondUri, path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience2')); + ioc.forceSettingsChanged( + secondUri, + interpreters.filter((i) => i.path !== activeInterpreter?.path)[0].path + ); + + // Then open a second time and make sure it uses this new path + const newWrapper = mountWebView(ioc, 'interactive'); + assert.ok(newWrapper, 'Could not mount a second time'); + const newWindow = (await interactiveWindowProvider.getOrCreateActive()) as InteractiveWindow; + await addCode(ioc, newWrapper, 'a=1\na', false, secondUri); + assert.notEqual( + newWindow.notebook!.getMatchingInterpreter()?.path, + activeInterpreter?.path, + 'Active intrepreter used to launch second notebook when it should not have' + ); + verifyHtmlOnCell(newWrapper, 'InteractiveCell', '1', CellPosition.Last); + } else { + context.skip(); + } + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Dispose test', + async () => { + // tslint:disable-next-line:no-any + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + await interactiveWindow.show(); // Have to wait for the load to finish + await interactiveWindow.dispose(); + // tslint:disable-next-line:no-any + const h2 = await getOrCreateInteractiveWindow(ioc); + // Check equal and then dispose so the test goes away + const equal = Object.is(interactiveWindow, h2); + await h2.show(); + assert.ok(!equal, 'Disposing is not removing the active interactive window'); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Editor Context', + async (wrapper) => { + // Before we have any cells, verify our contexts are not set + assert.equal( + ioc.getContext(EditorContexts.HaveInteractive), + false, + 'Should not have interactive before starting' + ); + assert.equal( + ioc.getContext(EditorContexts.HaveInteractiveCells), + false, + 'Should not have interactive cells before starting' + ); + assert.equal( + ioc.getContext(EditorContexts.HaveRedoableCells), + false, + 'Should not have redoable before starting' + ); + + // Verify we can send different commands to the UI and it will respond + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + + // Get an update promise so we can wait for the add code + const updatePromise = waitForUpdate(wrapper, InteractivePanel); + + // Send some code to the interactive window + await interactiveWindow.addCode('a=1\na', Uri.file('foo.py').fsPath, 2); + + // Wait for the render to go through + await updatePromise; + + // Now we should have the 3 editor contexts + assert.equal( + ioc.getContext(EditorContexts.HaveInteractive), + true, + 'Should have interactive after starting' + ); + assert.equal( + ioc.getContext(EditorContexts.HaveInteractiveCells), + true, + 'Should have interactive cells after starting' + ); + assert.equal( + ioc.getContext(EditorContexts.HaveRedoableCells), + false, + 'Should not have redoable after starting' + ); + + // Setup a listener for context change events. We have 3 separate contexts, so we have to wait for all 3. + let count = 0; + let deferred = createDeferred(); + const eventDispose = ioc.onContextSet((_a) => { + count += 1; + if (count >= 3) { + deferred.resolve(); + } + }); + disposables.push(eventDispose); + + // Create a method that resets the waiting + const resetWaiting = () => { + count = 0; + deferred = createDeferred(); + }; + + // Now send an undo command. This should change the state, so use our waitForInfo promise instead + resetWaiting(); + interactiveWindow.undoCells(); + await waitForPromise(deferred.promise, 2000); + assert.ok(deferred.resolved, 'Never got update to state'); + assert.equal( + ioc.getContext(EditorContexts.HaveInteractiveCells), + false, + 'Should not have interactive cells after undo as sysinfo is ignored' + ); + assert.equal(ioc.getContext(EditorContexts.HaveRedoableCells), true, 'Should have redoable after undo'); + + resetWaiting(); + interactiveWindow.redoCells(); + await waitForPromise(deferred.promise, 2000); + assert.ok(deferred.resolved, 'Never got update to state'); + assert.equal( + ioc.getContext(EditorContexts.HaveInteractiveCells), + true, + 'Should have interactive cells after redo' + ); + assert.equal( + ioc.getContext(EditorContexts.HaveRedoableCells), + false, + 'Should not have redoable after redo' + ); + + resetWaiting(); + interactiveWindow.removeAllCells(); + await waitForPromise(deferred.promise, 2000); + assert.ok(deferred.resolved, 'Never got update to state'); + assert.equal( + ioc.getContext(EditorContexts.HaveInteractiveCells), + false, + 'Should not have interactive cells after delete' + ); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Simple input', + async (wrapper) => { + // Create an interactive window so that it listens to the results. + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + await interactiveWindow.show(); + + // Then enter some code. + await enterInput(wrapper, ioc, 'a=1\na', 'InteractiveCell'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Copy to source input', + async (wrapper) => { + const showedEditor = createDeferred(); + ioc.addDocument('# No cells here', 'foo.py'); + const docManager = ioc.get(IDocumentManager) as MockDocumentManager; + const editor = (await docManager.showTextDocument(docManager.textDocuments[0])) as MockEditor; + editor.setRevealCallback(() => showedEditor.resolve()); + + // Create an interactive window so that it listens to the results. + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + await interactiveWindow.show(); + + // Then enter some code. + await enterInput(wrapper, ioc, 'a=1\na', 'InteractiveCell'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + const ImageButtons = getLastOutputCell(wrapper, 'InteractiveCell').find(ImageButton); + assert.equal(ImageButtons.length, 4, 'Cell buttons not found'); + const copyToSource = ImageButtons.at(2); + + // Then click the copy to source button + await waitForMessageResponse(ioc, () => copyToSource.simulate('click')); + await waitForPromise(showedEditor.promise, 100); + assert.ok(showedEditor.resolved, 'Copy to source is not adding code to the editor'); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Multiple input', + async (wrapper) => { + // Create an interactive window so that it listens to the results. + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + await interactiveWindow.show(); + + // Then enter some code. + await enterInput(wrapper, ioc, 'a=1\na', 'InteractiveCell'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + + // Then delete the node + const lastCell = getLastOutputCell(wrapper, 'InteractiveCell'); + const ImageButtons = lastCell.find(ImageButton); + assert.equal(ImageButtons.length, 4, 'Cell buttons not found'); + const deleteButton = ImageButtons.at(3); + + // Make sure delete works + const afterDelete = await getInteractiveCellResults(ioc, wrapper, async () => { + deleteButton.simulate('click'); + return Promise.resolve(); + }); + assert.equal(afterDelete.length, 1, `Delete should remove a cell`); + + // Should be able to enter again + await enterInput(wrapper, ioc, 'a=1\na', 'InteractiveCell'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + + // Try a 3rd time with some new input + addMockData(ioc, 'print("hello")', 'hello'); + await enterInput(wrapper, ioc, 'print("hello', 'InteractiveCell'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', 'hello', CellPosition.Last); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Restart with session failure', + async (wrapper) => { + // Prime the pump + await addCode(ioc, wrapper, 'a=1\na'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + + // Then something that could possibly timeout + addContinuousMockData(ioc, 'import time\r\ntime.sleep(1000)', (_c) => { + return Promise.resolve({ result: '', haveMore: true }); + }); + + // Then get our mock session and force it to not restart ever. + if (ioc.mockJupyter) { + const currentSession = ioc.mockJupyter.getCurrentSession(); + if (currentSession) { + currentSession.prolongRestarts(); + } + } + + // Then try executing our long running cell and restarting in the middle + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + const executed = createDeferred(); + // We have to wait until the execute goes through before we reset. + interactiveWindow.onExecutedCode(() => executed.resolve()); + const added = interactiveWindow.addCode('import time\r\ntime.sleep(1000)', Uri.file('foo').fsPath, 0); + await executed.promise; + await interactiveWindow.restartKernel(); + await added; + + // Now see if our wrapper still works. Interactive window should have forced a restart + await interactiveWindow.addCode('a=1\na', Uri.file('foo').fsPath, 0); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'LiveLossPlot', + async (wrapper) => { + // Only run this test when not mocking. Too complicated to mimic otherwise + if (!ioc.mockJupyter) { + // Load all of our cells + const testFile = path.join(srcDirectory(), 'liveloss.py'); + const version = 1; + const inputText = await fs.readFile(testFile, 'utf-8'); + const document = createDocument(inputText, testFile, version, TypeMoq.Times.atLeastOnce(), true); + const cells = generateCellsFromDocument(document.object); + assert.ok(cells, 'No cells generated'); + assert.equal(cells.length, 2, 'Not enough cells generated'); + + // Run the first cell + await addCode(ioc, wrapper, concatMultilineStringInput(cells[0].data.source)); + + // Last cell should generate a series of updates. Verify we end up with a single image + await addCode(ioc, wrapper, concatMultilineStringInput(cells[1].data.source)); + const cell = getLastOutputCell(wrapper, 'InteractiveCell'); + + const output = cell!.find('div.cell-output'); + assert.ok(output.length > 0, 'No output cell found'); + const outHtml = output.html(); + + const root = parse(outHtml) as any; + const png = root.querySelectorAll('img') as HTMLElement[]; + assert.ok(png, 'No pngs found'); + assert.equal(png.length, 1, 'Wrong number of pngs'); + } + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Gather code run from text editor', + async (wrapper) => { + ioc.getSettings().datascience.enableGather = true; + ioc.getSettings().datascience.gatherToScript = true; + // Enter some code. + const code = `${defaultCellMarker}\na=1\na`; + await addCode(ioc, wrapper, code); + addMockData(ioc, code, undefined); + const ImageButtons = getLastOutputCell(wrapper, 'InteractiveCell').find(ImageButton); // This isn't rendering correctly + assert.equal(ImageButtons.length, 4, 'Cell buttons not found'); + const gatherCode = ImageButtons.at(0); + + // Then click the gather code button + await waitForMessageResponse(ioc, () => gatherCode.simulate('click')); + const docManager = ioc.get(IDocumentManager) as MockDocumentManager; + assert.notEqual(docManager.activeTextEditor, undefined); + if (docManager.activeTextEditor) { + // Ignore white space. + assert.equal( + docManager.activeTextEditor.document.getText().trim(), + `${defaultCellMarker} [markdown]\n## Gather not available` + ); + } + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Gather code run from input box', + async (wrapper) => { + ioc.getSettings().datascience.enableGather = true; + ioc.getSettings().datascience.gatherToScript = true; + // Create an interactive window so that it listens to the results. + const interactiveWindow = await getOrCreateInteractiveWindow(ioc); + await interactiveWindow.show(); + + // Then enter some code. + await enterInput(wrapper, ioc, 'a=1\na', 'InteractiveCell'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + const ImageButtons = getLastOutputCell(wrapper, 'InteractiveCell').find(ImageButton); + assert.equal(ImageButtons.length, 4, 'Cell buttons not found'); + const gatherCode = ImageButtons.at(0); + + // Then click the gather code button + await waitForMessageResponse(ioc, () => gatherCode.simulate('click')); + const docManager = ioc.get(IDocumentManager) as MockDocumentManager; + assert.notEqual(docManager.activeTextEditor, undefined); + if (docManager.activeTextEditor) { + // Ignore whitespace + assert.equal( + docManager.activeTextEditor.document.getText().trim(), + `${defaultCellMarker} [markdown]\n## Gather not available` + ); + } + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Copy back to source', + async (_wrapper) => { + ioc.addDocument(`${defaultCellMarker}${os.EOL}print("bar")`, 'foo.py'); + const docManager = ioc.get(IDocumentManager); + docManager.showTextDocument(docManager.textDocuments[0]); + const window = (await getOrCreateInteractiveWindow(ioc)) as InteractiveWindow; + await window.show(); + await window.copyCode({ source: 'print("baz")' }); + assert.equal( + docManager.textDocuments[0].getText(), + `${defaultCellMarker}${os.EOL}print("baz")${os.EOL}${defaultCellMarker}${os.EOL}print("bar")`, + 'Text not inserted' + ); + const activeEditor = docManager.activeTextEditor as MockEditor; + activeEditor.selection = new Selection(1, 2, 1, 2); + await window.copyCode({ source: 'print("baz")' }); + assert.equal( + docManager.textDocuments[0].getText(), + `${defaultCellMarker}${os.EOL}${defaultCellMarker}${os.EOL}print("baz")${os.EOL}${defaultCellMarker}${os.EOL}print("baz")${os.EOL}${defaultCellMarker}${os.EOL}print("bar")`, + 'Text not inserted' + ); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Limit text output', + async (wrapper) => { + ioc.getSettings().datascience.textOutputLimit = 8; + + // Output should be trimmed to just two lines of output + const code = `print("hello\\nworld\\nhow\\nare\\nyou")`; + addMockData(ioc, code, 'are\nyou\n'); + await addCode(ioc, wrapper, code); + + verifyHtmlOnCell(wrapper, 'InteractiveCell', '>are\nyou', CellPosition.Last); + }, + () => { + return ioc; + } + ); + + runMountedTest( + 'Type in input', + async (wrapper) => { + const appShell = TypeMoq.Mock.ofType(); + appShell + .setup((a) => a.showInputBox(TypeMoq.It.isAny())) + .returns(() => { + return Promise.resolve('typed input'); + }); + ioc.serviceManager.rebindInstance(IApplicationShell, appShell.object); + + // Send in some special input + const code = `b = input('Test')\nb`; + addInputMockData(ioc, code, 'typed input'); + await addCode(ioc, wrapper, code); + + verifyHtmlOnCell(wrapper, 'InteractiveCell', 'typed input', CellPosition.Last); + }, + () => { + return ioc; + } + ); +}); diff --git a/src/test/datascience/interactiveWindowCommandListener.unit.test.ts b/src/test/datascience/interactiveWindowCommandListener.unit.test.ts index da3ed170ce08..97f8fedef1d5 100644 --- a/src/test/datascience/interactiveWindowCommandListener.unit.test.ts +++ b/src/test/datascience/interactiveWindowCommandListener.unit.test.ts @@ -106,7 +106,7 @@ suite('Interactive window command listener', async () => { // Setup defaults when(interpreterService.onDidChangeInterpreter).thenReturn(dummyEvent.event); - when(interpreterService.getInterpreterDetails(argThat(o => !o.includes || !o.includes('python')))).thenReject( + when(interpreterService.getInterpreterDetails(argThat((o) => !o.includes || !o.includes('python')))).thenReject( ('Unknown interpreter' as any) as Error ); @@ -158,7 +158,7 @@ suite('Interactive window command listener', async () => { when( fileSystem.writeFile( anything(), - argThat(o => { + argThat((o) => { lastFileContents = o; return true; }) @@ -220,7 +220,7 @@ suite('Interactive window command listener', async () => { test('Import', async () => { createCommandListener(); - when(applicationShell.showOpenDialog(argThat(o => o.openLabel && o.openLabel.includes('Import')))).thenReturn( + when(applicationShell.showOpenDialog(argThat((o) => o.openLabel && o.openLabel.includes('Import')))).thenReturn( Promise.resolve([Uri.file('foo')]) ); await commandManager.executeCommand(Commands.ImportNotebook, undefined, undefined); @@ -235,7 +235,7 @@ suite('Interactive window command listener', async () => { createCommandListener(); const doc = await documentManager.openTextDocument('bar.ipynb'); await documentManager.showTextDocument(doc); - when(applicationShell.showSaveDialog(argThat(o => o.saveLabel && o.saveLabel.includes('Export')))).thenReturn( + when(applicationShell.showSaveDialog(argThat((o) => o.saveLabel && o.saveLabel.includes('Export')))).thenReturn( Promise.resolve(Uri.file('foo')) ); when(applicationShell.showInformationMessage(anything(), anything())).thenReturn(Promise.resolve('moo')); @@ -262,10 +262,10 @@ suite('Interactive window command listener', async () => { when(jupyterExecution.connectToNotebookServer(anything(), anything())).thenResolve(server.object); const notebook = createTypeMoq('jupyter notebook'); server - .setup(s => s.createNotebook(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((s) => s.createNotebook(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(notebook.object)); notebook - .setup(n => + .setup((n) => n.execute( TypeMoq.It.isAny(), TypeMoq.It.isAny(), @@ -278,7 +278,7 @@ suite('Interactive window command listener', async () => { return Promise.resolve(generateCells(undefined, 'a=1', 'bar.py', 0, false, uuid())); }); - when(applicationShell.showSaveDialog(argThat(o => o.saveLabel && o.saveLabel.includes('Export')))).thenReturn( + when(applicationShell.showSaveDialog(argThat((o) => o.saveLabel && o.saveLabel.includes('Export')))).thenReturn( Promise.resolve(Uri.file('foo')) ); when(applicationShell.showInformationMessage(anything(), anything())).thenReturn(Promise.resolve('moo')); @@ -300,7 +300,7 @@ suite('Interactive window command listener', async () => { }); test('Export skipped on no file', async () => { createCommandListener(); - when(applicationShell.showSaveDialog(argThat(o => o.saveLabel && o.saveLabel.includes('Export')))).thenReturn( + when(applicationShell.showSaveDialog(argThat((o) => o.saveLabel && o.saveLabel.includes('Export')))).thenReturn( Promise.resolve(Uri.file('foo')) ); await commandManager.executeCommand(Commands.ExportFileAndOutputAsNotebook, Uri.file('bar.ipynb')); @@ -310,7 +310,7 @@ suite('Interactive window command listener', async () => { createCommandListener(); const doc = await documentManager.openTextDocument('bar.ipynb'); await documentManager.showTextDocument(doc); - when(applicationShell.showSaveDialog(argThat(o => o.saveLabel && o.saveLabel.includes('Export')))).thenReturn( + when(applicationShell.showSaveDialog(argThat((o) => o.saveLabel && o.saveLabel.includes('Export')))).thenReturn( Promise.resolve(Uri.file('foo')) ); await commandManager.executeCommand(Commands.ExportFileAsNotebook, undefined, undefined); diff --git a/src/test/datascience/interactiveWindowTestHelpers.tsx b/src/test/datascience/interactiveWindowTestHelpers.tsx index 46d890f1a273..e5bf28ff78c9 100644 --- a/src/test/datascience/interactiveWindowTestHelpers.tsx +++ b/src/test/datascience/interactiveWindowTestHelpers.tsx @@ -42,7 +42,7 @@ export function runMountedTest( testFunc: (wrapper: ReactWrapper, React.Component>, context: Mocha.Context) => Promise, getIOC: () => DataScienceIocContainer ) { - test(name, async function() { + test(name, async function () { const ioc = getIOC(); const jupyterExecution = ioc.get(IJupyterExecution); if (await jupyterExecution.isNotebookSupported()) { diff --git a/src/test/datascience/jupyter/interpreter/jupyterInterpreterService.unit.test.ts b/src/test/datascience/jupyter/interpreter/jupyterInterpreterService.unit.test.ts index 766bd3831aa2..4db58ba32a60 100644 --- a/src/test/datascience/jupyter/interpreter/jupyterInterpreterService.unit.test.ts +++ b/src/test/datascience/jupyter/interpreter/jupyterInterpreterService.unit.test.ts @@ -68,7 +68,7 @@ suite('Data Science - Jupyter Interpreter Service', () => { secondPythonInterpreter ); when(memento.update(anything(), anything())).thenResolve(); - jupyterInterpreterService.onDidChangeInterpreter(e => (selectedInterpreterEventArgs = e)); + jupyterInterpreterService.onDidChangeInterpreter((e) => (selectedInterpreterEventArgs = e)); when(interpreterSelector.selectInterpreter()).thenResolve(pythonInterpreter); }); diff --git a/src/test/datascience/jupyter/jupyterCellOutputMimeTypeTracker.unit.test.ts b/src/test/datascience/jupyter/jupyterCellOutputMimeTypeTracker.unit.test.ts index bc88722471cb..fdaf5ef453ad 100644 --- a/src/test/datascience/jupyter/jupyterCellOutputMimeTypeTracker.unit.test.ts +++ b/src/test/datascience/jupyter/jupyterCellOutputMimeTypeTracker.unit.test.ts @@ -28,12 +28,12 @@ suite('Data Science - Cell Output Mimetype Tracker', () => { public static telemetrySent: [string, Record][] = []; public static expectHashes(props: {}[]) { const mimeTypeTelemetry = Reporter.telemetrySent.filter( - item => item[0] === Telemetry.HashedCellOutputMimeType + (item) => item[0] === Telemetry.HashedCellOutputMimeType ); expect(mimeTypeTelemetry).to.be.lengthOf(props.length, 'Incorrect number of telemetry messages sent'); expect(mimeTypeTelemetry).to.deep.equal( - props.map(prop => [Telemetry.HashedCellOutputMimeType, prop]), + props.map((prop) => [Telemetry.HashedCellOutputMimeType, prop]), 'Contents in telemetry do not match' ); } @@ -113,9 +113,7 @@ suite('Data Science - Cell Output Mimetype Tracker', () => { return { data: { 'application/vnd.plotly.v1+json': '', 'text/html': '' }, output_type }; } function generateTelemetry(mimeType: string) { - const hashedName = sha256() - .update(mimeType) - .digest('hex'); + const hashedName = sha256().update(mimeType).digest('hex'); const lowerMimeType = mimeType.toLowerCase(); return { @@ -189,9 +187,9 @@ suite('Data Science - Cell Output Mimetype Tracker', () => { await fakeTimer.wait(); Reporter.expectHashes([]); }); - [CellState.editing, CellState.error, CellState.executing].forEach(cellState => { + [CellState.editing, CellState.error, CellState.executing].forEach((cellState) => { const cellStateValues = getNamesAndValues(CellState); - test(`If cell state is '${cellStateValues.find(item => item.value === cellState)?.name}'`, async () => { + test(`If cell state is '${cellStateValues.find((item) => item.value === cellState)?.name}'`, async () => { const cellTextOutput = generateCellWithOutput([generateStreamedOutput()]); cellTextOutput.state = cellState; @@ -224,7 +222,7 @@ suite('Data Science - Cell Output Mimetype Tracker', () => { await fakeTimer.wait(); Reporter.expectHashes(expectedTelemetry); }); - ['display_data', 'update_display_data', 'execute_result'].forEach(outputType => { + ['display_data', 'update_display_data', 'execute_result'].forEach((outputType) => { suite(`Send Telemetry for Output Type = ${outputType}`, () => { test('MimeType text/html', async () => { const expectedTelemetry = generateTelemetry('text/html'); diff --git a/src/test/datascience/jupyter/jupyterSession.unit.test.ts b/src/test/datascience/jupyter/jupyterSession.unit.test.ts index 1c52edb028ca..b741fdbe344f 100644 --- a/src/test/datascience/jupyter/jupyterSession.unit.test.ts +++ b/src/test/datascience/jupyter/jupyterSession.unit.test.ts @@ -92,7 +92,7 @@ suite('Data Science - JupyterSession', () => { // tslint:disable-next-line: no-any when(contentsManager.newUntitled(deepEqual({ type: 'notebook' }))).thenResolve({ path: nbFile } as any); when(sessionManager.startNew(anything())).thenResolve(instance(session)); - kernelSpec.setup(k => k.name).returns(() => 'some name'); + kernelSpec.setup((k) => k.name).returns(() => 'some name'); await jupyterSession.connect(); @@ -127,7 +127,7 @@ suite('Data Science - JupyterSession', () => { }); suite('Shutdown', () => { test('Remote', async () => { - connection.setup(c => c.localLaunch).returns(() => false); + connection.setup((c) => c.localLaunch).returns(() => false); when(sessionManager.refreshRunning()).thenResolve(); when(contentsManager.delete(anything())).thenResolve(); @@ -137,7 +137,7 @@ suite('Data Science - JupyterSession', () => { verify(contentsManager.delete(anything())).once(); }); test('Remote sessions', async () => { - connection.setup(c => c.localLaunch).returns(() => true); + connection.setup((c) => c.localLaunch).returns(() => true); when(sessionManager.refreshRunning()).thenResolve(); when(contentsManager.delete(anything())).thenResolve(); when(session.isRemoteSession).thenReturn(true); @@ -156,7 +156,7 @@ suite('Data Science - JupyterSession', () => { test('Local', async () => { verify(statusChangedSignal.connect(anything())).once(); - connection.setup(c => c.localLaunch).returns(() => true); + connection.setup((c) => c.localLaunch).returns(() => true); when(session.isRemoteSession).thenReturn(false); when(session.isDisposed).thenReturn(false); when(session.shutdown()).thenResolve(); @@ -343,7 +343,7 @@ suite('Data Science - JupyterSession', () => { test('Restart should create a new session & kill old session', async () => { const oldSessionShutDown = createDeferred(); - connection.setup(c => c.localLaunch).returns(() => true); + connection.setup((c) => c.localLaunch).returns(() => true); when(session.isRemoteSession).thenReturn(false); when(session.isDisposed).thenReturn(false); when(session.shutdown()).thenCall(() => { diff --git a/src/test/datascience/jupyter/kernels/kernelSelections.unit.test.ts b/src/test/datascience/jupyter/kernels/kernelSelections.unit.test.ts index 4219fed1dfe0..2b1d0992f300 100644 --- a/src/test/datascience/jupyter/kernels/kernelSelections.unit.test.ts +++ b/src/test/datascience/jupyter/kernels/kernelSelections.unit.test.ts @@ -147,7 +147,7 @@ suite('Data Science - KernelSelections', () => { }); test('Should return a list with the proper details in the quick pick for remote connections (excluding non-python kernels)', async () => { const activeKernels: IJupyterKernel[] = [activePython1KernelModel, activeJuliaKernelModel]; - const sessions = activeKernels.map(item => { + const sessions = activeKernels.map((item) => { return { id: 'sessionId', name: 'someSession', @@ -215,7 +215,7 @@ suite('Data Science - KernelSelections', () => { // - selection = kernel model + kernel spec // - description = last activity and # of connections. const expectedKernelItems: IKernelSpecQuickPickItem[] = [python1KernelSpecModel, python3KernelSpecModel].map( - item => { + (item) => { return { label: item.display_name, detail: '', @@ -223,7 +223,7 @@ suite('Data Science - KernelSelections', () => { }; } ); - const expectedInterpreterItems: IKernelSpecQuickPickItem[] = allInterpreters.map(item => { + const expectedInterpreterItems: IKernelSpecQuickPickItem[] = allInterpreters.map((item) => { return { ...item, label: item.label, diff --git a/src/test/datascience/jupyter/kernels/kernelSelector.unit.test.ts b/src/test/datascience/jupyter/kernels/kernelSelector.unit.test.ts index e3e4b7d0188d..0522ed14b732 100644 --- a/src/test/datascience/jupyter/kernels/kernelSelector.unit.test.ts +++ b/src/test/datascience/jupyter/kernels/kernelSelector.unit.test.ts @@ -189,7 +189,7 @@ suite('Data Science - KernelSelector', () => { session: {} as any } ]; - const quickPickItems: IKernelSpecQuickPickItem[] = kernelModels.map(kernelModel => { + const quickPickItems: IKernelSpecQuickPickItem[] = kernelModels.map((kernelModel) => { return { label: '', selection: { kernelModel, kernelSpec: undefined, interpreter: undefined } @@ -227,7 +227,7 @@ suite('Data Science - KernelSelector', () => { const suggestions = capture(appShell.showQuickPick).first()[0] as IKernelSpecQuickPickItem[]; assert.deepEqual( suggestions, - quickPickItems.filter(item => !['id2', 'id4'].includes(item.selection?.kernelModel?.id || '')) + quickPickItems.filter((item) => !['id2', 'id4'].includes(item.selection?.kernelModel?.id || '')) ); }); test('Should hide kernel from local sessions', async () => { @@ -269,7 +269,7 @@ suite('Data Science - KernelSelector', () => { session: {} as any } ]; - const quickPickItems: IKernelSpecQuickPickItem[] = kernelModels.map(kernelModel => { + const quickPickItems: IKernelSpecQuickPickItem[] = kernelModels.map((kernelModel) => { return { label: '', selection: { kernelModel, kernelSpec: undefined, interpreter: undefined } @@ -303,7 +303,7 @@ suite('Data Science - KernelSelector', () => { const suggestions = capture(appShell.showQuickPick).first()[0] as IKernelSpecQuickPickItem[]; assert.deepEqual( suggestions, - quickPickItems.filter(item => !['id2', 'id4'].includes(item.selection?.kernelModel?.id || '')) + quickPickItems.filter((item) => !['id2', 'id4'].includes(item.selection?.kernelModel?.id || '')) ); }); }); diff --git a/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts b/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts index 98aa37aab540..20a9570fa92a 100644 --- a/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts +++ b/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts @@ -96,7 +96,7 @@ suite('Data Science - Kernel Switcher', () => { }); }); - [true, false].forEach(isLocalConnection => { + [true, false].forEach((isLocalConnection) => { // tslint:disable-next-line: max-func-body-length suite(isLocalConnection ? 'Local Connection' : 'Remote Connection', () => { setup(() => { @@ -118,7 +118,7 @@ suite('Data Science - Kernel Switcher', () => { [ { title: 'Without an existing kernel', currentKernel: undefined }, { title: 'With an existing kernel', currentKernel } - ].forEach(currentKernelInfo => { + ].forEach((currentKernelInfo) => { suite(currentKernelInfo.title, () => { setup(() => { when(notebook.getKernelSpec()).thenReturn(currentKernelInfo.currentKernel); @@ -226,7 +226,7 @@ suite('Data Science - Kernel Switcher', () => { verify(notebook.setInterpreter(selectedInterpreter)).never(); }); suite('Display error if `JupyterSessionStartError` is throw and retry', () => { - setup(function() { + setup(function () { if (!isLocalConnection) { // tslint:disable-next-line: no-invalid-this this.skip(); diff --git a/src/test/datascience/jupyter/serverSelector.unit.test.ts b/src/test/datascience/jupyter/serverSelector.unit.test.ts index f8cbab2ecbd2..9314810c8c73 100644 --- a/src/test/datascience/jupyter/serverSelector.unit.test.ts +++ b/src/test/datascience/jupyter/serverSelector.unit.test.ts @@ -71,7 +71,7 @@ suite('Data Science - Jupyter Server URI Selector', () => { test('Local pick server uri', async () => { let value = ''; - const ds = createDataScienceObject('$(zap) Default', '', v => (value = v)); + const ds = createDataScienceObject('$(zap) Default', '', (v) => (value = v)); await ds.selectJupyterURI(true); assert.equal(value, Settings.JupyterServerLocalLaunch, 'Default should pick local launch'); @@ -150,21 +150,21 @@ suite('Data Science - Jupyter Server URI Selector', () => { test('Remote server uri', async () => { let value = ''; - const ds = createDataScienceObject('$(server) Existing', 'http://localhost:1111', v => (value = v)); + const ds = createDataScienceObject('$(server) Existing', 'http://localhost:1111', (v) => (value = v)); await ds.selectJupyterURI(true); assert.equal(value, 'http://localhost:1111', 'Already running should end up with the user inputed value'); }); test('Remote server uri no local', async () => { let value = ''; - const ds = createDataScienceObject('$(server) Existing', 'http://localhost:1111', v => (value = v)); + const ds = createDataScienceObject('$(server) Existing', 'http://localhost:1111', (v) => (value = v)); await ds.selectJupyterURI(false); assert.equal(value, 'http://localhost:1111', 'Already running should end up with the user inputed value'); }); test('Remote server uri (reload VSCode if there is a change in settings)', async () => { let value = ''; - const ds = createDataScienceObject('$(server) Existing', 'http://localhost:1111', v => (value = v)); + const ds = createDataScienceObject('$(server) Existing', 'http://localhost:1111', (v) => (value = v)); await ds.selectJupyterURI(true); assert.equal(value, 'http://localhost:1111', 'Already running should end up with the user inputed value'); verify(cmdManager.executeCommand(anything(), anything())).once(); @@ -172,7 +172,7 @@ suite('Data Science - Jupyter Server URI Selector', () => { test('Remote server uri (do not reload VSCode if there is no change in settings)', async () => { let value = ''; - const ds = createDataScienceObject('$(server) Existing', 'http://localhost:1111', v => (value = v)); + const ds = createDataScienceObject('$(server) Existing', 'http://localhost:1111', (v) => (value = v)); dsSettings.jupyterServerURI = 'http://localhost:1111'; await ds.selectJupyterURI(true); assert.equal(value, 'http://localhost:1111', 'Already running should end up with the user inputed value'); @@ -181,7 +181,7 @@ suite('Data Science - Jupyter Server URI Selector', () => { test('Invalid server uri', async () => { let value = ''; - const ds = createDataScienceObject('$(server) Existing', 'httx://localhost:1111', v => (value = v)); + const ds = createDataScienceObject('$(server) Existing', 'httx://localhost:1111', (v) => (value = v)); await ds.selectJupyterURI(true); assert.notEqual(value, 'httx://localhost:1111', 'Already running should validate'); assert.equal(value, '', 'Validation failed'); diff --git a/src/test/datascience/jupyterPasswordConnect.unit.test.ts b/src/test/datascience/jupyterPasswordConnect.unit.test.ts index 31207e24ad5c..84a6f64c1eea 100644 --- a/src/test/datascience/jupyterPasswordConnect.unit.test.ts +++ b/src/test/datascience/jupyterPasswordConnect.unit.test.ts @@ -20,7 +20,7 @@ suite('JupyterPasswordConnect', () => { setup(() => { appShell = typemoq.Mock.ofType(); - appShell.setup(a => a.showInputBox(typemoq.It.isAny())).returns(() => Promise.resolve('Python')); + appShell.setup((a) => a.showInputBox(typemoq.It.isAny())).returns(() => Promise.resolve('Python')); jupyterPasswordConnect = new JupyterPasswordConnect(appShell.object); }); @@ -32,20 +32,20 @@ suite('JupyterPasswordConnect', () => { const mockXsrfResponse = typemoq.Mock.ofType(nodeFetch.Response); const mockXsrfHeaders = typemoq.Mock.ofType(nodeFetch.Headers); mockXsrfHeaders - .setup(mh => mh.get('set-cookie')) + .setup((mh) => mh.get('set-cookie')) .returns(() => `_xsrf=${xsrfValue}`) .verifiable(typemoq.Times.once()); mockXsrfResponse - .setup(mr => mr.ok) + .setup((mr) => mr.ok) .returns(() => true) .verifiable(typemoq.Times.once()); mockXsrfResponse - .setup(mr => mr.headers) + .setup((mr) => mr.headers) .returns(() => mockXsrfHeaders.object) .verifiable(typemoq.Times.once()); fetchMock - .setup(fm => + .setup((fm) => //tslint:disable-next-line:no-http-string fm('http://TESTNAME:8888/login?', { method: 'get', @@ -60,21 +60,21 @@ suite('JupyterPasswordConnect', () => { const mockSessionResponse = typemoq.Mock.ofType(nodeFetch.Response); const mockSessionHeaders = typemoq.Mock.ofType(nodeFetch.Headers); mockSessionHeaders - .setup(mh => mh.get('set-cookie')) + .setup((mh) => mh.get('set-cookie')) .returns(() => `${sessionName}=${sessionValue}`) .verifiable(typemoq.Times.once()); mockSessionResponse - .setup(mr => mr.status) + .setup((mr) => mr.status) .returns(() => 302) .verifiable(typemoq.Times.once()); mockSessionResponse - .setup(mr => mr.headers) + .setup((mr) => mr.headers) .returns(() => mockSessionHeaders.object) .verifiable(typemoq.Times.once()); // typemoq doesn't love this comparison, so generalize it a bit fetchMock - .setup(fm => + .setup((fm) => fm( //tslint:disable-next-line:no-http-string 'http://TESTNAME:8888/login?', @@ -120,21 +120,21 @@ suite('JupyterPasswordConnect', () => { const mockXsrfResponse = typemoq.Mock.ofType(nodeFetch.Response); const mockXsrfHeaders = typemoq.Mock.ofType(nodeFetch.Headers); mockXsrfHeaders - .setup(mh => mh.get('set-cookie')) + .setup((mh) => mh.get('set-cookie')) .returns(() => `_xsrf=${xsrfValue}`) .verifiable(typemoq.Times.once()); mockXsrfResponse - .setup(mr => mr.ok) + .setup((mr) => mr.ok) .returns(() => true) .verifiable(typemoq.Times.once()); mockXsrfResponse - .setup(mr => mr.headers) + .setup((mr) => mr.headers) .returns(() => mockXsrfHeaders.object) .verifiable(typemoq.Times.once()); //tslint:disable-next-line:no-http-string fetchMock - .setup(fm => + .setup((fm) => fm( 'https://TESTNAME:8888/login?', typemoq.It.isObjectWith({ @@ -150,22 +150,22 @@ suite('JupyterPasswordConnect', () => { const mockSessionResponse = typemoq.Mock.ofType(nodeFetch.Response); const mockSessionHeaders = typemoq.Mock.ofType(nodeFetch.Headers); mockSessionHeaders - .setup(mh => mh.get('set-cookie')) + .setup((mh) => mh.get('set-cookie')) .returns(() => `${sessionName}=${sessionValue}`) .verifiable(typemoq.Times.once()); mockSessionResponse - .setup(mr => mr.status) + .setup((mr) => mr.status) .returns(() => 302) .verifiable(typemoq.Times.once()); mockSessionResponse - .setup(mr => mr.headers) + .setup((mr) => mr.headers) .returns(() => mockSessionHeaders.object) .verifiable(typemoq.Times.once()); // typemoq doesn't love this comparison, so generalize it a bit //tslint:disable-next-line:no-http-string fetchMock - .setup(fm => + .setup((fm) => fm( 'https://TESTNAME:8888/login?', typemoq.It.isObjectWith({ @@ -210,21 +210,21 @@ suite('JupyterPasswordConnect', () => { const mockXsrfResponse = typemoq.Mock.ofType(nodeFetch.Response); const mockXsrfHeaders = typemoq.Mock.ofType(nodeFetch.Headers); mockXsrfHeaders - .setup(mh => mh.get('set-cookie')) + .setup((mh) => mh.get('set-cookie')) .returns(() => `_xsrf=${xsrfValue}`) .verifiable(typemoq.Times.never()); // Status set to not ok and header fetch should not be hit mockXsrfResponse - .setup(mr => mr.ok) + .setup((mr) => mr.ok) .returns(() => false) .verifiable(typemoq.Times.once()); mockXsrfResponse - .setup(mr => mr.headers) + .setup((mr) => mr.headers) .returns(() => mockXsrfHeaders.object) .verifiable(typemoq.Times.never()); fetchMock - .setup(fm => + .setup((fm) => //tslint:disable-next-line:no-http-string fm('http://TESTNAME:8888/login?', { method: 'get', diff --git a/src/test/datascience/jupyterUtils.unit.test.ts b/src/test/datascience/jupyterUtils.unit.test.ts index 6a75a50ee7ec..b5043c589540 100644 --- a/src/test/datascience/jupyterUtils.unit.test.ts +++ b/src/test/datascience/jupyterUtils.unit.test.ts @@ -11,7 +11,7 @@ import { expandWorkingDir, modifyTraceback } from '../../client/datascience/jupy suite('Data Science JupyterUtils', () => { const workspaceService = mock(WorkspaceService); // tslint:disable: no-invalid-template-strings - test('expanding file variables', async function() { + test('expanding file variables', async function () { // tslint:disable-next-line: no-invalid-this this.timeout(10000); const uri = Uri.file('test/bar'); diff --git a/src/test/datascience/jupyterVariables.unit.test.ts b/src/test/datascience/jupyterVariables.unit.test.ts index 893bbba9e646..51a0abba7e9b 100644 --- a/src/test/datascience/jupyterVariables.unit.test.ts +++ b/src/test/datascience/jupyterVariables.unit.test.ts @@ -105,8 +105,8 @@ suite('JupyterVariables', () => { const config = createTypeMoq('Config '); fileSystem = typemoq.Mock.ofType(); - fileSystem.setup(fs => fs.readFile(typemoq.It.isAnyString())).returns(() => Promise.resolve('test')); - config.setup(s => s.getSettings(typemoq.It.isAny())).returns(() => pythonSettings); + fileSystem.setup((fs) => fs.readFile(typemoq.It.isAnyString())).returns(() => Promise.resolve('test')); + config.setup((s) => s.getSettings(typemoq.It.isAny())).returns(() => pythonSettings); jupyterVariables = new JupyterVariables(fileSystem.object, config.object); }); @@ -114,7 +114,7 @@ suite('JupyterVariables', () => { // No cells, no output, no text/plain test('getVariables no cells', async () => { fakeNotebook - .setup(fs => + .setup((fs) => fs.execute( typemoq.It.isAny(), typemoq.It.isValue(Identifiers.EmptyFileName), @@ -146,7 +146,7 @@ suite('JupyterVariables', () => { test('getVariables no output', async () => { fakeNotebook - .setup(fs => + .setup((fs) => fs.execute( typemoq.It.isAny(), typemoq.It.isValue(Identifiers.EmptyFileName), @@ -178,7 +178,7 @@ suite('JupyterVariables', () => { test('getVariables bad output type', async () => { fakeNotebook - .setup(fs => + .setup((fs) => fs.execute( typemoq.It.isAny(), typemoq.It.isValue(Identifiers.EmptyFileName), @@ -210,7 +210,7 @@ suite('JupyterVariables', () => { test('getVariables fake data', async () => { fakeNotebook - .setup(fs => + .setup((fs) => fs.execute( typemoq.It.isAny(), typemoq.It.isValue(Identifiers.EmptyFileName), @@ -230,7 +230,7 @@ suite('JupyterVariables', () => { ) .verifiable(typemoq.Times.once()); fakeNotebook - .setup(fs => fs.inspect(typemoq.It.isAny())) + .setup((fs) => fs.inspect(typemoq.It.isAny())) .returns(() => Promise.resolve({ 'text/plain': `\u001b[1;31mType:\u001b[0m complex @@ -268,7 +268,7 @@ This is equivalent to (real + imag*1j) where imag defaults to 0. // getValue failure paths are shared with getVariables, so no need to test them here test('getValue fake data', async () => { fakeNotebook - .setup(fs => + .setup((fs) => fs.execute( typemoq.It.isAny(), typemoq.It.isValue(Identifiers.EmptyFileName), @@ -281,7 +281,7 @@ This is equivalent to (real + imag*1j) where imag defaults to 0. .returns(() => Promise.resolve(generateCells(`['big_complex']`, 'execute_result'))) .verifiable(typemoq.Times.once()); fakeNotebook - .setup(fs => fs.inspect(typemoq.It.isValue('big_complex'))) + .setup((fs) => fs.inspect(typemoq.It.isValue('big_complex'))) .returns(() => Promise.resolve({ 'text/plain': `\u001b[1;31mType:\u001b[0m complex diff --git a/src/test/datascience/liveshare.functional.test.tsx b/src/test/datascience/liveshare.functional.test.tsx index 0fd19c24cfd9..2f1a6817be12 100644 --- a/src/test/datascience/liveshare.functional.test.tsx +++ b/src/test/datascience/liveshare.functional.test.tsx @@ -1,388 +1,388 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import * as assert from 'assert'; -import { ReactWrapper } from 'enzyme'; -import * as React from 'react'; -import * as TypeMoq from 'typemoq'; -import { Disposable, Uri } from 'vscode'; -import * as vsls from 'vsls/vscode'; - -import { - IApplicationShell, - ICommandManager, - IDocumentManager, - ILiveShareApi, - ILiveShareTestingApi -} from '../../client/common/application/types'; -import { IFileSystem } from '../../client/common/platform/types'; -import { Commands } from '../../client/datascience/constants'; -import { InteractiveWindowMessages } from '../../client/datascience/interactive-common/interactiveWindowTypes'; -import { - ICodeWatcher, - IDataScienceCommandListener, - IInteractiveWindow, - IInteractiveWindowProvider, - IJupyterExecution -} from '../../client/datascience/types'; -import { DataScienceIocContainer } from './dataScienceIocContainer'; -import { createDocument } from './editor-integration/helpers'; -import { addMockData, CellPosition, mountConnectedMainPanel, verifyHtmlOnCell, waitForMessage } from './testHelpers'; - -//import { asyncDump } from '../common/asyncDump'; -//tslint:disable:trailing-comma no-any no-multiline-string - -// tslint:disable-next-line:max-func-body-length no-any -suite('DataScience LiveShare tests', () => { - const disposables: Disposable[] = []; - let hostContainer: DataScienceIocContainer; - let guestContainer: DataScienceIocContainer; - let lastErrorMessage: string | undefined; - - setup(async () => { - hostContainer = createContainer(vsls.Role.Host); - guestContainer = createContainer(vsls.Role.Guest); - return Promise.all([hostContainer.activate(), guestContainer.activate()]); - }); - - teardown(async () => { - for (const disposable of disposables) { - if (!disposable) { - continue; - } - // tslint:disable-next-line:no-any - const promise = disposable.dispose() as Promise; - if (promise) { - await promise; - } - } - await hostContainer.dispose(); - await guestContainer.dispose(); - lastErrorMessage = undefined; - }); - - suiteTeardown(() => { - //asyncDump(); - }); - - function createContainer(role: vsls.Role): DataScienceIocContainer { - const result = new DataScienceIocContainer(); - result.registerDataScienceTypes(); - - // Rebind the appshell so we can change what happens on an error - const dummyDisposable = { - dispose: () => { - return; - } - }; - const appShell = TypeMoq.Mock.ofType(); - appShell.setup(a => a.showErrorMessage(TypeMoq.It.isAnyString())).returns(e => (lastErrorMessage = e)); - appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve('')); - appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns((_a1: string, a2: string, _a3: string) => Promise.resolve(a2)); - appShell - .setup(a => a.showSaveDialog(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(Uri.file('test.ipynb'))); - appShell.setup(a => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); - - result.serviceManager.rebindInstance(IApplicationShell, appShell.object); - - // Setup our webview panel - result.createWebView(() => mountConnectedMainPanel('interactive'), role); - - // Make sure the history provider and execution factory in the container is created (the extension does this on startup in the extension) - // This is necessary to get the appropriate live share services up and running. - result.get(IInteractiveWindowProvider); - result.get(IJupyterExecution); - return result; - } - - async function getOrCreateInteractiveWindow(role: vsls.Role): Promise { - // Get the container to use based on the role. - const container = role === vsls.Role.Host ? hostContainer : guestContainer; - const window = await container!.get(IInteractiveWindowProvider).getOrCreateActive(); - await window.show(); - return window; - } - - function isSessionStarted(role: vsls.Role): boolean { - const container = role === vsls.Role.Host ? hostContainer : guestContainer; - const api = container!.get(ILiveShareApi) as ILiveShareTestingApi; - return api.isSessionStarted; - } - - async function waitForResults( - role: vsls.Role, - resultGenerator: (both: boolean) => Promise - ): Promise, React.Component>> { - const container = role === vsls.Role.Host ? hostContainer : guestContainer; - - // If just the host session has started or nobody, just run the host. - const guestStarted = isSessionStarted(vsls.Role.Guest); - if (!guestStarted) { - const hostRenderPromise = waitForMessage(hostContainer, InteractiveWindowMessages.ExecutionRendered); - - // Generate our results - await resultGenerator(false); - - // Wait for all of the renders to go through - await hostRenderPromise; - } else { - // Otherwise more complicated. We have to wait for renders on both - - // Get a render promise with the expected number of renders for both wrappers - const hostRenderPromise = waitForMessage(hostContainer, InteractiveWindowMessages.ExecutionRendered); - const guestRenderPromise = waitForMessage(guestContainer, InteractiveWindowMessages.ExecutionRendered); - - // Generate our results - await resultGenerator(true); - - // Wait for all of the renders to go through. Guest may have been shutdown by now. - await Promise.all([ - hostRenderPromise, - isSessionStarted(vsls.Role.Guest) ? guestRenderPromise : Promise.resolve() - ]); - } - return container.wrapper!; - } - - async function addCodeToRole( - role: vsls.Role, - code: string - ): Promise, React.Component>> { - return waitForResults(role, async (both: boolean) => { - if (!both) { - const history = await getOrCreateInteractiveWindow(role); - await history.addCode(code, Uri.file('foo.py').fsPath, 2); - } else { - // Add code to the apropriate container - const host = await getOrCreateInteractiveWindow(vsls.Role.Host); - - // Make sure guest is still creatable - if (isSessionStarted(vsls.Role.Guest)) { - const guest = await getOrCreateInteractiveWindow(vsls.Role.Guest); - role === vsls.Role.Host - ? await host.addCode(code, Uri.file('foo.py').fsPath, 2) - : await guest.addCode(code, Uri.file('foo.py').fsPath, 2); - } else { - await host.addCode(code, Uri.file('foo.py').fsPath, 2); - } - } - }); - } - - function startSession(role: vsls.Role): Promise { - const container = role === vsls.Role.Host ? hostContainer : guestContainer; - const api = container!.get(ILiveShareApi) as ILiveShareTestingApi; - return api.startSession(); - } - - function stopSession(role: vsls.Role): Promise { - const container = role === vsls.Role.Host ? hostContainer : guestContainer; - const api = container!.get(ILiveShareApi) as ILiveShareTestingApi; - return api.stopSession(); - } - - function disableGuestChecker(role: vsls.Role) { - const container = role === vsls.Role.Host ? hostContainer : guestContainer; - const api = container!.get(ILiveShareApi) as ILiveShareTestingApi; - api.disableGuestChecker(); - } - - test('Host alone', async () => { - // Should only need mock data in host - addMockData(hostContainer!, 'a=1\na', 1); - - // Start the host session first - await startSession(vsls.Role.Host); - - // Just run some code in the host - const wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - }); - - test('Host & Guest Simple', async function() { - // tslint:disable-next-line: no-invalid-this - return this.skip(); - // Should only need mock data in host - addMockData(hostContainer!, 'a=1\na', 1); - - // Create the host history and then the guest history - await getOrCreateInteractiveWindow(vsls.Role.Host); - await startSession(vsls.Role.Host); - await getOrCreateInteractiveWindow(vsls.Role.Guest); - await startSession(vsls.Role.Guest); - - // Send code through the host - const wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - - // Verify it ended up on the guest too - assert.ok(guestContainer.wrapper, 'Guest wrapper not created'); - verifyHtmlOnCell(guestContainer.wrapper!, 'InteractiveCell', '1', CellPosition.Last); - }); - - test('Host starts LiveShare after starting Jupyter', async function() { - // tslint:disable-next-line: no-invalid-this - return this.skip(); - addMockData(hostContainer!, 'a=1\na', 1); - addMockData(hostContainer!, 'b=2\nb', 2); - await getOrCreateInteractiveWindow(vsls.Role.Host); - let wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - - await startSession(vsls.Role.Host); - await getOrCreateInteractiveWindow(vsls.Role.Guest); - await startSession(vsls.Role.Guest); - - wrapper = await addCodeToRole(vsls.Role.Host, 'b=2\nb'); - - assert.ok(guestContainer.wrapper, 'Guest wrapper not created'); - verifyHtmlOnCell(guestContainer.wrapper!, 'InteractiveCell', '2', CellPosition.Last); - }); - - test('Host Shutdown and Run', async () => { - // Should only need mock data in host - addMockData(hostContainer!, 'a=1\na', 1); - - // Create the host history and then the guest history - await getOrCreateInteractiveWindow(vsls.Role.Host); - await startSession(vsls.Role.Host); - - // Send code through the host - let wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - - // Stop the session - await stopSession(vsls.Role.Host); - - // Send code again. It should still work. - wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - }); - - test('Host startup and guest restart', async function() { - // tslint:disable-next-line: no-invalid-this - return this.skip(); - // Should only need mock data in host - addMockData(hostContainer!, 'a=1\na', 1); - - // Start the host, and add some data - const host = await getOrCreateInteractiveWindow(vsls.Role.Host); - await startSession(vsls.Role.Host); - - // Send code through the host - let wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - - // Shutdown the host - await host.dispose(); - - // Startup a guest and run some code. - await startSession(vsls.Role.Guest); - wrapper = await addCodeToRole(vsls.Role.Guest, 'a=1\na'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - - assert.ok(hostContainer.wrapper, 'Host wrapper not created'); - verifyHtmlOnCell(hostContainer.wrapper!, 'InteractiveCell', '1', CellPosition.Last); - }); - - test('Going through codewatcher', async () => { - // Should only need mock data in host - addMockData(hostContainer!, '#%%\na=1\na', 1); - - // Start both the host and the guest - await startSession(vsls.Role.Host); - await startSession(vsls.Role.Guest); - - // Setup a document and text - const fileName = 'test.py'; - const version = 1; - const inputText = '#%%\na=1\na'; - const document = createDocument(inputText, fileName, version, TypeMoq.Times.atLeastOnce()); - document.setup(doc => doc.getText(TypeMoq.It.isAny())).returns(() => inputText); - - const codeWatcher = guestContainer!.get(ICodeWatcher); - codeWatcher.setDocument(document.object); - - // Send code using a codewatcher instead (we're sending it through the guest) - const wrapper = await waitForResults(vsls.Role.Guest, async (both: boolean) => { - // Should always be both - assert.ok(both, 'Expected both guest and host to be used'); - await codeWatcher.runAllCells(); - }); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - assert.ok(hostContainer.wrapper, 'Host wrapper not created for some reason'); - verifyHtmlOnCell(hostContainer.wrapper!, 'InteractiveCell', '1', CellPosition.Last); - }); - - test('Export from guest', async () => { - // Should only need mock data in host - addMockData(hostContainer!, '#%%\na=1\na', 1); - - // Remap the fileSystem so we control the write for the notebook. Have to do this - // before the listener is created so that it uses this file system. - let outputContents: string | undefined; - const fileSystem = TypeMoq.Mock.ofType(); - guestContainer!.serviceManager.rebindInstance(IFileSystem, fileSystem.object); - fileSystem - .setup(f => f.writeFile(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns((_f, c) => { - outputContents = c.toString(); - return Promise.resolve(); - }); - fileSystem.setup(f => f.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => true); - fileSystem.setup(f => f.getSubDirectories(TypeMoq.It.isAny())).returns(() => Promise.resolve([])); - fileSystem.setup(f => f.directoryExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); - - // Need to register commands as our extension isn't actually loading. - const listeners = guestContainer!.getAll(IDataScienceCommandListener); - const guestCommandManager = guestContainer!.get(ICommandManager); - listeners.forEach(f => f.register(guestCommandManager)); - - // Start both the host and the guest - await startSession(vsls.Role.Host); - await startSession(vsls.Role.Guest); - - // Create a document on the guest - guestContainer!.addDocument('#%%\na=1\na', Uri.file('foo.py').fsPath); - guestContainer!.get(IDocumentManager).showTextDocument(Uri.file('foo.py')); - - // Attempt to export a file from the guest by running an ExportFileAndOutputAsNotebook - const executePromise = guestCommandManager.executeCommand( - Commands.ExportFileAndOutputAsNotebook, - Uri.file('foo.py') - ) as Promise; - assert.ok(executePromise, 'Export file did not return a promise'); - const savedUri = await executePromise; - assert.ok(savedUri, 'Uri not returned from export'); - assert.equal(savedUri.fsPath, Uri.file('test.ipynb').fsPath, 'Export did not work'); - assert.ok(outputContents, 'Output not exported'); - assert.ok(outputContents!.includes('data'), 'Output is empty'); - }); - - test('Guest does not have extension', async () => { - // Should only need mock data in host - addMockData(hostContainer!, '#%%\na=1\na', 1); - - // Start just the host and verify it works - await startSession(vsls.Role.Host); - let wrapper = await addCodeToRole(vsls.Role.Host, '#%%\na=1\na'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - - // Disable guest checking on the guest (same as if the guest doesn't have the python extension) - await startSession(vsls.Role.Guest); - disableGuestChecker(vsls.Role.Guest); - - // Host should now be in a state that if any code runs, the session should end. However - // the code should still run - wrapper = await addCodeToRole(vsls.Role.Host, '#%%\na=1\na'); - verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); - assert.equal(isSessionStarted(vsls.Role.Host), false, 'Host should have exited session'); - assert.equal(isSessionStarted(vsls.Role.Guest), false, 'Guest should have exited session'); - assert.ok(lastErrorMessage, 'Error was not set during session shutdown'); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import * as assert from 'assert'; +import { ReactWrapper } from 'enzyme'; +import * as React from 'react'; +import * as TypeMoq from 'typemoq'; +import { Disposable, Uri } from 'vscode'; +import * as vsls from 'vsls/vscode'; + +import { + IApplicationShell, + ICommandManager, + IDocumentManager, + ILiveShareApi, + ILiveShareTestingApi +} from '../../client/common/application/types'; +import { IFileSystem } from '../../client/common/platform/types'; +import { Commands } from '../../client/datascience/constants'; +import { InteractiveWindowMessages } from '../../client/datascience/interactive-common/interactiveWindowTypes'; +import { + ICodeWatcher, + IDataScienceCommandListener, + IInteractiveWindow, + IInteractiveWindowProvider, + IJupyterExecution +} from '../../client/datascience/types'; +import { DataScienceIocContainer } from './dataScienceIocContainer'; +import { createDocument } from './editor-integration/helpers'; +import { addMockData, CellPosition, mountConnectedMainPanel, verifyHtmlOnCell, waitForMessage } from './testHelpers'; + +//import { asyncDump } from '../common/asyncDump'; +//tslint:disable:trailing-comma no-any no-multiline-string + +// tslint:disable-next-line:max-func-body-length no-any +suite('DataScience LiveShare tests', () => { + const disposables: Disposable[] = []; + let hostContainer: DataScienceIocContainer; + let guestContainer: DataScienceIocContainer; + let lastErrorMessage: string | undefined; + + setup(async () => { + hostContainer = createContainer(vsls.Role.Host); + guestContainer = createContainer(vsls.Role.Guest); + return Promise.all([hostContainer.activate(), guestContainer.activate()]); + }); + + teardown(async () => { + for (const disposable of disposables) { + if (!disposable) { + continue; + } + // tslint:disable-next-line:no-any + const promise = disposable.dispose() as Promise; + if (promise) { + await promise; + } + } + await hostContainer.dispose(); + await guestContainer.dispose(); + lastErrorMessage = undefined; + }); + + suiteTeardown(() => { + //asyncDump(); + }); + + function createContainer(role: vsls.Role): DataScienceIocContainer { + const result = new DataScienceIocContainer(); + result.registerDataScienceTypes(); + + // Rebind the appshell so we can change what happens on an error + const dummyDisposable = { + dispose: () => { + return; + } + }; + const appShell = TypeMoq.Mock.ofType(); + appShell.setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString())).returns((e) => (lastErrorMessage = e)); + appShell + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(() => Promise.resolve('')); + appShell + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((_a1: string, a2: string, _a3: string) => Promise.resolve(a2)); + appShell + .setup((a) => a.showSaveDialog(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(Uri.file('test.ipynb'))); + appShell.setup((a) => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); + + result.serviceManager.rebindInstance(IApplicationShell, appShell.object); + + // Setup our webview panel + result.createWebView(() => mountConnectedMainPanel('interactive'), role); + + // Make sure the history provider and execution factory in the container is created (the extension does this on startup in the extension) + // This is necessary to get the appropriate live share services up and running. + result.get(IInteractiveWindowProvider); + result.get(IJupyterExecution); + return result; + } + + async function getOrCreateInteractiveWindow(role: vsls.Role): Promise { + // Get the container to use based on the role. + const container = role === vsls.Role.Host ? hostContainer : guestContainer; + const window = await container!.get(IInteractiveWindowProvider).getOrCreateActive(); + await window.show(); + return window; + } + + function isSessionStarted(role: vsls.Role): boolean { + const container = role === vsls.Role.Host ? hostContainer : guestContainer; + const api = container!.get(ILiveShareApi) as ILiveShareTestingApi; + return api.isSessionStarted; + } + + async function waitForResults( + role: vsls.Role, + resultGenerator: (both: boolean) => Promise + ): Promise, React.Component>> { + const container = role === vsls.Role.Host ? hostContainer : guestContainer; + + // If just the host session has started or nobody, just run the host. + const guestStarted = isSessionStarted(vsls.Role.Guest); + if (!guestStarted) { + const hostRenderPromise = waitForMessage(hostContainer, InteractiveWindowMessages.ExecutionRendered); + + // Generate our results + await resultGenerator(false); + + // Wait for all of the renders to go through + await hostRenderPromise; + } else { + // Otherwise more complicated. We have to wait for renders on both + + // Get a render promise with the expected number of renders for both wrappers + const hostRenderPromise = waitForMessage(hostContainer, InteractiveWindowMessages.ExecutionRendered); + const guestRenderPromise = waitForMessage(guestContainer, InteractiveWindowMessages.ExecutionRendered); + + // Generate our results + await resultGenerator(true); + + // Wait for all of the renders to go through. Guest may have been shutdown by now. + await Promise.all([ + hostRenderPromise, + isSessionStarted(vsls.Role.Guest) ? guestRenderPromise : Promise.resolve() + ]); + } + return container.wrapper!; + } + + async function addCodeToRole( + role: vsls.Role, + code: string + ): Promise, React.Component>> { + return waitForResults(role, async (both: boolean) => { + if (!both) { + const history = await getOrCreateInteractiveWindow(role); + await history.addCode(code, Uri.file('foo.py').fsPath, 2); + } else { + // Add code to the apropriate container + const host = await getOrCreateInteractiveWindow(vsls.Role.Host); + + // Make sure guest is still creatable + if (isSessionStarted(vsls.Role.Guest)) { + const guest = await getOrCreateInteractiveWindow(vsls.Role.Guest); + role === vsls.Role.Host + ? await host.addCode(code, Uri.file('foo.py').fsPath, 2) + : await guest.addCode(code, Uri.file('foo.py').fsPath, 2); + } else { + await host.addCode(code, Uri.file('foo.py').fsPath, 2); + } + } + }); + } + + function startSession(role: vsls.Role): Promise { + const container = role === vsls.Role.Host ? hostContainer : guestContainer; + const api = container!.get(ILiveShareApi) as ILiveShareTestingApi; + return api.startSession(); + } + + function stopSession(role: vsls.Role): Promise { + const container = role === vsls.Role.Host ? hostContainer : guestContainer; + const api = container!.get(ILiveShareApi) as ILiveShareTestingApi; + return api.stopSession(); + } + + function disableGuestChecker(role: vsls.Role) { + const container = role === vsls.Role.Host ? hostContainer : guestContainer; + const api = container!.get(ILiveShareApi) as ILiveShareTestingApi; + api.disableGuestChecker(); + } + + test('Host alone', async () => { + // Should only need mock data in host + addMockData(hostContainer!, 'a=1\na', 1); + + // Start the host session first + await startSession(vsls.Role.Host); + + // Just run some code in the host + const wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + }); + + test('Host & Guest Simple', async function () { + // tslint:disable-next-line: no-invalid-this + return this.skip(); + // Should only need mock data in host + addMockData(hostContainer!, 'a=1\na', 1); + + // Create the host history and then the guest history + await getOrCreateInteractiveWindow(vsls.Role.Host); + await startSession(vsls.Role.Host); + await getOrCreateInteractiveWindow(vsls.Role.Guest); + await startSession(vsls.Role.Guest); + + // Send code through the host + const wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + + // Verify it ended up on the guest too + assert.ok(guestContainer.wrapper, 'Guest wrapper not created'); + verifyHtmlOnCell(guestContainer.wrapper!, 'InteractiveCell', '1', CellPosition.Last); + }); + + test('Host starts LiveShare after starting Jupyter', async function () { + // tslint:disable-next-line: no-invalid-this + return this.skip(); + addMockData(hostContainer!, 'a=1\na', 1); + addMockData(hostContainer!, 'b=2\nb', 2); + await getOrCreateInteractiveWindow(vsls.Role.Host); + let wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + + await startSession(vsls.Role.Host); + await getOrCreateInteractiveWindow(vsls.Role.Guest); + await startSession(vsls.Role.Guest); + + wrapper = await addCodeToRole(vsls.Role.Host, 'b=2\nb'); + + assert.ok(guestContainer.wrapper, 'Guest wrapper not created'); + verifyHtmlOnCell(guestContainer.wrapper!, 'InteractiveCell', '2', CellPosition.Last); + }); + + test('Host Shutdown and Run', async () => { + // Should only need mock data in host + addMockData(hostContainer!, 'a=1\na', 1); + + // Create the host history and then the guest history + await getOrCreateInteractiveWindow(vsls.Role.Host); + await startSession(vsls.Role.Host); + + // Send code through the host + let wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + + // Stop the session + await stopSession(vsls.Role.Host); + + // Send code again. It should still work. + wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + }); + + test('Host startup and guest restart', async function () { + // tslint:disable-next-line: no-invalid-this + return this.skip(); + // Should only need mock data in host + addMockData(hostContainer!, 'a=1\na', 1); + + // Start the host, and add some data + const host = await getOrCreateInteractiveWindow(vsls.Role.Host); + await startSession(vsls.Role.Host); + + // Send code through the host + let wrapper = await addCodeToRole(vsls.Role.Host, 'a=1\na'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + + // Shutdown the host + await host.dispose(); + + // Startup a guest and run some code. + await startSession(vsls.Role.Guest); + wrapper = await addCodeToRole(vsls.Role.Guest, 'a=1\na'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + + assert.ok(hostContainer.wrapper, 'Host wrapper not created'); + verifyHtmlOnCell(hostContainer.wrapper!, 'InteractiveCell', '1', CellPosition.Last); + }); + + test('Going through codewatcher', async () => { + // Should only need mock data in host + addMockData(hostContainer!, '#%%\na=1\na', 1); + + // Start both the host and the guest + await startSession(vsls.Role.Host); + await startSession(vsls.Role.Guest); + + // Setup a document and text + const fileName = 'test.py'; + const version = 1; + const inputText = '#%%\na=1\na'; + const document = createDocument(inputText, fileName, version, TypeMoq.Times.atLeastOnce()); + document.setup((doc) => doc.getText(TypeMoq.It.isAny())).returns(() => inputText); + + const codeWatcher = guestContainer!.get(ICodeWatcher); + codeWatcher.setDocument(document.object); + + // Send code using a codewatcher instead (we're sending it through the guest) + const wrapper = await waitForResults(vsls.Role.Guest, async (both: boolean) => { + // Should always be both + assert.ok(both, 'Expected both guest and host to be used'); + await codeWatcher.runAllCells(); + }); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + assert.ok(hostContainer.wrapper, 'Host wrapper not created for some reason'); + verifyHtmlOnCell(hostContainer.wrapper!, 'InteractiveCell', '1', CellPosition.Last); + }); + + test('Export from guest', async () => { + // Should only need mock data in host + addMockData(hostContainer!, '#%%\na=1\na', 1); + + // Remap the fileSystem so we control the write for the notebook. Have to do this + // before the listener is created so that it uses this file system. + let outputContents: string | undefined; + const fileSystem = TypeMoq.Mock.ofType(); + guestContainer!.serviceManager.rebindInstance(IFileSystem, fileSystem.object); + fileSystem + .setup((f) => f.writeFile(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((_f, c) => { + outputContents = c.toString(); + return Promise.resolve(); + }); + fileSystem.setup((f) => f.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => true); + fileSystem.setup((f) => f.getSubDirectories(TypeMoq.It.isAny())).returns(() => Promise.resolve([])); + fileSystem.setup((f) => f.directoryExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); + + // Need to register commands as our extension isn't actually loading. + const listeners = guestContainer!.getAll(IDataScienceCommandListener); + const guestCommandManager = guestContainer!.get(ICommandManager); + listeners.forEach((f) => f.register(guestCommandManager)); + + // Start both the host and the guest + await startSession(vsls.Role.Host); + await startSession(vsls.Role.Guest); + + // Create a document on the guest + guestContainer!.addDocument('#%%\na=1\na', Uri.file('foo.py').fsPath); + guestContainer!.get(IDocumentManager).showTextDocument(Uri.file('foo.py')); + + // Attempt to export a file from the guest by running an ExportFileAndOutputAsNotebook + const executePromise = guestCommandManager.executeCommand( + Commands.ExportFileAndOutputAsNotebook, + Uri.file('foo.py') + ) as Promise; + assert.ok(executePromise, 'Export file did not return a promise'); + const savedUri = await executePromise; + assert.ok(savedUri, 'Uri not returned from export'); + assert.equal(savedUri.fsPath, Uri.file('test.ipynb').fsPath, 'Export did not work'); + assert.ok(outputContents, 'Output not exported'); + assert.ok(outputContents!.includes('data'), 'Output is empty'); + }); + + test('Guest does not have extension', async () => { + // Should only need mock data in host + addMockData(hostContainer!, '#%%\na=1\na', 1); + + // Start just the host and verify it works + await startSession(vsls.Role.Host); + let wrapper = await addCodeToRole(vsls.Role.Host, '#%%\na=1\na'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + + // Disable guest checking on the guest (same as if the guest doesn't have the python extension) + await startSession(vsls.Role.Guest); + disableGuestChecker(vsls.Role.Guest); + + // Host should now be in a state that if any code runs, the session should end. However + // the code should still run + wrapper = await addCodeToRole(vsls.Role.Host, '#%%\na=1\na'); + verifyHtmlOnCell(wrapper, 'InteractiveCell', '1', CellPosition.Last); + assert.equal(isSessionStarted(vsls.Role.Host), false, 'Host should have exited session'); + assert.equal(isSessionStarted(vsls.Role.Guest), false, 'Guest should have exited session'); + assert.ok(lastErrorMessage, 'Error was not set during session shutdown'); + }); +}); diff --git a/src/test/datascience/mockCommandManager.ts b/src/test/datascience/mockCommandManager.ts index 91e4dcd77bf5..834f4218f5d9 100644 --- a/src/test/datascience/mockCommandManager.ts +++ b/src/test/datascience/mockCommandManager.ts @@ -1,55 +1,55 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { noop } from 'lodash'; -import { Disposable, TextEditor, TextEditorEdit } from 'vscode'; - -import { ICommandNameArgumentTypeMapping } from '../../client/common/application/commands'; -import { ICommandManager } from '../../client/common/application/types'; - -// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length - -export class MockCommandManager implements ICommandManager { - private commands: Map any> = new Map any>(); - - public registerCommand< - E extends keyof ICommandNameArgumentTypeMapping, - U extends ICommandNameArgumentTypeMapping[E] - >(command: E, callback: (...args: U) => any, thisArg?: any): Disposable { - this.commands.set(command, thisArg ? (callback.bind(thisArg) as any) : (callback as any)); - return { - dispose: () => { - noop(); - } - }; - } - - public registerTextEditorCommand( - _command: string, - _callback: (textEditor: TextEditor, edit: TextEditorEdit, ...args: any[]) => void, - _thisArg?: any - ): Disposable { - throw new Error('Method not implemented.'); - } - public executeCommand< - T, - E extends keyof ICommandNameArgumentTypeMapping, - U extends ICommandNameArgumentTypeMapping[E] - >(command: E, ...rest: U): Thenable { - const func = this.commands.get(command); - if (func) { - const result = func(...rest); - const tPromise = result as Promise; - if (tPromise) { - return tPromise; - } - return Promise.resolve(result); - } - return Promise.resolve(undefined); - } - - public getCommands(_filterInternal?: boolean): Thenable { - const keys = Object.keys(this.commands); - return Promise.resolve(keys); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { noop } from 'lodash'; +import { Disposable, TextEditor, TextEditorEdit } from 'vscode'; + +import { ICommandNameArgumentTypeMapping } from '../../client/common/application/commands'; +import { ICommandManager } from '../../client/common/application/types'; + +// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length + +export class MockCommandManager implements ICommandManager { + private commands: Map any> = new Map any>(); + + public registerCommand< + E extends keyof ICommandNameArgumentTypeMapping, + U extends ICommandNameArgumentTypeMapping[E] + >(command: E, callback: (...args: U) => any, thisArg?: any): Disposable { + this.commands.set(command, thisArg ? (callback.bind(thisArg) as any) : (callback as any)); + return { + dispose: () => { + noop(); + } + }; + } + + public registerTextEditorCommand( + _command: string, + _callback: (textEditor: TextEditor, edit: TextEditorEdit, ...args: any[]) => void, + _thisArg?: any + ): Disposable { + throw new Error('Method not implemented.'); + } + public executeCommand< + T, + E extends keyof ICommandNameArgumentTypeMapping, + U extends ICommandNameArgumentTypeMapping[E] + >(command: E, ...rest: U): Thenable { + const func = this.commands.get(command); + if (func) { + const result = func(...rest); + const tPromise = result as Promise; + if (tPromise) { + return tPromise; + } + return Promise.resolve(result); + } + return Promise.resolve(undefined); + } + + public getCommands(_filterInternal?: boolean): Thenable { + const keys = Object.keys(this.commands); + return Promise.resolve(keys); + } +} diff --git a/src/test/datascience/mockCustomEditorService.ts b/src/test/datascience/mockCustomEditorService.ts index eb859669d515..e8cae4e88419 100644 --- a/src/test/datascience/mockCustomEditorService.ts +++ b/src/test/datascience/mockCustomEditorService.ts @@ -63,9 +63,9 @@ export class MockCustomEditorService implements ICustomEditorService { } public undo(file: Uri) { - this.popAndApply(file, this.undoStack, this.redoStack, e => { + this.popAndApply(file, this.undoStack, this.redoStack, (e) => { this.getModel(file) - .then(m => { + .then((m) => { if (m) { m.undoEdits([e as NotebookModelChange]); } @@ -75,9 +75,9 @@ export class MockCustomEditorService implements ICustomEditorService { } public redo(file: Uri) { - this.popAndApply(file, this.redoStack, this.undoStack, e => { + this.popAndApply(file, this.redoStack, this.undoStack, (e) => { this.getModel(file) - .then(m => { + .then((m) => { if (m) { m.applyEdits([e as NotebookModelChange]); } @@ -145,7 +145,7 @@ export class MockCustomEditorService implements ICustomEditorService { private openedEditor(editor: INotebookEditor) { // Listen for model changes this.getModel(editor.file) - .then(m => { + .then((m) => { if (m) { m.onDidEdit(this.onEditChange.bind(this, editor.file)); } diff --git a/src/test/datascience/mockDebugService.ts b/src/test/datascience/mockDebugService.ts index ad83d75e97e3..236360cdd364 100644 --- a/src/test/datascience/mockDebugService.ts +++ b/src/test/datascience/mockDebugService.ts @@ -220,15 +220,15 @@ export class MockDebuggerService implements IDebugService, IDisposable { private sendBreakpoints(): Promise { // Only supporting a single file now - const sbs = this._breakpoints.map(b => b as SourceBreakpoint); + const sbs = this._breakpoints.map((b) => b as SourceBreakpoint); const file = sbs[0].location.uri.fsPath; return this.sendMessage('setBreakpoints', { source: { name: path.basename(file), path: file }, - lines: sbs.map(sb => sb.location.range.start.line), - breakpoints: sbs.map(sb => { + lines: sbs.map((sb) => sb.location.range.start.line), + breakpoints: sbs.map((sb) => { return { line: sb.location.range.start.line }; }), sourceModified: true @@ -276,7 +276,7 @@ export class MockDebuggerService implements IDebugService, IDisposable { private async sendMessage(command: string, args?: any): Promise { const response = createDeferred(); this.protocolParser.once(`response_${command}`, () => response.resolve()); - this.socket!.on('error', err => response.reject(err)); + this.socket!.on('error', (err) => response.reject(err)); await this.emitMessage(command, args); await response.promise; } diff --git a/src/test/datascience/mockDocumentManager.ts b/src/test/datascience/mockDocumentManager.ts index 2691c7a330cb..56451411810e 100644 --- a/src/test/datascience/mockDocumentManager.ts +++ b/src/test/datascience/mockDocumentManager.ts @@ -1,140 +1,140 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import * as path from 'path'; -import { - DecorationRenderOptions, - Event, - EventEmitter, - Range, - TextDocument, - TextDocumentChangeEvent, - TextDocumentShowOptions, - TextEditor, - TextEditorDecorationType, - TextEditorOptionsChangeEvent, - TextEditorSelectionChangeEvent, - TextEditorViewColumnChangeEvent, - Uri, - ViewColumn, - WorkspaceEdit -} from 'vscode'; - -import { IDocumentManager } from '../../client/common/application/types'; -import { EXTENSION_ROOT_DIR } from '../../client/constants'; -import { MockDocument } from './mockDocument'; -import { MockEditor } from './mockTextEditor'; - -// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length - -export class MockDocumentManager implements IDocumentManager { - public textDocuments: TextDocument[] = []; - public activeTextEditor: TextEditor | undefined; - public visibleTextEditors: TextEditor[] = []; - public didChangeActiveTextEditorEmitter = new EventEmitter(); - private didOpenEmitter = new EventEmitter(); - private didChangeVisibleEmitter = new EventEmitter(); - private didChangeTextEditorSelectionEmitter = new EventEmitter(); - private didChangeTextEditorOptionsEmitter = new EventEmitter(); - private didChangeTextEditorViewColumnEmitter = new EventEmitter(); - private didCloseEmitter = new EventEmitter(); - private didSaveEmitter = new EventEmitter(); - private didChangeTextDocumentEmitter = new EventEmitter(); - public get onDidChangeActiveTextEditor(): Event { - return this.didChangeActiveTextEditorEmitter.event; - } - public get onDidChangeTextDocument(): Event { - return this.didChangeTextDocumentEmitter.event; - } - public get onDidOpenTextDocument(): Event { - return this.didOpenEmitter.event; - } - public get onDidChangeVisibleTextEditors(): Event { - return this.didChangeVisibleEmitter.event; - } - public get onDidChangeTextEditorSelection(): Event { - return this.didChangeTextEditorSelectionEmitter.event; - } - public get onDidChangeTextEditorOptions(): Event { - return this.didChangeTextEditorOptionsEmitter.event; - } - public get onDidChangeTextEditorViewColumn(): Event { - return this.didChangeTextEditorViewColumnEmitter.event; - } - public get onDidCloseTextDocument(): Event { - return this.didCloseEmitter.event; - } - public get onDidSaveTextDocument(): Event { - return this.didSaveEmitter.event; - } - public showTextDocument( - _document: TextDocument, - _column?: ViewColumn, - _preserveFocus?: boolean - ): Thenable; - public showTextDocument(_document: TextDocument | Uri, _options?: TextDocumentShowOptions): Thenable; - public showTextDocument(document: any, _column?: any, _preserveFocus?: any): Thenable { - this.visibleTextEditors.push(document); - const mockEditor = new MockEditor(this, this.lastDocument as MockDocument); - this.activeTextEditor = mockEditor; - this.didChangeActiveTextEditorEmitter.fire(this.activeTextEditor); - return Promise.resolve(mockEditor); - } - public openTextDocument(_fileName: string | Uri): Thenable; - public openTextDocument(_options?: { language?: string; content?: string }): Thenable; - public openTextDocument(_options?: any): Thenable { - if (_options && _options.content) { - const doc = new MockDocument(_options.content, 'Untitled-1', this.saveDocument); - this.textDocuments.push(doc); - } - return Promise.resolve(this.lastDocument); - } - public applyEdit(_edit: WorkspaceEdit): Thenable { - throw new Error('Method not implemented.'); - } - - public addDocument(code: string, file: string) { - const mockDoc = new MockDocument(code, file, this.saveDocument); - this.textDocuments.push(mockDoc); - } - - public changeDocument(file: string, changes: { range: Range; newText: string }[]) { - const doc = this.textDocuments.find(d => d.uri.fsPath === Uri.file(file).fsPath) as MockDocument; - if (doc) { - const contentChanges = changes.map(c => { - const startOffset = doc.offsetAt(c.range.start); - const endOffset = doc.offsetAt(c.range.end); - return { - range: c.range, - rangeOffset: startOffset, - rangeLength: endOffset - startOffset, - text: c.newText - }; - }); - const ev: TextDocumentChangeEvent = { - document: doc, - contentChanges - }; - // Changes are applied to the doc before it's sent. - ev.contentChanges.forEach(doc.edit.bind(doc)); - this.didChangeTextDocumentEmitter.fire(ev); - } - } - - public createTextEditorDecorationType(_options: DecorationRenderOptions): TextEditorDecorationType { - throw new Error('Method not implemented'); - } - - private get lastDocument(): TextDocument { - if (this.textDocuments.length > 0) { - return this.textDocuments[this.textDocuments.length - 1]; - } - throw new Error('No documents in MockDocumentManager'); - } - - private saveDocument = (doc: TextDocument): Promise => { - // Create a new document with the contents of the doc passed in - this.addDocument(doc.getText(), path.join(EXTENSION_ROOT_DIR, 'baz.py')); - return Promise.resolve(true); - }; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import * as path from 'path'; +import { + DecorationRenderOptions, + Event, + EventEmitter, + Range, + TextDocument, + TextDocumentChangeEvent, + TextDocumentShowOptions, + TextEditor, + TextEditorDecorationType, + TextEditorOptionsChangeEvent, + TextEditorSelectionChangeEvent, + TextEditorViewColumnChangeEvent, + Uri, + ViewColumn, + WorkspaceEdit +} from 'vscode'; + +import { IDocumentManager } from '../../client/common/application/types'; +import { EXTENSION_ROOT_DIR } from '../../client/constants'; +import { MockDocument } from './mockDocument'; +import { MockEditor } from './mockTextEditor'; + +// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length + +export class MockDocumentManager implements IDocumentManager { + public textDocuments: TextDocument[] = []; + public activeTextEditor: TextEditor | undefined; + public visibleTextEditors: TextEditor[] = []; + public didChangeActiveTextEditorEmitter = new EventEmitter(); + private didOpenEmitter = new EventEmitter(); + private didChangeVisibleEmitter = new EventEmitter(); + private didChangeTextEditorSelectionEmitter = new EventEmitter(); + private didChangeTextEditorOptionsEmitter = new EventEmitter(); + private didChangeTextEditorViewColumnEmitter = new EventEmitter(); + private didCloseEmitter = new EventEmitter(); + private didSaveEmitter = new EventEmitter(); + private didChangeTextDocumentEmitter = new EventEmitter(); + public get onDidChangeActiveTextEditor(): Event { + return this.didChangeActiveTextEditorEmitter.event; + } + public get onDidChangeTextDocument(): Event { + return this.didChangeTextDocumentEmitter.event; + } + public get onDidOpenTextDocument(): Event { + return this.didOpenEmitter.event; + } + public get onDidChangeVisibleTextEditors(): Event { + return this.didChangeVisibleEmitter.event; + } + public get onDidChangeTextEditorSelection(): Event { + return this.didChangeTextEditorSelectionEmitter.event; + } + public get onDidChangeTextEditorOptions(): Event { + return this.didChangeTextEditorOptionsEmitter.event; + } + public get onDidChangeTextEditorViewColumn(): Event { + return this.didChangeTextEditorViewColumnEmitter.event; + } + public get onDidCloseTextDocument(): Event { + return this.didCloseEmitter.event; + } + public get onDidSaveTextDocument(): Event { + return this.didSaveEmitter.event; + } + public showTextDocument( + _document: TextDocument, + _column?: ViewColumn, + _preserveFocus?: boolean + ): Thenable; + public showTextDocument(_document: TextDocument | Uri, _options?: TextDocumentShowOptions): Thenable; + public showTextDocument(document: any, _column?: any, _preserveFocus?: any): Thenable { + this.visibleTextEditors.push(document); + const mockEditor = new MockEditor(this, this.lastDocument as MockDocument); + this.activeTextEditor = mockEditor; + this.didChangeActiveTextEditorEmitter.fire(this.activeTextEditor); + return Promise.resolve(mockEditor); + } + public openTextDocument(_fileName: string | Uri): Thenable; + public openTextDocument(_options?: { language?: string; content?: string }): Thenable; + public openTextDocument(_options?: any): Thenable { + if (_options && _options.content) { + const doc = new MockDocument(_options.content, 'Untitled-1', this.saveDocument); + this.textDocuments.push(doc); + } + return Promise.resolve(this.lastDocument); + } + public applyEdit(_edit: WorkspaceEdit): Thenable { + throw new Error('Method not implemented.'); + } + + public addDocument(code: string, file: string) { + const mockDoc = new MockDocument(code, file, this.saveDocument); + this.textDocuments.push(mockDoc); + } + + public changeDocument(file: string, changes: { range: Range; newText: string }[]) { + const doc = this.textDocuments.find((d) => d.uri.fsPath === Uri.file(file).fsPath) as MockDocument; + if (doc) { + const contentChanges = changes.map((c) => { + const startOffset = doc.offsetAt(c.range.start); + const endOffset = doc.offsetAt(c.range.end); + return { + range: c.range, + rangeOffset: startOffset, + rangeLength: endOffset - startOffset, + text: c.newText + }; + }); + const ev: TextDocumentChangeEvent = { + document: doc, + contentChanges + }; + // Changes are applied to the doc before it's sent. + ev.contentChanges.forEach(doc.edit.bind(doc)); + this.didChangeTextDocumentEmitter.fire(ev); + } + } + + public createTextEditorDecorationType(_options: DecorationRenderOptions): TextEditorDecorationType { + throw new Error('Method not implemented'); + } + + private get lastDocument(): TextDocument { + if (this.textDocuments.length > 0) { + return this.textDocuments[this.textDocuments.length - 1]; + } + throw new Error('No documents in MockDocumentManager'); + } + + private saveDocument = (doc: TextDocument): Promise => { + // Create a new document with the contents of the doc passed in + this.addDocument(doc.getText(), path.join(EXTENSION_ROOT_DIR, 'baz.py')); + return Promise.resolve(true); + }; +} diff --git a/src/test/datascience/mockExtensions.ts b/src/test/datascience/mockExtensions.ts index 820dfaf717a6..9b25c811fd7e 100644 --- a/src/test/datascience/mockExtensions.ts +++ b/src/test/datascience/mockExtensions.ts @@ -1,21 +1,21 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { injectable } from 'inversify'; -import { Event, Extension, extensions } from 'vscode'; - -import { IExtensions } from '../../client/common/types'; - -// tslint:disable:no-any unified-signatures - -@injectable() -export class MockExtensions implements IExtensions { - public all: Extension[] = []; - public getExtension(_extensionId: string): Extension | undefined { - return undefined; - } - - public get onDidChange(): Event { - return extensions.onDidChange; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { injectable } from 'inversify'; +import { Event, Extension, extensions } from 'vscode'; + +import { IExtensions } from '../../client/common/types'; + +// tslint:disable:no-any unified-signatures + +@injectable() +export class MockExtensions implements IExtensions { + public all: Extension[] = []; + public getExtension(_extensionId: string): Extension | undefined { + return undefined; + } + + public get onDidChange(): Event { + return extensions.onDidChange; + } +} diff --git a/src/test/datascience/mockJupyterManager.ts b/src/test/datascience/mockJupyterManager.ts index e8ea67321d98..f7eef28ea83d 100644 --- a/src/test/datascience/mockJupyterManager.ts +++ b/src/test/datascience/mockJupyterManager.ts @@ -1,879 +1,877 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { nbformat } from '@jupyterlab/coreutils'; -import { ChildProcess } from 'child_process'; -import * as fs from 'fs-extra'; -import * as os from 'os'; -import * as path from 'path'; -import { Observable } from 'rxjs/Observable'; -import * as tmp from 'tmp'; -import * as TypeMoq from 'typemoq'; -import * as uuid from 'uuid/v4'; -import { EventEmitter, Uri } from 'vscode'; -import { CancellationToken } from 'vscode-jsonrpc'; - -import { Session } from '@jupyterlab/services'; -import { anything, instance, mock, when } from 'ts-mockito'; -import { Cancellation } from '../../client/common/cancellation'; -import { ProductInstaller } from '../../client/common/installer/productInstaller'; -import { - ExecutionResult, - IProcessServiceFactory, - IPythonExecutionFactory, - Output -} from '../../client/common/process/types'; -import { IConfigurationService, IInstaller, Product } from '../../client/common/types'; -import { EXTENSION_ROOT_DIR } from '../../client/constants'; -import { generateCells } from '../../client/datascience/cellFactory'; -import { CellMatcher } from '../../client/datascience/cellMatcher'; -import { CodeSnippits, Identifiers } from '../../client/datascience/constants'; -import { - ICell, - IConnection, - IJupyterKernel, - IJupyterKernelSpec, - IJupyterSession, - IJupyterSessionManager -} from '../../client/datascience/types'; -import { IInterpreterService, PythonInterpreter } from '../../client/interpreter/contracts'; -import { IServiceManager } from '../../client/ioc/types'; -import { concatMultilineStringInput } from '../../datascience-ui/common'; -import { noop, sleep } from '../core'; -import { MockJupyterSession } from './mockJupyterSession'; -import { MockProcessService } from './mockProcessService'; -import { MockPythonService } from './mockPythonService'; - -// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length - -const MockJupyterTimeDelay = 10; -const LineFeedRegEx = /(\r\n|\n)/g; - -export enum SupportedCommands { - none = 0, - ipykernel = 1, - nbconvert = 2, - notebook = 4, - kernelspec = 8, - all = 0xffff -} - -function createKernelSpecs(specs: { name: string; resourceDir: string }[]): Record { - const models: Record = {}; - specs.forEach(spec => { - models[spec.name] = { - resource_dir: spec.resourceDir, - spec: { - name: spec.name, - display_name: spec.name, - language: 'python' - } - }; - }); - return models; -} - -// This class is used to mock talking to jupyter. It mocks -// the process services, the interpreter services, the python services, and the jupyter session -export class MockJupyterManager implements IJupyterSessionManager { - public readonly productInstaller: IInstaller; - private pythonExecutionFactory = this.createTypeMoq('Python Exec Factory'); - private processServiceFactory = this.createTypeMoq('Process Exec Factory'); - private processService: MockProcessService = new MockProcessService(); - private interpreterService = this.createTypeMoq('Interpreter Service'); - private changedInterpreterEvent: EventEmitter = new EventEmitter(); - private installedInterpreters: PythonInterpreter[] = []; - private pythonServices: MockPythonService[] = []; - private activeInterpreter: PythonInterpreter | undefined; - private sessionTimeout: number | undefined; - private cellDictionary: Record = {}; - private kernelSpecs: { name: string; dir: string }[] = []; - private currentSession: MockJupyterSession | undefined; - private connInfo: IConnection | undefined; - private cleanTemp: (() => void) | undefined; - private pendingSessionFailure = false; - private pendingKernelChangeFailure = false; - - constructor(serviceManager: IServiceManager) { - // Make our process service factory always return this item - this.processServiceFactory.setup(p => p.create()).returns(() => Promise.resolve(this.processService)); - this.productInstaller = mock(ProductInstaller); - // Setup our interpreter service - this.interpreterService.setup(i => i.onDidChangeInterpreter).returns(() => this.changedInterpreterEvent.event); - this.interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(this.activeInterpreter)); - this.interpreterService - .setup(i => i.getInterpreters(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(this.installedInterpreters)); - this.interpreterService - .setup(i => i.getInterpreterDetails(TypeMoq.It.isAnyString())) - .returns(p => { - const found = this.installedInterpreters.find(i => i.path === p); - if (found) { - return Promise.resolve(found); - } - return Promise.reject('Unknown interpreter'); - }); - // Listen to configuration changes like the real interpreter service does so that we fire our settings changed event - const configService = serviceManager.get(IConfigurationService); - if (configService && configService !== null) { - configService.getSettings(undefined).onDidChange(this.onConfigChanged.bind(this, configService)); - } - - // Stick our services into the service manager - serviceManager.addSingletonInstance(IInterpreterService, this.interpreterService.object); - serviceManager.addSingletonInstance( - IPythonExecutionFactory, - this.pythonExecutionFactory.object - ); - serviceManager.addSingletonInstance( - IProcessServiceFactory, - this.processServiceFactory.object - ); - serviceManager.addSingletonInstance(IInstaller, instance(this.productInstaller)); - - // Setup our default kernel spec (this is just a dummy value) - // tslint:disable-next-line:no-octal-literal - this.kernelSpecs.push({ - name: '0e8519db-0895-416c-96df-fa80131ecea0', - dir: 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' - }); - - // Setup our default cells that happen for everything - this.addCell(CodeSnippits.MatplotLibInitSvg); - this.addCell(CodeSnippits.MatplotLibInitPng); - this.addCell(CodeSnippits.ConfigSvg); - this.addCell(CodeSnippits.ConfigPng); - this.addCell(CodeSnippits.UpdateCWDAndPath.format(path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience'))); - this.addCell( - CodeSnippits.UpdateCWDAndPath.format( - Uri.file(path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience')).fsPath - ) - ); - tmp.file((_e, p, _fd, cleanup) => { - this.addCell(CodeSnippits.UpdateCWDAndPath.format(path.dirname(p))); - this.cleanTemp = cleanup; - }); - this.addCell(`import sys\r\nsys.path.append('undefined')\r\nsys.path`); - this.addCell(`import ptvsd;ptvsd.enable_attach(('localhost', 0))`); - this.addCell(`import debugpy;debugpy.listen(('localhost', 0))`); - this.addCell("matplotlib.style.use('dark_background')"); - this.addCell(`matplotlib.rcParams.update(${Identifiers.MatplotLibDefaultParams})`); - this.addCell(`%cd "${path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience')}"`); - // When we have windows file names, we replace `\` with `\\`. - // Code is as follows `await this.notebook.execute(`__file__ = '${file.replace(/\\/g, '\\\\')}'`, file, line, uuid(), undefined, true); - // Found in src\client\datascience\interactive-common\interactiveBase.ts. - this.addCell(`%cd "${Uri.file(path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience')).fsPath}`); - // New root dir should be in the temp folder. - tmp.file((_e, p, _fd, cleanup) => { - this.addCell(`%cd "${path.dirname(p).toLowerCase()}"`); - this.cleanTemp = cleanup; - }); - this.addCell('import sys\r\nsys.version', '1.1.1.1'); - this.addCell('import sys\r\nsys.executable', 'python'); - this.addCell('import notebook\r\nnotebook.version_info', '1.1.1.1'); - - this.addCell(`__file__ = '${Uri.file('foo.py').fsPath}'`); - this.addCell(`__file__ = '${Uri.file('bar.py').fsPath}'`); - this.addCell(`__file__ = '${Uri.file('foo').fsPath}'`); - this.addCell(`__file__ = '${Uri.file('test.py').fsPath}'`); - - // When we have windows file names, we replace `\` with `\\`. - // Code is as follows `await this.notebook.execute(`__file__ = '${file.replace(/\\/g, '\\\\')}'`, file, line, uuid(), undefined, true); - // Found in src\client\datascience\interactive-common\interactiveBase.ts. - this.addCell(`__file__ = '${Uri.file('foo.py').fsPath.replace(/\\/g, '\\\\')}'`); - this.addCell(`__file__ = '${Uri.file('bar.py').fsPath.replace(/\\/g, '\\\\')}'`); - this.addCell(`__file__ = '${Uri.file('foo').fsPath.replace(/\\/g, '\\\\')}'`); - this.addCell(`__file__ = '${Uri.file('test.py').fsPath.replace(/\\/g, '\\\\')}'`); - this.addCell('import os\nos.getcwd()', `'${path.join(EXTENSION_ROOT_DIR)}'`); - this.addCell('import sys\nsys.path[0]', `'${path.join(EXTENSION_ROOT_DIR)}'`); - } - - public getConnInfo(): IConnection { - return this.connInfo!; - } - - public makeActive(interpreter: PythonInterpreter) { - this.activeInterpreter = interpreter; - } - - public getCurrentSession(): MockJupyterSession | undefined { - return this.currentSession; - } - - public forcePendingIdleFailure() { - this.pendingSessionFailure = true; - } - - public forcePendingKernelChangeFailure() { - this.pendingKernelChangeFailure = true; - } - - public getRunningKernels(): Promise { - return Promise.resolve([]); - } - - public getRunningSessions(): Promise { - return Promise.resolve([]); - } - - public setProcessDelay(timeout: number | undefined) { - this.processService.setDelay(timeout); - this.pythonServices.forEach(p => p.setDelay(timeout)); - } - - public addInterpreter( - interpreter: PythonInterpreter, - supportedCommands: SupportedCommands, - notebookStdErr?: string[], - notebookProc?: ChildProcess - ) { - this.installedInterpreters.push(interpreter); - - // Add the python calls first. - const pythonService = new MockPythonService(interpreter); - this.pythonServices.push(pythonService); - this.pythonExecutionFactory - .setup(f => - f.create( - TypeMoq.It.is(o => { - return o && o.pythonPath ? o.pythonPath === interpreter.path : false; - }) - ) - ) - .returns(() => Promise.resolve(pythonService)); - this.pythonExecutionFactory - .setup(f => - f.createDaemon( - TypeMoq.It.is(o => { - return o && o.pythonPath ? o.pythonPath === interpreter.path : false; - }) - ) - ) - .returns(() => Promise.resolve(pythonService)); - this.pythonExecutionFactory - .setup(f => - f.createActivatedEnvironment( - TypeMoq.It.is(o => { - return !o || JSON.stringify(o.interpreter) === JSON.stringify(interpreter); - }) - ) - ) - .returns(() => Promise.resolve(pythonService)); - this.setupSupportedPythonService(pythonService, interpreter, supportedCommands, notebookStdErr, notebookProc); - - // Then the process calls - this.setupSupportedProcessService(interpreter, supportedCommands, notebookStdErr); - - // Default to being the new active - this.makeActive(interpreter); - } - - public addError(code: string, message: string) { - // Turn the message into an nbformat.IError - const result: nbformat.IError = { - output_type: 'error', - ename: message, - evalue: message, - traceback: [message] - }; - - this.addCell(code, result); - } - - public addContinuousOutputCell( - code: string, - resultGenerator: (cancelToken: CancellationToken) => Promise<{ result: string; haveMore: boolean }> - ) { - const cells = generateCells(undefined, code, Uri.file('foo.py').fsPath, 1, true, uuid()); - cells.forEach(c => { - const key = concatMultilineStringInput(c.data.source) - .replace(LineFeedRegEx, '') - .toLowerCase(); - if (c.data.cell_type === 'code') { - const taggedResult = { - output_type: 'generator' - }; - const data: nbformat.ICodeCell = c.data as nbformat.ICodeCell; - data.outputs = [...data.outputs, taggedResult]; - - // Tag on our extra data - (taggedResult as any).resultGenerator = async (t: CancellationToken) => { - const result = await resultGenerator(t); - return { - result: this.createStreamResult(result.result), - haveMore: result.haveMore - }; - }; - - // Save in the cell. - c.data = data; - } - - // Save each in our dictionary for future use. - // Note: Our entire setup is recreated each test so this dictionary - // should be unique per test - this.cellDictionary[key] = c; - }); - } - - public addInputCell( - code: string, - result?: - | undefined - | string - | number - | nbformat.IUnrecognizedOutput - | nbformat.IExecuteResult - | nbformat.IDisplayData - | nbformat.IStream - | nbformat.IError, - mimeType?: string - ) { - const cells = generateCells(undefined, code, Uri.file('foo.py').fsPath, 1, true, uuid()); - cells.forEach(c => { - const key = concatMultilineStringInput(c.data.source) - .replace(LineFeedRegEx, '') - .toLowerCase(); - if (c.data.cell_type === 'code') { - const taggedResult = { - output_type: 'input' - }; - const massagedResult = this.massageCellResult(result, mimeType); - const data: nbformat.ICodeCell = c.data as nbformat.ICodeCell; - if (result) { - data.outputs = [...data.outputs, taggedResult, massagedResult]; - } else { - data.outputs = [...data.outputs, taggedResult]; - } - // Save in the cell. - c.data = data; - } - - // Save each in our dictionary for future use. - // Note: Our entire setup is recreated each test so this dictionary - // should be unique per test - this.cellDictionary[key] = c; - }); - } - - public addCell( - code: string, - result?: - | undefined - | string - | number - | nbformat.IUnrecognizedOutput - | nbformat.IExecuteResult - | nbformat.IDisplayData - | nbformat.IStream - | nbformat.IError - | string[], - mimeType?: string | string[] - ) { - const cells = generateCells(undefined, code, Uri.file('foo.py').fsPath, 1, true, uuid()); - cells.forEach(c => { - const cellMatcher = new CellMatcher(); - const key = cellMatcher - .stripFirstMarker(concatMultilineStringInput(c.data.source)) - .replace(LineFeedRegEx, '') - .toLowerCase(); - if (c.data.cell_type === 'code') { - if (mimeType && Array.isArray(mimeType) && Array.isArray(result)) { - for (let i = 0; i < mimeType.length; i = i + 1) { - this.addCellOutput(c, result[i], mimeType[i]); - } - } else if (!Array.isArray(result) && !Array.isArray(mimeType)) { - this.addCellOutput(c, result, mimeType); - } - } - - // Save each in our dictionary for future use. - // Note: Our entire setup is recreated each test so this dictionary - // should be unique per test - this.cellDictionary[key] = c; - }); - } - - public setWaitTime(timeout: number | undefined) { - this.sessionTimeout = timeout; - } - - public async dispose(): Promise { - if (this.cleanTemp) { - this.cleanTemp(); - } - } - - public async initialize(connInfo: IConnection): Promise { - this.connInfo = connInfo; - } - - public startNew(_kernelSpec: IJupyterKernelSpec, cancelToken?: CancellationToken): Promise { - if (this.sessionTimeout && cancelToken) { - const localTimeout = this.sessionTimeout; - return Cancellation.race(async () => { - await sleep(localTimeout); - return this.createNewSession(); - }, cancelToken); - } else { - return Promise.resolve(this.createNewSession()); - } - } - - public getKernelSpecs(): Promise { - return Promise.resolve([]); - } - - public changeWorkingDirectory(workingDir: string) { - this.addCell(CodeSnippits.UpdateCWDAndPath.format(workingDir)); - this.addCell('import os\nos.getcwd()', path.join(workingDir)); - this.addCell('import sys\nsys.path[0]', path.join(workingDir)); - } - - private addCellOutput( - cell: ICell, - result?: - | undefined - | string - | number - | nbformat.IUnrecognizedOutput - | nbformat.IExecuteResult - | nbformat.IDisplayData - | nbformat.IStream - | nbformat.IError, - mimeType?: string - ) { - const massagedResult = this.massageCellResult(result, mimeType); - const data: nbformat.ICodeCell = cell.data as nbformat.ICodeCell; - if (result) { - data.outputs = [...data.outputs, massagedResult]; - } else { - data.outputs = [...data.outputs]; - } - cell.data = data; - } - - private onConfigChanged(configService: IConfigurationService) { - const pythonPath = configService.getSettings().pythonPath; - if (this.activeInterpreter === undefined || pythonPath !== this.activeInterpreter.path) { - this.activeInterpreter = this.installedInterpreters.filter(f => f.path === pythonPath)[0]; - if (!this.activeInterpreter) { - this.activeInterpreter = this.installedInterpreters[0]; - } - this.changedInterpreterEvent.fire(); - } - } - - private createNewSession(): MockJupyterSession { - const sessionFailure = this.pendingSessionFailure; - const kernelChangeFailure = this.pendingKernelChangeFailure; - this.pendingSessionFailure = false; - this.pendingKernelChangeFailure = false; - this.currentSession = new MockJupyterSession( - this.cellDictionary, - MockJupyterTimeDelay, - sessionFailure, - kernelChangeFailure - ); - return this.currentSession; - } - - private createStreamResult(str: string): nbformat.IStream { - return { - output_type: 'stream', - name: 'stdout', - text: str - }; - } - - private massageCellResult( - result: - | undefined - | string - | number - | nbformat.IUnrecognizedOutput - | nbformat.IExecuteResult - | nbformat.IDisplayData - | nbformat.IStream - | nbformat.IError, - mimeType?: string - ): - | nbformat.IUnrecognizedOutput - | nbformat.IExecuteResult - | nbformat.IDisplayData - | nbformat.IStream - | nbformat.IError { - // See if undefined or string or number - if (!result) { - // This is an empty execute result - return { - output_type: 'execute_result', - execution_count: 1, - data: {}, - metadata: {} - }; - } else if (mimeType && mimeType === 'clear_true') { - return { - output_type: 'clear_true' - }; - } else if (mimeType && mimeType === 'stream') { - return { - output_type: 'stream', - text: result, - name: 'stdout' - }; - } else if (typeof result === 'string') { - const data = {}; - (data as any)[mimeType ? mimeType : 'text/plain'] = result; - return { - output_type: 'execute_result', - execution_count: 1, - data: data, - metadata: {} - }; - } else if (typeof result === 'number') { - return { - output_type: 'execute_result', - execution_count: 1, - data: { 'text/plain': result.toString() }, - metadata: {} - }; - } else { - return result; - } - } - - private createTempSpec(pythonPath: string): string { - const tempDir = os.tmpdir(); - const subDir = uuid(); - const filePath = path.join(tempDir, subDir, 'kernel.json'); - fs.ensureDirSync(path.dirname(filePath)); - fs.writeJSONSync(filePath, { - display_name: 'Python 3', - language: 'python', - argv: [pythonPath, '-m', 'ipykernel_launcher', '-f', '{connection_file}'] - }); - return filePath; - } - - private createTypeMoq(tag: string): TypeMoq.IMock { - // Use typemoqs for those things that are resolved as promises. mockito doesn't allow nesting of mocks. ES6 Proxy class - // is the problem. We still need to make it thenable though. See this issue: https://github.com/florinn/typemoq/issues/67 - const result = TypeMoq.Mock.ofType(); - (result as any).tag = tag; - result.setup((x: any) => x.then).returns(() => undefined); - return result; - } - - private setupPythonServiceExec( - service: MockPythonService, - module: string, - args: (string | RegExp)[], - result: () => Promise> - ) { - service.addExecResult(['-m', module, ...args], result); - service.addExecModuleResult(module, args, result); - } - - private setupPythonServiceExecObservable( - service: MockPythonService, - module: string, - args: (string | RegExp)[], - stderr: string[], - stdout: string[], - proc?: ChildProcess - ) { - const result = { - proc, - out: new Observable>(subscriber => { - stderr.forEach(s => subscriber.next({ source: 'stderr', out: s })); - stdout.forEach(s => subscriber.next({ source: 'stderr', out: s })); - }), - dispose: () => { - noop(); - } - }; - - service.addExecObservableResult(['-m', module, ...args], () => result); - service.addExecModuleObservableResult(module, args, () => result); - } - - private setupProcessServiceExec( - service: MockProcessService, - file: string, - args: (string | RegExp)[], - result: () => Promise> - ) { - service.addExecResult(file, args, result); - } - - private setupProcessServiceExecObservable( - service: MockProcessService, - file: string, - args: (string | RegExp)[], - stderr: string[], - stdout: string[] - ) { - service.addExecObservableResult(file, args, () => { - return { - proc: undefined, - out: new Observable>(subscriber => { - stderr.forEach(s => subscriber.next({ source: 'stderr', out: s })); - stdout.forEach(s => subscriber.next({ source: 'stderr', out: s })); - }), - dispose: () => { - noop(); - } - }; - }); - } - - private setupSupportedPythonService( - service: MockPythonService, - workingPython: PythonInterpreter, - supportedCommands: SupportedCommands, - notebookStdErr?: string[], - notebookProc?: ChildProcess - ) { - when(this.productInstaller.isInstalled(anything())).thenResolve(true); - when(this.productInstaller.isInstalled(anything(), anything())).thenResolve(true); - if ((supportedCommands & SupportedCommands.ipykernel) === SupportedCommands.ipykernel) { - this.setupPythonServiceExec(service, 'ipykernel', ['--version'], () => - Promise.resolve({ stdout: '1.1.1.1' }) - ); - this.setupPythonServiceExec( - service, - 'ipykernel', - ['install', '--user', '--name', /\w+-\w+-\w+-\w+-\w+/, '--display-name', `'Python Interactive'`], - () => { - const spec = this.addKernelSpec(workingPython.path); - return Promise.resolve({ stdout: `somename ${path.dirname(spec)}` }); - } - ); - } else { - when(this.productInstaller.isInstalled(Product.ipykernel)).thenResolve(false); - when(this.productInstaller.isInstalled(Product.ipykernel, anything())).thenResolve(false); - } - if ((supportedCommands & SupportedCommands.nbconvert) === SupportedCommands.nbconvert) { - this.setupPythonServiceExec(service, 'jupyter', ['nbconvert', '--version'], () => - Promise.resolve({ stdout: '1.1.1.1' }) - ); - this.setupPythonServiceExec( - service, - 'jupyter', - ['nbconvert', /.*/, '--to', 'python', '--stdout', '--template', /.*/], - () => { - return Promise.resolve({ - stdout: '#%%\r\nimport os\r\nos.chdir()\r\n#%%\r\na=1' - }); - } - ); - } else { - when(this.productInstaller.isInstalled(Product.nbconvert)).thenResolve(false); - when(this.productInstaller.isInstalled(Product.nbconvert, anything())).thenResolve(false); - } - if ((supportedCommands & SupportedCommands.notebook) === SupportedCommands.notebook) { - this.setupPythonServiceExec(service, 'jupyter', ['notebook', '--version'], () => - Promise.resolve({ stdout: '1.1.1.1' }) - ); - this.setupPythonServiceExecObservable( - service, - 'jupyter', - [ - 'notebook', - '--no-browser', - /--notebook-dir=.*/, - /.*/, - '--NotebookApp.iopub_data_rate_limit=10000000000.0' - ], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'], - notebookProc - ); - this.setupPythonServiceExecObservable( - service, - 'jupyter', - ['notebook', '--no-browser', /--notebook-dir=.*/, '--NotebookApp.iopub_data_rate_limit=10000000000.0'], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'], - notebookProc - ); - } else { - when(this.productInstaller.isInstalled(Product.notebook)).thenResolve(false); - when(this.productInstaller.isInstalled(Product.notebook, anything())).thenResolve(false); - } - if ((supportedCommands & SupportedCommands.kernelspec) === SupportedCommands.kernelspec) { - this.setupPythonServiceExec(service, 'jupyter', ['kernelspec', '--version'], () => - Promise.resolve({ stdout: '1.1.1.1' }) - ); - this.setupPythonServiceExec(service, 'jupyter', ['kernelspec', 'list', '--json'], () => { - const kernels = this.kernelSpecs.map(k => ({ name: k.name, resourceDir: k.dir })); - return Promise.resolve({ stdout: JSON.stringify(createKernelSpecs(kernels)) }); - }); - } else { - when(this.productInstaller.isInstalled(Product.kernelspec)).thenResolve(false); - when(this.productInstaller.isInstalled(Product.kernelspec, anything())).thenResolve(false); - } - } - - private addKernelSpec(pythonPath: string): string { - const spec = this.createTempSpec(pythonPath); - this.kernelSpecs.push({ name: `${this.kernelSpecs.length}Spec`, dir: `${path.dirname(spec)}` }); - return spec; - } - - private setupSupportedProcessService( - workingPython: PythonInterpreter, - supportedCommands: SupportedCommands, - notebookStdErr?: string[] - ) { - if ((supportedCommands & SupportedCommands.ipykernel) === SupportedCommands.ipykernel) { - // Don't mind the goofy path here. It's supposed to not find the item on your box. It's just testing the internal regex works - this.setupProcessServiceExec( - this.processService, - workingPython.path, - ['-m', 'jupyter', 'kernelspec', 'list', '--json'], - () => { - const kernels = this.kernelSpecs.map(k => ({ name: k.name, resourceDir: k.dir })); - return Promise.resolve({ stdout: JSON.stringify(createKernelSpecs(kernels)) }); - } - ); - this.setupProcessServiceExec( - this.processService, - workingPython.path, - [ - '-m', - 'ipykernel', - 'install', - '--user', - '--name', - /\w+-\w+-\w+-\w+-\w+/, - '--display-name', - `'Python Interactive'` - ], - () => { - const spec = this.addKernelSpec(workingPython.path); - return Promise.resolve({ - stdout: JSON.stringify( - createKernelSpecs([{ name: 'somename', resourceDir: path.dirname(spec) }]) - ) - }); - } - ); - const getServerInfoPath = path.join( - EXTENSION_ROOT_DIR, - 'pythonFiles', - 'vscode_datascience_helpers', - 'getServerInfo.py' - ); - this.setupProcessServiceExec(this.processService, workingPython.path, [getServerInfoPath], () => - Promise.resolve({ stdout: 'failure to get server infos' }) - ); - this.setupProcessServiceExecObservable( - this.processService, - workingPython.path, - ['-m', 'jupyter', 'kernelspec', 'list', '--json'], - [], - [] - ); - this.setupProcessServiceExecObservable( - this.processService, - workingPython.path, - [ - '-m', - 'jupyter', - 'notebook', - '--no-browser', - /--notebook-dir=.*/, - /.*/, - '--NotebookApp.iopub_data_rate_limit=10000000000.0' - ], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] - ); - this.setupProcessServiceExecObservable( - this.processService, - workingPython.path, - [ - '-m', - 'jupyter', - 'notebook', - '--no-browser', - /--notebook-dir=.*/, - '--NotebookApp.iopub_data_rate_limit=10000000000.0' - ], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] - ); - } else if ((supportedCommands & SupportedCommands.notebook) === SupportedCommands.notebook) { - this.setupProcessServiceExec( - this.processService, - workingPython.path, - ['-m', 'jupyter', 'kernelspec', 'list', '--json'], - () => { - const kernels = this.kernelSpecs.map(k => ({ name: k.name, resourceDir: k.dir })); - return Promise.resolve({ stdout: JSON.stringify(createKernelSpecs(kernels)) }); - } - ); - const getServerInfoPath = path.join( - EXTENSION_ROOT_DIR, - 'pythonFiles', - 'vscode_datascience_helpers', - 'getServerInfo.py' - ); - this.setupProcessServiceExec(this.processService, workingPython.path, [getServerInfoPath], () => - Promise.resolve({ stdout: 'failure to get server infos' }) - ); - this.setupProcessServiceExecObservable( - this.processService, - workingPython.path, - ['-m', 'jupyter', 'kernelspec', 'list', '--json'], - [], - [] - ); - this.setupProcessServiceExecObservable( - this.processService, - workingPython.path, - [ - '-m', - 'jupyter', - 'notebook', - '--no-browser', - /--notebook-dir=.*/, - /.*/, - '--NotebookApp.iopub_data_rate_limit=10000000000.0' - ], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] - ); - this.setupProcessServiceExecObservable( - this.processService, - workingPython.path, - [ - '-m', - 'jupyter', - 'notebook', - '--no-browser', - /--notebook-dir=.*/, - '--NotebookApp.iopub_data_rate_limit=10000000000.0' - ], - [], - notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] - ); - } - if ((supportedCommands & SupportedCommands.nbconvert) === SupportedCommands.nbconvert) { - this.setupProcessServiceExec( - this.processService, - workingPython.path, - ['-m', 'jupyter', 'nbconvert', /.*/, '--to', 'python', '--stdout', '--template', /.*/], - () => { - return Promise.resolve({ - stdout: '#%%\r\nimport os\r\nos.chdir()' - }); - } - ); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { nbformat } from '@jupyterlab/coreutils'; +import { ChildProcess } from 'child_process'; +import * as fs from 'fs-extra'; +import * as os from 'os'; +import * as path from 'path'; +import { Observable } from 'rxjs/Observable'; +import * as tmp from 'tmp'; +import * as TypeMoq from 'typemoq'; +import * as uuid from 'uuid/v4'; +import { EventEmitter, Uri } from 'vscode'; +import { CancellationToken } from 'vscode-jsonrpc'; + +import { Session } from '@jupyterlab/services'; +import { anything, instance, mock, when } from 'ts-mockito'; +import { Cancellation } from '../../client/common/cancellation'; +import { ProductInstaller } from '../../client/common/installer/productInstaller'; +import { + ExecutionResult, + IProcessServiceFactory, + IPythonExecutionFactory, + Output +} from '../../client/common/process/types'; +import { IConfigurationService, IInstaller, Product } from '../../client/common/types'; +import { EXTENSION_ROOT_DIR } from '../../client/constants'; +import { generateCells } from '../../client/datascience/cellFactory'; +import { CellMatcher } from '../../client/datascience/cellMatcher'; +import { CodeSnippits, Identifiers } from '../../client/datascience/constants'; +import { + ICell, + IConnection, + IJupyterKernel, + IJupyterKernelSpec, + IJupyterSession, + IJupyterSessionManager +} from '../../client/datascience/types'; +import { IInterpreterService, PythonInterpreter } from '../../client/interpreter/contracts'; +import { IServiceManager } from '../../client/ioc/types'; +import { concatMultilineStringInput } from '../../datascience-ui/common'; +import { noop, sleep } from '../core'; +import { MockJupyterSession } from './mockJupyterSession'; +import { MockProcessService } from './mockProcessService'; +import { MockPythonService } from './mockPythonService'; + +// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length + +const MockJupyterTimeDelay = 10; +const LineFeedRegEx = /(\r\n|\n)/g; + +export enum SupportedCommands { + none = 0, + ipykernel = 1, + nbconvert = 2, + notebook = 4, + kernelspec = 8, + all = 0xffff +} + +function createKernelSpecs(specs: { name: string; resourceDir: string }[]): Record { + const models: Record = {}; + specs.forEach((spec) => { + models[spec.name] = { + resource_dir: spec.resourceDir, + spec: { + name: spec.name, + display_name: spec.name, + language: 'python' + } + }; + }); + return models; +} + +// This class is used to mock talking to jupyter. It mocks +// the process services, the interpreter services, the python services, and the jupyter session +export class MockJupyterManager implements IJupyterSessionManager { + public readonly productInstaller: IInstaller; + private pythonExecutionFactory = this.createTypeMoq('Python Exec Factory'); + private processServiceFactory = this.createTypeMoq('Process Exec Factory'); + private processService: MockProcessService = new MockProcessService(); + private interpreterService = this.createTypeMoq('Interpreter Service'); + private changedInterpreterEvent: EventEmitter = new EventEmitter(); + private installedInterpreters: PythonInterpreter[] = []; + private pythonServices: MockPythonService[] = []; + private activeInterpreter: PythonInterpreter | undefined; + private sessionTimeout: number | undefined; + private cellDictionary: Record = {}; + private kernelSpecs: { name: string; dir: string }[] = []; + private currentSession: MockJupyterSession | undefined; + private connInfo: IConnection | undefined; + private cleanTemp: (() => void) | undefined; + private pendingSessionFailure = false; + private pendingKernelChangeFailure = false; + + constructor(serviceManager: IServiceManager) { + // Make our process service factory always return this item + this.processServiceFactory.setup((p) => p.create()).returns(() => Promise.resolve(this.processService)); + this.productInstaller = mock(ProductInstaller); + // Setup our interpreter service + this.interpreterService + .setup((i) => i.onDidChangeInterpreter) + .returns(() => this.changedInterpreterEvent.event); + this.interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(this.activeInterpreter)); + this.interpreterService + .setup((i) => i.getInterpreters(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(this.installedInterpreters)); + this.interpreterService + .setup((i) => i.getInterpreterDetails(TypeMoq.It.isAnyString())) + .returns((p) => { + const found = this.installedInterpreters.find((i) => i.path === p); + if (found) { + return Promise.resolve(found); + } + return Promise.reject('Unknown interpreter'); + }); + // Listen to configuration changes like the real interpreter service does so that we fire our settings changed event + const configService = serviceManager.get(IConfigurationService); + if (configService && configService !== null) { + configService.getSettings(undefined).onDidChange(this.onConfigChanged.bind(this, configService)); + } + + // Stick our services into the service manager + serviceManager.addSingletonInstance(IInterpreterService, this.interpreterService.object); + serviceManager.addSingletonInstance( + IPythonExecutionFactory, + this.pythonExecutionFactory.object + ); + serviceManager.addSingletonInstance( + IProcessServiceFactory, + this.processServiceFactory.object + ); + serviceManager.addSingletonInstance(IInstaller, instance(this.productInstaller)); + + // Setup our default kernel spec (this is just a dummy value) + // tslint:disable-next-line:no-octal-literal + this.kernelSpecs.push({ + name: '0e8519db-0895-416c-96df-fa80131ecea0', + dir: 'C:\\Users\\rchiodo\\AppData\\Roaming\\jupyter\\kernels\\0e8519db-0895-416c-96df-fa80131ecea0' + }); + + // Setup our default cells that happen for everything + this.addCell(CodeSnippits.MatplotLibInitSvg); + this.addCell(CodeSnippits.MatplotLibInitPng); + this.addCell(CodeSnippits.ConfigSvg); + this.addCell(CodeSnippits.ConfigPng); + this.addCell(CodeSnippits.UpdateCWDAndPath.format(path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience'))); + this.addCell( + CodeSnippits.UpdateCWDAndPath.format( + Uri.file(path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience')).fsPath + ) + ); + tmp.file((_e, p, _fd, cleanup) => { + this.addCell(CodeSnippits.UpdateCWDAndPath.format(path.dirname(p))); + this.cleanTemp = cleanup; + }); + this.addCell(`import sys\r\nsys.path.append('undefined')\r\nsys.path`); + this.addCell(`import ptvsd;ptvsd.enable_attach(('localhost', 0))`); + this.addCell(`import debugpy;debugpy.listen(('localhost', 0))`); + this.addCell("matplotlib.style.use('dark_background')"); + this.addCell(`matplotlib.rcParams.update(${Identifiers.MatplotLibDefaultParams})`); + this.addCell(`%cd "${path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience')}"`); + // When we have windows file names, we replace `\` with `\\`. + // Code is as follows `await this.notebook.execute(`__file__ = '${file.replace(/\\/g, '\\\\')}'`, file, line, uuid(), undefined, true); + // Found in src\client\datascience\interactive-common\interactiveBase.ts. + this.addCell(`%cd "${Uri.file(path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience')).fsPath}`); + // New root dir should be in the temp folder. + tmp.file((_e, p, _fd, cleanup) => { + this.addCell(`%cd "${path.dirname(p).toLowerCase()}"`); + this.cleanTemp = cleanup; + }); + this.addCell('import sys\r\nsys.version', '1.1.1.1'); + this.addCell('import sys\r\nsys.executable', 'python'); + this.addCell('import notebook\r\nnotebook.version_info', '1.1.1.1'); + + this.addCell(`__file__ = '${Uri.file('foo.py').fsPath}'`); + this.addCell(`__file__ = '${Uri.file('bar.py').fsPath}'`); + this.addCell(`__file__ = '${Uri.file('foo').fsPath}'`); + this.addCell(`__file__ = '${Uri.file('test.py').fsPath}'`); + + // When we have windows file names, we replace `\` with `\\`. + // Code is as follows `await this.notebook.execute(`__file__ = '${file.replace(/\\/g, '\\\\')}'`, file, line, uuid(), undefined, true); + // Found in src\client\datascience\interactive-common\interactiveBase.ts. + this.addCell(`__file__ = '${Uri.file('foo.py').fsPath.replace(/\\/g, '\\\\')}'`); + this.addCell(`__file__ = '${Uri.file('bar.py').fsPath.replace(/\\/g, '\\\\')}'`); + this.addCell(`__file__ = '${Uri.file('foo').fsPath.replace(/\\/g, '\\\\')}'`); + this.addCell(`__file__ = '${Uri.file('test.py').fsPath.replace(/\\/g, '\\\\')}'`); + this.addCell('import os\nos.getcwd()', `'${path.join(EXTENSION_ROOT_DIR)}'`); + this.addCell('import sys\nsys.path[0]', `'${path.join(EXTENSION_ROOT_DIR)}'`); + } + + public getConnInfo(): IConnection { + return this.connInfo!; + } + + public makeActive(interpreter: PythonInterpreter) { + this.activeInterpreter = interpreter; + } + + public getCurrentSession(): MockJupyterSession | undefined { + return this.currentSession; + } + + public forcePendingIdleFailure() { + this.pendingSessionFailure = true; + } + + public forcePendingKernelChangeFailure() { + this.pendingKernelChangeFailure = true; + } + + public getRunningKernels(): Promise { + return Promise.resolve([]); + } + + public getRunningSessions(): Promise { + return Promise.resolve([]); + } + + public setProcessDelay(timeout: number | undefined) { + this.processService.setDelay(timeout); + this.pythonServices.forEach((p) => p.setDelay(timeout)); + } + + public addInterpreter( + interpreter: PythonInterpreter, + supportedCommands: SupportedCommands, + notebookStdErr?: string[], + notebookProc?: ChildProcess + ) { + this.installedInterpreters.push(interpreter); + + // Add the python calls first. + const pythonService = new MockPythonService(interpreter); + this.pythonServices.push(pythonService); + this.pythonExecutionFactory + .setup((f) => + f.create( + TypeMoq.It.is((o) => { + return o && o.pythonPath ? o.pythonPath === interpreter.path : false; + }) + ) + ) + .returns(() => Promise.resolve(pythonService)); + this.pythonExecutionFactory + .setup((f) => + f.createDaemon( + TypeMoq.It.is((o) => { + return o && o.pythonPath ? o.pythonPath === interpreter.path : false; + }) + ) + ) + .returns(() => Promise.resolve(pythonService)); + this.pythonExecutionFactory + .setup((f) => + f.createActivatedEnvironment( + TypeMoq.It.is((o) => { + return !o || JSON.stringify(o.interpreter) === JSON.stringify(interpreter); + }) + ) + ) + .returns(() => Promise.resolve(pythonService)); + this.setupSupportedPythonService(pythonService, interpreter, supportedCommands, notebookStdErr, notebookProc); + + // Then the process calls + this.setupSupportedProcessService(interpreter, supportedCommands, notebookStdErr); + + // Default to being the new active + this.makeActive(interpreter); + } + + public addError(code: string, message: string) { + // Turn the message into an nbformat.IError + const result: nbformat.IError = { + output_type: 'error', + ename: message, + evalue: message, + traceback: [message] + }; + + this.addCell(code, result); + } + + public addContinuousOutputCell( + code: string, + resultGenerator: (cancelToken: CancellationToken) => Promise<{ result: string; haveMore: boolean }> + ) { + const cells = generateCells(undefined, code, Uri.file('foo.py').fsPath, 1, true, uuid()); + cells.forEach((c) => { + const key = concatMultilineStringInput(c.data.source).replace(LineFeedRegEx, '').toLowerCase(); + if (c.data.cell_type === 'code') { + const taggedResult = { + output_type: 'generator' + }; + const data: nbformat.ICodeCell = c.data as nbformat.ICodeCell; + data.outputs = [...data.outputs, taggedResult]; + + // Tag on our extra data + (taggedResult as any).resultGenerator = async (t: CancellationToken) => { + const result = await resultGenerator(t); + return { + result: this.createStreamResult(result.result), + haveMore: result.haveMore + }; + }; + + // Save in the cell. + c.data = data; + } + + // Save each in our dictionary for future use. + // Note: Our entire setup is recreated each test so this dictionary + // should be unique per test + this.cellDictionary[key] = c; + }); + } + + public addInputCell( + code: string, + result?: + | undefined + | string + | number + | nbformat.IUnrecognizedOutput + | nbformat.IExecuteResult + | nbformat.IDisplayData + | nbformat.IStream + | nbformat.IError, + mimeType?: string + ) { + const cells = generateCells(undefined, code, Uri.file('foo.py').fsPath, 1, true, uuid()); + cells.forEach((c) => { + const key = concatMultilineStringInput(c.data.source).replace(LineFeedRegEx, '').toLowerCase(); + if (c.data.cell_type === 'code') { + const taggedResult = { + output_type: 'input' + }; + const massagedResult = this.massageCellResult(result, mimeType); + const data: nbformat.ICodeCell = c.data as nbformat.ICodeCell; + if (result) { + data.outputs = [...data.outputs, taggedResult, massagedResult]; + } else { + data.outputs = [...data.outputs, taggedResult]; + } + // Save in the cell. + c.data = data; + } + + // Save each in our dictionary for future use. + // Note: Our entire setup is recreated each test so this dictionary + // should be unique per test + this.cellDictionary[key] = c; + }); + } + + public addCell( + code: string, + result?: + | undefined + | string + | number + | nbformat.IUnrecognizedOutput + | nbformat.IExecuteResult + | nbformat.IDisplayData + | nbformat.IStream + | nbformat.IError + | string[], + mimeType?: string | string[] + ) { + const cells = generateCells(undefined, code, Uri.file('foo.py').fsPath, 1, true, uuid()); + cells.forEach((c) => { + const cellMatcher = new CellMatcher(); + const key = cellMatcher + .stripFirstMarker(concatMultilineStringInput(c.data.source)) + .replace(LineFeedRegEx, '') + .toLowerCase(); + if (c.data.cell_type === 'code') { + if (mimeType && Array.isArray(mimeType) && Array.isArray(result)) { + for (let i = 0; i < mimeType.length; i = i + 1) { + this.addCellOutput(c, result[i], mimeType[i]); + } + } else if (!Array.isArray(result) && !Array.isArray(mimeType)) { + this.addCellOutput(c, result, mimeType); + } + } + + // Save each in our dictionary for future use. + // Note: Our entire setup is recreated each test so this dictionary + // should be unique per test + this.cellDictionary[key] = c; + }); + } + + public setWaitTime(timeout: number | undefined) { + this.sessionTimeout = timeout; + } + + public async dispose(): Promise { + if (this.cleanTemp) { + this.cleanTemp(); + } + } + + public async initialize(connInfo: IConnection): Promise { + this.connInfo = connInfo; + } + + public startNew(_kernelSpec: IJupyterKernelSpec, cancelToken?: CancellationToken): Promise { + if (this.sessionTimeout && cancelToken) { + const localTimeout = this.sessionTimeout; + return Cancellation.race(async () => { + await sleep(localTimeout); + return this.createNewSession(); + }, cancelToken); + } else { + return Promise.resolve(this.createNewSession()); + } + } + + public getKernelSpecs(): Promise { + return Promise.resolve([]); + } + + public changeWorkingDirectory(workingDir: string) { + this.addCell(CodeSnippits.UpdateCWDAndPath.format(workingDir)); + this.addCell('import os\nos.getcwd()', path.join(workingDir)); + this.addCell('import sys\nsys.path[0]', path.join(workingDir)); + } + + private addCellOutput( + cell: ICell, + result?: + | undefined + | string + | number + | nbformat.IUnrecognizedOutput + | nbformat.IExecuteResult + | nbformat.IDisplayData + | nbformat.IStream + | nbformat.IError, + mimeType?: string + ) { + const massagedResult = this.massageCellResult(result, mimeType); + const data: nbformat.ICodeCell = cell.data as nbformat.ICodeCell; + if (result) { + data.outputs = [...data.outputs, massagedResult]; + } else { + data.outputs = [...data.outputs]; + } + cell.data = data; + } + + private onConfigChanged(configService: IConfigurationService) { + const pythonPath = configService.getSettings().pythonPath; + if (this.activeInterpreter === undefined || pythonPath !== this.activeInterpreter.path) { + this.activeInterpreter = this.installedInterpreters.filter((f) => f.path === pythonPath)[0]; + if (!this.activeInterpreter) { + this.activeInterpreter = this.installedInterpreters[0]; + } + this.changedInterpreterEvent.fire(); + } + } + + private createNewSession(): MockJupyterSession { + const sessionFailure = this.pendingSessionFailure; + const kernelChangeFailure = this.pendingKernelChangeFailure; + this.pendingSessionFailure = false; + this.pendingKernelChangeFailure = false; + this.currentSession = new MockJupyterSession( + this.cellDictionary, + MockJupyterTimeDelay, + sessionFailure, + kernelChangeFailure + ); + return this.currentSession; + } + + private createStreamResult(str: string): nbformat.IStream { + return { + output_type: 'stream', + name: 'stdout', + text: str + }; + } + + private massageCellResult( + result: + | undefined + | string + | number + | nbformat.IUnrecognizedOutput + | nbformat.IExecuteResult + | nbformat.IDisplayData + | nbformat.IStream + | nbformat.IError, + mimeType?: string + ): + | nbformat.IUnrecognizedOutput + | nbformat.IExecuteResult + | nbformat.IDisplayData + | nbformat.IStream + | nbformat.IError { + // See if undefined or string or number + if (!result) { + // This is an empty execute result + return { + output_type: 'execute_result', + execution_count: 1, + data: {}, + metadata: {} + }; + } else if (mimeType && mimeType === 'clear_true') { + return { + output_type: 'clear_true' + }; + } else if (mimeType && mimeType === 'stream') { + return { + output_type: 'stream', + text: result, + name: 'stdout' + }; + } else if (typeof result === 'string') { + const data = {}; + (data as any)[mimeType ? mimeType : 'text/plain'] = result; + return { + output_type: 'execute_result', + execution_count: 1, + data: data, + metadata: {} + }; + } else if (typeof result === 'number') { + return { + output_type: 'execute_result', + execution_count: 1, + data: { 'text/plain': result.toString() }, + metadata: {} + }; + } else { + return result; + } + } + + private createTempSpec(pythonPath: string): string { + const tempDir = os.tmpdir(); + const subDir = uuid(); + const filePath = path.join(tempDir, subDir, 'kernel.json'); + fs.ensureDirSync(path.dirname(filePath)); + fs.writeJSONSync(filePath, { + display_name: 'Python 3', + language: 'python', + argv: [pythonPath, '-m', 'ipykernel_launcher', '-f', '{connection_file}'] + }); + return filePath; + } + + private createTypeMoq(tag: string): TypeMoq.IMock { + // Use typemoqs for those things that are resolved as promises. mockito doesn't allow nesting of mocks. ES6 Proxy class + // is the problem. We still need to make it thenable though. See this issue: https://github.com/florinn/typemoq/issues/67 + const result = TypeMoq.Mock.ofType(); + (result as any).tag = tag; + result.setup((x: any) => x.then).returns(() => undefined); + return result; + } + + private setupPythonServiceExec( + service: MockPythonService, + module: string, + args: (string | RegExp)[], + result: () => Promise> + ) { + service.addExecResult(['-m', module, ...args], result); + service.addExecModuleResult(module, args, result); + } + + private setupPythonServiceExecObservable( + service: MockPythonService, + module: string, + args: (string | RegExp)[], + stderr: string[], + stdout: string[], + proc?: ChildProcess + ) { + const result = { + proc, + out: new Observable>((subscriber) => { + stderr.forEach((s) => subscriber.next({ source: 'stderr', out: s })); + stdout.forEach((s) => subscriber.next({ source: 'stderr', out: s })); + }), + dispose: () => { + noop(); + } + }; + + service.addExecObservableResult(['-m', module, ...args], () => result); + service.addExecModuleObservableResult(module, args, () => result); + } + + private setupProcessServiceExec( + service: MockProcessService, + file: string, + args: (string | RegExp)[], + result: () => Promise> + ) { + service.addExecResult(file, args, result); + } + + private setupProcessServiceExecObservable( + service: MockProcessService, + file: string, + args: (string | RegExp)[], + stderr: string[], + stdout: string[] + ) { + service.addExecObservableResult(file, args, () => { + return { + proc: undefined, + out: new Observable>((subscriber) => { + stderr.forEach((s) => subscriber.next({ source: 'stderr', out: s })); + stdout.forEach((s) => subscriber.next({ source: 'stderr', out: s })); + }), + dispose: () => { + noop(); + } + }; + }); + } + + private setupSupportedPythonService( + service: MockPythonService, + workingPython: PythonInterpreter, + supportedCommands: SupportedCommands, + notebookStdErr?: string[], + notebookProc?: ChildProcess + ) { + when(this.productInstaller.isInstalled(anything())).thenResolve(true); + when(this.productInstaller.isInstalled(anything(), anything())).thenResolve(true); + if ((supportedCommands & SupportedCommands.ipykernel) === SupportedCommands.ipykernel) { + this.setupPythonServiceExec(service, 'ipykernel', ['--version'], () => + Promise.resolve({ stdout: '1.1.1.1' }) + ); + this.setupPythonServiceExec( + service, + 'ipykernel', + ['install', '--user', '--name', /\w+-\w+-\w+-\w+-\w+/, '--display-name', `'Python Interactive'`], + () => { + const spec = this.addKernelSpec(workingPython.path); + return Promise.resolve({ stdout: `somename ${path.dirname(spec)}` }); + } + ); + } else { + when(this.productInstaller.isInstalled(Product.ipykernel)).thenResolve(false); + when(this.productInstaller.isInstalled(Product.ipykernel, anything())).thenResolve(false); + } + if ((supportedCommands & SupportedCommands.nbconvert) === SupportedCommands.nbconvert) { + this.setupPythonServiceExec(service, 'jupyter', ['nbconvert', '--version'], () => + Promise.resolve({ stdout: '1.1.1.1' }) + ); + this.setupPythonServiceExec( + service, + 'jupyter', + ['nbconvert', /.*/, '--to', 'python', '--stdout', '--template', /.*/], + () => { + return Promise.resolve({ + stdout: '#%%\r\nimport os\r\nos.chdir()\r\n#%%\r\na=1' + }); + } + ); + } else { + when(this.productInstaller.isInstalled(Product.nbconvert)).thenResolve(false); + when(this.productInstaller.isInstalled(Product.nbconvert, anything())).thenResolve(false); + } + if ((supportedCommands & SupportedCommands.notebook) === SupportedCommands.notebook) { + this.setupPythonServiceExec(service, 'jupyter', ['notebook', '--version'], () => + Promise.resolve({ stdout: '1.1.1.1' }) + ); + this.setupPythonServiceExecObservable( + service, + 'jupyter', + [ + 'notebook', + '--no-browser', + /--notebook-dir=.*/, + /.*/, + '--NotebookApp.iopub_data_rate_limit=10000000000.0' + ], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'], + notebookProc + ); + this.setupPythonServiceExecObservable( + service, + 'jupyter', + ['notebook', '--no-browser', /--notebook-dir=.*/, '--NotebookApp.iopub_data_rate_limit=10000000000.0'], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'], + notebookProc + ); + } else { + when(this.productInstaller.isInstalled(Product.notebook)).thenResolve(false); + when(this.productInstaller.isInstalled(Product.notebook, anything())).thenResolve(false); + } + if ((supportedCommands & SupportedCommands.kernelspec) === SupportedCommands.kernelspec) { + this.setupPythonServiceExec(service, 'jupyter', ['kernelspec', '--version'], () => + Promise.resolve({ stdout: '1.1.1.1' }) + ); + this.setupPythonServiceExec(service, 'jupyter', ['kernelspec', 'list', '--json'], () => { + const kernels = this.kernelSpecs.map((k) => ({ name: k.name, resourceDir: k.dir })); + return Promise.resolve({ stdout: JSON.stringify(createKernelSpecs(kernels)) }); + }); + } else { + when(this.productInstaller.isInstalled(Product.kernelspec)).thenResolve(false); + when(this.productInstaller.isInstalled(Product.kernelspec, anything())).thenResolve(false); + } + } + + private addKernelSpec(pythonPath: string): string { + const spec = this.createTempSpec(pythonPath); + this.kernelSpecs.push({ name: `${this.kernelSpecs.length}Spec`, dir: `${path.dirname(spec)}` }); + return spec; + } + + private setupSupportedProcessService( + workingPython: PythonInterpreter, + supportedCommands: SupportedCommands, + notebookStdErr?: string[] + ) { + if ((supportedCommands & SupportedCommands.ipykernel) === SupportedCommands.ipykernel) { + // Don't mind the goofy path here. It's supposed to not find the item on your box. It's just testing the internal regex works + this.setupProcessServiceExec( + this.processService, + workingPython.path, + ['-m', 'jupyter', 'kernelspec', 'list', '--json'], + () => { + const kernels = this.kernelSpecs.map((k) => ({ name: k.name, resourceDir: k.dir })); + return Promise.resolve({ stdout: JSON.stringify(createKernelSpecs(kernels)) }); + } + ); + this.setupProcessServiceExec( + this.processService, + workingPython.path, + [ + '-m', + 'ipykernel', + 'install', + '--user', + '--name', + /\w+-\w+-\w+-\w+-\w+/, + '--display-name', + `'Python Interactive'` + ], + () => { + const spec = this.addKernelSpec(workingPython.path); + return Promise.resolve({ + stdout: JSON.stringify( + createKernelSpecs([{ name: 'somename', resourceDir: path.dirname(spec) }]) + ) + }); + } + ); + const getServerInfoPath = path.join( + EXTENSION_ROOT_DIR, + 'pythonFiles', + 'vscode_datascience_helpers', + 'getServerInfo.py' + ); + this.setupProcessServiceExec(this.processService, workingPython.path, [getServerInfoPath], () => + Promise.resolve({ stdout: 'failure to get server infos' }) + ); + this.setupProcessServiceExecObservable( + this.processService, + workingPython.path, + ['-m', 'jupyter', 'kernelspec', 'list', '--json'], + [], + [] + ); + this.setupProcessServiceExecObservable( + this.processService, + workingPython.path, + [ + '-m', + 'jupyter', + 'notebook', + '--no-browser', + /--notebook-dir=.*/, + /.*/, + '--NotebookApp.iopub_data_rate_limit=10000000000.0' + ], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] + ); + this.setupProcessServiceExecObservable( + this.processService, + workingPython.path, + [ + '-m', + 'jupyter', + 'notebook', + '--no-browser', + /--notebook-dir=.*/, + '--NotebookApp.iopub_data_rate_limit=10000000000.0' + ], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] + ); + } else if ((supportedCommands & SupportedCommands.notebook) === SupportedCommands.notebook) { + this.setupProcessServiceExec( + this.processService, + workingPython.path, + ['-m', 'jupyter', 'kernelspec', 'list', '--json'], + () => { + const kernels = this.kernelSpecs.map((k) => ({ name: k.name, resourceDir: k.dir })); + return Promise.resolve({ stdout: JSON.stringify(createKernelSpecs(kernels)) }); + } + ); + const getServerInfoPath = path.join( + EXTENSION_ROOT_DIR, + 'pythonFiles', + 'vscode_datascience_helpers', + 'getServerInfo.py' + ); + this.setupProcessServiceExec(this.processService, workingPython.path, [getServerInfoPath], () => + Promise.resolve({ stdout: 'failure to get server infos' }) + ); + this.setupProcessServiceExecObservable( + this.processService, + workingPython.path, + ['-m', 'jupyter', 'kernelspec', 'list', '--json'], + [], + [] + ); + this.setupProcessServiceExecObservable( + this.processService, + workingPython.path, + [ + '-m', + 'jupyter', + 'notebook', + '--no-browser', + /--notebook-dir=.*/, + /.*/, + '--NotebookApp.iopub_data_rate_limit=10000000000.0' + ], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] + ); + this.setupProcessServiceExecObservable( + this.processService, + workingPython.path, + [ + '-m', + 'jupyter', + 'notebook', + '--no-browser', + /--notebook-dir=.*/, + '--NotebookApp.iopub_data_rate_limit=10000000000.0' + ], + [], + notebookStdErr ? notebookStdErr : ['http://localhost:8888/?token=198'] + ); + } + if ((supportedCommands & SupportedCommands.nbconvert) === SupportedCommands.nbconvert) { + this.setupProcessServiceExec( + this.processService, + workingPython.path, + ['-m', 'jupyter', 'nbconvert', /.*/, '--to', 'python', '--stdout', '--template', /.*/], + () => { + return Promise.resolve({ + stdout: '#%%\r\nimport os\r\nos.chdir()' + }); + } + ); + } + } +} diff --git a/src/test/datascience/mockJupyterRequest.ts b/src/test/datascience/mockJupyterRequest.ts index 41a48f2c8bf2..c2bdc8011a36 100644 --- a/src/test/datascience/mockJupyterRequest.ts +++ b/src/test/datascience/mockJupyterRequest.ts @@ -1,351 +1,351 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { nbformat } from '@jupyterlab/coreutils'; -import { Kernel, KernelMessage } from '@jupyterlab/services'; -import { CancellationToken } from 'vscode-jsonrpc'; - -import { createDeferred, Deferred } from '../../client/common/utils/async'; -import { noop } from '../../client/common/utils/misc'; -import { ICell } from '../../client/datascience/types'; -import { concatMultilineStringInput } from '../../datascience-ui/common'; - -//tslint:disable:no-any -interface IMessageResult { - message: KernelMessage.IIOPubMessage | KernelMessage.IInputRequestMsg | KernelMessage.IMessage; - haveMore: boolean; -} - -interface IMessageProducer { - produceNextMessage(): Promise; - receiveInput(value: string): void; -} - -class SimpleMessageProducer implements IMessageProducer { - private type: KernelMessage.IOPubMessageType | KernelMessage.ShellMessageType; - private result: any; - private channel: string = 'iopub'; - - constructor( - type: KernelMessage.IOPubMessageType | KernelMessage.ShellMessageType, - result: any, - channel: string = 'iopub' - ) { - this.type = type; - this.result = result; - this.channel = channel; - } - - public produceNextMessage(): Promise { - return new Promise((resolve, _reject) => { - const message = - this.channel === 'iopub' - ? this.generateIOPubMessage(this.type as KernelMessage.IOPubMessageType, this.result) - : this.generateShellMessage(this.type as KernelMessage.ShellMessageType, this.result); - resolve({ message: message, haveMore: false }); - }); - } - - public receiveInput(_value: string): void { - noop(); - } - - protected generateIOPubMessage(msgType: KernelMessage.IOPubMessageType, result: any): KernelMessage.IIOPubMessage { - return { - channel: 'iopub', - header: { - username: 'foo', - version: '1.1', - session: '1111111111', - msg_id: '1.1', - msg_type: msgType, - date: '' - }, - parent_header: {}, - metadata: {}, - content: result - }; - } - - protected generateShellMessage( - msgType: KernelMessage.ShellMessageType, - result: any - ): KernelMessage.IShellControlMessage { - return { - channel: 'shell', - header: { - username: 'foo', - version: '1.1', - session: '1111111111', - msg_id: '1.1', - msg_type: msgType, - date: '' - }, - parent_header: {}, - metadata: {}, - content: result - }; - } - - protected generateInputMessage(): KernelMessage.IInputRequestMsg { - return { - channel: 'stdin', - header: { - username: 'foo', - version: '1.1', - session: '1111111111', - msg_id: '1.1', - msg_type: 'stdin' as any, - date: '' - }, - parent_header: {}, - metadata: {}, - content: { - prompt: 'Type Something', - password: false - } - }; - } - - protected generateClearMessage(wait: boolean): KernelMessage.IClearOutputMsg { - return { - channel: 'iopub', - header: { - username: 'foo', - version: '1.1', - session: '1111111111', - msg_id: '1.1', - msg_type: 'clear_output', - date: '' - }, - parent_header: {}, - metadata: {}, - content: { - wait - } - }; - } -} - -class OutputMessageProducer extends SimpleMessageProducer { - private output: nbformat.IOutput; - private cancelToken: CancellationToken; - private waitingForInput: Deferred | undefined; - - constructor(output: nbformat.IOutput, cancelToken: CancellationToken) { - super(output.output_type as KernelMessage.IOPubMessageType, output); - this.output = output; - this.cancelToken = cancelToken; - } - - public async produceNextMessage(): Promise { - // Special case the 'generator' cell that returns a function - // to generate output. - if (this.output.output_type === 'generator') { - const resultEntry = this.output.resultGenerator; - const resultGenerator = resultEntry as ( - t: CancellationToken - ) => Promise<{ result: nbformat.IStream; haveMore: boolean }>; - if (resultGenerator) { - const streamResult = await resultGenerator(this.cancelToken); - return { - message: this.generateIOPubMessage(streamResult.result.output_type, streamResult.result), - haveMore: streamResult.haveMore - }; - } - } else if (this.output.output_type === 'input') { - if (this.waitingForInput) { - await this.waitingForInput.promise; - this.waitingForInput = undefined; - return { - message: this.generateDummyMessage(), - haveMore: false - }; - } else { - this.waitingForInput = createDeferred(); - return { - message: this.generateInputMessage(), - haveMore: this.waitingForInput !== undefined - }; - } - } else if (this.output.output_type === 'clear_true') { - // Generate a clear message - return { - message: this.generateClearMessage(true), - haveMore: false - }; - } - - return super.produceNextMessage(); - } - - public receiveInput(value: string) { - if (this.waitingForInput) { - this.waitingForInput.resolve(value); - } - } - - private generateDummyMessage(): KernelMessage.IMessage { - return { - channel: 'shell', - header: { - username: 'foo', - version: '1.1', - session: '1111111111', - msg_id: '1.1', - msg_type: 'stdin' as any - }, - parent_header: {}, - metadata: {}, - content: {} - } as any; - } -} - -// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length -export class MockJupyterRequest implements Kernel.IFuture { - public msg: KernelMessage.IShellMessage; - public onReply: (msg: KernelMessage.IShellMessage) => void | PromiseLike; - public onStdin: (msg: KernelMessage.IStdinMessage) => void | PromiseLike; - public onIOPub: (msg: KernelMessage.IIOPubMessage) => void | PromiseLike; - public isDisposed: boolean = false; - - private deferred: Deferred = createDeferred(); - private executionCount: number; - private cell: ICell; - private cancelToken: CancellationToken; - private currentProducer: IMessageProducer | undefined; - - constructor(cell: ICell, delay: number, executionCount: number, cancelToken: CancellationToken) { - // Save our execution count, this is like our id - this.executionCount = executionCount; - this.cell = cell; - this.cancelToken = cancelToken; - - // Because the base type was implemented without undefined on unset items, we - // need to set all items for hygiene to work. - this.msg = { - channel: 'shell', - header: { - username: 'foo', - version: '1.1', - session: '1111111111', - msg_id: '1.1', - msg_type: ('shell' as any) as KernelMessage.ShellMessageType, - date: '' - }, - parent_header: {}, - metadata: {}, - content: {} - }; - this.onIOPub = noop; - this.onReply = noop; - this.onStdin = noop; - - // Start our sequence of events that is our cell running - this.executeRequest(delay); - } - - public get done(): Promise { - return this.deferred.promise; - } - public registerMessageHook(_hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike): void { - noop(); - } - public removeMessageHook(_hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike): void { - noop(); - } - public sendInputReply(content: KernelMessage.IInputReply): void { - if (this.currentProducer) { - this.currentProducer.receiveInput(content.value); - } - } - public dispose(): void { - if (!this.isDisposed) { - this.isDisposed = true; - } - } - - private executeRequest(delay: number) { - // The order of messages should be: - // 1 - Status busy - // 2 - Execute input - // 3 - N - Results/output - // N + 1 - Status idle - - // Create message producers for output first. - const outputs = this.cell.data.outputs as nbformat.IOutput[]; - const outputProducers = outputs.map( - o => new OutputMessageProducer({ ...o, execution_count: this.executionCount }, this.cancelToken) - ); - - // Then combine those into an array of producers for the rest of the messages - const producers = [ - new SimpleMessageProducer('status', { execution_state: 'busy' }), - new SimpleMessageProducer('execute_input', { - code: concatMultilineStringInput(this.cell.data.source), - execution_count: this.executionCount - }), - ...outputProducers, - new SimpleMessageProducer('status', { execution_state: 'idle' }) - ]; - - // Then send these until we're done - this.sendMessages(producers, delay); - } - - private sendMessages(producers: IMessageProducer[], delay: number) { - if (producers && producers.length > 0) { - // We have another producer, after a delay produce the next - // message - const producer = producers[0]; - this.currentProducer = producer; - setTimeout(() => { - // Produce the next message - producer - .produceNextMessage() - .then(r => { - // If there's a message, send it. - if (r.message && r.message.channel === 'iopub' && this.onIOPub) { - this.onIOPub(r.message as KernelMessage.IIOPubMessage); - } else if (r.message && r.message.channel === 'stdin' && this.onStdin) { - this.onStdin(r.message as KernelMessage.IStdinMessage); - } - - // Move onto the next producer if allowed - if (!this.cancelToken.isCancellationRequested) { - if (r.haveMore) { - this.sendMessages(producers, delay); - } else { - this.sendMessages(producers.slice(1), delay); - } - } - }) - .ignoreErrors(); - }, delay); - } else { - this.currentProducer = undefined; - // No more messages, send the execute reply message - const replyProducer = new SimpleMessageProducer( - 'execute_reply', - { execution_count: this.executionCount }, - 'shell' - ); - replyProducer - .produceNextMessage() - .then(r => { - this.onReply((r.message) as KernelMessage.IShellMessage); - }) - .ignoreErrors(); - - // Then the done message - const shellProducer = new SimpleMessageProducer('done' as any, { status: 'success' }, 'shell'); - shellProducer - .produceNextMessage() - .then(r => { - this.deferred.resolve((r.message) as KernelMessage.IShellMessage); - }) - .ignoreErrors(); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { nbformat } from '@jupyterlab/coreutils'; +import { Kernel, KernelMessage } from '@jupyterlab/services'; +import { CancellationToken } from 'vscode-jsonrpc'; + +import { createDeferred, Deferred } from '../../client/common/utils/async'; +import { noop } from '../../client/common/utils/misc'; +import { ICell } from '../../client/datascience/types'; +import { concatMultilineStringInput } from '../../datascience-ui/common'; + +//tslint:disable:no-any +interface IMessageResult { + message: KernelMessage.IIOPubMessage | KernelMessage.IInputRequestMsg | KernelMessage.IMessage; + haveMore: boolean; +} + +interface IMessageProducer { + produceNextMessage(): Promise; + receiveInput(value: string): void; +} + +class SimpleMessageProducer implements IMessageProducer { + private type: KernelMessage.IOPubMessageType | KernelMessage.ShellMessageType; + private result: any; + private channel: string = 'iopub'; + + constructor( + type: KernelMessage.IOPubMessageType | KernelMessage.ShellMessageType, + result: any, + channel: string = 'iopub' + ) { + this.type = type; + this.result = result; + this.channel = channel; + } + + public produceNextMessage(): Promise { + return new Promise((resolve, _reject) => { + const message = + this.channel === 'iopub' + ? this.generateIOPubMessage(this.type as KernelMessage.IOPubMessageType, this.result) + : this.generateShellMessage(this.type as KernelMessage.ShellMessageType, this.result); + resolve({ message: message, haveMore: false }); + }); + } + + public receiveInput(_value: string): void { + noop(); + } + + protected generateIOPubMessage(msgType: KernelMessage.IOPubMessageType, result: any): KernelMessage.IIOPubMessage { + return { + channel: 'iopub', + header: { + username: 'foo', + version: '1.1', + session: '1111111111', + msg_id: '1.1', + msg_type: msgType, + date: '' + }, + parent_header: {}, + metadata: {}, + content: result + }; + } + + protected generateShellMessage( + msgType: KernelMessage.ShellMessageType, + result: any + ): KernelMessage.IShellControlMessage { + return { + channel: 'shell', + header: { + username: 'foo', + version: '1.1', + session: '1111111111', + msg_id: '1.1', + msg_type: msgType, + date: '' + }, + parent_header: {}, + metadata: {}, + content: result + }; + } + + protected generateInputMessage(): KernelMessage.IInputRequestMsg { + return { + channel: 'stdin', + header: { + username: 'foo', + version: '1.1', + session: '1111111111', + msg_id: '1.1', + msg_type: 'stdin' as any, + date: '' + }, + parent_header: {}, + metadata: {}, + content: { + prompt: 'Type Something', + password: false + } + }; + } + + protected generateClearMessage(wait: boolean): KernelMessage.IClearOutputMsg { + return { + channel: 'iopub', + header: { + username: 'foo', + version: '1.1', + session: '1111111111', + msg_id: '1.1', + msg_type: 'clear_output', + date: '' + }, + parent_header: {}, + metadata: {}, + content: { + wait + } + }; + } +} + +class OutputMessageProducer extends SimpleMessageProducer { + private output: nbformat.IOutput; + private cancelToken: CancellationToken; + private waitingForInput: Deferred | undefined; + + constructor(output: nbformat.IOutput, cancelToken: CancellationToken) { + super(output.output_type as KernelMessage.IOPubMessageType, output); + this.output = output; + this.cancelToken = cancelToken; + } + + public async produceNextMessage(): Promise { + // Special case the 'generator' cell that returns a function + // to generate output. + if (this.output.output_type === 'generator') { + const resultEntry = this.output.resultGenerator; + const resultGenerator = resultEntry as ( + t: CancellationToken + ) => Promise<{ result: nbformat.IStream; haveMore: boolean }>; + if (resultGenerator) { + const streamResult = await resultGenerator(this.cancelToken); + return { + message: this.generateIOPubMessage(streamResult.result.output_type, streamResult.result), + haveMore: streamResult.haveMore + }; + } + } else if (this.output.output_type === 'input') { + if (this.waitingForInput) { + await this.waitingForInput.promise; + this.waitingForInput = undefined; + return { + message: this.generateDummyMessage(), + haveMore: false + }; + } else { + this.waitingForInput = createDeferred(); + return { + message: this.generateInputMessage(), + haveMore: this.waitingForInput !== undefined + }; + } + } else if (this.output.output_type === 'clear_true') { + // Generate a clear message + return { + message: this.generateClearMessage(true), + haveMore: false + }; + } + + return super.produceNextMessage(); + } + + public receiveInput(value: string) { + if (this.waitingForInput) { + this.waitingForInput.resolve(value); + } + } + + private generateDummyMessage(): KernelMessage.IMessage { + return { + channel: 'shell', + header: { + username: 'foo', + version: '1.1', + session: '1111111111', + msg_id: '1.1', + msg_type: 'stdin' as any + }, + parent_header: {}, + metadata: {}, + content: {} + } as any; + } +} + +// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length +export class MockJupyterRequest implements Kernel.IFuture { + public msg: KernelMessage.IShellMessage; + public onReply: (msg: KernelMessage.IShellMessage) => void | PromiseLike; + public onStdin: (msg: KernelMessage.IStdinMessage) => void | PromiseLike; + public onIOPub: (msg: KernelMessage.IIOPubMessage) => void | PromiseLike; + public isDisposed: boolean = false; + + private deferred: Deferred = createDeferred(); + private executionCount: number; + private cell: ICell; + private cancelToken: CancellationToken; + private currentProducer: IMessageProducer | undefined; + + constructor(cell: ICell, delay: number, executionCount: number, cancelToken: CancellationToken) { + // Save our execution count, this is like our id + this.executionCount = executionCount; + this.cell = cell; + this.cancelToken = cancelToken; + + // Because the base type was implemented without undefined on unset items, we + // need to set all items for hygiene to work. + this.msg = { + channel: 'shell', + header: { + username: 'foo', + version: '1.1', + session: '1111111111', + msg_id: '1.1', + msg_type: ('shell' as any) as KernelMessage.ShellMessageType, + date: '' + }, + parent_header: {}, + metadata: {}, + content: {} + }; + this.onIOPub = noop; + this.onReply = noop; + this.onStdin = noop; + + // Start our sequence of events that is our cell running + this.executeRequest(delay); + } + + public get done(): Promise { + return this.deferred.promise; + } + public registerMessageHook(_hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike): void { + noop(); + } + public removeMessageHook(_hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike): void { + noop(); + } + public sendInputReply(content: KernelMessage.IInputReply): void { + if (this.currentProducer) { + this.currentProducer.receiveInput(content.value); + } + } + public dispose(): void { + if (!this.isDisposed) { + this.isDisposed = true; + } + } + + private executeRequest(delay: number) { + // The order of messages should be: + // 1 - Status busy + // 2 - Execute input + // 3 - N - Results/output + // N + 1 - Status idle + + // Create message producers for output first. + const outputs = this.cell.data.outputs as nbformat.IOutput[]; + const outputProducers = outputs.map( + (o) => new OutputMessageProducer({ ...o, execution_count: this.executionCount }, this.cancelToken) + ); + + // Then combine those into an array of producers for the rest of the messages + const producers = [ + new SimpleMessageProducer('status', { execution_state: 'busy' }), + new SimpleMessageProducer('execute_input', { + code: concatMultilineStringInput(this.cell.data.source), + execution_count: this.executionCount + }), + ...outputProducers, + new SimpleMessageProducer('status', { execution_state: 'idle' }) + ]; + + // Then send these until we're done + this.sendMessages(producers, delay); + } + + private sendMessages(producers: IMessageProducer[], delay: number) { + if (producers && producers.length > 0) { + // We have another producer, after a delay produce the next + // message + const producer = producers[0]; + this.currentProducer = producer; + setTimeout(() => { + // Produce the next message + producer + .produceNextMessage() + .then((r) => { + // If there's a message, send it. + if (r.message && r.message.channel === 'iopub' && this.onIOPub) { + this.onIOPub(r.message as KernelMessage.IIOPubMessage); + } else if (r.message && r.message.channel === 'stdin' && this.onStdin) { + this.onStdin(r.message as KernelMessage.IStdinMessage); + } + + // Move onto the next producer if allowed + if (!this.cancelToken.isCancellationRequested) { + if (r.haveMore) { + this.sendMessages(producers, delay); + } else { + this.sendMessages(producers.slice(1), delay); + } + } + }) + .ignoreErrors(); + }, delay); + } else { + this.currentProducer = undefined; + // No more messages, send the execute reply message + const replyProducer = new SimpleMessageProducer( + 'execute_reply', + { execution_count: this.executionCount }, + 'shell' + ); + replyProducer + .produceNextMessage() + .then((r) => { + this.onReply((r.message) as KernelMessage.IShellMessage); + }) + .ignoreErrors(); + + // Then the done message + const shellProducer = new SimpleMessageProducer('done' as any, { status: 'success' }, 'shell'); + shellProducer + .produceNextMessage() + .then((r) => { + this.deferred.resolve((r.message) as KernelMessage.IShellMessage); + }) + .ignoreErrors(); + } + } +} diff --git a/src/test/datascience/mockJupyterSession.ts b/src/test/datascience/mockJupyterSession.ts index 801e9d7928c4..34f34cc88a64 100644 --- a/src/test/datascience/mockJupyterSession.ts +++ b/src/test/datascience/mockJupyterSession.ts @@ -1,284 +1,284 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { Kernel, KernelMessage } from '@jupyterlab/services'; -import { JSONObject } from '@phosphor/coreutils/lib/json'; -import { CancellationTokenSource, Event, EventEmitter } from 'vscode'; - -import { noop } from '../../client/common/utils/misc'; -import { JupyterInvalidKernelError } from '../../client/datascience/jupyter/jupyterInvalidKernelError'; -import { JupyterWaitForIdleError } from '../../client/datascience/jupyter/jupyterWaitForIdleError'; -import { JupyterKernelPromiseFailedError } from '../../client/datascience/jupyter/kernels/jupyterKernelPromiseFailedError'; -import { LiveKernelModel } from '../../client/datascience/jupyter/kernels/types'; -import { ICell, IJupyterKernelSpec, IJupyterSession } from '../../client/datascience/types'; -import { ServerStatus } from '../../datascience-ui/interactive-common/mainState'; -import { sleep } from '../core'; -import { MockJupyterRequest } from './mockJupyterRequest'; - -const LineFeedRegEx = /(\r\n|\n)/g; - -// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length -export class MockJupyterSession implements IJupyterSession { - private dict: Record; - private restartedEvent: EventEmitter = new EventEmitter(); - private onStatusChangedEvent: EventEmitter = new EventEmitter(); - private timedelay: number; - private executionCount: number = 0; - private outstandingRequestTokenSources: CancellationTokenSource[] = []; - private executes: string[] = []; - private forceRestartTimeout: boolean = false; - private completionTimeout: number = 1; - private lastRequest: MockJupyterRequest | undefined; - constructor( - cellDictionary: Record, - timedelay: number, - private pendingIdleFailure: boolean = false, - private pendingKernelChangeFailure: boolean = false - ) { - this.dict = cellDictionary; - this.timedelay = timedelay; - } - - public get onRestarted(): Event { - return this.restartedEvent.event; - } - - public get onSessionStatusChanged(): Event { - if (!this.onStatusChangedEvent) { - this.onStatusChangedEvent = new EventEmitter(); - } - return this.onStatusChangedEvent.event; - } - - public get status(): ServerStatus { - return ServerStatus.Idle; - } - - public async restart(_timeout: number): Promise { - // For every outstanding request, switch them to fail mode - const requests = [...this.outstandingRequestTokenSources]; - requests.forEach(r => r.cancel()); - - if (this.forceRestartTimeout) { - throw new JupyterKernelPromiseFailedError('Forcing restart timeout'); - } - - return sleep(this.timedelay); - } - public interrupt(_timeout: number): Promise { - const requests = [...this.outstandingRequestTokenSources]; - requests.forEach(r => r.cancel()); - return sleep(this.timedelay); - } - public waitForIdle(_timeout: number): Promise { - if (this.pendingIdleFailure) { - this.pendingIdleFailure = false; - return Promise.reject(new JupyterWaitForIdleError('Kernel is dead')); - } - return sleep(this.timedelay); - } - - public prolongRestarts() { - this.forceRestartTimeout = true; - } - public requestExecute( - content: KernelMessage.IExecuteRequestMsg['content'], - _disposeOnDone?: boolean, - _metadata?: JSONObject - ): Kernel.IFuture { - // Content should have the code - const cell = this.findCell(content.code); - if (cell) { - this.executes.push(content.code); - } - - // Create a new dummy request - this.executionCount += content.store_history && content.code.trim().length > 0 ? 1 : 0; - const tokenSource = new CancellationTokenSource(); - const request = new MockJupyterRequest(cell, this.timedelay, this.executionCount, tokenSource.token); - this.outstandingRequestTokenSources.push(tokenSource); - - // When it finishes, it should not be an outstanding request anymore - const removeHandler = () => { - this.outstandingRequestTokenSources = this.outstandingRequestTokenSources.filter(f => f !== tokenSource); - if (this.lastRequest === request) { - this.lastRequest = undefined; - } - }; - request.done.then(removeHandler).catch(removeHandler); - this.lastRequest = request; - return request; - } - - public requestInspect( - _content: KernelMessage.IInspectRequestMsg['content'] - ): Promise { - return Promise.resolve({ - content: { - status: 'ok', - metadata: {}, - found: true, - data: {} // Could add variable values here? - }, - channel: 'shell', - header: { - date: 'foo', - version: '1', - session: '1', - msg_id: '1', - msg_type: 'inspect_reply', - username: 'foo' - }, - parent_header: { - date: 'foo', - version: '1', - session: '1', - msg_id: '1', - msg_type: 'inspect_request', - username: 'foo' - }, - metadata: {} - }); - } - - public sendInputReply(content: string) { - if (this.lastRequest) { - this.lastRequest.sendInputReply({ value: content, status: 'ok' }); - } - } - - public async requestComplete( - _content: KernelMessage.ICompleteRequestMsg['content'] - ): Promise { - await sleep(this.completionTimeout); - - return { - content: { - matches: ['printly', '%%bash'], // This keeps this in the intellisense when the editor pairs down results - cursor_start: 0, - cursor_end: 7, - status: 'ok', - metadata: {} - }, - channel: 'shell', - header: { - username: 'foo', - version: '1', - session: '1', - msg_id: '1', - msg_type: 'complete' as any, - date: '' - }, - parent_header: {}, - metadata: {} - } as any; - } - - public dispose(): Promise { - return sleep(10); - } - - public getExecutes(): string[] { - return this.executes; - } - - public setCompletionTimeout(timeout: number) { - this.completionTimeout = timeout; - } - - public changeKernel(kernel: IJupyterKernelSpec | LiveKernelModel, _timeoutMS: number): Promise { - if (this.pendingKernelChangeFailure) { - this.pendingKernelChangeFailure = false; - return Promise.reject(new JupyterInvalidKernelError(kernel)); - } - return Promise.resolve(); - } - - public registerCommTarget( - _targetName: string, - _callback: (comm: Kernel.IComm, msg: KernelMessage.ICommOpenMsg) => void | PromiseLike - ) { - noop(); - } - - public sendCommMessage( - buffers: (ArrayBuffer | ArrayBufferView)[], - content: { comm_id: string; data: JSONObject; target_name: string | undefined }, - // tslint:disable-next-line: no-any - metadata: any, - // tslint:disable-next-line: no-any - msgId: any - ): Kernel.IShellFuture< - KernelMessage.IShellMessage<'comm_msg'>, - KernelMessage.IShellMessage - > { - const shellMessage = KernelMessage.createMessage>({ - // tslint:disable-next-line: no-any - msgType: 'comm_msg', - channel: 'shell', - buffers, - content, - metadata, - msgId, - session: '1', - username: '1' - }); - - return { - done: Promise.resolve(undefined), - msg: shellMessage, - onReply: noop, - onIOPub: noop, - onStdin: noop, - registerMessageHook: noop, - removeMessageHook: noop, - sendInputReply: noop, - isDisposed: false, - dispose: noop - }; - } - - public requestCommInfo( - _content: KernelMessage.ICommInfoRequestMsg['content'] - ): Promise { - const shellMessage = KernelMessage.createMessage({ - msgType: 'comm_info_reply', - channel: 'shell', - content: { - status: 'ok' - // tslint:disable-next-line: no-any - } as any, - metadata: {}, - session: '1', - username: '1' - }); - - return Promise.resolve(shellMessage); - } - public registerMessageHook( - _msgId: string, - _hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike - ): void { - noop(); - } - public removeMessageHook( - _msgId: string, - _hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike - ): void { - noop(); - } - - private findCell = (code: string): ICell => { - // Match skipping line separators - const withoutLines = code.replace(LineFeedRegEx, '').toLowerCase(); - - if (this.dict.hasOwnProperty(withoutLines)) { - return this.dict[withoutLines] as ICell; - } - // tslint:disable-next-line:no-console - console.log(`Cell '${code}' not found in mock`); - // tslint:disable-next-line:no-console - console.log(`Dict has these keys ${Object.keys(this.dict).join('","')}`); - throw new Error(`Cell '${code}' not found in mock`); - }; -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { Kernel, KernelMessage } from '@jupyterlab/services'; +import { JSONObject } from '@phosphor/coreutils/lib/json'; +import { CancellationTokenSource, Event, EventEmitter } from 'vscode'; + +import { noop } from '../../client/common/utils/misc'; +import { JupyterInvalidKernelError } from '../../client/datascience/jupyter/jupyterInvalidKernelError'; +import { JupyterWaitForIdleError } from '../../client/datascience/jupyter/jupyterWaitForIdleError'; +import { JupyterKernelPromiseFailedError } from '../../client/datascience/jupyter/kernels/jupyterKernelPromiseFailedError'; +import { LiveKernelModel } from '../../client/datascience/jupyter/kernels/types'; +import { ICell, IJupyterKernelSpec, IJupyterSession } from '../../client/datascience/types'; +import { ServerStatus } from '../../datascience-ui/interactive-common/mainState'; +import { sleep } from '../core'; +import { MockJupyterRequest } from './mockJupyterRequest'; + +const LineFeedRegEx = /(\r\n|\n)/g; + +// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length +export class MockJupyterSession implements IJupyterSession { + private dict: Record; + private restartedEvent: EventEmitter = new EventEmitter(); + private onStatusChangedEvent: EventEmitter = new EventEmitter(); + private timedelay: number; + private executionCount: number = 0; + private outstandingRequestTokenSources: CancellationTokenSource[] = []; + private executes: string[] = []; + private forceRestartTimeout: boolean = false; + private completionTimeout: number = 1; + private lastRequest: MockJupyterRequest | undefined; + constructor( + cellDictionary: Record, + timedelay: number, + private pendingIdleFailure: boolean = false, + private pendingKernelChangeFailure: boolean = false + ) { + this.dict = cellDictionary; + this.timedelay = timedelay; + } + + public get onRestarted(): Event { + return this.restartedEvent.event; + } + + public get onSessionStatusChanged(): Event { + if (!this.onStatusChangedEvent) { + this.onStatusChangedEvent = new EventEmitter(); + } + return this.onStatusChangedEvent.event; + } + + public get status(): ServerStatus { + return ServerStatus.Idle; + } + + public async restart(_timeout: number): Promise { + // For every outstanding request, switch them to fail mode + const requests = [...this.outstandingRequestTokenSources]; + requests.forEach((r) => r.cancel()); + + if (this.forceRestartTimeout) { + throw new JupyterKernelPromiseFailedError('Forcing restart timeout'); + } + + return sleep(this.timedelay); + } + public interrupt(_timeout: number): Promise { + const requests = [...this.outstandingRequestTokenSources]; + requests.forEach((r) => r.cancel()); + return sleep(this.timedelay); + } + public waitForIdle(_timeout: number): Promise { + if (this.pendingIdleFailure) { + this.pendingIdleFailure = false; + return Promise.reject(new JupyterWaitForIdleError('Kernel is dead')); + } + return sleep(this.timedelay); + } + + public prolongRestarts() { + this.forceRestartTimeout = true; + } + public requestExecute( + content: KernelMessage.IExecuteRequestMsg['content'], + _disposeOnDone?: boolean, + _metadata?: JSONObject + ): Kernel.IFuture { + // Content should have the code + const cell = this.findCell(content.code); + if (cell) { + this.executes.push(content.code); + } + + // Create a new dummy request + this.executionCount += content.store_history && content.code.trim().length > 0 ? 1 : 0; + const tokenSource = new CancellationTokenSource(); + const request = new MockJupyterRequest(cell, this.timedelay, this.executionCount, tokenSource.token); + this.outstandingRequestTokenSources.push(tokenSource); + + // When it finishes, it should not be an outstanding request anymore + const removeHandler = () => { + this.outstandingRequestTokenSources = this.outstandingRequestTokenSources.filter((f) => f !== tokenSource); + if (this.lastRequest === request) { + this.lastRequest = undefined; + } + }; + request.done.then(removeHandler).catch(removeHandler); + this.lastRequest = request; + return request; + } + + public requestInspect( + _content: KernelMessage.IInspectRequestMsg['content'] + ): Promise { + return Promise.resolve({ + content: { + status: 'ok', + metadata: {}, + found: true, + data: {} // Could add variable values here? + }, + channel: 'shell', + header: { + date: 'foo', + version: '1', + session: '1', + msg_id: '1', + msg_type: 'inspect_reply', + username: 'foo' + }, + parent_header: { + date: 'foo', + version: '1', + session: '1', + msg_id: '1', + msg_type: 'inspect_request', + username: 'foo' + }, + metadata: {} + }); + } + + public sendInputReply(content: string) { + if (this.lastRequest) { + this.lastRequest.sendInputReply({ value: content, status: 'ok' }); + } + } + + public async requestComplete( + _content: KernelMessage.ICompleteRequestMsg['content'] + ): Promise { + await sleep(this.completionTimeout); + + return { + content: { + matches: ['printly', '%%bash'], // This keeps this in the intellisense when the editor pairs down results + cursor_start: 0, + cursor_end: 7, + status: 'ok', + metadata: {} + }, + channel: 'shell', + header: { + username: 'foo', + version: '1', + session: '1', + msg_id: '1', + msg_type: 'complete' as any, + date: '' + }, + parent_header: {}, + metadata: {} + } as any; + } + + public dispose(): Promise { + return sleep(10); + } + + public getExecutes(): string[] { + return this.executes; + } + + public setCompletionTimeout(timeout: number) { + this.completionTimeout = timeout; + } + + public changeKernel(kernel: IJupyterKernelSpec | LiveKernelModel, _timeoutMS: number): Promise { + if (this.pendingKernelChangeFailure) { + this.pendingKernelChangeFailure = false; + return Promise.reject(new JupyterInvalidKernelError(kernel)); + } + return Promise.resolve(); + } + + public registerCommTarget( + _targetName: string, + _callback: (comm: Kernel.IComm, msg: KernelMessage.ICommOpenMsg) => void | PromiseLike + ) { + noop(); + } + + public sendCommMessage( + buffers: (ArrayBuffer | ArrayBufferView)[], + content: { comm_id: string; data: JSONObject; target_name: string | undefined }, + // tslint:disable-next-line: no-any + metadata: any, + // tslint:disable-next-line: no-any + msgId: any + ): Kernel.IShellFuture< + KernelMessage.IShellMessage<'comm_msg'>, + KernelMessage.IShellMessage + > { + const shellMessage = KernelMessage.createMessage>({ + // tslint:disable-next-line: no-any + msgType: 'comm_msg', + channel: 'shell', + buffers, + content, + metadata, + msgId, + session: '1', + username: '1' + }); + + return { + done: Promise.resolve(undefined), + msg: shellMessage, + onReply: noop, + onIOPub: noop, + onStdin: noop, + registerMessageHook: noop, + removeMessageHook: noop, + sendInputReply: noop, + isDisposed: false, + dispose: noop + }; + } + + public requestCommInfo( + _content: KernelMessage.ICommInfoRequestMsg['content'] + ): Promise { + const shellMessage = KernelMessage.createMessage({ + msgType: 'comm_info_reply', + channel: 'shell', + content: { + status: 'ok' + // tslint:disable-next-line: no-any + } as any, + metadata: {}, + session: '1', + username: '1' + }); + + return Promise.resolve(shellMessage); + } + public registerMessageHook( + _msgId: string, + _hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike + ): void { + noop(); + } + public removeMessageHook( + _msgId: string, + _hook: (msg: KernelMessage.IIOPubMessage) => boolean | PromiseLike + ): void { + noop(); + } + + private findCell = (code: string): ICell => { + // Match skipping line separators + const withoutLines = code.replace(LineFeedRegEx, '').toLowerCase(); + + if (this.dict.hasOwnProperty(withoutLines)) { + return this.dict[withoutLines] as ICell; + } + // tslint:disable-next-line:no-console + console.log(`Cell '${code}' not found in mock`); + // tslint:disable-next-line:no-console + console.log(`Dict has these keys ${Object.keys(this.dict).join('","')}`); + throw new Error(`Cell '${code}' not found in mock`); + }; +} diff --git a/src/test/datascience/mockLanguageClient.ts b/src/test/datascience/mockLanguageClient.ts index 9bae511fd8a2..de84da6f1beb 100644 --- a/src/test/datascience/mockLanguageClient.ts +++ b/src/test/datascience/mockLanguageClient.ts @@ -227,7 +227,7 @@ export class MockLanguageClient extends LanguageClient { private getDocumentCompletions(): CompletionItem[] { const lines = this.contents.splitLines(); - return lines.map(l => { + return lines.map((l) => { return { label: l, insertText: l, diff --git a/src/test/datascience/mockLanguageServer.ts b/src/test/datascience/mockLanguageServer.ts index 549da770e42f..9f7f29086519 100644 --- a/src/test/datascience/mockLanguageServer.ts +++ b/src/test/datascience/mockLanguageServer.ts @@ -131,7 +131,7 @@ export class MockLanguageServer implements ILanguageServer { } private applyChanges(changes: TextDocumentContentChangeEvent[]) { - changes.forEach(c => { + changes.forEach((c) => { const before = this.contents.substr(0, c.rangeOffset); const after = this.contents.substr(c.rangeOffset + c.rangeLength); this.contents = `${before}${c.text}${after}`; diff --git a/src/test/datascience/mockLiveShare.ts b/src/test/datascience/mockLiveShare.ts index ed18f22205de..b388861e1f3b 100644 --- a/src/test/datascience/mockLiveShare.ts +++ b/src/test/datascience/mockLiveShare.ts @@ -1,444 +1,444 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { inject, injectable } from 'inversify'; -import * as uuid from 'uuid/v4'; -import { - CancellationToken, - CancellationTokenSource, - Disposable, - Event, - EventEmitter, - TreeDataProvider, - Uri -} from 'vscode'; -import * as vsls from 'vsls/vscode'; - -import { IApplicationShell, ILiveShareTestingApi } from '../../client/common/application/types'; -import { IConfigurationService, IDisposable, IDisposableRegistry } from '../../client/common/types'; -import { noop } from '../../client/common/utils/misc'; -import { LiveShare } from '../../client/datascience/constants'; -import { LiveShareProxy } from '../../client/datascience/liveshare/liveshareProxy'; - -// tslint:disable:no-any unified-signatures max-classes-per-file - -class MockLiveService implements vsls.SharedService, vsls.SharedServiceProxy { - public isServiceAvailable: boolean = true; - private changeIsServiceAvailableEmitter: EventEmitter = new EventEmitter(); - private requestHandlers: Map = new Map(); - private notifyHandlers: Map = new Map(); - private defaultCancellationSource = new CancellationTokenSource(); - private sibling: MockLiveService | undefined; - - public setSibling(sibling: MockLiveService) { - this.sibling = sibling; - } - - public get onDidChangeIsServiceAvailable(): Event { - return this.changeIsServiceAvailableEmitter.event; - } - public request(name: string, args: any[], cancellation?: CancellationToken): Promise { - // See if any handlers. - const handler = this.sibling ? this.sibling.requestHandlers.get(name) : undefined; - if (handler) { - return handler(args, cancellation ? cancellation : this.defaultCancellationSource.token); - } - return Promise.resolve(); - } - public onRequest(name: string, handler: vsls.RequestHandler): void { - this.requestHandlers.set(name, handler); - } - public onNotify(name: string, handler: vsls.NotifyHandler): void { - this.notifyHandlers.set(name, handler); - } - public notify(name: string, args: object): void { - // See if any handlers. - const handler = this.sibling ? this.sibling.notifyHandlers.get(name) : undefined; - if (handler) { - handler(args); - } - } - - public clearHandlers(): void { - this.requestHandlers.clear(); - this.notifyHandlers.clear(); - } -} - -type ArgumentType = 'boolean' | 'number' | 'string' | 'object' | 'function' | 'array' | 'uri'; - -function checkArg(value: any, name: string, type?: ArgumentType) { - if (!value) { - throw new Error(`Argument \'${name}\' is required.`); - } else if (type) { - if (type === 'array') { - if (!Array.isArray(value)) { - throw new Error(`Argument \'${name}\' must be an array.`); - } - } else if (type === 'uri') { - if (!(value instanceof Uri)) { - throw new Error(`Argument \'${name}\' must be a Uri object.`); - } - } else if (type === 'object' && Array.isArray(value)) { - throw new Error(`Argument \'${name}\' must be a a non-array object.`); - } else if (typeof value !== type) { - throw new Error(`Argument \'${name}\' must be type \'' + type + '\'.`); - } - } -} - -type Listener = [Function, any] | Function; - -class Emitter { - private _event: Event | undefined; - private _disposed: boolean = false; - private _deliveryQueue: { listener: Listener; event?: T }[] = []; - private _listeners: Listener[] = []; - - get event(): Event { - if (!this._event) { - this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]) => { - this._listeners.push(!thisArgs ? listener : [listener, thisArgs]); - let result: IDisposable; - result = { - dispose: () => { - result.dispose = noop; - if (!this._disposed) { - this._listeners = this._listeners.filter(l => l !== listener); - } - } - }; - if (Array.isArray(disposables)) { - disposables.push(result); - } - - return result; - }; - } - return this._event; - } - - public async fire(event?: T): Promise { - if (this._listeners) { - // put all [listener,event]-pairs into delivery queue - // then emit all event. an inner/nested event might be - // the driver of this - - if (!this._deliveryQueue) { - this._deliveryQueue = []; - } - - for (const l of this._listeners) { - this._deliveryQueue.push({ listener: l, event }); - } - - while (this._deliveryQueue.length > 0) { - const item = this._deliveryQueue.shift(); - let result: any; - try { - if (item && item.listener) { - if (typeof item.listener === 'function') { - result = item.listener.call(undefined, item.event); - } else { - const func = item.listener[0]; - if (func) { - result = func.call(item.listener[1], item.event); - } - } - } - } catch (e) { - // Do nothinga - } - if (result) { - const promise = result as Promise; - if (promise) { - await promise; - } - } - } - } - } - - public dispose() { - if (this._listeners) { - this._listeners = []; - } - if (this._deliveryQueue) { - this._deliveryQueue = []; - } - this._disposed = true; - } -} - -class MockLiveShare implements vsls.LiveShare, vsls.Session, vsls.Peer, IDisposable { - private static others: MockLiveShare[] = []; - private static services: Map = new Map(); - private changeSessionEmitter = new Emitter(); - private changePeersEmitter = new EventEmitter(); - private currentPeers: vsls.Peer[] = []; - private _id = uuid(); - private _peerNumber = 0; - private _visibleRole = vsls.Role.None; - constructor(private _role: vsls.Role) { - this._peerNumber = _role === vsls.Role.Host ? 0 : 1; - MockLiveShare.others.push(this); - } - - public onPeerConnected(peer: MockLiveShare) { - if (peer.role !== this.role) { - this.currentPeers.push(peer); - this.changePeersEmitter.fire({ added: [peer], removed: [] }); - } - } - - public dispose() { - MockLiveShare.others = MockLiveShare.others.filter(o => o._id !== this._id); - } - - public get session(): vsls.Session { - return this; - } - - public async start(): Promise { - this._visibleRole = this._role; - - // Special case, we need to wait for the fire to finish. This means - // the real product can have a race condition between starting the session and registering commands? - // Nope, because the guest side can't do anything until the session starts up. - await this.changeSessionEmitter.fire({ session: this }); - if (this._role === vsls.Role.Guest) { - for (const o of MockLiveShare.others) { - if (o._id !== this._id) { - o.onPeerConnected(this); - } - } - } - } - - public async stop(): Promise { - this._visibleRole = vsls.Role.None; - const existingPeers = this.currentPeers; - this.currentPeers = []; - this.changePeersEmitter.fire({ added: [], removed: existingPeers }); - await this.changeSessionEmitter.fire({ session: this }); - } - - public removeHandlers(serviceName: string) { - const services = MockLiveShare.services.get(serviceName); - if (!services) { - throw new Error(`${serviceName} failure to add service to map`); - } - - // Remove just the one corresponding to the role of this api - if (this.role === vsls.Role.Guest) { - services[1].clearHandlers(); - } else { - services[0].clearHandlers(); - } - } - - public getContacts(_emails: string[]): Promise { - throw new Error('Method not implemented.'); - } - - public get role(): vsls.Role { - return this._visibleRole; - } - public get id(): string { - return this._id; - } - public get peerNumber(): number { - return this._peerNumber; - } - public get user(): vsls.UserInfo { - return { - displayName: 'Test', - emailAddress: 'Test@Microsoft.Com', - userName: 'Test', - id: '0' - }; - } - public get access(): vsls.Access { - return vsls.Access.None; - } - - public get onDidChangeSession(): Event { - return this.changeSessionEmitter.event; - } - public get peers(): vsls.Peer[] { - return this.currentPeers; - } - public get onDidChangePeers(): Event { - return this.changePeersEmitter.event; - } - public share(_options?: vsls.ShareOptions): Promise { - throw new Error('Method not implemented.'); - } - public join(_link: Uri, _options?: vsls.JoinOptions): Promise { - throw new Error('Method not implemented.'); - } - public async end(): Promise { - // If we're the guest, just stop ourselves. If we're the host, stop everybody - if (this._role === vsls.Role.Guest) { - await this.stop(); - } else { - await Promise.all(MockLiveShare.others.map(p => p.stop())); - } - } - public shareService(name: string): Promise { - if (!MockLiveShare.services.has(name)) { - MockLiveShare.services.set(name, this.generateServicePair()); - } - const services = MockLiveShare.services.get(name); - if (!services) { - throw new Error(`${name} failure to add service to map`); - } - - // Host is always the first - return Promise.resolve(services[0]); - } - public unshareService(name: string): Promise { - MockLiveShare.services.delete(name); - return Promise.resolve(); - } - public getSharedService(name: string): Promise { - if (!MockLiveShare.services.has(name)) { - // Don't wait for the host to start. It shouldn't be necessary anyway. - MockLiveShare.services.set(name, this.generateServicePair()); - } - const services = MockLiveShare.services.get(name); - if (!services) { - throw new Error(`${name} failure to add service to map`); - } - - // Guest is always the second one - return Promise.resolve(services[1]); - } - public convertLocalUriToShared(localUri: Uri): Uri { - // Do the same checking that liveshare does - checkArg(localUri, 'localUri', 'uri'); - - if (this.session.role !== vsls.Role.Host) { - throw new Error('Only the host role can convert shared URIs.'); - } - - const scheme = 'vsls'; - if (localUri.scheme === scheme) { - throw new Error(`URI is already a ${scheme} URI: ${localUri}`); - } - - if (localUri.scheme !== 'file') { - throw new Error(`Not a workspace file URI: ${localUri}`); - } - - return Uri.parse(`vsls:${localUri.fsPath}`); - } - public convertSharedUriToLocal(sharedUri: Uri): Uri { - checkArg(sharedUri, 'sharedUri', 'uri'); - - if (this.session.role !== vsls.Role.Host) { - throw new Error('Only the host role can convert shared URIs.'); - } - - const scheme = 'vsls'; - if (sharedUri.scheme !== scheme) { - throw new Error(`Not a shared URI: ${sharedUri}`); - } - - return Uri.file(sharedUri.fsPath); - } - public registerCommand(_command: string, _isEnabled?: () => boolean, _thisArg?: any): Disposable { - throw new Error('Method not implemented.'); - } - public registerTreeDataProvider(_viewId: vsls.View, _treeDataProvider: TreeDataProvider): Disposable { - throw new Error('Method not implemented.'); - } - public registerContactServiceProvider( - _name: string, - _contactServiceProvider: vsls.ContactServiceProvider - ): Disposable { - throw new Error('Method not implemented.'); - } - public shareServer(_server: vsls.Server): Promise { - // Ignore for now. We don't need to port forward during a test - return Promise.resolve({ dispose: noop }); - } - - private generateServicePair(): MockLiveService[] { - const hostService = new MockLiveService(); - const guestService = new MockLiveService(); - hostService.setSibling(guestService); - guestService.setSibling(hostService); - // Host is always first - return [hostService, guestService]; - } -} - -@injectable() -export class MockLiveShareApi implements ILiveShareTestingApi { - private currentRole: vsls.Role = vsls.Role.None; - private internalApi: MockLiveShare | null = null; - private externalProxy: vsls.LiveShare | null = null; - private sessionStarted = false; - - constructor( - @inject(IDisposableRegistry) private disposables: IDisposableRegistry, - @inject(IApplicationShell) private appShell: IApplicationShell, - @inject(IConfigurationService) private config: IConfigurationService - ) {} - - public getApi(): Promise { - return Promise.resolve(this.externalProxy); - } - - public forceRole(role: vsls.Role) { - // Force a role on our live share api - if (role !== this.currentRole) { - this.internalApi = new MockLiveShare(role); - this.externalProxy = new LiveShareProxy( - this.appShell, - this.config.getSettings().datascience.liveShareConnectionTimeout, - this.internalApi - ); - this.internalApi.onDidChangeSession(this.onInternalSessionChanged, this); - this.currentRole = role; - this.disposables.push(this.internalApi); - } - } - - public async startSession(): Promise { - if (this.internalApi) { - await this.internalApi.start(); - this.sessionStarted = true; - } else { - throw Error('Cannot start session without a role.'); - } - } - - public async stopSession(): Promise { - if (this.internalApi) { - await this.internalApi.stop(); - this.sessionStarted = false; - } else { - throw Error('Cannot start session without a role.'); - } - } - - public disableGuestChecker() { - // Remove the handlers for the guest checker notification - if (this.internalApi) { - this.internalApi.removeHandlers(LiveShare.GuestCheckerService); - } - this.externalProxy = null; - } - - public get isSessionStarted(): boolean { - return this.sessionStarted; - } - - private onInternalSessionChanged(_ev: vsls.SessionChangeEvent) { - if (this.internalApi) { - this.sessionStarted = this.internalApi.role !== vsls.Role.None; - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { inject, injectable } from 'inversify'; +import * as uuid from 'uuid/v4'; +import { + CancellationToken, + CancellationTokenSource, + Disposable, + Event, + EventEmitter, + TreeDataProvider, + Uri +} from 'vscode'; +import * as vsls from 'vsls/vscode'; + +import { IApplicationShell, ILiveShareTestingApi } from '../../client/common/application/types'; +import { IConfigurationService, IDisposable, IDisposableRegistry } from '../../client/common/types'; +import { noop } from '../../client/common/utils/misc'; +import { LiveShare } from '../../client/datascience/constants'; +import { LiveShareProxy } from '../../client/datascience/liveshare/liveshareProxy'; + +// tslint:disable:no-any unified-signatures max-classes-per-file + +class MockLiveService implements vsls.SharedService, vsls.SharedServiceProxy { + public isServiceAvailable: boolean = true; + private changeIsServiceAvailableEmitter: EventEmitter = new EventEmitter(); + private requestHandlers: Map = new Map(); + private notifyHandlers: Map = new Map(); + private defaultCancellationSource = new CancellationTokenSource(); + private sibling: MockLiveService | undefined; + + public setSibling(sibling: MockLiveService) { + this.sibling = sibling; + } + + public get onDidChangeIsServiceAvailable(): Event { + return this.changeIsServiceAvailableEmitter.event; + } + public request(name: string, args: any[], cancellation?: CancellationToken): Promise { + // See if any handlers. + const handler = this.sibling ? this.sibling.requestHandlers.get(name) : undefined; + if (handler) { + return handler(args, cancellation ? cancellation : this.defaultCancellationSource.token); + } + return Promise.resolve(); + } + public onRequest(name: string, handler: vsls.RequestHandler): void { + this.requestHandlers.set(name, handler); + } + public onNotify(name: string, handler: vsls.NotifyHandler): void { + this.notifyHandlers.set(name, handler); + } + public notify(name: string, args: object): void { + // See if any handlers. + const handler = this.sibling ? this.sibling.notifyHandlers.get(name) : undefined; + if (handler) { + handler(args); + } + } + + public clearHandlers(): void { + this.requestHandlers.clear(); + this.notifyHandlers.clear(); + } +} + +type ArgumentType = 'boolean' | 'number' | 'string' | 'object' | 'function' | 'array' | 'uri'; + +function checkArg(value: any, name: string, type?: ArgumentType) { + if (!value) { + throw new Error(`Argument \'${name}\' is required.`); + } else if (type) { + if (type === 'array') { + if (!Array.isArray(value)) { + throw new Error(`Argument \'${name}\' must be an array.`); + } + } else if (type === 'uri') { + if (!(value instanceof Uri)) { + throw new Error(`Argument \'${name}\' must be a Uri object.`); + } + } else if (type === 'object' && Array.isArray(value)) { + throw new Error(`Argument \'${name}\' must be a a non-array object.`); + } else if (typeof value !== type) { + throw new Error(`Argument \'${name}\' must be type \'' + type + '\'.`); + } + } +} + +type Listener = [Function, any] | Function; + +class Emitter { + private _event: Event | undefined; + private _disposed: boolean = false; + private _deliveryQueue: { listener: Listener; event?: T }[] = []; + private _listeners: Listener[] = []; + + get event(): Event { + if (!this._event) { + this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]) => { + this._listeners.push(!thisArgs ? listener : [listener, thisArgs]); + let result: IDisposable; + result = { + dispose: () => { + result.dispose = noop; + if (!this._disposed) { + this._listeners = this._listeners.filter((l) => l !== listener); + } + } + }; + if (Array.isArray(disposables)) { + disposables.push(result); + } + + return result; + }; + } + return this._event; + } + + public async fire(event?: T): Promise { + if (this._listeners) { + // put all [listener,event]-pairs into delivery queue + // then emit all event. an inner/nested event might be + // the driver of this + + if (!this._deliveryQueue) { + this._deliveryQueue = []; + } + + for (const l of this._listeners) { + this._deliveryQueue.push({ listener: l, event }); + } + + while (this._deliveryQueue.length > 0) { + const item = this._deliveryQueue.shift(); + let result: any; + try { + if (item && item.listener) { + if (typeof item.listener === 'function') { + result = item.listener.call(undefined, item.event); + } else { + const func = item.listener[0]; + if (func) { + result = func.call(item.listener[1], item.event); + } + } + } + } catch (e) { + // Do nothinga + } + if (result) { + const promise = result as Promise; + if (promise) { + await promise; + } + } + } + } + } + + public dispose() { + if (this._listeners) { + this._listeners = []; + } + if (this._deliveryQueue) { + this._deliveryQueue = []; + } + this._disposed = true; + } +} + +class MockLiveShare implements vsls.LiveShare, vsls.Session, vsls.Peer, IDisposable { + private static others: MockLiveShare[] = []; + private static services: Map = new Map(); + private changeSessionEmitter = new Emitter(); + private changePeersEmitter = new EventEmitter(); + private currentPeers: vsls.Peer[] = []; + private _id = uuid(); + private _peerNumber = 0; + private _visibleRole = vsls.Role.None; + constructor(private _role: vsls.Role) { + this._peerNumber = _role === vsls.Role.Host ? 0 : 1; + MockLiveShare.others.push(this); + } + + public onPeerConnected(peer: MockLiveShare) { + if (peer.role !== this.role) { + this.currentPeers.push(peer); + this.changePeersEmitter.fire({ added: [peer], removed: [] }); + } + } + + public dispose() { + MockLiveShare.others = MockLiveShare.others.filter((o) => o._id !== this._id); + } + + public get session(): vsls.Session { + return this; + } + + public async start(): Promise { + this._visibleRole = this._role; + + // Special case, we need to wait for the fire to finish. This means + // the real product can have a race condition between starting the session and registering commands? + // Nope, because the guest side can't do anything until the session starts up. + await this.changeSessionEmitter.fire({ session: this }); + if (this._role === vsls.Role.Guest) { + for (const o of MockLiveShare.others) { + if (o._id !== this._id) { + o.onPeerConnected(this); + } + } + } + } + + public async stop(): Promise { + this._visibleRole = vsls.Role.None; + const existingPeers = this.currentPeers; + this.currentPeers = []; + this.changePeersEmitter.fire({ added: [], removed: existingPeers }); + await this.changeSessionEmitter.fire({ session: this }); + } + + public removeHandlers(serviceName: string) { + const services = MockLiveShare.services.get(serviceName); + if (!services) { + throw new Error(`${serviceName} failure to add service to map`); + } + + // Remove just the one corresponding to the role of this api + if (this.role === vsls.Role.Guest) { + services[1].clearHandlers(); + } else { + services[0].clearHandlers(); + } + } + + public getContacts(_emails: string[]): Promise { + throw new Error('Method not implemented.'); + } + + public get role(): vsls.Role { + return this._visibleRole; + } + public get id(): string { + return this._id; + } + public get peerNumber(): number { + return this._peerNumber; + } + public get user(): vsls.UserInfo { + return { + displayName: 'Test', + emailAddress: 'Test@Microsoft.Com', + userName: 'Test', + id: '0' + }; + } + public get access(): vsls.Access { + return vsls.Access.None; + } + + public get onDidChangeSession(): Event { + return this.changeSessionEmitter.event; + } + public get peers(): vsls.Peer[] { + return this.currentPeers; + } + public get onDidChangePeers(): Event { + return this.changePeersEmitter.event; + } + public share(_options?: vsls.ShareOptions): Promise { + throw new Error('Method not implemented.'); + } + public join(_link: Uri, _options?: vsls.JoinOptions): Promise { + throw new Error('Method not implemented.'); + } + public async end(): Promise { + // If we're the guest, just stop ourselves. If we're the host, stop everybody + if (this._role === vsls.Role.Guest) { + await this.stop(); + } else { + await Promise.all(MockLiveShare.others.map((p) => p.stop())); + } + } + public shareService(name: string): Promise { + if (!MockLiveShare.services.has(name)) { + MockLiveShare.services.set(name, this.generateServicePair()); + } + const services = MockLiveShare.services.get(name); + if (!services) { + throw new Error(`${name} failure to add service to map`); + } + + // Host is always the first + return Promise.resolve(services[0]); + } + public unshareService(name: string): Promise { + MockLiveShare.services.delete(name); + return Promise.resolve(); + } + public getSharedService(name: string): Promise { + if (!MockLiveShare.services.has(name)) { + // Don't wait for the host to start. It shouldn't be necessary anyway. + MockLiveShare.services.set(name, this.generateServicePair()); + } + const services = MockLiveShare.services.get(name); + if (!services) { + throw new Error(`${name} failure to add service to map`); + } + + // Guest is always the second one + return Promise.resolve(services[1]); + } + public convertLocalUriToShared(localUri: Uri): Uri { + // Do the same checking that liveshare does + checkArg(localUri, 'localUri', 'uri'); + + if (this.session.role !== vsls.Role.Host) { + throw new Error('Only the host role can convert shared URIs.'); + } + + const scheme = 'vsls'; + if (localUri.scheme === scheme) { + throw new Error(`URI is already a ${scheme} URI: ${localUri}`); + } + + if (localUri.scheme !== 'file') { + throw new Error(`Not a workspace file URI: ${localUri}`); + } + + return Uri.parse(`vsls:${localUri.fsPath}`); + } + public convertSharedUriToLocal(sharedUri: Uri): Uri { + checkArg(sharedUri, 'sharedUri', 'uri'); + + if (this.session.role !== vsls.Role.Host) { + throw new Error('Only the host role can convert shared URIs.'); + } + + const scheme = 'vsls'; + if (sharedUri.scheme !== scheme) { + throw new Error(`Not a shared URI: ${sharedUri}`); + } + + return Uri.file(sharedUri.fsPath); + } + public registerCommand(_command: string, _isEnabled?: () => boolean, _thisArg?: any): Disposable { + throw new Error('Method not implemented.'); + } + public registerTreeDataProvider(_viewId: vsls.View, _treeDataProvider: TreeDataProvider): Disposable { + throw new Error('Method not implemented.'); + } + public registerContactServiceProvider( + _name: string, + _contactServiceProvider: vsls.ContactServiceProvider + ): Disposable { + throw new Error('Method not implemented.'); + } + public shareServer(_server: vsls.Server): Promise { + // Ignore for now. We don't need to port forward during a test + return Promise.resolve({ dispose: noop }); + } + + private generateServicePair(): MockLiveService[] { + const hostService = new MockLiveService(); + const guestService = new MockLiveService(); + hostService.setSibling(guestService); + guestService.setSibling(hostService); + // Host is always first + return [hostService, guestService]; + } +} + +@injectable() +export class MockLiveShareApi implements ILiveShareTestingApi { + private currentRole: vsls.Role = vsls.Role.None; + private internalApi: MockLiveShare | null = null; + private externalProxy: vsls.LiveShare | null = null; + private sessionStarted = false; + + constructor( + @inject(IDisposableRegistry) private disposables: IDisposableRegistry, + @inject(IApplicationShell) private appShell: IApplicationShell, + @inject(IConfigurationService) private config: IConfigurationService + ) {} + + public getApi(): Promise { + return Promise.resolve(this.externalProxy); + } + + public forceRole(role: vsls.Role) { + // Force a role on our live share api + if (role !== this.currentRole) { + this.internalApi = new MockLiveShare(role); + this.externalProxy = new LiveShareProxy( + this.appShell, + this.config.getSettings().datascience.liveShareConnectionTimeout, + this.internalApi + ); + this.internalApi.onDidChangeSession(this.onInternalSessionChanged, this); + this.currentRole = role; + this.disposables.push(this.internalApi); + } + } + + public async startSession(): Promise { + if (this.internalApi) { + await this.internalApi.start(); + this.sessionStarted = true; + } else { + throw Error('Cannot start session without a role.'); + } + } + + public async stopSession(): Promise { + if (this.internalApi) { + await this.internalApi.stop(); + this.sessionStarted = false; + } else { + throw Error('Cannot start session without a role.'); + } + } + + public disableGuestChecker() { + // Remove the handlers for the guest checker notification + if (this.internalApi) { + this.internalApi.removeHandlers(LiveShare.GuestCheckerService); + } + this.externalProxy = null; + } + + public get isSessionStarted(): boolean { + return this.sessionStarted; + } + + private onInternalSessionChanged(_ev: vsls.SessionChangeEvent) { + if (this.internalApi) { + this.sessionStarted = this.internalApi.role !== vsls.Role.None; + } + } +} diff --git a/src/test/datascience/mockProcessService.ts b/src/test/datascience/mockProcessService.ts index 59308749305e..7b795902a141 100644 --- a/src/test/datascience/mockProcessService.ts +++ b/src/test/datascience/mockProcessService.ts @@ -1,108 +1,108 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { Observable } from 'rxjs/Observable'; - -import { Cancellation, CancellationError } from '../../client/common/cancellation'; -import { - ExecutionResult, - IProcessService, - ObservableExecutionResult, - Output, - ShellOptions, - SpawnOptions -} from '../../client/common/process/types'; -import { noop, sleep } from '../core'; - -export class MockProcessService implements IProcessService { - private execResults: { file: string; args: (string | RegExp)[]; result(): Promise> }[] = []; - private execObservableResults: { - file: string; - args: (string | RegExp)[]; - result(): ObservableExecutionResult; - }[] = []; - private timeDelay: number | undefined; - - public execObservable(file: string, args: string[], _options: SpawnOptions): ObservableExecutionResult { - const match = this.execObservableResults.find(f => this.argsMatch(f.args, args) && f.file === file); - if (match) { - return match.result(); - } - - return this.defaultObservable([file, ...args]); - } - - public async exec(file: string, args: string[], options: SpawnOptions): Promise> { - const match = this.execResults.find(f => this.argsMatch(f.args, args) && f.file === file); - if (match) { - // Might need a delay before executing to mimic it taking a while. - if (this.timeDelay) { - try { - const localTime = this.timeDelay; - await Cancellation.race(_t => sleep(localTime), options.token); - } catch (exc) { - if (exc instanceof CancellationError) { - return this.defaultExecutionResult([file, ...args]); - } - } - } - return match.result(); - } - - return this.defaultExecutionResult([file, ...args]); - } - - public shellExec(command: string, _options: ShellOptions): Promise> { - // Not supported - return this.defaultExecutionResult([command]); - } - - public addExecResult(file: string, args: (string | RegExp)[], result: () => Promise>) { - this.execResults.splice(0, 0, { file: file, args: args, result: result }); - } - - public addExecObservableResult( - file: string, - args: (string | RegExp)[], - result: () => ObservableExecutionResult - ) { - this.execObservableResults.splice(0, 0, { file: file, args: args, result: result }); - } - - public setDelay(timeout: number | undefined) { - this.timeDelay = timeout; - } - - public on() { - return this; - } - - public dispose() { - return; - } - - private argsMatch(matchers: (string | RegExp)[], args: string[]): boolean { - if (matchers.length === args.length) { - return args.every((s, i) => { - const r = matchers[i] as RegExp; - return r && r.test ? r.test(s) : s === matchers[i]; - }); - } - return false; - } - - private defaultObservable(args: string[]): ObservableExecutionResult { - const output = new Observable>(subscriber => { - subscriber.next({ out: `Invalid call to ${args.join(' ')}`, source: 'stderr' }); - }); - return { - proc: undefined, - out: output, - dispose: () => noop - }; - } - - private defaultExecutionResult(args: string[]): Promise> { - return Promise.resolve({ stderr: `Invalid call to ${args.join(' ')}`, stdout: '' }); - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { Observable } from 'rxjs/Observable'; + +import { Cancellation, CancellationError } from '../../client/common/cancellation'; +import { + ExecutionResult, + IProcessService, + ObservableExecutionResult, + Output, + ShellOptions, + SpawnOptions +} from '../../client/common/process/types'; +import { noop, sleep } from '../core'; + +export class MockProcessService implements IProcessService { + private execResults: { file: string; args: (string | RegExp)[]; result(): Promise> }[] = []; + private execObservableResults: { + file: string; + args: (string | RegExp)[]; + result(): ObservableExecutionResult; + }[] = []; + private timeDelay: number | undefined; + + public execObservable(file: string, args: string[], _options: SpawnOptions): ObservableExecutionResult { + const match = this.execObservableResults.find((f) => this.argsMatch(f.args, args) && f.file === file); + if (match) { + return match.result(); + } + + return this.defaultObservable([file, ...args]); + } + + public async exec(file: string, args: string[], options: SpawnOptions): Promise> { + const match = this.execResults.find((f) => this.argsMatch(f.args, args) && f.file === file); + if (match) { + // Might need a delay before executing to mimic it taking a while. + if (this.timeDelay) { + try { + const localTime = this.timeDelay; + await Cancellation.race((_t) => sleep(localTime), options.token); + } catch (exc) { + if (exc instanceof CancellationError) { + return this.defaultExecutionResult([file, ...args]); + } + } + } + return match.result(); + } + + return this.defaultExecutionResult([file, ...args]); + } + + public shellExec(command: string, _options: ShellOptions): Promise> { + // Not supported + return this.defaultExecutionResult([command]); + } + + public addExecResult(file: string, args: (string | RegExp)[], result: () => Promise>) { + this.execResults.splice(0, 0, { file: file, args: args, result: result }); + } + + public addExecObservableResult( + file: string, + args: (string | RegExp)[], + result: () => ObservableExecutionResult + ) { + this.execObservableResults.splice(0, 0, { file: file, args: args, result: result }); + } + + public setDelay(timeout: number | undefined) { + this.timeDelay = timeout; + } + + public on() { + return this; + } + + public dispose() { + return; + } + + private argsMatch(matchers: (string | RegExp)[], args: string[]): boolean { + if (matchers.length === args.length) { + return args.every((s, i) => { + const r = matchers[i] as RegExp; + return r && r.test ? r.test(s) : s === matchers[i]; + }); + } + return false; + } + + private defaultObservable(args: string[]): ObservableExecutionResult { + const output = new Observable>((subscriber) => { + subscriber.next({ out: `Invalid call to ${args.join(' ')}`, source: 'stderr' }); + }); + return { + proc: undefined, + out: output, + dispose: () => noop + }; + } + + private defaultExecutionResult(args: string[]): Promise> { + return Promise.resolve({ stderr: `Invalid call to ${args.join(' ')}`, stdout: '' }); + } +} diff --git a/src/test/datascience/mockProtocol2CodeConverter.ts b/src/test/datascience/mockProtocol2CodeConverter.ts index 67f1de56a146..45c02d3154bd 100644 --- a/src/test/datascience/mockProtocol2CodeConverter.ts +++ b/src/test/datascience/mockProtocol2CodeConverter.ts @@ -312,7 +312,7 @@ export class MockProtocol2CodeConverter implements Protocol2CodeConverter { } private isStringArray(value: any): value is string[] { - return Array.isArray(value) && (value).every(elem => typeof elem === 'string'); + return Array.isArray(value) && (value).every((elem) => typeof elem === 'string'); } private asCompletionInsertText( diff --git a/src/test/datascience/mockPythonService.ts b/src/test/datascience/mockPythonService.ts index a59c9d088dd5..292acee75880 100644 --- a/src/test/datascience/mockPythonService.ts +++ b/src/test/datascience/mockPythonService.ts @@ -1,83 +1,83 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { - ExecutionResult, - InterpreterInfomation, - IPythonExecutionService, - ObservableExecutionResult, - SpawnOptions -} from '../../client/common/process/types'; -import { PythonInterpreter } from '../../client/interpreter/contracts'; -import { MockProcessService } from './mockProcessService'; - -export class MockPythonService implements IPythonExecutionService { - private interpreter: PythonInterpreter; - private procService: MockProcessService = new MockProcessService(); - - constructor(interpreter: PythonInterpreter) { - this.interpreter = interpreter; - } - - public getInterpreterInformation(): Promise { - return Promise.resolve(this.interpreter); - } - - public getExecutablePath(): Promise { - return Promise.resolve(this.interpreter.path); - } - - public isModuleInstalled(_moduleName: string): Promise { - return Promise.resolve(false); - } - - public execObservable(args: string[], options: SpawnOptions): ObservableExecutionResult { - return this.procService.execObservable(this.interpreter.path, args, options); - } - public execModuleObservable( - moduleName: string, - args: string[], - options: SpawnOptions - ): ObservableExecutionResult { - return this.procService.execObservable(this.interpreter.path, ['-m', moduleName, ...args], options); - } - public exec(args: string[], options: SpawnOptions): Promise> { - return this.procService.exec(this.interpreter.path, args, options); - } - - public execModule(moduleName: string, args: string[], options: SpawnOptions): Promise> { - return this.procService.exec(this.interpreter.path, ['-m', moduleName, ...args], options); - } - - public addExecResult(args: (string | RegExp)[], result: () => Promise>) { - this.procService.addExecResult(this.interpreter.path, args, result); - } - - public addExecModuleResult( - moduleName: string, - args: (string | RegExp)[], - result: () => Promise> - ) { - this.procService.addExecResult(this.interpreter.path, ['-m', moduleName, ...args], result); - } - - public addExecObservableResult(args: (string | RegExp)[], result: () => ObservableExecutionResult) { - this.procService.addExecObservableResult(this.interpreter.path, args, result); - } - - public addExecModuleObservableResult( - moduleName: string, - args: (string | RegExp)[], - result: () => ObservableExecutionResult - ) { - this.procService.addExecObservableResult(this.interpreter.path, ['-m', moduleName, ...args], result); - } - - public setDelay(timeout: number | undefined) { - this.procService.setDelay(timeout); - } - - public getExecutionInfo(args: string[]) { - return { command: this.interpreter.path, args }; - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { + ExecutionResult, + InterpreterInfomation, + IPythonExecutionService, + ObservableExecutionResult, + SpawnOptions +} from '../../client/common/process/types'; +import { PythonInterpreter } from '../../client/interpreter/contracts'; +import { MockProcessService } from './mockProcessService'; + +export class MockPythonService implements IPythonExecutionService { + private interpreter: PythonInterpreter; + private procService: MockProcessService = new MockProcessService(); + + constructor(interpreter: PythonInterpreter) { + this.interpreter = interpreter; + } + + public getInterpreterInformation(): Promise { + return Promise.resolve(this.interpreter); + } + + public getExecutablePath(): Promise { + return Promise.resolve(this.interpreter.path); + } + + public isModuleInstalled(_moduleName: string): Promise { + return Promise.resolve(false); + } + + public execObservable(args: string[], options: SpawnOptions): ObservableExecutionResult { + return this.procService.execObservable(this.interpreter.path, args, options); + } + public execModuleObservable( + moduleName: string, + args: string[], + options: SpawnOptions + ): ObservableExecutionResult { + return this.procService.execObservable(this.interpreter.path, ['-m', moduleName, ...args], options); + } + public exec(args: string[], options: SpawnOptions): Promise> { + return this.procService.exec(this.interpreter.path, args, options); + } + + public execModule(moduleName: string, args: string[], options: SpawnOptions): Promise> { + return this.procService.exec(this.interpreter.path, ['-m', moduleName, ...args], options); + } + + public addExecResult(args: (string | RegExp)[], result: () => Promise>) { + this.procService.addExecResult(this.interpreter.path, args, result); + } + + public addExecModuleResult( + moduleName: string, + args: (string | RegExp)[], + result: () => Promise> + ) { + this.procService.addExecResult(this.interpreter.path, ['-m', moduleName, ...args], result); + } + + public addExecObservableResult(args: (string | RegExp)[], result: () => ObservableExecutionResult) { + this.procService.addExecObservableResult(this.interpreter.path, args, result); + } + + public addExecModuleObservableResult( + moduleName: string, + args: (string | RegExp)[], + result: () => ObservableExecutionResult + ) { + this.procService.addExecObservableResult(this.interpreter.path, ['-m', moduleName, ...args], result); + } + + public setDelay(timeout: number | undefined) { + this.procService.setDelay(timeout); + } + + public getExecutionInfo(args: string[]) { + return { command: this.interpreter.path, args }; + } +} diff --git a/src/test/datascience/mockQuickPick.ts b/src/test/datascience/mockQuickPick.ts index 00448cecabd2..3f8a7b0f3675 100644 --- a/src/test/datascience/mockQuickPick.ts +++ b/src/test/datascience/mockQuickPick.ts @@ -60,7 +60,7 @@ export class MockQuickPick implements QuickPick { public show(): void { // After a timeout select the item setTimeout(() => { - const item = this.items.find(a => a.label === this._pickedItem); + const item = this.items.find((a) => a.label === this._pickedItem); if (item) { this.didChangeSelectedEmitter.fire([item]); } else { diff --git a/src/test/datascience/mockTextEditor.ts b/src/test/datascience/mockTextEditor.ts index d6b8d6e075a6..88a8019c0bc4 100644 --- a/src/test/datascience/mockTextEditor.ts +++ b/src/test/datascience/mockTextEditor.ts @@ -75,7 +75,7 @@ export class MockEditor implements TextEditor { callback: (editBuilder: TextEditorEdit) => void, _options?: { undoStopBefore: boolean; undoStopAfter: boolean } | undefined ): Thenable { - return new Promise(r => { + return new Promise((r) => { const editor = new MockEditorEdit(this._documentManager, this._document); callback(editor); r(true); diff --git a/src/test/datascience/mockWorkspaceConfig.ts b/src/test/datascience/mockWorkspaceConfig.ts index aa8c5706c09a..12d5b8d3c268 100644 --- a/src/test/datascience/mockWorkspaceConfig.ts +++ b/src/test/datascience/mockWorkspaceConfig.ts @@ -12,7 +12,7 @@ export class MockWorkspaceConfiguration implements WorkspaceConfiguration { constructor(defaultSettings?: any) { if (defaultSettings) { const keys = [...Object.keys(defaultSettings)]; - keys.forEach(k => this.values.set(k, defaultSettings[k])); + keys.forEach((k) => this.values.set(k, defaultSettings[k])); } // Special case python path (not in the object) diff --git a/src/test/datascience/nativeEditor.functional.test.tsx b/src/test/datascience/nativeEditor.functional.test.tsx index ef0884ec5f16..01640ea3c882 100644 --- a/src/test/datascience/nativeEditor.functional.test.tsx +++ b/src/test/datascience/nativeEditor.functional.test.tsx @@ -98,7 +98,7 @@ suite('DataScience Native Editor', () => { })(originalPlatform) ); - [false, true].forEach(useCustomEditorApi => { + [false, true].forEach((useCustomEditorApi) => { //import { asyncDump } from '../common/asyncDump'; suite(`${useCustomEditorApi ? 'With' : 'Without'} Custom Editor API`, () => { function createFileCell(cell: any, data: any): ICell { @@ -137,18 +137,18 @@ suite('DataScience Native Editor', () => { const appShell = TypeMoq.Mock.ofType(); appShell - .setup(a => a.showErrorMessage(TypeMoq.It.isAnyString())) - .returns(_e => Promise.resolve('')); + .setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString())) + .returns((_e) => Promise.resolve('')); appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve('')); appShell - .setup(a => + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns((_a1: string, a2: string, _a3: string) => Promise.resolve(a2)); appShell - .setup(a => + .setup((a) => a.showInformationMessage( TypeMoq.It.isAny(), TypeMoq.It.isAny(), @@ -158,7 +158,7 @@ suite('DataScience Native Editor', () => { ) .returns((_a1: string, _a2: any, _a3: string, a4: string) => Promise.resolve(a4)); appShell - .setup(a => a.showSaveDialog(TypeMoq.It.isAny())) + .setup((a) => a.showSaveDialog(TypeMoq.It.isAny())) .returns(() => Promise.resolve(Uri.file('foo.ipynb'))); ioc.serviceManager.rebindInstance(IApplicationShell, appShell.object); tempNotebookFile = await createTemporaryFile('.ipynb'); @@ -190,7 +190,7 @@ suite('DataScience Native Editor', () => { runMountedTest( 'Simple text', - async wrapper => { + async (wrapper) => { // Create an editor so something is listening to messages await createNewEditor(ioc); @@ -279,7 +279,7 @@ suite('DataScience Native Editor', () => { runMountedTest( 'Mime Types', - async wrapper => { + async (wrapper) => { // Create an editor so something is listening to messages await createNewEditor(ioc); @@ -329,7 +329,7 @@ df.head()`; const cursors = ['|', '/', '-', '\\']; let cursorPos = 0; let loops = 3; - addContinuousMockData(ioc, spinningCursor, async _c => { + addContinuousMockData(ioc, spinningCursor, async (_c) => { const result = `${cursors[cursorPos]}\r`; cursorPos += 1; if (cursorPos >= cursors.length) { @@ -363,19 +363,19 @@ df.head()`; runMountedTest( 'Click buttons', - async wrapper => { + async (wrapper) => { // Goto source should cause the visible editor to be picked as long as its filename matches const showedEditor = createDeferred(); const textEditors: TextEditor[] = []; const docManager = TypeMoq.Mock.ofType(); const visibleEditor = TypeMoq.Mock.ofType(); const dummyDocument = TypeMoq.Mock.ofType(); - dummyDocument.setup(d => d.fileName).returns(() => Uri.file('foo.py').fsPath); - visibleEditor.setup(v => v.show()).returns(() => showedEditor.resolve()); - visibleEditor.setup(v => v.revealRange(TypeMoq.It.isAny())).returns(noop); - visibleEditor.setup(v => v.document).returns(() => dummyDocument.object); + dummyDocument.setup((d) => d.fileName).returns(() => Uri.file('foo.py').fsPath); + visibleEditor.setup((v) => v.show()).returns(() => showedEditor.resolve()); + visibleEditor.setup((v) => v.revealRange(TypeMoq.It.isAny())).returns(noop); + visibleEditor.setup((v) => v.document).returns(() => dummyDocument.object); textEditors.push(visibleEditor.object); - docManager.setup(a => a.visibleTextEditors).returns(() => textEditors); + docManager.setup((a) => a.visibleTextEditors).returns(() => textEditors); ioc.serviceManager.rebindInstance(IDocumentManager, docManager.object); // Create an editor so something is listening to messages await createNewEditor(ioc); @@ -421,7 +421,7 @@ df.head()`; runMountedTest( 'Select Jupyter Server', - async _wrapper => { + async (_wrapper) => { // tslint:disable-next-line: no-console console.log('Test skipped until user can change jupyter server selection again'); // let selectorCalled = false; @@ -446,7 +446,7 @@ df.head()`; runMountedTest( 'Select Jupyter Kernel', - async _wrapper => { + async (_wrapper) => { // tslint:disable-next-line: no-console console.log('Tests skipped, as we need better tests'); // let selectorCalled = false; @@ -529,7 +529,7 @@ df.head()`; runMountedTest( 'Convert to python', - async wrapper => { + async (wrapper) => { // Export should cause the export dialog to come up. Remap appshell so we can check const dummyDisposable = { dispose: () => { @@ -538,19 +538,19 @@ df.head()`; }; const appShell = TypeMoq.Mock.ofType(); appShell - .setup(a => a.showErrorMessage(TypeMoq.It.isAnyString())) - .returns(e => { + .setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString())) + .returns((e) => { throw e; }); appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve('')); appShell - .setup(a => a.showSaveDialog(TypeMoq.It.isAny())) + .setup((a) => a.showSaveDialog(TypeMoq.It.isAny())) .returns(() => { return Promise.resolve(Uri.file(tempNotebookFile.filePath)); }); - appShell.setup(a => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); + appShell.setup((a) => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); ioc.serviceManager.rebindInstance(IApplicationShell, appShell.object); // Make sure to create the interactive window after the rebind or it gets the wrong application shell. @@ -587,7 +587,7 @@ df.head()`; runMountedTest( 'Save As', - async wrapper => { + async (wrapper) => { if (useCustomEditorApi) { return; } @@ -600,19 +600,19 @@ df.head()`; }; const appShell = TypeMoq.Mock.ofType(); appShell - .setup(a => a.showErrorMessage(TypeMoq.It.isAnyString())) - .returns(e => { + .setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString())) + .returns((e) => { throw e; }); appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve('')); appShell - .setup(a => a.showSaveDialog(TypeMoq.It.isAny())) + .setup((a) => a.showSaveDialog(TypeMoq.It.isAny())) .returns(() => { return Promise.resolve(Uri.file(tempNotebookFile.filePath)); }); - appShell.setup(a => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); + appShell.setup((a) => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); ioc.serviceManager.rebindInstance(IApplicationShell, appShell.object); // Make sure to create the interactive window after the rebind or it gets the wrong application shell. @@ -641,7 +641,7 @@ df.head()`; runMountedTest( 'RunAllCells', - async wrapper => { + async (wrapper) => { addMockData(ioc, 'print(1)\na=1', 1); addMockData(ioc, 'a=a+1\nprint(a)', 2); addMockData(ioc, 'print(a+1)', 3); @@ -651,7 +651,7 @@ df.head()`; { id: 'NotebookImport#1', data: { source: 'a=a+1\nprint(a)' } }, { id: 'NotebookImport#2', data: { source: 'print(a+1)' } } ]; - const runAllCells = baseFile.map(cell => { + const runAllCells = baseFile.map((cell) => { return createFileCell(cell, cell.data); }); const notebook = await ioc @@ -678,7 +678,7 @@ df.head()`; runMountedTest( 'Startup and shutdown', - async wrapper => { + async (wrapper) => { // Stub the `stat` method to return a dummy value. try { sinon @@ -697,7 +697,7 @@ df.head()`; { id: 'NotebookImport#1', data: { source: 'b=2\nb' } }, { id: 'NotebookImport#2', data: { source: 'c=3\nc' } } ]; - const runAllCells = baseFile.map(cell => { + const runAllCells = baseFile.map((cell) => { return createFileCell(cell, cell.data); }); const notebook = await ioc @@ -778,7 +778,7 @@ df.head()`; assert.ok(cell, 'Cannot find the first cell'); const imageButtons = cell!.find(ImageButton); assert.equal(imageButtons.length, 6, 'Cell buttons not found'); - const runButton = imageButtons.findWhere(w => w.props().tooltip === 'Run cell'); + const runButton = imageButtons.findWhere((w) => w.props().tooltip === 'Run cell'); assert.equal(runButton.length, 1, 'No run button found'); const update = waitForMessage(ioc, InteractiveWindowMessages.ExecutionRendered, { numberOfTimes: 3 @@ -953,10 +953,7 @@ df.head()`; function clickCell(cellIndex: number) { wrapper.update(); - wrapper - .find(NativeCell) - .at(cellIndex) - .simulate('click'); + wrapper.find(NativeCell).at(cellIndex).simulate('click'); wrapper.update(); } @@ -1048,7 +1045,7 @@ df.head()`; } suite('Selection/Focus', () => { - setup(async function() { + setup(async function () { await initIoc(); // tslint:disable-next-line: no-invalid-this await setupFunction.call(this); @@ -1101,13 +1098,7 @@ df.head()`; await update; assert.ok(isCellFocused(wrapper, 'NativeCell', 0)); - assert.equal( - wrapper - .find(NativeCell) - .at(0) - .find(MonacoEditor).length, - 1 - ); + assert.equal(wrapper.find(NativeCell).at(0).find(MonacoEditor).length, 1); // Verify cell content const currentEditor = getNativeFocusedEditor(wrapper); @@ -1145,7 +1136,7 @@ df.head()`; }); suite('Model updates', () => { - setup(async function() { + setup(async function () { await initIoc(); // tslint:disable-next-line: no-invalid-this await setupFunction.call(this); @@ -1291,12 +1282,12 @@ df.head()`; // Lets create deferreds that we can await on, and each will be resolved with the edit it received. // For first edit, we'll expect `H`, then `i`, then `!` const modelEditsInExtension = stringToType.split('').map(createDeferred); - model?.changed(e => { + model?.changed((e) => { if (e.kind === 'edit') { // Find the first deferred that's no completed. - const deferred = modelEditsInExtension.find(d => !d.completed); + const deferred = modelEditsInExtension.find((d) => !d.completed); // Resolve promise with the character/string it received as edit. - deferred?.resolve(e.forward.map(m => m.text).join('')); + deferred?.resolve(e.forward.map((m) => m.text).join('')); } }); @@ -1348,9 +1339,9 @@ df.head()`; const modelEditsInExtension = createDeferred(); // Create deferred to detect changes to cellType. const modelCellChangedInExtension = createDeferred(); - model?.changed(e => { + model?.changed((e) => { // Resolve promise when we receive last edit (the last character `!`). - if (e.kind === 'edit' && e.forward.map(m => m.text).join('') === '!') { + if (e.kind === 'edit' && e.forward.map((m) => m.text).join('') === '!') { modelEditsInExtension.resolve(); } if (e.kind === 'changeCellType') { @@ -1406,7 +1397,7 @@ df.head()`; }); suite('Keyboard Shortcuts', () => { - setup(async function() { + setup(async function () { (window.navigator as any).platform = originalPlatform; await initIoc(); // tslint:disable-next-line: no-invalid-this @@ -1477,11 +1468,7 @@ df.head()`; // For some reason we cannot allow setting focus to monaco editor. // Tests are known to fall over if allowed. wrapper.update(); - const editor = wrapper - .find(NativeCell) - .at(1) - .find(Editor) - .first(); + const editor = wrapper.find(NativeCell).at(1).find(Editor).first(); (editor.instance() as Editor).giveFocus = () => editor.props().focused!(); const update = waitForUpdate(wrapper, NativeEditor, 1); @@ -1819,11 +1806,7 @@ df.head()`; test("Toggle line numbers using the 'l' key", async () => { clickCell(1); - const monacoEditorComponent = wrapper - .find(NativeCell) - .at(1) - .find(MonacoEditor) - .first(); + const monacoEditorComponent = wrapper.find(NativeCell).at(1).find(MonacoEditor).first(); const editor = (monacoEditorComponent.instance().state as IMonacoEditorState).editor!; const optionsUpdated = sinon.spy(editor, 'updateOptions'); @@ -1875,13 +1858,7 @@ df.head()`; await update; assert.ok(isCellFocused(wrapper, 'NativeCell', 1)); - assert.equal( - wrapper - .find(NativeCell) - .at(1) - .find(MonacoEditor).length, - 1 - ); + assert.equal(wrapper.find(NativeCell).at(1).find(MonacoEditor).length, 1); // Change the markdown let editor = getNativeFocusedEditor(wrapper); @@ -1896,13 +1873,7 @@ df.head()`; // Confirm markdown output is rendered assert.ok(!isCellFocused(wrapper, 'NativeCell', 1), '1st cell is focused'); assert.ok(isCellMarkdown(wrapper, 'NativeCell', 1), '1st cell is not markdown'); - assert.equal( - wrapper - .find(NativeCell) - .at(1) - .find(MonacoEditor).length, - 0 - ); + assert.equal(wrapper.find(NativeCell).at(1).find(MonacoEditor).length, 0); // Switch to code update = waitForMessage(ioc, CommonActionType.CHANGE_CELL_TYPE); @@ -1923,7 +1894,7 @@ df.head()`; assert.equal('foo', monacoEditor.state.editor!.getValue(), 'Changing cell type lost input'); }); - test("Test undo using the key 'z'", async function() { + test("Test undo using the key 'z'", async function () { if (useCustomEditorApi) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -2000,7 +1971,7 @@ df.head()`; } }); - test("Test save using the key 'ctrl+s' on Windows", async function() { + test("Test save using the key 'ctrl+s' on Windows", async function () { if (useCustomEditorApi) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -2029,7 +2000,7 @@ df.head()`; assert.ok(!editor!.isDirty, 'Editor should not be dirty after saving'); }); - test("Test save using the key 'ctrl+s' on Mac", async function() { + test("Test save using the key 'ctrl+s' on Mac", async function () { if (useCustomEditorApi) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -2060,7 +2031,7 @@ df.head()`; assert.ok(editor!.isDirty, 'Editor be dirty as nothing got saved'); }); - test("Test save using the key 'cmd+s' on a Mac", async function() { + test("Test save using the key 'cmd+s' on a Mac", async function () { if (useCustomEditorApi) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -2091,7 +2062,7 @@ df.head()`; assert.ok(!editor!.isDirty, 'Editor should not be dirty after saving'); }); - test("Test save using the key 'cmd+s' on a Windows", async function() { + test("Test save using the key 'cmd+s' on a Windows", async function () { if (useCustomEditorApi) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -2125,7 +2096,7 @@ df.head()`; suite('Auto Save', () => { let windowStateChangeHandlers: ((e: WindowState) => any)[] = []; - setup(async function() { + setup(async function () { if (useCustomEditorApi) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -2135,8 +2106,8 @@ df.head()`; windowStateChangeHandlers = []; // Keep track of all handlers for the onDidChangeWindowState event. ioc.applicationShell - .setup(app => app.onDidChangeWindowState(TypeMoq.It.isAny())) - .callback(cb => windowStateChangeHandlers.push(cb)); + .setup((app) => app.onDidChangeWindowState(TypeMoq.It.isAny())) + .callback((cb) => windowStateChangeHandlers.push(cb)); // tslint:disable-next-line: no-invalid-this await setupFunction.call(this); @@ -2244,8 +2215,8 @@ df.head()`; const docManager = ioc.get(IDocumentManager) as MockDocumentManager; docManager.didChangeActiveTextEditorEmitter.fire(); // Also, send notification about changes to window state. - windowStateChangeHandlers.forEach(item => item({ focused: false })); - windowStateChangeHandlers.forEach(item => item({ focused: true })); + windowStateChangeHandlers.forEach((item) => item({ focused: false })); + windowStateChangeHandlers.forEach((item) => item({ focused: true })); // Confirm the message is not clean, trying to wait for it to get saved will timeout (i.e. rejected). await expect(cleanPromise).to.eventually.be.rejected; @@ -2321,7 +2292,7 @@ df.head()`; ioc.forceSettingsChanged(undefined, ioc.getSettings().pythonPath); // Now that the notebook is dirty, send notification about changes to window state. - windowStateChangeHandlers.forEach(item => item({ focused })); + windowStateChangeHandlers.forEach((item) => item({ focused })); // At this point a message should be sent to extension asking it to save. // After the save, the extension should send a message to react letting it know that it was saved successfully. @@ -2352,8 +2323,8 @@ df.head()`; // Now that the notebook is dirty, change window state. // This should not trigger a save of notebook (as its configured to save only when focus is changed). - windowStateChangeHandlers.forEach(item => item({ focused: false })); - windowStateChangeHandlers.forEach(item => item({ focused: true })); + windowStateChangeHandlers.forEach((item) => item({ focused: false })); + windowStateChangeHandlers.forEach((item) => item({ focused: true })); // Confirm the message is not clean, trying to wait for it to get saved will timeout (i.e. rejected). await expect(cleanPromise).to.eventually.be.rejected; @@ -2431,7 +2402,7 @@ df.head()`; }; suite('Update Metadata', () => { - setup(async function() { + setup(async function () { await initIoc(); // tslint:disable-next-line: no-invalid-this await setupFunction.call(this, JSON.stringify(oldJson)); @@ -2477,20 +2448,14 @@ df.head()`; }); suite('Clear Outputs', () => { - setup(async function() { + setup(async function () { await initIoc(); // tslint:disable-next-line: no-invalid-this await setupFunction.call(this, JSON.stringify(oldJson)); }); function verifyExecutionCount(cellIndex: number, executionCountContent: string) { - assert.equal( - wrapper - .find(ExecutionCount) - .at(cellIndex) - .props().count, - executionCountContent - ); + assert.equal(wrapper.find(ExecutionCount).at(cellIndex).props().count, executionCountContent); } test('Clear Outputs in WebView', async () => { diff --git a/src/test/datascience/nativeEditor.toolbar.functional.test.tsx b/src/test/datascience/nativeEditor.toolbar.functional.test.tsx index bdd1d34f9b64..d76c85566575 100644 --- a/src/test/datascience/nativeEditor.toolbar.functional.test.tsx +++ b/src/test/datascience/nativeEditor.toolbar.functional.test.tsx @@ -167,7 +167,7 @@ suite('DataScience Native Toolbar', () => { }); }); suite('Restart & Interrupt Kernel', () => { - getNamesAndValues(ServerStatus).forEach(status => { + getNamesAndValues(ServerStatus).forEach((status) => { // Should always be disabled if busy. test(`If Kernel status is ${status.name} and busy, both are disabled`, () => { props.kernel.jupyterServerStatus = status.name as any; diff --git a/src/test/datascience/nativeEditorTestHelpers.tsx b/src/test/datascience/nativeEditorTestHelpers.tsx index 199b57195c77..31051658c3c0 100644 --- a/src/test/datascience/nativeEditorTestHelpers.tsx +++ b/src/test/datascience/nativeEditorTestHelpers.tsx @@ -70,7 +70,7 @@ export function runMountedTest( testFunc: (wrapper: ReactWrapper, React.Component>, context: Mocha.Context) => Promise, getIOC: () => DataScienceIocContainer ) { - test(name, async function() { + test(name, async function () { const ioc = getIOC(); const wrapper = await setupWebview(ioc); if (wrapper) { diff --git a/src/test/datascience/notebook.functional.test.ts b/src/test/datascience/notebook.functional.test.ts index bbab43aba693..2fb75837a6a6 100644 --- a/src/test/datascience/notebook.functional.test.ts +++ b/src/test/datascience/notebook.functional.test.ts @@ -1,1528 +1,1528 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { nbformat } from '@jupyterlab/coreutils'; -import { assert } from 'chai'; -import { ChildProcess } from 'child_process'; -import * as fs from 'fs-extra'; -import { injectable } from 'inversify'; -import * as os from 'os'; -import * as path from 'path'; -import { SemVer } from 'semver'; -import { Readable, Writable } from 'stream'; -import { anything, instance, mock, when } from 'ts-mockito'; -import * as TypeMoq from 'typemoq'; -import * as uuid from 'uuid/v4'; -import { Disposable, Uri } from 'vscode'; -import { CancellationToken, CancellationTokenSource } from 'vscode-jsonrpc'; -import { ApplicationShell } from '../../client/common/application/applicationShell'; -import { IApplicationShell } from '../../client/common/application/types'; -import { Cancellation, CancellationError } from '../../client/common/cancellation'; -import { EXTENSION_ROOT_DIR } from '../../client/common/constants'; -import { traceError, traceInfo } from '../../client/common/logger'; -import { IFileSystem } from '../../client/common/platform/types'; -import { IPythonExecutionFactory, IPythonExecutionService, Output } from '../../client/common/process/types'; -import { Product } from '../../client/common/types'; -import { createDeferred, waitForPromise } from '../../client/common/utils/async'; -import { noop } from '../../client/common/utils/misc'; -import { Architecture } from '../../client/common/utils/platform'; -import { Identifiers } from '../../client/datascience/constants'; -import { ModuleExistsStatus } from '../../client/datascience/jupyter/interpreter/jupyterCommandFinder'; -import { getMessageForLibrariesNotInstalled } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService'; -import { JupyterExecutionFactory } from '../../client/datascience/jupyter/jupyterExecutionFactory'; -import { JupyterKernelPromiseFailedError } from '../../client/datascience/jupyter/kernels/jupyterKernelPromiseFailedError'; -import { HostJupyterNotebook } from '../../client/datascience/jupyter/liveshare/hostJupyterNotebook'; -import { - CellState, - ICell, - IConnection, - IJupyterExecution, - IJupyterKernelSpec, - INotebook, - INotebookExecutionLogger, - INotebookExporter, - INotebookImporter, - InterruptResult -} from '../../client/datascience/types'; -import { - IInterpreterService, - IKnownSearchPathsForInterpreters, - InterpreterType, - PythonInterpreter -} from '../../client/interpreter/contracts'; -import { concatMultilineStringInput } from '../../datascience-ui/common'; -import { generateTestState, ICellViewModel } from '../../datascience-ui/interactive-common/mainState'; -import { sleep } from '../core'; -import { DataScienceIocContainer } from './dataScienceIocContainer'; -import { getConnectionInfo, getIPConnectionInfo } from './jupyterHelpers'; -import { SupportedCommands } from './mockJupyterManager'; -import { MockPythonService } from './mockPythonService'; - -// tslint:disable:no-any no-multiline-string max-func-body-length no-console max-classes-per-file trailing-comma -suite('DataScience notebook tests', () => { - const disposables: Disposable[] = []; - let jupyterExecution: IJupyterExecution; - let pythonFactory: IPythonExecutionFactory; - let ioc: DataScienceIocContainer; - let modifiedConfig = false; - const baseUri = Uri.file('foo.py'); - - setup(async () => { - ioc = new DataScienceIocContainer(); - ioc.registerDataScienceTypes(); - await ioc.activate(); - }); - - teardown(async () => { - try { - if (modifiedConfig) { - traceInfo('Attempting to put jupyter default config back'); - const procService = await createPythonService(); - if (procService) { - await procService.exec(['-m', 'jupyter', 'notebook', '--generate-config', '-y'], {}); - } - } - traceInfo('Shutting down after test.'); - // tslint:disable-next-line:prefer-for-of - for (let i = 0; i < disposables.length; i += 1) { - const disposable = disposables[i]; - if (disposable) { - const promise = disposable.dispose() as Promise; - if (promise) { - await promise; - } - } - } - await ioc.dispose(); - traceInfo('Shutdown after test complete.'); - } catch (e) { - traceError(e); - } - if (process.env.PYTHONWARNINGS) { - delete process.env.PYTHONWARNINGS; - } - }); - - function escapePath(p: string) { - return p.replace(/\\/g, '\\\\'); - } - - function srcDirectory() { - return path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience'); - } - - function extractDataOutput(cell: ICell): any { - assert.equal(cell.data.cell_type, 'code', `Wrong type of cell returned`); - const codeCell = cell.data as nbformat.ICodeCell; - if (codeCell.outputs.length > 0) { - assert.equal(codeCell.outputs.length, 1, 'Cell length not correct'); - const data = codeCell.outputs[0].data; - const error = codeCell.outputs[0].evalue; - if (error) { - assert.fail(`Unexpected error: ${error}`); - } - assert.ok(data, `No data object on the cell`); - if (data) { - // For linter - assert.ok(data.hasOwnProperty('text/plain'), `Cell mime type not correct`); - assert.ok((data as any)['text/plain'], `Cell mime type not correct`); - return (data as any)['text/plain']; - } - } - } - - async function verifySimple( - notebook: INotebook | undefined, - code: string, - expectedValue: any, - pathVerify = false - ): Promise { - const cells = await notebook!.execute(code, path.join(srcDirectory(), 'foo.py'), 2, uuid()); - assert.equal(cells.length, 1, `Wrong number of cells returned`); - const data = extractDataOutput(cells[0]); - if (pathVerify) { - // For a path comparison normalize output and add single quotes on expected value - const normalizedOutput = path.normalize(data).toUpperCase(); - const normalizedTarget = `'${path.normalize(expectedValue).toUpperCase()}'`; - assert.equal(normalizedOutput, normalizedTarget, 'Cell path values does not match'); - } else { - assert.equal(data, expectedValue, 'Cell value does not match'); - } - } - - async function verifyError(notebook: INotebook | undefined, code: string, errorString: string): Promise { - const cells = await notebook!.execute(code, path.join(srcDirectory(), 'foo.py'), 2, uuid()); - assert.equal(cells.length, 1, `Wrong number of cells returned`); - assert.equal(cells[0].data.cell_type, 'code', `Wrong type of cell returned`); - const cell = cells[0].data as nbformat.ICodeCell; - assert.equal(cell.outputs.length, 1, `Cell length not correct`); - const error = cell.outputs[0].evalue; - if (error) { - assert.ok(error, 'Error not found when expected'); - assert.equal(error, errorString, 'Unexpected error found'); - } - } - - async function verifyCell( - notebook: INotebook | undefined, - index: number, - code: string, - mimeType: string, - cellType: string, - verifyValue: (data: any) => void - ): Promise { - // Verify results of an execute - const cells = await notebook!.execute(code, path.join(srcDirectory(), 'foo.py'), 2, uuid()); - assert.equal(cells.length, 1, `${index}: Wrong number of cells returned`); - if (cellType === 'code') { - assert.equal(cells[0].data.cell_type, cellType, `${index}: Wrong type of cell returned`); - const cell = cells[0].data as nbformat.ICodeCell; - assert.ok(cell.outputs.length >= 1, `${index}: Cell length not correct`); - const error = cell.outputs[0].evalue; - if (error) { - assert.ok(false, `${index}: Unexpected error: ${error}`); - } - const data = cell.outputs[0].data; - const text = cell.outputs[0].text; - assert.ok(data || text, `${index}: No data object on the cell for ${code}`); - if (data) { - // For linter - assert.ok( - data.hasOwnProperty(mimeType) || data.hasOwnProperty('text/plain'), - `${index}: Cell mime type not correct for ${JSON.stringify(data)}` - ); - const actualMimeType = data.hasOwnProperty(mimeType) ? mimeType : 'text/plain'; - assert.ok((data as any)[actualMimeType], `${index}: Cell mime type not correct`); - verifyValue((data as any)[actualMimeType]); - } - if (text) { - verifyValue(text); - } - } else if (cellType === 'markdown') { - assert.equal(cells[0].data.cell_type, cellType, `${index}: Wrong type of cell returned`); - const cell = cells[0].data as nbformat.IMarkdownCell; - const outputSource = concatMultilineStringInput(cell.source); - verifyValue(outputSource); - } else if (cellType === 'error') { - const cell = cells[0].data as nbformat.ICodeCell; - assert.equal(cell.outputs.length, 1, `${index}: Cell length not correct`); - const error = cell.outputs[0].evalue; - assert.ok(error, 'Error not found when expected'); - verifyValue(error); - } - } - - function testMimeTypes( - types: { - markdownRegEx: string | undefined; - code: string; - mimeType: string; - result: any; - cellType: string; - verifyValue(data: any): void; - }[] - ) { - runTest('MimeTypes', async () => { - // Prefill with the output (This is only necessary for mocking) - types.forEach(t => { - addMockData(t.code, t.result, t.mimeType, t.cellType); - }); - - // Test all mime types together so we don't have to startup and shutdown between - // each - const server = await createNotebook( - true, - false, - false, - 'history', - undefined, - path.join(srcDirectory(), 'foo.py') - ); - if (server) { - for (let i = 0; i < types.length; i += 1) { - const markdownRegex = types[i].markdownRegEx ? types[i].markdownRegEx : ''; - ioc.getSettings().datascience.markdownRegularExpression = markdownRegex!; - await verifyCell( - server, - i, - types[i].code, - types[i].mimeType, - types[i].cellType, - types[i].verifyValue - ); - } - } - }); - } - - function runTest( - name: string, - func: (_this: Mocha.Context) => Promise, - _notebookProc?: ChildProcess, - rebindFunc?: () => void - ) { - test(name, async function() { - // Give tests a chance to rebind IOC services before we fetch jupyterExecution and processFactory - if (rebindFunc) { - rebindFunc(); - } - jupyterExecution = ioc.serviceManager.get(IJupyterExecution); - pythonFactory = ioc.serviceManager.get(IPythonExecutionFactory); - console.log(`Starting test ${name} ...`); - if (await jupyterExecution.isNotebookSupported()) { - // tslint:disable-next-line: no-invalid-this - return func(this); - } else { - // tslint:disable-next-line:no-console - console.log(`Skipping test ${name}, no jupyter installed.`); - } - }); - } - - async function createNotebook( - useDefaultConfig: boolean, - expectFailure?: boolean, - usingDarkTheme?: boolean, - purpose?: string, - workingDir?: string, - launchingFile?: string - ): Promise { - // Catch exceptions. Throw a specific assertion if the promise fails - try { - const server = await jupyterExecution.connectToNotebookServer({ - usingDarkTheme, - skipUsingDefaultConfig: !useDefaultConfig, - workingDir: workingDir ? workingDir : ioc.getSettings().datascience.notebookFileRoot, - purpose: purpose ? purpose : '1', - allowUI: () => false - }); - if (expectFailure) { - assert.ok(false, `Expected server to not be created`); - } - if (server) { - const notebook = await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)); - // If specified set our launch file - if (launchingFile) { - await notebook.setLaunchingFile(launchingFile); - } - return notebook; - } - } catch (exc) { - if (!expectFailure) { - assert.ok(false, `Expected server to be created, but got ${exc}`); - } - } - } - - function addMockData(code: string, result: string | number, mimeType?: string, cellType?: string) { - if (ioc.mockJupyter) { - if (cellType && cellType === 'error') { - ioc.mockJupyter.addError(code, result.toString()); - } else { - ioc.mockJupyter.addCell(code, result, mimeType); - } - } - } - - function changeMockWorkingDirectory(workingDir: string) { - if (ioc.mockJupyter) { - ioc.mockJupyter.changeWorkingDirectory(workingDir); - } - } - - function addInterruptableMockData( - code: string, - resultGenerator: (c: CancellationToken) => Promise<{ result: string; haveMore: boolean }> - ) { - if (ioc.mockJupyter) { - ioc.mockJupyter.addContinuousOutputCell(code, resultGenerator); - } - } - - async function createPythonService(versionRequirement?: number): Promise { - if (!ioc.mockJupyter) { - const python = await ioc.getJupyterCapableInterpreter(); - - if ( - python && - python.version?.major && - (!versionRequirement || python.version?.major > versionRequirement) - ) { - return pythonFactory.createActivatedEnvironment({ - resource: undefined, - interpreter: python, - allowEnvironmentFetchExceptions: true, - bypassCondaExecution: true - }); - } - } - } - - runTest('Remote Self Certs', async (_this: Mocha.Context) => { - // Skip this. Entered a bug here to fix: https://github.com/microsoft/vscode-python/issues/10622 - _this.skip(); - - /* - const pythonService = await createPythonService(2); - - if (pythonService) { - // We will only connect if we allow for self signed cert connections - ioc.getSettings().datascience.allowUnauthorizedRemoteConnection = true; - - const connectionFound = createDeferred(); - const configFile = path.join( - EXTENSION_ROOT_DIR, - 'src', - 'test', - 'datascience', - 'serverConfigFiles', - 'selfCert.py' - ); - const pemFile = path.join( - EXTENSION_ROOT_DIR, - 'src', - 'test', - 'datascience', - 'serverConfigFiles', - 'jcert.pem' - ); - const keyFile = path.join( - EXTENSION_ROOT_DIR, - 'src', - 'test', - 'datascience', - 'serverConfigFiles', - 'jkey.key' - ); - - const exeResult = pythonService.execObservable( - [ - '-m', - 'jupyter', - 'notebook', - `--config=${configFile}`, - `--certfile=${pemFile}`, - `--keyfile=${keyFile}` - ], - { - throwOnStdErr: false - } - ); - disposables.push(exeResult); - - exeResult.out.subscribe((output: Output) => { - const connectionURL = getIPConnectionInfo(output.out); - if (connectionURL) { - connectionFound.resolve(connectionURL); - } - }); - - const connString = await connectionFound.promise; - const uri = connString as string; - - // We have a connection string here, so try to connect jupyterExecution to the notebook server - const server = await jupyterExecution.connectToNotebookServer({ - uri, - purpose: '', - allowUI: () => false - }); - const notebook = server - ? await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)) - : undefined; - if (!notebook) { - assert.fail('Failed to connect to remote self cert server'); - } else { - await verifySimple(notebook, `a=1${os.EOL}a`, 1); - } - // Have to dispose here otherwise the process may exit before hand and mess up cleanup. - await server!.dispose(); - } else { - traceInfo('Remote Self Cert is not supported on 2.7'); - _this.skip(); - } - */ - }); - - // Connect to a server that doesn't have a token or password, customers use this and we regressed it once - runTest( - 'Remote No Auth', - async () => { - const pythonService = await createPythonService(); - - if (pythonService) { - const connectionFound = createDeferred(); - const configFile = path.join( - EXTENSION_ROOT_DIR, - 'src', - 'test', - 'datascience', - 'serverConfigFiles', - 'remoteNoAuth.py' - ); - const exeResult = pythonService.execObservable( - ['-m', 'jupyter', 'notebook', `--config=${configFile}`], - { throwOnStdErr: false } - ); - disposables.push(exeResult); - - exeResult.out.subscribe((output: Output) => { - traceInfo(`remote jupyter output: ${output.out}`); - const connectionURL = getIPConnectionInfo(output.out); - if (connectionURL) { - connectionFound.resolve(connectionURL); - } - }); - - const connString = await connectionFound.promise; - const uri = connString as string; - - // We have a connection string here, so try to connect jupyterExecution to the notebook server - const server = await jupyterExecution.connectToNotebookServer({ - uri, - purpose: '', - allowUI: () => false - }); - const notebook = server - ? await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)) - : undefined; - if (!notebook) { - assert.fail('Failed to connect to remote password server'); - } else { - await verifySimple(notebook, `a=1${os.EOL}a`, 1); - } - // Have to dispose here otherwise the process may exit before hand and mess up cleanup. - await server!.dispose(); - } - }, - undefined, - () => { - const dummyDisposable = { - dispose: () => { - return; - } - }; - const appShell = TypeMoq.Mock.ofType(); - appShell - .setup(a => a.showErrorMessage(TypeMoq.It.isAnyString())) - .returns(e => { - throw e; - }); - appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve('')); - appShell - .setup(a => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns((_a1: string, a2: string, _a3: string) => Promise.resolve(a2)); - appShell - .setup(a => - a.showInformationMessage( - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny() - ) - ) - .returns((_a1: string, a2: string, _a3: string, _a4: string) => Promise.resolve(a2)); - appShell.setup(a => a.showInputBox(TypeMoq.It.isAny())).returns(() => Promise.resolve('')); - appShell.setup(a => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); - ioc.serviceManager.rebindInstance(IApplicationShell, appShell.object); - } - ); - - runTest('Remote Password', async () => { - const pythonService = await createPythonService(); - - if (pythonService) { - const connectionFound = createDeferred(); - const configFile = path.join( - EXTENSION_ROOT_DIR, - 'src', - 'test', - 'datascience', - 'serverConfigFiles', - 'remotePassword.py' - ); - const exeResult = pythonService.execObservable(['-m', 'jupyter', 'notebook', `--config=${configFile}`], { - throwOnStdErr: false - }); - disposables.push(exeResult); - - exeResult.out.subscribe((output: Output) => { - traceInfo(`remote jupyter output: ${output.out}`); - const connectionURL = getIPConnectionInfo(output.out); - if (connectionURL) { - connectionFound.resolve(connectionURL); - } - }); - - const connString = await connectionFound.promise; - const uri = connString as string; - - // We have a connection string here, so try to connect jupyterExecution to the notebook server - const server = await jupyterExecution.connectToNotebookServer({ - uri, - purpose: '', - allowUI: () => false - }); - const notebook = server - ? await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)) - : undefined; - if (!notebook) { - assert.fail('Failed to connect to remote password server'); - } else { - await verifySimple(notebook, `a=1${os.EOL}a`, 1); - } - // Have to dispose here otherwise the process may exit before hand and mess up cleanup. - await server!.dispose(); - } - }); - - runTest('Remote', async () => { - const pythonService = await createPythonService(); - - if (pythonService) { - const connectionFound = createDeferred(); - const configFile = path.join( - EXTENSION_ROOT_DIR, - 'src', - 'test', - 'datascience', - 'serverConfigFiles', - 'remoteToken.py' - ); - const exeResult = pythonService.execObservable(['-m', 'jupyter', 'notebook', `--config=${configFile}`], { - throwOnStdErr: false - }); - disposables.push(exeResult); - - exeResult.out.subscribe((output: Output) => { - traceInfo(`remote jupyter output: ${output.out}`); - const connectionURL = getConnectionInfo(output.out); - if (connectionURL) { - connectionFound.resolve(connectionURL); - } - }); - - const connString = await connectionFound.promise; - const uri = connString as string; - - // We have a connection string here, so try to connect jupyterExecution to the notebook server - const server = await jupyterExecution.connectToNotebookServer({ - uri, - purpose: '', - allowUI: () => false - }); - const notebook = server - ? await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)) - : undefined; - if (!notebook) { - assert.fail('Failed to connect to remote server'); - } else { - await verifySimple(notebook, `a=1${os.EOL}a`, 1); - } - - // Have to dispose here otherwise the process may exit before hand and mess up cleanup. - await server!.dispose(); - } - }); - - runTest('Creation', async () => { - await createNotebook(true); - }); - - runTest('Failure', async () => { - // Make a dummy class that will fail during launch - class FailedProcess extends JupyterExecutionFactory { - public isNotebookSupported = (): Promise => { - return Promise.resolve(false); - }; - } - ioc.serviceManager.rebind(IJupyterExecution, FailedProcess); - jupyterExecution = ioc.serviceManager.get(IJupyterExecution); - await createNotebook(true, true); - }); - - test('Not installed', async () => { - jupyterExecution = ioc.serviceManager.get(IJupyterExecution); - // Rewire our data we use to search for processes - @injectable() - class EmptyInterpreterService implements IInterpreterService { - public get hasInterpreters(): Promise { - return Promise.resolve(true); - } - public onDidChangeInterpreter( - _listener: (e: void) => any, - _thisArgs?: any, - _disposables?: Disposable[] - ): Disposable { - return { dispose: noop }; - } - public onDidChangeInterpreterInformation( - _listener: (e: PythonInterpreter) => any, - _thisArgs?: any, - _disposables?: Disposable[] - ): Disposable { - return { dispose: noop }; - } - public getInterpreters(_resource?: Uri): Promise { - return Promise.resolve([]); - } - public autoSetInterpreter(): Promise { - throw new Error('Method not implemented'); - } - public getActiveInterpreter(_resource?: Uri): Promise { - return Promise.resolve(undefined); - } - public getInterpreterDetails(_pythonPath: string, _resoure?: Uri): Promise { - throw new Error('Method not implemented'); - } - public refresh(_resource: Uri): Promise { - throw new Error('Method not implemented'); - } - public initialize(): void { - throw new Error('Method not implemented'); - } - public getDisplayName(_interpreter: Partial): Promise { - throw new Error('Method not implemented'); - } - public shouldAutoSetInterpreter(): Promise { - throw new Error('Method not implemented'); - } - } - @injectable() - class EmptyPathService implements IKnownSearchPathsForInterpreters { - public getSearchPaths(): string[] { - return []; - } - } - ioc.serviceManager.rebind(IInterpreterService, EmptyInterpreterService); - ioc.serviceManager.rebind(IKnownSearchPathsForInterpreters, EmptyPathService); - jupyterExecution = ioc.serviceManager.get(IJupyterExecution); - await createNotebook(true, true); - }); - - runTest('Export/Import', async () => { - // Get a bunch of test cells (use our test cells from the react controls) - const testFolderPath = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience'); - const testState = generateTestState(testFolderPath); - const cells = testState.cellVMs.map((cellVM: ICellViewModel, _index: number) => { - return cellVM.cell; - }); - - // Translate this into a notebook - - // Make sure we have a change dir happening - const settings = { ...ioc.getSettings().datascience }; - settings.changeDirOnImportExport = true; - ioc.forceSettingsChanged(undefined, ioc.getSettings().pythonPath, settings); - - const exporter = ioc.serviceManager.get(INotebookExporter); - const newFolderPath = path.join( - EXTENSION_ROOT_DIR, - 'src', - 'test', - 'datascience', - 'WorkspaceDir', - 'WorkspaceSubDir', - 'foo.ipynb' - ); - const notebook = await exporter.translateToNotebook(cells, newFolderPath); - assert.ok(notebook, 'Translate to notebook is failing'); - - // Make sure we added in our chdir - if (notebook) { - const nbcells = notebook.cells; - if (nbcells) { - const firstCellText: string = nbcells[0].source as string; - assert.ok(firstCellText.includes('os.chdir'), `${firstCellText} does not include 'os.chdir`); - } - } - - // Save to a temp file - const fileSystem = ioc.serviceManager.get(IFileSystem); - const importer = ioc.serviceManager.get(INotebookImporter); - const temp = await fileSystem.createTemporaryFile('.ipynb'); - - try { - await fs.writeFile(temp.filePath, JSON.stringify(notebook), 'utf8'); - // Try importing this. This should verify export works and that importing is possible - const results = await importer.importFromFile(temp.filePath); - - // Make sure we have a single chdir in our results - const first = results.indexOf('os.chdir'); - assert.ok(first >= 0, 'No os.chdir in import'); - const second = results.indexOf('os.chdir', first + 1); - assert.equal(second, -1, 'More than one chdir in the import. It should be skipped'); - - // Make sure we have a cell in our results - assert.ok(/#\s*%%/.test(results), 'No cells in returned import'); - } finally { - importer.dispose(); - temp.dispose(); - } - }); - - runTest('Verify manual working directory', async () => { - // Instead of default, manually set a working directory - const notebook = await createNotebook(true, undefined, undefined, undefined, EXTENSION_ROOT_DIR); - await verifySimple(notebook, 'import os\nos.getcwd()', EXTENSION_ROOT_DIR, true); - await verifySimple(notebook, 'import sys\nsys.path[0]', EXTENSION_ROOT_DIR, true); - }); - - // tslint:disable-next-line:no-invalid-template-strings - runTest('Verify ${fileDirname} working directory', async () => { - // Verify that the default ${fileDirname} setting sets the working directory to the file path - changeMockWorkingDirectory(`'${srcDirectory()}'`); - const notebook = await createNotebook( - true, - undefined, - undefined, - undefined, - undefined, - path.join(srcDirectory(), 'foo.py') - ); - await verifySimple(notebook, 'import os\nos.getcwd()', srcDirectory(), true); - await verifySimple(notebook, 'import sys\nsys.path[0]', srcDirectory(), true); - }); - - runTest('Change Interpreter', async () => { - const isRollingBuild = process.env ? process.env.VSCODE_PYTHON_ROLLING !== undefined : false; - - // Real Jupyter doesn't help this test at all and is tricky to set up for it, so just skip it - if (!isRollingBuild) { - const server = await createNotebook(true); - - // Create again, we should get the same server from the cache - const server2 = await createNotebook(true); - // tslint:disable-next-line: triple-equals - assert.ok(server == server2, 'With no settings changed we should return the cached server'); - - // Create a new mock interpreter with a different path - const newPython: PythonInterpreter = { - path: '/foo/bar/baz/python.exe', - version: new SemVer('3.6.6-final'), - sysVersion: '1.0.0.0', - sysPrefix: 'Python', - type: InterpreterType.Unknown, - architecture: Architecture.x64 - }; - - // Add interpreter into mock jupyter service and set it as active - ioc.addInterpreter(newPython, SupportedCommands.all); - - // Create a new notebook, we should still be the same as interpreter is just saved for notebook creation - const server3 = await createNotebook(true); - // tslint:disable-next-line: triple-equals - assert.ok(server == server3, 'With interpreter changed we should not return a new server'); - } else { - console.log(`Skipping Change Interpreter test in non-mocked Jupyter case`); - } - }); - - runTest('Restart kernel', async () => { - addMockData(`a=1${os.EOL}a`, 1); - addMockData(`a+=1${os.EOL}a`, 2); - addMockData(`a+=4${os.EOL}a`, 6); - addMockData('a', `name 'a' is not defined`, 'error'); - - const server = await createNotebook(true); - - // Setup some state and verify output is correct - await verifySimple(server, `a=1${os.EOL}a`, 1); - await verifySimple(server, `a+=1${os.EOL}a`, 2); - await verifySimple(server, `a+=4${os.EOL}a`, 6); - - console.log('Waiting for idle'); - - // In unit tests we have to wait for status idle before restarting. Unit tests - // seem to be timing out if the restart throws any exceptions (even if they're caught) - await server!.waitForIdle(10000); - - console.log('Restarting kernel'); - try { - await server!.restartKernel(10000); - - console.log('Waiting for idle'); - await server!.waitForIdle(10000); - - console.log('Verifying restart'); - await verifyError(server, 'a', `name 'a' is not defined`); - } catch (exc) { - assert.ok( - exc instanceof JupyterKernelPromiseFailedError, - `Restarting did not timeout correctly for ${exc}` - ); - } - }); - - class TaggedCancellationTokenSource extends CancellationTokenSource { - public tag: string; - constructor(tag: string) { - super(); - this.tag = tag; - } - } - - async function testCancelableCall( - method: (t: CancellationToken) => Promise, - messageFormat: string, - timeout: number - ): Promise { - const tokenSource = new TaggedCancellationTokenSource(messageFormat.format(timeout.toString())); - const disp = setTimeout( - _s => { - tokenSource.cancel(); - }, - timeout, - tokenSource.tag - ); - - try { - // tslint:disable-next-line:no-string-literal - (tokenSource.token as any)['tag'] = messageFormat.format(timeout.toString()); - await method(tokenSource.token); - } catch (exc) { - // This should happen. This means it was canceled. - assert.ok(exc instanceof CancellationError, `Non cancellation error found : ${exc.stack}`); - } finally { - clearTimeout(disp); - tokenSource.dispose(); - } - - return true; - } - - async function testCancelableMethod( - method: (t: CancellationToken) => Promise, - messageFormat: string, - short?: boolean - ): Promise { - const timeouts = short ? [10, 20, 30, 100] : [300, 400, 500, 1000]; - // tslint:disable-next-line:prefer-for-of - for (let i = 0; i < timeouts.length; i += 1) { - await testCancelableCall(method, messageFormat, timeouts[i]); - } - - return true; - } - - runTest('Cancel execution', async () => { - if (ioc.mockJupyter) { - ioc.mockJupyter.setProcessDelay(2000); - addMockData(`a=1${os.EOL}a`, 1); - } - - // Try different timeouts, canceling after the timeout on each - assert.ok( - await testCancelableMethod( - (t: CancellationToken) => jupyterExecution.connectToNotebookServer(undefined, t), - 'Cancel did not cancel start after {0}ms' - ) - ); - - if (ioc.mockJupyter) { - ioc.mockJupyter.setProcessDelay(undefined); - } - - // Make sure doing normal start still works - const nonCancelSource = new CancellationTokenSource(); - const server = await jupyterExecution.connectToNotebookServer(undefined, nonCancelSource.token); - const notebook = server - ? await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)) - : undefined; - assert.ok(notebook, 'Server not found with a cancel token that does not cancel'); - - // Make sure can run some code too - await verifySimple(notebook, `a=1${os.EOL}a`, 1); - - if (ioc.mockJupyter) { - ioc.mockJupyter.setProcessDelay(200); - } - - // Force a settings changed so that all of the cached data is cleared - ioc.forceSettingsChanged(undefined, '/usr/bin/test3/python'); - - assert.ok( - await testCancelableMethod( - (t: CancellationToken) => jupyterExecution.getUsableJupyterPython(t), - 'Cancel did not cancel getusable after {0}ms', - true - ) - ); - assert.ok( - await testCancelableMethod( - (t: CancellationToken) => jupyterExecution.isNotebookSupported(t), - 'Cancel did not cancel isNotebook after {0}ms', - true - ) - ); - assert.ok( - await testCancelableMethod( - (t: CancellationToken) => jupyterExecution.isImportSupported(t), - 'Cancel did not cancel isImport after {0}ms', - true - ) - ); - }); - - async function interruptExecute( - notebook: INotebook | undefined, - code: string, - interruptMs: number, - sleepMs: number - ): Promise { - let interrupted = false; - let finishedBefore = false; - const finishedPromise = createDeferred(); - let error; - const observable = notebook!.executeObservable(code, Uri.file('foo.py').fsPath, 0, uuid(), false); - observable.subscribe( - c => { - if (c.length > 0 && c[0].state === CellState.error) { - finishedBefore = !interrupted; - finishedPromise.resolve(); - } - if (c.length > 0 && c[0].state === CellState.finished) { - finishedBefore = !interrupted; - finishedPromise.resolve(); - } - }, - err => { - error = err; - finishedPromise.resolve(); - }, - () => finishedPromise.resolve() - ); - - // Then interrupt - interrupted = true; - const result = await notebook!.interruptKernel(interruptMs); - - // Then we should get our finish unless there was a restart - await waitForPromise(finishedPromise.promise, sleepMs); - assert.equal(finishedBefore, false, 'Finished before the interruption'); - assert.equal(error, undefined, 'Error thrown during interrupt'); - assert.ok( - finishedPromise.completed || result === InterruptResult.TimedOut || result === InterruptResult.Success, - `Interrupt restarted ${result} for: ${code}` - ); - - return result; - } - - runTest('Interrupt kernel', async () => { - const returnable = `import signal -import _thread -import time - -keep_going = True -def handler(signum, frame): - global keep_going - print('signal') - keep_going = False - -signal.signal(signal.SIGINT, handler) - -while keep_going: - print(".") - time.sleep(.1)`; - const fourSecondSleep = `import time${os.EOL}time.sleep(4)${os.EOL}print("foo")`; - const kill = `import signal -import time -import os - -keep_going = True -def handler(signum, frame): - global keep_going - print('signal') - os._exit(-2) - -signal.signal(signal.SIGINT, handler) - -while keep_going: - print(".") - time.sleep(.1)`; - - // Add to our mock each of these, with each one doing something specific. - addInterruptableMockData(returnable, async (cancelToken: CancellationToken) => { - // This one goes forever until a cancellation happens - let haveMore = true; - try { - await Cancellation.race(_t => sleep(100), cancelToken); - } catch { - haveMore = false; - } - return { result: '.', haveMore: haveMore }; - }); - addInterruptableMockData(fourSecondSleep, async (_cancelToken: CancellationToken) => { - // This one sleeps for four seconds and then it's done. - await sleep(4000); - return { result: 'foo', haveMore: false }; - }); - addInterruptableMockData(kill, async (cancelToken: CancellationToken) => { - // This one goes forever until a cancellation happens - let haveMore = true; - try { - await Cancellation.race(_t => sleep(100), cancelToken); - } catch { - haveMore = false; - } - return { result: '.', haveMore: haveMore }; - }); - - const server = await createNotebook(true); - - // Give some time for the server to finish. Otherwise our first interrupt will - // happen so fast, we'll interrupt startup. - await sleep(100); - - // Try with something we can interrupt - await interruptExecute(server, returnable, 1000, 1000); - - // Try again with something that doesn't return. However it should finish before - // we get to our own sleep. Note: We need the print so that the test knows something happened. - await interruptExecute(server, fourSecondSleep, 7000, 7000); - - // Try again with something that doesn't return. Make sure it times out - await interruptExecute(server, fourSecondSleep, 100, 7000); - - // The tough one, somethign that causes a kernel reset. - await interruptExecute(server, kill, 1000, 1000); - }); - - testMimeTypes([ - { - markdownRegEx: undefined, - code: `a=1 -a`, - mimeType: 'text/plain', - cellType: 'code', - result: 1, - verifyValue: d => assert.equal(d, 1, 'Plain text invalid') - }, - { - markdownRegEx: undefined, - code: `import pandas as pd -df = pd.read("${escapePath(path.join(srcDirectory(), 'DefaultSalesReport.csv'))}") -df.head()`, - mimeType: 'text/html', - result: `pd has no attribute 'read'`, - cellType: 'error', - // tslint:disable-next-line:quotemark - verifyValue: d => assert.ok((d as string).includes("has no attribute 'read'"), 'Unexpected error result') - }, - { - markdownRegEx: undefined, - code: `import pandas as pd -df = pd.read_csv("${escapePath(path.join(srcDirectory(), 'DefaultSalesReport.csv'))}") -df.head()`, - mimeType: 'text/html', - result: `A table`, - cellType: 'code', - verifyValue: d => assert.ok(d.toString().includes(''), 'Table not found') - }, - { - markdownRegEx: undefined, - code: `#%% [markdown]# -# #HEADER`, - mimeType: 'text/plain', - cellType: 'markdown', - result: '#HEADER', - verifyValue: d => assert.equal(d, ' #HEADER', 'Markdown incorrect') - }, - { - markdownRegEx: '\\s*#\\s*', - code: `# -# #HEADER`, - mimeType: 'text/plain', - cellType: 'markdown', - result: '#HEADER', - verifyValue: d => assert.equal(d, ' #HEADER', 'Markdown incorrect') - }, - { - // Test relative directories too. - markdownRegEx: undefined, - code: `import pandas as pd -df = pd.read_csv("./DefaultSalesReport.csv") -df.head()`, - mimeType: 'text/html', - cellType: 'code', - result: `A table`, - verifyValue: d => assert.ok(d.toString().includes(''), 'Table not found') - }, - { - // Important to test as multiline cell magics only work if they are the first item in the cell - markdownRegEx: undefined, - code: `#%% -%%bash -echo 'hello'`, - mimeType: 'text/plain', - cellType: 'code', - result: 'hello', - verifyValue: _d => noop() // Anything is fine as long as it tries it. - }, - { - // Test shell command should work on PC / Mac / Linux - markdownRegEx: undefined, - code: `!echo world`, - mimeType: 'text/plain', - cellType: 'code', - result: 'world', - verifyValue: d => assert.ok(d.includes('world'), 'Cell command incorrect') - }, - { - // Plotly - markdownRegEx: undefined, - code: `import matplotlib.pyplot as plt -import matplotlib as mpl -import numpy as np -import pandas as pd -x = np.linspace(0, 20, 100) -plt.plot(x, np.sin(x)) -plt.show()`, - result: `00000`, - mimeType: 'image/svg+xml', - cellType: 'code', - verifyValue: _d => { - return; - } - } - ]); - - async function generateNonDefaultConfig() { - const usable = await ioc.getJupyterCapableInterpreter(); - assert.ok(usable, 'Cant find jupyter enabled python'); - - // Manually generate an invalid jupyter config - const procService = await createPythonService(); - assert.ok(procService, 'Can not get a process service'); - const results = await procService!.exec(['-m', 'jupyter', 'notebook', '--generate-config', '-y'], {}); - - // Results should have our path to the config. - const match = /^.*\s+(.*jupyter_notebook_config.py)\s+.*$/m.exec(results.stdout); - assert.ok(match && match !== null && match.length > 0, 'Jupyter is not outputting the path to the config'); - const configPath = match !== null ? match[1] : ''; - const filesystem = ioc.serviceContainer.get(IFileSystem); - await filesystem.writeFile(configPath, 'c.NotebookApp.password_required = True'); // This should make jupyter fail - modifiedConfig = true; - } - - runTest('Non default config fails', async () => { - if (!ioc.mockJupyter) { - await generateNonDefaultConfig(); - try { - await createNotebook(false); - assert.fail('Should not be able to connect to notebook server with bad config'); - } catch { - noop(); - } - } else { - // In the mock case, just make sure not using a config works - await createNotebook(false); - } - }); - - runTest('Non default config does not mess up default config', async () => { - if (!ioc.mockJupyter) { - await generateNonDefaultConfig(); - const server = await createNotebook(true); - assert.ok(server, 'Never connected to a default server with a bad default config'); - - await verifySimple(server, `a=1${os.EOL}a`, 1); - } - }); - - runTest('Custom command line', async () => { - if (!ioc.mockJupyter) { - const tempDir = os.tmpdir(); - const settings = ioc.getSettings(); - settings.datascience.jupyterCommandLineArguments = ['--NotebookApp.port=9975', `--notebook-dir=${tempDir}`]; - ioc.forceSettingsChanged(undefined, settings.pythonPath, settings.datascience); - const notebook = await createNotebook(true); - assert.ok(notebook, 'Server should have started on port 9975'); - const hs = notebook as HostJupyterNotebook; - // Check port number. Should have at least started with the one specified. - assert.ok(hs.server.getConnectionInfo()?.baseUrl.startsWith('http://localhost:99'), 'Port was not used'); - - await verifySimple(hs, `a=1${os.EOL}a`, 1); - } - }); - - runTest('Invalid kernel spec works', async () => { - if (ioc.mockJupyter) { - // Make a dummy class that will fail during launch - class FailedKernelSpec extends JupyterExecutionFactory { - protected async getMatchingKernelSpec( - _connection?: IConnection, - _cancelToken?: CancellationToken - ): Promise { - return Promise.resolve(undefined); - } - } - ioc.serviceManager.rebind(IJupyterExecution, FailedKernelSpec); - jupyterExecution = ioc.serviceManager.get(IJupyterExecution); - addMockData(`a=1${os.EOL}a`, 1); - - const server = await createNotebook(true); - assert.ok(server, 'Empty kernel spec messes up creating a server'); - - await verifySimple(server, `a=1${os.EOL}a`, 1); - } - }); - - runTest('Server cache working', async () => { - console.log('Staring server cache test'); - const s1 = await createNotebook(true, false, false, 'same'); - console.log('Creating s2'); - const s2 = await createNotebook(true, false, false, 'same'); - console.log('Testing s1 and s2, creating s3'); - assert.ok(s1 === s2, 'Two servers not the same when they should be'); - const s3 = await createNotebook(false, false, false, 'same'); - console.log('Testing s1 and s3, creating s4'); - assert.ok(s1 !== s3, 'Different config should create different server'); - const s4 = await createNotebook(true, false, false, 'different'); - console.log('Testing s1 and s4, creating s5'); - assert.ok(s1 !== s4, 'Different purpose should create different server'); - const s5 = await createNotebook(true, false, true, 'different'); - assert.ok(s4 === s5, 'Dark theme should be same server'); - console.log('Disposing of all'); - await s1!.dispose(); - await s3!.dispose(); - await s4!.dispose(); - }); - - class DyingProcess implements ChildProcess { - public stdin: Writable; - public stdout: Readable; - public stderr: Readable; - public stdio: [Writable, Readable, Readable]; - public killed: boolean = false; - public pid: number = 1; - public connected: boolean = true; - constructor(private timeout: number) { - noop(); - this.stderr = this.stdout = new Readable(); - this.stdin = new Writable(); - this.stdio = [this.stdin, this.stdout, this.stderr]; - } - public kill(_signal?: string): void { - throw new Error('Method not implemented.'); - } - public send(_message: any, _sendHandle?: any, _options?: any, _callback?: any): any { - throw new Error('Method not implemented.'); - } - public disconnect(): void { - throw new Error('Method not implemented.'); - } - public unref(): void { - throw new Error('Method not implemented.'); - } - public ref(): void { - throw new Error('Method not implemented.'); - } - public addListener(_event: any, _listener: any): this { - throw new Error('Method not implemented.'); - } - public emit(_event: any, _message?: any, _sendHandle?: any, ..._rest: any[]): any { - throw new Error('Method not implemented.'); - } - public on(event: any, listener: any): this { - if (event === 'exit') { - setTimeout(() => listener(2), this.timeout); - } - return this; - } - public off(_event: string | symbol, _listener: (...args: any[]) => void): this { - throw new Error('Method not implemented.'); - } - public once(_event: any, _listener: any): this { - throw new Error('Method not implemented.'); - } - public prependListener(_event: any, _listener: any): this { - throw new Error('Method not implemented.'); - } - public prependOnceListener(_event: any, _listener: any): this { - throw new Error('Method not implemented.'); - } - public removeListener(_event: string | symbol, _listener: (...args: any[]) => void): this { - return this; - } - public removeAllListeners(_event?: string | symbol): this { - throw new Error('Method not implemented.'); - } - public setMaxListeners(_n: number): this { - throw new Error('Method not implemented.'); - } - public getMaxListeners(): number { - throw new Error('Method not implemented.'); - } - public listeners(_event: string | symbol): Function[] { - throw new Error('Method not implemented.'); - } - public rawListeners(_event: string | symbol): Function[] { - throw new Error('Method not implemented.'); - } - public eventNames(): (string | symbol)[] { - throw new Error('Method not implemented.'); - } - public listenerCount(_type: string | symbol): number { - throw new Error('Method not implemented.'); - } - } - - runTest( - 'Server death', - async () => { - if (ioc.mockJupyter) { - // Only run this test for mocks. We need to mock the server dying. - addMockData(`a=1${os.EOL}a`, 1); - const server = await createNotebook(true); - assert.ok(server, 'Server died before running'); - - // Sleep for 100 ms so it crashes - await sleep(100); - - try { - await verifySimple(server, `a=1${os.EOL}a`, 1); - assert.ok(false, 'Exception should have been thrown'); - } catch { - noop(); - } - } - }, - new DyingProcess(100) - ); - - runTest('Execution logging', async () => { - const cellInputs: string[] = []; - const outputs: string[] = []; - @injectable() - class Logger implements INotebookExecutionLogger { - public async preExecute(cell: ICell, _silent: boolean): Promise { - cellInputs.push(concatMultilineStringInput(cell.data.source)); - } - public async postExecute(cell: ICell, _silent: boolean): Promise { - outputs.push(extractDataOutput(cell)); - } - } - ioc.serviceManager.add(INotebookExecutionLogger, Logger); - addMockData(`a=1${os.EOL}a`, 1); - const server = await createNotebook(true); - assert.ok(server, 'Server not created in logging case'); - await server!.execute(`a=1${os.EOL}a`, path.join(srcDirectory(), 'foo.py'), 2, uuid()); - assert.equal(cellInputs.length, 2, 'Not enough cell inputs'); - assert.ok(outputs.length >= 1, 'Not enough cell outputs'); - assert.equal(cellInputs[1], 'a=1\na', 'Cell inputs not captured'); - assert.equal(outputs[outputs.length - 1], '1', 'Cell outputs not captured'); - }); - - async function disableJupyter(pythonPath: string) { - const factory = ioc.serviceManager.get(IPythonExecutionFactory); - const service = await factory.create({ pythonPath }); - const mockService = service as MockPythonService; - // Used by commands (can be removed when `src/client/datascience/jupyter/interpreter/jupyterCommand.ts` is deleted). - mockService.addExecResult(['-m', 'jupyter', 'notebook', '--version'], () => { - return Promise.resolve({ - stdout: '9.9.9.9', - stderr: 'Not supported' - }); - }); - - // Used by commands (can be removed when `src/client/datascience/jupyter/interpreter/jupyterCommand.ts` is deleted). - mockService.addExecResult(['-m', 'notebook', '--version'], () => { - return Promise.resolve({ - stdout: '', - stderr: 'Not supported' - }); - }); - // For new approach. - when(ioc.mockJupyter?.productInstaller.isInstalled(Product.jupyter)).thenResolve(false as any); - when(ioc.mockJupyter?.productInstaller.isInstalled(Product.notebook)).thenResolve(false as any); - when(ioc.mockJupyter?.productInstaller.isInstalled(Product.jupyter, anything())).thenResolve(false as any); - when(ioc.mockJupyter?.productInstaller.isInstalled(Product.notebook, anything())).thenResolve(false as any); - } - - test('Notebook launch failure', async function() { - if (!ioc.mockJupyter) { - // tslint:disable-next-line: no-invalid-this - this.skip(); - } else { - const application = mock(ApplicationShell); - when(application.withProgress(anything(), anything())).thenResolve({ - status: ModuleExistsStatus.NotFound - } as any); - ioc.serviceManager.rebindInstance(IApplicationShell, instance(application)); - - jupyterExecution = ioc.serviceManager.get(IJupyterExecution); - - // Change notebook command to fail with some goofy output - await disableJupyter(ioc.workingInterpreter.path); - await disableJupyter(ioc.workingInterpreter2.path); - - // Try creating a notebook - let threw = false; - try { - const testDir = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience'); - await jupyterExecution.connectToNotebookServer({ - usingDarkTheme: false, - workingDir: testDir, - purpose: '1', - allowUI: () => false - }); - } catch (e) { - threw = true; - // When using old command finder, the error is `Not Supported` (directly from stdout). - can be deprecated when jupyterCommandFinder.ts is deleted. - // When using new approach, we inform user that some packages are not installed. - const expectedErrorMsg = getMessageForLibrariesNotInstalled( - [Product.jupyter, Product.notebook], - 'Python' - ); - - assert.ok( - e.message.includes('Not supported') || e.message.includes(expectedErrorMsg), - `Wrong error thrown when notebook is created. Error is ${e.message}` - ); - } - - assert.ok(threw, 'No exception thrown during notebook creation'); - } - }); - - test('Notebook launch with PYTHONWARNINGS', async function() { - if (ioc.mockJupyter) { - // tslint:disable-next-line: no-invalid-this - this.skip(); - } else { - // Force python warnings to always - process.env[`PYTHONWARNINGS`] = 'always'; - jupyterExecution = ioc.serviceManager.get(IJupyterExecution); - - // Try creating a notebook - const server = await createNotebook(true); - assert.ok(server, 'Server died before running'); - } - }); - - // tslint:disable-next-line: no-function-expression - runTest('Notebook launch retry', async function(_this: Mocha.Context) { - // Skipping for now. Renable to test idle timeouts - _this.skip(); - ioc.getSettings().datascience.jupyterLaunchRetries = 1; - ioc.getSettings().datascience.jupyterLaunchTimeout = 10000; - // ioc.getSettings().datascience.runStartupCommands = '%config Application.log_level="DEBUG"'; - // const log = `import logging - // logger = logging.getLogger() - // fhandler = logging.FileHandler(filename='D:\\Training\\mylog.log', mode='a') - // formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - // fhandler.setFormatter(formatter) - // logger.addHandler(fhandler) - // logger.setLevel(logging.DEBUG)`; - for (let i = 0; i < 100; i += 1) { - const notebook = await createNotebook(true, false); - assert.ok(notebook, 'did not create notebook'); - await notebook!.dispose(); - const exec = ioc.get(IJupyterExecution); - await exec.dispose(); - } - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { nbformat } from '@jupyterlab/coreutils'; +import { assert } from 'chai'; +import { ChildProcess } from 'child_process'; +import * as fs from 'fs-extra'; +import { injectable } from 'inversify'; +import * as os from 'os'; +import * as path from 'path'; +import { SemVer } from 'semver'; +import { Readable, Writable } from 'stream'; +import { anything, instance, mock, when } from 'ts-mockito'; +import * as TypeMoq from 'typemoq'; +import * as uuid from 'uuid/v4'; +import { Disposable, Uri } from 'vscode'; +import { CancellationToken, CancellationTokenSource } from 'vscode-jsonrpc'; +import { ApplicationShell } from '../../client/common/application/applicationShell'; +import { IApplicationShell } from '../../client/common/application/types'; +import { Cancellation, CancellationError } from '../../client/common/cancellation'; +import { EXTENSION_ROOT_DIR } from '../../client/common/constants'; +import { traceError, traceInfo } from '../../client/common/logger'; +import { IFileSystem } from '../../client/common/platform/types'; +import { IPythonExecutionFactory, IPythonExecutionService, Output } from '../../client/common/process/types'; +import { Product } from '../../client/common/types'; +import { createDeferred, waitForPromise } from '../../client/common/utils/async'; +import { noop } from '../../client/common/utils/misc'; +import { Architecture } from '../../client/common/utils/platform'; +import { Identifiers } from '../../client/datascience/constants'; +import { ModuleExistsStatus } from '../../client/datascience/jupyter/interpreter/jupyterCommandFinder'; +import { getMessageForLibrariesNotInstalled } from '../../client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService'; +import { JupyterExecutionFactory } from '../../client/datascience/jupyter/jupyterExecutionFactory'; +import { JupyterKernelPromiseFailedError } from '../../client/datascience/jupyter/kernels/jupyterKernelPromiseFailedError'; +import { HostJupyterNotebook } from '../../client/datascience/jupyter/liveshare/hostJupyterNotebook'; +import { + CellState, + ICell, + IConnection, + IJupyterExecution, + IJupyterKernelSpec, + INotebook, + INotebookExecutionLogger, + INotebookExporter, + INotebookImporter, + InterruptResult +} from '../../client/datascience/types'; +import { + IInterpreterService, + IKnownSearchPathsForInterpreters, + InterpreterType, + PythonInterpreter +} from '../../client/interpreter/contracts'; +import { concatMultilineStringInput } from '../../datascience-ui/common'; +import { generateTestState, ICellViewModel } from '../../datascience-ui/interactive-common/mainState'; +import { sleep } from '../core'; +import { DataScienceIocContainer } from './dataScienceIocContainer'; +import { getConnectionInfo, getIPConnectionInfo } from './jupyterHelpers'; +import { SupportedCommands } from './mockJupyterManager'; +import { MockPythonService } from './mockPythonService'; + +// tslint:disable:no-any no-multiline-string max-func-body-length no-console max-classes-per-file trailing-comma +suite('DataScience notebook tests', () => { + const disposables: Disposable[] = []; + let jupyterExecution: IJupyterExecution; + let pythonFactory: IPythonExecutionFactory; + let ioc: DataScienceIocContainer; + let modifiedConfig = false; + const baseUri = Uri.file('foo.py'); + + setup(async () => { + ioc = new DataScienceIocContainer(); + ioc.registerDataScienceTypes(); + await ioc.activate(); + }); + + teardown(async () => { + try { + if (modifiedConfig) { + traceInfo('Attempting to put jupyter default config back'); + const procService = await createPythonService(); + if (procService) { + await procService.exec(['-m', 'jupyter', 'notebook', '--generate-config', '-y'], {}); + } + } + traceInfo('Shutting down after test.'); + // tslint:disable-next-line:prefer-for-of + for (let i = 0; i < disposables.length; i += 1) { + const disposable = disposables[i]; + if (disposable) { + const promise = disposable.dispose() as Promise; + if (promise) { + await promise; + } + } + } + await ioc.dispose(); + traceInfo('Shutdown after test complete.'); + } catch (e) { + traceError(e); + } + if (process.env.PYTHONWARNINGS) { + delete process.env.PYTHONWARNINGS; + } + }); + + function escapePath(p: string) { + return p.replace(/\\/g, '\\\\'); + } + + function srcDirectory() { + return path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience'); + } + + function extractDataOutput(cell: ICell): any { + assert.equal(cell.data.cell_type, 'code', `Wrong type of cell returned`); + const codeCell = cell.data as nbformat.ICodeCell; + if (codeCell.outputs.length > 0) { + assert.equal(codeCell.outputs.length, 1, 'Cell length not correct'); + const data = codeCell.outputs[0].data; + const error = codeCell.outputs[0].evalue; + if (error) { + assert.fail(`Unexpected error: ${error}`); + } + assert.ok(data, `No data object on the cell`); + if (data) { + // For linter + assert.ok(data.hasOwnProperty('text/plain'), `Cell mime type not correct`); + assert.ok((data as any)['text/plain'], `Cell mime type not correct`); + return (data as any)['text/plain']; + } + } + } + + async function verifySimple( + notebook: INotebook | undefined, + code: string, + expectedValue: any, + pathVerify = false + ): Promise { + const cells = await notebook!.execute(code, path.join(srcDirectory(), 'foo.py'), 2, uuid()); + assert.equal(cells.length, 1, `Wrong number of cells returned`); + const data = extractDataOutput(cells[0]); + if (pathVerify) { + // For a path comparison normalize output and add single quotes on expected value + const normalizedOutput = path.normalize(data).toUpperCase(); + const normalizedTarget = `'${path.normalize(expectedValue).toUpperCase()}'`; + assert.equal(normalizedOutput, normalizedTarget, 'Cell path values does not match'); + } else { + assert.equal(data, expectedValue, 'Cell value does not match'); + } + } + + async function verifyError(notebook: INotebook | undefined, code: string, errorString: string): Promise { + const cells = await notebook!.execute(code, path.join(srcDirectory(), 'foo.py'), 2, uuid()); + assert.equal(cells.length, 1, `Wrong number of cells returned`); + assert.equal(cells[0].data.cell_type, 'code', `Wrong type of cell returned`); + const cell = cells[0].data as nbformat.ICodeCell; + assert.equal(cell.outputs.length, 1, `Cell length not correct`); + const error = cell.outputs[0].evalue; + if (error) { + assert.ok(error, 'Error not found when expected'); + assert.equal(error, errorString, 'Unexpected error found'); + } + } + + async function verifyCell( + notebook: INotebook | undefined, + index: number, + code: string, + mimeType: string, + cellType: string, + verifyValue: (data: any) => void + ): Promise { + // Verify results of an execute + const cells = await notebook!.execute(code, path.join(srcDirectory(), 'foo.py'), 2, uuid()); + assert.equal(cells.length, 1, `${index}: Wrong number of cells returned`); + if (cellType === 'code') { + assert.equal(cells[0].data.cell_type, cellType, `${index}: Wrong type of cell returned`); + const cell = cells[0].data as nbformat.ICodeCell; + assert.ok(cell.outputs.length >= 1, `${index}: Cell length not correct`); + const error = cell.outputs[0].evalue; + if (error) { + assert.ok(false, `${index}: Unexpected error: ${error}`); + } + const data = cell.outputs[0].data; + const text = cell.outputs[0].text; + assert.ok(data || text, `${index}: No data object on the cell for ${code}`); + if (data) { + // For linter + assert.ok( + data.hasOwnProperty(mimeType) || data.hasOwnProperty('text/plain'), + `${index}: Cell mime type not correct for ${JSON.stringify(data)}` + ); + const actualMimeType = data.hasOwnProperty(mimeType) ? mimeType : 'text/plain'; + assert.ok((data as any)[actualMimeType], `${index}: Cell mime type not correct`); + verifyValue((data as any)[actualMimeType]); + } + if (text) { + verifyValue(text); + } + } else if (cellType === 'markdown') { + assert.equal(cells[0].data.cell_type, cellType, `${index}: Wrong type of cell returned`); + const cell = cells[0].data as nbformat.IMarkdownCell; + const outputSource = concatMultilineStringInput(cell.source); + verifyValue(outputSource); + } else if (cellType === 'error') { + const cell = cells[0].data as nbformat.ICodeCell; + assert.equal(cell.outputs.length, 1, `${index}: Cell length not correct`); + const error = cell.outputs[0].evalue; + assert.ok(error, 'Error not found when expected'); + verifyValue(error); + } + } + + function testMimeTypes( + types: { + markdownRegEx: string | undefined; + code: string; + mimeType: string; + result: any; + cellType: string; + verifyValue(data: any): void; + }[] + ) { + runTest('MimeTypes', async () => { + // Prefill with the output (This is only necessary for mocking) + types.forEach((t) => { + addMockData(t.code, t.result, t.mimeType, t.cellType); + }); + + // Test all mime types together so we don't have to startup and shutdown between + // each + const server = await createNotebook( + true, + false, + false, + 'history', + undefined, + path.join(srcDirectory(), 'foo.py') + ); + if (server) { + for (let i = 0; i < types.length; i += 1) { + const markdownRegex = types[i].markdownRegEx ? types[i].markdownRegEx : ''; + ioc.getSettings().datascience.markdownRegularExpression = markdownRegex!; + await verifyCell( + server, + i, + types[i].code, + types[i].mimeType, + types[i].cellType, + types[i].verifyValue + ); + } + } + }); + } + + function runTest( + name: string, + func: (_this: Mocha.Context) => Promise, + _notebookProc?: ChildProcess, + rebindFunc?: () => void + ) { + test(name, async function () { + // Give tests a chance to rebind IOC services before we fetch jupyterExecution and processFactory + if (rebindFunc) { + rebindFunc(); + } + jupyterExecution = ioc.serviceManager.get(IJupyterExecution); + pythonFactory = ioc.serviceManager.get(IPythonExecutionFactory); + console.log(`Starting test ${name} ...`); + if (await jupyterExecution.isNotebookSupported()) { + // tslint:disable-next-line: no-invalid-this + return func(this); + } else { + // tslint:disable-next-line:no-console + console.log(`Skipping test ${name}, no jupyter installed.`); + } + }); + } + + async function createNotebook( + useDefaultConfig: boolean, + expectFailure?: boolean, + usingDarkTheme?: boolean, + purpose?: string, + workingDir?: string, + launchingFile?: string + ): Promise { + // Catch exceptions. Throw a specific assertion if the promise fails + try { + const server = await jupyterExecution.connectToNotebookServer({ + usingDarkTheme, + skipUsingDefaultConfig: !useDefaultConfig, + workingDir: workingDir ? workingDir : ioc.getSettings().datascience.notebookFileRoot, + purpose: purpose ? purpose : '1', + allowUI: () => false + }); + if (expectFailure) { + assert.ok(false, `Expected server to not be created`); + } + if (server) { + const notebook = await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)); + // If specified set our launch file + if (launchingFile) { + await notebook.setLaunchingFile(launchingFile); + } + return notebook; + } + } catch (exc) { + if (!expectFailure) { + assert.ok(false, `Expected server to be created, but got ${exc}`); + } + } + } + + function addMockData(code: string, result: string | number, mimeType?: string, cellType?: string) { + if (ioc.mockJupyter) { + if (cellType && cellType === 'error') { + ioc.mockJupyter.addError(code, result.toString()); + } else { + ioc.mockJupyter.addCell(code, result, mimeType); + } + } + } + + function changeMockWorkingDirectory(workingDir: string) { + if (ioc.mockJupyter) { + ioc.mockJupyter.changeWorkingDirectory(workingDir); + } + } + + function addInterruptableMockData( + code: string, + resultGenerator: (c: CancellationToken) => Promise<{ result: string; haveMore: boolean }> + ) { + if (ioc.mockJupyter) { + ioc.mockJupyter.addContinuousOutputCell(code, resultGenerator); + } + } + + async function createPythonService(versionRequirement?: number): Promise { + if (!ioc.mockJupyter) { + const python = await ioc.getJupyterCapableInterpreter(); + + if ( + python && + python.version?.major && + (!versionRequirement || python.version?.major > versionRequirement) + ) { + return pythonFactory.createActivatedEnvironment({ + resource: undefined, + interpreter: python, + allowEnvironmentFetchExceptions: true, + bypassCondaExecution: true + }); + } + } + } + + runTest('Remote Self Certs', async (_this: Mocha.Context) => { + // Skip this. Entered a bug here to fix: https://github.com/microsoft/vscode-python/issues/10622 + _this.skip(); + + /* + const pythonService = await createPythonService(2); + + if (pythonService) { + // We will only connect if we allow for self signed cert connections + ioc.getSettings().datascience.allowUnauthorizedRemoteConnection = true; + + const connectionFound = createDeferred(); + const configFile = path.join( + EXTENSION_ROOT_DIR, + 'src', + 'test', + 'datascience', + 'serverConfigFiles', + 'selfCert.py' + ); + const pemFile = path.join( + EXTENSION_ROOT_DIR, + 'src', + 'test', + 'datascience', + 'serverConfigFiles', + 'jcert.pem' + ); + const keyFile = path.join( + EXTENSION_ROOT_DIR, + 'src', + 'test', + 'datascience', + 'serverConfigFiles', + 'jkey.key' + ); + + const exeResult = pythonService.execObservable( + [ + '-m', + 'jupyter', + 'notebook', + `--config=${configFile}`, + `--certfile=${pemFile}`, + `--keyfile=${keyFile}` + ], + { + throwOnStdErr: false + } + ); + disposables.push(exeResult); + + exeResult.out.subscribe((output: Output) => { + const connectionURL = getIPConnectionInfo(output.out); + if (connectionURL) { + connectionFound.resolve(connectionURL); + } + }); + + const connString = await connectionFound.promise; + const uri = connString as string; + + // We have a connection string here, so try to connect jupyterExecution to the notebook server + const server = await jupyterExecution.connectToNotebookServer({ + uri, + purpose: '', + allowUI: () => false + }); + const notebook = server + ? await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)) + : undefined; + if (!notebook) { + assert.fail('Failed to connect to remote self cert server'); + } else { + await verifySimple(notebook, `a=1${os.EOL}a`, 1); + } + // Have to dispose here otherwise the process may exit before hand and mess up cleanup. + await server!.dispose(); + } else { + traceInfo('Remote Self Cert is not supported on 2.7'); + _this.skip(); + } + */ + }); + + // Connect to a server that doesn't have a token or password, customers use this and we regressed it once + runTest( + 'Remote No Auth', + async () => { + const pythonService = await createPythonService(); + + if (pythonService) { + const connectionFound = createDeferred(); + const configFile = path.join( + EXTENSION_ROOT_DIR, + 'src', + 'test', + 'datascience', + 'serverConfigFiles', + 'remoteNoAuth.py' + ); + const exeResult = pythonService.execObservable( + ['-m', 'jupyter', 'notebook', `--config=${configFile}`], + { throwOnStdErr: false } + ); + disposables.push(exeResult); + + exeResult.out.subscribe((output: Output) => { + traceInfo(`remote jupyter output: ${output.out}`); + const connectionURL = getIPConnectionInfo(output.out); + if (connectionURL) { + connectionFound.resolve(connectionURL); + } + }); + + const connString = await connectionFound.promise; + const uri = connString as string; + + // We have a connection string here, so try to connect jupyterExecution to the notebook server + const server = await jupyterExecution.connectToNotebookServer({ + uri, + purpose: '', + allowUI: () => false + }); + const notebook = server + ? await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)) + : undefined; + if (!notebook) { + assert.fail('Failed to connect to remote password server'); + } else { + await verifySimple(notebook, `a=1${os.EOL}a`, 1); + } + // Have to dispose here otherwise the process may exit before hand and mess up cleanup. + await server!.dispose(); + } + }, + undefined, + () => { + const dummyDisposable = { + dispose: () => { + return; + } + }; + const appShell = TypeMoq.Mock.ofType(); + appShell + .setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString())) + .returns((e) => { + throw e; + }); + appShell + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(() => Promise.resolve('')); + appShell + .setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((_a1: string, a2: string, _a3: string) => Promise.resolve(a2)); + appShell + .setup((a) => + a.showInformationMessage( + TypeMoq.It.isAny(), + TypeMoq.It.isAny(), + TypeMoq.It.isAny(), + TypeMoq.It.isAny() + ) + ) + .returns((_a1: string, a2: string, _a3: string, _a4: string) => Promise.resolve(a2)); + appShell.setup((a) => a.showInputBox(TypeMoq.It.isAny())).returns(() => Promise.resolve('')); + appShell.setup((a) => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable); + ioc.serviceManager.rebindInstance(IApplicationShell, appShell.object); + } + ); + + runTest('Remote Password', async () => { + const pythonService = await createPythonService(); + + if (pythonService) { + const connectionFound = createDeferred(); + const configFile = path.join( + EXTENSION_ROOT_DIR, + 'src', + 'test', + 'datascience', + 'serverConfigFiles', + 'remotePassword.py' + ); + const exeResult = pythonService.execObservable(['-m', 'jupyter', 'notebook', `--config=${configFile}`], { + throwOnStdErr: false + }); + disposables.push(exeResult); + + exeResult.out.subscribe((output: Output) => { + traceInfo(`remote jupyter output: ${output.out}`); + const connectionURL = getIPConnectionInfo(output.out); + if (connectionURL) { + connectionFound.resolve(connectionURL); + } + }); + + const connString = await connectionFound.promise; + const uri = connString as string; + + // We have a connection string here, so try to connect jupyterExecution to the notebook server + const server = await jupyterExecution.connectToNotebookServer({ + uri, + purpose: '', + allowUI: () => false + }); + const notebook = server + ? await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)) + : undefined; + if (!notebook) { + assert.fail('Failed to connect to remote password server'); + } else { + await verifySimple(notebook, `a=1${os.EOL}a`, 1); + } + // Have to dispose here otherwise the process may exit before hand and mess up cleanup. + await server!.dispose(); + } + }); + + runTest('Remote', async () => { + const pythonService = await createPythonService(); + + if (pythonService) { + const connectionFound = createDeferred(); + const configFile = path.join( + EXTENSION_ROOT_DIR, + 'src', + 'test', + 'datascience', + 'serverConfigFiles', + 'remoteToken.py' + ); + const exeResult = pythonService.execObservable(['-m', 'jupyter', 'notebook', `--config=${configFile}`], { + throwOnStdErr: false + }); + disposables.push(exeResult); + + exeResult.out.subscribe((output: Output) => { + traceInfo(`remote jupyter output: ${output.out}`); + const connectionURL = getConnectionInfo(output.out); + if (connectionURL) { + connectionFound.resolve(connectionURL); + } + }); + + const connString = await connectionFound.promise; + const uri = connString as string; + + // We have a connection string here, so try to connect jupyterExecution to the notebook server + const server = await jupyterExecution.connectToNotebookServer({ + uri, + purpose: '', + allowUI: () => false + }); + const notebook = server + ? await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)) + : undefined; + if (!notebook) { + assert.fail('Failed to connect to remote server'); + } else { + await verifySimple(notebook, `a=1${os.EOL}a`, 1); + } + + // Have to dispose here otherwise the process may exit before hand and mess up cleanup. + await server!.dispose(); + } + }); + + runTest('Creation', async () => { + await createNotebook(true); + }); + + runTest('Failure', async () => { + // Make a dummy class that will fail during launch + class FailedProcess extends JupyterExecutionFactory { + public isNotebookSupported = (): Promise => { + return Promise.resolve(false); + }; + } + ioc.serviceManager.rebind(IJupyterExecution, FailedProcess); + jupyterExecution = ioc.serviceManager.get(IJupyterExecution); + await createNotebook(true, true); + }); + + test('Not installed', async () => { + jupyterExecution = ioc.serviceManager.get(IJupyterExecution); + // Rewire our data we use to search for processes + @injectable() + class EmptyInterpreterService implements IInterpreterService { + public get hasInterpreters(): Promise { + return Promise.resolve(true); + } + public onDidChangeInterpreter( + _listener: (e: void) => any, + _thisArgs?: any, + _disposables?: Disposable[] + ): Disposable { + return { dispose: noop }; + } + public onDidChangeInterpreterInformation( + _listener: (e: PythonInterpreter) => any, + _thisArgs?: any, + _disposables?: Disposable[] + ): Disposable { + return { dispose: noop }; + } + public getInterpreters(_resource?: Uri): Promise { + return Promise.resolve([]); + } + public autoSetInterpreter(): Promise { + throw new Error('Method not implemented'); + } + public getActiveInterpreter(_resource?: Uri): Promise { + return Promise.resolve(undefined); + } + public getInterpreterDetails(_pythonPath: string, _resoure?: Uri): Promise { + throw new Error('Method not implemented'); + } + public refresh(_resource: Uri): Promise { + throw new Error('Method not implemented'); + } + public initialize(): void { + throw new Error('Method not implemented'); + } + public getDisplayName(_interpreter: Partial): Promise { + throw new Error('Method not implemented'); + } + public shouldAutoSetInterpreter(): Promise { + throw new Error('Method not implemented'); + } + } + @injectable() + class EmptyPathService implements IKnownSearchPathsForInterpreters { + public getSearchPaths(): string[] { + return []; + } + } + ioc.serviceManager.rebind(IInterpreterService, EmptyInterpreterService); + ioc.serviceManager.rebind(IKnownSearchPathsForInterpreters, EmptyPathService); + jupyterExecution = ioc.serviceManager.get(IJupyterExecution); + await createNotebook(true, true); + }); + + runTest('Export/Import', async () => { + // Get a bunch of test cells (use our test cells from the react controls) + const testFolderPath = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience'); + const testState = generateTestState(testFolderPath); + const cells = testState.cellVMs.map((cellVM: ICellViewModel, _index: number) => { + return cellVM.cell; + }); + + // Translate this into a notebook + + // Make sure we have a change dir happening + const settings = { ...ioc.getSettings().datascience }; + settings.changeDirOnImportExport = true; + ioc.forceSettingsChanged(undefined, ioc.getSettings().pythonPath, settings); + + const exporter = ioc.serviceManager.get(INotebookExporter); + const newFolderPath = path.join( + EXTENSION_ROOT_DIR, + 'src', + 'test', + 'datascience', + 'WorkspaceDir', + 'WorkspaceSubDir', + 'foo.ipynb' + ); + const notebook = await exporter.translateToNotebook(cells, newFolderPath); + assert.ok(notebook, 'Translate to notebook is failing'); + + // Make sure we added in our chdir + if (notebook) { + const nbcells = notebook.cells; + if (nbcells) { + const firstCellText: string = nbcells[0].source as string; + assert.ok(firstCellText.includes('os.chdir'), `${firstCellText} does not include 'os.chdir`); + } + } + + // Save to a temp file + const fileSystem = ioc.serviceManager.get(IFileSystem); + const importer = ioc.serviceManager.get(INotebookImporter); + const temp = await fileSystem.createTemporaryFile('.ipynb'); + + try { + await fs.writeFile(temp.filePath, JSON.stringify(notebook), 'utf8'); + // Try importing this. This should verify export works and that importing is possible + const results = await importer.importFromFile(temp.filePath); + + // Make sure we have a single chdir in our results + const first = results.indexOf('os.chdir'); + assert.ok(first >= 0, 'No os.chdir in import'); + const second = results.indexOf('os.chdir', first + 1); + assert.equal(second, -1, 'More than one chdir in the import. It should be skipped'); + + // Make sure we have a cell in our results + assert.ok(/#\s*%%/.test(results), 'No cells in returned import'); + } finally { + importer.dispose(); + temp.dispose(); + } + }); + + runTest('Verify manual working directory', async () => { + // Instead of default, manually set a working directory + const notebook = await createNotebook(true, undefined, undefined, undefined, EXTENSION_ROOT_DIR); + await verifySimple(notebook, 'import os\nos.getcwd()', EXTENSION_ROOT_DIR, true); + await verifySimple(notebook, 'import sys\nsys.path[0]', EXTENSION_ROOT_DIR, true); + }); + + // tslint:disable-next-line:no-invalid-template-strings + runTest('Verify ${fileDirname} working directory', async () => { + // Verify that the default ${fileDirname} setting sets the working directory to the file path + changeMockWorkingDirectory(`'${srcDirectory()}'`); + const notebook = await createNotebook( + true, + undefined, + undefined, + undefined, + undefined, + path.join(srcDirectory(), 'foo.py') + ); + await verifySimple(notebook, 'import os\nos.getcwd()', srcDirectory(), true); + await verifySimple(notebook, 'import sys\nsys.path[0]', srcDirectory(), true); + }); + + runTest('Change Interpreter', async () => { + const isRollingBuild = process.env ? process.env.VSCODE_PYTHON_ROLLING !== undefined : false; + + // Real Jupyter doesn't help this test at all and is tricky to set up for it, so just skip it + if (!isRollingBuild) { + const server = await createNotebook(true); + + // Create again, we should get the same server from the cache + const server2 = await createNotebook(true); + // tslint:disable-next-line: triple-equals + assert.ok(server == server2, 'With no settings changed we should return the cached server'); + + // Create a new mock interpreter with a different path + const newPython: PythonInterpreter = { + path: '/foo/bar/baz/python.exe', + version: new SemVer('3.6.6-final'), + sysVersion: '1.0.0.0', + sysPrefix: 'Python', + type: InterpreterType.Unknown, + architecture: Architecture.x64 + }; + + // Add interpreter into mock jupyter service and set it as active + ioc.addInterpreter(newPython, SupportedCommands.all); + + // Create a new notebook, we should still be the same as interpreter is just saved for notebook creation + const server3 = await createNotebook(true); + // tslint:disable-next-line: triple-equals + assert.ok(server == server3, 'With interpreter changed we should not return a new server'); + } else { + console.log(`Skipping Change Interpreter test in non-mocked Jupyter case`); + } + }); + + runTest('Restart kernel', async () => { + addMockData(`a=1${os.EOL}a`, 1); + addMockData(`a+=1${os.EOL}a`, 2); + addMockData(`a+=4${os.EOL}a`, 6); + addMockData('a', `name 'a' is not defined`, 'error'); + + const server = await createNotebook(true); + + // Setup some state and verify output is correct + await verifySimple(server, `a=1${os.EOL}a`, 1); + await verifySimple(server, `a+=1${os.EOL}a`, 2); + await verifySimple(server, `a+=4${os.EOL}a`, 6); + + console.log('Waiting for idle'); + + // In unit tests we have to wait for status idle before restarting. Unit tests + // seem to be timing out if the restart throws any exceptions (even if they're caught) + await server!.waitForIdle(10000); + + console.log('Restarting kernel'); + try { + await server!.restartKernel(10000); + + console.log('Waiting for idle'); + await server!.waitForIdle(10000); + + console.log('Verifying restart'); + await verifyError(server, 'a', `name 'a' is not defined`); + } catch (exc) { + assert.ok( + exc instanceof JupyterKernelPromiseFailedError, + `Restarting did not timeout correctly for ${exc}` + ); + } + }); + + class TaggedCancellationTokenSource extends CancellationTokenSource { + public tag: string; + constructor(tag: string) { + super(); + this.tag = tag; + } + } + + async function testCancelableCall( + method: (t: CancellationToken) => Promise, + messageFormat: string, + timeout: number + ): Promise { + const tokenSource = new TaggedCancellationTokenSource(messageFormat.format(timeout.toString())); + const disp = setTimeout( + (_s) => { + tokenSource.cancel(); + }, + timeout, + tokenSource.tag + ); + + try { + // tslint:disable-next-line:no-string-literal + (tokenSource.token as any)['tag'] = messageFormat.format(timeout.toString()); + await method(tokenSource.token); + } catch (exc) { + // This should happen. This means it was canceled. + assert.ok(exc instanceof CancellationError, `Non cancellation error found : ${exc.stack}`); + } finally { + clearTimeout(disp); + tokenSource.dispose(); + } + + return true; + } + + async function testCancelableMethod( + method: (t: CancellationToken) => Promise, + messageFormat: string, + short?: boolean + ): Promise { + const timeouts = short ? [10, 20, 30, 100] : [300, 400, 500, 1000]; + // tslint:disable-next-line:prefer-for-of + for (let i = 0; i < timeouts.length; i += 1) { + await testCancelableCall(method, messageFormat, timeouts[i]); + } + + return true; + } + + runTest('Cancel execution', async () => { + if (ioc.mockJupyter) { + ioc.mockJupyter.setProcessDelay(2000); + addMockData(`a=1${os.EOL}a`, 1); + } + + // Try different timeouts, canceling after the timeout on each + assert.ok( + await testCancelableMethod( + (t: CancellationToken) => jupyterExecution.connectToNotebookServer(undefined, t), + 'Cancel did not cancel start after {0}ms' + ) + ); + + if (ioc.mockJupyter) { + ioc.mockJupyter.setProcessDelay(undefined); + } + + // Make sure doing normal start still works + const nonCancelSource = new CancellationTokenSource(); + const server = await jupyterExecution.connectToNotebookServer(undefined, nonCancelSource.token); + const notebook = server + ? await server.createNotebook(baseUri, Uri.parse(Identifiers.InteractiveWindowIdentity)) + : undefined; + assert.ok(notebook, 'Server not found with a cancel token that does not cancel'); + + // Make sure can run some code too + await verifySimple(notebook, `a=1${os.EOL}a`, 1); + + if (ioc.mockJupyter) { + ioc.mockJupyter.setProcessDelay(200); + } + + // Force a settings changed so that all of the cached data is cleared + ioc.forceSettingsChanged(undefined, '/usr/bin/test3/python'); + + assert.ok( + await testCancelableMethod( + (t: CancellationToken) => jupyterExecution.getUsableJupyterPython(t), + 'Cancel did not cancel getusable after {0}ms', + true + ) + ); + assert.ok( + await testCancelableMethod( + (t: CancellationToken) => jupyterExecution.isNotebookSupported(t), + 'Cancel did not cancel isNotebook after {0}ms', + true + ) + ); + assert.ok( + await testCancelableMethod( + (t: CancellationToken) => jupyterExecution.isImportSupported(t), + 'Cancel did not cancel isImport after {0}ms', + true + ) + ); + }); + + async function interruptExecute( + notebook: INotebook | undefined, + code: string, + interruptMs: number, + sleepMs: number + ): Promise { + let interrupted = false; + let finishedBefore = false; + const finishedPromise = createDeferred(); + let error; + const observable = notebook!.executeObservable(code, Uri.file('foo.py').fsPath, 0, uuid(), false); + observable.subscribe( + (c) => { + if (c.length > 0 && c[0].state === CellState.error) { + finishedBefore = !interrupted; + finishedPromise.resolve(); + } + if (c.length > 0 && c[0].state === CellState.finished) { + finishedBefore = !interrupted; + finishedPromise.resolve(); + } + }, + (err) => { + error = err; + finishedPromise.resolve(); + }, + () => finishedPromise.resolve() + ); + + // Then interrupt + interrupted = true; + const result = await notebook!.interruptKernel(interruptMs); + + // Then we should get our finish unless there was a restart + await waitForPromise(finishedPromise.promise, sleepMs); + assert.equal(finishedBefore, false, 'Finished before the interruption'); + assert.equal(error, undefined, 'Error thrown during interrupt'); + assert.ok( + finishedPromise.completed || result === InterruptResult.TimedOut || result === InterruptResult.Success, + `Interrupt restarted ${result} for: ${code}` + ); + + return result; + } + + runTest('Interrupt kernel', async () => { + const returnable = `import signal +import _thread +import time + +keep_going = True +def handler(signum, frame): + global keep_going + print('signal') + keep_going = False + +signal.signal(signal.SIGINT, handler) + +while keep_going: + print(".") + time.sleep(.1)`; + const fourSecondSleep = `import time${os.EOL}time.sleep(4)${os.EOL}print("foo")`; + const kill = `import signal +import time +import os + +keep_going = True +def handler(signum, frame): + global keep_going + print('signal') + os._exit(-2) + +signal.signal(signal.SIGINT, handler) + +while keep_going: + print(".") + time.sleep(.1)`; + + // Add to our mock each of these, with each one doing something specific. + addInterruptableMockData(returnable, async (cancelToken: CancellationToken) => { + // This one goes forever until a cancellation happens + let haveMore = true; + try { + await Cancellation.race((_t) => sleep(100), cancelToken); + } catch { + haveMore = false; + } + return { result: '.', haveMore: haveMore }; + }); + addInterruptableMockData(fourSecondSleep, async (_cancelToken: CancellationToken) => { + // This one sleeps for four seconds and then it's done. + await sleep(4000); + return { result: 'foo', haveMore: false }; + }); + addInterruptableMockData(kill, async (cancelToken: CancellationToken) => { + // This one goes forever until a cancellation happens + let haveMore = true; + try { + await Cancellation.race((_t) => sleep(100), cancelToken); + } catch { + haveMore = false; + } + return { result: '.', haveMore: haveMore }; + }); + + const server = await createNotebook(true); + + // Give some time for the server to finish. Otherwise our first interrupt will + // happen so fast, we'll interrupt startup. + await sleep(100); + + // Try with something we can interrupt + await interruptExecute(server, returnable, 1000, 1000); + + // Try again with something that doesn't return. However it should finish before + // we get to our own sleep. Note: We need the print so that the test knows something happened. + await interruptExecute(server, fourSecondSleep, 7000, 7000); + + // Try again with something that doesn't return. Make sure it times out + await interruptExecute(server, fourSecondSleep, 100, 7000); + + // The tough one, somethign that causes a kernel reset. + await interruptExecute(server, kill, 1000, 1000); + }); + + testMimeTypes([ + { + markdownRegEx: undefined, + code: `a=1 +a`, + mimeType: 'text/plain', + cellType: 'code', + result: 1, + verifyValue: (d) => assert.equal(d, 1, 'Plain text invalid') + }, + { + markdownRegEx: undefined, + code: `import pandas as pd +df = pd.read("${escapePath(path.join(srcDirectory(), 'DefaultSalesReport.csv'))}") +df.head()`, + mimeType: 'text/html', + result: `pd has no attribute 'read'`, + cellType: 'error', + // tslint:disable-next-line:quotemark + verifyValue: (d) => assert.ok((d as string).includes("has no attribute 'read'"), 'Unexpected error result') + }, + { + markdownRegEx: undefined, + code: `import pandas as pd +df = pd.read_csv("${escapePath(path.join(srcDirectory(), 'DefaultSalesReport.csv'))}") +df.head()`, + mimeType: 'text/html', + result: `A table`, + cellType: 'code', + verifyValue: (d) => assert.ok(d.toString().includes(''), 'Table not found') + }, + { + markdownRegEx: undefined, + code: `#%% [markdown]# +# #HEADER`, + mimeType: 'text/plain', + cellType: 'markdown', + result: '#HEADER', + verifyValue: (d) => assert.equal(d, ' #HEADER', 'Markdown incorrect') + }, + { + markdownRegEx: '\\s*#\\s*', + code: `# +# #HEADER`, + mimeType: 'text/plain', + cellType: 'markdown', + result: '#HEADER', + verifyValue: (d) => assert.equal(d, ' #HEADER', 'Markdown incorrect') + }, + { + // Test relative directories too. + markdownRegEx: undefined, + code: `import pandas as pd +df = pd.read_csv("./DefaultSalesReport.csv") +df.head()`, + mimeType: 'text/html', + cellType: 'code', + result: `A table`, + verifyValue: (d) => assert.ok(d.toString().includes(''), 'Table not found') + }, + { + // Important to test as multiline cell magics only work if they are the first item in the cell + markdownRegEx: undefined, + code: `#%% +%%bash +echo 'hello'`, + mimeType: 'text/plain', + cellType: 'code', + result: 'hello', + verifyValue: (_d) => noop() // Anything is fine as long as it tries it. + }, + { + // Test shell command should work on PC / Mac / Linux + markdownRegEx: undefined, + code: `!echo world`, + mimeType: 'text/plain', + cellType: 'code', + result: 'world', + verifyValue: (d) => assert.ok(d.includes('world'), 'Cell command incorrect') + }, + { + // Plotly + markdownRegEx: undefined, + code: `import matplotlib.pyplot as plt +import matplotlib as mpl +import numpy as np +import pandas as pd +x = np.linspace(0, 20, 100) +plt.plot(x, np.sin(x)) +plt.show()`, + result: `00000`, + mimeType: 'image/svg+xml', + cellType: 'code', + verifyValue: (_d) => { + return; + } + } + ]); + + async function generateNonDefaultConfig() { + const usable = await ioc.getJupyterCapableInterpreter(); + assert.ok(usable, 'Cant find jupyter enabled python'); + + // Manually generate an invalid jupyter config + const procService = await createPythonService(); + assert.ok(procService, 'Can not get a process service'); + const results = await procService!.exec(['-m', 'jupyter', 'notebook', '--generate-config', '-y'], {}); + + // Results should have our path to the config. + const match = /^.*\s+(.*jupyter_notebook_config.py)\s+.*$/m.exec(results.stdout); + assert.ok(match && match !== null && match.length > 0, 'Jupyter is not outputting the path to the config'); + const configPath = match !== null ? match[1] : ''; + const filesystem = ioc.serviceContainer.get(IFileSystem); + await filesystem.writeFile(configPath, 'c.NotebookApp.password_required = True'); // This should make jupyter fail + modifiedConfig = true; + } + + runTest('Non default config fails', async () => { + if (!ioc.mockJupyter) { + await generateNonDefaultConfig(); + try { + await createNotebook(false); + assert.fail('Should not be able to connect to notebook server with bad config'); + } catch { + noop(); + } + } else { + // In the mock case, just make sure not using a config works + await createNotebook(false); + } + }); + + runTest('Non default config does not mess up default config', async () => { + if (!ioc.mockJupyter) { + await generateNonDefaultConfig(); + const server = await createNotebook(true); + assert.ok(server, 'Never connected to a default server with a bad default config'); + + await verifySimple(server, `a=1${os.EOL}a`, 1); + } + }); + + runTest('Custom command line', async () => { + if (!ioc.mockJupyter) { + const tempDir = os.tmpdir(); + const settings = ioc.getSettings(); + settings.datascience.jupyterCommandLineArguments = ['--NotebookApp.port=9975', `--notebook-dir=${tempDir}`]; + ioc.forceSettingsChanged(undefined, settings.pythonPath, settings.datascience); + const notebook = await createNotebook(true); + assert.ok(notebook, 'Server should have started on port 9975'); + const hs = notebook as HostJupyterNotebook; + // Check port number. Should have at least started with the one specified. + assert.ok(hs.server.getConnectionInfo()?.baseUrl.startsWith('http://localhost:99'), 'Port was not used'); + + await verifySimple(hs, `a=1${os.EOL}a`, 1); + } + }); + + runTest('Invalid kernel spec works', async () => { + if (ioc.mockJupyter) { + // Make a dummy class that will fail during launch + class FailedKernelSpec extends JupyterExecutionFactory { + protected async getMatchingKernelSpec( + _connection?: IConnection, + _cancelToken?: CancellationToken + ): Promise { + return Promise.resolve(undefined); + } + } + ioc.serviceManager.rebind(IJupyterExecution, FailedKernelSpec); + jupyterExecution = ioc.serviceManager.get(IJupyterExecution); + addMockData(`a=1${os.EOL}a`, 1); + + const server = await createNotebook(true); + assert.ok(server, 'Empty kernel spec messes up creating a server'); + + await verifySimple(server, `a=1${os.EOL}a`, 1); + } + }); + + runTest('Server cache working', async () => { + console.log('Staring server cache test'); + const s1 = await createNotebook(true, false, false, 'same'); + console.log('Creating s2'); + const s2 = await createNotebook(true, false, false, 'same'); + console.log('Testing s1 and s2, creating s3'); + assert.ok(s1 === s2, 'Two servers not the same when they should be'); + const s3 = await createNotebook(false, false, false, 'same'); + console.log('Testing s1 and s3, creating s4'); + assert.ok(s1 !== s3, 'Different config should create different server'); + const s4 = await createNotebook(true, false, false, 'different'); + console.log('Testing s1 and s4, creating s5'); + assert.ok(s1 !== s4, 'Different purpose should create different server'); + const s5 = await createNotebook(true, false, true, 'different'); + assert.ok(s4 === s5, 'Dark theme should be same server'); + console.log('Disposing of all'); + await s1!.dispose(); + await s3!.dispose(); + await s4!.dispose(); + }); + + class DyingProcess implements ChildProcess { + public stdin: Writable; + public stdout: Readable; + public stderr: Readable; + public stdio: [Writable, Readable, Readable]; + public killed: boolean = false; + public pid: number = 1; + public connected: boolean = true; + constructor(private timeout: number) { + noop(); + this.stderr = this.stdout = new Readable(); + this.stdin = new Writable(); + this.stdio = [this.stdin, this.stdout, this.stderr]; + } + public kill(_signal?: string): void { + throw new Error('Method not implemented.'); + } + public send(_message: any, _sendHandle?: any, _options?: any, _callback?: any): any { + throw new Error('Method not implemented.'); + } + public disconnect(): void { + throw new Error('Method not implemented.'); + } + public unref(): void { + throw new Error('Method not implemented.'); + } + public ref(): void { + throw new Error('Method not implemented.'); + } + public addListener(_event: any, _listener: any): this { + throw new Error('Method not implemented.'); + } + public emit(_event: any, _message?: any, _sendHandle?: any, ..._rest: any[]): any { + throw new Error('Method not implemented.'); + } + public on(event: any, listener: any): this { + if (event === 'exit') { + setTimeout(() => listener(2), this.timeout); + } + return this; + } + public off(_event: string | symbol, _listener: (...args: any[]) => void): this { + throw new Error('Method not implemented.'); + } + public once(_event: any, _listener: any): this { + throw new Error('Method not implemented.'); + } + public prependListener(_event: any, _listener: any): this { + throw new Error('Method not implemented.'); + } + public prependOnceListener(_event: any, _listener: any): this { + throw new Error('Method not implemented.'); + } + public removeListener(_event: string | symbol, _listener: (...args: any[]) => void): this { + return this; + } + public removeAllListeners(_event?: string | symbol): this { + throw new Error('Method not implemented.'); + } + public setMaxListeners(_n: number): this { + throw new Error('Method not implemented.'); + } + public getMaxListeners(): number { + throw new Error('Method not implemented.'); + } + public listeners(_event: string | symbol): Function[] { + throw new Error('Method not implemented.'); + } + public rawListeners(_event: string | symbol): Function[] { + throw new Error('Method not implemented.'); + } + public eventNames(): (string | symbol)[] { + throw new Error('Method not implemented.'); + } + public listenerCount(_type: string | symbol): number { + throw new Error('Method not implemented.'); + } + } + + runTest( + 'Server death', + async () => { + if (ioc.mockJupyter) { + // Only run this test for mocks. We need to mock the server dying. + addMockData(`a=1${os.EOL}a`, 1); + const server = await createNotebook(true); + assert.ok(server, 'Server died before running'); + + // Sleep for 100 ms so it crashes + await sleep(100); + + try { + await verifySimple(server, `a=1${os.EOL}a`, 1); + assert.ok(false, 'Exception should have been thrown'); + } catch { + noop(); + } + } + }, + new DyingProcess(100) + ); + + runTest('Execution logging', async () => { + const cellInputs: string[] = []; + const outputs: string[] = []; + @injectable() + class Logger implements INotebookExecutionLogger { + public async preExecute(cell: ICell, _silent: boolean): Promise { + cellInputs.push(concatMultilineStringInput(cell.data.source)); + } + public async postExecute(cell: ICell, _silent: boolean): Promise { + outputs.push(extractDataOutput(cell)); + } + } + ioc.serviceManager.add(INotebookExecutionLogger, Logger); + addMockData(`a=1${os.EOL}a`, 1); + const server = await createNotebook(true); + assert.ok(server, 'Server not created in logging case'); + await server!.execute(`a=1${os.EOL}a`, path.join(srcDirectory(), 'foo.py'), 2, uuid()); + assert.equal(cellInputs.length, 2, 'Not enough cell inputs'); + assert.ok(outputs.length >= 1, 'Not enough cell outputs'); + assert.equal(cellInputs[1], 'a=1\na', 'Cell inputs not captured'); + assert.equal(outputs[outputs.length - 1], '1', 'Cell outputs not captured'); + }); + + async function disableJupyter(pythonPath: string) { + const factory = ioc.serviceManager.get(IPythonExecutionFactory); + const service = await factory.create({ pythonPath }); + const mockService = service as MockPythonService; + // Used by commands (can be removed when `src/client/datascience/jupyter/interpreter/jupyterCommand.ts` is deleted). + mockService.addExecResult(['-m', 'jupyter', 'notebook', '--version'], () => { + return Promise.resolve({ + stdout: '9.9.9.9', + stderr: 'Not supported' + }); + }); + + // Used by commands (can be removed when `src/client/datascience/jupyter/interpreter/jupyterCommand.ts` is deleted). + mockService.addExecResult(['-m', 'notebook', '--version'], () => { + return Promise.resolve({ + stdout: '', + stderr: 'Not supported' + }); + }); + // For new approach. + when(ioc.mockJupyter?.productInstaller.isInstalled(Product.jupyter)).thenResolve(false as any); + when(ioc.mockJupyter?.productInstaller.isInstalled(Product.notebook)).thenResolve(false as any); + when(ioc.mockJupyter?.productInstaller.isInstalled(Product.jupyter, anything())).thenResolve(false as any); + when(ioc.mockJupyter?.productInstaller.isInstalled(Product.notebook, anything())).thenResolve(false as any); + } + + test('Notebook launch failure', async function () { + if (!ioc.mockJupyter) { + // tslint:disable-next-line: no-invalid-this + this.skip(); + } else { + const application = mock(ApplicationShell); + when(application.withProgress(anything(), anything())).thenResolve({ + status: ModuleExistsStatus.NotFound + } as any); + ioc.serviceManager.rebindInstance(IApplicationShell, instance(application)); + + jupyterExecution = ioc.serviceManager.get(IJupyterExecution); + + // Change notebook command to fail with some goofy output + await disableJupyter(ioc.workingInterpreter.path); + await disableJupyter(ioc.workingInterpreter2.path); + + // Try creating a notebook + let threw = false; + try { + const testDir = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'datascience'); + await jupyterExecution.connectToNotebookServer({ + usingDarkTheme: false, + workingDir: testDir, + purpose: '1', + allowUI: () => false + }); + } catch (e) { + threw = true; + // When using old command finder, the error is `Not Supported` (directly from stdout). - can be deprecated when jupyterCommandFinder.ts is deleted. + // When using new approach, we inform user that some packages are not installed. + const expectedErrorMsg = getMessageForLibrariesNotInstalled( + [Product.jupyter, Product.notebook], + 'Python' + ); + + assert.ok( + e.message.includes('Not supported') || e.message.includes(expectedErrorMsg), + `Wrong error thrown when notebook is created. Error is ${e.message}` + ); + } + + assert.ok(threw, 'No exception thrown during notebook creation'); + } + }); + + test('Notebook launch with PYTHONWARNINGS', async function () { + if (ioc.mockJupyter) { + // tslint:disable-next-line: no-invalid-this + this.skip(); + } else { + // Force python warnings to always + process.env[`PYTHONWARNINGS`] = 'always'; + jupyterExecution = ioc.serviceManager.get(IJupyterExecution); + + // Try creating a notebook + const server = await createNotebook(true); + assert.ok(server, 'Server died before running'); + } + }); + + // tslint:disable-next-line: no-function-expression + runTest('Notebook launch retry', async function (_this: Mocha.Context) { + // Skipping for now. Renable to test idle timeouts + _this.skip(); + ioc.getSettings().datascience.jupyterLaunchRetries = 1; + ioc.getSettings().datascience.jupyterLaunchTimeout = 10000; + // ioc.getSettings().datascience.runStartupCommands = '%config Application.log_level="DEBUG"'; + // const log = `import logging + // logger = logging.getLogger() + // fhandler = logging.FileHandler(filename='D:\\Training\\mylog.log', mode='a') + // formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + // fhandler.setFormatter(formatter) + // logger.addHandler(fhandler) + // logger.setLevel(logging.DEBUG)`; + for (let i = 0; i < 100; i += 1) { + const notebook = await createNotebook(true, false); + assert.ok(notebook, 'did not create notebook'); + await notebook!.dispose(); + const exec = ioc.get(IJupyterExecution); + await exec.dispose(); + } + }); +}); diff --git a/src/test/datascience/plotViewer.functional.test.tsx b/src/test/datascience/plotViewer.functional.test.tsx index 4e666e68fb64..22f454c852f2 100644 --- a/src/test/datascience/plotViewer.functional.test.tsx +++ b/src/test/datascience/plotViewer.functional.test.tsx @@ -100,12 +100,12 @@ suite('DataScience PlotViewer tests', () => { const cancelSvg = `Cancel_16xMD`; - runMountedTest('Simple SVG', async wrapper => { + runMountedTest('Simple SVG', async (wrapper) => { await waitForPlot(wrapper, cancelSvg); verifySvgValue(wrapper, cancelSvg); }); - runMountedTest('Export', async _wrapper => { + runMountedTest('Export', async (_wrapper) => { // Export isn't runnable inside of JSDOM. So this test does nothing. }); }); diff --git a/src/test/datascience/reactHelpers.ts b/src/test/datascience/reactHelpers.ts index e83b03062509..95fdcf8e5969 100644 --- a/src/test/datascience/reactHelpers.ts +++ b/src/test/datascience/reactHelpers.ts @@ -9,12 +9,12 @@ // tslint:disable:no-var-requires no-require-imports no-any no-function-expression const Module = require('module'); -(function() { +(function () { const origRequire = Module.prototype.require; const _require = (context: any, filepath: any) => { return origRequire.call(context, filepath); }; - Module.prototype.require = function(filepath: string) { + Module.prototype.require = function (filepath: string) { if (filepath === 'canvas') { try { // Make sure we aren't inside of vscode. The nodejs version of Canvas won't match. At least sometimes. @@ -317,7 +317,7 @@ export function setUpDomEnvironment() { } // tslint:disable-next-line: no-function-expression - window.HTMLCanvasElement.prototype.toDataURL = function() { + window.HTMLCanvasElement.prototype.toDataURL = function () { return ''; }; @@ -409,7 +409,7 @@ export function setUpDomEnvironment() { const configurationRegex = /.*(\\|\/)node_modules(\\|\/)monaco-editor(\\|\/)esm(\\|\/)vs(\\|\/)editor(\\|\/)browser(\\|\/)config(\\|\/)configuration\.js/g; const _oldLoader = require.extensions['.js']; // tslint:disable-next-line:no-function-expression - require.extensions['.js'] = function(mod: any, filename) { + require.extensions['.js'] = function (mod: any, filename) { if (configurationRegex.test(filename)) { let content = require('fs').readFileSync(filename, 'utf8'); content += 'export function getCSSBasedConfiguration() { return CSSBasedConfiguration.INSTANCE; };\n'; @@ -432,7 +432,7 @@ export function setupTranspile() { } function copyProps(src: any, target: any) { - const props = Object.getOwnPropertyNames(src).filter(prop => typeof target[prop] === undefined); + const props = Object.getOwnPropertyNames(src).filter((prop) => typeof target[prop] === undefined); props.forEach((p: string) => { target[p] = src[p]; }); diff --git a/src/test/datascience/shiftEnterBanner.unit.test.ts b/src/test/datascience/shiftEnterBanner.unit.test.ts index 493d2203f23a..37ddf3799ee9 100644 --- a/src/test/datascience/shiftEnterBanner.unit.test.ts +++ b/src/test/datascience/shiftEnterBanner.unit.test.ts @@ -133,9 +133,9 @@ function loadBanner( // Config persist state const persistService: typemoq.IMock = typemoq.Mock.ofType(); const enabledState: typemoq.IMock> = typemoq.Mock.ofType>(); - enabledState.setup(a => a.value).returns(() => stateEnabled); + enabledState.setup((a) => a.value).returns(() => stateEnabled); persistService - .setup(a => + .setup((a) => a.createGlobalPersistentState( typemoq.It.isValue(InteractiveShiftEnterStateKeys.ShowBanner), typemoq.It.isValue(true) @@ -145,7 +145,7 @@ function loadBanner( return enabledState.object; }); persistService - .setup(a => + .setup((a) => a.createGlobalPersistentState( typemoq.It.isValue(InteractiveShiftEnterStateKeys.ShowBanner), typemoq.It.isValue(false) @@ -158,14 +158,14 @@ function loadBanner( // Config settings const pythonSettings = typemoq.Mock.ofType(); const dataScienceSettings = typemoq.Mock.ofType(); - dataScienceSettings.setup(d => d.enabled).returns(() => true); - dataScienceSettings.setup(d => d.sendSelectionToInteractiveWindow).returns(() => false); - pythonSettings.setup(p => p.datascience).returns(() => dataScienceSettings.object); - config.setup(c => c.getSettings(typemoq.It.isAny())).returns(() => pythonSettings.object); + dataScienceSettings.setup((d) => d.enabled).returns(() => true); + dataScienceSettings.setup((d) => d.sendSelectionToInteractiveWindow).returns(() => false); + pythonSettings.setup((p) => p.datascience).returns(() => dataScienceSettings.object); + config.setup((c) => c.getSettings(typemoq.It.isAny())).returns(() => pythonSettings.object); // Config Jupyter jupyterExecution - .setup(j => j.isNotebookSupported()) + .setup((j) => j.isNotebookSupported()) .returns(() => { return Promise.resolve(jupyterFound); }) @@ -176,13 +176,13 @@ function loadBanner( // Config AppShell appShell - .setup(a => a.showInformationMessage(typemoq.It.isAny(), typemoq.It.isValue(yes), typemoq.It.isValue(no))) + .setup((a) => a.showInformationMessage(typemoq.It.isAny(), typemoq.It.isValue(yes), typemoq.It.isValue(no))) .returns(() => Promise.resolve(questionResponse)) .verifiable(bannerShown ? typemoq.Times.once() : typemoq.Times.never()); // Config settings config - .setup(c => + .setup((c) => c.updateSetting( typemoq.It.isValue('dataScience.sendSelectionToInteractiveWindow'), typemoq.It.isAny(), diff --git a/src/test/datascience/testHelpers.tsx b/src/test/datascience/testHelpers.tsx index 4dabc7eeebaa..dfa32dd459fb 100644 --- a/src/test/datascience/testHelpers.tsx +++ b/src/test/datascience/testHelpers.tsx @@ -152,8 +152,8 @@ export function runDoubleTest( ) { // Just run the test twice. Originally mounted twice, but too hard trying to figure out disposing. test(`${name} (interactive)`, async () => - testInnerLoop(name, ioc => mountWebView(ioc, 'interactive'), testFunc, getIOC)); - test(`${name} (native)`, async () => testInnerLoop(name, ioc => mountWebView(ioc, 'native'), testFunc, getIOC)); + testInnerLoop(name, (ioc) => mountWebView(ioc, 'interactive'), testFunc, getIOC)); + test(`${name} (native)`, async () => testInnerLoop(name, (ioc) => mountWebView(ioc, 'native'), testFunc, getIOC)); } export function mountWebView( @@ -398,10 +398,7 @@ export function verifyCellIndex( cellId: string, expectedCellIndex: number ) { - const nativeCell = wrapper - .find(cellId) - .first() - .find('NativeCell'); + const nativeCell = wrapper.find(cellId).first().find('NativeCell'); const secondCell = wrapper.find('NativeCell').at(expectedCellIndex); assert.equal(nativeCell.html(), secondCell.html()); } diff --git a/src/test/datascience/testPersistentStateFactory.ts b/src/test/datascience/testPersistentStateFactory.ts index 53743a202de1..da364bb4a88d 100644 --- a/src/test/datascience/testPersistentStateFactory.ts +++ b/src/test/datascience/testPersistentStateFactory.ts @@ -33,7 +33,7 @@ export class TestPersistentStateFactory implements IPersistentStateFactory { defaultValue?: T | undefined, expiryDurationMs?: number | undefined ): IPersistentState { - if (PrefixesToStore.find(p => key.startsWith(p))) { + if (PrefixesToStore.find((p) => key.startsWith(p))) { return new TestPersistentState(key, defaultValue); } @@ -44,7 +44,7 @@ export class TestPersistentStateFactory implements IPersistentStateFactory { defaultValue?: T | undefined, expiryDurationMs?: number | undefined ): IPersistentState { - if (PrefixesToStore.find(p => key.startsWith(p))) { + if (PrefixesToStore.find((p) => key.startsWith(p))) { return new TestPersistentState(key, defaultValue); } diff --git a/src/test/datascience/uiTests/helpers.ts b/src/test/datascience/uiTests/helpers.ts index eef5c4de8436..eab48b41247e 100644 --- a/src/test/datascience/uiTests/helpers.ts +++ b/src/test/datascience/uiTests/helpers.ts @@ -90,7 +90,7 @@ export class BaseWebUI implements IAsyncDisposable { : undefined; let timesMessageReceived = 0; const dispatchedAction = `DISPATCHED_ACTION_${message}`; - const disposable = this.webServer.onDidReceiveMessage(msg => { + const disposable = this.webServer.onDidReceiveMessage((msg) => { const messageType = msg.type; if (messageType !== message && messageType !== dispatchedAction) { return; diff --git a/src/test/datascience/uiTests/ipywidget.ui.functional.test.ts b/src/test/datascience/uiTests/ipywidget.ui.functional.test.ts index 743e91835428..600ef2fb2ee6 100644 --- a/src/test/datascience/uiTests/ipywidget.ui.functional.test.ts +++ b/src/test/datascience/uiTests/ipywidget.ui.functional.test.ts @@ -31,20 +31,20 @@ const retryIfFail = (fn: () => Promise) => retryIfFailOriginal(fn, wait use(chaiAsPromised); -[false].forEach(useCustomEditorApi => { +[false].forEach((useCustomEditorApi) => { //import { asyncDump } from '../common/asyncDump'; suite(`DataScience IPyWidgets (${useCustomEditorApi ? 'With' : 'Without'} Custom Editor API)`, () => { const disposables: Disposable[] = []; let ioc: DataScienceIocContainer; - suiteSetup(function() { + suiteSetup(function () { // These are UI tests, hence nothing to do with platforms. UseCustomEditor.enabled = useCustomEditorApi; this.timeout(30_000); // UI Tests, need time to start jupyter. this.retries(3); // UI Tests can be flaky. }); let testRecorder: TestRecorder; - setup(async function() { + setup(async function () { const testFileName = path.join( EXTENSION_ROOT_DIR, `src/test/datascience/uiTests/recordedTests/test_log_${sanitize(this.currentTest?.title)}.log` @@ -96,7 +96,7 @@ use(chaiAsPromised); mockedVSCodeNamespaces.window?.reset(); }); let notebookUi: NotebookEditorUI; - teardown(async function() { + teardown(async function () { if (this.test && this.test.state === 'failed') { const imageName = `${sanitize(this.currentTest?.title)}.png`; await notebookUi.captureScreenshot(path.join(os.tmpdir(), 'tmp', 'screenshots', imageName)); @@ -113,7 +113,7 @@ use(chaiAsPromised); delete nb.metadata.kernelspec; } // Clear all output (from previous executions). - nb.cells.forEach(cell => { + nb.cells.forEach((cell) => { if (Array.isArray(cell.outputs)) { cell.outputs = []; } @@ -255,7 +255,7 @@ use(chaiAsPromised); }); }); suite('With real Jupyter', () => { - setup(function() { + setup(function () { if (ioc.mockJupyter) { return this.skip(); } @@ -386,7 +386,7 @@ use(chaiAsPromised); // Confirm slider lable reads `0`. await retryIfFail(async () => { - const sliderValue = await notebookUI.page?.evaluate(ele => ele.innerHTML.trim(), sliderLabel); + const sliderValue = await notebookUI.page?.evaluate((ele) => ele.innerHTML.trim(), sliderLabel); assert.equal(sliderValue || '', '0'); }); @@ -396,7 +396,7 @@ use(chaiAsPromised); const textboxes = await cellOutput.$$('input[type=number]'); assert.equal(textboxes.length, 1); - const value = await notebookUI.page?.evaluate(el => (el as HTMLInputElement).value, textboxes[0]); + const value = await notebookUI.page?.evaluate((el) => (el as HTMLInputElement).value, textboxes[0]); assert.equal(value || '', '0'); return textboxes[0]; @@ -417,7 +417,7 @@ use(chaiAsPromised); // Confirm slider label reads `50`. await retryIfFail(async () => { - const sliderValue = await notebookUI.page?.evaluate(ele => ele.innerHTML.trim(), sliderLabel); + const sliderValue = await notebookUI.page?.evaluate((ele) => ele.innerHTML.trim(), sliderLabel); assert.equal(sliderValue || '', '50'); }); @@ -553,7 +553,7 @@ use(chaiAsPromised); const cellOutput = await notebookUI.getCellOutput(4); const dots = await cellOutput.$$('path.dot'); assert.isAtLeast(dots.length, 1); - const dotHtml = await notebookUI.page?.evaluate(ele => ele.outerHTML, dots[0]); + const dotHtml = await notebookUI.page?.evaluate((ele) => ele.outerHTML, dots[0]); // Confirm color of dot is red. assert.include(dotHtml || '', 'red'); }); @@ -564,7 +564,7 @@ use(chaiAsPromised); const cellOutput = await notebookUI.getCellOutput(4); const dots = await cellOutput.$$('path.dot'); assert.isAtLeast(dots.length, 1); - const dotHtml = await notebookUI.page?.evaluate(ele => ele.outerHTML, dots[0]); + const dotHtml = await notebookUI.page?.evaluate((ele) => ele.outerHTML, dots[0]); // Confirm color of dot is red. assert.include(dotHtml || '', 'yellow'); }); diff --git a/src/test/datascience/uiTests/notebookHelpers.ts b/src/test/datascience/uiTests/notebookHelpers.ts index 7c37034d4fd8..7f756dfb7742 100644 --- a/src/test/datascience/uiTests/notebookHelpers.ts +++ b/src/test/datascience/uiTests/notebookHelpers.ts @@ -62,7 +62,7 @@ function createWebViewPanel(): WebviewPanel { }; mockedVSCodeNamespaces.window - ?.setup(w => + ?.setup((w) => w.createWebviewPanel(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) // tslint:disable-next-line: no-any @@ -90,7 +90,7 @@ export async function openNotebook( // I.e. wait until we open the notebook react ui in browser. const originalWaitForConnection = WebServer.prototype.waitForConnection; const waitForConnection = sinon.stub(WebServer.prototype, 'waitForConnection'); - waitForConnection.callsFake(async function(this: WebServer) { + waitForConnection.callsFake(async function (this: WebServer) { waitForConnection.restore(); // Hook up the message service with the notebook class. // Used to send/receive messages (postOffice) via webSockets in webserver. diff --git a/src/test/datascience/uiTests/notebookUi.ts b/src/test/datascience/uiTests/notebookUi.ts index 8c3efc764563..02c37af64c29 100644 --- a/src/test/datascience/uiTests/notebookUi.ts +++ b/src/test/datascience/uiTests/notebookUi.ts @@ -69,10 +69,10 @@ export class NotebookEditorUI extends BaseWebUI { } private async getCellToolbar(cellIndex: number): Promise> { const cell = await this.getCell(cellIndex); - return cell.$$('.native-editor-celltoolbar-middle').then(items => items[0]); + return cell.$$('.native-editor-celltoolbar-middle').then((items) => items[0]); } private async getToolbarButton(cellIndex: number, button: CellToolbarButton): Promise> { const toolbar = await this.getCellToolbar(cellIndex); - return toolbar.$$('button[role=button]').then(items => items[button]); + return toolbar.$$('button[role=button]').then((items) => items[button]); } } diff --git a/src/test/datascience/uiTests/recorder.ts b/src/test/datascience/uiTests/recorder.ts index 229a287d57b4..6445ba2660a9 100644 --- a/src/test/datascience/uiTests/recorder.ts +++ b/src/test/datascience/uiTests/recorder.ts @@ -76,7 +76,7 @@ export class TestRecorder { this.messages.push({ payload: message, type: 'fromUI' }); } else { // Find the message from the recorded list. - const index = this.messages.findIndex(item => { + const index = this.messages.findIndex((item) => { if (item.type === 'fromUI' && item.payload.type === message.type) { return true; } @@ -88,7 +88,7 @@ export class TestRecorder { } private sendMessageToUIUntilNextUIRequest() { // Now send all messages till the next request. - const nextRequestIndex = this.messages.findIndex(item => item.type === 'fromUI'); + const nextRequestIndex = this.messages.findIndex((item) => item.type === 'fromUI'); if (nextRequestIndex === 0 || this.messages.length === 0) { return; } diff --git a/src/test/datascience/uiTests/webBrowserPanel.ts b/src/test/datascience/uiTests/webBrowserPanel.ts index 4de184110ee9..3e681e09b70d 100644 --- a/src/test/datascience/uiTests/webBrowserPanel.ts +++ b/src/test/datascience/uiTests/webBrowserPanel.ts @@ -40,7 +40,7 @@ export class WebServer implements IWebServer { this.server?.close(); this.io?.close(); this.disposed = true; - this.socketPromise.promise.then(s => s.disconnect()).catch(noop); + this.socketPromise.promise.then((s) => s.disconnect()).catch(noop); } public postMessage(message: {}) { if (this.disposed) { @@ -50,7 +50,7 @@ export class WebServer implements IWebServer { .then(() => { this.socket?.emit('fromServer', message); }) - .catch(ex => { + .catch((ex) => { console.error('Failed to connect to socket', ex); }); } @@ -70,11 +70,11 @@ export class WebServer implements IWebServer { this.app.use(nocache()); this.app.disable('view cache'); - this.io.on('connection', socket => { + this.io.on('connection', (socket) => { // Possible we close browser and reconnect, or hit refresh button. this.socket = socket; this.socketPromise.resolve(socket); - socket.on('fromClient', data => { + socket.on('fromClient', (data) => { this._onDidReceiveMessage.fire(data); }); }); @@ -97,7 +97,7 @@ export class WebServer implements IWebServer { window // tslint:disable-next-line: messages-must-be-localized .showInformationMessage(`Open browser to '${url}'`, 'Copy') - .then(selection => { + .then((selection) => { if (selection === 'Copy') { env.clipboard.writeText(url).then(noop, noop); } @@ -150,7 +150,7 @@ export class WebBrowserPanel implements IWebPanel, IDisposable { }) ); - this.launchServer(this.options.cwd, this.options.rootPath).catch(ex => + this.launchServer(this.options.cwd, this.options.rootPath).catch((ex) => // tslint:disable-next-line: no-console console.error('Failed to start Web Browser Panel', ex) ); @@ -194,7 +194,7 @@ export class WebBrowserPanel implements IWebPanel, IDisposable { const portToUse = isNaN(dsUIPort) ? 0 : dsUIPort; this.server = WebServer.create(); - this.server.onDidReceiveMessage(data => { + this.server.onDidReceiveMessage((data) => { this.options.listener.onMessage(data.type, data.payload); }); diff --git a/src/test/datascience/variableexplorer.functional.test.tsx b/src/test/datascience/variableexplorer.functional.test.tsx index 1d20a8cd47b4..13a6fb99be73 100644 --- a/src/test/datascience/variableexplorer.functional.test.tsx +++ b/src/test/datascience/variableexplorer.functional.test.tsx @@ -26,7 +26,7 @@ suite('DataScience Interactive Window variable explorer tests', () => { let ioc: DataScienceIocContainer; let createdNotebook = false; - suiteSetup(function() { + suiteSetup(function () { // These test require python, so only run with a non-mocked jupyter const isRollingBuild = process.env ? process.env.VSCODE_PYTHON_ROLLING !== undefined : false; if (!isRollingBuild) { @@ -94,7 +94,7 @@ suite('DataScience Interactive Window variable explorer tests', () => { runDoubleTest( 'Variable explorer - Exclude', - async wrapper => { + async (wrapper) => { const basicCode: string = `import numpy as np import pandas as pd value = 'hello world'`; @@ -160,7 +160,7 @@ value = 'hello world'`; runDoubleTest( 'Variable explorer - Update', - async wrapper => { + async (wrapper) => { const basicCode: string = `value = 'hello world'`; const basicCode2: string = `value2 = 'hello world 2'`; @@ -256,7 +256,7 @@ value = 'hello world'`; // Test our display of basic types. We render 8 rows by default so only 8 values per test runDoubleTest( 'Variable explorer - Types A', - async wrapper => { + async (wrapper) => { const basicCode: string = `myList = [1, 2, 3] mySet = set([42]) myDict = {'a': 1}`; @@ -319,7 +319,7 @@ myDict = {'a': 1}`; runDoubleTest( 'Variable explorer - Basic B', - async wrapper => { + async (wrapper) => { const basicCode: string = `import numpy as np import pandas as pd myComplex = complex(1, 1) @@ -453,7 +453,7 @@ Name: 0, dtype: float64`, // Test our limits. Create 1050 items. runDoubleTest( 'Variable explorer - A lot of items', - async wrapper => { + async (wrapper) => { const basicCode: string = `for _i in range(1050): exec("var{}=[{} ** 2 % 17 for _l in range(100000)]".format(_i, _i))`; diff --git a/src/test/debugger/attach.ptvsd.test.ts b/src/test/debugger/attach.ptvsd.test.ts index 0108771025f1..0be2a3801c87 100644 --- a/src/test/debugger/attach.ptvsd.test.ts +++ b/src/test/debugger/attach.ptvsd.test.ts @@ -44,7 +44,7 @@ suite('Debugging - Attach Debugger', () => { let debugClient: DebugClient; let proc: ChildProcess; - setup(async function() { + setup(async function () { if (!IS_MULTI_ROOT_TEST || !TEST_DEBUGGER) { this.skip(); } @@ -82,7 +82,7 @@ suite('Debugging - Attach Debugger', () => { fileToDebug.fileToCommandArgument() ]; proc = spawn(PYTHON_PATH, pythonArgs, { env: env, cwd: path.dirname(fileToDebug) }); - const exited = new Promise(resolve => proc.once('close', resolve)); + const exited = new Promise((resolve) => proc.once('close', resolve)); await sleep(3000); // Send initialize, attach @@ -107,15 +107,17 @@ suite('Debugging - Attach Debugger', () => { debugOptions: [DebugOptions.RedirectOutput] }; const platformService = TypeMoq.Mock.ofType(); - platformService.setup(p => p.isWindows).returns(() => isLocalHostWindows); + platformService.setup((p) => p.isWindows).returns(() => isLocalHostWindows); const serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(IPlatformService, TypeMoq.It.isAny())).returns(() => platformService.object); + serviceContainer + .setup((c) => c.get(IPlatformService, TypeMoq.It.isAny())) + .returns(() => platformService.object); const workspaceService = TypeMoq.Mock.ofType(); const documentManager = TypeMoq.Mock.ofType(); const configurationService = TypeMoq.Mock.ofType(); const experiments = TypeMoq.Mock.ofType(); - experiments.setup(e => e.inExperiment(DebugAdapterNewPtvsd.experiment)).returns(() => true); + experiments.setup((e) => e.inExperiment(DebugAdapterNewPtvsd.experiment)).returns(() => true); const launchResolver = TypeMoq.Mock.ofType>(); const attachResolver = new AttachConfigurationResolver( @@ -166,7 +168,7 @@ suite('Debugging - Attach Debugger', () => { await continueDebugging(debugClient); await exited; } - test('Confirm we are able to attach to a running program', async function() { + test('Confirm we are able to attach to a running program', async function () { // Skipping to get nightly build to pass. Opened this issue: // https://github.com/microsoft/vscode-python/issues/7411 this.skip(); diff --git a/src/test/debugger/capabilities.test.ts b/src/test/debugger/capabilities.test.ts index 3cb1ce624acf..735f37cad683 100644 --- a/src/test/debugger/capabilities.test.ts +++ b/src/test/debugger/capabilities.test.ts @@ -30,11 +30,11 @@ const fileToDebug = path.join( 'remoteDebugger-start-with-ptvsd-nowait.py' ); -suite('Debugging - Capabilities', function() { +suite('Debugging - Capabilities', function () { this.timeout(30000); let disposables: { dispose?: Function; destroy?: Function }[]; let proc: ChildProcess; - setup(function() { + setup(function () { // Skipping to get nightly build to pass. Opened this issue: // https://github.com/microsoft/vscode-python/issues/7411 this.skip(); @@ -44,7 +44,7 @@ suite('Debugging - Capabilities', function() { disposables = []; }); teardown(() => { - disposables.forEach(disposable => { + disposables.forEach((disposable) => { try { disposable.dispose!(); } catch { @@ -81,7 +81,7 @@ suite('Debugging - Capabilities', function() { let initializeResponse = { body: {} } as DebugProtocol.InitializeResponse; - this.sendResponse = resp => (initializeResponse = resp); + this.sendResponse = (resp) => (initializeResponse = resp); this.initializeRequest(initializeResponse, { supportsRunInTerminalRequest: true, adapterID: '' }); return initializeResponse; @@ -112,7 +112,7 @@ suite('Debugging - Capabilities', function() { const protocolParser = new ProtocolParser(); protocolParser.connect(socket!); disposables.push(protocolParser); - const actualResponsePromise = new Promise(resolve => + const actualResponsePromise = new Promise((resolve) => protocolParser.once('response_initialize', resolve) ); protocolWriter.write(socket, initializeRequest); @@ -127,12 +127,12 @@ suite('Debugging - Capabilities', function() { logToFile: false, debugOptions: [] }); - const attached = new Promise(resolve => protocolParser.once('response_attach', resolve)); + const attached = new Promise((resolve) => protocolParser.once('response_attach', resolve)); protocolWriter.write(socket, attachRequest); await attached; const configRequest: DebugProtocol.ConfigurationDoneRequest = createRequest('configurationDone', {}); - const configured = new Promise(resolve => protocolParser.once('response_configurationDone', resolve)); + const configured = new Promise((resolve) => protocolParser.once('response_configurationDone', resolve)); protocolWriter.write(socket, configRequest); await configured; diff --git a/src/test/debugger/common/debugStreamProvider.test.ts b/src/test/debugger/common/debugStreamProvider.test.ts index d25e6d95a17e..112c4f8d285f 100644 --- a/src/test/debugger/common/debugStreamProvider.test.ts +++ b/src/test/debugger/common/debugStreamProvider.test.ts @@ -21,7 +21,7 @@ suite('Debugging - Stream Provider', () => { }); test('Process is returned as is if there is no port number if args', async () => { const mockProcess = { argv: [], env: [], stdin: '1234', stdout: '5678' }; - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => mockProcess); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => mockProcess); const streams = await streamProvider.getInputAndOutputStreams(); expect(streams.input).to.be.equal(mockProcess.stdin); @@ -30,12 +30,12 @@ suite('Debugging - Stream Provider', () => { test('Starts a socketserver on the port provided and returns the client socket', async () => { const port = await getFreePort({ host: 'localhost', port: 3000 }); const mockProcess = { argv: ['node', 'index.js', `--server=${port}`], env: [], stdin: '1234', stdout: '5678' }; - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => mockProcess); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => mockProcess); const streamsPromise = streamProvider.getInputAndOutputStreams(); await sleep(1); - await new Promise(resolve => { + await new Promise((resolve) => { net.connect({ port, host: 'localhost' }, resolve); }); @@ -46,14 +46,14 @@ suite('Debugging - Stream Provider', () => { test('Ensure existence of port is identified', async () => { const port = await getFreePort({ host: 'localhost', port: 3000 }); const mockProcess = { argv: ['node', 'index.js', `--server=${port}`], env: [], stdin: '1234', stdout: '5678' }; - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => mockProcess); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => mockProcess); expect(streamProvider.useDebugSocketStream).to.be.equal(true, 'incorrect'); }); test('Ensure non-existence of port is identified', async () => { const port = await getFreePort({ host: 'localhost', port: 3000 }); const mockProcess = { argv: ['node', 'index.js', `--other=${port}`], env: [], stdin: '1234', stdout: '5678' }; - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => mockProcess); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => mockProcess); expect(streamProvider.useDebugSocketStream).to.not.be.equal(true, 'incorrect'); }); diff --git a/src/test/debugger/common/protocoloLogger.test.ts b/src/test/debugger/common/protocoloLogger.test.ts index dde6bc79abd0..259dd68ca82d 100644 --- a/src/test/debugger/common/protocoloLogger.test.ts +++ b/src/test/debugger/common/protocoloLogger.test.ts @@ -31,12 +31,12 @@ suite('Debugging - Protocol Logger', () => { const logger = TypeMoq.Mock.ofType(); protocolLogger.setup(logger.object); - logger.verify(l => l.verbose('From Client:'), TypeMoq.Times.exactly(3)); - logger.verify(l => l.verbose('To Client:'), TypeMoq.Times.exactly(3)); + logger.verify((l) => l.verbose('From Client:'), TypeMoq.Times.exactly(3)); + logger.verify((l) => l.verbose('To Client:'), TypeMoq.Times.exactly(3)); const expectedLogFileContents = ['A', '1', 'B', 'C', '2', '3']; for (const expectedContent of expectedLogFileContents) { - logger.verify(l => l.verbose(expectedContent), TypeMoq.Times.once()); + logger.verify((l) => l.verbose(expectedContent), TypeMoq.Times.once()); } }); test('Ensure messages are are logged as they arrive', async () => { @@ -57,12 +57,12 @@ suite('Debugging - Protocol Logger', () => { outputStream.write('2'); outputStream.write('3'); - logger.verify(l => l.verbose('From Client:'), TypeMoq.Times.exactly(3)); - logger.verify(l => l.verbose('To Client:'), TypeMoq.Times.exactly(3)); + logger.verify((l) => l.verbose('From Client:'), TypeMoq.Times.exactly(3)); + logger.verify((l) => l.verbose('To Client:'), TypeMoq.Times.exactly(3)); const expectedLogFileContents = ['A', '1', 'B', 'C', '2', '3']; for (const expectedContent of expectedLogFileContents) { - logger.verify(l => l.verbose(expectedContent), TypeMoq.Times.once()); + logger.verify((l) => l.verbose(expectedContent), TypeMoq.Times.once()); } }); test('Ensure nothing is logged once logging is disabled', async () => { @@ -84,17 +84,17 @@ suite('Debugging - Protocol Logger', () => { outputStream.write('2'); outputStream.write('3'); - logger.verify(l => l.verbose('From Client:'), TypeMoq.Times.exactly(1)); - logger.verify(l => l.verbose('To Client:'), TypeMoq.Times.exactly(1)); + logger.verify((l) => l.verbose('From Client:'), TypeMoq.Times.exactly(1)); + logger.verify((l) => l.verbose('To Client:'), TypeMoq.Times.exactly(1)); const expectedLogFileContents = ['A', '1']; const notExpectedLogFileContents = ['B', 'C', '2', '3']; for (const expectedContent of expectedLogFileContents) { - logger.verify(l => l.verbose(expectedContent), TypeMoq.Times.once()); + logger.verify((l) => l.verbose(expectedContent), TypeMoq.Times.once()); } for (const notExpectedContent of notExpectedLogFileContents) { - logger.verify(l => l.verbose(notExpectedContent), TypeMoq.Times.never()); + logger.verify((l) => l.verbose(notExpectedContent), TypeMoq.Times.never()); } }); }); diff --git a/src/test/debugger/common/protocolparser.test.ts b/src/test/debugger/common/protocolparser.test.ts index f478a74c0e64..3c29bcb84948 100644 --- a/src/test/debugger/common/protocolparser.test.ts +++ b/src/test/debugger/common/protocolparser.test.ts @@ -15,13 +15,13 @@ suite('Debugging - Protocol Parser', () => { protocolParser.connect(stream); let messagesDetected = 0; protocolParser.on('data', () => (messagesDetected += 1)); - const requestDetected = new Promise(resolve => { + const requestDetected = new Promise((resolve) => { protocolParser.on('request_initialize', () => resolve(true)); }); - const responseDetected = new Promise(resolve => { + const responseDetected = new Promise((resolve) => { protocolParser.on('response_initialize', () => resolve(true)); }); - const eventDetected = new Promise(resolve => { + const eventDetected = new Promise((resolve) => { protocolParser.on('event_initialized', () => resolve(true)); }); @@ -47,7 +47,7 @@ suite('Debugging - Protocol Parser', () => { protocolParser.connect(stream); let messagesDetected = 0; protocolParser.on('data', () => (messagesDetected += 1)); - const requestDetected = new Promise(resolve => { + const requestDetected = new Promise((resolve) => { protocolParser.on('request_initialize', () => resolve(true)); }); stream.write( diff --git a/src/test/debugger/debugAdapter/debugClients/launcherProvider.unit.test.ts b/src/test/debugger/debugAdapter/debugClients/launcherProvider.unit.test.ts index ff0f84096137..8d6a5dce82b1 100644 --- a/src/test/debugger/debugAdapter/debugClients/launcherProvider.unit.test.ts +++ b/src/test/debugger/debugAdapter/debugClients/launcherProvider.unit.test.ts @@ -33,7 +33,7 @@ suite('Debugger - Launcher Script Provider', () => { } ]; - testsForLaunchProvider.forEach(testParams => { + testsForLaunchProvider.forEach((testParams) => { suite(testParams.testName, async () => { test('Test debug launcher args', async () => { const args = new DebuggerLauncherScriptProvider(testParams.path).getLauncherArgs({ @@ -118,7 +118,7 @@ suite('Debugger - Launcher Script Provider', () => { path: path.join('path', 'to', 'ptvsd_launcher', 'with spaces'), expectedPath: '"path/to/ptvsd_launcher/with spaces"' } - ].forEach(testParams => { + ].forEach((testParams) => { suite(testParams.testName, async () => { test('Test remote debug launcher args (and do not wait for debugger to attach)', async () => { const args = new RemoteDebuggerExternalLauncherScriptProvider(testParams.path).getLauncherArgs({ diff --git a/src/test/debugger/debugAdapter/serviceRegistry.unit.test.ts b/src/test/debugger/debugAdapter/serviceRegistry.unit.test.ts index a6a26854a320..b968e674b2b9 100644 --- a/src/test/debugger/debugAdapter/serviceRegistry.unit.test.ts +++ b/src/test/debugger/debugAdapter/serviceRegistry.unit.test.ts @@ -1,57 +1,57 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect } from 'chai'; -import { anything, instance, mock, verify } from 'ts-mockito'; -import { SocketServer } from '../../../client/common/net/socket/socketServer'; -import { CurrentProcess } from '../../../client/common/process/currentProcess'; -import { BufferDecoder } from '../../../client/common/process/decoder'; -import { IBufferDecoder, IProcessServiceFactory } from '../../../client/common/process/types'; -import { ICurrentProcess, IDisposableRegistry, ISocketServer } from '../../../client/common/types'; -import { DebugStreamProvider } from '../../../client/debugger/debugAdapter/Common/debugStreamProvider'; -import { DebuggerProcessServiceFactory } from '../../../client/debugger/debugAdapter/Common/processServiceFactory'; -import { ProtocolLogger } from '../../../client/debugger/debugAdapter/Common/protocolLogger'; -import { ProtocolParser } from '../../../client/debugger/debugAdapter/Common/protocolParser'; -import { ProtocolMessageWriter } from '../../../client/debugger/debugAdapter/Common/protocolWriter'; -import { initializeIoc, registerTypes } from '../../../client/debugger/debugAdapter/serviceRegistry'; -import { - IDebugStreamProvider, - IProtocolLogger, - IProtocolMessageWriter, - IProtocolParser -} from '../../../client/debugger/debugAdapter/types'; -import { ServiceContainer } from '../../../client/ioc/container'; -import { ServiceManager } from '../../../client/ioc/serviceManager'; -import { IServiceManager } from '../../../client/ioc/types'; - -suite('Debugger debug adapter Service Registry', () => { - let serviceManager: IServiceManager; - - setup(() => { - serviceManager = mock(ServiceManager); - }); - - test('Ensure services are registered', async () => { - registerTypes(instance(serviceManager)); - verify(serviceManager.addSingleton(ICurrentProcess, CurrentProcess)).once(); - verify(serviceManager.addSingletonInstance(IDisposableRegistry, anything())).once(); - verify(serviceManager.addSingleton(IDebugStreamProvider, DebugStreamProvider)).once(); - verify(serviceManager.addSingleton(IProtocolLogger, ProtocolLogger)).once(); - verify(serviceManager.add(IProtocolParser, ProtocolParser)).once(); - verify(serviceManager.addSingleton(ISocketServer, SocketServer)).once(); - verify( - serviceManager.addSingleton(IProtocolMessageWriter, ProtocolMessageWriter) - ).once(); - verify(serviceManager.addSingleton(IBufferDecoder, BufferDecoder)).once(); - verify( - serviceManager.addSingleton(IProcessServiceFactory, DebuggerProcessServiceFactory) - ).once(); - }); - - test('Ensure service container is initialized', async () => { - const serviceContainer = initializeIoc(); - expect(serviceContainer).to.be.instanceOf(ServiceContainer); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { expect } from 'chai'; +import { anything, instance, mock, verify } from 'ts-mockito'; +import { SocketServer } from '../../../client/common/net/socket/socketServer'; +import { CurrentProcess } from '../../../client/common/process/currentProcess'; +import { BufferDecoder } from '../../../client/common/process/decoder'; +import { IBufferDecoder, IProcessServiceFactory } from '../../../client/common/process/types'; +import { ICurrentProcess, IDisposableRegistry, ISocketServer } from '../../../client/common/types'; +import { DebugStreamProvider } from '../../../client/debugger/debugAdapter/Common/debugStreamProvider'; +import { DebuggerProcessServiceFactory } from '../../../client/debugger/debugAdapter/Common/processServiceFactory'; +import { ProtocolLogger } from '../../../client/debugger/debugAdapter/Common/protocolLogger'; +import { ProtocolParser } from '../../../client/debugger/debugAdapter/Common/protocolParser'; +import { ProtocolMessageWriter } from '../../../client/debugger/debugAdapter/Common/protocolWriter'; +import { initializeIoc, registerTypes } from '../../../client/debugger/debugAdapter/serviceRegistry'; +import { + IDebugStreamProvider, + IProtocolLogger, + IProtocolMessageWriter, + IProtocolParser +} from '../../../client/debugger/debugAdapter/types'; +import { ServiceContainer } from '../../../client/ioc/container'; +import { ServiceManager } from '../../../client/ioc/serviceManager'; +import { IServiceManager } from '../../../client/ioc/types'; + +suite('Debugger debug adapter Service Registry', () => { + let serviceManager: IServiceManager; + + setup(() => { + serviceManager = mock(ServiceManager); + }); + + test('Ensure services are registered', async () => { + registerTypes(instance(serviceManager)); + verify(serviceManager.addSingleton(ICurrentProcess, CurrentProcess)).once(); + verify(serviceManager.addSingletonInstance(IDisposableRegistry, anything())).once(); + verify(serviceManager.addSingleton(IDebugStreamProvider, DebugStreamProvider)).once(); + verify(serviceManager.addSingleton(IProtocolLogger, ProtocolLogger)).once(); + verify(serviceManager.add(IProtocolParser, ProtocolParser)).once(); + verify(serviceManager.addSingleton(ISocketServer, SocketServer)).once(); + verify( + serviceManager.addSingleton(IProtocolMessageWriter, ProtocolMessageWriter) + ).once(); + verify(serviceManager.addSingleton(IBufferDecoder, BufferDecoder)).once(); + verify( + serviceManager.addSingleton(IProcessServiceFactory, DebuggerProcessServiceFactory) + ).once(); + }); + + test('Ensure service container is initialized', async () => { + const serviceContainer = initializeIoc(); + expect(serviceContainer).to.be.instanceOf(ServiceContainer); + }); +}); diff --git a/src/test/debugger/envVars.test.ts b/src/test/debugger/envVars.test.ts index 3b69837bda6a..37eedf8d316d 100644 --- a/src/test/debugger/envVars.test.ts +++ b/src/test/debugger/envVars.test.ts @@ -26,7 +26,7 @@ suite('Resolving Environment Variables when Debugging', () => { let pathVariableName: string; let mockProcess: ICurrentProcess; - suiteSetup(async function() { + suiteSetup(async function () { if (!IS_MULTI_ROOT_TEST || !TEST_DEBUGGER) { // tslint:disable-next-line:no-invalid-this return this.skip(); @@ -209,7 +209,7 @@ suite('Resolving Environment Variables when Debugging', () => { Object.keys(mockProcess.env).length, 'Variables is not a subset' ); - Object.keys(mockProcess.env).forEach(key => { + Object.keys(mockProcess.env).forEach((key) => { if (key === pathVariableName || key === 'PYTHONPATH') { return; } @@ -221,7 +221,7 @@ suite('Resolving Environment Variables when Debugging', () => { } } - test('Confirm paths get appended correctly when using json variables and launched in external terminal', async function() { + test('Confirm paths get appended correctly when using json variables and launched in external terminal', async function () { // test is flakey on windows, path separator problems. GH issue #4758 if (isOs(OSType.Windows)) { // tslint:disable-next-line:no-invalid-this @@ -230,7 +230,7 @@ suite('Resolving Environment Variables when Debugging', () => { await testAppendingOfPaths('externalTerminal', 6, false); }); - test('Confirm paths get appended correctly when using json variables and launched in integrated terminal', async function() { + test('Confirm paths get appended correctly when using json variables and launched in integrated terminal', async function () { // test is flakey on windows, path separator problems. GH issue #4758 if (isOs(OSType.Windows)) { // tslint:disable-next-line:no-invalid-this @@ -239,7 +239,7 @@ suite('Resolving Environment Variables when Debugging', () => { await testAppendingOfPaths('integratedTerminal', 6, false); }); - test('Confirm paths get appended correctly when using json variables and launched in debug console', async function() { + test('Confirm paths get appended correctly when using json variables and launched in debug console', async function () { // test is flakey on windows, path separator problems. GH issue #4758 if (isOs(OSType.Windows)) { // tslint:disable-next-line:no-invalid-this diff --git a/src/test/debugger/extension/adapter/logging.unit.test.ts b/src/test/debugger/extension/adapter/logging.unit.test.ts index 8065c2ef856d..cae4d7b19328 100644 --- a/src/test/debugger/extension/adapter/logging.unit.test.ts +++ b/src/test/debugger/extension/adapter/logging.unit.test.ts @@ -106,7 +106,7 @@ suite('Debugging - Session Logging', () => { const logs: string[] = []; when(fsService.createWriteStream(filePath)).thenReturn(instance(writeStream)); - when(writeStream.write(anything())).thenCall(msg => logs.push(msg)); + when(writeStream.write(anything())).thenCall((msg) => logs.push(msg)); const message = new TestMessage(1, 1, 'test-message'); const logger = await loggerFactory.createDebugAdapterTracker(session); diff --git a/src/test/debugger/extension/adapter/outdatedDebuggerPrompt.unit.test.ts b/src/test/debugger/extension/adapter/outdatedDebuggerPrompt.unit.test.ts index 73d738d00ac0..1895c3d9c41e 100644 --- a/src/test/debugger/extension/adapter/outdatedDebuggerPrompt.unit.test.ts +++ b/src/test/debugger/extension/adapter/outdatedDebuggerPrompt.unit.test.ts @@ -191,7 +191,7 @@ suite('Debugging - Outdated Debugger Prompt tests.', () => { body: { category: 'stdout', output: 'ptvsd' } }; - [someRequest, someEvent, someOutputEvent].forEach(message => { + [someRequest, someEvent, someOutputEvent].forEach((message) => { test(`Don't show prompt when in new debugger experiment and debugger telemetry event: ${JSON.stringify( message )}`, async () => { diff --git a/src/test/debugger/extension/banner.unit.test.ts b/src/test/debugger/extension/banner.unit.test.ts index 1f47a1039738..e8ec8824b5b0 100644 --- a/src/test/debugger/extension/banner.unit.test.ts +++ b/src/test/debugger/extension/banner.unit.test.ts @@ -51,7 +51,7 @@ suite('Debugging - Banner', () => { userSelectedState = typemoq.Mock.ofType>(); const factory = typemoq.Mock.ofType(); factory - .setup(f => + .setup((f) => f.createGlobalPersistentState( typemoq.It.isValue(PersistentStateKeys.DebuggerLaunchCounter), typemoq.It.isAny() @@ -59,12 +59,12 @@ suite('Debugging - Banner', () => { ) .returns(() => launchCounterState.object); factory - .setup(f => + .setup((f) => f.createGlobalPersistentState(typemoq.It.isValue(PersistentStateKeys.ShowBanner), typemoq.It.isAny()) ) .returns(() => showBannerState.object); factory - .setup(f => + .setup((f) => f.createGlobalPersistentState( typemoq.It.isValue(PersistentStateKeys.DebuggerLaunchThresholdCounter), typemoq.It.isAny() @@ -72,32 +72,32 @@ suite('Debugging - Banner', () => { ) .returns(() => launchThresholdCounterState.object); factory - .setup(f => + .setup((f) => f.createGlobalPersistentState(typemoq.It.isValue(PersistentStateKeys.UserSelected), typemoq.It.isAny()) ) .returns(() => userSelectedState.object); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IBrowserService))).returns(() => browser.object); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IPersistentStateFactory))).returns(() => factory.object); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IDebugService))).returns(() => debugService.object); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IDisposableRegistry))).returns(() => []); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IApplicationShell))).returns(() => appShell.object); - serviceContainer.setup(s => s.get(typemoq.It.isValue(IRandom))).returns(() => runtime.object); - userSelectedState.setup(s => s.value).returns(() => userSelected); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IBrowserService))).returns(() => browser.object); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IPersistentStateFactory))).returns(() => factory.object); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IDebugService))).returns(() => debugService.object); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IDisposableRegistry))).returns(() => []); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IApplicationShell))).returns(() => appShell.object); + serviceContainer.setup((s) => s.get(typemoq.It.isValue(IRandom))).returns(() => runtime.object); + userSelectedState.setup((s) => s.value).returns(() => userSelected); banner = new DebuggerBanner(serviceContainer.object); }); test('Browser is displayed when launching service along with debugger launch counter', async () => { const debuggerLaunchCounter = 1234; launchCounterState - .setup(l => l.value) + .setup((l) => l.value) .returns(() => debuggerLaunchCounter) .verifiable(typemoq.Times.once()); browser - .setup(b => b.launch(typemoq.It.isValue(`https://www.research.net/r/N7B25RV?n=${debuggerLaunchCounter}`))) + .setup((b) => b.launch(typemoq.It.isValue(`https://www.research.net/r/N7B25RV?n=${debuggerLaunchCounter}`))) .verifiable(typemoq.Times.once()); appShell - .setup(a => + .setup((a) => a.showInformationMessage( typemoq.It.isValue(message), typemoq.It.isValue(yes), @@ -116,15 +116,15 @@ suite('Debugging - Banner', () => { const randomSample = i; const expected = i < 10; test(`users are selected 10% of the time (random: ${i})`, async () => { - showBannerState.setup(s => s.value).returns(() => true); - launchCounterState.setup(l => l.value).returns(() => 10); - launchThresholdCounterState.setup(t => t.value).returns(() => 10); + showBannerState.setup((s) => s.value).returns(() => true); + launchCounterState.setup((l) => l.value).returns(() => 10); + launchThresholdCounterState.setup((t) => t.value).returns(() => 10); userSelected = undefined; runtime - .setup(r => r.getRandomInt(typemoq.It.isValue(0), typemoq.It.isValue(100))) + .setup((r) => r.getRandomInt(typemoq.It.isValue(0), typemoq.It.isValue(100))) .returns(() => randomSample); userSelectedState - .setup(u => u.updateValue(typemoq.It.isValue(expected))) + .setup((u) => u.updateValue(typemoq.It.isValue(expected))) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); @@ -137,15 +137,15 @@ suite('Debugging - Banner', () => { for (const randomSample of [0, 10]) { const expected = randomSample < 10; test(`user selection does not change (random: ${randomSample})`, async () => { - showBannerState.setup(s => s.value).returns(() => true); - launchCounterState.setup(l => l.value).returns(() => 10); - launchThresholdCounterState.setup(t => t.value).returns(() => 10); + showBannerState.setup((s) => s.value).returns(() => true); + launchCounterState.setup((l) => l.value).returns(() => 10); + launchThresholdCounterState.setup((t) => t.value).returns(() => 10); userSelected = undefined; runtime - .setup(r => r.getRandomInt(typemoq.It.isValue(0), typemoq.It.isValue(100))) + .setup((r) => r.getRandomInt(typemoq.It.isValue(0), typemoq.It.isValue(100))) .returns(() => randomSample); userSelectedState - .setup(u => u.updateValue(typemoq.It.isValue(expected))) + .setup((u) => u.updateValue(typemoq.It.isValue(expected))) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); @@ -161,20 +161,20 @@ suite('Debugging - Banner', () => { test('Increment Debugger Launch Counter when debug session starts', async () => { let onDidTerminateDebugSessionCb: (e: DebugSession) => Promise; debugService - .setup(d => d.onDidTerminateDebugSession(typemoq.It.isAny())) - .callback(cb => (onDidTerminateDebugSessionCb = cb)) + .setup((d) => d.onDidTerminateDebugSession(typemoq.It.isAny())) + .callback((cb) => (onDidTerminateDebugSessionCb = cb)) .verifiable(typemoq.Times.once()); const debuggerLaunchCounter = 1234; launchCounterState - .setup(l => l.value) + .setup((l) => l.value) .returns(() => debuggerLaunchCounter) .verifiable(typemoq.Times.atLeastOnce()); launchCounterState - .setup(l => l.updateValue(typemoq.It.isValue(debuggerLaunchCounter + 1))) + .setup((l) => l.updateValue(typemoq.It.isValue(debuggerLaunchCounter + 1))) .verifiable(typemoq.Times.once()); showBannerState - .setup(s => s.value) + .setup((s) => s.value) .returns(() => true) .verifiable(typemoq.Times.atLeastOnce()); @@ -187,18 +187,18 @@ suite('Debugging - Banner', () => { showBannerState.verifyAll(); }); test('Do not Increment Debugger Launch Counter when debug session starts and Banner is disabled', async () => { - debugService.setup(d => d.onDidTerminateDebugSession(typemoq.It.isAny())).verifiable(typemoq.Times.never()); + debugService.setup((d) => d.onDidTerminateDebugSession(typemoq.It.isAny())).verifiable(typemoq.Times.never()); const debuggerLaunchCounter = 1234; launchCounterState - .setup(l => l.value) + .setup((l) => l.value) .returns(() => debuggerLaunchCounter) .verifiable(typemoq.Times.never()); launchCounterState - .setup(l => l.updateValue(typemoq.It.isValue(debuggerLaunchCounter + 1))) + .setup((l) => l.updateValue(typemoq.It.isValue(debuggerLaunchCounter + 1))) .verifiable(typemoq.Times.never()); showBannerState - .setup(s => s.value) + .setup((s) => s.value) .returns(() => false) .verifiable(typemoq.Times.atLeastOnce()); @@ -211,7 +211,7 @@ suite('Debugging - Banner', () => { }); test('shouldShow must return false when Banner is disabled', async () => { showBannerState - .setup(s => s.value) + .setup((s) => s.value) .returns(() => false) .verifiable(typemoq.Times.once()); @@ -221,15 +221,15 @@ suite('Debugging - Banner', () => { }); test('shouldShow must return false when Banner is enabled and debug counter is not same as threshold', async () => { showBannerState - .setup(s => s.value) + .setup((s) => s.value) .returns(() => true) .verifiable(typemoq.Times.once()); launchCounterState - .setup(l => l.value) + .setup((l) => l.value) .returns(() => 1) .verifiable(typemoq.Times.once()); launchThresholdCounterState - .setup(t => t.value) + .setup((t) => t.value) .returns(() => 10) .verifiable(typemoq.Times.atLeastOnce()); @@ -241,15 +241,15 @@ suite('Debugging - Banner', () => { }); test('shouldShow must return true when Banner is enabled and debug counter is same as threshold', async () => { showBannerState - .setup(s => s.value) + .setup((s) => s.value) .returns(() => true) .verifiable(typemoq.Times.once()); launchCounterState - .setup(l => l.value) + .setup((l) => l.value) .returns(() => 10) .verifiable(typemoq.Times.once()); launchThresholdCounterState - .setup(t => t.value) + .setup((t) => t.value) .returns(() => 10) .verifiable(typemoq.Times.atLeastOnce()); @@ -264,28 +264,28 @@ suite('Debugging - Banner', () => { const currentLaunchCounter = 50; debugService - .setup(d => d.onDidTerminateDebugSession(typemoq.It.isAny())) - .callback(cb => (onDidTerminateDebugSessionCb = cb)) + .setup((d) => d.onDidTerminateDebugSession(typemoq.It.isAny())) + .callback((cb) => (onDidTerminateDebugSessionCb = cb)) .verifiable(typemoq.Times.atLeastOnce()); showBannerState - .setup(s => s.value) + .setup((s) => s.value) .returns(() => true) .verifiable(typemoq.Times.atLeastOnce()); launchCounterState - .setup(l => l.value) + .setup((l) => l.value) .returns(() => currentLaunchCounter) .verifiable(typemoq.Times.atLeastOnce()); launchThresholdCounterState - .setup(t => t.value) + .setup((t) => t.value) .returns(() => 10) .verifiable(typemoq.Times.atLeastOnce()); launchCounterState - .setup(l => l.updateValue(typemoq.It.isValue(currentLaunchCounter + 1))) + .setup((l) => l.updateValue(typemoq.It.isValue(currentLaunchCounter + 1))) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.atLeastOnce()); appShell - .setup(a => + .setup((a) => a.showInformationMessage( typemoq.It.isValue(message), typemoq.It.isValue(yes), @@ -307,27 +307,27 @@ suite('Debugging - Banner', () => { let currentLaunchCounter = 50; debugService - .setup(d => d.onDidTerminateDebugSession(typemoq.It.isAny())) - .callback(cb => (onDidTerminateDebugSessionCb = cb)) + .setup((d) => d.onDidTerminateDebugSession(typemoq.It.isAny())) + .callback((cb) => (onDidTerminateDebugSessionCb = cb)) .verifiable(typemoq.Times.atLeastOnce()); showBannerState - .setup(s => s.value) + .setup((s) => s.value) .returns(() => true) .verifiable(typemoq.Times.atLeastOnce()); launchCounterState - .setup(l => l.value) + .setup((l) => l.value) .returns(() => currentLaunchCounter) .verifiable(typemoq.Times.atLeastOnce()); launchThresholdCounterState - .setup(t => t.value) + .setup((t) => t.value) .returns(() => 10) .verifiable(typemoq.Times.atLeastOnce()); launchCounterState - .setup(l => l.updateValue(typemoq.It.isAny())) + .setup((l) => l.updateValue(typemoq.It.isAny())) .callback(() => (currentLaunchCounter = currentLaunchCounter + 1)); appShell - .setup(a => + .setup((a) => a.showInformationMessage( typemoq.It.isValue(message), typemoq.It.isValue(yes), @@ -350,7 +350,7 @@ suite('Debugging - Banner', () => { expect(currentLaunchCounter).to.be.equal(54); }); test("Disabling banner must store value of 'false' in global store", async () => { - showBannerState.setup(s => s.updateValue(typemoq.It.isValue(false))).verifiable(typemoq.Times.once()); + showBannerState.setup((s) => s.updateValue(typemoq.It.isValue(false))).verifiable(typemoq.Times.once()); await banner.disable(); diff --git a/src/test/debugger/extension/configuration/debugConfigurationService.unit.test.ts b/src/test/debugger/extension/configuration/debugConfigurationService.unit.test.ts index 6fa9f12f3013..c945f55c4f4d 100644 --- a/src/test/debugger/extension/configuration/debugConfigurationService.unit.test.ts +++ b/src/test/debugger/extension/configuration/debugConfigurationService.unit.test.ts @@ -53,13 +53,13 @@ suite('Debugging - Configuration Service', () => { const expectedConfig = { yay: 1 }; attachResolver - .setup(a => + .setup((a) => a.resolveDebugConfiguration(typemoq.It.isValue(folder), typemoq.It.isValue(config), typemoq.It.isAny()) ) .returns(() => Promise.resolve(expectedConfig as any)) .verifiable(typemoq.Times.once()); launchResolver - .setup(a => a.resolveDebugConfiguration(typemoq.It.isAny(), typemoq.It.isAny(), typemoq.It.isAny())) + .setup((a) => a.resolveDebugConfiguration(typemoq.It.isAny(), typemoq.It.isAny(), typemoq.It.isAny())) .verifiable(typemoq.Times.never()); const resolvedConfig = await configService.resolveDebugConfiguration(folder, config as any); @@ -68,13 +68,13 @@ suite('Debugging - Configuration Service', () => { attachResolver.verifyAll(); launchResolver.verifyAll(); }); - [{ request: 'launch' }, { request: undefined }].forEach(config => { + [{ request: 'launch' }, { request: undefined }].forEach((config) => { test(`Should use launch resolver when passing launch config with request=${config.request}`, async () => { const folder = { name: '1', index: 0, uri: Uri.parse('1234') }; const expectedConfig = { yay: 1 }; launchResolver - .setup(a => + .setup((a) => a.resolveDebugConfiguration( typemoq.It.isValue(folder), typemoq.It.isValue((config as any) as LaunchRequestArguments), @@ -84,7 +84,7 @@ suite('Debugging - Configuration Service', () => { .returns(() => Promise.resolve(expectedConfig as any)) .verifiable(typemoq.Times.once()); attachResolver - .setup(a => a.resolveDebugConfiguration(typemoq.It.isAny(), typemoq.It.isAny(), typemoq.It.isAny())) + .setup((a) => a.resolveDebugConfiguration(typemoq.It.isAny(), typemoq.It.isAny(), typemoq.It.isAny())) .verifiable(typemoq.Times.never()); const resolvedConfig = await configService.resolveDebugConfiguration(folder, config as any); @@ -99,7 +99,7 @@ suite('Debugging - Configuration Service', () => { const state = ({ configs: [], folder: {}, token: undefined } as any) as DebugConfigurationState; const multiStepInput = typemoq.Mock.ofType>(); multiStepInput - .setup(i => i.showQuickPick(typemoq.It.isAny())) + .setup((i) => i.showQuickPick(typemoq.It.isAny())) .returns(() => Promise.resolve(undefined as any)) .verifiable(typemoq.Times.once()); @@ -112,7 +112,7 @@ suite('Debugging - Configuration Service', () => { const state = ({ configs: [1, 2, 3], folder: {}, token: undefined } as any) as DebugConfigurationState; const multiStepInput = typemoq.Mock.ofType>(); multiStepInput - .setup(i => i.showQuickPick(typemoq.It.isAny())) + .setup((i) => i.showQuickPick(typemoq.It.isAny())) .returns(() => Promise.resolve(undefined as any)) .verifiable(typemoq.Times.once()); @@ -130,7 +130,7 @@ suite('Debugging - Configuration Service', () => { } }; multiStepFactory - .setup(f => f.create()) + .setup((f) => f.create()) .returns(() => multiStepInput as any) .verifiable(typemoq.Times.once()); configService.pickDebugConfiguration = (_, state) => { @@ -148,7 +148,7 @@ suite('Debugging - Configuration Service', () => { }; const folder = { name: '1', index: 0, uri: Uri.parse('1234') }; multiStepFactory - .setup(f => f.create()) + .setup((f) => f.create()) .returns(() => multiStepInput as any) .verifiable(typemoq.Times.once()); const config = await configService.resolveDebugConfiguration(folder, {} as any); diff --git a/src/test/debugger/extension/configuration/launch.json/completionProvider.unit.test.ts b/src/test/debugger/extension/configuration/launch.json/completionProvider.unit.test.ts index 43adc5aea207..3108676e8c5f 100644 --- a/src/test/debugger/extension/configuration/launch.json/completionProvider.unit.test.ts +++ b/src/test/debugger/extension/configuration/launch.json/completionProvider.unit.test.ts @@ -41,18 +41,18 @@ suite('Debugging - launch.json Completion Provider', () => { test('Cannot provide completions for non launch.json files', () => { const document = typemoq.Mock.ofType(); const position = new Position(0, 0); - document.setup(doc => doc.uri).returns(() => Uri.file(__filename)); + document.setup((doc) => doc.uri).returns(() => Uri.file(__filename)); assert.equal(completionProvider.canProvideCompletions(document.object, position), false); document.reset(); - document.setup(doc => doc.uri).returns(() => Uri.file('settings.json')); + document.setup((doc) => doc.uri).returns(() => Uri.file('settings.json')); assert.equal(completionProvider.canProvideCompletions(document.object, position), false); }); function testCanProvideCompletions(position: Position, offset: number, json: string, expectedValue: boolean) { const document = typemoq.Mock.ofType(); - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => json); - document.setup(doc => doc.uri).returns(() => Uri.file('launch.json')); - document.setup(doc => doc.offsetAt(typemoq.It.isAny())).returns(() => offset); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => json); + document.setup((doc) => doc.uri).returns(() => Uri.file('launch.json')); + document.setup((doc) => doc.offsetAt(typemoq.It.isAny())).returns(() => offset); const canProvideCompletions = completionProvider.canProvideCompletions(document.object, position); assert.equal(canProvideCompletions, expectedValue); } @@ -83,7 +83,7 @@ suite('Debugging - launch.json Completion Provider', () => { }); test('No Completions for non launch.json', async () => { const document = typemoq.Mock.ofType(); - document.setup(doc => doc.uri).returns(() => Uri.file('settings.json')); + document.setup((doc) => doc.uri).returns(() => Uri.file('settings.json')); const token = new CancellationTokenSource().token; const position = new Position(0, 0); @@ -93,7 +93,7 @@ suite('Debugging - launch.json Completion Provider', () => { }); test('No Completions for files ending with launch.json', async () => { const document = typemoq.Mock.ofType(); - document.setup(doc => doc.uri).returns(() => Uri.file('x-launch.json')); + document.setup((doc) => doc.uri).returns(() => Uri.file('x-launch.json')); const token = new CancellationTokenSource().token; const position = new Position(0, 0); @@ -110,9 +110,9 @@ suite('Debugging - launch.json Completion Provider', () => { }`; const document = typemoq.Mock.ofType(); - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => json); - document.setup(doc => doc.uri).returns(() => Uri.file('launch.json')); - document.setup(doc => doc.offsetAt(typemoq.It.isAny())).returns(() => json.indexOf('# Cursor Position')); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => json); + document.setup((doc) => doc.uri).returns(() => Uri.file('launch.json')); + document.setup((doc) => doc.offsetAt(typemoq.It.isAny())).returns(() => json.indexOf('# Cursor Position')); const position = new Position(0, 0); const token = new CancellationTokenSource().token; diff --git a/src/test/debugger/extension/configuration/launch.json/updaterServer.unit.test.ts b/src/test/debugger/extension/configuration/launch.json/updaterServer.unit.test.ts index 85405b4c3f68..6b62a3962f19 100644 --- a/src/test/debugger/extension/configuration/launch.json/updaterServer.unit.test.ts +++ b/src/test/debugger/extension/configuration/launch.json/updaterServer.unit.test.ts @@ -70,7 +70,7 @@ suite('Debugging - launch.json Updater Service', () => { version: '', configurations: [] }; - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => JSON.stringify(config)); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => JSON.stringify(config)); const isEmpty = helper.isConfigurationArrayEmpty(document.object); assert.equal(isEmpty, true); @@ -87,7 +87,7 @@ suite('Debugging - launch.json Updater Service', () => { } ] }; - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => JSON.stringify(config)); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => JSON.stringify(config)); const isEmpty = helper.isConfigurationArrayEmpty(document.object); assert.equal(isEmpty, false); @@ -104,8 +104,8 @@ suite('Debugging - launch.json Updater Service', () => { } ] }; - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => JSON.stringify(config)); - document.setup(doc => doc.offsetAt(typemoq.It.isAny())).returns(() => 10); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => JSON.stringify(config)); + document.setup((doc) => doc.offsetAt(typemoq.It.isAny())).returns(() => 10); const cursorPosition = helper.getCursorPositionInConfigurationsArray(document.object, new Position(0, 0)); assert.equal(cursorPosition, undefined); @@ -118,8 +118,8 @@ suite('Debugging - launch.json Updater Service', () => { # Cursor Position ] }`; - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => json); - document.setup(doc => doc.offsetAt(typemoq.It.isAny())).returns(() => json.indexOf('#')); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => json); + document.setup((doc) => doc.offsetAt(typemoq.It.isAny())).returns(() => json.indexOf('#')); const cursorPosition = helper.getCursorPositionInConfigurationsArray(document.object, new Position(0, 0)); assert.equal(cursorPosition, 'InsideEmptyArray'); @@ -134,8 +134,8 @@ suite('Debugging - launch.json Updater Service', () => { } ] }`; - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => json); - document.setup(doc => doc.offsetAt(typemoq.It.isAny())).returns(() => json.lastIndexOf('{') - 1); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => json); + document.setup((doc) => doc.offsetAt(typemoq.It.isAny())).returns(() => json.lastIndexOf('{') - 1); const cursorPosition = helper.getCursorPositionInConfigurationsArray(document.object, new Position(0, 0)); assert.equal(cursorPosition, 'BeforeItem'); @@ -152,8 +152,8 @@ suite('Debugging - launch.json Updater Service', () => { } ] }`; - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => json); - document.setup(doc => doc.offsetAt(typemoq.It.isAny())).returns(() => json.indexOf(',{') + 1); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => json); + document.setup((doc) => doc.offsetAt(typemoq.It.isAny())).returns(() => json.indexOf(',{') + 1); const cursorPosition = helper.getCursorPositionInConfigurationsArray(document.object, new Position(0, 0)); assert.equal(cursorPosition, 'BeforeItem'); @@ -167,8 +167,8 @@ suite('Debugging - launch.json Updater Service', () => { "name":"wow" }] }`; - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => json); - document.setup(doc => doc.offsetAt(typemoq.It.isAny())).returns(() => json.lastIndexOf('}]') + 1); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => json); + document.setup((doc) => doc.offsetAt(typemoq.It.isAny())).returns(() => json.lastIndexOf('}]') + 1); const cursorPosition = helper.getCursorPositionInConfigurationsArray(document.object, new Position(0, 0)); assert.equal(cursorPosition, 'AfterItem'); @@ -185,8 +185,8 @@ suite('Debugging - launch.json Updater Service', () => { } ] }`; - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => json); - document.setup(doc => doc.offsetAt(typemoq.It.isAny())).returns(() => json.indexOf('},') + 1); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => json); + document.setup((doc) => doc.offsetAt(typemoq.It.isAny())).returns(() => json.indexOf('},') + 1); const cursorPosition = helper.getCursorPositionInConfigurationsArray(document.object, new Position(0, 0)); assert.equal(cursorPosition, 'AfterItem'); @@ -236,8 +236,8 @@ suite('Debugging - launch.json Updater Service', () => { }`; const config = {} as any; const document = typemoq.Mock.ofType(); - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => json); - document.setup(doc => doc.offsetAt(typemoq.It.isAny())).returns(() => json.indexOf('},') + 1); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => json); + document.setup((doc) => doc.offsetAt(typemoq.It.isAny())).returns(() => json.indexOf('},') + 1); when(documentManager.applyEdit(anything())).thenResolve(); when(commandManager.executeCommand('editor.action.formatDocument')).thenResolve(); @@ -268,7 +268,7 @@ suite('Debugging - launch.json Updater Service', () => { const token = new CancellationTokenSource().token; const textEditor = typemoq.Mock.ofType(); textEditor - .setup(t => t.document) + .setup((t) => t.document) .returns(() => 'x' as any) .verifiable(typemoq.Times.atLeastOnce()); when(documentManager.activeTextEditor).thenReturn(textEditor.object); @@ -296,11 +296,11 @@ suite('Debugging - launch.json Updater Service', () => { const folderUri = Uri.file('Folder Uri'); const folder = { name: '', index: 0, uri: folderUri }; document - .setup(doc => doc.uri) + .setup((doc) => doc.uri) .returns(() => docUri) .verifiable(typemoq.Times.atLeastOnce()); textEditor - .setup(t => t.document) + .setup((t) => t.document) .returns(() => document.object) .verifiable(typemoq.Times.atLeastOnce()); when(documentManager.activeTextEditor).thenReturn(textEditor.object); @@ -330,11 +330,11 @@ suite('Debugging - launch.json Updater Service', () => { const folderUri = Uri.file('Folder Uri'); const folder = { name: '', index: 0, uri: folderUri }; document - .setup(doc => doc.uri) + .setup((doc) => doc.uri) .returns(() => docUri) .verifiable(typemoq.Times.atLeastOnce()); textEditor - .setup(t => t.document) + .setup((t) => t.document) .returns(() => document.object) .verifiable(typemoq.Times.atLeastOnce()); when(documentManager.activeTextEditor).thenReturn(textEditor.object); @@ -364,11 +364,11 @@ suite('Debugging - launch.json Updater Service', () => { const folderUri = Uri.file('Folder Uri'); const folder = { name: '', index: 0, uri: folderUri }; document - .setup(doc => doc.uri) + .setup((doc) => doc.uri) .returns(() => docUri) .verifiable(typemoq.Times.atLeastOnce()); textEditor - .setup(t => t.document) + .setup((t) => t.document) .returns(() => document.object) .verifiable(typemoq.Times.atLeastOnce()); when(documentManager.activeTextEditor).thenReturn(textEditor.object); @@ -393,11 +393,11 @@ suite('Debugging - launch.json Updater Service', () => { const document = typemoq.Mock.ofType(); const position = new Position(1, 0); document - .setup(doc => doc.lineAt(1)) + .setup((doc) => doc.lineAt(1)) .returns(() => ({ range: new Range(1, 0, 1, 1) } as any)) .verifiable(typemoq.Times.atLeastOnce()); document - .setup(doc => doc.getText(typemoq.It.isAny())) + .setup((doc) => doc.getText(typemoq.It.isAny())) .returns(() => '') .verifiable(typemoq.Times.atLeastOnce()); @@ -411,11 +411,11 @@ suite('Debugging - launch.json Updater Service', () => { const document = typemoq.Mock.ofType(); const position = new Position(2, 2); document - .setup(doc => doc.lineAt(2)) + .setup((doc) => doc.lineAt(2)) .returns(() => ({ range: new Range(2, 0, 1, 5) } as any)) .verifiable(typemoq.Times.atLeastOnce()); document - .setup(doc => doc.getText(typemoq.It.isAny())) + .setup((doc) => doc.getText(typemoq.It.isAny())) .returns(() => 'Hello') .verifiable(typemoq.Times.atLeastOnce()); @@ -429,11 +429,11 @@ suite('Debugging - launch.json Updater Service', () => { const document = typemoq.Mock.ofType(); const position = new Position(2, 2); document - .setup(doc => doc.lineAt(2)) + .setup((doc) => doc.lineAt(2)) .returns(() => ({ range: new Range(2, 0, 2, 3) } as any)) .verifiable(typemoq.Times.atLeastOnce()); document - .setup(doc => doc.getText(typemoq.It.isAny())) + .setup((doc) => doc.getText(typemoq.It.isAny())) .returns(() => '}, ') .verifiable(typemoq.Times.atLeastOnce()); @@ -447,15 +447,15 @@ suite('Debugging - launch.json Updater Service', () => { const document = typemoq.Mock.ofType(); const position = new Position(2, 2); document - .setup(doc => doc.lineAt(1)) + .setup((doc) => doc.lineAt(1)) .returns(() => ({ range: new Range(1, 0, 1, 3), text: '}, ' } as any)) .verifiable(typemoq.Times.atLeastOnce()); document - .setup(doc => doc.lineAt(2)) + .setup((doc) => doc.lineAt(2)) .returns(() => ({ range: new Range(2, 0, 2, 3), text: ' ' } as any)) .verifiable(typemoq.Times.atLeastOnce()); document - .setup(doc => doc.getText(typemoq.It.isAny())) + .setup((doc) => doc.getText(typemoq.It.isAny())) .returns(() => ' ') .verifiable(typemoq.Times.atLeastOnce()); @@ -469,15 +469,15 @@ suite('Debugging - launch.json Updater Service', () => { const document = typemoq.Mock.ofType(); const position = new Position(2, 2); document - .setup(doc => doc.lineAt(1)) + .setup((doc) => doc.lineAt(1)) .returns(() => ({ range: new Range(1, 0, 1, 3), text: '} ' } as any)) .verifiable(typemoq.Times.atLeastOnce()); document - .setup(doc => doc.lineAt(2)) + .setup((doc) => doc.lineAt(2)) .returns(() => ({ range: new Range(2, 0, 2, 3), text: ' ' } as any)) .verifiable(typemoq.Times.atLeastOnce()); document - .setup(doc => doc.getText(typemoq.It.isAny())) + .setup((doc) => doc.getText(typemoq.It.isAny())) .returns(() => ' ') .verifiable(typemoq.Times.atLeastOnce()); diff --git a/src/test/debugger/extension/configuration/providers/providerFactory.unit.test.ts b/src/test/debugger/extension/configuration/providers/providerFactory.unit.test.ts index feba5a60b56b..668439d09940 100644 --- a/src/test/debugger/extension/configuration/providers/providerFactory.unit.test.ts +++ b/src/test/debugger/extension/configuration/providers/providerFactory.unit.test.ts @@ -16,7 +16,7 @@ suite('Debugging - Configuration Provider Factory', () => { let factory: IDebugConfigurationProviderFactory; setup(() => { mappedProviders = new Map(); - getNamesAndValues(DebugConfigurationType).forEach(item => { + getNamesAndValues(DebugConfigurationType).forEach((item) => { mappedProviders.set(item.value, (item.value as any) as IDebugConfigurationProvider); }); factory = new DebugConfigurationProviderFactory( @@ -29,7 +29,7 @@ suite('Debugging - Configuration Provider Factory', () => { mappedProviders.get(DebugConfigurationType.pidAttach)! ); }); - getNamesAndValues(DebugConfigurationType).forEach(item => { + getNamesAndValues(DebugConfigurationType).forEach((item) => { test(`Configuration Provider for ${item.name}`, () => { const provider = factory.create(item.value); expect(provider).to.equal(mappedProviders.get(item.value)); diff --git a/src/test/debugger/extension/configuration/resolvers/attach.unit.test.ts b/src/test/debugger/extension/configuration/resolvers/attach.unit.test.ts index ad4b855768d9..a36aaf374a25 100644 --- a/src/test/debugger/extension/configuration/resolvers/attach.unit.test.ts +++ b/src/test/debugger/extension/configuration/resolvers/attach.unit.test.ts @@ -55,13 +55,13 @@ getInfoPerOS().forEach(([osName, osType, path]) => { configurationService = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService))) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) .returns(() => platformService.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); setUpOSMocks(osType, platformService); documentManager = TypeMoq.Mock.ofType(); experimentsManager = TypeMoq.Mock.ofType(); - experimentsManager.setup(e => e.inExperiment(DebugAdapterNewPtvsd.experiment)).returns(() => true); + experimentsManager.setup((e) => e.inExperiment(DebugAdapterNewPtvsd.experiment)).returns(() => true); debugProvider = new AttachConfigurationResolver( workspaceService.object, @@ -73,29 +73,29 @@ getInfoPerOS().forEach(([osName, osType, path]) => { }); function createMoqWorkspaceFolder(folderPath: string) { const folder = TypeMoq.Mock.ofType(); - folder.setup(f => f.uri).returns(() => Uri.file(folderPath)); + folder.setup((f) => f.uri).returns(() => Uri.file(folderPath)); return folder.object; } function setupActiveEditor(fileName: string | undefined, languageId: string) { if (fileName) { const textEditor = TypeMoq.Mock.ofType(); const document = TypeMoq.Mock.ofType(); - document.setup(d => d.languageId).returns(() => languageId); - document.setup(d => d.fileName).returns(() => fileName); - textEditor.setup(t => t.document).returns(() => document.object); - documentManager.setup(d => d.activeTextEditor).returns(() => textEditor.object); + document.setup((d) => d.languageId).returns(() => languageId); + document.setup((d) => d.fileName).returns(() => fileName); + textEditor.setup((t) => t.document).returns(() => document.object); + documentManager.setup((d) => d.activeTextEditor).returns(() => textEditor.object); } else { - documentManager.setup(d => d.activeTextEditor).returns(() => undefined); + documentManager.setup((d) => d.activeTextEditor).returns(() => undefined); } serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IDocumentManager))) + .setup((c) => c.get(TypeMoq.It.isValue(IDocumentManager))) .returns(() => documentManager.object); } function setupWorkspaces(folders: string[]) { const workspaceFolders = folders.map(createMoqWorkspaceFolder); - workspaceService.setup(w => w.workspaceFolders).returns(() => workspaceFolders); + workspaceService.setup((w) => w.workspaceFolders).returns(() => workspaceFolders); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); } test('Defaults should be returned when an empty object is passed with a Workspace Folder and active file', async () => { @@ -110,9 +110,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(Object.keys(debugConfig!)).to.have.lengthOf.above(3); expect(debugConfig).to.have.property('request', 'attach'); - expect(debugConfig) - .to.have.property('debugOptions') - .deep.equal(debugOptionsAvailable); + expect(debugConfig).to.have.property('debugOptions').deep.equal(debugOptionsAvailable); }); test('Defaults should be returned when an empty object is passed without Workspace Folder, no workspaces and active file', async () => { const pythonFile = 'xyz.py'; @@ -126,9 +124,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(Object.keys(debugConfig!)).to.have.lengthOf.least(3); expect(debugConfig).to.have.property('request', 'attach'); - expect(debugConfig) - .to.have.property('debugOptions') - .deep.equal(debugOptionsAvailable); + expect(debugConfig).to.have.property('debugOptions').deep.equal(debugOptionsAvailable); expect(debugConfig).to.have.property('host', 'localhost'); }); test('Defaults should be returned when an empty object is passed without Workspace Folder, no workspaces and no active file', async () => { @@ -141,9 +137,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(Object.keys(debugConfig!)).to.have.lengthOf.least(3); expect(debugConfig).to.have.property('request', 'attach'); - expect(debugConfig) - .to.have.property('debugOptions') - .deep.equal(debugOptionsAvailable); + expect(debugConfig).to.have.property('debugOptions').deep.equal(debugOptionsAvailable); expect(debugConfig).to.have.property('host', 'localhost'); }); test('Defaults should be returned when an empty object is passed without Workspace Folder, no workspaces and non python file', async () => { @@ -158,9 +152,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(Object.keys(debugConfig!)).to.have.lengthOf.least(3); expect(debugConfig).to.have.property('request', 'attach'); - expect(debugConfig) - .to.have.property('debugOptions') - .deep.equal(debugOptionsAvailable); + expect(debugConfig).to.have.property('debugOptions').deep.equal(debugOptionsAvailable); expect(debugConfig).to.not.have.property('localRoot'); expect(debugConfig).to.have.property('host', 'localhost'); }); @@ -176,9 +168,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(Object.keys(debugConfig!)).to.have.lengthOf.least(3); expect(debugConfig).to.have.property('request', 'attach'); - expect(debugConfig) - .to.have.property('debugOptions') - .deep.equal(debugOptionsAvailable); + expect(debugConfig).to.have.property('debugOptions').deep.equal(debugOptionsAvailable); expect(debugConfig).to.have.property('host', 'localhost'); }); test('Default host should not be added if connect is available.', async () => { @@ -222,7 +212,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(debugConfig).to.have.property('localRoot', localRoot); }); - ['localhost', 'LOCALHOST', '127.0.0.1', '::1'].forEach(host => { + ['localhost', 'LOCALHOST', '127.0.0.1', '::1'].forEach((host) => { test(`Ensure path mappings are automatically added when host is '${host}'`, async () => { const activeFile = 'xyz.py'; const workspaceFolder = createMoqWorkspaceFolder(__dirname); @@ -243,7 +233,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(pathMappings![0].localRoot).to.be.equal(workspaceFolder.uri.fsPath); expect(pathMappings![0].remoteRoot).to.be.equal(workspaceFolder.uri.fsPath); }); - test(`Ensure drive letter is lower cased for local path mappings on Windows when host is '${host}'`, async function() { + test(`Ensure drive letter is lower cased for local path mappings on Windows when host is '${host}'`, async function () { if (getOSType() !== OSType.Windows || osType !== OSType.Windows) { return this.skip(); } @@ -265,7 +255,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(pathMappings![0].localRoot).to.be.equal(expected); expect(pathMappings![0].remoteRoot).to.be.equal(workspaceFolder.uri.fsPath); }); - test(`Ensure drive letter is not lower cased for local path mappings on non-Windows when host is '${host}'`, async function() { + test(`Ensure drive letter is not lower cased for local path mappings on non-Windows when host is '${host}'`, async function () { if (getOSType() === OSType.Windows || osType === OSType.Windows) { return this.skip(); } @@ -287,7 +277,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(pathMappings![0].localRoot).to.be.equal(expected); expect(pathMappings![0].remoteRoot).to.be.equal(workspaceFolder.uri.fsPath); }); - test(`Ensure drive letter is lower cased for local path mappings on Windows when host is '${host}' and with existing path mappings`, async function() { + test(`Ensure drive letter is lower cased for local path mappings on Windows when host is '${host}' and with existing path mappings`, async function () { if (getOSType() !== OSType.Windows || osType !== OSType.Windows) { return this.skip(); } @@ -313,7 +303,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(pathMappings![0].localRoot).to.be.equal(expected); expect(pathMappings![0].remoteRoot).to.be.equal('/app/'); }); - test(`Ensure drive letter is not lower cased for local path mappings on non-Windows when host is '${host}' and with existing path mappings`, async function() { + test(`Ensure drive letter is not lower cased for local path mappings on non-Windows when host is '${host}' and with existing path mappings`, async function () { if (getOSType() === OSType.Windows || osType === OSType.Windows) { return this.skip(); } @@ -358,7 +348,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { expect(pathMappings![0].remoteRoot).to.be.equal(workspaceFolder.uri.fsPath); }); }); - ['192.168.1.123', 'don.debugger.com'].forEach(host => { + ['192.168.1.123', 'don.debugger.com'].forEach((host) => { test(`Ensure path mappings are not automatically added when host is '${host}'`, async () => { const activeFile = 'xyz.py'; const workspaceFolder = createMoqWorkspaceFolder(__dirname); @@ -458,9 +448,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { request: 'attach' } as any) as DebugConfiguration); - expect(debugConfig) - .to.have.property('debugOptions') - .to.be.deep.equal(expectedDebugOptions); + expect(debugConfig).to.have.property('debugOptions').to.be.deep.equal(expectedDebugOptions); }); const testsForJustMyCode = [ @@ -519,7 +507,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { const debugOptions = debugOptionsAvailable.slice().concat(DebugOptions.Jinja, DebugOptions.Sudo); - testsForJustMyCode.forEach(async testParams => { + testsForJustMyCode.forEach(async (testParams) => { const debugConfig = await debugProvider.resolveDebugConfiguration!(workspaceFolder, ({ debugOptions, request: 'attach', @@ -537,7 +525,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { const defaultWorkspace = path.join('usr', 'desktop'); setupWorkspaces([defaultWorkspace]); experimentsManager.reset(); - experimentsManager.setup(e => e.inExperiment(DebugAdapterNewPtvsd.experiment)).returns(() => false); + experimentsManager.setup((e) => e.inExperiment(DebugAdapterNewPtvsd.experiment)).returns(() => false); const promise = debugProvider.resolveDebugConfiguration!(workspaceFolder, ({ request: 'attach' @@ -552,7 +540,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { const defaultWorkspace = path.join('usr', 'desktop'); setupWorkspaces([defaultWorkspace]); experimentsManager.reset(); - experimentsManager.setup(e => e.inExperiment(DebugAdapterNewPtvsd.experiment)).returns(() => false); + experimentsManager.setup((e) => e.inExperiment(DebugAdapterNewPtvsd.experiment)).returns(() => false); const promise = debugProvider.resolveDebugConfiguration!(workspaceFolder, ({ request: 'attach', diff --git a/src/test/debugger/extension/configuration/resolvers/base.unit.test.ts b/src/test/debugger/extension/configuration/resolvers/base.unit.test.ts index e9c602f2f632..d9b45fb1286b 100644 --- a/src/test/debugger/extension/configuration/resolvers/base.unit.test.ts +++ b/src/test/debugger/extension/configuration/resolvers/base.unit.test.ts @@ -77,13 +77,13 @@ suite('Debugging - Config Resolver', () => { const doc = typemoq.Mock.ofType(); editor - .setup(e => e.document) + .setup((e) => e.document) .returns(() => doc.object) .verifiable(typemoq.Times.once()); - doc.setup(d => d.languageId) + doc.setup((d) => d.languageId) .returns(() => PYTHON_LANGUAGE) .verifiable(typemoq.Times.once()); - doc.setup(d => d.fileName) + doc.setup((d) => d.fileName) .returns(() => expectedFileName) .verifiable(typemoq.Times.once()); when(documentManager.activeTextEditor).thenReturn(editor.object); @@ -97,10 +97,10 @@ suite('Debugging - Config Resolver', () => { const doc = typemoq.Mock.ofType(); editor - .setup(e => e.document) + .setup((e) => e.document) .returns(() => doc.object) .verifiable(typemoq.Times.once()); - doc.setup(d => d.languageId) + doc.setup((d) => d.languageId) .returns(() => 'C#') .verifiable(typemoq.Times.once()); when(documentManager.activeTextEditor).thenReturn(editor.object); @@ -130,7 +130,7 @@ suite('Debugging - Config Resolver', () => { workspaceFolders: undefined }, { title: 'Should get directory of active program when there are 0 workspace folders', workspaceFolders: [] } - ].forEach(item => { + ].forEach((item) => { test(item.title, () => { const programPath = path.join('one', 'two', 'three.xyz'); @@ -215,7 +215,7 @@ suite('Debugging - Config Resolver', () => { '156.1.2.3': false, '::2': false }; - Object.keys(localHostTestMatrix).forEach(key => { + Object.keys(localHostTestMatrix).forEach((key) => { test(`Local host = ${localHostTestMatrix[key]} for ${key}`, () => { const isLocalHost = resolver.isLocalHost(key); diff --git a/src/test/debugger/extension/configuration/resolvers/common.ts b/src/test/debugger/extension/configuration/resolvers/common.ts index 1c8ac0b7065b..7c4af1600474 100644 --- a/src/test/debugger/extension/configuration/resolvers/common.ts +++ b/src/test/debugger/extension/configuration/resolvers/common.ts @@ -27,7 +27,7 @@ type OSTestInfo = [ // For each supported OS, provide a set of helpers to use in tests. export function getInfoPerOS(): OSTestInfo[] { - return getNamesAndValues(OSType).map(os => { + return getNamesAndValues(OSType).map((os) => { const osType = os.value as OSType; return [os.name, osType, getPathModuleForOS(osType)]; }); @@ -48,7 +48,7 @@ function getPathModuleForOS(osType: OSType): IPathModule { // Generate the function to use for populating the // relevant mocks relative to the target OS. export function setUpOSMocks(osType: OSType, platformService: TypeMoq.IMock) { - platformService.setup(p => p.isWindows).returns(() => osType === OSType.Windows); - platformService.setup(p => p.isMac).returns(() => osType === OSType.OSX); - platformService.setup(p => p.isLinux).returns(() => osType === OSType.Linux); + platformService.setup((p) => p.isWindows).returns(() => osType === OSType.Windows); + platformService.setup((p) => p.isMac).returns(() => osType === OSType.OSX); + platformService.setup((p) => p.isLinux).returns(() => osType === OSType.Linux); } diff --git a/src/test/debugger/extension/configuration/resolvers/launch.unit.test.ts b/src/test/debugger/extension/configuration/resolvers/launch.unit.test.ts index 2ae9cb99f182..ff9fdc3e8ab5 100644 --- a/src/test/debugger/extension/configuration/resolvers/launch.unit.test.ts +++ b/src/test/debugger/extension/configuration/resolvers/launch.unit.test.ts @@ -41,7 +41,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { let configExperiment: TypeMoq.IMock; function createMoqWorkspaceFolder(folderPath: string) { const folder = TypeMoq.Mock.ofType(); - folder.setup(f => f.uri).returns(() => Uri.file(folderPath)); + folder.setup((f) => f.uri).returns(() => Uri.file(folderPath)); return folder.object; } function setupIoc(pythonPath: string, workspaceFolder?: WorkspaceFolder) { @@ -59,23 +59,25 @@ getInfoPerOS().forEach(([osName, osType, path]) => { pythonExecutionService.setup((x: any) => x.then).returns(() => undefined); const factory = TypeMoq.Mock.ofType(); factory - .setup(f => f.create(TypeMoq.It.isAny())) + .setup((f) => f.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(pythonExecutionService.object)); - helper.setup(h => h.getInterpreterInformation(TypeMoq.It.isAny())).returns(() => Promise.resolve({})); + helper.setup((h) => h.getInterpreterInformation(TypeMoq.It.isAny())).returns(() => Promise.resolve({})); diagnosticsService - .setup(h => h.validatePythonPath(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((h) => h.validatePythonPath(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)); const settings = TypeMoq.Mock.ofType(); - settings.setup(s => s.pythonPath).returns(() => pythonPath); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); if (workspaceFolder) { - settings.setup(s => s.envFile).returns(() => path.join(workspaceFolder!.uri.fsPath, '.env2')); + settings.setup((s) => s.envFile).returns(() => path.join(workspaceFolder!.uri.fsPath, '.env2')); } - confgService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + confgService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); setUpOSMocks(osType, platformService); - debugEnvHelper.setup(x => x.getEnvironmentVariables(TypeMoq.It.isAny())).returns(() => Promise.resolve({})); + debugEnvHelper + .setup((x) => x.getEnvironmentVariables(TypeMoq.It.isAny())) + .returns(() => Promise.resolve({})); configExperiment - .setup(c => c.modifyConfigurationBasedOnExperiment(TypeMoq.It.isAny())) + .setup((c) => c.modifyConfigurationBasedOnExperiment(TypeMoq.It.isAny())) .returns(() => { return; }); @@ -94,17 +96,17 @@ getInfoPerOS().forEach(([osName, osType, path]) => { if (fileName) { const textEditor = TypeMoq.Mock.ofType(); const document = TypeMoq.Mock.ofType(); - document.setup(d => d.languageId).returns(() => languageId); - document.setup(d => d.fileName).returns(() => fileName); - textEditor.setup(t => t.document).returns(() => document.object); - documentManager.setup(d => d.activeTextEditor).returns(() => textEditor.object); + document.setup((d) => d.languageId).returns(() => languageId); + document.setup((d) => d.fileName).returns(() => fileName); + textEditor.setup((t) => t.document).returns(() => document.object); + documentManager.setup((d) => d.activeTextEditor).returns(() => textEditor.object); } else { - documentManager.setup(d => d.activeTextEditor).returns(() => undefined); + documentManager.setup((d) => d.activeTextEditor).returns(() => undefined); } } function setupWorkspaces(folders: string[]) { const workspaceFolders = folders.map(createMoqWorkspaceFolder); - workspaceService.setup(w => w.workspaceFolders).returns(() => workspaceFolders); + workspaceService.setup((w) => w.workspaceFolders).returns(() => workspaceFolders); } test('Defaults should be returned when an empty object is passed with a Workspace Folder and active file', async () => { const pythonPath = `PythonPath_${new Date().toString()}`; @@ -400,7 +402,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { } ]); }); - test('Ensure drive letter is lower cased for local path mappings on Windows when with existing path mappings', async function() { + test('Ensure drive letter is lower cased for local path mappings on Windows when with existing path mappings', async function () { if (getOSType() !== OSType.Windows || osType !== OSType.Windows) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -430,7 +432,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { } ]); }); - test('Ensure drive letter is not lower cased for local path mappings on non-Windows when with existing path mappings', async function() { + test('Ensure drive letter is not lower cased for local path mappings on non-Windows when with existing path mappings', async function () { if (getOSType() === OSType.Windows || osType === OSType.Windows) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -637,7 +639,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { const pythonFile = 'xyz.py'; setupIoc(pythonPath); setupActiveEditor(pythonFile, PYTHON_LANGUAGE); - testsForJustMyCode.forEach(async testParams => { + testsForJustMyCode.forEach(async (testParams) => { const debugConfig = await debugProvider.resolveDebugConfiguration!(workspaceFolder, { debugStdLib: testParams.debugStdLib, justMyCode: testParams.justMyCode @@ -698,7 +700,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { const pythonFile = 'xyz.py'; setupIoc(pythonPath); setupActiveEditor(pythonFile, PYTHON_LANGUAGE); - testsForRedirectOutput.forEach(async testParams => { + testsForRedirectOutput.forEach(async (testParams) => { const debugConfig = await debugProvider.resolveDebugConfiguration!(workspaceFolder, { console: testParams.console, redirectOutput: testParams.redirectOutput @@ -722,13 +724,9 @@ getInfoPerOS().forEach(([osName, osType, path]) => { {} as DebugConfiguration ); if (osType === OSType.Windows) { - expect(debugConfig) - .to.have.property('debugOptions') - .contains(DebugOptions.FixFilePathCase); + expect(debugConfig).to.have.property('debugOptions').contains(DebugOptions.FixFilePathCase); } else { - expect(debugConfig) - .to.have.property('debugOptions') - .not.contains(DebugOptions.FixFilePathCase); + expect(debugConfig).to.have.property('debugOptions').not.contains(DebugOptions.FixFilePathCase); } }); test('Jinja added for Pyramid', async () => { @@ -772,7 +770,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { diagnosticsService.reset(); diagnosticsService - .setup(h => + .setup((h) => h.validatePythonPath(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve(false)) @@ -795,7 +793,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { diagnosticsService.reset(); diagnosticsService - .setup(h => + .setup((h) => h.validatePythonPath(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve(true)) @@ -821,7 +819,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { diagnosticsService.reset(); diagnosticsService - .setup(h => + .setup((h) => h.validatePythonPath(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve(true)); @@ -858,7 +856,7 @@ getInfoPerOS().forEach(([osName, osType, path]) => { } type LaunchOrAttach = 'launch' | 'attach'; const items: LaunchOrAttach[] = ['launch', 'attach']; - items.forEach(requestType => { + items.forEach((requestType) => { test(`Must not contain Sub Process when not specified (${requestType})`, async () => { await testSetting(requestType, {}, DebugOptions.SubProcess, false); }); diff --git a/src/test/debugger/extension/configuration/resolvers/launchConfigExperiments.unit.test.ts b/src/test/debugger/extension/configuration/resolvers/launchConfigExperiments.unit.test.ts index 089d4d2b8712..f361640a5198 100644 --- a/src/test/debugger/extension/configuration/resolvers/launchConfigExperiments.unit.test.ts +++ b/src/test/debugger/extension/configuration/resolvers/launchConfigExperiments.unit.test.ts @@ -126,14 +126,14 @@ suite('Debugging - Config Resolver Launch Experiments', () => { function createTestConfigurations() { const testConfigs: TestConfiguration[] = []; - newDebuggerExperiment.forEach(newDbgExp => { - reloadExperiment.forEach(reloadExp => { - subProcessValues.forEach(subProcessValue => { - noReloadSwitches.forEach(noReloadSwitch => { - webFramework.forEach(framework => { + newDebuggerExperiment.forEach((newDbgExp) => { + reloadExperiment.forEach((reloadExp) => { + subProcessValues.forEach((subProcessValue) => { + noReloadSwitches.forEach((noReloadSwitch) => { + webFramework.forEach((framework) => { const usingReloadSwitch = ['run', noReloadSwitch, '--other-switch']; const withoutUsingReloadSwitch = ['run', '--other-switch']; - [usingReloadSwitch, withoutUsingReloadSwitch].forEach(args => { + [usingReloadSwitch, withoutUsingReloadSwitch].forEach((args) => { testConfigs.push({ newDebuggerExperiment: newDbgExp, reloadExperiment: reloadExp, diff --git a/src/test/debugger/misc.test.ts b/src/test/debugger/misc.test.ts index 522deb1d1cb5..26c4f760801e 100644 --- a/src/test/debugger/misc.test.ts +++ b/src/test/debugger/misc.test.ts @@ -22,14 +22,14 @@ suite(`Standard Debugging - Misc tests: ${debuggerType}`, () => { let debugClient: DebugClient; // All tests in this suite are failed // Check https://github.com/Microsoft/vscode-python/issues/4067 - setup(async function() { + setup(async function () { return this.skip(); if (!IS_MULTI_ROOT_TEST || !TEST_DEBUGGER) { // tslint:disable-next-line:no-invalid-this return this.skip(); } - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); debugClient = createDebugAdapter(); debugClient.defaultTimeout = DEBUGGER_TIMEOUT; await debugClient.start(); @@ -75,7 +75,7 @@ suite(`Standard Debugging - Misc tests: ${debuggerType}`, () => { } // Check https://github.com/Microsoft/vscode-python/issues/4067 - test('Should run program to the end', async function() { + test('Should run program to the end', async function () { return this.skip(); await Promise.all([ debugClient.configurationSequence(), @@ -85,7 +85,7 @@ suite(`Standard Debugging - Misc tests: ${debuggerType}`, () => { ]); }); // Check https://github.com/Microsoft/vscode-python/issues/4067 - test('test stderr output for Python', async function() { + test('test stderr output for Python', async function () { return this.skip(); await Promise.all([ debugClient.configurationSequence(), diff --git a/src/test/debugger/portAndHost.test.ts b/src/test/debugger/portAndHost.test.ts index 1dfbc383f309..0902820c5881 100644 --- a/src/test/debugger/portAndHost.test.ts +++ b/src/test/debugger/portAndHost.test.ts @@ -25,24 +25,24 @@ const debuggerType = DebuggerTypeName; // tslint:disable-next-line:max-func-body-length suite(`Standard Debugging of ports and hosts: ${debuggerType}`, () => { let debugClient: DebugClient; - suiteSetup(async function() { + suiteSetup(async function () { // https://github.com/microsoft/vscode-python/issues/9383 // tslint:disable-next-line:no-invalid-this return this.skip(); }); - setup(async function() { + setup(async function () { if (!IS_MULTI_ROOT_TEST || !TEST_DEBUGGER) { // tslint:disable-next-line:no-invalid-this this.skip(); } - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); debugClient = new DebugClient(process.env.NODE_PATH || 'node', testAdapterFilePath, debuggerType); debugClient.defaultTimeout = DEBUGGER_TIMEOUT; await debugClient.start(); }); teardown(async () => { // Wait for a second before starting another test (sometimes, sockets take a while to get closed). - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); try { debugClient.stop().catch(noop); // tslint:disable-next-line:no-empty @@ -117,7 +117,7 @@ suite(`Standard Debugging of ports and hosts: ${debuggerType}`, () => { }); test('Confirm debuggig fails when provided port is in use', async () => { const server = net.createServer(noop); - const port = await new Promise(resolve => + const port = await new Promise((resolve) => server.listen({ host: 'localhost', port: 0 }, () => resolve((server.address() as net.AddressInfo).port)) ); let exception: Error | undefined; diff --git a/src/test/debugger/run.test.ts b/src/test/debugger/run.test.ts index 4cbced83ba55..0f35147363a3 100644 --- a/src/test/debugger/run.test.ts +++ b/src/test/debugger/run.test.ts @@ -18,11 +18,11 @@ const debugFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'py const debuggerType = DebuggerTypeName; suite('Run without Debugging', () => { let debugClient: DebugClient; - setup(async function() { + setup(async function () { if (!IS_MULTI_ROOT_TEST || !TEST_DEBUGGER) { this.skip(); } - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); debugClient = await createDebugAdapter(); }); teardown(async () => { diff --git a/src/test/debugger/utils.ts b/src/test/debugger/utils.ts index 516374e56e02..46e210a09ff8 100644 --- a/src/test/debugger/utils.ts +++ b/src/test/debugger/utils.ts @@ -22,7 +22,7 @@ const debuggerType = DebuggerTypeName; * @returns {DebugClient} */ export async function createDebugAdapter(): Promise { - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); const debugClient = new DebugClient(process.env.NODE_PATH || 'node', testAdapterFilePath, debuggerType); debugClient.defaultTimeout = DEBUGGER_TIMEOUT; await debugClient.start(); @@ -55,7 +55,7 @@ export async function validateVariablesInFrame( const variables = await debugClient.variablesRequest({ variablesReference }); for (const expectedVariable of expectedVariables) { - const variable = variables.body.variables.find(item => item.name === expectedVariable.name)!; + const variable = variables.body.variables.find((item) => item.name === expectedVariable.name)!; expect(variable).to.be.not.equal('undefined', `variable '${expectedVariable.name}' is undefined`); expect(variable.type).to.be.equal(expectedVariable.type); expect(variable.value).to.be.equal(expectedVariable.value); diff --git a/src/test/debuggerTest.ts b/src/test/debuggerTest.ts index 4e4e3cea7598..c7fc3e1058d1 100644 --- a/src/test/debuggerTest.ts +++ b/src/test/debuggerTest.ts @@ -20,7 +20,7 @@ function start() { launchArgs: [workspacePath], version: 'stable', extensionTestsEnv: { ...process.env, UITEST_DISABLE_INSIDERS: '1' } - }).catch(ex => { + }).catch((ex) => { console.error('End Debugger tests (with errors)', ex); process.exit(1); }); diff --git a/src/test/extension-version.functional.test.ts b/src/test/extension-version.functional.test.ts index 70adee3a9489..7d2913b0f34b 100644 --- a/src/test/extension-version.functional.test.ts +++ b/src/test/extension-version.functional.test.ts @@ -18,7 +18,7 @@ suite('Extension version tests', () => { let applicationEnvironment: IApplicationEnvironment; const branchName = process.env.CI_BRANCH_NAME; - suiteSetup(async function() { + suiteSetup(async function () { // Skip the entire suite if running locally if (!branchName) { // tslint:disable-next-line: no-invalid-this @@ -31,7 +31,7 @@ suite('Extension version tests', () => { version = applicationEnvironment.packageJson.version; }); - test('If we are running a pipeline in the master branch, the extension version in `package.json` should have the "-dev" suffix', async function() { + test('If we are running a pipeline in the master branch, the extension version in `package.json` should have the "-dev" suffix', async function () { if (branchName !== 'master') { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -43,7 +43,7 @@ suite('Extension version tests', () => { ).to.be.true; }); - test('If we are running a pipeline in the release branch, the extension version in `package.json` should not have the "-dev" suffix', async function() { + test('If we are running a pipeline in the release branch, the extension version in `package.json` should not have the "-dev" suffix', async function () { if (!branchName!.startsWith('release')) { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -59,7 +59,7 @@ suite('Extension version tests', () => { suite('Extension localization files', () => { test('Load localization file', () => { const filesFailed: string[] = []; - glob.sync('package.nls.*.json', { sync: true, cwd: EXTENSION_ROOT_DIR }).forEach(localizationFile => { + glob.sync('package.nls.*.json', { sync: true, cwd: EXTENSION_ROOT_DIR }).forEach((localizationFile) => { try { JSON.parse(fs.readFileSync(path.join(EXTENSION_ROOT_DIR, localizationFile)).toString()); } catch { diff --git a/src/test/format/extension.dispatch.test.ts b/src/test/format/extension.dispatch.test.ts index ba19f47d72d4..19d937a4cfc6 100644 --- a/src/test/format/extension.dispatch.test.ts +++ b/src/test/format/extension.dispatch.test.ts @@ -107,7 +107,7 @@ suite('Formatting - Dispatcher', () => { ): TypeMoq.IMock { const provider = TypeMoq.Mock.ofType(); provider - .setup(p => p.provideOnTypeFormattingEdits(document, position, ch, options, cancellationToken)) + .setup((p) => p.provideOnTypeFormattingEdits(document, position, ch, options, cancellationToken)) .returns(() => result) .verifiable(TypeMoq.Times.once()); return provider; diff --git a/src/test/format/extension.format.test.ts b/src/test/format/extension.format.test.ts index 5c1fe8768018..331b2e5eefa7 100644 --- a/src/test/format/extension.format.test.ts +++ b/src/test/format/extension.format.test.ts @@ -44,7 +44,7 @@ suite('Formatting - General', () => { suiteSetup(async () => { await initialize(); initializeDI(); - [autoPep8FileToFormat, blackFileToFormat, yapfFileToFormat].forEach(file => { + [autoPep8FileToFormat, blackFileToFormat, yapfFileToFormat].forEach((file) => { fs.copySync(originalUnformattedFile, file, { overwrite: true }); }); formattedYapf = fs.readFileSync(yapfFormatted).toString(); @@ -64,7 +64,7 @@ suite('Formatting - General', () => { initializeDI(); }); suiteTeardown(async () => { - [autoPep8FileToFormat, blackFileToFormat, yapfFileToFormat].forEach(file => { + [autoPep8FileToFormat, blackFileToFormat, yapfFileToFormat].forEach((file) => { if (fs.existsSync(file)) { fs.unlinkSync(file); } @@ -127,8 +127,8 @@ suite('Formatting - General', () => { await injectFormatOutput(outputFileName); const edits = await formatter.formatDocument(textDocument, options, new CancellationTokenSource().token); - await textEditor.edit(editBuilder => { - edits.forEach(edit => editBuilder.replace(edit.range, edit.newText)); + await textEditor.edit((editBuilder) => { + edits.forEach((edit) => editBuilder.replace(edit.range, edit.newText)); }); compareFiles(formattedContents, textEditor.document.getText()); } @@ -142,7 +142,7 @@ suite('Formatting - General', () => { ); }); // tslint:disable-next-line:no-function-expression - test('Black', async function() { + test('Black', async function () { if (!(await formattingTestIsBlackSupported())) { // Skip for versions of python below 3.6, as Black doesn't support them at all. // tslint:disable-next-line:no-invalid-this @@ -175,7 +175,7 @@ suite('Formatting - General', () => { const textDocument = await workspace.openTextDocument(fileToFormat); const textEditor = await window.showTextDocument(textDocument); - await textEditor.edit(builder => { + await textEditor.edit((builder) => { // Make file dirty. Trailing blanks will be removed. builder.insert(new Position(0, 0), '\n \n'); }); @@ -190,8 +190,8 @@ suite('Formatting - General', () => { const options = { insertSpaces: textEditor.options.insertSpaces! as boolean, tabSize: 1 }; const formatter = new YapfFormatter(ioc.serviceContainer); const edits = await formatter.formatDocument(textDocument, options, new CancellationTokenSource().token); - await textEditor.edit(editBuilder => { - edits.forEach(edit => editBuilder.replace(edit.range, edit.newText)); + await textEditor.edit((editBuilder) => { + edits.forEach((edit) => editBuilder.replace(edit.range, edit.newText)); }); const expected = fs.readFileSync(formattedFile).toString(); diff --git a/src/test/format/extension.lineFormatter.test.ts b/src/test/format/extension.lineFormatter.test.ts index 5410b6c28623..37c6b33edc93 100644 --- a/src/test/format/extension.lineFormatter.test.ts +++ b/src/test/format/extension.lineFormatter.test.ts @@ -196,16 +196,18 @@ suite('Formatting - line formatter', () => { const document = TypeMoq.Mock.ofType(); document - .setup(x => x.lineAt(TypeMoq.It.isAnyNumber())) - .returns(n => { + .setup((x) => x.lineAt(TypeMoq.It.isAnyNumber())) + .returns((n) => { const line = TypeMoq.Mock.ofType(); - line.setup(x => x.text).returns(() => lines[n]); - line.setup(x => x.range).returns(() => new Range(new Position(n, 0), new Position(n, lines[n].length))); + line.setup((x) => x.text).returns(() => lines[n]); + line.setup((x) => x.range).returns( + () => new Range(new Position(n, 0), new Position(n, lines[n].length)) + ); return line.object; }); document - .setup(x => x.getText(TypeMoq.It.isAny())) - .returns(o => { + .setup((x) => x.getText(TypeMoq.It.isAny())) + .returns((o) => { const r = o as Range; const bits: string[] = []; @@ -221,8 +223,8 @@ suite('Formatting - line formatter', () => { return bits.join('\n'); }); document - .setup(x => x.offsetAt(TypeMoq.It.isAny())) - .returns(o => { + .setup((x) => x.offsetAt(TypeMoq.It.isAny())) + .returns((o) => { const p = o as Position; let offset = 0; for (let i = 0; i < p.line; i += 1) { @@ -236,10 +238,10 @@ suite('Formatting - line formatter', () => { function formatLine(text: string): string { const line = TypeMoq.Mock.ofType(); - line.setup(x => x.text).returns(() => text); + line.setup((x) => x.text).returns(() => text); const document = TypeMoq.Mock.ofType(); - document.setup(x => x.lineAt(TypeMoq.It.isAnyNumber())).returns(() => line.object); + document.setup((x) => x.lineAt(TypeMoq.It.isAnyNumber())).returns(() => line.object); return formatter.formatLine(document.object, 0); } diff --git a/src/test/format/extension.onTypeFormat.test.ts b/src/test/format/extension.onTypeFormat.test.ts index ee7db6084bd0..767d378db80d 100644 --- a/src/test/format/extension.onTypeFormat.test.ts +++ b/src/test/format/extension.onTypeFormat.test.ts @@ -36,11 +36,11 @@ function testFormatting( let textDocument: vscode.TextDocument; return vscode.workspace .openTextDocument(fileToFormat) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { return provider.provideOnTypeFormattingEdits( textDocument, position, @@ -50,7 +50,7 @@ function testFormatting( ); }) .then( - edits => { + (edits) => { assert.equal(edits.length, expectedEdits.length, 'Number of edits not the same'); edits.forEach((edit, index) => { const expectedEdit = expectedEdits[index]; @@ -67,7 +67,7 @@ function testFormatting( ); }); }, - reason => { + (reason) => { assert.fail(reason, undefined, 'Type Formatting failed', ''); } ); @@ -78,7 +78,7 @@ suite('Else block with if in first line of file', () => { await initialize(); fs.ensureDirSync(path.dirname(outPythoFilesPath)); - ['elseBlocksFirstLine2.py', 'elseBlocksFirstLine4.py', 'elseBlocksFirstLineTab.py'].forEach(file => { + ['elseBlocksFirstLine2.py', 'elseBlocksFirstLine4.py', 'elseBlocksFirstLineTab.py'].forEach((file) => { const targetFile = path.join(outPythoFilesPath, file); if (fs.existsSync(targetFile)) { fs.unlinkSync(targetFile); @@ -128,7 +128,7 @@ suite('Else block with if in first line of file', () => { ]; testCases.forEach((testCase, index) => { - test(`${index + 1}. ${testCase.title}`, done => { + test(`${index + 1}. ${testCase.title}`, (done) => { const pos = new vscode.Position(testCase.line, testCase.column); testFormatting(testCase.filePath, pos, testCase.expectedEdits, testCase.formatOptions).then(done, done); }); @@ -140,7 +140,7 @@ suite('Try blocks with indentation of 2 spaces', () => { await initialize(); fs.ensureDirSync(path.dirname(outPythoFilesPath)); - ['tryBlocks2.py'].forEach(file => { + ['tryBlocks2.py'].forEach((file) => { const targetFile = path.join(outPythoFilesPath, file); if (fs.existsSync(targetFile)) { fs.unlinkSync(targetFile); @@ -226,7 +226,7 @@ suite('Try blocks with indentation of 2 spaces', () => { }; testCases.forEach((testCase, index) => { - test(`${index + 1}. ${testCase.title}`, done => { + test(`${index + 1}. ${testCase.title}`, (done) => { const pos = new vscode.Position(testCase.line, testCase.column); testFormatting(tryBlock2OutFilePath, pos, testCase.expectedEdits, formatOptions).then(done, done); }); @@ -238,7 +238,7 @@ suite('Try blocks with indentation of 4 spaces', () => { await initialize(); fs.ensureDirSync(path.dirname(outPythoFilesPath)); - ['tryBlocks4.py'].forEach(file => { + ['tryBlocks4.py'].forEach((file) => { const targetFile = path.join(outPythoFilesPath, file); if (fs.existsSync(targetFile)) { fs.unlinkSync(targetFile); @@ -324,7 +324,7 @@ suite('Try blocks with indentation of 4 spaces', () => { }; testCases.forEach((testCase, index) => { - test(`${index + 1}. ${testCase.title}`, done => { + test(`${index + 1}. ${testCase.title}`, (done) => { const pos = new vscode.Position(testCase.line, testCase.column); testFormatting(tryBlock4OutFilePath, pos, testCase.expectedEdits, formatOptions).then(done, done); }); @@ -336,7 +336,7 @@ suite('Try blocks with indentation of Tab', () => { await initialize(); fs.ensureDirSync(path.dirname(outPythoFilesPath)); - ['tryBlocksTab.py'].forEach(file => { + ['tryBlocksTab.py'].forEach((file) => { const targetFile = path.join(outPythoFilesPath, file); if (fs.existsSync(targetFile)) { fs.unlinkSync(targetFile); @@ -426,7 +426,7 @@ suite('Try blocks with indentation of Tab', () => { }; testCases.forEach((testCase, index) => { - test(`${index + 1}. ${testCase.title}`, done => { + test(`${index + 1}. ${testCase.title}`, (done) => { const pos = new vscode.Position(testCase.line, testCase.column); testFormatting(tryBlockTabOutFilePath, pos, testCase.expectedEdits, formatOptions).then(done, done); }); @@ -438,7 +438,7 @@ suite('Else blocks with indentation of 2 spaces', () => { await initialize(); fs.ensureDirSync(path.dirname(outPythoFilesPath)); - ['elseBlocks2.py'].forEach(file => { + ['elseBlocks2.py'].forEach((file) => { const targetFile = path.join(outPythoFilesPath, file); if (fs.existsSync(targetFile)) { fs.unlinkSync(targetFile); @@ -549,7 +549,7 @@ suite('Else blocks with indentation of 2 spaces', () => { }; testCases.forEach((testCase, index) => { - test(`${index + 1}. ${testCase.title}`, done => { + test(`${index + 1}. ${testCase.title}`, (done) => { const pos = new vscode.Position(testCase.line, testCase.column); testFormatting(elseBlock2OutFilePath, pos, testCase.expectedEdits, formatOptions).then(done, done); }); @@ -561,7 +561,7 @@ suite('Else blocks with indentation of 4 spaces', () => { await initialize(); fs.ensureDirSync(path.dirname(outPythoFilesPath)); - ['elseBlocks4.py'].forEach(file => { + ['elseBlocks4.py'].forEach((file) => { const targetFile = path.join(outPythoFilesPath, file); if (fs.existsSync(targetFile)) { fs.unlinkSync(targetFile); @@ -665,7 +665,7 @@ suite('Else blocks with indentation of 4 spaces', () => { }; testCases.forEach((testCase, index) => { - test(`${index + 1}. ${testCase.title}`, done => { + test(`${index + 1}. ${testCase.title}`, (done) => { const pos = new vscode.Position(testCase.line, testCase.column); testFormatting(elseBlock4OutFilePath, pos, testCase.expectedEdits, formatOptions).then(done, done); }); @@ -677,7 +677,7 @@ suite('Else blocks with indentation of Tab', () => { await initialize(); fs.ensureDirSync(path.dirname(outPythoFilesPath)); - ['elseBlocksTab.py'].forEach(file => { + ['elseBlocksTab.py'].forEach((file) => { const targetFile = path.join(outPythoFilesPath, file); if (fs.existsSync(targetFile)) { fs.unlinkSync(targetFile); @@ -782,7 +782,7 @@ suite('Else blocks with indentation of Tab', () => { }; testCases.forEach((testCase, index) => { - test(`${index + 1}. ${testCase.title}`, done => { + test(`${index + 1}. ${testCase.title}`, (done) => { const pos = new vscode.Position(testCase.line, testCase.column); testFormatting(elseBlockTabOutFilePath, pos, testCase.expectedEdits, formatOptions).then(done, done); }); diff --git a/src/test/format/extension.sort.test.ts b/src/test/format/extension.sort.test.ts index bb942d4a1868..adb740fe1aa9 100644 --- a/src/test/format/extension.sort.test.ts +++ b/src/test/format/extension.sort.test.ts @@ -68,18 +68,18 @@ suite('Sorting', () => { expect(edit.entries()).to.be.lengthOf(1); const edits = edit.entries()[0][1]; assert.equal( - edits.filter(value => value.newText === EOL && value.range.isEqual(new Range(2, 0, 2, 0))).length, + edits.filter((value) => value.newText === EOL && value.range.isEqual(new Range(2, 0, 2, 0))).length, 1, 'EOL not found' ); assert.equal( - edits.filter(value => value.newText === '' && value.range.isEqual(new Range(3, 0, 4, 0))).length, + edits.filter((value) => value.newText === '' && value.range.isEqual(new Range(3, 0, 4, 0))).length, 1, '"" not found' ); assert.equal( edits.filter( - value => + (value) => value.newText === `from rope.base import libutils${EOL}from rope.refactor.extract import ExtractMethod, ExtractVariable${EOL}from rope.refactor.rename import Rename${EOL}` && value.range.isEqual(new Range(6, 0, 6, 0)) @@ -88,7 +88,7 @@ suite('Sorting', () => { 'Text not found' ); assert.equal( - edits.filter(value => value.newText === '' && value.range.isEqual(new Range(13, 0, 18, 0))).length, + edits.filter((value) => value.newText === '' && value.range.isEqual(new Range(13, 0, 18, 0))).length, 1, '"" not found' ); @@ -110,7 +110,7 @@ suite('Sorting', () => { const edits = edit.entries()[0][1]; const newValue = `from third_party import lib2${EOL}from third_party import lib3${EOL}from third_party import lib4${EOL}from third_party import lib5${EOL}from third_party import lib6${EOL}from third_party import lib7${EOL}from third_party import lib8${EOL}from third_party import lib9${EOL}`; assert.equal( - edits.filter(value => value.newText === newValue && value.range.isEqual(new Range(0, 0, 3, 0))).length, + edits.filter((value) => value.newText === newValue && value.range.isEqual(new Range(0, 0, 3, 0))).length, 1, 'New Text not found' ); @@ -133,7 +133,7 @@ suite('Sorting', () => { ); const textDocument = await workspace.openTextDocument(fileToFormatWithConfig); const editor = await window.showTextDocument(textDocument); - await editor.edit(builder => { + await editor.edit((builder) => { builder.insert(new Position(0, 0), `from third_party import lib0${EOL}`); }); const edit = (await sorter.provideDocumentSortImportsEdits(textDocument.uri))!; @@ -150,7 +150,7 @@ suite('Sorting', () => { ); const textDocument = await workspace.openTextDocument(fileToFormatWithConfig); const editor = await window.showTextDocument(textDocument); - await editor.edit(builder => { + await editor.edit((builder) => { builder.insert(new Position(0, 0), `from third_party import lib0${EOL}`); }); const originalContent = textDocument.getText(); diff --git a/src/test/format/format.helper.test.ts b/src/test/format/format.helper.test.ts index d14e09a4e64c..9ec60c994cb8 100644 --- a/src/test/format/format.helper.test.ts +++ b/src/test/format/format.helper.test.ts @@ -18,14 +18,14 @@ suite('Formatting - Helper', () => { ioc = new UnitTestIocContainer(); const config = TypeMoq.Mock.ofType(); - config.setup(x => x.getSettings(TypeMoq.It.isAny())).returns(() => getExtensionSettings(undefined)); + config.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => getExtensionSettings(undefined)); ioc.serviceManager.addSingletonInstance(IConfigurationService, config.object); formatHelper = new FormatterHelper(ioc.serviceManager); }); test('Ensure product is set in Execution Info', async () => { - [Product.autopep8, Product.black, Product.yapf].forEach(formatter => { + [Product.autopep8, Product.black, Product.yapf].forEach((formatter) => { const info = formatHelper.getExecutionInfo(formatter, []); assert.equal(info.product, formatter, `Incorrect products for ${formatHelper.translateToId(formatter)}`); }); @@ -34,7 +34,7 @@ suite('Formatting - Helper', () => { test('Ensure executable is set in Execution Info', async () => { const settings = getExtensionSettings(undefined); - [Product.autopep8, Product.black, Product.yapf].forEach(formatter => { + [Product.autopep8, Product.black, Product.yapf].forEach((formatter) => { const info = formatHelper.getExecutionInfo(formatter, []); const names = formatHelper.getSettingsPropertyNames(formatter); const execPath = settings.formatting[names.pathName] as string; @@ -51,7 +51,7 @@ suite('Formatting - Helper', () => { const settings = getExtensionSettings(undefined); const customArgs = ['1', '2', '3']; - [Product.autopep8, Product.black, Product.yapf].forEach(formatter => { + [Product.autopep8, Product.black, Product.yapf].forEach((formatter) => { const names = formatHelper.getSettingsPropertyNames(formatter); const args: string[] = Array.isArray(settings.formatting[names.argsName]) ? (settings.formatting[names.argsName] as string[]) @@ -67,7 +67,7 @@ suite('Formatting - Helper', () => { }); test('Ensure correct setting names are returned', async () => { - [Product.autopep8, Product.black, Product.yapf].forEach(formatter => { + [Product.autopep8, Product.black, Product.yapf].forEach((formatter) => { const translatedId = formatHelper.translateToId(formatter)!; const settings = { argsName: `${translatedId}Args` as keyof IFormattingSettings, @@ -88,7 +88,7 @@ suite('Formatting - Helper', () => { formatterMapping.set(Product.black, 'black'); formatterMapping.set(Product.yapf, 'yapf'); - [Product.autopep8, Product.black, Product.yapf].forEach(formatter => { + [Product.autopep8, Product.black, Product.yapf].forEach((formatter) => { const translatedId = formatHelper.translateToId(formatter); assert.equal( translatedId, @@ -98,7 +98,7 @@ suite('Formatting - Helper', () => { }); }); - EnumEx.getValues(Product).forEach(product => { + EnumEx.getValues(Product).forEach((product) => { const formatterMapping = new Map(); formatterMapping.set(Product.autopep8, 'autopep8'); formatterMapping.set(Product.black, 'black'); diff --git a/src/test/format/formatter.unit.test.ts b/src/test/format/formatter.unit.test.ts index 7eaf3ee8df6a..679b0d06f093 100644 --- a/src/test/format/formatter.unit.test.ts +++ b/src/test/format/formatter.unit.test.ts @@ -72,10 +72,10 @@ suite('Formatting - Test Arguments', () => { workspace = mock(WorkspaceService); settings = mock(PythonSettings); document = typemoq.Mock.ofType(); - document.setup(doc => doc.getText(typemoq.It.isAny())).returns(() => ''); - document.setup(doc => doc.isDirty).returns(() => false); - document.setup(doc => doc.fileName).returns(() => docUri.fsPath); - document.setup(doc => doc.uri).returns(() => docUri); + document.setup((doc) => doc.getText(typemoq.It.isAny())).returns(() => ''); + document.setup((doc) => doc.isDirty).returns(() => false); + document.setup((doc) => doc.fileName).returns(() => docUri.fsPath); + document.setup((doc) => doc.uri).returns(() => docUri); pythonToolExecutionService = mock(PythonToolExecutionService); const configService = mock(ConfigurationService); diff --git a/src/test/index.ts b/src/test/index.ts index 9568d37ce9ba..a9d7bcd417c3 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -116,7 +116,7 @@ function activatePythonExtensionScript() { const initializationPromise = initialize(); const promise = Promise.race([initializationPromise, failed]); // tslint:disable-next-line: no-console - promise.finally(() => clearTimeout(timer!)).catch(e => console.error(e)); + promise.finally(() => clearTimeout(timer!)).catch((e) => console.error(e)); return initializationPromise; } @@ -156,7 +156,7 @@ export async function run(): Promise { }); // Setup test files that need to be run. - testFiles.forEach(file => mocha.addFile(path.join(testsRoot, file))); + testFiles.forEach((file) => mocha.addFile(path.join(testsRoot, file))); // tslint:disable: no-console console.time('Time taken to activate the extension'); @@ -169,7 +169,7 @@ export async function run(): Promise { // Run the tests. await new Promise((resolve, reject) => { - mocha.run(failures => { + mocha.run((failures) => { if (failures > 0) { return reject(new Error(`${failures} total failures`)); } diff --git a/src/test/initialize.ts b/src/test/initialize.ts index 7b83f5b7a332..dd949fd0b318 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -73,7 +73,7 @@ export async function closeActiveWindows(): Promise { clearTimeout(timer); resolve(); }, - ex => { + (ex) => { clearTimeout(timer); reject(ex); } diff --git a/src/test/install/channelManager.channels.test.ts b/src/test/install/channelManager.channels.test.ts index db116d0ababf..b1b3eda7ba75 100644 --- a/src/test/install/channelManager.channels.test.ts +++ b/src/test/install/channelManager.channels.test.ts @@ -95,7 +95,7 @@ suite('Installation - installation channels', () => { path: 'pipenv', type: InterpreterType.VirtualEnv }; - pipEnv.setup(x => x.getInterpreters(TypeMoq.It.isAny())).returns(() => Promise.resolve([interpreter])); + pipEnv.setup((x) => x.getInterpreters(TypeMoq.It.isAny())).returns(() => Promise.resolve([interpreter])); const cm = new InstallationChannelManager(serviceContainer); const channels = await cm.getInstallationChannels(); @@ -113,7 +113,7 @@ suite('Installation - installation channels', () => { // tslint:disable-next-line:no-any let items: any[] | undefined; appShell - .setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((x) => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .callback((i: string[], _o: QuickPickOptions) => { items = i; }) @@ -121,8 +121,8 @@ suite('Installation - installation channels', () => { () => new Promise((resolve, _reject) => resolve(undefined)) ); - installer1.setup(x => x.displayName).returns(() => 'Name 1'); - installer2.setup(x => x.displayName).returns(() => 'Name 2'); + installer1.setup((x) => x.displayName).returns(() => 'Name 1'); + installer2.setup((x) => x.displayName).returns(() => 'Name 2'); const cm = new InstallationChannelManager(serviceContainer); await cm.getInstallationChannel(Product.pylint); @@ -136,11 +136,11 @@ suite('Installation - installation channels', () => { function mockInstaller(supported: boolean, name: string, priority?: number): TypeMoq.IMock { const installer = TypeMoq.Mock.ofType(); installer - .setup(x => x.isSupported(TypeMoq.It.isAny())) + .setup((x) => x.isSupported(TypeMoq.It.isAny())) .returns( - () => new Promise(resolve => resolve(supported)) + () => new Promise((resolve) => resolve(supported)) ); - installer.setup(x => x.priority).returns(() => (priority ? priority : 0)); + installer.setup((x) => x.priority).returns(() => (priority ? priority : 0)); serviceManager.addSingletonInstance(IModuleInstaller, installer.object, name); return installer; } diff --git a/src/test/install/channelManager.messages.test.ts b/src/test/install/channelManager.messages.test.ts index 3173c14694fd..18419c20f1a0 100644 --- a/src/test/install/channelManager.messages.test.ts +++ b/src/test/install/channelManager.messages.test.ts @@ -67,7 +67,7 @@ suite('Installation - channel messages', () => { }); test('No installers message: Unknown/Windows', async () => { - platform.setup(x => x.isWindows).returns(() => true); + platform.setup((x) => x.isWindows).returns(() => true); await testInstallerMissingMessage(InterpreterType.Unknown, async (message: string, url: string) => { verifyMessage(message, ['Pip'], ['Conda']); verifyUrl(url, ['Windows', 'Pip']); @@ -75,7 +75,7 @@ suite('Installation - channel messages', () => { }); test('No installers message: Conda/Windows', async () => { - platform.setup(x => x.isWindows).returns(() => true); + platform.setup((x) => x.isWindows).returns(() => true); await testInstallerMissingMessage(InterpreterType.Conda, async (message: string, url: string) => { verifyMessage(message, ['Pip', 'Conda'], []); verifyUrl(url, ['Windows', 'Pip', 'Conda']); @@ -83,8 +83,8 @@ suite('Installation - channel messages', () => { }); test('No installers message: Unknown/Mac', async () => { - platform.setup(x => x.isWindows).returns(() => false); - platform.setup(x => x.isMac).returns(() => true); + platform.setup((x) => x.isWindows).returns(() => false); + platform.setup((x) => x.isMac).returns(() => true); await testInstallerMissingMessage(InterpreterType.Unknown, async (message: string, url: string) => { verifyMessage(message, ['Pip'], ['Conda']); verifyUrl(url, ['Mac', 'Pip']); @@ -92,8 +92,8 @@ suite('Installation - channel messages', () => { }); test('No installers message: Conda/Mac', async () => { - platform.setup(x => x.isWindows).returns(() => false); - platform.setup(x => x.isMac).returns(() => true); + platform.setup((x) => x.isWindows).returns(() => false); + platform.setup((x) => x.isMac).returns(() => true); await testInstallerMissingMessage(InterpreterType.Conda, async (message: string, url: string) => { verifyMessage(message, ['Pip', 'Conda'], []); verifyUrl(url, ['Mac', 'Pip', 'Conda']); @@ -101,9 +101,9 @@ suite('Installation - channel messages', () => { }); test('No installers message: Unknown/Linux', async () => { - platform.setup(x => x.isWindows).returns(() => false); - platform.setup(x => x.isMac).returns(() => false); - platform.setup(x => x.isLinux).returns(() => true); + platform.setup((x) => x.isWindows).returns(() => false); + platform.setup((x) => x.isMac).returns(() => false); + platform.setup((x) => x.isLinux).returns(() => true); await testInstallerMissingMessage(InterpreterType.Unknown, async (message: string, url: string) => { verifyMessage(message, ['Pip'], ['Conda']); verifyUrl(url, ['Linux', 'Pip']); @@ -111,9 +111,9 @@ suite('Installation - channel messages', () => { }); test('No installers message: Conda/Linux', async () => { - platform.setup(x => x.isWindows).returns(() => false); - platform.setup(x => x.isMac).returns(() => false); - platform.setup(x => x.isLinux).returns(() => true); + platform.setup((x) => x.isWindows).returns(() => false); + platform.setup((x) => x.isMac).returns(() => false); + platform.setup((x) => x.isLinux).returns(() => true); await testInstallerMissingMessage(InterpreterType.Conda, async (message: string, url: string) => { verifyMessage(message, ['Pip', 'Conda'], []); verifyUrl(url, ['Linux', 'Pip', 'Conda']); @@ -121,7 +121,7 @@ suite('Installation - channel messages', () => { }); test('No channels message', async () => { - platform.setup(x => x.isWindows).returns(() => true); + platform.setup((x) => x.isWindows).returns(() => true); await testInstallerMissingMessage( InterpreterType.Unknown, async (message: string, url: string) => { @@ -159,7 +159,7 @@ suite('Installation - channel messages', () => { path: '' }; interpreters - .setup(x => x.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((x) => x.getActiveInterpreter(TypeMoq.It.isAny())) .returns( () => new Promise((resolve, _reject) => resolve(activeInterpreter)) ); @@ -169,7 +169,7 @@ suite('Installation - channel messages', () => { let message: string = ''; let search: string = ''; appShell - .setup(x => x.showErrorMessage(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) + .setup((x) => x.showErrorMessage(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) .callback((m: string, s: string) => { message = m; search = s; @@ -178,7 +178,7 @@ suite('Installation - channel messages', () => { () => new Promise((resolve, _reject) => resolve(search)) ); appShell - .setup(x => x.openUrl(TypeMoq.It.isAnyString())) + .setup((x) => x.openUrl(TypeMoq.It.isAnyString())) .callback((s: string) => { url = s; }); diff --git a/src/test/interpreters/activation/service.unit.test.ts b/src/test/interpreters/activation/service.unit.test.ts index 18c9d9014dc9..1cb9741efcee 100644 --- a/src/test/interpreters/activation/service.unit.test.ts +++ b/src/test/interpreters/activation/service.unit.test.ts @@ -86,8 +86,8 @@ suite('Interpreters Activation - Python Environment Variables', () => { return `${resource ? 'With a resource' : 'Without a resource'}${interpreter ? ' and an interpreter' : ''}`; } - [undefined, Uri.parse('a')].forEach(resource => - [undefined, pythonInterpreter].forEach(interpreter => { + [undefined, Uri.parse('a')].forEach((resource) => + [undefined, pythonInterpreter].forEach((interpreter) => { suite(title(resource, interpreter), () => { setup(initSetup); test('Unknown os will return empty variables', async () => { @@ -98,9 +98,9 @@ suite('Interpreters Activation - Python Environment Variables', () => { expect(env).to.equal(undefined, 'Should not have any variables'); }); - const osTypes = getNamesAndValues(OSType).filter(osType => osType.value !== OSType.Unknown); + const osTypes = getNamesAndValues(OSType).filter((osType) => osType.value !== OSType.Unknown); - osTypes.forEach(osType => { + osTypes.forEach((osType) => { suite(osType.name, () => { setup(initSetup); test('getEnvironmentActivationShellCommands will be invoked', async () => { @@ -279,9 +279,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { expect(env).to.deep.equal(varsFromEnv); // All same objects. - expect(env) - .to.equal(env2) - .to.equal(env3); + expect(env).to.equal(env2).to.equal(env3); // All methods invoked only once. verify( diff --git a/src/test/interpreters/activation/terminalEnvironmentActivationService.unit.test.ts b/src/test/interpreters/activation/terminalEnvironmentActivationService.unit.test.ts index d4b078a83466..e2640ecc1d8b 100644 --- a/src/test/interpreters/activation/terminalEnvironmentActivationService.unit.test.ts +++ b/src/test/interpreters/activation/terminalEnvironmentActivationService.unit.test.ts @@ -50,8 +50,8 @@ suite('Interpreters Activation - Python Environment Variables (using terminals)' ); }); - [undefined, Uri.file('some Resource')].forEach(resource => { - [undefined, mockInterpreter].forEach(interpreter => { + [undefined, Uri.file('some Resource')].forEach((resource) => { + [undefined, mockInterpreter].forEach((interpreter) => { suite(resource ? 'With a resource' : 'Without a resource', () => { suite(interpreter ? 'With an interpreter' : 'Without an interpreter', () => { test('Should create a terminal with user defined custom env vars', async () => { diff --git a/src/test/interpreters/activation/wrapperEnvironmentActivationService.unit.test.ts b/src/test/interpreters/activation/wrapperEnvironmentActivationService.unit.test.ts index a05489223a2a..d09858a4412b 100644 --- a/src/test/interpreters/activation/wrapperEnvironmentActivationService.unit.test.ts +++ b/src/test/interpreters/activation/wrapperEnvironmentActivationService.unit.test.ts @@ -43,10 +43,10 @@ suite('Interpreters Activation - Python Environment Variables (wrap terminal and }; // tslint:disable-next-line: max-func-body-length - [undefined, Uri.file('some Resource')].forEach(resource => { + [undefined, Uri.file('some Resource')].forEach((resource) => { // tslint:disable-next-line: max-func-body-length - [undefined, mockInterpreter].forEach(interpreter => { - [undefined, path.join('a')].forEach(storagePath => { + [undefined, mockInterpreter].forEach((interpreter) => { + [undefined, path.join('a')].forEach((storagePath) => { // tslint:disable-next-line: max-func-body-length suite(resource ? 'With an extension storagepath' : 'Without an extension storagepath', () => { suite(resource ? 'With a resource' : 'Without a resource', () => { @@ -66,9 +66,9 @@ suite('Interpreters Activation - Python Environment Variables (wrap terminal and } // tslint:disable-next-line: no-any } as any; - when(crypto.createHash(anything(), anything(), anything())).thenCall(value => value); + when(crypto.createHash(anything(), anything(), anything())).thenCall((value) => value); when(experiment.inExperiment(anything())).thenReturn(true); - when(envVarsProvider.getCustomEnvironmentVariables(anything())).thenCall(value => + when(envVarsProvider.getCustomEnvironmentVariables(anything())).thenCall((value) => Promise.resolve({ key: (value || {}).toString() }) @@ -297,7 +297,7 @@ suite('Interpreters Activation - Python Environment Variables (wrap terminal and procActivation.getActivatedEnvironmentVariables(anything(), anything(), anything()) ).thrice(); }); - test('Use variables from file cache', async function() { + test('Use variables from file cache', async function () { if (!storagePath) { // tslint:disable-next-line: no-invalid-this return this.skip(); diff --git a/src/test/interpreters/autoSelection/proxy.unit.test.ts b/src/test/interpreters/autoSelection/proxy.unit.test.ts index eeec80ee7308..001e18be51df 100644 --- a/src/test/interpreters/autoSelection/proxy.unit.test.ts +++ b/src/test/interpreters/autoSelection/proxy.unit.test.ts @@ -47,7 +47,7 @@ suite('Interpreters - Auto Selection Proxy', () => { expect(eventRaised).to.be.equal(true, 'Change event not fired'); }); - [undefined, Uri.parse('one')].forEach(resource => { + [undefined, Uri.parse('one')].forEach((resource) => { const suffix = resource ? '(with a resource)' : '(without a resource)'; test(`getAutoSelectedInterpreter should return undefined when instance isn't registered ${suffix}`, () => { diff --git a/src/test/interpreters/autoSelection/rules/cached.unit.test.ts b/src/test/interpreters/autoSelection/rules/cached.unit.test.ts index db2dbb9df1d2..020bb7897b72 100644 --- a/src/test/interpreters/autoSelection/rules/cached.unit.test.ts +++ b/src/test/interpreters/autoSelection/rules/cached.unit.test.ts @@ -98,7 +98,7 @@ suite('Interpreters - Auto Selection - Cached Rule', () => { const moq = typemoq.Mock.ofInstance(rule, typemoq.MockBehavior.Loose, true); moq.callBase = true; - moq.setup(m => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny())) + moq.setup((m) => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny())) .returns(() => Promise.resolve(false)) .verifiable(typemoq.Times.once()); @@ -122,7 +122,7 @@ suite('Interpreters - Auto Selection - Cached Rule', () => { const moq = typemoq.Mock.ofInstance(rule, typemoq.MockBehavior.Loose, true); moq.callBase = true; - moq.setup(m => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny())) + moq.setup((m) => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(typemoq.Times.once()); diff --git a/src/test/interpreters/autoSelection/rules/currentPath.unit.test.ts b/src/test/interpreters/autoSelection/rules/currentPath.unit.test.ts index 6fb84d62bcfe..1215ca88af70 100644 --- a/src/test/interpreters/autoSelection/rules/currentPath.unit.test.ts +++ b/src/test/interpreters/autoSelection/rules/currentPath.unit.test.ts @@ -86,7 +86,7 @@ suite('Interpreters - Auto Selection - Current Path Rule', () => { const moq = typemoq.Mock.ofInstance(rule, typemoq.MockBehavior.Loose, true); moq.callBase = true; - moq.setup(m => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny())) + moq.setup((m) => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny())) .returns(() => Promise.resolve(false)) .verifiable(typemoq.Times.once()); @@ -105,7 +105,7 @@ suite('Interpreters - Auto Selection - Current Path Rule', () => { const moq = typemoq.Mock.ofInstance(rule, typemoq.MockBehavior.Loose, true); moq.callBase = true; - moq.setup(m => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny())) + moq.setup((m) => m.setGlobalInterpreter(typemoq.It.isAny(), typemoq.It.isAny())) .returns(() => Promise.resolve(true)) .verifiable(typemoq.Times.once()); diff --git a/src/test/interpreters/autoSelection/rules/winRegistry.unit.test.ts b/src/test/interpreters/autoSelection/rules/winRegistry.unit.test.ts index 4b6c43e74823..97d7aa9599e1 100644 --- a/src/test/interpreters/autoSelection/rules/winRegistry.unit.test.ts +++ b/src/test/interpreters/autoSelection/rules/winRegistry.unit.test.ts @@ -71,8 +71,8 @@ suite('Interpreters - Auto Selection - Windows Registry Rule', () => { ); }); - getNamesAndValues(OSType).forEach(osType => { - test(`Invoke next rule if platform is not windows (${osType.name})`, async function() { + getNamesAndValues(OSType).forEach((osType) => { + test(`Invoke next rule if platform is not windows (${osType.name})`, async function () { const manager = mock(InterpreterAutoSelectionService); if (osType.value === OSType.Windows) { return this.skip(); diff --git a/src/test/interpreters/autoSelection/rules/workspaceEnv.unit.test.ts b/src/test/interpreters/autoSelection/rules/workspaceEnv.unit.test.ts index 8fdaf830209d..254858a0220d 100644 --- a/src/test/interpreters/autoSelection/rules/workspaceEnv.unit.test.ts +++ b/src/test/interpreters/autoSelection/rules/workspaceEnv.unit.test.ts @@ -126,7 +126,7 @@ suite('Interpreters - Auto Selection - Workspace Virtual Envs Rule', () => { const pythonPathInConfig = typemoq.Mock.ofType(); const pythonPathValue = 'Hello there.exe'; pythonPathInConfig - .setup(p => p.workspaceFolderValue) + .setup((p) => p.workspaceFolderValue) .returns(() => pythonPathValue) .verifiable(typemoq.Times.once()); @@ -222,7 +222,7 @@ suite('Interpreters - Auto Selection - Workspace Virtual Envs Rule', () => { const envs = await rule.getWorkspaceVirtualEnvInterpreters(resource); expect(envs).to.be.deep.equal([interpreter2, interpreter3]); }); - [OSType.OSX, OSType.Linux].forEach(osType => { + [OSType.OSX, OSType.Linux].forEach((osType) => { test(`getWorkspaceVirtualEnvInterpreters will not return any interpreters if interpreters are not in workspace folder (${osType})`, async () => { const folderPath = path.join('one', 'two', 'three'); const interpreter1 = { path: path.join('one', 'two', 'bin', 'python.exe') }; @@ -291,7 +291,7 @@ suite('Interpreters - Auto Selection - Workspace Virtual Envs Rule', () => { const pythonPathInConfig = typemoq.Mock.ofType(); const pythonPath = { inspect: () => pythonPathInConfig.object }; pythonPathInConfig - .setup(p => p.workspaceFolderValue) + .setup((p) => p.workspaceFolderValue) .returns(() => undefined as any) .verifiable(typemoq.Times.once()); when(helper.getActiveWorkspaceUri(anything())).thenReturn({ folderUri } as any); @@ -322,7 +322,7 @@ suite('Interpreters - Auto Selection - Workspace Virtual Envs Rule', () => { const pythonPathInConfig = typemoq.Mock.ofType(); const pythonPath = { inspect: () => pythonPathInConfig.object }; pythonPathInConfig - .setup(p => p.workspaceFolderValue) + .setup((p) => p.workspaceFolderValue) .returns(() => undefined as any) .verifiable(typemoq.Times.once()); when(helper.getActiveWorkspaceUri(anything())).thenReturn({ folderUri } as any); @@ -353,7 +353,7 @@ suite('Interpreters - Auto Selection - Workspace Virtual Envs Rule', () => { const pythonPathInConfig = typemoq.Mock.ofType(); const pythonPath = { inspect: () => pythonPathInConfig.object }; pythonPathInConfig - .setup(p => p.workspaceFolderValue) + .setup((p) => p.workspaceFolderValue) .returns(() => undefined as any) .verifiable(typemoq.Times.once()); when(helper.getActiveWorkspaceUri(anything())).thenReturn({ folderUri } as any); @@ -384,7 +384,7 @@ suite('Interpreters - Auto Selection - Workspace Virtual Envs Rule', () => { const pythonPathInConfig = typemoq.Mock.ofType(); const pythonPath = { inspect: () => pythonPathInConfig.object }; pythonPathInConfig - .setup(p => p.workspaceFolderValue) + .setup((p) => p.workspaceFolderValue) .returns(() => undefined as any) .verifiable(typemoq.Times.once()); when(helper.getActiveWorkspaceUri(anything())).thenReturn({ folderUri } as any); diff --git a/src/test/interpreters/condaEnvFileService.unit.test.ts b/src/test/interpreters/condaEnvFileService.unit.test.ts index 84d31eb265f9..c7a4ceaa7e0d 100644 --- a/src/test/interpreters/condaEnvFileService.unit.test.ts +++ b/src/test/interpreters/condaEnvFileService.unit.test.ts @@ -28,11 +28,11 @@ suite('Interpreters from Conda Environments Text File', () => { const serviceContainer = TypeMoq.Mock.ofType(); const stateFactory = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPersistentStateFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(IPersistentStateFactory))) .returns(() => stateFactory.object); const state = new MockState(undefined); stateFactory - .setup(s => s.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((s) => s.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => state); condaService = TypeMoq.Mock.ofType(); @@ -46,23 +46,23 @@ suite('Interpreters from Conda Environments Text File', () => { ); }); test('Must return an empty list if environment file cannot be found', async () => { - condaService.setup(c => c.condaEnvironmentsFile).returns(() => undefined); + condaService.setup((c) => c.condaEnvironmentsFile).returns(() => undefined); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); const interpreters = await condaFileProvider.getInterpreters(); assert.equal(interpreters.length, 0, 'Incorrect number of entries'); }); test('Must return an empty list for an empty file', async () => { - condaService.setup(c => c.condaEnvironmentsFile).returns(() => environmentsFilePath); + condaService.setup((c) => c.condaEnvironmentsFile).returns(() => environmentsFilePath); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(environmentsFilePath))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(environmentsFilePath))) .returns(() => Promise.resolve(true)); fileSystem - .setup(fs => fs.readFile(TypeMoq.It.isValue(environmentsFilePath))) + .setup((fs) => fs.readFile(TypeMoq.It.isValue(environmentsFilePath))) .returns(() => Promise.resolve('')); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); const interpreters = await condaFileProvider.getInterpreters(); assert.equal(interpreters.length, 0, 'Incorrect number of entries'); @@ -78,18 +78,18 @@ suite('Interpreters from Conda Environments Text File', () => { path.join(environmentsPath, 'xyz', 'two'), path.join(environmentsPath, 'xyz', 'python.exe') ].concat(validPaths); - condaService.setup(c => c.condaEnvironmentsFile).returns(() => environmentsFilePath); + condaService.setup((c) => c.condaEnvironmentsFile).returns(() => environmentsFilePath); condaService - .setup(c => c.getInterpreterPath(TypeMoq.It.isAny())) - .returns(environmentPath => { + .setup((c) => c.getInterpreterPath(TypeMoq.It.isAny())) + .returns((environmentPath) => { return isWindows ? path.join(environmentPath, 'python.exe') : path.join(environmentPath, 'bin', 'python'); }); condaService - .setup(c => c.getCondaEnvironments(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironments(TypeMoq.It.isAny())) .returns(() => { - const condaEnvironments = validPaths.map(item => { + const condaEnvironments = validPaths.map((item) => { return { path: item, name: path.basename(item) @@ -98,21 +98,23 @@ suite('Interpreters from Conda Environments Text File', () => { return Promise.resolve(condaEnvironments); }); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(environmentsFilePath))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(environmentsFilePath))) .returns(() => Promise.resolve(true)); fileSystem - .setup(fs => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((fs) => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((p1: string, p2: string) => (isWindows ? p1 === p2 : p1.toUpperCase() === p2.toUpperCase())); - validPaths.forEach(validPath => { + validPaths.forEach((validPath) => { const pythonPath = isWindows ? path.join(validPath, 'python.exe') : path.join(validPath, 'bin', 'python'); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(true)); + fileSystem + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))) + .returns(() => Promise.resolve(true)); }); fileSystem - .setup(fs => fs.readFile(TypeMoq.It.isValue(environmentsFilePath))) + .setup((fs) => fs.readFile(TypeMoq.It.isValue(environmentsFilePath))) .returns(() => Promise.resolve(interpreterPaths.join(EOL))); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); const interpreters = await condaFileProvider.getInterpreters(); diff --git a/src/test/interpreters/condaEnvService.unit.test.ts b/src/test/interpreters/condaEnvService.unit.test.ts index b16f2c5937a6..f2610a210d38 100644 --- a/src/test/interpreters/condaEnvService.unit.test.ts +++ b/src/test/interpreters/condaEnvService.unit.test.ts @@ -25,11 +25,11 @@ suite('Interpreters from Conda Environments', () => { const serviceContainer = TypeMoq.Mock.ofType(); const stateFactory = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPersistentStateFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(IPersistentStateFactory))) .returns(() => stateFactory.object); const state = new MockState(undefined); stateFactory - .setup(s => s.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((s) => s.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => state); condaService = TypeMoq.Mock.ofType(); @@ -72,18 +72,20 @@ suite('Interpreters from Conda Environments', () => { '3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]' }; condaService - .setup(c => c.getInterpreterPath(TypeMoq.It.isAny())) - .returns(environmentPath => { + .setup((c) => c.getInterpreterPath(TypeMoq.It.isAny())) + .returns((environmentPath) => { return isWindows ? path.join(environmentPath, 'python.exe') : path.join(environmentPath, 'bin', 'python'); }); - info.envs.forEach(validPath => { + info.envs.forEach((validPath) => { const pythonPath = isWindows ? path.join(validPath, 'python.exe') : path.join(validPath, 'bin', 'python'); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(true)); + fileSystem + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))) + .returns(() => Promise.resolve(true)); }); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); const interpreters = await parseCondaInfo( @@ -129,23 +131,25 @@ suite('Interpreters from Conda Environments', () => { '3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]' }; condaService - .setup(c => c.getInterpreterPath(TypeMoq.It.isAny())) - .returns(environmentPath => { + .setup((c) => c.getInterpreterPath(TypeMoq.It.isAny())) + .returns((environmentPath) => { return isWindows ? path.join(environmentPath, 'python.exe') : path.join(environmentPath, 'bin', 'python'); }); - info.envs.forEach(validPath => { + info.envs.forEach((validPath) => { const pythonPath = isWindows ? path.join(validPath, 'python.exe') : path.join(validPath, 'bin', 'python'); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(true)); + fileSystem + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))) + .returns(() => Promise.resolve(true)); }); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); - condaService.setup(c => c.getCondaFile()).returns(() => Promise.resolve('conda')); - condaService.setup(c => c.getCondaInfo()).returns(() => Promise.resolve(info)); + condaService.setup((c) => c.getCondaFile()).returns(() => Promise.resolve('conda')); + condaService.setup((c) => c.getCondaInfo()).returns(() => Promise.resolve(info)); condaService - .setup(c => c.getCondaEnvironments(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironments(TypeMoq.It.isAny())) .returns(() => Promise.resolve([ { name: 'base', path: environmentsPath }, @@ -154,7 +158,7 @@ suite('Interpreters from Conda Environments', () => { ]) ); fileSystem - .setup(fs => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((fs) => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((p1: string, p2: string) => (isWindows ? p1 === p2 : p1.toUpperCase() === p2.toUpperCase())); const interpreters = await condaProvider.getInterpreters(); @@ -192,18 +196,20 @@ suite('Interpreters from Conda Environments', () => { 'sys.version': '3.6.1 |Anaonda 4.4.0 (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]' }; condaService - .setup(c => c.getInterpreterPath(TypeMoq.It.isAny())) - .returns(environmentPath => { + .setup((c) => c.getInterpreterPath(TypeMoq.It.isAny())) + .returns((environmentPath) => { return isWindows ? path.join(environmentPath, 'python.exe') : path.join(environmentPath, 'bin', 'python'); }); - info.envs.forEach(validPath => { + info.envs.forEach((validPath) => { const pythonPath = isWindows ? path.join(validPath, 'python.exe') : path.join(validPath, 'bin', 'python'); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(true)); + fileSystem + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))) + .returns(() => Promise.resolve(true)); }); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); const interpreters = await parseCondaInfo( @@ -237,11 +243,11 @@ suite('Interpreters from Conda Environments', () => { 'sys.version': '3.6.1 |Anaonda 4.4.0 (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]' }; interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); - condaService.setup(c => c.getCondaInfo()).returns(() => Promise.resolve(info)); + condaService.setup((c) => c.getCondaInfo()).returns(() => Promise.resolve(info)); condaService - .setup(c => c.getCondaEnvironments(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironments(TypeMoq.It.isAny())) .returns(() => Promise.resolve([ { name: 'base', path: environmentsPath }, @@ -250,21 +256,23 @@ suite('Interpreters from Conda Environments', () => { ]) ); condaService - .setup(c => c.getInterpreterPath(TypeMoq.It.isAny())) - .returns(environmentPath => { + .setup((c) => c.getInterpreterPath(TypeMoq.It.isAny())) + .returns((environmentPath) => { return isWindows ? path.join(environmentPath, 'python.exe') : path.join(environmentPath, 'bin', 'python'); }); - info.envs.forEach(validPath => { + info.envs.forEach((validPath) => { const pythonPath = isWindows ? path.join(validPath, 'python.exe') : path.join(validPath, 'bin', 'python'); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(true)); + fileSystem + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))) + .returns(() => Promise.resolve(true)); }); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); fileSystem - .setup(fs => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((fs) => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((p1: string, p2: string) => (isWindows ? p1 === p2 : p1.toUpperCase() === p2.toUpperCase())); const interpreters = await condaProvider.getInterpreters(); @@ -291,18 +299,20 @@ suite('Interpreters from Conda Environments', () => { envs: [path.join(environmentsPath, 'conda', 'envs', 'numpy')] }; condaService - .setup(c => c.getInterpreterPath(TypeMoq.It.isAny())) - .returns(environmentPath => { + .setup((c) => c.getInterpreterPath(TypeMoq.It.isAny())) + .returns((environmentPath) => { return isWindows ? path.join(environmentPath, 'python.exe') : path.join(environmentPath, 'bin', 'python'); }); - info.envs.forEach(validPath => { + info.envs.forEach((validPath) => { const pythonPath = isWindows ? path.join(validPath, 'python.exe') : path.join(validPath, 'bin', 'python'); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(true)); + fileSystem + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))) + .returns(() => Promise.resolve(true)); }); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); const interpreters = await parseCondaInfo( @@ -335,23 +345,25 @@ suite('Interpreters from Conda Environments', () => { envs: [path.join(environmentsPath, 'conda', 'envs', 'numpy')] }; condaService - .setup(c => c.getInterpreterPath(TypeMoq.It.isAny())) - .returns(environmentPath => { + .setup((c) => c.getInterpreterPath(TypeMoq.It.isAny())) + .returns((environmentPath) => { return isWindows ? path.join(environmentPath, 'python.exe') : path.join(environmentPath, 'bin', 'python'); }); - info.envs.forEach(validPath => { + info.envs.forEach((validPath) => { const pythonPath = isWindows ? path.join(validPath, 'python.exe') : path.join(validPath, 'bin', 'python'); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(true)); + fileSystem + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))) + .returns(() => Promise.resolve(true)); }); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); - condaService.setup(c => c.getCondaFile()).returns(() => Promise.resolve('conda')); - condaService.setup(c => c.getCondaInfo()).returns(() => Promise.resolve(info)); + condaService.setup((c) => c.getCondaFile()).returns(() => Promise.resolve('conda')); + condaService.setup((c) => c.getCondaInfo()).returns(() => Promise.resolve(info)); condaService - .setup(c => c.getCondaEnvironments(TypeMoq.It.isAny())) + .setup((c) => c.getCondaEnvironments(TypeMoq.It.isAny())) .returns(() => Promise.resolve([ { name: 'base', path: environmentsPath }, @@ -360,7 +372,7 @@ suite('Interpreters from Conda Environments', () => { ]) ); fileSystem - .setup(fs => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((fs) => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((p1: string, p2: string) => (isWindows ? p1 === p2 : p1.toUpperCase() === p2.toUpperCase())); const interpreters = await condaProvider.getInterpreters(); @@ -387,8 +399,8 @@ suite('Interpreters from Conda Environments', () => { default_prefix: path.join(environmentsPath, 'conda', 'envs', 'numpy') }; condaService - .setup(c => c.getInterpreterPath(TypeMoq.It.isAny())) - .returns(environmentPath => { + .setup((c) => c.getInterpreterPath(TypeMoq.It.isAny())) + .returns((environmentPath) => { return isWindows ? path.join(environmentPath, 'python.exe') : path.join(environmentPath, 'bin', 'python'); @@ -396,9 +408,9 @@ suite('Interpreters from Conda Environments', () => { const pythonPath = isWindows ? path.join(info.default_prefix, 'python.exe') : path.join(info.default_prefix, 'bin', 'python'); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(true)); + fileSystem.setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(true)); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); const interpreters = await parseCondaInfo( @@ -438,18 +450,20 @@ suite('Interpreters from Conda Environments', () => { }; const validPaths = info.envs.filter((_, index) => index % 2 === 0); interpreterHelper - .setup(i => i.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((i) => i.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: undefined })); - validPaths.forEach(envPath => { + validPaths.forEach((envPath) => { condaService - .setup(c => c.getInterpreterPath(TypeMoq.It.isValue(envPath))) - .returns(environmentPath => { + .setup((c) => c.getInterpreterPath(TypeMoq.It.isValue(envPath))) + .returns((environmentPath) => { return isWindows ? path.join(environmentPath, 'python.exe') : path.join(environmentPath, 'bin', 'python'); }); const pythonPath = isWindows ? path.join(envPath, 'python.exe') : path.join(envPath, 'bin', 'python'); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(true)); + fileSystem + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))) + .returns(() => Promise.resolve(true)); }); const interpreters = await parseCondaInfo( diff --git a/src/test/interpreters/condaService.unit.test.ts b/src/test/interpreters/condaService.unit.test.ts index e11e904b27a5..fbe60a15e47c 100644 --- a/src/test/interpreters/condaService.unit.test.ts +++ b/src/test/interpreters/condaService.unit.test.ts @@ -70,48 +70,48 @@ suite('Interpreters Conda Service', () => { procServiceFactory = TypeMoq.Mock.ofType(); processService.setup((x: any) => x.then).returns(() => undefined); procServiceFactory - .setup(p => p.create(TypeMoq.It.isAny())) + .setup((p) => p.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(processService.object)); disposableRegistry = []; const e = new EventEmitter(); - interpreterService.setup(x => x.onDidChangeInterpreter).returns(() => e.event); + interpreterService.setup((x) => x.onDidChangeInterpreter).returns(() => e.event); resetMockState(undefined); persistentStateFactory - .setup(s => s.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((s) => s.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => mockState); terminalProvider = TypeMoq.Mock.ofType(); - terminalProvider.setup(p => p.isShellSupported(TypeMoq.It.isAny())).returns(() => true); + terminalProvider.setup((p) => p.isShellSupported(TypeMoq.It.isAny())).returns(() => true); terminalProvider - .setup(p => p.getActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.getActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(['activate'])); terminalProvider - .setup(p => p.getActivationCommandsForInterpreter!(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.getActivationCommandsForInterpreter!(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(['activate'])); serviceContainer = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())) .returns(() => procServiceFactory.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) .returns(() => platformService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) .returns(() => fileSystem.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) .returns(() => config.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ITerminalActivationCommandProvider), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ITerminalActivationCommandProvider), TypeMoq.It.isAny())) .returns(() => terminalProvider.object); serviceContainer - .setup(c => c.getAll(TypeMoq.It.isValue(ITerminalActivationCommandProvider), TypeMoq.It.isAny())) + .setup((c) => c.getAll(TypeMoq.It.isValue(ITerminalActivationCommandProvider), TypeMoq.It.isAny())) .returns(() => [terminalProvider.object]); - config.setup(c => c.getSettings(TypeMoq.It.isValue(undefined))).returns(() => settings.object); - settings.setup(p => p.condaPath).returns(() => condaPathSetting); + config.setup((c) => c.getSettings(TypeMoq.It.isValue(undefined))).returns(() => settings.object); + settings.setup((p) => p.condaPath).returns(() => condaPathSetting); fileSystem - .setup(fs => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((fs) => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((p1, p2) => { const utils = FileSystemPathUtils.withDefaults( FileSystemPaths.withDefaults(platformService.object.isWindows) @@ -141,9 +141,9 @@ suite('Interpreters Conda Service', () => { isLinux: boolean, pythonPath: string ) { - platformService.setup(p => p.isLinux).returns(() => isLinux); - platformService.setup(p => p.isWindows).returns(() => isWindows); - platformService.setup(p => p.isMac).returns(() => isOsx); + platformService.setup((p) => p.isLinux).returns(() => isLinux); + platformService.setup((p) => p.isWindows).returns(() => isWindows); + platformService.setup((p) => p.isMac).returns(() => isOsx); const isCondaEnv = await condaService.isCondaEnvironment(pythonPath); expect(isCondaEnv).to.be.equal(true, 'Path not identified as a conda path'); @@ -152,7 +152,7 @@ suite('Interpreters Conda Service', () => { test('Correctly identifies a python path as a conda environment (windows)', async () => { const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'enva', 'python.exe'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(true)); await identifyPythonPathAsCondaEnvironment(true, false, false, pythonPath); }); @@ -160,7 +160,9 @@ suite('Interpreters Conda Service', () => { test('Correctly identifies a python path as a conda environment (linux)', async () => { const pythonPath = path.join('users', 'xyz', '.conda', 'envs', 'enva', 'bin', 'python'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(true)); await identifyPythonPathAsCondaEnvironment(false, false, true, pythonPath); }); @@ -168,7 +170,9 @@ suite('Interpreters Conda Service', () => { test('Correctly identifies a python path as a conda environment (osx)', async () => { const pythonPath = path.join('users', 'xyz', '.conda', 'envs', 'enva', 'bin', 'python'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(true)); await identifyPythonPathAsCondaEnvironment(false, true, false, pythonPath); }); @@ -179,15 +183,17 @@ suite('Interpreters Conda Service', () => { isLinux: boolean, pythonPath: string ) { - platformService.setup(p => p.isLinux).returns(() => isLinux); - platformService.setup(p => p.isWindows).returns(() => isWindows); - platformService.setup(p => p.isMac).returns(() => isOsx); + platformService.setup((p) => p.isLinux).returns(() => isLinux); + platformService.setup((p) => p.isWindows).returns(() => isWindows); + platformService.setup((p) => p.isMac).returns(() => isOsx); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(false)); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(false)); const isCondaEnv = await condaService.isCondaEnvironment(pythonPath); @@ -225,9 +231,9 @@ suite('Interpreters Conda Service', () => { { name: 'nine 9', path: path.join(condaEnvsPath, 'nine 9') } ]; - platformService.setup(p => p.isLinux).returns(() => isLinux); - platformService.setup(p => p.isWindows).returns(() => isWindows); - platformService.setup(p => p.isMac).returns(() => isOsx); + platformService.setup((p) => p.isLinux).returns(() => isLinux); + platformService.setup((p) => p.isWindows).returns(() => isWindows); + platformService.setup((p) => p.isMac).returns(() => isOsx); resetMockState({ data: condaEnvironments }); @@ -240,7 +246,7 @@ suite('Interpreters Conda Service', () => { const condaEnvDir = path.join('c', 'users', 'xyz', '.conda', 'envs'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(true)); await checkCondaNameAndPathForCondaEnvironments(true, false, false, pythonPath, condaEnvDir, { name: 'One', @@ -253,7 +259,7 @@ suite('Interpreters Conda Service', () => { const condaEnvDir = path.join('c', 'users', 'xyz', '.conda', 'envs'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(true)); await checkCondaNameAndPathForCondaEnvironments(true, false, false, pythonPath, condaEnvDir, { name: 'Eight', @@ -266,7 +272,9 @@ suite('Interpreters Conda Service', () => { const condaEnvDir = path.join('c', 'users', 'xyz', '.conda', 'envs'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(true)); await checkCondaNameAndPathForCondaEnvironments(false, true, false, pythonPath, condaEnvDir, { name: 'One', @@ -279,7 +287,9 @@ suite('Interpreters Conda Service', () => { const condaEnvDir = path.join('c', 'users', 'xyz', '.conda', 'envs'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(true)); await checkCondaNameAndPathForCondaEnvironments(false, true, false, pythonPath, condaEnvDir, { name: 'Eight', @@ -292,7 +302,9 @@ suite('Interpreters Conda Service', () => { const condaEnvDir = path.join('c', 'users', 'xyz', '.conda', 'envs'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(true)); await checkCondaNameAndPathForCondaEnvironments(false, false, true, pythonPath, condaEnvDir, { name: 'One', @@ -305,7 +317,9 @@ suite('Interpreters Conda Service', () => { const condaEnvDir = path.join('c', 'users', 'xyz', '.conda', 'envs'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta')))) + .setup((f) => + f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), '..', 'conda-meta'))) + ) .returns(() => Promise.resolve(true)); await checkCondaNameAndPathForCondaEnvironments(false, false, true, pythonPath, condaEnvDir, { name: 'Eight', @@ -325,12 +339,12 @@ suite('Interpreters Conda Service', () => { { name: 'nine 9', path: path.join(condaEnvsPath, 'nine 9') } ]; - platformService.setup(p => p.isLinux).returns(() => false); - platformService.setup(p => p.isWindows).returns(() => true); - platformService.setup(p => p.isMac).returns(() => false); + platformService.setup((p) => p.isLinux).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => true); + platformService.setup((p) => p.isMac).returns(() => false); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(true)); resetMockState({ data: condaEnvironments }); @@ -347,10 +361,10 @@ suite('Interpreters Conda Service', () => { ]; processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'xyz' })); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: envList.join(EOL) })); const condaEnv = await condaService.getCondaEnvironment(pythonPath); @@ -373,12 +387,12 @@ suite('Interpreters Conda Service', () => { { name: 'nine 9', path: path.join(condaEnvsPath, 'nine 9') } ]; - platformService.setup(p => p.isLinux).returns(() => false); - platformService.setup(p => p.isWindows).returns(() => true); - platformService.setup(p => p.isMac).returns(() => false); + platformService.setup((p) => p.isLinux).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => true); + platformService.setup((p) => p.isMac).returns(() => false); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(true)); resetMockState({ data: condaEnvironments }); @@ -394,10 +408,10 @@ suite('Interpreters Conda Service', () => { ]; processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'xyz' })); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: envList.join(EOL) })); const condaEnv = await condaService.getCondaEnvironment(pythonPath); @@ -435,23 +449,23 @@ suite('Interpreters Conda Service', () => { companyDisplayName: 'Continuum Analytics, Inc.', type: InterpreterType.Unknown } - ].map(item => { + ].map((item) => { return { ...info, ...item }; }); - const condaInterpreterIndex = registryInterpreters.findIndex(i => i.displayName === 'Anaconda'); + const condaInterpreterIndex = registryInterpreters.findIndex((i) => i.displayName === 'Anaconda'); const expectedCodnaPath = path.join( path.dirname(registryInterpreters[condaInterpreterIndex].path), 'conda.exe' ); - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.reject(new Error('Not Found'))); registryInterpreterLocatorService - .setup(r => r.getInterpreters(TypeMoq.It.isAny())) + .setup((r) => r.getInterpreters(TypeMoq.It.isAny())) .returns(() => Promise.resolve(registryInterpreters)); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isAny())) + .setup((fs) => fs.fileExists(TypeMoq.It.isAny())) .returns((file: string) => Promise.resolve(file === expectedCodnaPath)); const condaExe = await condaService.getCondaFile(); @@ -509,20 +523,20 @@ suite('Interpreters Conda Service', () => { companyDisplayName: 'Continuum Analytics, Inc.', type: InterpreterType.Unknown } - ].map(item => { + ].map((item) => { return { ...info, ...item }; }); const indexOfLatestVersion = 3; const expectedCodnaPath = path.join(path.dirname(registryInterpreters[indexOfLatestVersion].path), 'conda.exe'); - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.reject(new Error('Not Found'))); registryInterpreterLocatorService - .setup(r => r.getInterpreters(TypeMoq.It.isAny())) + .setup((r) => r.getInterpreters(TypeMoq.It.isAny())) .returns(() => Promise.resolve(registryInterpreters)); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isAny())) + .setup((fs) => fs.fileExists(TypeMoq.It.isAny())) .returns((file: string) => Promise.resolve(file === expectedCodnaPath)); const condaExe = await condaService.getCondaFile(); @@ -580,18 +594,18 @@ suite('Interpreters Conda Service', () => { companyDisplayName: 'Continuum Analytics, Inc.', type: InterpreterType.Unknown } - ].map(item => { + ].map((item) => { return { ...info, ...item }; }); - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.reject(new Error('Not Found'))); registryInterpreterLocatorService - .setup(r => r.getInterpreters(TypeMoq.It.isAny())) + .setup((r) => r.getInterpreters(TypeMoq.It.isAny())) .returns(() => Promise.resolve(registryInterpreters)); - fileSystem.setup(fs => fs.search(TypeMoq.It.isAnyString())).returns(async () => []); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isAny())).returns((_file: string) => Promise.resolve(false)); + fileSystem.setup((fs) => fs.search(TypeMoq.It.isAnyString())).returns(async () => []); + fileSystem.setup((fs) => fs.fileExists(TypeMoq.It.isAny())).returns((_file: string) => Promise.resolve(false)); const condaExe = await condaService.getCondaFile(); assert.equal(condaExe, 'conda', 'Failed to identify conda.exe'); @@ -600,9 +614,9 @@ suite('Interpreters Conda Service', () => { test('Get conda file from default/known locations', async () => { const expected = 'C:/ProgramData/Miniconda2/Scripts/conda.exe'; - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); - fileSystem.setup(f => f.search(TypeMoq.It.isAnyString())).returns(() => Promise.resolve([expected])); + fileSystem.setup((f) => f.search(TypeMoq.It.isAnyString())).returns(() => Promise.resolve([expected])); const CondaServiceForTesting = class extends CondaService { public async isCondaInCurrentPath() { return false; @@ -626,7 +640,7 @@ suite('Interpreters Conda Service', () => { condaPathSetting = 'spam-spam-conda-spam-spam'; // We ensure that conda would otherwise be found. processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']))) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']))) .returns(() => Promise.resolve({ stdout: 'xyz' })) .verifiable(TypeMoq.Times.never()); @@ -635,39 +649,39 @@ suite('Interpreters Conda Service', () => { // We should not try to call other unwanted methods. processService.verifyAll(); - registryInterpreterLocatorService.verify(r => r.getInterpreters(TypeMoq.It.isAny()), TypeMoq.Times.never()); + registryInterpreterLocatorService.verify((r) => r.getInterpreters(TypeMoq.It.isAny()), TypeMoq.Times.never()); }); test("Must use 'conda' if is available in the current path", async () => { processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']))) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']))) .returns(() => Promise.resolve({ stdout: 'xyz' })); const condaExe = await condaService.getCondaFile(); assert.equal(condaExe, 'conda', 'Failed to identify conda.exe'); // We should not try to call other unwanted methods. - registryInterpreterLocatorService.verify(r => r.getInterpreters(TypeMoq.It.isAny()), TypeMoq.Times.never()); + registryInterpreterLocatorService.verify((r) => r.getInterpreters(TypeMoq.It.isAny()), TypeMoq.Times.never()); }); test('Must invoke process only once to check if conda is in the current path', async () => { processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']))) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']))) .returns(() => Promise.resolve({ stdout: 'xyz' })); const condaExe = await condaService.getCondaFile(); assert.equal(condaExe, 'conda', 'Failed to identify conda.exe'); processService.verify( - p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (p) => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once() ); // We should not try to call other unwanted methods. - registryInterpreterLocatorService.verify(r => r.getInterpreters(TypeMoq.It.isAny()), TypeMoq.Times.never()); + registryInterpreterLocatorService.verify((r) => r.getInterpreters(TypeMoq.It.isAny()), TypeMoq.Times.never()); await condaService.getCondaFile(); processService.verify( - p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (p) => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once() ); }); @@ -679,18 +693,20 @@ suite('Interpreters Conda Service', () => { '~/miniconda2/bin/conda', '~/anaconda3/bin/conda', '~/miniconda3/bin/conda' - ].forEach(knownLocation => { + ].forEach((knownLocation) => { test(`Must return conda path from known location '${knownLocation}' (non windows)`, async () => { const expectedCondaLocation = untildify(knownLocation); - platformService.setup(p => p.isWindows).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => false); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => + p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny()) + ) .returns(() => Promise.reject(new Error('Not Found'))); fileSystem - .setup(fs => fs.search(TypeMoq.It.isAny())) + .setup((fs) => fs.search(TypeMoq.It.isAny())) .returns(() => Promise.resolve([expectedCondaLocation])); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(expectedCondaLocation))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(expectedCondaLocation))) .returns(() => Promise.resolve(true)); const condaExe = await condaService.getCondaFile(); @@ -699,12 +715,12 @@ suite('Interpreters Conda Service', () => { }); test("Must return 'conda' if conda could not be found in known locations", async () => { - platformService.setup(p => p.isWindows).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => false); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.reject(new Error('Not Found'))); - fileSystem.setup(fs => fs.search(TypeMoq.It.isAny())).returns(() => Promise.resolve([])); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isAny())).returns((_file: string) => Promise.resolve(false)); + fileSystem.setup((fs) => fs.search(TypeMoq.It.isAny())).returns(() => Promise.resolve([])); + fileSystem.setup((fs) => fs.fileExists(TypeMoq.It.isAny())).returns((_file: string) => Promise.resolve(false)); const condaExe = await condaService.getCondaFile(); assert.equal(condaExe, 'conda', 'Failed to identify'); @@ -712,14 +728,14 @@ suite('Interpreters Conda Service', () => { test('Correctly identify interpreter location relative to entironment path (non windows)', async () => { const environmentPath = path.join('a', 'b', 'c'); - platformService.setup(p => p.isWindows).returns(() => false); + platformService.setup((p) => p.isWindows).returns(() => false); const pythonPath = condaService.getInterpreterPath(environmentPath); assert.equal(pythonPath, path.join(environmentPath, 'bin', 'python'), 'Incorrect path'); }); test('Correctly identify interpreter location relative to entironment path (windows)', async () => { const environmentPath = path.join('a', 'b', 'c'); - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); const pythonPath = condaService.getInterpreterPath(environmentPath); assert.equal(pythonPath, path.join(environmentPath, 'python.exe'), 'Incorrect path'); }); @@ -735,10 +751,12 @@ suite('Interpreters Conda Service', () => { '3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]' }; processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'xyz' })); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['info', '--json']), TypeMoq.It.isAny())) + .setup((p) => + p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['info', '--json']), TypeMoq.It.isAny()) + ) .returns(() => Promise.resolve({ stdout: JSON.stringify(expectedInfo) })); const condaInfo = await condaService.getCondaInfo(); @@ -747,10 +765,12 @@ suite('Interpreters Conda Service', () => { test("Returns undefined if there's and error in getting the info", async () => { processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'xyz' })); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['info', '--json']), TypeMoq.It.isAny())) + .setup((p) => + p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['info', '--json']), TypeMoq.It.isAny()) + ) .returns(() => Promise.reject(new Error('unknown'))); const condaInfo = await condaService.getCondaInfo(); @@ -759,10 +779,10 @@ suite('Interpreters Conda Service', () => { test('Returns conda environments when conda exists', async () => { processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'xyz' })); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: '' })); const environments = await condaService.getCondaEnvironments(true); assert.equal(environments, undefined, 'Conda environments do not match'); @@ -770,10 +790,10 @@ suite('Interpreters Conda Service', () => { test('Logs information message when conda does not exist', async () => { processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.reject(new Error('Not Found'))); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) .returns(() => Promise.reject(new Error('Not Found'))); const environments = await condaService.getCondaEnvironments(true); assert.equal(environments, undefined, 'Conda environments do not match'); @@ -783,10 +803,10 @@ suite('Interpreters Conda Service', () => { resetMockState({ data: 'CachedInfo' }); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'xyz' })); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: '' })); const environments = await condaService.getCondaEnvironments(false); assert.equal(environments, 'CachedInfo', 'Conda environments do not match'); @@ -805,10 +825,10 @@ suite('Interpreters Conda Service', () => { ]; processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'xyz' })); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: envList.join(EOL) })); const environments = await condaService.getCondaEnvironments(false); expect(environments).lengthOf(6, 'Incorrect number of environments'); @@ -821,10 +841,12 @@ suite('Interpreters Conda Service', () => { test("Returns undefined if there's and error in getting the info", async () => { processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'xyz' })); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['info', '--json']), TypeMoq.It.isAny())) + .setup((p) => + p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['info', '--json']), TypeMoq.It.isAny()) + ) .returns(() => Promise.reject(new Error('unknown'))); const condaInfo = await condaService.getCondaInfo(); @@ -873,19 +895,21 @@ suite('Interpreters Conda Service', () => { companyDisplayName: 'Continuum Analytics, Inc.', type: InterpreterType.Unknown } - ].map(item => { + ].map((item) => { return { ...info, ...item }; }); const expectedCodaExe = path.join(path.dirname(condaPythonExePath), 'conda.exe'); - platformService.setup(p => p.isWindows).returns(() => true); + platformService.setup((p) => p.isWindows).returns(() => true); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.reject(new Error('Not Found'))); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(expectedCodaExe))).returns(() => Promise.resolve(true)); + fileSystem + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(expectedCodaExe))) + .returns(() => Promise.resolve(true)); registryInterpreterLocatorService - .setup(r => r.getInterpreters(TypeMoq.It.isAny())) + .setup((r) => r.getInterpreters(TypeMoq.It.isAny())) .returns(() => Promise.resolve(registryInterpreters)); const condaExe = await condaService.getCondaFile(); @@ -894,7 +918,7 @@ suite('Interpreters Conda Service', () => { test('isAvailable will return true if conda is available', async () => { processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: '4.4.4' })); const isAvailable = await condaService.isCondaAvailable(); assert.equal(isAvailable, true); @@ -903,11 +927,11 @@ suite('Interpreters Conda Service', () => { test('isAvailable will return false if conda is not available', async () => { condaService.getCondaFile = () => Promise.resolve('conda'); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.reject(new Error('not found'))); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); - fileSystem.setup(fs => fs.search(TypeMoq.It.isAny())).returns(() => Promise.resolve([])); - platformService.setup(p => p.isWindows).returns(() => false); + fileSystem.setup((fs) => fs.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); + fileSystem.setup((fs) => fs.search(TypeMoq.It.isAny())).returns(() => Promise.resolve([])); + platformService.setup((p) => p.isWindows).returns(() => false); condaService.getCondaInfo = () => Promise.reject('Not Found'); const isAvailable = await condaService.isCondaAvailable(); assert.equal(isAvailable, false); @@ -918,7 +942,7 @@ suite('Interpreters Conda Service', () => { condaService.getCondaFile = () => Promise.resolve('conda'); const expectedVersion = parse('4.4.4')!.raw; processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: '4.4.4' })); const version = await condaService.getCondaVersion(); @@ -927,7 +951,7 @@ suite('Interpreters Conda Service', () => { test('isCondaInCurrentPath will return true if conda is available', async () => { processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'xyz' })); const isAvailable = await condaService.isCondaInCurrentPath(); assert.equal(isAvailable, true); @@ -935,10 +959,10 @@ suite('Interpreters Conda Service', () => { test('isCondaInCurrentPath will return false if conda is not available', async () => { processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.reject(new Error('not found'))); - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); - platformService.setup(p => p.isWindows).returns(() => false); + fileSystem.setup((fs) => fs.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); + platformService.setup((p) => p.isWindows).returns(() => false); const isAvailable = await condaService.isCondaInCurrentPath(); assert.equal(isAvailable, false); @@ -950,16 +974,16 @@ suite('Interpreters Conda Service', () => { isLinux: boolean, pythonPath: string ) { - platformService.setup(p => p.isLinux).returns(() => isLinux); - platformService.setup(p => p.isWindows).returns(() => isWindows); - platformService.setup(p => p.isMac).returns(() => isOsx); + platformService.setup((p) => p.isLinux).returns(() => isLinux); + platformService.setup((p) => p.isWindows).returns(() => isWindows); + platformService.setup((p) => p.isMac).returns(() => isOsx); resetMockState({ data: undefined }); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'some value' })); processService - .setup(p => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('conda'), TypeMoq.It.isValue(['env', 'list']), TypeMoq.It.isAny())) .returns(() => Promise.reject(new Error('Failed'))); const condaEnv = await condaService.getCondaEnvironment(pythonPath); expect(condaEnv).to.be.equal(undefined, 'Conda should be undefined'); @@ -967,21 +991,21 @@ suite('Interpreters Conda Service', () => { test('Fails to identify an environment as a conda env (windows)', async () => { const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'one', 'python.exe'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(true)); await testFailureOfGettingCondaEnvironments(true, false, false, pythonPath); }); test('Fails to identify an environment as a conda env (linux)', async () => { const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'one', 'python'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(true)); await testFailureOfGettingCondaEnvironments(false, false, true, pythonPath); }); test('Fails to identify an environment as a conda env (osx)', async () => { const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'one', 'python'); fileSystem - .setup(f => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) + .setup((f) => f.directoryExists(TypeMoq.It.isValue(path.join(path.dirname(pythonPath), 'conda-meta')))) .returns(() => Promise.resolve(true)); await testFailureOfGettingCondaEnvironments(false, true, false, pythonPath); }); @@ -1020,15 +1044,15 @@ suite('Interpreters Conda Service', () => { } ]; - testsForInterpreter.forEach(t => { + testsForInterpreter.forEach((t) => { test(`Finds conda.exe for subenvironment ${t.environmentName}`, async () => { - platformService.setup(p => p.isLinux).returns(() => t.isLinux); - platformService.setup(p => p.isWindows).returns(() => !t.isLinux); - platformService.setup(p => p.isMac).returns(() => false); + platformService.setup((p) => p.isLinux).returns(() => t.isLinux); + platformService.setup((p) => p.isWindows).returns(() => !t.isLinux); + platformService.setup((p) => p.isMac).returns(() => false); fileSystem - .setup(f => + .setup((f) => f.fileExists( - TypeMoq.It.is(p => { + TypeMoq.It.is((p) => { if (p === t.expectedCondaPath) { return true; } @@ -1042,13 +1066,13 @@ suite('Interpreters Conda Service', () => { assert.equal(condaFile, t.expectedCondaPath); }); test(`Finds conda.exe for different ${t.environmentName}`, async () => { - platformService.setup(p => p.isLinux).returns(() => t.isLinux); - platformService.setup(p => p.isWindows).returns(() => !t.isLinux); - platformService.setup(p => p.isMac).returns(() => false); + platformService.setup((p) => p.isLinux).returns(() => t.isLinux); + platformService.setup((p) => p.isWindows).returns(() => !t.isLinux); + platformService.setup((p) => p.isMac).returns(() => false); fileSystem - .setup(f => + .setup((f) => f.fileExists( - TypeMoq.It.is(p => { + TypeMoq.It.is((p) => { if (p === t.expectedCondaPath) { return true; } diff --git a/src/test/interpreters/currentPathService.unit.test.ts b/src/test/interpreters/currentPathService.unit.test.ts index 02668f114b90..e97ad5f24195 100644 --- a/src/test/interpreters/currentPathService.unit.test.ts +++ b/src/test/interpreters/currentPathService.unit.test.ts @@ -44,37 +44,37 @@ suite('Interpreters CurrentPath Service', () => { interpreterHelper = TypeMoq.Mock.ofType(); const configurationService = TypeMoq.Mock.ofType(); pythonSettings = TypeMoq.Mock.ofType(); - configurationService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + configurationService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); const persistentStateFactory = TypeMoq.Mock.ofType(); persistentState = TypeMoq.Mock.ofType>(); processService.setup((x: any) => x.then).returns(() => undefined); - persistentState.setup(p => p.value).returns(() => undefined as any); - persistentState.setup(p => p.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + persistentState.setup((p) => p.value).returns(() => undefined as any); + persistentState.setup((p) => p.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); fileSystem = TypeMoq.Mock.ofType(); platformService = TypeMoq.Mock.ofType(); persistentStateFactory - .setup(p => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => persistentState.object); const procServiceFactory = TypeMoq.Mock.ofType(); procServiceFactory - .setup(p => p.create(TypeMoq.It.isAny())) + .setup((p) => p.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(processService.object)); serviceContainer = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IVirtualEnvironmentManager), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IVirtualEnvironmentManager), TypeMoq.It.isAny())) .returns(() => virtualEnvironmentManager.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterVersionService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterVersionService), TypeMoq.It.isAny())) .returns(() => interpreterHelper.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) .returns(() => fileSystem.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPersistentStateFactory), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPersistentStateFactory), TypeMoq.It.isAny())) .returns(() => persistentStateFactory.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) .returns(() => configurationService.object); pythonInPathCommandProvider = new PythonInPathCommandProvider(platformService.object); currentPathService = new CurrentPathService( @@ -85,51 +85,53 @@ suite('Interpreters CurrentPath Service', () => { ); }); - [true, false].forEach(isWindows => { + [true, false].forEach((isWindows) => { test(`Interpreters that do not exist on the file system are not excluded from the list (${ isWindows ? 'windows' : 'not windows' })`, async () => { // Specific test for 1305 const version = new SemVer('1.0.0'); - platformService.setup(p => p.isWindows).returns(() => isWindows); - platformService.setup(p => p.osType).returns(() => (isWindows ? OSType.Windows : OSType.Linux)); + platformService.setup((p) => p.isWindows).returns(() => isWindows); + platformService.setup((p) => p.osType).returns(() => (isWindows ? OSType.Windows : OSType.Linux)); interpreterHelper - .setup(v => v.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((v) => v.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version })); const execArgs = ['-c', 'import sys;print(sys.executable)']; - pythonSettings.setup(p => p.pythonPath).returns(() => 'root:Python'); + pythonSettings.setup((p) => p.pythonPath).returns(() => 'root:Python'); processService - .setup(p => p.exec(TypeMoq.It.isValue('root:Python'), TypeMoq.It.isValue(execArgs), TypeMoq.It.isAny())) + .setup((p) => + p.exec(TypeMoq.It.isValue('root:Python'), TypeMoq.It.isValue(execArgs), TypeMoq.It.isAny()) + ) .returns(() => Promise.resolve({ stdout: 'c:/root:python' })) .verifiable(TypeMoq.Times.once()); processService - .setup(p => p.exec(TypeMoq.It.isValue('python'), TypeMoq.It.isValue(execArgs), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('python'), TypeMoq.It.isValue(execArgs), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'c:/python1' })) .verifiable(TypeMoq.Times.once()); processService - .setup(p => p.exec(TypeMoq.It.isValue('python2'), TypeMoq.It.isValue(execArgs), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('python2'), TypeMoq.It.isValue(execArgs), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'c:/python2' })) .verifiable(TypeMoq.Times.once()); processService - .setup(p => p.exec(TypeMoq.It.isValue('python3'), TypeMoq.It.isValue(execArgs), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('python3'), TypeMoq.It.isValue(execArgs), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'c:/python3' })) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue('c:/root:python'))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue('c:/root:python'))) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue('c:/python1'))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue('c:/python1'))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue('c:/python2'))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue('c:/python2'))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue('c:/python3'))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue('c:/python3'))) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); diff --git a/src/test/interpreters/display.unit.test.ts b/src/test/interpreters/display.unit.test.ts index b44a986d4f09..f2c51a0f3fc4 100644 --- a/src/test/interpreters/display.unit.test.ts +++ b/src/test/interpreters/display.unit.test.ts @@ -66,50 +66,50 @@ suite('Interpreters Display', () => { autoSelection = mock(InterpreterAutoSelectionService); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IApplicationShell))) + .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell))) .returns(() => applicationShell.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService))) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService))) .returns(() => interpreterService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IVirtualEnvironmentManager))) + .setup((c) => c.get(TypeMoq.It.isValue(IVirtualEnvironmentManager))) .returns(() => virtualEnvMgr.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => disposableRegistry); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => disposableRegistry); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configurationService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterHelper))) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterHelper))) .returns(() => interpreterHelper.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPathUtils))).returns(() => pathUtils.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IPathUtils))).returns(() => pathUtils.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterAutoSelectionService))) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterAutoSelectionService))) .returns(() => instance(autoSelection)); applicationShell - .setup(a => a.createStatusBarItem(TypeMoq.It.isValue(StatusBarAlignment.Left), TypeMoq.It.isValue(100))) + .setup((a) => a.createStatusBarItem(TypeMoq.It.isValue(StatusBarAlignment.Left), TypeMoq.It.isValue(100))) .returns(() => statusBar.object); - pathUtils.setup(p => p.getDisplayName(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(p => p); + pathUtils.setup((p) => p.getDisplayName(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((p) => p); interpreterDisplay = new InterpreterDisplay(serviceContainer.object); }); function setupWorkspaceFolder(resource: Uri, workspaceFolder?: Uri) { if (workspaceFolder) { const mockFolder = TypeMoq.Mock.ofType(); - mockFolder.setup(w => w.uri).returns(() => workspaceFolder); + mockFolder.setup((w) => w.uri).returns(() => workspaceFolder); workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(resource))) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource))) .returns(() => mockFolder.object); } else { - workspaceService.setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(resource))).returns(() => undefined); + workspaceService.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource))).returns(() => undefined); } } test('Sattusbar must be created and have command name initialized', () => { - statusBar.verify(s => (s.command = TypeMoq.It.isValue('python.setInterpreter')), TypeMoq.Times.once()); + statusBar.verify((s) => (s.command = TypeMoq.It.isValue('python.setInterpreter')), TypeMoq.Times.once()); expect(disposableRegistry).to.be.lengthOf.above(0); expect(disposableRegistry).contain(statusBar.object); }); @@ -125,17 +125,17 @@ suite('Interpreters Display', () => { setupWorkspaceFolder(resource, workspaceFolder); when(autoSelection.autoSelectInterpreter(anything())).thenResolve(); interpreterService - .setup(i => i.getInterpreters(TypeMoq.It.isValue(workspaceFolder))) + .setup((i) => i.getInterpreters(TypeMoq.It.isValue(workspaceFolder))) .returns(() => Promise.resolve([])); interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isValue(workspaceFolder))) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isValue(workspaceFolder))) .returns(() => Promise.resolve(activeInterpreter)); await interpreterDisplay.refresh(resource); verify(autoSelection.autoSelectInterpreter(anything())).once(); - statusBar.verify(s => (s.text = TypeMoq.It.isValue(activeInterpreter.displayName)!), TypeMoq.Times.once()); - statusBar.verify(s => (s.tooltip = TypeMoq.It.isValue(activeInterpreter.path)!), TypeMoq.Times.once()); + statusBar.verify((s) => (s.text = TypeMoq.It.isValue(activeInterpreter.displayName)!), TypeMoq.Times.once()); + statusBar.verify((s) => (s.tooltip = TypeMoq.It.isValue(activeInterpreter.path)!), TypeMoq.Times.once()); }); test('If interpreter is not identified then tooltip should point to python Path', async () => { const resource = Uri.file('x'); @@ -149,13 +149,13 @@ suite('Interpreters Display', () => { path: pythonPath } as any) as PythonInterpreter; interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isValue(workspaceFolder))) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isValue(workspaceFolder))) .returns(() => Promise.resolve(pythonInterpreter)); await interpreterDisplay.refresh(resource); - statusBar.verify(s => (s.tooltip = TypeMoq.It.isValue(pythonPath)), TypeMoq.Times.once()); - statusBar.verify(s => (s.text = TypeMoq.It.isValue(displayName)), TypeMoq.Times.once()); + statusBar.verify((s) => (s.tooltip = TypeMoq.It.isValue(pythonPath)), TypeMoq.Times.once()); + statusBar.verify((s) => (s.text = TypeMoq.It.isValue(displayName)), TypeMoq.Times.once()); }); test('If interpreter file does not exist then update status bar accordingly', async () => { const resource = Uri.file('x'); @@ -164,26 +164,26 @@ suite('Interpreters Display', () => { setupWorkspaceFolder(resource, workspaceFolder); // tslint:disable-next-line:no-any interpreterService - .setup(i => i.getInterpreters(TypeMoq.It.isValue(workspaceFolder))) + .setup((i) => i.getInterpreters(TypeMoq.It.isValue(workspaceFolder))) .returns(() => Promise.resolve([{} as any])); interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isValue(workspaceFolder))) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isValue(workspaceFolder))) .returns(() => Promise.resolve(undefined)); - configurationService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); - pythonSettings.setup(p => p.pythonPath).returns(() => pythonPath); - fileSystem.setup(f => f.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(false)); + configurationService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + pythonSettings.setup((p) => p.pythonPath).returns(() => pythonPath); + fileSystem.setup((f) => f.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(false)); interpreterHelper - .setup(v => v.getInterpreterInformation(TypeMoq.It.isValue(pythonPath))) + .setup((v) => v.getInterpreterInformation(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(undefined)); virtualEnvMgr - .setup(v => v.getEnvironmentName(TypeMoq.It.isValue(pythonPath))) + .setup((v) => v.getEnvironmentName(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve('')); await interpreterDisplay.refresh(resource); - statusBar.verify(s => (s.color = TypeMoq.It.isValue('yellow')), TypeMoq.Times.once()); + statusBar.verify((s) => (s.color = TypeMoq.It.isValue('yellow')), TypeMoq.Times.once()); statusBar.verify( - s => (s.text = TypeMoq.It.isValue('$(alert) Select Python Interpreter')), + (s) => (s.text = TypeMoq.It.isValue('$(alert) Select Python Interpreter')), TypeMoq.Times.once() ); }); @@ -198,16 +198,16 @@ suite('Interpreters Display', () => { companyDisplayName: 'Company Name', path: pythonPath }; - fileSystem.setup(fs => fs.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + fileSystem.setup((fs) => fs.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); virtualEnvMgr - .setup(v => v.getEnvironmentName(TypeMoq.It.isValue(pythonPath))) + .setup((v) => v.getEnvironmentName(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve('')); interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isValue(resource))) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isValue(resource))) .returns(() => Promise.resolve(activeInterpreter)) .verifiable(TypeMoq.Times.once()); interpreterHelper - .setup(i => i.getActiveWorkspaceUri(undefined)) + .setup((i) => i.getActiveWorkspaceUri(undefined)) .returns(() => { return { folderUri: workspaceFolder, configTarget: ConfigurationTarget.Workspace }; }) @@ -217,7 +217,7 @@ suite('Interpreters Display', () => { interpreterHelper.verifyAll(); interpreterService.verifyAll(); - statusBar.verify(s => (s.text = TypeMoq.It.isValue(activeInterpreter.displayName)!), TypeMoq.Times.once()); - statusBar.verify(s => (s.tooltip = TypeMoq.It.isValue(pythonPath)!), TypeMoq.Times.once()); + statusBar.verify((s) => (s.text = TypeMoq.It.isValue(activeInterpreter.displayName)!), TypeMoq.Times.once()); + statusBar.verify((s) => (s.tooltip = TypeMoq.It.isValue(pythonPath)!), TypeMoq.Times.once()); }); }); diff --git a/src/test/interpreters/helpers.unit.test.ts b/src/test/interpreters/helpers.unit.test.ts index f71b1995eb82..481e6bb5ed31 100644 --- a/src/test/interpreters/helpers.unit.test.ts +++ b/src/test/interpreters/helpers.unit.test.ts @@ -26,22 +26,24 @@ suite('Interpreters Display Helper', () => { hashProviderFactory = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDocumentManager))).returns(() => documentManager.object); + serviceContainer + .setup((c) => c.get(TypeMoq.It.isValue(IDocumentManager))) + .returns(() => documentManager.object); helper = new InterpreterHelper(serviceContainer.object, hashProviderFactory.object); }); test('getActiveWorkspaceUri should return undefined if there are no workspaces', () => { - workspaceService.setup(w => w.workspaceFolders).returns(() => []); - documentManager.setup(doc => doc.activeTextEditor).returns(() => undefined); + workspaceService.setup((w) => w.workspaceFolders).returns(() => []); + documentManager.setup((doc) => doc.activeTextEditor).returns(() => undefined); const workspace = helper.getActiveWorkspaceUri(undefined); expect(workspace).to.be.equal(undefined, 'incorrect value'); }); test('getActiveWorkspaceUri should return the workspace if there is only one', () => { const folderUri = Uri.file('abc'); // tslint:disable-next-line:no-any - workspaceService.setup(w => w.workspaceFolders).returns(() => [{ uri: folderUri } as any]); + workspaceService.setup((w) => w.workspaceFolders).returns(() => [{ uri: folderUri } as any]); const workspace = helper.getActiveWorkspaceUri(undefined); expect(workspace).to.be.not.equal(undefined, 'incorrect value'); @@ -51,8 +53,8 @@ suite('Interpreters Display Helper', () => { test('getActiveWorkspaceUri should return undefined if we no active editor and have more than one workspace folder', () => { const folderUri = Uri.file('abc'); // tslint:disable-next-line:no-any - workspaceService.setup(w => w.workspaceFolders).returns(() => [{ uri: folderUri } as any, undefined as any]); - documentManager.setup(d => d.activeTextEditor).returns(() => undefined); + workspaceService.setup((w) => w.workspaceFolders).returns(() => [{ uri: folderUri } as any, undefined as any]); + documentManager.setup((d) => d.activeTextEditor).returns(() => undefined); const workspace = helper.getActiveWorkspaceUri(undefined); expect(workspace).to.be.equal(undefined, 'incorrect value'); @@ -61,13 +63,13 @@ suite('Interpreters Display Helper', () => { const folderUri = Uri.file('abc'); const documentUri = Uri.file('file'); // tslint:disable-next-line:no-any - workspaceService.setup(w => w.workspaceFolders).returns(() => [{ uri: folderUri } as any, undefined as any]); + workspaceService.setup((w) => w.workspaceFolders).returns(() => [{ uri: folderUri } as any, undefined as any]); const textEditor = TypeMoq.Mock.ofType(); const document = TypeMoq.Mock.ofType(); - textEditor.setup(t => t.document).returns(() => document.object); - document.setup(d => d.uri).returns(() => documentUri); - documentManager.setup(d => d.activeTextEditor).returns(() => textEditor.object); - workspaceService.setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(documentUri))).returns(() => undefined); + textEditor.setup((t) => t.document).returns(() => document.object); + document.setup((d) => d.uri).returns(() => documentUri); + documentManager.setup((d) => d.activeTextEditor).returns(() => textEditor.object); + workspaceService.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(documentUri))).returns(() => undefined); const workspace = helper.getActiveWorkspaceUri(undefined); expect(workspace).to.be.equal(undefined, 'incorrect value'); @@ -77,15 +79,15 @@ suite('Interpreters Display Helper', () => { const documentWorkspaceFolderUri = Uri.file('file.abc'); const documentUri = Uri.file('file'); // tslint:disable-next-line:no-any - workspaceService.setup(w => w.workspaceFolders).returns(() => [{ uri: folderUri } as any, undefined as any]); + workspaceService.setup((w) => w.workspaceFolders).returns(() => [{ uri: folderUri } as any, undefined as any]); const textEditor = TypeMoq.Mock.ofType(); const document = TypeMoq.Mock.ofType(); - textEditor.setup(t => t.document).returns(() => document.object); - document.setup(d => d.uri).returns(() => documentUri); - documentManager.setup(d => d.activeTextEditor).returns(() => textEditor.object); + textEditor.setup((t) => t.document).returns(() => document.object); + document.setup((d) => d.uri).returns(() => documentUri); + documentManager.setup((d) => d.activeTextEditor).returns(() => textEditor.object); // tslint:disable-next-line:no-any workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isValue(documentUri))) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(documentUri))) .returns(() => { return { uri: documentWorkspaceFolderUri } as any; }); diff --git a/src/test/interpreters/interpreterService.unit.test.ts b/src/test/interpreters/interpreterService.unit.test.ts index c234db194efc..dd12a384234c 100644 --- a/src/test/interpreters/interpreterService.unit.test.ts +++ b/src/test/interpreters/interpreterService.unit.test.ts @@ -88,17 +88,17 @@ suite('Interpreters service', () => { hashProviderFactory = TypeMoq.Mock.ofType(); pythonSettings = TypeMoq.Mock.ofType(); - pythonSettings.setup(s => s.pythonPath).returns(() => PYTHON_PATH); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + pythonSettings.setup((s) => s.pythonPath).returns(() => PYTHON_PATH); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); pythonExecutionService.setup((p: any) => p.then).returns(() => undefined); - workspace.setup(x => x.getConfiguration('python', TypeMoq.It.isAny())).returns(() => config.object); + workspace.setup((x) => x.getConfiguration('python', TypeMoq.It.isAny())).returns(() => config.object); pythonExecutionFactory - .setup(f => f.create(TypeMoq.It.isAny())) + .setup((f) => f.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(pythonExecutionService.object)); - fileSystem.setup(fs => fs.getFileHash(TypeMoq.It.isAny())).returns(() => Promise.resolve('')); + fileSystem.setup((fs) => fs.getFileHash(TypeMoq.It.isAny())).returns(() => Promise.resolve('')); persistentStateFactory - .setup(p => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => { const state = { updateValue: () => Promise.resolve() @@ -148,12 +148,12 @@ suite('Interpreters service', () => { } suite('Misc', () => { setup(setupSuite); - [undefined, Uri.file('xyz')].forEach(resource => { + [undefined, Uri.file('xyz')].forEach((resource) => { const resourceTestSuffix = `(${resource ? 'with' : 'without'} a resource)`; test(`Refresh invokes refresh of display ${resourceTestSuffix}`, async () => { interpreterDisplay - .setup(i => i.refresh(TypeMoq.It.isValue(resource))) + .setup((i) => i.refresh(TypeMoq.It.isValue(resource))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); @@ -165,7 +165,7 @@ suite('Interpreters service', () => { test(`get Interpreters uses interpreter locactors to get interpreters ${resourceTestSuffix}`, async () => { locator - .setup(l => l.getInterpreters(TypeMoq.It.isValue(resource))) + .setup((l) => l.getInterpreters(TypeMoq.It.isValue(resource))) .returns(() => Promise.resolve([])) .verifiable(TypeMoq.Times.once()); @@ -182,8 +182,8 @@ suite('Interpreters service', () => { let activeTextEditorChangeHandler: Function | undefined; documentManager - .setup(d => d.onDidChangeActiveTextEditor(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(handler => { + .setup((d) => d.onDidChangeActiveTextEditor(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((handler) => { activeTextEditorChangeHandler = handler; return { dispose: noop }; }); @@ -194,11 +194,11 @@ suite('Interpreters service', () => { const textEditor = TypeMoq.Mock.ofType(); const uri = Uri.file(path.join('usr', 'file.py')); const document = TypeMoq.Mock.ofType(); - textEditor.setup(t => t.document).returns(() => document.object); - document.setup(d => d.uri).returns(() => uri); + textEditor.setup((t) => t.document).returns(() => document.object); + document.setup((d) => d.uri).returns(() => uri); activeTextEditorChangeHandler!(textEditor.object); - interpreterDisplay.verify(i => i.refresh(TypeMoq.It.isValue(uri)), TypeMoq.Times.once()); + interpreterDisplay.verify((i) => i.refresh(TypeMoq.It.isValue(uri)), TypeMoq.Times.once()); }); test('If there is no active document then interpreter.refresh should not be invoked', async () => { @@ -207,8 +207,8 @@ suite('Interpreters service', () => { let activeTextEditorChangeHandler: Function | undefined; documentManager - .setup(d => d.onDidChangeActiveTextEditor(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(handler => { + .setup((d) => d.onDidChangeActiveTextEditor(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((handler) => { activeTextEditorChangeHandler = handler; return { dispose: noop }; }); @@ -218,34 +218,34 @@ suite('Interpreters service', () => { service.initialize(); activeTextEditorChangeHandler!(); - interpreterDisplay.verify(i => i.refresh(TypeMoq.It.isValue(undefined)), TypeMoq.Times.never()); + interpreterDisplay.verify((i) => i.refresh(TypeMoq.It.isValue(undefined)), TypeMoq.Times.never()); }); }); suite('Get Interpreter Details', () => { setup(setupSuite); - [undefined, Uri.file('some workspace')].forEach(resource => { + [undefined, Uri.file('some workspace')].forEach((resource) => { test(`Ensure undefined is returned if we're unable to retrieve interpreter info (Resource is ${resource})`, async () => { const pythonPath = 'SOME VALUE'; const service = new InterpreterService(serviceContainer, hashProviderFactory.object); locator - .setup(l => l.getInterpreters(TypeMoq.It.isValue(resource))) + .setup((l) => l.getInterpreters(TypeMoq.It.isValue(resource))) .returns(() => Promise.resolve([])) .verifiable(TypeMoq.Times.once()); helper - .setup(h => h.getInterpreterInformation(TypeMoq.It.isValue(pythonPath))) + .setup((h) => h.getInterpreterInformation(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); virtualEnvMgr - .setup(v => v.getEnvironmentName(TypeMoq.It.isValue(pythonPath))) + .setup((v) => v.getEnvironmentName(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve('')) .verifiable(TypeMoq.Times.once()); virtualEnvMgr - .setup(v => v.getEnvironmentType(TypeMoq.It.isValue(pythonPath))) + .setup((v) => v.getEnvironmentType(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(InterpreterType.Unknown)) .verifiable(TypeMoq.Times.once()); pythonExecutionService - .setup(p => p.getExecutablePath()) + .setup((p) => p.getExecutablePath()) .returns(() => Promise.resolve(pythonPath)) .verifiable(TypeMoq.Times.once()); @@ -271,7 +271,7 @@ suite('Interpreters service', () => { const hash = `-${md5(JSON.stringify({ ...interpreterInfo, displayName: '' }))}`; const expectedDisplayName = 'Formatted display name'; persistentStateFactory - .setup(p => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => { const state = { updateValue: () => Promise.resolve(), @@ -293,17 +293,17 @@ suite('Interpreters service', () => { const fileHash = 'File_Hash'; const hashProvider = TypeMoq.Mock.ofType(); hashProviderFactory - .setup(factory => factory.create(TypeMoq.It.isAny())) + .setup((factory) => factory.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(hashProvider.object)) .verifiable(TypeMoq.Times.atLeastOnce()); hashProvider - .setup(provider => provider.getInterpreterHash(TypeMoq.It.isValue(pythonPath))) + .setup((provider) => provider.getInterpreterHash(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(fileHash)) .verifiable(TypeMoq.Times.once()); - hashProvider.setup(provider => (provider as any).then).returns(() => undefined); + hashProvider.setup((provider) => (provider as any).then).returns(() => undefined); const expectedDisplayName = 'Formatted display name'; persistentStateFactory - .setup(p => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => { const state = { updateValue: () => Promise.resolve(), @@ -327,25 +327,25 @@ suite('Interpreters service', () => { // Also we have special handling for certain types of interpreters. suite('Display Format (with all permutations)', () => { setup(setupSuite); - [undefined, Uri.file('xyz')].forEach(resource => { - [undefined, new SemVer('1.2.3-alpha')].forEach(version => { + [undefined, Uri.file('xyz')].forEach((resource) => { + [undefined, new SemVer('1.2.3-alpha')].forEach((version) => { // Forced cast to ignore TS warnings. (EnumEx.getNamesAndValues(Architecture) as ( | { name: string; value: Architecture } | undefined )[]) .concat(undefined) - .forEach(arch => { - [undefined, path.join('a', 'b', 'c', 'd', 'bin', 'python')].forEach(pythonPath => { + .forEach((arch) => { + [undefined, path.join('a', 'b', 'c', 'd', 'bin', 'python')].forEach((pythonPath) => { // Forced cast to ignore TS warnings. (EnumEx.getNamesAndValues(InterpreterType) as ( | { name: string; value: InterpreterType } | undefined )[]) .concat(undefined) - .forEach(interpreterType => { - [undefined, 'my env name'].forEach(envName => { - ['', 'my pipenv name'].forEach(pipEnvName => { + .forEach((interpreterType) => { + [undefined, 'my env name'].forEach((envName) => { + ['', 'my pipenv name'].forEach((pipEnvName) => { const testName = [ `${resource ? 'With' : 'Without'} a workspace`, `${version ? 'with' : 'without'} version information`, @@ -375,7 +375,7 @@ suite('Interpreters service', () => { interpreterType.value === InterpreterType.Pipenv ) { virtualEnvMgr - .setup(v => + .setup((v) => v.getEnvironmentName( TypeMoq.It.isValue(interpreterInfo.path!), TypeMoq.It.isAny() @@ -385,7 +385,7 @@ suite('Interpreters service', () => { } if (interpreterType) { helper - .setup(h => + .setup((h) => h.getInterpreterTypeDisplayName( TypeMoq.It.isValue(interpreterType.value) ) @@ -463,19 +463,19 @@ suite('Interpreters service', () => { const pythonPath = 'Some Python Path'; const hashProvider = TypeMoq.Mock.ofType(); hashProviderFactory - .setup(factory => factory.create(TypeMoq.It.isAny())) + .setup((factory) => factory.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(hashProvider.object)) .verifiable(TypeMoq.Times.atLeastOnce()); hashProvider - .setup(provider => provider.getInterpreterHash(TypeMoq.It.isValue(pythonPath))) + .setup((provider) => provider.getInterpreterHash(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(fileHash)) .verifiable(TypeMoq.Times.once()); - hashProvider.setup(provider => (provider as any).then).returns(() => undefined); + hashProvider.setup((provider) => (provider as any).then).returns(() => undefined); const state = TypeMoq.Mock.ofType>(); const info = { path: 'hell', type: InterpreterType.Venv }; state - .setup(s => s.value) + .setup((s) => s.value) .returns(() => { return { fileHash, @@ -484,12 +484,12 @@ suite('Interpreters service', () => { }) .verifiable(TypeMoq.Times.atLeastOnce()); state - .setup(s => s.updateValue(TypeMoq.It.isAny())) + .setup((s) => s.updateValue(TypeMoq.It.isAny())) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); - state.setup(s => (s as any).then).returns(() => undefined); + state.setup((s) => (s as any).then).returns(() => undefined); persistentStateFactory - .setup(f => f.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((f) => f.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => state.object) .verifiable(TypeMoq.Times.once()); @@ -508,19 +508,19 @@ suite('Interpreters service', () => { const pythonPath = 'Some Python Path'; const hashProvider = TypeMoq.Mock.ofType(); hashProviderFactory - .setup(factory => factory.create(TypeMoq.It.isAny())) + .setup((factory) => factory.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(hashProvider.object)) .verifiable(TypeMoq.Times.atLeastOnce()); hashProvider - .setup(provider => provider.getInterpreterHash(TypeMoq.It.isValue(pythonPath))) + .setup((provider) => provider.getInterpreterHash(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve('different value')) .verifiable(TypeMoq.Times.once()); - hashProvider.setup(provider => (provider as any).then).returns(() => undefined); + hashProvider.setup((provider) => (provider as any).then).returns(() => undefined); const state = TypeMoq.Mock.ofType>(); const info = { path: 'hell', type: InterpreterType.Venv }; state - .setup(s => s.value) + .setup((s) => s.value) .returns(() => { return { fileHash, @@ -529,12 +529,12 @@ suite('Interpreters service', () => { }) .verifiable(TypeMoq.Times.atLeastOnce()); state - .setup(s => s.updateValue(TypeMoq.It.isValue({ fileHash: 'different value' }))) + .setup((s) => s.updateValue(TypeMoq.It.isValue({ fileHash: 'different value' }))) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); - state.setup(s => (s as any).then).returns(() => undefined); + state.setup((s) => (s as any).then).returns(() => undefined); persistentStateFactory - .setup(f => f.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((f) => f.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => state.object) .verifiable(TypeMoq.Times.once()); diff --git a/src/test/interpreters/interpreterVersion.unit.test.ts b/src/test/interpreters/interpreterVersion.unit.test.ts index 37329448d5be..71e6990ee7de 100644 --- a/src/test/interpreters/interpreterVersion.unit.test.ts +++ b/src/test/interpreters/interpreterVersion.unit.test.ts @@ -19,14 +19,14 @@ suite('Interpreters display version', () => { // tslint:disable-next-line:no-any processService.setup((p: any) => p.then).returns(() => undefined); - processFactory.setup(p => p.create()).returns(() => Promise.resolve(processService.object)); + processFactory.setup((p) => p.create()).returns(() => Promise.resolve(processService.object)); interpreterVersionService = new InterpreterVersionService(processFactory.object); }); test('Must return the Python Version', async () => { const pythonPath = path.join('a', 'b', 'python'); const pythonVersion = 'Output from the Procecss'; processService - .setup(p => p.exec(typeMoq.It.isValue(pythonPath), typeMoq.It.isValue(['--version']), typeMoq.It.isAny())) + .setup((p) => p.exec(typeMoq.It.isValue(pythonPath), typeMoq.It.isValue(['--version']), typeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: pythonVersion })) .verifiable(typeMoq.Times.once()); @@ -36,7 +36,7 @@ suite('Interpreters display version', () => { test('Must return the default value when Python path is invalid', async () => { const pythonPath = path.join('a', 'b', 'python'); processService - .setup(p => p.exec(typeMoq.It.isValue(pythonPath), typeMoq.It.isValue(['--version']), typeMoq.It.isAny())) + .setup((p) => p.exec(typeMoq.It.isValue(pythonPath), typeMoq.It.isValue(['--version']), typeMoq.It.isAny())) .returns(() => Promise.reject({})) .verifiable(typeMoq.Times.once()); @@ -47,7 +47,7 @@ suite('Interpreters display version', () => { const pythonPath = path.join('a', 'b', 'python'); const pipVersion = '1.2.3'; processService - .setup(p => + .setup((p) => p.exec( typeMoq.It.isValue(pythonPath), typeMoq.It.isValue(['-m', 'pip', '--version']), @@ -63,7 +63,7 @@ suite('Interpreters display version', () => { test('Must throw an exception when pip version cannot be determined', async () => { const pythonPath = path.join('a', 'b', 'python'); processService - .setup(p => + .setup((p) => p.exec( typeMoq.It.isValue(pythonPath), typeMoq.It.isValue(['-m', 'pip', '--version']), diff --git a/src/test/interpreters/knownPathService.unit.test.ts b/src/test/interpreters/knownPathService.unit.test.ts index 509c82bc8817..1dad8da743cd 100644 --- a/src/test/interpreters/knownPathService.unit.test.ts +++ b/src/test/interpreters/knownPathService.unit.test.ts @@ -27,13 +27,13 @@ suite('Interpreters Known Paths', () => { platformService = TypeMoq.Mock.ofType(); pathUtils = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ICurrentProcess), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ICurrentProcess), TypeMoq.It.isAny())) .returns(() => currentProcess.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) .returns(() => platformService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPathUtils), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPathUtils), TypeMoq.It.isAny())) .returns(() => pathUtils.object); knownSearchPaths = new KnownSearchPathsForInterpreters(serviceContainer.object); @@ -42,16 +42,16 @@ suite('Interpreters Known Paths', () => { test('Ensure known list of paths are returned', async () => { const pathDelimiter = 'X'; const pathsInPATHVar = [path.join('a', 'b', 'c'), '', path.join('1', '2'), '3']; - pathUtils.setup(p => p.delimiter).returns(() => pathDelimiter); - platformService.setup(p => p.isWindows).returns(() => true); - platformService.setup(p => p.pathVariableName).returns(() => 'PATH'); + pathUtils.setup((p) => p.delimiter).returns(() => pathDelimiter); + platformService.setup((p) => p.isWindows).returns(() => true); + platformService.setup((p) => p.pathVariableName).returns(() => 'PATH'); currentProcess - .setup(p => p.env) + .setup((p) => p.env) .returns(() => { return { PATH: pathsInPATHVar.join(pathDelimiter) }; }); - const expectedPaths = [...pathsInPATHVar].filter(item => item.length > 0); + const expectedPaths = [...pathsInPATHVar].filter((item) => item.length > 0); const paths = knownSearchPaths.getSearchPaths(); @@ -61,18 +61,18 @@ suite('Interpreters Known Paths', () => { test('Ensure known list of paths are returned on non-windows', async () => { const homeDir = '/users/peter Smith'; const pathDelimiter = 'X'; - pathUtils.setup(p => p.delimiter).returns(() => pathDelimiter); - pathUtils.setup(p => p.home).returns(() => homeDir); - platformService.setup(p => p.isWindows).returns(() => false); - platformService.setup(p => p.pathVariableName).returns(() => 'PATH'); + pathUtils.setup((p) => p.delimiter).returns(() => pathDelimiter); + pathUtils.setup((p) => p.home).returns(() => homeDir); + platformService.setup((p) => p.isWindows).returns(() => false); + platformService.setup((p) => p.pathVariableName).returns(() => 'PATH'); currentProcess - .setup(p => p.env) + .setup((p) => p.env) .returns(() => { return { PATH: '' }; }); const expectedPaths: string[] = []; - ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/sbin'].forEach(p => { + ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/sbin'].forEach((p) => { expectedPaths.push(p); expectedPaths.push(path.join(homeDir, p)); }); @@ -89,18 +89,18 @@ suite('Interpreters Known Paths', () => { const homeDir = '/users/peter Smith'; const pathDelimiter = 'X'; const pathsInPATHVar = [path.join('a', 'b', 'c'), '', path.join('1', '2'), '3']; - pathUtils.setup(p => p.delimiter).returns(() => pathDelimiter); - pathUtils.setup(p => p.home).returns(() => homeDir); - platformService.setup(p => p.isWindows).returns(() => false); - platformService.setup(p => p.pathVariableName).returns(() => 'PATH'); + pathUtils.setup((p) => p.delimiter).returns(() => pathDelimiter); + pathUtils.setup((p) => p.home).returns(() => homeDir); + platformService.setup((p) => p.isWindows).returns(() => false); + platformService.setup((p) => p.pathVariableName).returns(() => 'PATH'); currentProcess - .setup(p => p.env) + .setup((p) => p.env) .returns(() => { return { PATH: pathsInPATHVar.join(pathDelimiter) }; }); - const expectedPaths = [...pathsInPATHVar].filter(item => item.length > 0); - ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/sbin'].forEach(p => { + const expectedPaths = [...pathsInPATHVar].filter((item) => item.length > 0); + ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/sbin'].forEach((p) => { expectedPaths.push(p); expectedPaths.push(path.join(homeDir, p)); }); diff --git a/src/test/interpreters/locators/helpers.unit.test.ts b/src/test/interpreters/locators/helpers.unit.test.ts index 33d59543a822..25adb73ea1f9 100644 --- a/src/test/interpreters/locators/helpers.unit.test.ts +++ b/src/test/interpreters/locators/helpers.unit.test.ts @@ -43,27 +43,27 @@ suite('Interpreters - Locators Helper', () => { fs = TypeMoq.Mock.ofType(); pipEnvHelper = mock(PipEnvServiceHelper); interpreterServiceHelper = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPlatformService))).returns(() => platform.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fs.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))).returns(() => platform.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fs.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterHelper))) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterHelper))) .returns(() => interpreterServiceHelper.object); helper = new InterpreterLocatorHelper(fs.object, instance(pipEnvHelper)); }); test('Ensure default Mac interpreter is not excluded from the list of interpreters', async () => { - platform.setup(p => p.isWindows).returns(() => false); - platform.setup(p => p.isLinux).returns(() => false); + platform.setup((p) => p.isWindows).returns(() => false); + platform.setup((p) => p.isLinux).returns(() => false); platform - .setup(p => p.isMac) + .setup((p) => p.isMac) .returns(() => true) .verifiable(TypeMoq.Times.never()); - fs.setup(f => f.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + fs.setup((f) => f.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); const interpreters: PythonInterpreter[] = []; - ['conda', 'virtualenv', 'mac', 'pyenv'].forEach(name => { + ['conda', 'virtualenv', 'mac', 'pyenv'].forEach((name) => { const interpreter = { architecture: Architecture.Unknown, displayName: name, @@ -77,7 +77,7 @@ suite('Interpreters - Locators Helper', () => { // Treat 'mac' as as mac interpreter. interpreterServiceHelper - .setup(i => i.isMacDefaultPythonPath(TypeMoq.It.isValue(interpreter.path))) + .setup((i) => i.isMacDefaultPythonPath(TypeMoq.It.isValue(interpreter.path))) .returns(() => name === 'mac') .verifiable(TypeMoq.Times.never()); }); @@ -93,13 +93,13 @@ suite('Interpreters - Locators Helper', () => { expect(items).to.be.lengthOf(4); expect(items).to.be.deep.equal(expectedInterpreters); }); - getNamesAndValues(OS).forEach(os => { + getNamesAndValues(OS).forEach((os) => { test(`Ensure duplicates are removed (same version and same interpreter directory on ${os.name})`, async () => { - interpreterServiceHelper.setup(i => i.isMacDefaultPythonPath(TypeMoq.It.isAny())).returns(() => false); - platform.setup(p => p.isWindows).returns(() => os.value === OS.Windows); - platform.setup(p => p.isLinux).returns(() => os.value === OS.Linux); - platform.setup(p => p.isMac).returns(() => os.value === OS.Mac); - fs.setup(f => f.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + interpreterServiceHelper.setup((i) => i.isMacDefaultPythonPath(TypeMoq.It.isAny())).returns(() => false); + platform.setup((p) => p.isWindows).returns(() => os.value === OS.Windows); + platform.setup((p) => p.isLinux).returns(() => os.value === OS.Linux); + platform.setup((p) => p.isMac).returns(() => os.value === OS.Mac); + fs.setup((f) => f.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((a, b) => a === b) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -158,13 +158,13 @@ suite('Interpreters - Locators Helper', () => { expect(items).to.be.deep.equal(expectedInterpreters); }); }); - getNamesAndValues(OS).forEach(os => { + getNamesAndValues(OS).forEach((os) => { test(`Ensure interpreter types are identified from other locators (${os.name})`, async () => { - interpreterServiceHelper.setup(i => i.isMacDefaultPythonPath(TypeMoq.It.isAny())).returns(() => false); - platform.setup(p => p.isWindows).returns(() => os.value === OS.Windows); - platform.setup(p => p.isLinux).returns(() => os.value === OS.Linux); - platform.setup(p => p.isMac).returns(() => os.value === OS.Mac); - fs.setup(f => f.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + interpreterServiceHelper.setup((i) => i.isMacDefaultPythonPath(TypeMoq.It.isAny())).returns(() => false); + platform.setup((p) => p.isWindows).returns(() => os.value === OS.Windows); + platform.setup((p) => p.isLinux).returns(() => os.value === OS.Linux); + platform.setup((p) => p.isMac).returns(() => os.value === OS.Mac); + fs.setup((f) => f.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((a, b) => a === b && a === path.join('users', 'python', 'bin')) .verifiable(TypeMoq.Times.atLeastOnce()); diff --git a/src/test/interpreters/locators/index.unit.test.ts b/src/test/interpreters/locators/index.unit.test.ts index 917177ef2421..e3057a95883c 100644 --- a/src/test/interpreters/locators/index.unit.test.ts +++ b/src/test/interpreters/locators/index.unit.test.ts @@ -42,14 +42,16 @@ suite('Interpreters - Locators Index', () => { platformSvc = TypeMoq.Mock.ofType(); helper = TypeMoq.Mock.ofType(); filter = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPlatformService))).returns(() => platformSvc.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IInterpreterLocatorHelper))).returns(() => helper.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))).returns(() => platformSvc.object); + serviceContainer + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterLocatorHelper))) + .returns(() => helper.object); locator = new PythonInterpreterLocatorService(serviceContainer.object, filter.object); }); - [undefined, Uri.file('Something')].forEach(resource => { - getNamesAndValues(OSType).forEach(osType => { + [undefined, Uri.file('Something')].forEach((resource) => { + getNamesAndValues(OSType).forEach((osType) => { if (osType.value === OSType.Unknown) { return; } @@ -59,10 +61,10 @@ suite('Interpreters - Locators Index', () => { if (osType.value === OSType.Windows) { locatorsTypes.push(WINDOWS_REGISTRY_SERVICE); } - platformSvc.setup(p => p.osType).returns(() => osType.value); - platformSvc.setup(p => p.isWindows).returns(() => osType.value === OSType.Windows); - platformSvc.setup(p => p.isLinux).returns(() => osType.value === OSType.Linux); - platformSvc.setup(p => p.isMac).returns(() => osType.value === OSType.OSX); + platformSvc.setup((p) => p.osType).returns(() => osType.value); + platformSvc.setup((p) => p.isWindows).returns(() => osType.value === OSType.Windows); + platformSvc.setup((p) => p.isLinux).returns(() => osType.value === OSType.Linux); + platformSvc.setup((p) => p.isMac).returns(() => osType.value === OSType.OSX); locatorsTypes.push(CONDA_ENV_SERVICE); locatorsTypes.push(CONDA_ENV_FILE_SERVICE); @@ -72,7 +74,7 @@ suite('Interpreters - Locators Index', () => { locatorsTypes.push(KNOWN_PATH_SERVICE); locatorsTypes.push(CURRENT_PATH_SERVICE); - const locatorsWithInterpreters = locatorsTypes.map(typeName => { + const locatorsWithInterpreters = locatorsTypes.map((typeName) => { const interpreter: PythonInterpreter = { architecture: Architecture.Unknown, displayName: typeName, @@ -85,16 +87,18 @@ suite('Interpreters - Locators Index', () => { const typeLocator = TypeMoq.Mock.ofType(); typeLocator - .setup(l => l.hasInterpreters) + .setup((l) => l.hasInterpreters) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); typeLocator - .setup(l => l.getInterpreters(TypeMoq.It.isValue(resource))) + .setup((l) => l.getInterpreters(TypeMoq.It.isValue(resource))) .returns(() => Promise.resolve([interpreter])) .verifiable(TypeMoq.Times.once()); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterLocatorService), TypeMoq.It.isValue(typeName))) + .setup((c) => + c.get(TypeMoq.It.isValue(IInterpreterLocatorService), TypeMoq.It.isValue(typeName)) + ) .returns(() => typeLocator.object); return { @@ -105,13 +109,13 @@ suite('Interpreters - Locators Index', () => { }); helper - .setup(h => h.mergeInterpreters(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(locatorsWithInterpreters.map(item => item.interpreters[0]))) + .setup((h) => h.mergeInterpreters(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(locatorsWithInterpreters.map((item) => item.interpreters[0]))) .verifiable(TypeMoq.Times.once()); await locator.getInterpreters(resource); - locatorsWithInterpreters.forEach(item => item.locator.verifyAll()); + locatorsWithInterpreters.forEach((item) => item.locator.verifyAll()); helper.verifyAll(); }); test(`Interpreter Sources are sorted correctly and merged ${testSuffix}`, async () => { @@ -119,10 +123,10 @@ suite('Interpreters - Locators Index', () => { if (osType.value === OSType.Windows) { locatorsTypes.push(WINDOWS_REGISTRY_SERVICE); } - platformSvc.setup(p => p.osType).returns(() => osType.value); - platformSvc.setup(p => p.isWindows).returns(() => osType.value === OSType.Windows); - platformSvc.setup(p => p.isLinux).returns(() => osType.value === OSType.Linux); - platformSvc.setup(p => p.isMac).returns(() => osType.value === OSType.OSX); + platformSvc.setup((p) => p.osType).returns(() => osType.value); + platformSvc.setup((p) => p.isWindows).returns(() => osType.value === OSType.Windows); + platformSvc.setup((p) => p.isLinux).returns(() => osType.value === OSType.Linux); + platformSvc.setup((p) => p.isMac).returns(() => osType.value === OSType.OSX); locatorsTypes.push(CONDA_ENV_SERVICE); locatorsTypes.push(CONDA_ENV_FILE_SERVICE); @@ -132,7 +136,7 @@ suite('Interpreters - Locators Index', () => { locatorsTypes.push(KNOWN_PATH_SERVICE); locatorsTypes.push(CURRENT_PATH_SERVICE); - const locatorsWithInterpreters = locatorsTypes.map(typeName => { + const locatorsWithInterpreters = locatorsTypes.map((typeName) => { const interpreter: PythonInterpreter = { architecture: Architecture.Unknown, displayName: typeName, @@ -145,16 +149,18 @@ suite('Interpreters - Locators Index', () => { const typeLocator = TypeMoq.Mock.ofType(); typeLocator - .setup(l => l.hasInterpreters) + .setup((l) => l.hasInterpreters) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); typeLocator - .setup(l => l.getInterpreters(TypeMoq.It.isValue(resource))) + .setup((l) => l.getInterpreters(TypeMoq.It.isValue(resource))) .returns(() => Promise.resolve([interpreter])) .verifiable(TypeMoq.Times.once()); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterLocatorService), TypeMoq.It.isValue(typeName))) + .setup((c) => + c.get(TypeMoq.It.isValue(IInterpreterLocatorService), TypeMoq.It.isValue(typeName)) + ) .returns(() => typeLocator.object); return { @@ -164,15 +170,15 @@ suite('Interpreters - Locators Index', () => { }; }); - const expectedInterpreters = locatorsWithInterpreters.map(item => item.interpreters[0]); + const expectedInterpreters = locatorsWithInterpreters.map((item) => item.interpreters[0]); helper - .setup(h => h.mergeInterpreters(TypeMoq.It.isAny())) + .setup((h) => h.mergeInterpreters(TypeMoq.It.isAny())) .returns(() => Promise.resolve(expectedInterpreters)) .verifiable(TypeMoq.Times.once()); const interpreters = await locator.getInterpreters(resource); - locatorsWithInterpreters.forEach(item => item.locator.verifyAll()); + locatorsWithInterpreters.forEach((item) => item.locator.verifyAll()); helper.verifyAll(); expect(interpreters).to.be.lengthOf(locatorsTypes.length); expect(interpreters).to.be.deep.equal(expectedInterpreters); diff --git a/src/test/interpreters/locators/interpreterLocatorService.testvirtualenvs.ts b/src/test/interpreters/locators/interpreterLocatorService.testvirtualenvs.ts index b67919bad5dd..b9ba16d84100 100644 --- a/src/test/interpreters/locators/interpreterLocatorService.testvirtualenvs.ts +++ b/src/test/interpreters/locators/interpreterLocatorService.testvirtualenvs.ts @@ -1,94 +1,94 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect } from 'chai'; -import * as path from 'path'; -import { RegistryImplementation } from '../../../client/common/platform/registry'; -import { IRegistry } from '../../../client/common/platform/types'; -import { - IInterpreterLocatorService, - INTERPRETER_LOCATOR_SERVICE, - InterpreterType, - PythonInterpreter -} from '../../../client/interpreter/contracts'; -import { getOSType, OSType } from '../../common'; -import { TEST_TIMEOUT } from '../../constants'; -import { closeActiveWindows, initialize, initializeTest } from '../../initialize'; -import { UnitTestIocContainer } from '../../testing/serviceRegistry'; - -suite('Python interpreter locator service', () => { - let ioc: UnitTestIocContainer; - let interpreters: PythonInterpreter[]; - suiteSetup(async function() { - // tslint:disable-next-line:no-invalid-this - this.timeout(getOSType() === OSType.Windows ? TEST_TIMEOUT * 7 : TEST_TIMEOUT * 2); - await initialize(); - initializeDI(); - const locator = ioc.serviceContainer.get( - IInterpreterLocatorService, - INTERPRETER_LOCATOR_SERVICE - ); - interpreters = await locator.getInterpreters(); - }); - - setup(async () => { - await initializeTest(); - initializeDI(); - }); - - teardown(async () => { - await ioc.dispose(); - await closeActiveWindows(); - }); - suiteTeardown(closeActiveWindows); - - function initializeDI() { - ioc = new UnitTestIocContainer(); - ioc.registerCommonTypes(); - ioc.registerUnitTestTypes(); - ioc.registerMockProcessTypes(); - ioc.registerVariableTypes(); - ioc.registerInterpreterTypes(); - ioc.serviceManager.addSingleton(IRegistry, RegistryImplementation); - } - - test('Ensure we are getting conda environment created using command `conda create -n "test_env1" -y python`', async () => { - // Created in CI using command `conda create -n "test_env1" -y python` - const filteredInterpreters = interpreters.filter( - i => i.envName === 'test_env1' && i.type === InterpreterType.Conda - ); - expect(filteredInterpreters.length).to.be.greaterThan(0, 'Environment test_env1 not found'); - }); - test('Ensure we are getting conda environment created using command `conda create -p "./test_env2`"', async () => { - // Created in CI using command `conda create -p "./test_env2" -y python` - const filteredInterpreters = interpreters.filter(i => { - let dirName = path.dirname(i.path); - if (dirName.endsWith('bin') || dirName.endsWith('Scripts')) { - dirName = path.dirname(dirName); - } - return dirName.endsWith('test_env2') && i.type === InterpreterType.Conda; - }); - expect(filteredInterpreters.length).to.be.greaterThan(0, 'Environment test_env2 not found'); - }); - test('Ensure we are getting conda environment created using command `conda create -p "/test_env3" -y python`', async () => { - // Created in CI using command `conda create -p "/test_env3" -y python` - const filteredInterpreters = interpreters.filter(i => { - let dirName = path.dirname(i.path); - if (dirName.endsWith('bin') || dirName.endsWith('Scripts')) { - dirName = path.dirname(dirName); - } - return dirName.endsWith('test_env3') && i.type === InterpreterType.Conda; - }); - expect(filteredInterpreters.length).to.be.greaterThan(0, 'Environment test_env3 not found'); - }); - - test('Ensure we are getting the base conda environment', async () => { - // Base conda environment in CI - const filteredInterpreters = interpreters.filter( - i => (i.envName === 'base' || i.envName === 'miniconda') && i.type === InterpreterType.Conda - ); - expect(filteredInterpreters.length).to.be.greaterThan(0, 'Base environment not found'); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { expect } from 'chai'; +import * as path from 'path'; +import { RegistryImplementation } from '../../../client/common/platform/registry'; +import { IRegistry } from '../../../client/common/platform/types'; +import { + IInterpreterLocatorService, + INTERPRETER_LOCATOR_SERVICE, + InterpreterType, + PythonInterpreter +} from '../../../client/interpreter/contracts'; +import { getOSType, OSType } from '../../common'; +import { TEST_TIMEOUT } from '../../constants'; +import { closeActiveWindows, initialize, initializeTest } from '../../initialize'; +import { UnitTestIocContainer } from '../../testing/serviceRegistry'; + +suite('Python interpreter locator service', () => { + let ioc: UnitTestIocContainer; + let interpreters: PythonInterpreter[]; + suiteSetup(async function () { + // tslint:disable-next-line:no-invalid-this + this.timeout(getOSType() === OSType.Windows ? TEST_TIMEOUT * 7 : TEST_TIMEOUT * 2); + await initialize(); + initializeDI(); + const locator = ioc.serviceContainer.get( + IInterpreterLocatorService, + INTERPRETER_LOCATOR_SERVICE + ); + interpreters = await locator.getInterpreters(); + }); + + setup(async () => { + await initializeTest(); + initializeDI(); + }); + + teardown(async () => { + await ioc.dispose(); + await closeActiveWindows(); + }); + suiteTeardown(closeActiveWindows); + + function initializeDI() { + ioc = new UnitTestIocContainer(); + ioc.registerCommonTypes(); + ioc.registerUnitTestTypes(); + ioc.registerMockProcessTypes(); + ioc.registerVariableTypes(); + ioc.registerInterpreterTypes(); + ioc.serviceManager.addSingleton(IRegistry, RegistryImplementation); + } + + test('Ensure we are getting conda environment created using command `conda create -n "test_env1" -y python`', async () => { + // Created in CI using command `conda create -n "test_env1" -y python` + const filteredInterpreters = interpreters.filter( + (i) => i.envName === 'test_env1' && i.type === InterpreterType.Conda + ); + expect(filteredInterpreters.length).to.be.greaterThan(0, 'Environment test_env1 not found'); + }); + test('Ensure we are getting conda environment created using command `conda create -p "./test_env2`"', async () => { + // Created in CI using command `conda create -p "./test_env2" -y python` + const filteredInterpreters = interpreters.filter((i) => { + let dirName = path.dirname(i.path); + if (dirName.endsWith('bin') || dirName.endsWith('Scripts')) { + dirName = path.dirname(dirName); + } + return dirName.endsWith('test_env2') && i.type === InterpreterType.Conda; + }); + expect(filteredInterpreters.length).to.be.greaterThan(0, 'Environment test_env2 not found'); + }); + test('Ensure we are getting conda environment created using command `conda create -p "/test_env3" -y python`', async () => { + // Created in CI using command `conda create -p "/test_env3" -y python` + const filteredInterpreters = interpreters.filter((i) => { + let dirName = path.dirname(i.path); + if (dirName.endsWith('bin') || dirName.endsWith('Scripts')) { + dirName = path.dirname(dirName); + } + return dirName.endsWith('test_env3') && i.type === InterpreterType.Conda; + }); + expect(filteredInterpreters.length).to.be.greaterThan(0, 'Environment test_env3 not found'); + }); + + test('Ensure we are getting the base conda environment', async () => { + // Base conda environment in CI + const filteredInterpreters = interpreters.filter( + (i) => (i.envName === 'base' || i.envName === 'miniconda') && i.type === InterpreterType.Conda + ); + expect(filteredInterpreters.length).to.be.greaterThan(0, 'Base environment not found'); + }); +}); diff --git a/src/test/interpreters/locators/workspaceVirtualEnvService.test.ts b/src/test/interpreters/locators/workspaceVirtualEnvService.test.ts index 1e85d11442c6..e5eaa3a71a51 100644 --- a/src/test/interpreters/locators/workspaceVirtualEnvService.test.ts +++ b/src/test/interpreters/locators/workspaceVirtualEnvService.test.ts @@ -76,7 +76,7 @@ class Venvs { } const timeoutMs = IS_CI_SERVER ? 60_000 : 15_000; -suite('Interpreters - Workspace VirtualEnv Service', function() { +suite('Interpreters - Workspace VirtualEnv Service', function () { this.timeout(timeoutMs); this.retries(0); @@ -108,7 +108,7 @@ suite('Interpreters - Workspace VirtualEnv Service', function() { const envNameToLookFor = path.basename(venvRoot); const predicate = async () => { const items = await locator.getInterpreters(workspaceUri); - return items.some(item => item.envName === envNameToLookFor); + return items.some((item) => item.envName === envNameToLookFor); }; const promise = waitForCondition( predicate, @@ -123,7 +123,7 @@ suite('Interpreters - Workspace VirtualEnv Service', function() { return venvs.create(envSuffix); } - suiteSetup(async function() { + suiteSetup(async function () { // skip for Python < 3, no venv support if (await isPythonVersionInProcess(undefined, '2')) { return this.skip(); @@ -157,7 +157,7 @@ suite('Interpreters - Workspace VirtualEnv Service', function() { await waitForInterpreterToBeDetected(env2); }); - test('Detect a new Virtual Environment, and other workspace folder must not be affected (multiroot)', async function() { + test('Detect a new Virtual Environment, and other workspace folder must not be affected (multiroot)', async function () { if (!IS_MULTI_ROOT_TEST) { return this.skip(); } diff --git a/src/test/interpreters/locators/workspaceVirtualEnvWatcherService.unit.test.ts b/src/test/interpreters/locators/workspaceVirtualEnvWatcherService.unit.test.ts index c3e9675cff0e..e6812523feb9 100644 --- a/src/test/interpreters/locators/workspaceVirtualEnvWatcherService.unit.test.ts +++ b/src/test/interpreters/locators/workspaceVirtualEnvWatcherService.unit.test.ts @@ -20,13 +20,13 @@ import { WorkspaceVirtualEnvWatcherService } from '../../../client/interpreter/l suite('Interpreters - Workspace VirtualEnv Watcher Service', () => { let disposables: Disposable[] = []; - setup(function() { + setup(function () { if (!isUnitTestExecution()) { return this.skip(); } }); teardown(() => { - disposables.forEach(d => { + disposables.forEach((d) => { try { d.dispose(); } catch { diff --git a/src/test/interpreters/mocks.ts b/src/test/interpreters/mocks.ts index 4016bd9d3da6..e32e5028c039 100644 --- a/src/test/interpreters/mocks.ts +++ b/src/test/interpreters/mocks.ts @@ -11,7 +11,7 @@ export class MockRegistry implements IRegistry { private values: { key: string; hive: RegistryHive; arch?: Architecture; value: string; name?: string }[] ) {} public async getKeys(key: string, hive: RegistryHive, arch?: Architecture): Promise { - const items = this.keys.find(item => { + const items = this.keys.find((item) => { if (typeof item.arch === 'number') { return item.key === key && item.hive === hive && item.arch === arch; } @@ -26,7 +26,7 @@ export class MockRegistry implements IRegistry { arch?: Architecture, name?: string ): Promise { - const items = this.values.find(item => { + const items = this.values.find((item) => { if (item.key !== key || item.hive !== hive) { return false; } diff --git a/src/test/interpreters/pipEnvService.unit.test.ts b/src/test/interpreters/pipEnvService.unit.test.ts index b3583db3c8ef..65a6f325de88 100644 --- a/src/test/interpreters/pipEnvService.unit.test.ts +++ b/src/test/interpreters/pipEnvService.unit.test.ts @@ -38,8 +38,8 @@ enum OS { suite('Interpreters - PipEnv', () => { const rootWorkspace = Uri.file(path.join('usr', 'desktop', 'wkspc1')).fsPath; - getNamesAndValues(OS).forEach(os => { - [undefined, Uri.file(path.join(rootWorkspace, 'one.py'))].forEach(resource => { + getNamesAndValues(OS).forEach((os) => { + [undefined, Uri.file(path.join(rootWorkspace, 'one.py'))].forEach((resource) => { const testSuffix = ` (${os.name}, ${resource ? 'with' : 'without'} a workspace)`; let pipEnvService: PipEnvService; @@ -72,64 +72,64 @@ suite('Interpreters - PipEnv', () => { pipEnvServiceHelper = mock(PipEnvServiceHelper); processService.setup((x: any) => x.then).returns(() => undefined); procServiceFactory - .setup(p => p.create(TypeMoq.It.isAny())) + .setup((p) => p.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(processService.object)); // tslint:disable-next-line:no-any const persistentState = TypeMoq.Mock.ofType>(); persistentStateFactory - .setup(p => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => persistentState.object); persistentStateFactory - .setup(p => p.createWorkspacePersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.createWorkspacePersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => persistentState.object); - persistentState.setup(p => p.value).returns(() => undefined); - persistentState.setup(p => p.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + persistentState.setup((p) => p.value).returns(() => undefined); + persistentState.setup((p) => p.updateValue(TypeMoq.It.isAny())).returns(() => Promise.resolve()); const workspaceFolder = TypeMoq.Mock.ofType(); - workspaceFolder.setup(w => w.uri).returns(() => Uri.file(rootWorkspace)); + workspaceFolder.setup((w) => w.uri).returns(() => Uri.file(rootWorkspace)); workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())) .returns(() => workspaceFolder.object); - workspaceService.setup(w => w.rootPath).returns(() => rootWorkspace); + workspaceService.setup((w) => w.rootPath).returns(() => rootWorkspace); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())) .returns(() => procServiceFactory.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterHelper))) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterHelper))) .returns(() => interpreterHelper.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ICurrentProcess))) + .setup((c) => c.get(TypeMoq.It.isValue(ICurrentProcess))) .returns(() => currentProcess.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IApplicationShell))) + .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell))) .returns(() => appShell.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPersistentStateFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(IPersistentStateFactory))) .returns(() => persistentStateFactory.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider))) + .setup((c) => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider))) .returns(() => envVarsProvider.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService))) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) .returns(() => platformService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) .returns(() => config.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPipEnvServiceHelper), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPipEnvServiceHelper), TypeMoq.It.isAny())) .returns(() => instance(pipEnvServiceHelper)); when(pipEnvServiceHelper.trackWorkspaceFolder(anything(), anything())).thenResolve(); config = TypeMoq.Mock.ofType(); settings = TypeMoq.Mock.ofType(); - config.setup(c => c.getSettings(TypeMoq.It.isValue(undefined))).returns(() => settings.object); - settings.setup(p => p.pipenvPath).returns(() => pipenvPathSetting); + config.setup((c) => c.getSettings(TypeMoq.It.isValue(undefined))).returns(() => settings.object); + settings.setup((p) => p.pipenvPath).returns(() => pipenvPathSetting); pipenvPathSetting = 'pipenv'; pipEnvService = new PipEnvService(serviceContainer.object); @@ -142,12 +142,12 @@ suite('Interpreters - PipEnv', () => { test(`Should return an empty list if there is no \'PipFile\'${testSuffix}`, async () => { const env = {}; envVarsProvider - .setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())) + .setup((e) => e.getEnvironmentVariables(TypeMoq.It.isAny())) .returns(() => Promise.resolve({})) .verifiable(TypeMoq.Times.once()); - currentProcess.setup(c => c.env).returns(() => env); + currentProcess.setup((c) => c.env).returns(() => env); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); const environments = await pipEnvService.getInterpreters(resource); @@ -157,19 +157,19 @@ suite('Interpreters - PipEnv', () => { }); test(`Should display warning message if there is a \'PipFile\' but \'pipenv --version\' fails ${testSuffix}`, async () => { const env = {}; - currentProcess.setup(c => c.env).returns(() => env); + currentProcess.setup((c) => c.env).returns(() => env); processService - .setup(p => + .setup((p) => p.exec(TypeMoq.It.isValue('pipenv'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny()) ) .returns(() => Promise.reject('')); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))) .returns(() => Promise.resolve(true)); const warningMessage = "Workspace contains Pipfile but 'pipenv' was not found. Make sure 'pipenv' is on the PATH."; appShell - .setup(a => a.showWarningMessage(warningMessage)) + .setup((a) => a.showWarningMessage(warningMessage)) .returns(() => Promise.resolve('')) .verifiable(TypeMoq.Times.once()); const environments = await pipEnvService.getInterpreters(resource); @@ -179,24 +179,24 @@ suite('Interpreters - PipEnv', () => { }); test(`Should display warning message if there is a \'PipFile\' but \'pipenv --venv\' fails with stderr ${testSuffix}`, async () => { const env = {}; - currentProcess.setup(c => c.env).returns(() => env); + currentProcess.setup((c) => c.env).returns(() => env); processService - .setup(p => + .setup((p) => p.exec(TypeMoq.It.isValue('pipenv'), TypeMoq.It.isValue(['--version']), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve({ stderr: '', stdout: 'pipenv, version 2018.11.26' })); processService - .setup(p => + .setup((p) => p.exec(TypeMoq.It.isValue('pipenv'), TypeMoq.It.isValue(['--venv']), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve({ stderr: 'Aborted!', stdout: '' })); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))) .returns(() => Promise.resolve(true)); const warningMessage = 'Workspace contains Pipfile but the associated virtual environment has not been setup. Setup the virtual environment manually if needed.'; appShell - .setup(a => a.showWarningMessage(warningMessage)) + .setup((a) => a.showWarningMessage(warningMessage)) .returns(() => Promise.resolve('')) .verifiable(TypeMoq.Times.once()); const environments = await pipEnvService.getInterpreters(resource); @@ -208,22 +208,22 @@ suite('Interpreters - PipEnv', () => { const env = {}; const pythonPath = 'one'; envVarsProvider - .setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())) + .setup((e) => e.getEnvironmentVariables(TypeMoq.It.isAny())) .returns(() => Promise.resolve({})) .verifiable(TypeMoq.Times.once()); - currentProcess.setup(c => c.env).returns(() => env); + currentProcess.setup((c) => c.env).returns(() => env); processService - .setup(p => p.exec(TypeMoq.It.isValue('pipenv'), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('pipenv'), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: pythonPath })); interpreterHelper - .setup(v => v.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((v) => v.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: new SemVer('1.0.0') })); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))) .returns(() => Promise.resolve(true)) .verifiable(); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(true)) .verifiable(); @@ -239,26 +239,26 @@ suite('Interpreters - PipEnv', () => { }; const pythonPath = 'one'; envVarsProvider - .setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())) + .setup((e) => e.getEnvironmentVariables(TypeMoq.It.isAny())) .returns(() => Promise.resolve({})) .verifiable(TypeMoq.Times.once()); - currentProcess.setup(c => c.env).returns(() => env); + currentProcess.setup((c) => c.env).returns(() => env); processService - .setup(p => p.exec(TypeMoq.It.isValue('pipenv'), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isValue('pipenv'), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: pythonPath })); interpreterHelper - .setup(v => v.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((v) => v.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ version: new SemVer('1.0.0') })); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, 'Pipfile')))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.never()); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, envPipFile)))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(path.join(rootWorkspace, envPipFile)))) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(fs => fs.fileExists(TypeMoq.It.isValue(pythonPath))) + .setup((fs) => fs.fileExists(TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(true)) .verifiable(); const environments = await pipEnvService.getInterpreters(resource); diff --git a/src/test/interpreters/pythonPathUpdater.test.ts b/src/test/interpreters/pythonPathUpdater.test.ts index 74f4e312aaf7..86f695bde851 100644 --- a/src/test/interpreters/pythonPathUpdater.test.ts +++ b/src/test/interpreters/pythonPathUpdater.test.ts @@ -16,14 +16,14 @@ suite('Python Path Settings Updater', () => { serviceContainer = TypeMoq.Mock.ofType(); workspaceService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); updaterServiceFactory = new PythonPathUpdaterServiceFactory(serviceContainer.object); } function setupConfigProvider(resource?: Uri): TypeMoq.IMock { const workspaceConfig = TypeMoq.Mock.ofType(); workspaceService - .setup(w => w.getConfiguration(TypeMoq.It.isValue('python'), TypeMoq.It.isValue(resource))) + .setup((w) => w.getConfiguration(TypeMoq.It.isValue('python'), TypeMoq.It.isValue(resource))) .returns(() => workspaceConfig.object); return workspaceConfig; } @@ -34,7 +34,7 @@ suite('Python Path Settings Updater', () => { const pythonPath = `xGlobalPythonPath${new Date().getMilliseconds()}`; const workspaceConfig = setupConfigProvider(); workspaceConfig - .setup(w => w.inspect(TypeMoq.It.isValue('pythonPath'))) + .setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))) .returns(() => { // tslint:disable-next-line:no-any return { globalValue: pythonPath } as any; @@ -42,7 +42,7 @@ suite('Python Path Settings Updater', () => { await updater.updatePythonPath(pythonPath); workspaceConfig.verify( - w => w.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (w) => w.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); }); @@ -50,11 +50,11 @@ suite('Python Path Settings Updater', () => { const updater = updaterServiceFactory.getGlobalPythonPathConfigurationService(); const pythonPath = `xGlobalPythonPath${new Date().getMilliseconds()}`; const workspaceConfig = setupConfigProvider(); - workspaceConfig.setup(w => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); + workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); await updater.updatePythonPath(pythonPath); workspaceConfig.verify( - w => + (w) => w.update( TypeMoq.It.isValue('pythonPath'), TypeMoq.It.isValue(pythonPath), @@ -74,7 +74,7 @@ suite('Python Path Settings Updater', () => { const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; const workspaceConfig = setupConfigProvider(workspaceFolder); workspaceConfig - .setup(w => w.inspect(TypeMoq.It.isValue('pythonPath'))) + .setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))) .returns(() => { // tslint:disable-next-line:no-any return { workspaceFolderValue: pythonPath } as any; @@ -82,7 +82,7 @@ suite('Python Path Settings Updater', () => { await updater.updatePythonPath(pythonPath); workspaceConfig.verify( - w => w.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (w) => w.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); }); @@ -92,11 +92,11 @@ suite('Python Path Settings Updater', () => { const updater = updaterServiceFactory.getWorkspaceFolderPythonPathConfigurationService(workspaceFolder); const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig.setup(w => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); + workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); await updater.updatePythonPath(pythonPath); workspaceConfig.verify( - w => + (w) => w.update( TypeMoq.It.isValue('pythonPath'), TypeMoq.It.isValue(pythonPath), @@ -112,11 +112,11 @@ suite('Python Path Settings Updater', () => { const pythonPath = Uri.file(path.join(workspaceFolderPath, 'env', 'bin', 'python')).fsPath; const expectedPythonPath = path.join('env', 'bin', 'python'); const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig.setup(w => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); + workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); await updater.updatePythonPath(pythonPath); workspaceConfig.verify( - w => + (w) => w.update( TypeMoq.It.isValue('pythonPath'), TypeMoq.It.isValue(expectedPythonPath), @@ -135,7 +135,7 @@ suite('Python Path Settings Updater', () => { const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; const workspaceConfig = setupConfigProvider(workspaceFolder); workspaceConfig - .setup(w => w.inspect(TypeMoq.It.isValue('pythonPath'))) + .setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))) .returns(() => { // tslint:disable-next-line:no-any return { workspaceValue: pythonPath } as any; @@ -143,7 +143,7 @@ suite('Python Path Settings Updater', () => { await updater.updatePythonPath(pythonPath); workspaceConfig.verify( - w => w.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (w) => w.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); }); @@ -153,11 +153,11 @@ suite('Python Path Settings Updater', () => { const updater = updaterServiceFactory.getWorkspacePythonPathConfigurationService(workspaceFolder); const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig.setup(w => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); + workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); await updater.updatePythonPath(pythonPath); workspaceConfig.verify( - w => + (w) => w.update( TypeMoq.It.isValue('pythonPath'), TypeMoq.It.isValue(pythonPath), @@ -173,11 +173,11 @@ suite('Python Path Settings Updater', () => { const pythonPath = Uri.file(path.join(workspaceFolderPath, 'env', 'bin', 'python')).fsPath; const expectedPythonPath = path.join('env', 'bin', 'python'); const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig.setup(w => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); + workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); await updater.updatePythonPath(pythonPath); workspaceConfig.verify( - w => + (w) => w.update( TypeMoq.It.isValue('pythonPath'), TypeMoq.It.isValue(expectedPythonPath), diff --git a/src/test/interpreters/serviceRegistry.unit.test.ts b/src/test/interpreters/serviceRegistry.unit.test.ts index db7f0aca598d..0547d111de6e 100644 --- a/src/test/interpreters/serviceRegistry.unit.test.ts +++ b/src/test/interpreters/serviceRegistry.unit.test.ts @@ -176,7 +176,7 @@ suite('Interpreters - Service Registry', () => { [WindowsStoreInterpreter, WindowsStoreInterpreter], [InterpreterHashProvider, InterpreterHashProvider], [InterpeterHashProviderFactory, InterpeterHashProviderFactory] - ].forEach(mapping => { + ].forEach((mapping) => { verify(serviceManager.addSingleton.apply(serviceManager, mapping as any)).once(); }); verify( diff --git a/src/test/interpreters/venv.unit.test.ts b/src/test/interpreters/venv.unit.test.ts index e026c51cff2f..fdd2a6393681 100644 --- a/src/test/interpreters/venv.unit.test.ts +++ b/src/test/interpreters/venv.unit.test.ts @@ -45,7 +45,7 @@ suite('Virtual environments', () => { process = TypeMoq.Mock.ofType(); virtualEnvMgr = TypeMoq.Mock.ofType(); - config.setup(x => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + config.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); serviceManager.addSingletonInstance(IConfigurationService, config.object); serviceManager.addSingletonInstance(IWorkspaceService, workspace.object); @@ -69,16 +69,18 @@ suite('Virtual environments', () => { const homedir = os.homedir(); const folders = ['Envs', 'testpath']; - settings.setup(x => x.venvFolders).returns(() => folders); - virtualEnvMgr.setup(v => v.getPyEnvRoot(TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined)); + settings.setup((x) => x.venvFolders).returns(() => folders); + virtualEnvMgr.setup((v) => v.getPyEnvRoot(TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined)); let paths = await pathProvider.getSearchPaths(); - let expected = ['envs', '.pyenv', '.direnv', '.virtualenvs', ...folders].map(item => path.join(homedir, item)); + let expected = ['envs', '.pyenv', '.direnv', '.virtualenvs', ...folders].map((item) => + path.join(homedir, item) + ); virtualEnvMgr.verifyAll(); expect(paths).to.deep.equal(expected, 'Global search folder list is incorrect.'); virtualEnvMgr.reset(); - virtualEnvMgr.setup(v => v.getPyEnvRoot(TypeMoq.It.isAny())).returns(() => Promise.resolve('pyenv_path')); + virtualEnvMgr.setup((v) => v.getPyEnvRoot(TypeMoq.It.isAny())).returns(() => Promise.resolve('pyenv_path')); paths = await pathProvider.getSearchPaths(); virtualEnvMgr.verifyAll(); @@ -90,7 +92,7 @@ suite('Virtual environments', () => { const pathProvider = new GlobalVirtualEnvironmentsSearchPathProvider(serviceContainer); const folders = ['.virtualenvs', '.direnv']; - settings.setup(x => x.venvFolders).returns(() => folders); + settings.setup((x) => x.venvFolders).returns(() => folders); const paths = await pathProvider.getSearchPaths(); expect([...new Set(paths)]).to.deep.equal( @@ -105,14 +107,14 @@ suite('Virtual environments', () => { const homedir = os.homedir(); const workonFolder = path.join('~', '.workonFolder'); process - .setup(p => p.env) + .setup((p) => p.env) .returns(() => { return { WORKON_HOME: workonFolder }; }); - settings.setup(x => x.venvFolders).returns(() => []); + settings.setup((x) => x.venvFolders).returns(() => []); const paths = await pathProvider.getSearchPaths(); - const expected = ['envs', '.pyenv', '.direnv', '.virtualenvs'].map(item => path.join(homedir, item)); + const expected = ['envs', '.pyenv', '.direnv', '.virtualenvs'].map((item) => path.join(homedir, item)); expected.push(untildify(workonFolder)); expect(paths).to.deep.equal(expected, 'WORKON_HOME environment variable not read.'); @@ -124,30 +126,30 @@ suite('Virtual environments', () => { const homedir = os.homedir(); const workonFolder = path.join('path', 'to', '.workonFolder'); process - .setup(p => p.env) + .setup((p) => p.env) .returns(() => { return { WORKON_HOME: workonFolder }; }); - settings.setup(x => x.venvFolders).returns(() => []); + settings.setup((x) => x.venvFolders).returns(() => []); const paths = await pathProvider.getSearchPaths(); - const expected = ['envs', '.pyenv', '.direnv', '.virtualenvs'].map(item => path.join(homedir, item)); + const expected = ['envs', '.pyenv', '.direnv', '.virtualenvs'].map((item) => path.join(homedir, item)); expected.push(workonFolder); expect(paths).to.deep.equal(expected, 'WORKON_HOME environment variable not read.'); }); test('Workspace search paths', async () => { - settings.setup(x => x.venvPath).returns(() => path.join('~', 'foo')); + settings.setup((x) => x.venvPath).returns(() => path.join('~', 'foo')); const wsRoot = TypeMoq.Mock.ofType(); - wsRoot.setup(x => x.uri).returns(() => Uri.file('root')); + wsRoot.setup((x) => x.uri).returns(() => Uri.file('root')); const folder1 = TypeMoq.Mock.ofType(); - folder1.setup(x => x.uri).returns(() => Uri.file('dir1')); + folder1.setup((x) => x.uri).returns(() => Uri.file('dir1')); - workspace.setup(x => x.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => wsRoot.object); - workspace.setup(x => x.workspaceFolders).returns(() => [wsRoot.object, folder1.object]); + workspace.setup((x) => x.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => wsRoot.object); + workspace.setup((x) => x.workspaceFolders).returns(() => [wsRoot.object, folder1.object]); const pathProvider = new WorkspaceVirtualEnvironmentsSearchPathProvider(serviceContainer); const paths = await pathProvider.getSearchPaths(Uri.file('')); @@ -156,7 +158,7 @@ suite('Virtual environments', () => { const isWindows = new PlatformService(); const fixCase = (item: string) => (isWindows ? item.toUpperCase() : item); const expected = [path.join(homedir, 'foo'), 'root', path.join('root', '.direnv')] - .map(item => Uri.file(item).fsPath) + .map((item) => Uri.file(item).fsPath) .map(fixCase); expect(paths.map(fixCase)).to.deep.equal(expected, 'Workspace venv folder search list does not match.'); }); diff --git a/src/test/interpreters/virtualEnvManager.unit.test.ts b/src/test/interpreters/virtualEnvManager.unit.test.ts index 970c26a6eeff..96afb7ff872f 100644 --- a/src/test/interpreters/virtualEnvManager.unit.test.ts +++ b/src/test/interpreters/virtualEnvManager.unit.test.ts @@ -27,12 +27,12 @@ suite('Virtual environment manager', () => { test('Use environment folder as env name', async () => { const serviceContainer = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IPipEnvService))) + .setup((s) => s.get(TypeMoq.It.isValue(IPipEnvService))) .returns(() => TypeMoq.Mock.ofType().object); const workspaceService = TypeMoq.Mock.ofType(); - workspaceService.setup(w => w.hasWorkspaceFolders).returns(() => false); + workspaceService.setup((w) => w.hasWorkspaceFolders).returns(() => false); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((s) => s.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); const venvManager = new VirtualEnvironmentManager(serviceContainer.object); @@ -45,23 +45,23 @@ suite('Virtual environment manager', () => { const serviceContainer = TypeMoq.Mock.ofType(); const pipEnvService = TypeMoq.Mock.ofType(); pipEnvService - .setup(p => p.isRelatedPipEnvironment(TypeMoq.It.isAny(), TypeMoq.It.isValue(pythonPath))) + .setup((p) => p.isRelatedPipEnvironment(TypeMoq.It.isAny(), TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IProcessServiceFactory))) + .setup((s) => s.get(TypeMoq.It.isValue(IProcessServiceFactory))) .returns(() => TypeMoq.Mock.ofType().object); - serviceContainer.setup(s => s.get(TypeMoq.It.isValue(IPipEnvService))).returns(() => pipEnvService.object); + serviceContainer.setup((s) => s.get(TypeMoq.It.isValue(IPipEnvService))).returns(() => pipEnvService.object); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IFileSystem))) + .setup((s) => s.get(TypeMoq.It.isValue(IFileSystem))) .returns(() => TypeMoq.Mock.ofType().object); const workspaceUri = Uri.file(path.join('root', 'sub', 'wkspace folder')); const workspaceFolder: WorkspaceFolder = { name: 'wkspace folder', index: 0, uri: workspaceUri }; const workspaceService = TypeMoq.Mock.ofType(); - workspaceService.setup(w => w.hasWorkspaceFolders).returns(() => true); - workspaceService.setup(w => w.workspaceFolders).returns(() => [workspaceFolder]); + workspaceService.setup((w) => w.hasWorkspaceFolders).returns(() => true); + workspaceService.setup((w) => w.workspaceFolders).returns(() => [workspaceFolder]); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((s) => s.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); const venvManager = new VirtualEnvironmentManager(serviceContainer.object); @@ -74,25 +74,27 @@ suite('Virtual environment manager', () => { async function testSuffix(expectedEnvName: string, isPipEnvironment: boolean = false, resource?: Uri) { const serviceContainer = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IProcessServiceFactory))) + .setup((s) => s.get(TypeMoq.It.isValue(IProcessServiceFactory))) .returns(() => TypeMoq.Mock.ofType().object); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IFileSystem))) + .setup((s) => s.get(TypeMoq.It.isValue(IFileSystem))) .returns(() => TypeMoq.Mock.ofType().object); const pipEnvService = TypeMoq.Mock.ofType(); pipEnvService - .setup(w => w.isRelatedPipEnvironment(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((w) => w.isRelatedPipEnvironment(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(isPipEnvironment)); - serviceContainer.setup(s => s.get(TypeMoq.It.isValue(IPipEnvService))).returns(() => pipEnvService.object); + serviceContainer.setup((s) => s.get(TypeMoq.It.isValue(IPipEnvService))).returns(() => pipEnvService.object); const workspaceService = TypeMoq.Mock.ofType(); - workspaceService.setup(w => w.hasWorkspaceFolders).returns(() => false); + workspaceService.setup((w) => w.hasWorkspaceFolders).returns(() => false); if (resource) { const workspaceFolder = TypeMoq.Mock.ofType(); - workspaceFolder.setup(w => w.uri).returns(() => resource); - workspaceService.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder.object); + workspaceFolder.setup((w) => w.uri).returns(() => resource); + workspaceService + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())) + .returns(() => workspaceFolder.object); } serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((s) => s.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); const venvManager = new VirtualEnvironmentManager(serviceContainer.object); diff --git a/src/test/interpreters/virtualEnvs/condaInheritEnvPrompt.unit.test.ts b/src/test/interpreters/virtualEnvs/condaInheritEnvPrompt.unit.test.ts index e4d3232644d8..94c7b84ea96f 100644 --- a/src/test/interpreters/virtualEnvs/condaInheritEnvPrompt.unit.test.ts +++ b/src/test/interpreters/virtualEnvs/condaInheritEnvPrompt.unit.test.ts @@ -1,524 +1,524 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { assert, expect } from 'chai'; -import * as sinon from 'sinon'; -import { instance, mock, verify, when } from 'ts-mockito'; -import * as TypeMoq from 'typemoq'; -import { ConfigurationTarget, Uri, WorkspaceConfiguration } from 'vscode'; -import { IApplicationShell, IWorkspaceService } from '../../../client/common/application/types'; -import { PersistentStateFactory } from '../../../client/common/persistentState'; -import { IPlatformService } from '../../../client/common/platform/types'; -import { IBrowserService, IPersistentState, IPersistentStateFactory } from '../../../client/common/types'; -import { createDeferred, createDeferredFromPromise, sleep } from '../../../client/common/utils/async'; -import { Common, InteractiveShiftEnterBanner, Interpreters } from '../../../client/common/utils/localize'; -import { IInterpreterService, InterpreterType } from '../../../client/interpreter/contracts'; -import { - CondaInheritEnvPrompt, - condaInheritEnvPromptKey -} from '../../../client/interpreter/virtualEnvs/condaInheritEnvPrompt'; - -// tslint:disable:no-any - -// tslint:disable:max-func-body-length -suite('Conda Inherit Env Prompt', async () => { - const resource = Uri.file('a'); - let workspaceService: TypeMoq.IMock; - let appShell: TypeMoq.IMock; - let interpreterService: TypeMoq.IMock; - let platformService: TypeMoq.IMock; - let browserService: TypeMoq.IMock; - let persistentStateFactory: IPersistentStateFactory; - let notificationPromptEnabled: TypeMoq.IMock>; - let condaInheritEnvPrompt: CondaInheritEnvPrompt; - function verifyAll() { - workspaceService.verifyAll(); - appShell.verifyAll(); - interpreterService.verifyAll(); - } - - suite('Method shouldShowPrompt()', () => { - setup(() => { - workspaceService = TypeMoq.Mock.ofType(); - appShell = TypeMoq.Mock.ofType(); - browserService = TypeMoq.Mock.ofType(); - interpreterService = TypeMoq.Mock.ofType(); - persistentStateFactory = mock(PersistentStateFactory); - platformService = TypeMoq.Mock.ofType(); - condaInheritEnvPrompt = new CondaInheritEnvPrompt( - interpreterService.object, - workspaceService.object, - browserService.object, - appShell.object, - instance(persistentStateFactory), - platformService.object - ); - }); - test('Returns false if prompt has already been shown in the current session', async () => { - condaInheritEnvPrompt = new CondaInheritEnvPrompt( - interpreterService.object, - workspaceService.object, - browserService.object, - appShell.object, - instance(persistentStateFactory), - platformService.object, - true - ); - const workspaceConfig = TypeMoq.Mock.ofType(); - interpreterService - .setup(is => is.getActiveInterpreter(resource)) - .returns(() => Promise.resolve(undefined) as any) - .verifiable(TypeMoq.Times.never()); - workspaceService - .setup(ws => ws.getConfiguration('terminal', resource)) - .returns(() => workspaceConfig.object) - .verifiable(TypeMoq.Times.never()); - const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); - expect(result).to.equal(false, 'Prompt should not be shown'); - expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(true, 'Should be true'); - verifyAll(); - }); - test('Returns false if on Windows', async () => { - platformService - .setup(ps => ps.isWindows) - .returns(() => true) - .verifiable(TypeMoq.Times.once()); - const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); - expect(result).to.equal(false, 'Prompt should not be shown'); - expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(false, 'Should be false'); - verifyAll(); - }); - test('Returns false if active interpreter is not of type Conda', async () => { - const interpreter = { - type: InterpreterType.Pipenv - }; - const workspaceConfig = TypeMoq.Mock.ofType(); - platformService - .setup(ps => ps.isWindows) - .returns(() => false) - .verifiable(TypeMoq.Times.once()); - interpreterService - .setup(is => is.getActiveInterpreter(resource)) - .returns(() => Promise.resolve(interpreter) as any) - .verifiable(TypeMoq.Times.once()); - workspaceService - .setup(ws => ws.getConfiguration('terminal', resource)) - .returns(() => workspaceConfig.object) - .verifiable(TypeMoq.Times.never()); - const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); - expect(result).to.equal(false, 'Prompt should not be shown'); - expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(false, 'Should be false'); - verifyAll(); - }); - test('Returns false if no active interpreter is present', async () => { - const workspaceConfig = TypeMoq.Mock.ofType(); - platformService - .setup(ps => ps.isWindows) - .returns(() => false) - .verifiable(TypeMoq.Times.once()); - interpreterService - .setup(is => is.getActiveInterpreter(resource)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.once()); - workspaceService - .setup(ws => ws.getConfiguration('terminal', resource)) - .returns(() => workspaceConfig.object) - .verifiable(TypeMoq.Times.never()); - const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); - expect(result).to.equal(false, 'Prompt should not be shown'); - expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(false, 'Should be false'); - verifyAll(); - }); - test('Returns false if settings returned is `undefined`', async () => { - const interpreter = { - type: InterpreterType.Conda - }; - const workspaceConfig = TypeMoq.Mock.ofType(); - platformService - .setup(ps => ps.isWindows) - .returns(() => false) - .verifiable(TypeMoq.Times.once()); - interpreterService - .setup(is => is.getActiveInterpreter(resource)) - .returns(() => Promise.resolve(interpreter) as any) - .verifiable(TypeMoq.Times.once()); - workspaceService - .setup(ws => ws.getConfiguration('terminal', resource)) - .returns(() => workspaceConfig.object) - .verifiable(TypeMoq.Times.once()); - workspaceConfig - .setup(ws => ws.inspect('integrated.inheritEnv')) - .returns(() => undefined) - .verifiable(TypeMoq.Times.once()); - const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); - expect(result).to.equal(false, 'Prompt should not be shown'); - expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(false, 'Should be false'); - verifyAll(); - }); - [ - { - name: 'Returns false if globalValue `terminal.integrated.inheritEnv` setting is set', - settings: { - globalValue: true, - workspaceValue: undefined, - workspaceFolderValue: undefined - } - }, - { - name: 'Returns false if workspaceValue of `terminal.integrated.inheritEnv` setting is set', - settings: { - globalValue: undefined, - workspaceValue: true, - workspaceFolderValue: undefined - } - }, - { - name: 'Returns false if workspaceFolderValue of `terminal.integrated.inheritEnv` setting is set', - settings: { - globalValue: undefined, - workspaceValue: undefined, - workspaceFolderValue: false - } - } - ].forEach(testParams => { - test(testParams.name, async () => { - const interpreter = { - type: InterpreterType.Conda - }; - const workspaceConfig = TypeMoq.Mock.ofType(); - platformService - .setup(ps => ps.isWindows) - .returns(() => false) - .verifiable(TypeMoq.Times.once()); - interpreterService - .setup(is => is.getActiveInterpreter(resource)) - .returns(() => Promise.resolve(interpreter) as any) - .verifiable(TypeMoq.Times.once()); - workspaceService - .setup(ws => ws.getConfiguration('terminal', resource)) - .returns(() => workspaceConfig.object) - .verifiable(TypeMoq.Times.once()); - workspaceConfig - .setup(ws => ws.inspect('integrated.inheritEnv')) - .returns(() => testParams.settings as any); - const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); - expect(result).to.equal(false, 'Prompt should not be shown'); - expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(false, 'Should be false'); - verifyAll(); - }); - }); - test('Returns true otherwise', async () => { - const interpreter = { - type: InterpreterType.Conda - }; - const settings = { - globalValue: undefined, - workspaceValue: undefined, - workspaceFolderValue: undefined - }; - const workspaceConfig = TypeMoq.Mock.ofType(); - platformService - .setup(ps => ps.isWindows) - .returns(() => false) - .verifiable(TypeMoq.Times.once()); - interpreterService - .setup(is => is.getActiveInterpreter(resource)) - .returns(() => Promise.resolve(interpreter) as any) - .verifiable(TypeMoq.Times.once()); - workspaceService - .setup(ws => ws.getConfiguration('terminal', resource)) - .returns(() => workspaceConfig.object) - .verifiable(TypeMoq.Times.once()); - workspaceConfig.setup(ws => ws.inspect('integrated.inheritEnv')).returns(() => settings as any); - const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); - expect(result).to.equal(true, 'Prompt should be shown'); - expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(true, 'Should be true'); - verifyAll(); - }); - }); - suite('Method activate()', () => { - let initializeInBackground: sinon.SinonStub; - setup(() => { - workspaceService = TypeMoq.Mock.ofType(); - appShell = TypeMoq.Mock.ofType(); - browserService = TypeMoq.Mock.ofType(); - interpreterService = TypeMoq.Mock.ofType(); - persistentStateFactory = mock(PersistentStateFactory); - platformService = TypeMoq.Mock.ofType(); - }); - - teardown(() => { - sinon.restore(); - }); - - test('Invokes initializeInBackground() in the background', async () => { - const initializeInBackgroundDeferred = createDeferred(); - initializeInBackground = sinon.stub(CondaInheritEnvPrompt.prototype, 'initializeInBackground'); - initializeInBackground.callsFake(() => initializeInBackgroundDeferred.promise); - condaInheritEnvPrompt = new CondaInheritEnvPrompt( - interpreterService.object, - workspaceService.object, - browserService.object, - appShell.object, - instance(persistentStateFactory), - platformService.object - ); - - const promise = condaInheritEnvPrompt.activate(resource); - const deferred = createDeferredFromPromise(promise); - await sleep(1); - - // Ensure activate() function has completed while initializeInBackground() is still not resolved - assert.equal(deferred.completed, true); - - initializeInBackgroundDeferred.resolve(); - await sleep(1); - assert.ok(initializeInBackground.calledOnce); - }); - - test('Ignores errors raised by initializeInBackground()', async () => { - initializeInBackground = sinon.stub(CondaInheritEnvPrompt.prototype, 'initializeInBackground'); - initializeInBackground.rejects(new Error('Kaboom')); - condaInheritEnvPrompt = new CondaInheritEnvPrompt( - interpreterService.object, - workspaceService.object, - browserService.object, - appShell.object, - instance(persistentStateFactory), - platformService.object - ); - await condaInheritEnvPrompt.activate(resource); - assert.ok(initializeInBackground.calledOnce); - }); - }); - - suite('Method initializeInBackground()', () => { - let shouldShowPrompt: sinon.SinonStub; - let promptAndUpdate: sinon.SinonStub; - setup(() => { - workspaceService = TypeMoq.Mock.ofType(); - appShell = TypeMoq.Mock.ofType(); - browserService = TypeMoq.Mock.ofType(); - interpreterService = TypeMoq.Mock.ofType(); - persistentStateFactory = mock(PersistentStateFactory); - platformService = TypeMoq.Mock.ofType(); - }); - - teardown(() => { - sinon.restore(); - }); - - test('Show prompt if shouldShowPrompt() returns true', async () => { - shouldShowPrompt = sinon.stub(CondaInheritEnvPrompt.prototype, 'shouldShowPrompt'); - shouldShowPrompt.callsFake(() => Promise.resolve(true)); - promptAndUpdate = sinon.stub(CondaInheritEnvPrompt.prototype, 'promptAndUpdate'); - promptAndUpdate.callsFake(() => Promise.resolve(undefined)); - condaInheritEnvPrompt = new CondaInheritEnvPrompt( - interpreterService.object, - workspaceService.object, - browserService.object, - appShell.object, - instance(persistentStateFactory), - platformService.object - ); - await condaInheritEnvPrompt.initializeInBackground(resource); - assert.ok(shouldShowPrompt.calledOnce); - assert.ok(promptAndUpdate.calledOnce); - }); - - test('Do not show prompt if shouldShowPrompt() returns false', async () => { - shouldShowPrompt = sinon.stub(CondaInheritEnvPrompt.prototype, 'shouldShowPrompt'); - shouldShowPrompt.callsFake(() => Promise.resolve(false)); - promptAndUpdate = sinon.stub(CondaInheritEnvPrompt.prototype, 'promptAndUpdate'); - promptAndUpdate.callsFake(() => Promise.resolve(undefined)); - condaInheritEnvPrompt = new CondaInheritEnvPrompt( - interpreterService.object, - workspaceService.object, - browserService.object, - appShell.object, - instance(persistentStateFactory), - platformService.object - ); - await condaInheritEnvPrompt.initializeInBackground(resource); - assert.ok(shouldShowPrompt.calledOnce); - assert.ok(promptAndUpdate.notCalled); - }); - }); - - suite('Method promptAndUpdate()', () => { - const prompts = [ - InteractiveShiftEnterBanner.bannerLabelYes(), - InteractiveShiftEnterBanner.bannerLabelNo(), - Common.moreInfo() - ]; - setup(() => { - workspaceService = TypeMoq.Mock.ofType(); - appShell = TypeMoq.Mock.ofType(); - interpreterService = TypeMoq.Mock.ofType(); - persistentStateFactory = mock(PersistentStateFactory); - browserService = TypeMoq.Mock.ofType(); - notificationPromptEnabled = TypeMoq.Mock.ofType>(); - platformService = TypeMoq.Mock.ofType(); - when(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).thenReturn( - notificationPromptEnabled.object - ); - condaInheritEnvPrompt = new CondaInheritEnvPrompt( - interpreterService.object, - workspaceService.object, - browserService.object, - appShell.object, - instance(persistentStateFactory), - platformService.object - ); - }); - - test('Does not display prompt if it is disabled', async () => { - notificationPromptEnabled - .setup(n => n.value) - .returns(() => false) - .verifiable(TypeMoq.Times.once()); - appShell - .setup(a => a.showInformationMessage(Interpreters.condaInheritEnvMessage(), ...prompts)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.never()); - await condaInheritEnvPrompt.promptAndUpdate(); - verify(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).once(); - verifyAll(); - notificationPromptEnabled.verifyAll(); - }); - test('Do nothing if no option is selected', async () => { - const workspaceConfig = TypeMoq.Mock.ofType(); - notificationPromptEnabled - .setup(n => n.value) - .returns(() => true) - .verifiable(TypeMoq.Times.once()); - appShell - .setup(a => a.showInformationMessage(Interpreters.condaInheritEnvMessage(), ...prompts)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.once()); - workspaceService - .setup(ws => ws.getConfiguration('terminal')) - .returns(() => workspaceConfig.object) - .verifiable(TypeMoq.Times.never()); - workspaceConfig - .setup(wc => wc.update('integrated.inheritEnv', false, ConfigurationTarget.Global)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.never()); - notificationPromptEnabled - .setup(n => n.updateValue(false)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.never()); - browserService - .setup(b => b.launch('https://aka.ms/AA66i8f')) - .returns(() => undefined) - .verifiable(TypeMoq.Times.never()); - await condaInheritEnvPrompt.promptAndUpdate(); - verify(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).once(); - verifyAll(); - workspaceConfig.verifyAll(); - notificationPromptEnabled.verifyAll(); - browserService.verifyAll(); - }); - test('Update terminal settings if `Yes` is selected', async () => { - const workspaceConfig = TypeMoq.Mock.ofType(); - notificationPromptEnabled - .setup(n => n.value) - .returns(() => true) - .verifiable(TypeMoq.Times.once()); - appShell - .setup(a => a.showInformationMessage(Interpreters.condaInheritEnvMessage(), ...prompts)) - .returns(() => Promise.resolve(InteractiveShiftEnterBanner.bannerLabelYes())) - .verifiable(TypeMoq.Times.once()); - workspaceService - .setup(ws => ws.getConfiguration('terminal')) - .returns(() => workspaceConfig.object) - .verifiable(TypeMoq.Times.once()); - workspaceConfig - .setup(wc => wc.update('integrated.inheritEnv', false, ConfigurationTarget.Global)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); - notificationPromptEnabled - .setup(n => n.updateValue(false)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.never()); - browserService - .setup(b => b.launch('https://aka.ms/AA66i8f')) - .returns(() => undefined) - .verifiable(TypeMoq.Times.never()); - await condaInheritEnvPrompt.promptAndUpdate(); - verify(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).once(); - verifyAll(); - workspaceConfig.verifyAll(); - notificationPromptEnabled.verifyAll(); - browserService.verifyAll(); - }); - test('Disable notification prompt if `No` is selected', async () => { - const workspaceConfig = TypeMoq.Mock.ofType(); - notificationPromptEnabled - .setup(n => n.value) - .returns(() => true) - .verifiable(TypeMoq.Times.once()); - appShell - .setup(a => a.showInformationMessage(Interpreters.condaInheritEnvMessage(), ...prompts)) - .returns(() => Promise.resolve(InteractiveShiftEnterBanner.bannerLabelNo())) - .verifiable(TypeMoq.Times.once()); - workspaceService - .setup(ws => ws.getConfiguration('terminal')) - .returns(() => workspaceConfig.object) - .verifiable(TypeMoq.Times.never()); - workspaceConfig - .setup(wc => wc.update('integrated.inheritEnv', false, ConfigurationTarget.Global)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.never()); - notificationPromptEnabled - .setup(n => n.updateValue(false)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.once()); - browserService - .setup(b => b.launch('https://aka.ms/AA66i8f')) - .returns(() => undefined) - .verifiable(TypeMoq.Times.never()); - await condaInheritEnvPrompt.promptAndUpdate(); - verify(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).once(); - verifyAll(); - workspaceConfig.verifyAll(); - notificationPromptEnabled.verifyAll(); - browserService.verifyAll(); - }); - test('Launch browser if `More info` option is selected', async () => { - const workspaceConfig = TypeMoq.Mock.ofType(); - notificationPromptEnabled - .setup(n => n.value) - .returns(() => true) - .verifiable(TypeMoq.Times.once()); - appShell - .setup(a => a.showInformationMessage(Interpreters.condaInheritEnvMessage(), ...prompts)) - .returns(() => Promise.resolve(Common.moreInfo())) - .verifiable(TypeMoq.Times.once()); - workspaceService - .setup(ws => ws.getConfiguration('terminal')) - .returns(() => workspaceConfig.object) - .verifiable(TypeMoq.Times.never()); - workspaceConfig - .setup(wc => wc.update('integrated.inheritEnv', false, ConfigurationTarget.Global)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.never()); - notificationPromptEnabled - .setup(n => n.updateValue(false)) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.never()); - browserService - .setup(b => b.launch('https://aka.ms/AA66i8f')) - .returns(() => undefined) - .verifiable(TypeMoq.Times.once()); - await condaInheritEnvPrompt.promptAndUpdate(); - verify(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).once(); - verifyAll(); - workspaceConfig.verifyAll(); - notificationPromptEnabled.verifyAll(); - browserService.verifyAll(); - }); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { assert, expect } from 'chai'; +import * as sinon from 'sinon'; +import { instance, mock, verify, when } from 'ts-mockito'; +import * as TypeMoq from 'typemoq'; +import { ConfigurationTarget, Uri, WorkspaceConfiguration } from 'vscode'; +import { IApplicationShell, IWorkspaceService } from '../../../client/common/application/types'; +import { PersistentStateFactory } from '../../../client/common/persistentState'; +import { IPlatformService } from '../../../client/common/platform/types'; +import { IBrowserService, IPersistentState, IPersistentStateFactory } from '../../../client/common/types'; +import { createDeferred, createDeferredFromPromise, sleep } from '../../../client/common/utils/async'; +import { Common, InteractiveShiftEnterBanner, Interpreters } from '../../../client/common/utils/localize'; +import { IInterpreterService, InterpreterType } from '../../../client/interpreter/contracts'; +import { + CondaInheritEnvPrompt, + condaInheritEnvPromptKey +} from '../../../client/interpreter/virtualEnvs/condaInheritEnvPrompt'; + +// tslint:disable:no-any + +// tslint:disable:max-func-body-length +suite('Conda Inherit Env Prompt', async () => { + const resource = Uri.file('a'); + let workspaceService: TypeMoq.IMock; + let appShell: TypeMoq.IMock; + let interpreterService: TypeMoq.IMock; + let platformService: TypeMoq.IMock; + let browserService: TypeMoq.IMock; + let persistentStateFactory: IPersistentStateFactory; + let notificationPromptEnabled: TypeMoq.IMock>; + let condaInheritEnvPrompt: CondaInheritEnvPrompt; + function verifyAll() { + workspaceService.verifyAll(); + appShell.verifyAll(); + interpreterService.verifyAll(); + } + + suite('Method shouldShowPrompt()', () => { + setup(() => { + workspaceService = TypeMoq.Mock.ofType(); + appShell = TypeMoq.Mock.ofType(); + browserService = TypeMoq.Mock.ofType(); + interpreterService = TypeMoq.Mock.ofType(); + persistentStateFactory = mock(PersistentStateFactory); + platformService = TypeMoq.Mock.ofType(); + condaInheritEnvPrompt = new CondaInheritEnvPrompt( + interpreterService.object, + workspaceService.object, + browserService.object, + appShell.object, + instance(persistentStateFactory), + platformService.object + ); + }); + test('Returns false if prompt has already been shown in the current session', async () => { + condaInheritEnvPrompt = new CondaInheritEnvPrompt( + interpreterService.object, + workspaceService.object, + browserService.object, + appShell.object, + instance(persistentStateFactory), + platformService.object, + true + ); + const workspaceConfig = TypeMoq.Mock.ofType(); + interpreterService + .setup((is) => is.getActiveInterpreter(resource)) + .returns(() => Promise.resolve(undefined) as any) + .verifiable(TypeMoq.Times.never()); + workspaceService + .setup((ws) => ws.getConfiguration('terminal', resource)) + .returns(() => workspaceConfig.object) + .verifiable(TypeMoq.Times.never()); + const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); + expect(result).to.equal(false, 'Prompt should not be shown'); + expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(true, 'Should be true'); + verifyAll(); + }); + test('Returns false if on Windows', async () => { + platformService + .setup((ps) => ps.isWindows) + .returns(() => true) + .verifiable(TypeMoq.Times.once()); + const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); + expect(result).to.equal(false, 'Prompt should not be shown'); + expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(false, 'Should be false'); + verifyAll(); + }); + test('Returns false if active interpreter is not of type Conda', async () => { + const interpreter = { + type: InterpreterType.Pipenv + }; + const workspaceConfig = TypeMoq.Mock.ofType(); + platformService + .setup((ps) => ps.isWindows) + .returns(() => false) + .verifiable(TypeMoq.Times.once()); + interpreterService + .setup((is) => is.getActiveInterpreter(resource)) + .returns(() => Promise.resolve(interpreter) as any) + .verifiable(TypeMoq.Times.once()); + workspaceService + .setup((ws) => ws.getConfiguration('terminal', resource)) + .returns(() => workspaceConfig.object) + .verifiable(TypeMoq.Times.never()); + const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); + expect(result).to.equal(false, 'Prompt should not be shown'); + expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(false, 'Should be false'); + verifyAll(); + }); + test('Returns false if no active interpreter is present', async () => { + const workspaceConfig = TypeMoq.Mock.ofType(); + platformService + .setup((ps) => ps.isWindows) + .returns(() => false) + .verifiable(TypeMoq.Times.once()); + interpreterService + .setup((is) => is.getActiveInterpreter(resource)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.once()); + workspaceService + .setup((ws) => ws.getConfiguration('terminal', resource)) + .returns(() => workspaceConfig.object) + .verifiable(TypeMoq.Times.never()); + const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); + expect(result).to.equal(false, 'Prompt should not be shown'); + expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(false, 'Should be false'); + verifyAll(); + }); + test('Returns false if settings returned is `undefined`', async () => { + const interpreter = { + type: InterpreterType.Conda + }; + const workspaceConfig = TypeMoq.Mock.ofType(); + platformService + .setup((ps) => ps.isWindows) + .returns(() => false) + .verifiable(TypeMoq.Times.once()); + interpreterService + .setup((is) => is.getActiveInterpreter(resource)) + .returns(() => Promise.resolve(interpreter) as any) + .verifiable(TypeMoq.Times.once()); + workspaceService + .setup((ws) => ws.getConfiguration('terminal', resource)) + .returns(() => workspaceConfig.object) + .verifiable(TypeMoq.Times.once()); + workspaceConfig + .setup((ws) => ws.inspect('integrated.inheritEnv')) + .returns(() => undefined) + .verifiable(TypeMoq.Times.once()); + const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); + expect(result).to.equal(false, 'Prompt should not be shown'); + expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(false, 'Should be false'); + verifyAll(); + }); + [ + { + name: 'Returns false if globalValue `terminal.integrated.inheritEnv` setting is set', + settings: { + globalValue: true, + workspaceValue: undefined, + workspaceFolderValue: undefined + } + }, + { + name: 'Returns false if workspaceValue of `terminal.integrated.inheritEnv` setting is set', + settings: { + globalValue: undefined, + workspaceValue: true, + workspaceFolderValue: undefined + } + }, + { + name: 'Returns false if workspaceFolderValue of `terminal.integrated.inheritEnv` setting is set', + settings: { + globalValue: undefined, + workspaceValue: undefined, + workspaceFolderValue: false + } + } + ].forEach((testParams) => { + test(testParams.name, async () => { + const interpreter = { + type: InterpreterType.Conda + }; + const workspaceConfig = TypeMoq.Mock.ofType(); + platformService + .setup((ps) => ps.isWindows) + .returns(() => false) + .verifiable(TypeMoq.Times.once()); + interpreterService + .setup((is) => is.getActiveInterpreter(resource)) + .returns(() => Promise.resolve(interpreter) as any) + .verifiable(TypeMoq.Times.once()); + workspaceService + .setup((ws) => ws.getConfiguration('terminal', resource)) + .returns(() => workspaceConfig.object) + .verifiable(TypeMoq.Times.once()); + workspaceConfig + .setup((ws) => ws.inspect('integrated.inheritEnv')) + .returns(() => testParams.settings as any); + const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); + expect(result).to.equal(false, 'Prompt should not be shown'); + expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(false, 'Should be false'); + verifyAll(); + }); + }); + test('Returns true otherwise', async () => { + const interpreter = { + type: InterpreterType.Conda + }; + const settings = { + globalValue: undefined, + workspaceValue: undefined, + workspaceFolderValue: undefined + }; + const workspaceConfig = TypeMoq.Mock.ofType(); + platformService + .setup((ps) => ps.isWindows) + .returns(() => false) + .verifiable(TypeMoq.Times.once()); + interpreterService + .setup((is) => is.getActiveInterpreter(resource)) + .returns(() => Promise.resolve(interpreter) as any) + .verifiable(TypeMoq.Times.once()); + workspaceService + .setup((ws) => ws.getConfiguration('terminal', resource)) + .returns(() => workspaceConfig.object) + .verifiable(TypeMoq.Times.once()); + workspaceConfig.setup((ws) => ws.inspect('integrated.inheritEnv')).returns(() => settings as any); + const result = await condaInheritEnvPrompt.shouldShowPrompt(resource); + expect(result).to.equal(true, 'Prompt should be shown'); + expect(condaInheritEnvPrompt.hasPromptBeenShownInCurrentSession).to.equal(true, 'Should be true'); + verifyAll(); + }); + }); + suite('Method activate()', () => { + let initializeInBackground: sinon.SinonStub; + setup(() => { + workspaceService = TypeMoq.Mock.ofType(); + appShell = TypeMoq.Mock.ofType(); + browserService = TypeMoq.Mock.ofType(); + interpreterService = TypeMoq.Mock.ofType(); + persistentStateFactory = mock(PersistentStateFactory); + platformService = TypeMoq.Mock.ofType(); + }); + + teardown(() => { + sinon.restore(); + }); + + test('Invokes initializeInBackground() in the background', async () => { + const initializeInBackgroundDeferred = createDeferred(); + initializeInBackground = sinon.stub(CondaInheritEnvPrompt.prototype, 'initializeInBackground'); + initializeInBackground.callsFake(() => initializeInBackgroundDeferred.promise); + condaInheritEnvPrompt = new CondaInheritEnvPrompt( + interpreterService.object, + workspaceService.object, + browserService.object, + appShell.object, + instance(persistentStateFactory), + platformService.object + ); + + const promise = condaInheritEnvPrompt.activate(resource); + const deferred = createDeferredFromPromise(promise); + await sleep(1); + + // Ensure activate() function has completed while initializeInBackground() is still not resolved + assert.equal(deferred.completed, true); + + initializeInBackgroundDeferred.resolve(); + await sleep(1); + assert.ok(initializeInBackground.calledOnce); + }); + + test('Ignores errors raised by initializeInBackground()', async () => { + initializeInBackground = sinon.stub(CondaInheritEnvPrompt.prototype, 'initializeInBackground'); + initializeInBackground.rejects(new Error('Kaboom')); + condaInheritEnvPrompt = new CondaInheritEnvPrompt( + interpreterService.object, + workspaceService.object, + browserService.object, + appShell.object, + instance(persistentStateFactory), + platformService.object + ); + await condaInheritEnvPrompt.activate(resource); + assert.ok(initializeInBackground.calledOnce); + }); + }); + + suite('Method initializeInBackground()', () => { + let shouldShowPrompt: sinon.SinonStub; + let promptAndUpdate: sinon.SinonStub; + setup(() => { + workspaceService = TypeMoq.Mock.ofType(); + appShell = TypeMoq.Mock.ofType(); + browserService = TypeMoq.Mock.ofType(); + interpreterService = TypeMoq.Mock.ofType(); + persistentStateFactory = mock(PersistentStateFactory); + platformService = TypeMoq.Mock.ofType(); + }); + + teardown(() => { + sinon.restore(); + }); + + test('Show prompt if shouldShowPrompt() returns true', async () => { + shouldShowPrompt = sinon.stub(CondaInheritEnvPrompt.prototype, 'shouldShowPrompt'); + shouldShowPrompt.callsFake(() => Promise.resolve(true)); + promptAndUpdate = sinon.stub(CondaInheritEnvPrompt.prototype, 'promptAndUpdate'); + promptAndUpdate.callsFake(() => Promise.resolve(undefined)); + condaInheritEnvPrompt = new CondaInheritEnvPrompt( + interpreterService.object, + workspaceService.object, + browserService.object, + appShell.object, + instance(persistentStateFactory), + platformService.object + ); + await condaInheritEnvPrompt.initializeInBackground(resource); + assert.ok(shouldShowPrompt.calledOnce); + assert.ok(promptAndUpdate.calledOnce); + }); + + test('Do not show prompt if shouldShowPrompt() returns false', async () => { + shouldShowPrompt = sinon.stub(CondaInheritEnvPrompt.prototype, 'shouldShowPrompt'); + shouldShowPrompt.callsFake(() => Promise.resolve(false)); + promptAndUpdate = sinon.stub(CondaInheritEnvPrompt.prototype, 'promptAndUpdate'); + promptAndUpdate.callsFake(() => Promise.resolve(undefined)); + condaInheritEnvPrompt = new CondaInheritEnvPrompt( + interpreterService.object, + workspaceService.object, + browserService.object, + appShell.object, + instance(persistentStateFactory), + platformService.object + ); + await condaInheritEnvPrompt.initializeInBackground(resource); + assert.ok(shouldShowPrompt.calledOnce); + assert.ok(promptAndUpdate.notCalled); + }); + }); + + suite('Method promptAndUpdate()', () => { + const prompts = [ + InteractiveShiftEnterBanner.bannerLabelYes(), + InteractiveShiftEnterBanner.bannerLabelNo(), + Common.moreInfo() + ]; + setup(() => { + workspaceService = TypeMoq.Mock.ofType(); + appShell = TypeMoq.Mock.ofType(); + interpreterService = TypeMoq.Mock.ofType(); + persistentStateFactory = mock(PersistentStateFactory); + browserService = TypeMoq.Mock.ofType(); + notificationPromptEnabled = TypeMoq.Mock.ofType>(); + platformService = TypeMoq.Mock.ofType(); + when(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).thenReturn( + notificationPromptEnabled.object + ); + condaInheritEnvPrompt = new CondaInheritEnvPrompt( + interpreterService.object, + workspaceService.object, + browserService.object, + appShell.object, + instance(persistentStateFactory), + platformService.object + ); + }); + + test('Does not display prompt if it is disabled', async () => { + notificationPromptEnabled + .setup((n) => n.value) + .returns(() => false) + .verifiable(TypeMoq.Times.once()); + appShell + .setup((a) => a.showInformationMessage(Interpreters.condaInheritEnvMessage(), ...prompts)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.never()); + await condaInheritEnvPrompt.promptAndUpdate(); + verify(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).once(); + verifyAll(); + notificationPromptEnabled.verifyAll(); + }); + test('Do nothing if no option is selected', async () => { + const workspaceConfig = TypeMoq.Mock.ofType(); + notificationPromptEnabled + .setup((n) => n.value) + .returns(() => true) + .verifiable(TypeMoq.Times.once()); + appShell + .setup((a) => a.showInformationMessage(Interpreters.condaInheritEnvMessage(), ...prompts)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.once()); + workspaceService + .setup((ws) => ws.getConfiguration('terminal')) + .returns(() => workspaceConfig.object) + .verifiable(TypeMoq.Times.never()); + workspaceConfig + .setup((wc) => wc.update('integrated.inheritEnv', false, ConfigurationTarget.Global)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.never()); + notificationPromptEnabled + .setup((n) => n.updateValue(false)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.never()); + browserService + .setup((b) => b.launch('https://aka.ms/AA66i8f')) + .returns(() => undefined) + .verifiable(TypeMoq.Times.never()); + await condaInheritEnvPrompt.promptAndUpdate(); + verify(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).once(); + verifyAll(); + workspaceConfig.verifyAll(); + notificationPromptEnabled.verifyAll(); + browserService.verifyAll(); + }); + test('Update terminal settings if `Yes` is selected', async () => { + const workspaceConfig = TypeMoq.Mock.ofType(); + notificationPromptEnabled + .setup((n) => n.value) + .returns(() => true) + .verifiable(TypeMoq.Times.once()); + appShell + .setup((a) => a.showInformationMessage(Interpreters.condaInheritEnvMessage(), ...prompts)) + .returns(() => Promise.resolve(InteractiveShiftEnterBanner.bannerLabelYes())) + .verifiable(TypeMoq.Times.once()); + workspaceService + .setup((ws) => ws.getConfiguration('terminal')) + .returns(() => workspaceConfig.object) + .verifiable(TypeMoq.Times.once()); + workspaceConfig + .setup((wc) => wc.update('integrated.inheritEnv', false, ConfigurationTarget.Global)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); + notificationPromptEnabled + .setup((n) => n.updateValue(false)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.never()); + browserService + .setup((b) => b.launch('https://aka.ms/AA66i8f')) + .returns(() => undefined) + .verifiable(TypeMoq.Times.never()); + await condaInheritEnvPrompt.promptAndUpdate(); + verify(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).once(); + verifyAll(); + workspaceConfig.verifyAll(); + notificationPromptEnabled.verifyAll(); + browserService.verifyAll(); + }); + test('Disable notification prompt if `No` is selected', async () => { + const workspaceConfig = TypeMoq.Mock.ofType(); + notificationPromptEnabled + .setup((n) => n.value) + .returns(() => true) + .verifiable(TypeMoq.Times.once()); + appShell + .setup((a) => a.showInformationMessage(Interpreters.condaInheritEnvMessage(), ...prompts)) + .returns(() => Promise.resolve(InteractiveShiftEnterBanner.bannerLabelNo())) + .verifiable(TypeMoq.Times.once()); + workspaceService + .setup((ws) => ws.getConfiguration('terminal')) + .returns(() => workspaceConfig.object) + .verifiable(TypeMoq.Times.never()); + workspaceConfig + .setup((wc) => wc.update('integrated.inheritEnv', false, ConfigurationTarget.Global)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.never()); + notificationPromptEnabled + .setup((n) => n.updateValue(false)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.once()); + browserService + .setup((b) => b.launch('https://aka.ms/AA66i8f')) + .returns(() => undefined) + .verifiable(TypeMoq.Times.never()); + await condaInheritEnvPrompt.promptAndUpdate(); + verify(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).once(); + verifyAll(); + workspaceConfig.verifyAll(); + notificationPromptEnabled.verifyAll(); + browserService.verifyAll(); + }); + test('Launch browser if `More info` option is selected', async () => { + const workspaceConfig = TypeMoq.Mock.ofType(); + notificationPromptEnabled + .setup((n) => n.value) + .returns(() => true) + .verifiable(TypeMoq.Times.once()); + appShell + .setup((a) => a.showInformationMessage(Interpreters.condaInheritEnvMessage(), ...prompts)) + .returns(() => Promise.resolve(Common.moreInfo())) + .verifiable(TypeMoq.Times.once()); + workspaceService + .setup((ws) => ws.getConfiguration('terminal')) + .returns(() => workspaceConfig.object) + .verifiable(TypeMoq.Times.never()); + workspaceConfig + .setup((wc) => wc.update('integrated.inheritEnv', false, ConfigurationTarget.Global)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.never()); + notificationPromptEnabled + .setup((n) => n.updateValue(false)) + .returns(() => Promise.resolve(undefined)) + .verifiable(TypeMoq.Times.never()); + browserService + .setup((b) => b.launch('https://aka.ms/AA66i8f')) + .returns(() => undefined) + .verifiable(TypeMoq.Times.once()); + await condaInheritEnvPrompt.promptAndUpdate(); + verify(persistentStateFactory.createGlobalPersistentState(condaInheritEnvPromptKey, true)).once(); + verifyAll(); + workspaceConfig.verifyAll(); + notificationPromptEnabled.verifyAll(); + browserService.verifyAll(); + }); + }); +}); diff --git a/src/test/interpreters/virtualEnvs/index.unit.test.ts b/src/test/interpreters/virtualEnvs/index.unit.test.ts index bc723fe5fd55..b0ab3c9139c8 100644 --- a/src/test/interpreters/virtualEnvs/index.unit.test.ts +++ b/src/test/interpreters/virtualEnvs/index.unit.test.ts @@ -42,21 +42,21 @@ suite('Virtual Environment Manager', () => { terminalActivation = TypeMoq.Mock.ofType(); platformService = TypeMoq.Mock.ofType(); - processService.setup(p => (p as any).then).returns(() => undefined); - processFactory.setup(p => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); + processService.setup((p) => (p as any).then).returns(() => undefined); + processFactory.setup((p) => p.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService.object)); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(IProcessServiceFactory))) .returns(() => processFactory.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => process.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPathUtils))).returns(() => pathUtils.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fs.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))).returns(() => workspace.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPipEnvService))).returns(() => pipEnvService.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICurrentProcess))).returns(() => process.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IPathUtils))).returns(() => pathUtils.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fs.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))).returns(() => workspace.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IPipEnvService))).returns(() => pipEnvService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ITerminalActivationCommandProvider), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ITerminalActivationCommandProvider), TypeMoq.It.isAny())) .returns(() => terminalActivation.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) .returns(() => platformService.object); virtualEnvMgr = new VirtualEnvironmentManager(serviceContainer.object); @@ -64,7 +64,7 @@ suite('Virtual Environment Manager', () => { test('Get PyEnv Root from PYENV_ROOT', async () => { process - .setup(p => p.env) + .setup((p) => p.env) .returns(() => { return { PYENV_ROOT: 'yes' }; }) @@ -78,13 +78,13 @@ suite('Virtual Environment Manager', () => { test('Get PyEnv Root from current PYENV_ROOT', async () => { process - .setup(p => p.env) + .setup((p) => p.env) .returns(() => { return {}; }) .verifiable(TypeMoq.Times.once()); processService - .setup(p => p.exec(TypeMoq.It.isValue('pyenv'), TypeMoq.It.isValue(['root']))) + .setup((p) => p.exec(TypeMoq.It.isValue('pyenv'), TypeMoq.It.isValue(['root']))) .returns(() => Promise.resolve({ stdout: 'PROC' })) .verifiable(TypeMoq.Times.once()); @@ -97,17 +97,17 @@ suite('Virtual Environment Manager', () => { test('Get default PyEnv Root path', async () => { process - .setup(p => p.env) + .setup((p) => p.env) .returns(() => { return {}; }) .verifiable(TypeMoq.Times.once()); processService - .setup(p => p.exec(TypeMoq.It.isValue('pyenv'), TypeMoq.It.isValue(['root']))) + .setup((p) => p.exec(TypeMoq.It.isValue('pyenv'), TypeMoq.It.isValue(['root']))) .returns(() => Promise.resolve({ stdout: '', stderr: 'err' })) .verifiable(TypeMoq.Times.once()); pathUtils - .setup(p => p.home) + .setup((p) => p.home) .returns(() => 'HOME') .verifiable(TypeMoq.Times.once()); const pyenvRoot = await virtualEnvMgr.getPyEnvRoot(); @@ -121,7 +121,7 @@ suite('Virtual Environment Manager', () => { const pythonPath = path.join('a', 'b', 'c', 'python'); const dir = path.dirname(pythonPath); - fs.setup(f => f.fileExists(TypeMoq.It.isValue(path.join(dir, 'pyvenv.cfg')))) + fs.setup((f) => f.fileExists(TypeMoq.It.isValue(path.join(dir, 'pyvenv.cfg')))) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); @@ -134,7 +134,7 @@ suite('Virtual Environment Manager', () => { const pythonPath = path.join('a', 'b', 'c', 'python'); const dir = path.dirname(pythonPath); - fs.setup(f => f.fileExists(TypeMoq.It.isValue(path.join(dir, 'pyvenv.cfg')))) + fs.setup((f) => f.fileExists(TypeMoq.It.isValue(path.join(dir, 'pyvenv.cfg')))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); @@ -148,7 +148,7 @@ suite('Virtual Environment Manager', () => { const pythonPath = path.join('py-env-root', 'b', 'c', 'python'); process - .setup(p => p.env) + .setup((p) => p.env) .returns(() => { return { PYENV_ROOT: path.join('py-env-root', 'b') }; }) @@ -164,7 +164,7 @@ suite('Virtual Environment Manager', () => { const pythonPath = path.join('a', 'b', 'c', 'python'); process - .setup(p => p.env) + .setup((p) => p.env) .returns(() => { return { PYENV_ROOT: path.join('py-env-root', 'b') }; }) @@ -179,16 +179,16 @@ suite('Virtual Environment Manager', () => { test('Get Environment Type, detects pipenv', async () => { const pythonPath = path.join('x', 'b', 'c', 'python'); workspace - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); const ws = [{ uri: Uri.file('x') }]; workspace - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => ws as any) .verifiable(TypeMoq.Times.atLeastOnce()); pipEnvService - .setup(p => p.isRelatedPipEnvironment(TypeMoq.It.isAny(), TypeMoq.It.isValue(pythonPath))) + .setup((p) => p.isRelatedPipEnvironment(TypeMoq.It.isAny(), TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); @@ -202,16 +202,16 @@ suite('Virtual Environment Manager', () => { test('Get Environment Type, does not detect pipenv incorrectly', async () => { const pythonPath = path.join('x', 'b', 'c', 'python'); workspace - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); const ws = [{ uri: Uri.file('x') }]; workspace - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => ws as any) .verifiable(TypeMoq.Times.atLeastOnce()); pipEnvService - .setup(p => p.isRelatedPipEnvironment(TypeMoq.It.isAny(), TypeMoq.It.isValue(pythonPath))) + .setup((p) => p.isRelatedPipEnvironment(TypeMoq.It.isAny(), TypeMoq.It.isValue(pythonPath))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); @@ -227,11 +227,13 @@ suite('Virtual Environment Manager', () => { test(`Get Environment Type, detects virtualenv ${testTitleSuffix}`, async () => { const pythonPath = path.join('x', 'b', 'c', 'python'); terminalActivation - .setup(t => t.isShellSupported(TypeMoq.It.isAny())) + .setup((t) => t.isShellSupported(TypeMoq.It.isAny())) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); terminalActivation - .setup(t => t.getActivationCommandsForInterpreter!(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny())) + .setup((t) => + t.getActivationCommandsForInterpreter!(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny()) + ) .returns(() => Promise.resolve(['1'])) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -244,11 +246,13 @@ suite('Virtual Environment Manager', () => { test(`Get Environment Type, does not detect virtualenv incorrectly ${testTitleSuffix}`, async () => { const pythonPath = path.join('x', 'b', 'c', 'python'); terminalActivation - .setup(t => t.isShellSupported(TypeMoq.It.isAny())) + .setup((t) => t.isShellSupported(TypeMoq.It.isAny())) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); terminalActivation - .setup(t => t.getActivationCommandsForInterpreter!(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny())) + .setup((t) => + t.getActivationCommandsForInterpreter!(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny()) + ) .returns(() => Promise.resolve([])) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -259,11 +263,13 @@ suite('Virtual Environment Manager', () => { terminalActivation.reset(); terminalActivation - .setup(t => t.isShellSupported(TypeMoq.It.isAny())) + .setup((t) => t.isShellSupported(TypeMoq.It.isAny())) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); terminalActivation - .setup(t => t.getActivationCommandsForInterpreter!(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny())) + .setup((t) => + t.getActivationCommandsForInterpreter!(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny()) + ) .returns(() => Promise.resolve([])) .verifiable(TypeMoq.Times.never()); diff --git a/src/test/interpreters/virtualEnvs/virtualEnvPrompt.unit.test.ts b/src/test/interpreters/virtualEnvs/virtualEnvPrompt.unit.test.ts index 1c3f3af6fa39..d3ed199313cc 100644 --- a/src/test/interpreters/virtualEnvs/virtualEnvPrompt.unit.test.ts +++ b/src/test/interpreters/virtualEnvs/virtualEnvPrompt.unit.test.ts @@ -79,7 +79,7 @@ suite('Virtual Environment Prompt', () => { when(persistentStateFactory.createWorkspacePersistentState(anything(), true)).thenReturn( notificationPromptEnabled.object ); - notificationPromptEnabled.setup(n => n.value).returns(() => true); + notificationPromptEnabled.setup((n) => n.value).returns(() => true); when(appShell.showInformationMessage(anything(), ...prompts)).thenResolve(); await environmentPrompt.handleNewEnvironment(resource); @@ -102,7 +102,7 @@ suite('Virtual Environment Prompt', () => { when(persistentStateFactory.createWorkspacePersistentState(anything(), true)).thenReturn( notificationPromptEnabled.object ); - notificationPromptEnabled.setup(n => n.value).returns(() => true); + notificationPromptEnabled.setup((n) => n.value).returns(() => true); when(appShell.showInformationMessage(anything(), ...prompts)).thenResolve(prompts[0] as any); when( pythonPathUpdaterService.updatePythonPath( @@ -139,7 +139,7 @@ suite('Virtual Environment Prompt', () => { when(persistentStateFactory.createWorkspacePersistentState(anything(), true)).thenReturn( notificationPromptEnabled.object ); - notificationPromptEnabled.setup(n => n.value).returns(() => true); + notificationPromptEnabled.setup((n) => n.value).returns(() => true); when(appShell.showInformationMessage(anything(), ...prompts)).thenResolve(prompts[1] as any); when( pythonPathUpdaterService.updatePythonPath( @@ -150,7 +150,7 @@ suite('Virtual Environment Prompt', () => { ) ).thenResolve(); notificationPromptEnabled - .setup(n => n.updateValue(false)) + .setup((n) => n.updateValue(false)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); @@ -181,10 +181,10 @@ suite('Virtual Environment Prompt', () => { when(persistentStateFactory.createWorkspacePersistentState(anything(), true)).thenReturn( notificationPromptEnabled.object ); - notificationPromptEnabled.setup(n => n.value).returns(() => true); + notificationPromptEnabled.setup((n) => n.value).returns(() => true); when(appShell.showInformationMessage(anything(), ...prompts)).thenResolve(prompts[2] as any); notificationPromptEnabled - .setup(n => n.updateValue(false)) + .setup((n) => n.updateValue(false)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); @@ -207,7 +207,7 @@ suite('Virtual Environment Prompt', () => { when(persistentStateFactory.createWorkspacePersistentState(anything(), true)).thenReturn( notificationPromptEnabled.object ); - notificationPromptEnabled.setup(n => n.value).returns(() => false); + notificationPromptEnabled.setup((n) => n.value).returns(() => false); when(appShell.showInformationMessage(anything(), ...prompts)).thenResolve(prompts[0] as any); await environmentPrompt.notifyUser(interpreter1 as any, resource); diff --git a/src/test/interpreters/windowsRegistryService.unit.test.ts b/src/test/interpreters/windowsRegistryService.unit.test.ts index 0a0597149d42..039e8d427215 100644 --- a/src/test/interpreters/windowsRegistryService.unit.test.ts +++ b/src/test/interpreters/windowsRegistryService.unit.test.ts @@ -29,34 +29,34 @@ suite('Interpreters from Windows Registry (unit)', () => { platformService = TypeMoq.Mock.ofType(); fs = TypeMoq.Mock.ofType(); windowsStoreInterpreter = TypeMoq.Mock.ofType(); - windowsStoreInterpreter.setup(w => w.isHiddenInterpreter(TypeMoq.It.isAny())).returns(() => false); - windowsStoreInterpreter.setup(w => w.isWindowsStoreInterpreter(TypeMoq.It.isAny())).returns(() => false); + windowsStoreInterpreter.setup((w) => w.isHiddenInterpreter(TypeMoq.It.isAny())).returns(() => false); + windowsStoreInterpreter.setup((w) => w.isWindowsStoreInterpreter(TypeMoq.It.isAny())).returns(() => false); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPersistentStateFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(IPersistentStateFactory))) .returns(() => stateFactory.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterHelper))) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterHelper))) .returns(() => interpreterHelper.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPathUtils))).returns(() => pathUtils.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fs.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IPathUtils))).returns(() => pathUtils.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fs.object); pathUtils - .setup(p => p.basename(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.basename(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((p: string) => p.split(/[\\,\/]/).reverse()[0]); // So effectively these are functional tests... - fs.setup(f => f.fileExists(TypeMoq.It.isAny())).returns(filename => { + fs.setup((f) => f.fileExists(TypeMoq.It.isAny())).returns((filename) => { return fsextra.pathExists(filename); }); const state = new MockState(undefined); interpreterHelper - .setup(h => h.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((h) => h.getInterpreterInformation(TypeMoq.It.isAny())) // tslint:disable-next-line:no-empty no-any .returns(() => Promise.resolve({} as any)); stateFactory - .setup(s => s.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((s) => s.createGlobalPersistentState(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => state); }); function setup64Bit(is64Bit: boolean) { - platformService.setup(ps => ps.is64bit).returns(() => is64Bit); + platformService.setup((ps) => ps.is64bit).returns(() => is64Bit); return platformService.object; } test('Must return an empty list (x86)', async () => { @@ -144,7 +144,7 @@ suite('Interpreters from Windows Registry (unit)', () => { interpreterHelper.reset(); interpreterHelper - .setup(h => h.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((h) => h.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ architecture: Architecture.x86 })); const interpreters = await winRegistry.getInterpreters(); @@ -192,7 +192,7 @@ suite('Interpreters from Windows Registry (unit)', () => { interpreterHelper.reset(); interpreterHelper - .setup(h => h.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((h) => h.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ architecture: Architecture.x86 })); const interpreters = await winRegistry.getInterpreters(); @@ -270,7 +270,7 @@ suite('Interpreters from Windows Registry (unit)', () => { ); interpreterHelper.reset(); interpreterHelper - .setup(h => h.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((h) => h.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ architecture: Architecture.x86 })); const interpreters = await winRegistry.getInterpreters(); @@ -314,16 +314,16 @@ suite('Interpreters from Windows Registry (unit)', () => { ); interpreterHelper.reset(); interpreterHelper - .setup(h => h.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((h) => h.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ architecture: Architecture.x86 })); windowsStoreInterpreter.reset(); const expectedPythonPath = path.join(environmentsPath, 'path1', 'python.exe'); windowsStoreInterpreter - .setup(w => w.isHiddenInterpreter(TypeMoq.It.isValue(expectedPythonPath))) + .setup((w) => w.isHiddenInterpreter(TypeMoq.It.isValue(expectedPythonPath))) .returns(() => false) .verifiable(TypeMoq.Times.atLeastOnce()); windowsStoreInterpreter - .setup(w => w.isWindowsStoreInterpreter(TypeMoq.It.isValue(expectedPythonPath))) + .setup((w) => w.isWindowsStoreInterpreter(TypeMoq.It.isValue(expectedPythonPath))) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -365,12 +365,12 @@ suite('Interpreters from Windows Registry (unit)', () => { ); interpreterHelper.reset(); interpreterHelper - .setup(h => h.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((h) => h.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ architecture: Architecture.x86 })); windowsStoreInterpreter.reset(); const expectedPythonPath = path.join(environmentsPath, 'path1', 'python.exe'); windowsStoreInterpreter - .setup(w => w.isHiddenInterpreter(TypeMoq.It.isValue(expectedPythonPath))) + .setup((w) => w.isHiddenInterpreter(TypeMoq.It.isValue(expectedPythonPath))) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -414,7 +414,12 @@ suite('Interpreters from Windows Registry (unit)', () => { values: ['\\Software\\Python\\Company Three\\6.0.0'] }, { key: '\\Software\\Python', hive: RegistryHive.HKLM, arch: Architecture.x86, values: ['7.0.0'] }, - { key: '\\Software\\Python\\Company A', hive: RegistryHive.HKLM, arch: Architecture.x86, values: ['8.0.0'] } + { + key: '\\Software\\Python\\Company A', + hive: RegistryHive.HKLM, + arch: Architecture.x86, + values: ['8.0.0'] + } ]; const registryValues = [ { @@ -524,7 +529,7 @@ suite('Interpreters from Windows Registry (unit)', () => { ); interpreterHelper.reset(); interpreterHelper - .setup(h => h.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((h) => h.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ architecture: Architecture.x86 })); const interpreters = await winRegistry.getInterpreters(); @@ -737,7 +742,7 @@ suite('Interpreters from Windows Registry (unit)', () => { ); interpreterHelper.reset(); interpreterHelper - .setup(h => h.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((h) => h.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ architecture: Architecture.x86 })); const interpreters = await winRegistry.getInterpreters(); @@ -950,7 +955,7 @@ suite('Interpreters from Windows Registry (unit)', () => { ); interpreterHelper.reset(); interpreterHelper - .setup(h => h.getInterpreterInformation(TypeMoq.It.isAny())) + .setup((h) => h.getInterpreterInformation(TypeMoq.It.isAny())) .returns(() => Promise.resolve({ architecture: Architecture.x86 })); const interpreters = await winRegistry.getInterpreters(); diff --git a/src/test/language/braceCounter.unit.test.ts b/src/test/language/braceCounter.unit.test.ts index e45e2863e3a9..4762ae40cd2a 100644 --- a/src/test/language/braceCounter.unit.test.ts +++ b/src/test/language/braceCounter.unit.test.ts @@ -13,7 +13,7 @@ suite('Language.BraceCounter', () => { assert.equal(counter.count, 0); }); - ['(x)', '[x]', '{x}'].forEach(text => { + ['(x)', '[x]', '{x}'].forEach((text) => { test(`Brace counting: ${text}`, () => { const counter = new BraceCounter(); const tokens = new Tokenizer().tokenize(text); @@ -35,7 +35,7 @@ suite('Language.BraceCounter', () => { }); }); - ['(x))', '[x]]', '{x}}'].forEach(text => { + ['(x))', '[x]]', '{x}}'].forEach((text) => { test(`Brace counting with additional close brace: ${text}`, () => { const counter = new BraceCounter(); const tokens = new Tokenizer().tokenize(text); diff --git a/src/test/language/languageConfiguration.unit.test.ts b/src/test/language/languageConfiguration.unit.test.ts index 0542477db42c..07233e89cfbf 100644 --- a/src/test/language/languageConfiguration.unit.test.ts +++ b/src/test/language/languageConfiguration.unit.test.ts @@ -198,7 +198,7 @@ suite('Language Configuration', () => { '', ' ', ' ' - ].forEach(base => { + ].forEach((base) => { [ ['', '', '', ''], // leading @@ -215,7 +215,7 @@ suite('Language Configuration', () => { ['', '', '', ' '], ['', '', '', '# a comment'], ['', '', '', ' # ...'] - ].forEach(whitespace => { + ].forEach((whitespace) => { const [leading, postKeyword, preColon, trailing] = whitespace; const [_example, invalid, ignored] = resolveExample(base, leading, postKeyword, preColon, trailing); if (ignored) { diff --git a/src/test/language/tokenizer.unit.test.ts b/src/test/language/tokenizer.unit.test.ts index fae6b94530e1..e0ad13dc6afd 100644 --- a/src/test/language/tokenizer.unit.test.ts +++ b/src/test/language/tokenizer.unit.test.ts @@ -414,14 +414,14 @@ suite('Language.Tokenizer', () => { } }); - [-1, 10].forEach(start => { + [-1, 10].forEach((start) => { test(`Exceptions: out-of-range start = ${start}`, () => { assert.throws(() => { new Tokenizer().tokenize('', start, 0, TokenizerMode.Full); }, new Error('Invalid range start')); }); }); - [-1, 10].forEach(length => { + [-1, 10].forEach((length) => { test(`Exceptions: out-of-range length = ${length}`, () => { assert.throws(() => { new Tokenizer().tokenize('abc', 1, length, TokenizerMode.Full); @@ -439,7 +439,7 @@ suite('Language.Tokenizer', () => { [':', TokenType.Colon], [';', TokenType.Semicolon], ['.', TokenType.Operator] - ].forEach(pair => { + ].forEach((pair) => { const text: string = pair[0] as string; const expected = pair[1]; test(`Character tokens: ${text}`, () => { @@ -462,7 +462,7 @@ suite('Language.Tokenizer', () => { ['-0B1', TokenType.Number], ['-0o1', TokenType.Number], ['-0O1', TokenType.Number] - ].forEach(pair => { + ].forEach((pair) => { const text: string = pair[0] as string; const expected = pair[1]; test(`Possible numbers: ${text}`, () => { @@ -478,7 +478,7 @@ suite('Language.Tokenizer', () => { [':+1', TokenType.Number], [';+1', TokenType.Number], ['=+1', TokenType.Number] - ].forEach(pair => { + ].forEach((pair) => { const text: string = pair[0] as string; const expected = pair[1]; test(`Numbers after braces or operators: ${text}`, () => { diff --git a/src/test/languageServers/jedi/autocomplete/base.test.ts b/src/test/languageServers/jedi/autocomplete/base.test.ts index 76c197439417..5c748a415aee 100644 --- a/src/test/languageServers/jedi/autocomplete/base.test.ts +++ b/src/test/languageServers/jedi/autocomplete/base.test.ts @@ -23,14 +23,14 @@ const fileEncodingUsed = path.join(autoCompPath, 'five.py'); const fileSuppress = path.join(autoCompPath, 'suppress.py'); // tslint:disable-next-line:max-func-body-length -suite('Autocomplete Base Tests', function() { +suite('Autocomplete Base Tests', function () { // Attempt to fix #1301 // tslint:disable-next-line:no-invalid-this this.timeout(60000); let ioc: UnitTestIocContainer; let isPy38: boolean; - suiteSetup(async function() { + suiteSetup(async function () { // Attempt to fix #1301 // tslint:disable-next-line:no-invalid-this this.timeout(60000); @@ -51,11 +51,11 @@ suite('Autocomplete Base Tests', function() { ioc.registerProcessTypes(); } - test('For "sys."', done => { + test('For "sys."', (done) => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileOne) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) @@ -68,9 +68,9 @@ suite('Autocomplete Base Tests', function() { position ); }) - .then(list => { + .then((list) => { assert.equal( - list!.items.filter(item => item.label === 'api_version').length, + list!.items.filter((item) => item.label === 'api_version').length, 1, 'api_version not found' ); @@ -116,7 +116,7 @@ suite('Autocomplete Base Tests', function() { }); // https://github.com/DonJayamanne/pythonVSCode/issues/265 - test('For "lambda"', async function() { + test('For "lambda"', async function () { if (await isPythonVersion('2')) { // tslint:disable-next-line:no-invalid-this return this.skip(); @@ -129,9 +129,9 @@ suite('Autocomplete Base Tests', function() { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'append').length, 0, 'append not found'); - assert.notEqual(list!.items.filter(item => item.label === 'clear').length, 0, 'clear not found'); - assert.notEqual(list!.items.filter(item => item.label === 'count').length, 0, 'cound not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'append').length, 0, 'append not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'clear').length, 0, 'clear not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'count').length, 0, 'cound not found'); }); // https://github.com/DonJayamanne/pythonVSCode/issues/630 @@ -144,9 +144,9 @@ suite('Autocomplete Base Tests', function() { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'ABCMeta').length, 0, 'ABCMeta not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'ABCMeta').length, 0, 'ABCMeta not found'); assert.notEqual( - list!.items.filter(item => item.label === 'abstractmethod').length, + list!.items.filter((item) => item.label === 'abstractmethod').length, 0, 'abstractmethod not found' ); @@ -157,9 +157,9 @@ suite('Autocomplete Base Tests', function() { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'ABCMeta').length, 0, 'ABCMeta not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'ABCMeta').length, 0, 'ABCMeta not found'); assert.notEqual( - list!.items.filter(item => item.label === 'abstractmethod').length, + list!.items.filter((item) => item.label === 'abstractmethod').length, 0, 'abstractmethod not found' ); @@ -170,9 +170,9 @@ suite('Autocomplete Base Tests', function() { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'ABCMeta').length, 0, 'ABCMeta not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'ABCMeta').length, 0, 'ABCMeta not found'); assert.notEqual( - list!.items.filter(item => item.label === 'abstractmethod').length, + list!.items.filter((item) => item.label === 'abstractmethod').length, 0, 'abstractmethod not found' ); @@ -191,21 +191,21 @@ suite('Autocomplete Base Tests', function() { position ); - const items = list!.items.filter(item => item.label === 'sleep'); + const items = list!.items.filter((item) => item.label === 'sleep'); assert.notEqual(items.length, 0, 'sleep not found'); checkDocumentation(items[0], 'Delay execution for a given number of seconds. The argument may be'); }); - test('For custom class', done => { + test('For custom class', (done) => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileOne) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(30, 4); return vscode.commands.executeCommand( @@ -214,22 +214,22 @@ suite('Autocomplete Base Tests', function() { position ); }) - .then(list => { - assert.notEqual(list!.items.filter(item => item.label === 'method1').length, 0, 'method1 not found'); - assert.notEqual(list!.items.filter(item => item.label === 'method2').length, 0, 'method2 not found'); + .then((list) => { + assert.notEqual(list!.items.filter((item) => item.label === 'method1').length, 0, 'method1 not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'method2').length, 0, 'method2 not found'); }) .then(done, done); }); - test('With Unicode Characters', done => { + test('With Unicode Characters', (done) => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileEncoding) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(25, 4); return vscode.commands.executeCommand( @@ -238,8 +238,8 @@ suite('Autocomplete Base Tests', function() { position ); }) - .then(list => { - const items = list!.items.filter(item => item.label === 'bar'); + .then((list) => { + const items = list!.items.filter((item) => item.label === 'bar'); assert.equal(items.length, 1, 'bar not found'); const expected1 = '说明 - keep this line, it works'; @@ -251,7 +251,7 @@ suite('Autocomplete Base Tests', function() { .then(done, done); }); - test('Across files With Unicode Characters', function(done) { + test('Across files With Unicode Characters', function (done) { // tslint:disable-next-line:no-suspicious-comment // TODO (GH-10399) Fix this test. if (isOs(OSType.Windows) && isPy38) { @@ -261,11 +261,11 @@ suite('Autocomplete Base Tests', function() { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileEncodingUsed) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(1, 5); return vscode.commands.executeCommand( @@ -274,12 +274,12 @@ suite('Autocomplete Base Tests', function() { position ); }) - .then(list => { - let items = list!.items.filter(item => item.label === 'Foo'); + .then((list) => { + let items = list!.items.filter((item) => item.label === 'Foo'); assert.equal(items.length, 1, 'Foo not found'); checkDocumentation(items[0], '说明'); - items = list!.items.filter(item => item.label === 'showMessage'); + items = list!.items.filter((item) => item.label === 'showMessage'); assert.equal(items.length, 1, 'showMessage not found'); const expected1 = @@ -316,7 +316,7 @@ suite('Autocomplete Base Tests', function() { textDocument.uri, positions[i] ); - const result = list!.items.filter(item => item.label === 'abs').length; + const result = list!.items.filter((item) => item.label === 'abs').length; assert.equal( result > 0, expected[i], diff --git a/src/test/languageServers/jedi/autocomplete/pep484.test.ts b/src/test/languageServers/jedi/autocomplete/pep484.test.ts index 8a032dee3434..143bda870d6c 100644 --- a/src/test/languageServers/jedi/autocomplete/pep484.test.ts +++ b/src/test/languageServers/jedi/autocomplete/pep484.test.ts @@ -17,7 +17,7 @@ const filePep484 = path.join(autoCompPath, 'pep484.py'); suite('Autocomplete PEP 484', () => { let isPython2: boolean; let ioc: UnitTestIocContainer; - suiteSetup(async function() { + suiteSetup(async function () { await initialize(); initializeDI(); isPython2 = (await ioc.getPythonMajorVersion(rootWorkspaceUri!)) === 2; @@ -50,9 +50,9 @@ suite('Autocomplete PEP 484', () => { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'capitalize').length, 0, 'capitalize not found'); - assert.notEqual(list!.items.filter(item => item.label === 'upper').length, 0, 'upper not found'); - assert.notEqual(list!.items.filter(item => item.label === 'lower').length, 0, 'lower not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'capitalize').length, 0, 'capitalize not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'upper').length, 0, 'upper not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'lower').length, 0, 'lower not found'); }); test('return value', async () => { @@ -65,7 +65,7 @@ suite('Autocomplete PEP 484', () => { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'bit_length').length, 0, 'bit_length not found'); - assert.notEqual(list!.items.filter(item => item.label === 'from_bytes').length, 0, 'from_bytes not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'bit_length').length, 0, 'bit_length not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'from_bytes').length, 0, 'from_bytes not found'); }); }); diff --git a/src/test/languageServers/jedi/autocomplete/pep526.test.ts b/src/test/languageServers/jedi/autocomplete/pep526.test.ts index 942d821ed473..18ac683a2a98 100644 --- a/src/test/languageServers/jedi/autocomplete/pep526.test.ts +++ b/src/test/languageServers/jedi/autocomplete/pep526.test.ts @@ -17,7 +17,7 @@ const filePep526 = path.join(autoCompPath, 'pep526.py'); // tslint:disable-next-line:max-func-body-length suite('Autocomplete PEP 526', () => { let ioc: UnitTestIocContainer; - suiteSetup(async function() { + suiteSetup(async function () { // Pep526 only valid for 3.6+ (#2545) if (await isPythonVersion('2', '3.4', '3.5')) { // tslint:disable-next-line:no-invalid-this @@ -49,9 +49,9 @@ suite('Autocomplete PEP 526', () => { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'capitalize').length, 0, 'capitalize not found'); - assert.notEqual(list!.items.filter(item => item.label === 'upper').length, 0, 'upper not found'); - assert.notEqual(list!.items.filter(item => item.label === 'lower').length, 0, 'lower not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'capitalize').length, 0, 'capitalize not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'upper').length, 0, 'upper not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'lower').length, 0, 'lower not found'); }); test('variable (abc: str = "")', async () => { @@ -64,9 +64,9 @@ suite('Autocomplete PEP 526', () => { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'capitalize').length, 0, 'capitalize not found'); - assert.notEqual(list!.items.filter(item => item.label === 'upper').length, 0, 'upper not found'); - assert.notEqual(list!.items.filter(item => item.label === 'lower').length, 0, 'lower not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'capitalize').length, 0, 'capitalize not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'upper').length, 0, 'upper not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'lower').length, 0, 'lower not found'); }); test('variable (abc = UNKNOWN # type: str)', async () => { @@ -79,9 +79,9 @@ suite('Autocomplete PEP 526', () => { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'capitalize').length, 0, 'capitalize not found'); - assert.notEqual(list!.items.filter(item => item.label === 'upper').length, 0, 'upper not found'); - assert.notEqual(list!.items.filter(item => item.label === 'lower').length, 0, 'lower not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'capitalize').length, 0, 'capitalize not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'upper').length, 0, 'upper not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'lower').length, 0, 'lower not found'); }); test('class methods', async () => { @@ -94,7 +94,7 @@ suite('Autocomplete PEP 526', () => { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'a').length, 0, 'method a not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'a').length, 0, 'method a not found'); position = new vscode.Position(21, 4); list = await vscode.commands.executeCommand( @@ -102,7 +102,7 @@ suite('Autocomplete PEP 526', () => { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'b').length, 0, 'method b not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'b').length, 0, 'method b not found'); }); test('class method types', async () => { @@ -115,6 +115,6 @@ suite('Autocomplete PEP 526', () => { textDocument.uri, position ); - assert.notEqual(list!.items.filter(item => item.label === 'bit_length').length, 0, 'bit_length not found'); + assert.notEqual(list!.items.filter((item) => item.label === 'bit_length').length, 0, 'bit_length not found'); }); }); diff --git a/src/test/languageServers/jedi/completionSource.unit.test.ts b/src/test/languageServers/jedi/completionSource.unit.test.ts index 72daa9e023ae..48b1610ba944 100644 --- a/src/test/languageServers/jedi/completionSource.unit.test.ts +++ b/src/test/languageServers/jedi/completionSource.unit.test.ts @@ -28,12 +28,12 @@ suite('Completion Provider', () => { autoCompleteSettings = TypeMoq.Mock.ofType(); autoCompleteSettings = TypeMoq.Mock.ofType(); - jediFactory.setup(j => j.getJediProxyHandler(TypeMoq.It.isAny())).returns(() => jediHandler.object); + jediFactory.setup((j) => j.getJediProxyHandler(TypeMoq.It.isAny())).returns(() => jediHandler.object); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) + .setup((s) => s.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) .returns(() => configService.object); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); - pythonSettings.setup(p => p.autoComplete).returns(() => autoCompleteSettings.object); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + pythonSettings.setup((p) => p.autoComplete).returns(() => autoCompleteSettings.object); itemInfoSource = TypeMoq.Mock.ofType(); completionSource = new CompletionSource(jediFactory.object, serviceContainer.object, itemInfoSource.object); }); @@ -57,24 +57,24 @@ suite('Completion Provider', () => { } ]; - autoCompleteSettings.setup(a => a.addBrackets).returns(() => addBrackets); - doc.setup(d => d.fileName).returns(() => ''); - doc.setup(d => d.getText(TypeMoq.It.isAny())).returns(() => source); - doc.setup(d => d.lineAt(TypeMoq.It.isAny())).returns(() => lineText.object); - doc.setup(d => d.offsetAt(TypeMoq.It.isAny())).returns(() => 0); - lineText.setup(l => l.text).returns(() => source); - completionResult.setup(c => c.requestId).returns(() => 1); - completionResult.setup(c => c.items).returns(() => autoCompleteItems); + autoCompleteSettings.setup((a) => a.addBrackets).returns(() => addBrackets); + doc.setup((d) => d.fileName).returns(() => ''); + doc.setup((d) => d.getText(TypeMoq.It.isAny())).returns(() => source); + doc.setup((d) => d.lineAt(TypeMoq.It.isAny())).returns(() => lineText.object); + doc.setup((d) => d.offsetAt(TypeMoq.It.isAny())).returns(() => 0); + lineText.setup((l) => l.text).returns(() => source); + completionResult.setup((c) => c.requestId).returns(() => 1); + completionResult.setup((c) => c.items).returns(() => autoCompleteItems); completionResult.setup((c: any) => c.then).returns(() => undefined); jediHandler - .setup(j => j.sendCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((j) => j.sendCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => { return Promise.resolve(completionResult.object); }); const expectedSource = `${source}${autoCompleteItems[0].text}`; itemInfoSource - .setup(i => + .setup((i) => i.getItemInfoFromText( TypeMoq.It.isAny(), TypeMoq.It.isAny(), diff --git a/src/test/languageServers/jedi/definitions/hover.jedi.test.ts b/src/test/languageServers/jedi/definitions/hover.jedi.test.ts index e18eac61d02d..2ad50885e89f 100644 --- a/src/test/languageServers/jedi/definitions/hover.jedi.test.ts +++ b/src/test/languageServers/jedi/definitions/hover.jedi.test.ts @@ -32,15 +32,15 @@ suite('Hover Definition (Jedi)', () => { suiteTeardown(closeActiveWindows); teardown(closeActiveWindows); - test('Method', done => { + test('Method', (done) => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileOne) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(30, 5); return vscode.commands.executeCommand( @@ -49,7 +49,7 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(result => { + .then((result) => { const def = result!; assert.equal(def.length, 1, 'Definition length is incorrect'); assert.equal( @@ -74,7 +74,7 @@ suite('Hover Definition (Jedi)', () => { .then(done, done); }); - test('Across files', function(done) { + test('Across files', function (done) { // tslint:disable-next-line:no-suspicious-comment // TODO (GH-10399) Fix this test. if (isOs(OSType.Windows) && isPy38) { @@ -84,11 +84,11 @@ suite('Hover Definition (Jedi)', () => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileThree) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(1, 12); return vscode.commands.executeCommand( @@ -97,7 +97,7 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(result => { + .then((result) => { const def = result!; assert.equal(def.length, 1, 'Definition length is incorrect'); assert.equal( @@ -120,15 +120,15 @@ suite('Hover Definition (Jedi)', () => { .then(done, done); }); - test('With Unicode Characters', done => { + test('With Unicode Characters', (done) => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileEncoding) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(25, 6); return vscode.commands.executeCommand( @@ -137,7 +137,7 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(result => { + .then((result) => { const def = result!; assert.equal(def.length, 1, 'Definition length is incorrect'); assert.equal( @@ -170,7 +170,7 @@ suite('Hover Definition (Jedi)', () => { .then(done, done); }); - test('Across files with Unicode Characters', function(done) { + test('Across files with Unicode Characters', function (done) { // tslint:disable-next-line:no-suspicious-comment // TODO (GH-10399) Fix this test. if (isOs(OSType.Windows) && isPy38) { @@ -180,11 +180,11 @@ suite('Hover Definition (Jedi)', () => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileEncodingUsed) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(1, 11); return vscode.commands.executeCommand( @@ -193,7 +193,7 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(result => { + .then((result) => { const def = result!; assert.equal(def.length, 1, 'Definition length is incorrect'); assert.equal( @@ -224,15 +224,15 @@ suite('Hover Definition (Jedi)', () => { .then(done, done); }); - test('Nothing for keywords (class)', done => { + test('Nothing for keywords (class)', (done) => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileOne) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(5, 1); return vscode.commands.executeCommand( @@ -241,21 +241,21 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(def => { + .then((def) => { assert.equal(def!.length, 0, 'Definition length is incorrect'); }) .then(done, done); }); - test('Nothing for keywords (for)', done => { + test('Nothing for keywords (for)', (done) => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileHover) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(3, 1); return vscode.commands.executeCommand( @@ -264,13 +264,13 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(def => { + .then((def) => { assert.equal(def!.length, 0, 'Definition length is incorrect'); }) .then(done, done); }); - test('Highlighting Class', function(done) { + test('Highlighting Class', function (done) { // tslint:disable-next-line:no-suspicious-comment // TODO (GH-10399) Fix this test. if (isOs(OSType.Windows) && isPy38) { @@ -280,11 +280,11 @@ suite('Hover Definition (Jedi)', () => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileHover) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(11, 15); return vscode.commands.executeCommand( @@ -293,7 +293,7 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(result => { + .then((result) => { const def = result!; assert.equal(def.length, 1, 'Definition length is incorrect'); assert.equal( @@ -339,7 +339,7 @@ suite('Hover Definition (Jedi)', () => { .then(done, done); }); - test('Highlight Method', function(done) { + test('Highlight Method', function (done) { // tslint:disable-next-line:no-suspicious-comment // TODO (GH-10399) Fix this test. if (isOs(OSType.Windows) && isPy38) { @@ -349,11 +349,11 @@ suite('Hover Definition (Jedi)', () => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileHover) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(12, 10); return vscode.commands.executeCommand( @@ -362,7 +362,7 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(result => { + .then((result) => { const def = result!; assert.equal(def.length, 1, 'Definition length is incorrect'); assert.equal( @@ -391,15 +391,15 @@ suite('Hover Definition (Jedi)', () => { .then(done, done); }); - test('Highlight Function', done => { + test('Highlight Function', (done) => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileHover) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(8, 14); return vscode.commands.executeCommand( @@ -408,7 +408,7 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(result => { + .then((result) => { const def = result!; assert.equal(def.length, 1, 'Definition length is incorrect'); assert.equal( @@ -437,7 +437,7 @@ suite('Hover Definition (Jedi)', () => { .then(done, done); }); - test('Highlight Multiline Method Signature', function(done) { + test('Highlight Multiline Method Signature', function (done) { // tslint:disable-next-line:no-suspicious-comment // TODO (GH-10399) Fix this test. if (isOs(OSType.Windows) && isPy38) { @@ -447,11 +447,11 @@ suite('Hover Definition (Jedi)', () => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileHover) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(14, 14); return vscode.commands.executeCommand( @@ -460,7 +460,7 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(result => { + .then((result) => { const def = result!; assert.equal(def.length, 1, 'Definition length is incorrect'); assert.equal( @@ -493,15 +493,15 @@ suite('Hover Definition (Jedi)', () => { .then(done, done); }); - test('Variable', done => { + test('Variable', (done) => { let textDocument: vscode.TextDocument; vscode.workspace .openTextDocument(fileHover) - .then(document => { + .then((document) => { textDocument = document; return vscode.window.showTextDocument(textDocument); }) - .then(_editor => { + .then((_editor) => { assert(vscode.window.activeTextEditor, 'No active editor'); const position = new vscode.Position(6, 2); return vscode.commands.executeCommand( @@ -510,7 +510,7 @@ suite('Hover Definition (Jedi)', () => { position ); }) - .then(result => { + .then((result) => { const def = result!; assert.equal(def.length, 1, 'Definition length is incorrect'); assert.equal(def[0].contents.length, 1, 'Only expected one result'); diff --git a/src/test/languageServers/jedi/definitions/parallel.jedi.test.ts b/src/test/languageServers/jedi/definitions/parallel.jedi.test.ts index a1f3897ce10e..e7b612ad6fb7 100644 --- a/src/test/languageServers/jedi/definitions/parallel.jedi.test.ts +++ b/src/test/languageServers/jedi/definitions/parallel.jedi.test.ts @@ -41,7 +41,7 @@ suite('Code, Hover Definition and Intellisense (Jedi)', () => { position ); - assert.equal(list!.items.filter(item => item.label === 'api_version').length, 1, 'api_version not found'); + assert.equal(list!.items.filter((item) => item.label === 'api_version').length, 1, 'api_version not found'); assert.equal(codeDef!.length, 1, 'Definition length is incorrect'); const expectedPath = IS_WINDOWS ? fileOne.toUpperCase() : fileOne; diff --git a/src/test/languageServers/jedi/pythonSignatureProvider.unit.test.ts b/src/test/languageServers/jedi/pythonSignatureProvider.unit.test.ts index 5eafa2ffde62..3d4b87ad9dda 100644 --- a/src/test/languageServers/jedi/pythonSignatureProvider.unit.test.ts +++ b/src/test/languageServers/jedi/pythonSignatureProvider.unit.test.ts @@ -23,7 +23,7 @@ suite('Signature Provider unit tests', () => { setup(() => { const jediFactory = TypeMoq.Mock.ofType(JediFactory); jediHandler = TypeMoq.Mock.ofType>(); - jediFactory.setup(j => j.getJediProxyHandler(TypeMoq.It.isAny())).returns(() => jediHandler.object); + jediFactory.setup((j) => j.getJediProxyHandler(TypeMoq.It.isAny())).returns(() => jediHandler.object); pySignatureProvider = new PythonSignatureProvider(jediFactory.object); argResultItems = { definitions: [ @@ -52,21 +52,21 @@ suite('Signature Provider unit tests', () => { const lineText = TypeMoq.Mock.ofType(); const argsResult = TypeMoq.Mock.ofType(); const cancelToken = TypeMoq.Mock.ofType(); - cancelToken.setup(ct => ct.isCancellationRequested).returns(() => false); + cancelToken.setup((ct) => ct.isCancellationRequested).returns(() => false); - doc.setup(d => d.fileName).returns(() => ''); - doc.setup(d => d.getText(TypeMoq.It.isAny())).returns(() => source); - doc.setup(d => d.lineAt(TypeMoq.It.isAny())).returns(() => lineText.object); - doc.setup(d => d.offsetAt(TypeMoq.It.isAny())).returns(() => pos - 1); // pos is 1-based + doc.setup((d) => d.fileName).returns(() => ''); + doc.setup((d) => d.getText(TypeMoq.It.isAny())).returns(() => source); + doc.setup((d) => d.lineAt(TypeMoq.It.isAny())).returns(() => lineText.object); + doc.setup((d) => d.offsetAt(TypeMoq.It.isAny())).returns(() => pos - 1); // pos is 1-based const docUri = TypeMoq.Mock.ofType(); - docUri.setup(u => u.scheme).returns(() => 'http'); - doc.setup(d => d.uri).returns(() => docUri.object); - lineText.setup(l => l.text).returns(() => source); - argsResult.setup(c => c.requestId).returns(() => 1); + docUri.setup((u) => u.scheme).returns(() => 'http'); + doc.setup((d) => d.uri).returns(() => docUri.object); + lineText.setup((l) => l.text).returns(() => source); + argsResult.setup((c) => c.requestId).returns(() => 1); // tslint:disable-next-line:no-any - argsResult.setup(c => c.definitions).returns(() => (argResultItems as any)[0].definitions); + argsResult.setup((c) => c.definitions).returns(() => (argResultItems as any)[0].definitions); jediHandler - .setup(j => j.sendCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((j) => j.sendCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => { return Promise.resolve(argResultItems); }); @@ -76,14 +76,14 @@ suite('Signature Provider unit tests', () => { function testIsInsideStringOrComment(sourceLine: string, sourcePos: number): boolean { const textLine: TypeMoq.IMock = TypeMoq.Mock.ofType(); - textLine.setup(t => t.text).returns(() => sourceLine); + textLine.setup((t) => t.text).returns(() => sourceLine); const doc: TypeMoq.IMock = TypeMoq.Mock.ofType(); const pos: Position = new Position(1, sourcePos); - doc.setup(d => d.fileName).returns(() => ''); - doc.setup(d => d.getText(TypeMoq.It.isAny())).returns(() => sourceLine); - doc.setup(d => d.lineAt(TypeMoq.It.isAny())).returns(() => textLine.object); - doc.setup(d => d.offsetAt(TypeMoq.It.isAny())).returns(() => sourcePos); + doc.setup((d) => d.fileName).returns(() => ''); + doc.setup((d) => d.getText(TypeMoq.It.isAny())).returns(() => sourceLine); + doc.setup((d) => d.lineAt(TypeMoq.It.isAny())).returns(() => textLine.object); + doc.setup((d) => d.offsetAt(TypeMoq.It.isAny())).returns(() => sourcePos); return isPositionInsideStringOrComment(doc.object, pos); } diff --git a/src/test/languageServers/jedi/signature/signature.jedi.test.ts b/src/test/languageServers/jedi/signature/signature.jedi.test.ts index 141291520a2d..c6ceb65e1867 100644 --- a/src/test/languageServers/jedi/signature/signature.jedi.test.ts +++ b/src/test/languageServers/jedi/signature/signature.jedi.test.ts @@ -104,7 +104,7 @@ suite('Signatures (Jedi)', () => { } }); - test('For ellipsis', async function() { + test('For ellipsis', async function () { if (isPython2) { // tslint:disable-next-line:no-invalid-this return this.skip(); diff --git a/src/test/languageServers/jedi/symbolProvider.unit.test.ts b/src/test/languageServers/jedi/symbolProvider.unit.test.ts index fa44ca86c154..556d14e3350b 100644 --- a/src/test/languageServers/jedi/symbolProvider.unit.test.ts +++ b/src/test/languageServers/jedi/symbolProvider.unit.test.ts @@ -45,9 +45,9 @@ suite('Jedi Symbol Provider', () => { fileSystem = TypeMoq.Mock.ofType(); doc = TypeMoq.Mock.ofType(); - jediFactory.setup(j => j.getJediProxyHandler(TypeMoq.It.isAny())).returns(() => jediHandler.object); + jediFactory.setup((j) => j.getJediProxyHandler(TypeMoq.It.isAny())).returns(() => jediHandler.object); - serviceContainer.setup(c => c.get(IFileSystem)).returns(() => fileSystem.object); + serviceContainer.setup((c) => c.get(IFileSystem)).returns(() => fileSystem.object); }); async function testDocumentation( @@ -57,7 +57,7 @@ suite('Jedi Symbol Provider', () => { token?: CancellationToken, isUntitled = false ) { - fileSystem.setup(fs => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => true); + fileSystem.setup((fs) => fs.arePathsSame(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => true); token = token ? token : new CancellationTokenSource().token; const symbolResult = TypeMoq.Mock.ofType(); @@ -74,15 +74,15 @@ suite('Jedi Symbol Provider', () => { ]; uri = Uri.file(fileName); - doc.setup(d => d.uri).returns(() => uri); - doc.setup(d => d.fileName).returns(() => fileName); - doc.setup(d => d.isUntitled).returns(() => isUntitled); - doc.setup(d => d.getText(TypeMoq.It.isAny())).returns(() => ''); - symbolResult.setup(c => c.requestId).returns(() => requestId); - symbolResult.setup(c => c.definitions).returns(() => definitions); + doc.setup((d) => d.uri).returns(() => uri); + doc.setup((d) => d.fileName).returns(() => fileName); + doc.setup((d) => d.isUntitled).returns(() => isUntitled); + doc.setup((d) => d.getText(TypeMoq.It.isAny())).returns(() => ''); + symbolResult.setup((c) => c.requestId).returns(() => requestId); + symbolResult.setup((c) => c.definitions).returns(() => definitions); symbolResult.setup((c: any) => c.then).returns(() => undefined); jediHandler - .setup(j => j.sendCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((j) => j.sendCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(symbolResult.object)); const items = await provider.provideDocumentSymbols(doc.object, token); @@ -157,34 +157,34 @@ suite('Jedi Symbol Provider', () => { ]); }); test('Ensure IFileSystem.arePathsSame is used', async () => { - doc.setup(d => d.getText()) + doc.setup((d) => d.getText()) .returns(() => '') .verifiable(TypeMoq.Times.once()); - doc.setup(d => d.isDirty) + doc.setup((d) => d.isDirty) .returns(() => true) .verifiable(TypeMoq.Times.once()); - doc.setup(d => d.fileName).returns(() => __filename); + doc.setup((d) => d.fileName).returns(() => __filename); const symbols = TypeMoq.Mock.ofType(); symbols.setup((s: any) => s.then).returns(() => undefined); const definitions: IDefinition[] = []; for (let counter = 0; counter < 3; counter += 1) { const def = TypeMoq.Mock.ofType(); - def.setup(d => d.fileName).returns(() => counter.toString()); + def.setup((d) => d.fileName).returns(() => counter.toString()); definitions.push(def.object); fileSystem - .setup(fs => fs.arePathsSame(TypeMoq.It.isValue(counter.toString()), TypeMoq.It.isValue(__filename))) + .setup((fs) => fs.arePathsSame(TypeMoq.It.isValue(counter.toString()), TypeMoq.It.isValue(__filename))) .returns(() => false) .verifiable(TypeMoq.Times.exactly(1)); } symbols - .setup(s => s.definitions) + .setup((s) => s.definitions) .returns(() => definitions) .verifiable(TypeMoq.Times.atLeastOnce()); jediHandler - .setup(j => j.sendCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((j) => j.sendCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(symbols.object)) .verifiable(TypeMoq.Times.once()); @@ -203,7 +203,7 @@ suite('Language Server Symbol Provider', () => { const langClient = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); for (const [doc, symbols] of results) { langClient - .setup(l => + .setup((l) => l.sendRequest( TypeMoq.It.isValue('textDocument/documentSymbol'), TypeMoq.It.isValue(doc), @@ -384,16 +384,16 @@ suite('Language Server Symbol Provider', () => { function createDoc(uri?: Uri, filename?: string, isUntitled?: boolean, text?: string): TypeMoq.IMock { const doc = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); if (uri !== undefined) { - doc.setup(d => d.uri).returns(() => uri); + doc.setup((d) => d.uri).returns(() => uri); } if (filename !== undefined) { - doc.setup(d => d.fileName).returns(() => filename); + doc.setup((d) => d.fileName).returns(() => filename); } if (isUntitled !== undefined) { - doc.setup(d => d.isUntitled).returns(() => isUntitled); + doc.setup((d) => d.isUntitled).returns(() => isUntitled); } if (text !== undefined) { - doc.setup(d => d.getText(TypeMoq.It.isAny())).returns(() => text); + doc.setup((d) => d.getText(TypeMoq.It.isAny())).returns(() => text); } return doc; } diff --git a/src/test/linters/common.ts b/src/test/linters/common.ts index 5bfccedebb4a..e010c8aab750 100644 --- a/src/test/linters/common.ts +++ b/src/test/linters/common.ts @@ -28,7 +28,7 @@ import { ILinter, ILinterManager, ILintMessage, LinterId } from '../../client/li export function newMockDocument(filename: string): TypeMoq.IMock { const uri = Uri.file(filename); const doc = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - doc.setup(s => s.uri).returns(() => uri); + doc.setup((s) => s.uri).returns(() => uri); return doc; } @@ -208,25 +208,25 @@ export class BaseTestFixture { this.appShell = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); this.serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) .returns(() => filesystem); this.serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) .returns(() => this.workspaceService.object); this.serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInstaller), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IInstaller), TypeMoq.It.isAny())) .returns(() => this.installer.object); this.serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) .returns(() => platformService); this.serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPythonToolExecutionService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPythonToolExecutionService), TypeMoq.It.isAny())) .returns(() => pythonToolExecService); this.serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IPythonExecutionFactory), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IPythonExecutionFactory), TypeMoq.It.isAny())) .returns(() => pythonExecFactory); this.serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) .returns(() => this.appShell.object); this.initServices(); @@ -239,10 +239,10 @@ export class BaseTestFixture { this.lintingSettings = new LintingSettings(); this.serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) .returns(() => this.configService.object); - this.configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => this.pythonSettings.object); - this.pythonSettings.setup(s => s.linting).returns(() => this.lintingSettings); + this.configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => this.pythonSettings.object); + this.pythonSettings.setup((s) => s.linting).returns(() => this.lintingSettings); this.initConfig(ignoreConfigUpdates); // data @@ -250,7 +250,7 @@ export class BaseTestFixture { this.outputChannel = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); this.serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isAny())) .returns(() => this.outputChannel.object); this.initData(); @@ -263,7 +263,7 @@ export class BaseTestFixture { this.linterManager = new LinterManager(this.serviceContainer.object, this.workspaceService.object!); this.serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILinterManager), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ILinterManager), TypeMoq.It.isAny())) .returns(() => this.linterManager); } @@ -291,19 +291,21 @@ export class BaseTestFixture { private initServices(): void { const workspaceFolder = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - workspaceFolder.setup(f => f.uri).returns(() => Uri.file(this.workspaceDir)); + workspaceFolder.setup((f) => f.uri).returns(() => Uri.file(this.workspaceDir)); this.workspaceService - .setup(s => s.getWorkspaceFolder(TypeMoq.It.isAny())) + .setup((s) => s.getWorkspaceFolder(TypeMoq.It.isAny())) .returns(() => workspaceFolder.object); this.appShell - .setup(a => a.showErrorMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((a) => a.showErrorMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); } private initConfig(ignoreUpdates = false): void { this.configService - .setup(c => c.updateSetting(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((c) => + c.updateSetting(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) + ) .callback((setting, value) => { if (ignoreUpdates) { return; @@ -316,13 +318,13 @@ export class BaseTestFixture { }) .returns(() => Promise.resolve(undefined)); - this.pythonSettings.setup(s => s.jediEnabled).returns(() => true); + this.pythonSettings.setup((s) => s.jediEnabled).returns(() => true); } private initData(): void { this.outputChannel - .setup(o => o.appendLine(TypeMoq.It.isAny())) - .callback(line => { + .setup((o) => o.appendLine(TypeMoq.It.isAny())) + .callback((line) => { if (this.output === '') { this.output = line; } else { @@ -330,10 +332,10 @@ export class BaseTestFixture { } }); this.outputChannel - .setup(o => o.append(TypeMoq.It.isAny())) - .callback(data => { + .setup((o) => o.append(TypeMoq.It.isAny())) + .callback((data) => { this.output += data; }); - this.outputChannel.setup(o => o.show()); + this.outputChannel.setup((o) => o.show()); } } diff --git a/src/test/linters/lint.args.test.ts b/src/test/linters/lint.args.test.ts index 777cb283f3a0..0cac56804c11 100644 --- a/src/test/linters/lint.args.test.ts +++ b/src/test/linters/lint.args.test.ts @@ -42,11 +42,11 @@ import { initialize } from '../initialize'; import { MockAutoSelectionService } from '../mocks/autoSelector'; suite('Linting - Arguments', () => { - [undefined, path.join('users', 'dev_user')].forEach(workspaceUri => { + [undefined, path.join('users', 'dev_user')].forEach((workspaceUri) => { [ Uri.file(path.join('users', 'dev_user', 'development path to', 'one.py')), Uri.file(path.join('users', 'dev_user', 'development', 'one.py')) - ].forEach(fileUri => { + ].forEach((fileUri) => { suite( `File path ${fileUri.fsPath.indexOf(' ') > 0 ? 'with' : 'without'} spaces and ${ workspaceUri ? 'without' : 'with' @@ -72,10 +72,10 @@ suite('Linting - Arguments', () => { outputChannel = TypeMoq.Mock.ofType(); const fs = TypeMoq.Mock.ofType(); - fs.setup(x => x.fileExists(TypeMoq.It.isAny())).returns( + fs.setup((x) => x.fileExists(TypeMoq.It.isAny())).returns( () => new Promise((resolve, _reject) => resolve(true)) ); - fs.setup(x => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns( + fs.setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns( () => true ); serviceManager.addSingletonInstance(IFileSystem, fs.object); @@ -102,14 +102,14 @@ suite('Linting - Arguments', () => { serviceManager.addSingletonInstance(IDocumentManager, docManager.object); const lintSettings = TypeMoq.Mock.ofType(); - lintSettings.setup(x => x.enabled).returns(() => true); - lintSettings.setup(x => x.lintOnSave).returns(() => true); + lintSettings.setup((x) => x.enabled).returns(() => true); + lintSettings.setup((x) => x.lintOnSave).returns(() => true); settings = TypeMoq.Mock.ofType(); - settings.setup(x => x.linting).returns(() => lintSettings.object); + settings.setup((x) => x.linting).returns(() => lintSettings.object); configService = TypeMoq.Mock.ofType(); - configService.setup(x => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + configService.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); serviceManager.addSingletonInstance( IConfigurationService, configService.object @@ -120,7 +120,7 @@ suite('Linting - Arguments', () => { : undefined; workspaceService = TypeMoq.Mock.ofType(); workspaceService - .setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())) + .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())) .returns(() => workspaceFolder); serviceManager.addSingletonInstance( IWorkspaceService, @@ -139,7 +139,7 @@ suite('Linting - Arguments', () => { }); async function testLinter(linter: BaseLinter, expectedArgs: string[]) { - document.setup(d => d.uri).returns(() => fileUri); + document.setup((d) => d.uri).returns(() => fileUri); let invoked = false; (linter as any).run = (args: string[]) => { @@ -185,7 +185,7 @@ suite('Linting - Arguments', () => { }); test('Pylint', async () => { const linter = new Pylint(outputChannel.object, serviceContainer); - document.setup(d => d.uri).returns(() => fileUri); + document.setup((d) => d.uri).returns(() => fileUri); let invoked = false; (linter as any).run = (args: any[], _doc: any, _token: any) => { diff --git a/src/test/linters/lint.functional.test.ts b/src/test/linters/lint.functional.test.ts index 9d5980d27b80..8fc688b9ce43 100644 --- a/src/test/linters/lint.functional.test.ts +++ b/src/test/linters/lint.functional.test.ts @@ -623,12 +623,12 @@ class TestFixture extends BaseTestFixture { const configService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); const processLogger = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); processLogger - .setup(p => p.logProcess(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.logProcess(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => { return; }); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IProcessLogger), TypeMoq.It.isAny())) + .setup((s) => s.get(TypeMoq.It.isValue(IProcessLogger), TypeMoq.It.isAny())) .returns(() => processLogger.object); const platformService = new PlatformService(); @@ -646,7 +646,7 @@ class TestFixture extends BaseTestFixture { printLogs ); - this.pythonSettings.setup(s => s.pythonPath).returns(() => PYTHON_PATH); + this.pythonSettings.setup((s) => s.pythonPath).returns(() => PYTHON_PATH); } private static newPythonToolExecService(serviceContainer: IServiceContainer): IPythonToolExecutionService { @@ -664,14 +664,14 @@ class TestFixture extends BaseTestFixture { TypeMoq.MockBehavior.Strict ); envVarsService - .setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())) + .setup((e) => e.getEnvironmentVariables(TypeMoq.It.isAny())) .returns(() => Promise.resolve(process.env)); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider), TypeMoq.It.isAny())) .returns(() => envVarsService.object); const disposableRegistry: IDisposableRegistry = []; serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) .returns(() => disposableRegistry); const envActivationService = TypeMoq.Mock.ofType( @@ -681,25 +681,25 @@ class TestFixture extends BaseTestFixture { const decoder = new BufferDecoder(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IBufferDecoder), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IBufferDecoder), TypeMoq.It.isAny())) .returns(() => decoder); const interpreterService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - interpreterService.setup(i => i.hasInterpreters).returns(() => Promise.resolve(true)); + interpreterService.setup((i) => i.hasInterpreters).returns(() => Promise.resolve(true)); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) .returns(() => interpreterService.object); const condaService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); condaService - .setup(c => c.getCondaEnvironment(TypeMoq.It.isAnyString())) + .setup((c) => c.getCondaEnvironment(TypeMoq.It.isAnyString())) .returns(() => Promise.resolve(undefined)); - condaService.setup(c => c.getCondaVersion()).returns(() => Promise.resolve(undefined)); - condaService.setup(c => c.getCondaFile()).returns(() => Promise.resolve('conda')); + condaService.setup((c) => c.getCondaVersion()).returns(() => Promise.resolve(undefined)); + condaService.setup((c) => c.getCondaFile()).returns(() => Promise.resolve('conda')); const processLogger = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); processLogger - .setup(p => p.logProcess(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.logProcess(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => { return; }); @@ -723,13 +723,10 @@ class TestFixture extends BaseTestFixture { public makeDocument(filename: string): TextDocument { const doc = newMockDocument(filename); - doc.setup(d => d.lineAt(TypeMoq.It.isAny())).returns(lno => { - const lines = fs - .readFileSync(filename) - .toString() - .split(os.EOL); + doc.setup((d) => d.lineAt(TypeMoq.It.isAny())).returns((lno) => { + const lines = fs.readFileSync(filename).toString().split(os.EOL); const textline = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - textline.setup(t => t.text).returns(() => lines[lno]); + textline.setup((t) => t.text).returns(() => lines[lno]); return textline.object; }); return doc.object; @@ -772,8 +769,8 @@ suite('Linting Functional Tests', () => { } } for (const product of LINTERID_BY_PRODUCT.keys()) { - test(getProductName(product), async function() { - if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some(p => p === product)) { + test(getProductName(product), async function () { + if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some((p) => p === product)) { // tslint:disable-next-line:no-invalid-this return this.skip(); } @@ -785,8 +782,8 @@ suite('Linting Functional Tests', () => { } for (const product of LINTERID_BY_PRODUCT.keys()) { // tslint:disable-next-line:max-func-body-length - test(`${getProductName(product)} with config in root`, async function() { - if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some(p => p === product)) { + test(`${getProductName(product)} with config in root`, async function () { + if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some((p) => p === product)) { // tslint:disable-next-line:no-invalid-this return this.skip(); } diff --git a/src/test/linters/lint.manager.unit.test.ts b/src/test/linters/lint.manager.unit.test.ts index 04e2091d2e8a..fabe314e2b68 100644 --- a/src/test/linters/lint.manager.unit.test.ts +++ b/src/test/linters/lint.manager.unit.test.ts @@ -25,8 +25,8 @@ function getServiceContainerMockForLinterManagerTests(): TypeMoq.IMock(); const configMock = TypeMoq.Mock.ofType(); const pythonSettingsMock = TypeMoq.Mock.ofType(); - configMock.setup(cm => cm.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettingsMock.object); - serviceContainerMock.setup(c => c.get(IConfigurationService)).returns(() => configMock.object); + configMock.setup((cm) => cm.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettingsMock.object); + serviceContainerMock.setup((c) => c.get(IConfigurationService)).returns(() => configMock.object); return serviceContainerMock; } diff --git a/src/test/linters/lint.multilinter.test.ts b/src/test/linters/lint.multilinter.test.ts index a2ac74cd82b8..9d2336ab0761 100644 --- a/src/test/linters/lint.multilinter.test.ts +++ b/src/test/linters/lint.multilinter.test.ts @@ -78,7 +78,7 @@ suite('Linting - Multiple Linters Enabled Test', () => { await configService.updateSetting('linting.lintOnSave', false, rootWorkspaceUri, target); await configService.updateSetting('linting.pylintUseMinimalCheckers', false, workspaceUri); - linterManager.getAllLinterInfos().forEach(async x => { + linterManager.getAllLinterInfos().forEach(async (x) => { await configService.updateSetting(makeSettingKey(x.product), false, rootWorkspaceUri, target); }); } @@ -103,7 +103,7 @@ suite('Linting - Multiple Linters Enabled Test', () => { const messages = collection!.get(document.uri); assert.notEqual(messages!.length, 0, 'No diagnostic messages.'); - assert.notEqual(messages!.filter(x => x.source === 'pylint').length, 0, 'No pylint messages.'); - assert.notEqual(messages!.filter(x => x.source === 'flake8').length, 0, 'No flake8 messages.'); + assert.notEqual(messages!.filter((x) => x.source === 'pylint').length, 0, 'No pylint messages.'); + assert.notEqual(messages!.filter((x) => x.source === 'flake8').length, 0, 'No flake8 messages.'); }); }); diff --git a/src/test/linters/lint.multiroot.test.ts b/src/test/linters/lint.multiroot.test.ts index 912404729406..2a8630575d41 100644 --- a/src/test/linters/lint.multiroot.test.ts +++ b/src/test/linters/lint.multiroot.test.ts @@ -30,7 +30,7 @@ suite('Multiroot Linting', () => { const flake8Setting = 'linting.flake8Enabled'; let ioc: UnitTestIocContainer; - suiteSetup(function() { + suiteSetup(function () { if (!IS_MULTI_ROOT_TEST) { this.skip(); } @@ -138,7 +138,7 @@ suite('Multiroot Linting', () => { ]); await testLinterInWorkspaceFolder(product, 'workspace1', wks); await Promise.all( - [ConfigurationTarget.Global, ConfigurationTarget.Workspace].map(configTarget => + [ConfigurationTarget.Global, ConfigurationTarget.Workspace].map((configTarget) => config.updateSetting(setting, undefined, Uri.file(multirootPath), configTarget) ) ); diff --git a/src/test/linters/lint.provider.test.ts b/src/test/linters/lint.provider.test.ts index 6ee7e17438d6..ff4ae1c34953 100644 --- a/src/test/linters/lint.provider.test.ts +++ b/src/test/linters/lint.provider.test.ts @@ -59,10 +59,10 @@ suite('Linting - Provider', () => { serviceContainer = new ServiceContainer(cont); fs = TypeMoq.Mock.ofType(); - fs.setup(x => x.fileExists(TypeMoq.It.isAny())).returns( + fs.setup((x) => x.fileExists(TypeMoq.It.isAny())).returns( () => new Promise((resolve, _reject) => resolve(true)) ); - fs.setup(x => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => true); + fs.setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => true); serviceManager.addSingletonInstance(IFileSystem, fs.object); interpreterService = TypeMoq.Mock.ofType(); @@ -75,14 +75,14 @@ suite('Linting - Provider', () => { serviceManager.addSingletonInstance(IDocumentManager, docManager.object); const lintSettings = TypeMoq.Mock.ofType(); - lintSettings.setup(x => x.enabled).returns(() => true); - lintSettings.setup(x => x.lintOnSave).returns(() => true); + lintSettings.setup((x) => x.enabled).returns(() => true); + lintSettings.setup((x) => x.lintOnSave).returns(() => true); settings = TypeMoq.Mock.ofType(); - settings.setup(x => x.linting).returns(() => lintSettings.object); + settings.setup((x) => x.linting).returns(() => lintSettings.object); configService = TypeMoq.Mock.ofType(); - configService.setup(x => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + configService.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); serviceManager.addSingletonInstance(IConfigurationService, configService.object); appShell = TypeMoq.Mock.ofType(); @@ -110,62 +110,62 @@ suite('Linting - Provider', () => { }); test('Lint on open file', async () => { - docManager.setup(x => x.onDidOpenTextDocument).returns(() => emitter.event); - document.setup(x => x.uri).returns(() => vscode.Uri.file('test.py')); - document.setup(x => x.languageId).returns(() => 'python'); + docManager.setup((x) => x.onDidOpenTextDocument).returns(() => emitter.event); + document.setup((x) => x.uri).returns(() => vscode.Uri.file('test.py')); + document.setup((x) => x.languageId).returns(() => 'python'); const linterProvider = new LinterProvider(serviceContainer); await linterProvider.activate(); emitter.fire(document.object); - engine.verify(x => x.lintDocument(document.object, 'auto'), TypeMoq.Times.once()); + engine.verify((x) => x.lintDocument(document.object, 'auto'), TypeMoq.Times.once()); }); test('Lint on save file', async () => { - docManager.setup(x => x.onDidSaveTextDocument).returns(() => emitter.event); - document.setup(x => x.uri).returns(() => vscode.Uri.file('test.py')); - document.setup(x => x.languageId).returns(() => 'python'); + docManager.setup((x) => x.onDidSaveTextDocument).returns(() => emitter.event); + document.setup((x) => x.uri).returns(() => vscode.Uri.file('test.py')); + document.setup((x) => x.languageId).returns(() => 'python'); const linterProvider = new LinterProvider(serviceContainer); await linterProvider.activate(); emitter.fire(document.object); - engine.verify(x => x.lintDocument(document.object, 'save'), TypeMoq.Times.once()); + engine.verify((x) => x.lintDocument(document.object, 'save'), TypeMoq.Times.once()); }); test('No lint on open other files', async () => { - docManager.setup(x => x.onDidOpenTextDocument).returns(() => emitter.event); - document.setup(x => x.uri).returns(() => vscode.Uri.file('test.cs')); - document.setup(x => x.languageId).returns(() => 'csharp'); + docManager.setup((x) => x.onDidOpenTextDocument).returns(() => emitter.event); + document.setup((x) => x.uri).returns(() => vscode.Uri.file('test.cs')); + document.setup((x) => x.languageId).returns(() => 'csharp'); const linterProvider = new LinterProvider(serviceContainer); await linterProvider.activate(); emitter.fire(document.object); - engine.verify(x => x.lintDocument(document.object, 'save'), TypeMoq.Times.never()); + engine.verify((x) => x.lintDocument(document.object, 'save'), TypeMoq.Times.never()); }); test('No lint on save other files', async () => { - docManager.setup(x => x.onDidSaveTextDocument).returns(() => emitter.event); - document.setup(x => x.uri).returns(() => vscode.Uri.file('test.cs')); - document.setup(x => x.languageId).returns(() => 'csharp'); + docManager.setup((x) => x.onDidSaveTextDocument).returns(() => emitter.event); + document.setup((x) => x.uri).returns(() => vscode.Uri.file('test.cs')); + document.setup((x) => x.languageId).returns(() => 'csharp'); const linterProvider = new LinterProvider(serviceContainer); await linterProvider.activate(); emitter.fire(document.object); - engine.verify(x => x.lintDocument(document.object, 'save'), TypeMoq.Times.never()); + engine.verify((x) => x.lintDocument(document.object, 'save'), TypeMoq.Times.never()); }); test('Lint on change interpreters', async () => { const e = new vscode.EventEmitter(); - interpreterService.setup(x => x.onDidChangeInterpreter).returns(() => e.event); + interpreterService.setup((x) => x.onDidChangeInterpreter).returns(() => e.event); const linterProvider = new LinterProvider(serviceContainer); await linterProvider.activate(); e.fire(); - engine.verify(x => x.lintOpenPythonFiles(), TypeMoq.Times.once()); + engine.verify((x) => x.lintOpenPythonFiles(), TypeMoq.Times.once()); }); test('Lint on save pylintrc', async () => { - docManager.setup(x => x.onDidSaveTextDocument).returns(() => emitter.event); - document.setup(x => x.uri).returns(() => vscode.Uri.file('.pylintrc')); + docManager.setup((x) => x.onDidSaveTextDocument).returns(() => emitter.event); + document.setup((x) => x.uri).returns(() => vscode.Uri.file('.pylintrc')); await lm.setActiveLintersAsync([Product.pylint]); const linterProvider = new LinterProvider(serviceContainer); @@ -175,25 +175,25 @@ suite('Linting - Provider', () => { const deferred = createDeferred(); setTimeout(() => deferred.resolve(), 2000); await deferred.promise; - engine.verify(x => x.lintOpenPythonFiles(), TypeMoq.Times.once()); + engine.verify((x) => x.lintOpenPythonFiles(), TypeMoq.Times.once()); }); test('Diagnostic cleared on file close', async () => testClearDiagnosticsOnClose(true)); test('Diagnostic not cleared on file opened in another tab', async () => testClearDiagnosticsOnClose(false)); async function testClearDiagnosticsOnClose(closed: boolean) { - docManager.setup(x => x.onDidCloseTextDocument).returns(() => emitter.event); + docManager.setup((x) => x.onDidCloseTextDocument).returns(() => emitter.event); const uri = vscode.Uri.file('test.py'); - document.setup(x => x.uri).returns(() => uri); - document.setup(x => x.isClosed).returns(() => closed); + document.setup((x) => x.uri).returns(() => uri); + document.setup((x) => x.isClosed).returns(() => closed); - docManager.setup(x => x.textDocuments).returns(() => (closed ? [] : [document.object])); + docManager.setup((x) => x.textDocuments).returns(() => (closed ? [] : [document.object])); const linterProvider = new LinterProvider(serviceContainer); await linterProvider.activate(); emitter.fire(document.object); const timesExpected = closed ? TypeMoq.Times.once() : TypeMoq.Times.never(); - engine.verify(x => x.clearDiagnostics(TypeMoq.It.isAny()), timesExpected); + engine.verify((x) => x.clearDiagnostics(TypeMoq.It.isAny()), timesExpected); } }); diff --git a/src/test/linters/lint.test.ts b/src/test/linters/lint.test.ts index ffcf9a43759e..2843e2bffa8e 100644 --- a/src/test/linters/lint.test.ts +++ b/src/test/linters/lint.test.ts @@ -34,7 +34,7 @@ suite('Linting Settings', () => { let linterManager: ILinterManager; let configService: IConfigurationService; - suiteSetup(async function() { + suiteSetup(async function () { // These tests are still consistently failing during teardown. // See gh-4326. // tslint:disable-next-line:no-invalid-this @@ -103,7 +103,7 @@ suite('Linting Settings', () => { await configService.updateSetting('linting.lintOnSave', false, rootWorkspaceUri, target); await configService.updateSetting('linting.pylintUseMinimalCheckers', false, workspaceUri); - linterManager.getAllLinterInfos().forEach(async x => { + linterManager.getAllLinterInfos().forEach(async (x) => { const settingKey = `linting.${x.enabledSettingName}`; await configService.updateSetting(settingKey, false, rootWorkspaceUri, target); }); @@ -132,7 +132,7 @@ suite('Linting Settings', () => { // tslint:disable-next-line:no-any assert.equal((settings.linting as any)[`${Product[product]}Enabled`], true, 'mismatch'); - linterManager.getAllLinterInfos().forEach(async x => { + linterManager.getAllLinterInfos().forEach(async (x) => { if (x.product !== product) { // tslint:disable-next-line:no-any assert.equal((settings.linting as any)[x.enabledSettingName], false, 'mismatch'); diff --git a/src/test/linters/lint.unit.test.ts b/src/test/linters/lint.unit.test.ts index 1b1a55d0c72e..7c92fac4c0cc 100644 --- a/src/test/linters/lint.unit.test.ts +++ b/src/test/linters/lint.unit.test.ts @@ -548,14 +548,16 @@ class TestFixture extends BaseTestFixture { this.pythonExecService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); this.pythonExecFactory = pythonExecFactory; - this.filesystem.setup(f => f.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + this.filesystem.setup((f) => f.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); // tslint:disable-next-line:no-any this.pythonExecService.setup((s: any) => s.then).returns(() => undefined); - this.pythonExecService.setup(s => s.isModuleInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + this.pythonExecService + .setup((s) => s.isModuleInstalled(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(true)); this.pythonExecFactory - .setup(f => f.create(TypeMoq.It.isAny())) + .setup((f) => f.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(this.pythonExecService.object)); } @@ -563,8 +565,8 @@ class TestFixture extends BaseTestFixture { const doc = this.newMockDocument(filename); if (product === Product.pydocstyle) { const dummyLine = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - dummyLine.setup(d => d.text).returns(() => ' ...'); - doc.setup(s => s.lineAt(TypeMoq.It.isAny())).returns(() => dummyLine.object); + dummyLine.setup((d) => d.text).returns(() => ' ...'); + doc.setup((s) => s.lineAt(TypeMoq.It.isAny())).returns(() => dummyLine.object); } return doc.object; } @@ -616,7 +618,7 @@ class TestFixture extends BaseTestFixture { public setStdout(stdout: string) { this.pythonToolExecService - .setup(s => s.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((s) => s.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: stdout })); } } @@ -694,10 +696,10 @@ suite('Linting Scenarios', () => { } for (const product of LINTERID_BY_PRODUCT.keys()) { for (const enabled of [false, true]) { - test(`${enabled ? 'Enable' : 'Disable'} ${getProductName(product)} and run linter`, async function() { + test(`${enabled ? 'Enable' : 'Disable'} ${getProductName(product)} and run linter`, async function () { // tslint:disable-next-line:no-suspicious-comment // TODO: Add coverage for these linters. - if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some(p => p === product)) { + if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some((p) => p === product)) { // tslint:disable-next-line:no-invalid-this this.skip(); } @@ -739,10 +741,10 @@ suite('Linting Scenarios', () => { } } for (const product of LINTERID_BY_PRODUCT.keys()) { - test(`Check ${getProductName(product)} messages`, async function() { + test(`Check ${getProductName(product)} messages`, async function () { // tslint:disable-next-line:no-suspicious-comment // TODO: Add coverage for these linters. - if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some(p => p === product)) { + if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some((p) => p === product)) { // tslint:disable-next-line:no-invalid-this this.skip(); } @@ -778,9 +780,9 @@ suite('Linting Scenarios', () => { const PRODUCTS = Object.keys(Product) // tslint:disable-next-line:no-any - .filter(key => !isNaN(Number(Product[key as any]))) + .filter((key) => !isNaN(Number(Product[key as any]))) // tslint:disable-next-line:no-any - .map(key => Product[key as any]); + .map((key) => Product[key as any]); // tslint:disable-next-line:max-func-body-length suite('Linting Products', () => { diff --git a/src/test/linters/lintengine.test.ts b/src/test/linters/lintengine.test.ts index a7cc3fd2622b..e2db5059dfab 100644 --- a/src/test/linters/lintengine.test.ts +++ b/src/test/linters/lintengine.test.ts @@ -29,43 +29,43 @@ suite('Linting - LintingEngine', () => { const docManager = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IDocumentManager), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IDocumentManager), TypeMoq.It.isAny())) .returns(() => docManager.object); const workspaceService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) .returns(() => workspaceService.object); fileSystem = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) .returns(() => fileSystem.object); lintSettings = TypeMoq.Mock.ofType(); settings = TypeMoq.Mock.ofType(); const configService = TypeMoq.Mock.ofType(); - configService.setup(x => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); - configService.setup(x => x.isTestExecution()).returns(() => true); + configService.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + configService.setup((x) => x.isTestExecution()).returns(() => true); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) .returns(() => configService.object); const outputChannel = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isValue(STANDARD_OUTPUT_CHANNEL))) + .setup((c) => c.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isValue(STANDARD_OUTPUT_CHANNEL))) .returns(() => outputChannel.object); lintManager = TypeMoq.Mock.ofType(); - lintManager.setup(x => x.isLintingEnabled(TypeMoq.It.isAny())).returns(async () => true); + lintManager.setup((x) => x.isLintingEnabled(TypeMoq.It.isAny())).returns(async () => true); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILinterManager), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ILinterManager), TypeMoq.It.isAny())) .returns(() => lintManager.object); lintingEngine = new LintingEngine(serviceContainer.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILintingEngine), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(ILintingEngine), TypeMoq.It.isAny())) .returns(() => lintingEngine); }); @@ -75,7 +75,7 @@ suite('Linting - LintingEngine', () => { lintingEngine.lintDocument(doc, 'auto').ignoreErrors(); } catch { lintManager.verify( - l => l.isLintingEnabled(TypeMoq.It.isAny(), TypeMoq.It.isValue(doc.uri)), + (l) => l.isLintingEnabled(TypeMoq.It.isAny(), TypeMoq.It.isValue(doc.uri)), TypeMoq.Times.once() ); } @@ -86,7 +86,7 @@ suite('Linting - LintingEngine', () => { lintingEngine.lintDocument(doc, 'auto').ignoreErrors(); } catch { lintManager.verify( - l => + (l) => l.createLinter( TypeMoq.It.isAny(), TypeMoq.It.isAny(), @@ -102,7 +102,7 @@ suite('Linting - LintingEngine', () => { const doc = mockTextDocument('a1.py', PYTHON_LANGUAGE, true, ['a*.py']); await lintingEngine.lintDocument(doc, 'auto'); lintManager.verify( - l => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); }); @@ -111,7 +111,7 @@ suite('Linting - LintingEngine', () => { const doc = mockTextDocument('a.ts', 'typescript', true); await lintingEngine.lintDocument(doc, 'auto'); lintManager.verify( - l => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); }); @@ -120,7 +120,7 @@ suite('Linting - LintingEngine', () => { const doc = mockTextDocument('a1.py', PYTHON_LANGUAGE, false, [], 'git'); await lintingEngine.lintDocument(doc, 'auto'); lintManager.verify( - l => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); }); @@ -128,7 +128,7 @@ suite('Linting - LintingEngine', () => { const doc = mockTextDocument('a1.py', PYTHON_LANGUAGE, false, [], 'showModifications'); await lintingEngine.lintDocument(doc, 'auto'); lintManager.verify( - l => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); }); @@ -136,7 +136,7 @@ suite('Linting - LintingEngine', () => { const doc = mockTextDocument('a1.py', PYTHON_LANGUAGE, false, [], 'svn'); await lintingEngine.lintDocument(doc, 'auto'); lintManager.verify( - l => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); }); @@ -145,7 +145,7 @@ suite('Linting - LintingEngine', () => { const doc = mockTextDocument('file.py', PYTHON_LANGUAGE, false, []); await lintingEngine.lintDocument(doc, 'auto'); lintManager.verify( - l => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); }); @@ -157,19 +157,19 @@ suite('Linting - LintingEngine', () => { ignorePattern: string[] = [], scheme?: string ): TextDocument { - fileSystem.setup(x => x.fileExists(TypeMoq.It.isAnyString())).returns(() => Promise.resolve(exists)); + fileSystem.setup((x) => x.fileExists(TypeMoq.It.isAnyString())).returns(() => Promise.resolve(exists)); - lintSettings.setup(l => l.ignorePatterns).returns(() => ignorePattern); - settings.setup(x => x.linting).returns(() => lintSettings.object); + lintSettings.setup((l) => l.ignorePatterns).returns(() => ignorePattern); + settings.setup((x) => x.linting).returns(() => lintSettings.object); const doc = TypeMoq.Mock.ofType(); if (scheme) { - doc.setup(d => d.uri).returns(() => Uri.parse(`${scheme}:${fileName}`)); + doc.setup((d) => d.uri).returns(() => Uri.parse(`${scheme}:${fileName}`)); } else { - doc.setup(d => d.uri).returns(() => Uri.file(fileName)); + doc.setup((d) => d.uri).returns(() => Uri.file(fileName)); } - doc.setup(d => d.fileName).returns(() => fileName); - doc.setup(d => d.languageId).returns(() => language); + doc.setup((d) => d.fileName).returns(() => fileName); + doc.setup((d) => d.languageId).returns(() => language); return doc.object; } }); diff --git a/src/test/linters/linter.availability.unit.test.ts b/src/test/linters/linter.availability.unit.test.ts index dd968f5206c5..55583d229534 100644 --- a/src/test/linters/linter.availability.unit.test.ts +++ b/src/test/linters/linter.availability.unit.test.ts @@ -260,14 +260,14 @@ suite('Linter Availability Provider tests', () => { const notificationPromptEnabled = TypeMoq.Mock.ofType>(); factoryMock - .setup(f => f.createWorkspacePersistentState(TypeMoq.It.isAny(), true)) + .setup((f) => f.createWorkspacePersistentState(TypeMoq.It.isAny(), true)) .returns(() => notificationPromptEnabled.object); - notificationPromptEnabled.setup(n => n.value).returns(() => promptEnabled); + notificationPromptEnabled.setup((n) => n.value).returns(() => promptEnabled); const selections: ['enable', 'ignore', 'disablePrompt'] = ['enable', 'ignore', 'disablePrompt']; const optButtons = [Linters.enableLinter().format(linterInfo.id), Common.notNow(), Common.doNotShowAgain()]; if (promptEnabled) { appShellMock - .setup(ap => + .setup((ap) => ap.showInformationMessage( TypeMoq.It.isValue(Linters.enablePylint().format(linterInfo.id)), TypeMoq.It.isValue(Linters.enableLinter().format(linterInfo.id)), @@ -279,13 +279,13 @@ suite('Linter Availability Provider tests', () => { .verifiable(TypeMoq.Times.once()); if (promptAction === 'disablePrompt') { notificationPromptEnabled - .setup(n => n.updateValue(false)) + .setup((n) => n.updateValue(false)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); } } else { appShellMock - .setup(ap => + .setup((ap) => ap.showInformationMessage( TypeMoq.It.isValue(Linters.enablePylint().format(linterInfo.id)), TypeMoq.It.isValue(Linters.enableLinter().format(linterInfo.id)), @@ -406,7 +406,7 @@ suite('Linter Availability Provider tests', () => { const selections: ['enable', 'ignore', 'disablePrompt'] = ['enable', 'ignore', 'disablePrompt']; const optButtons = [Linters.enableLinter().format(linterInfo.id), Common.notNow(), Common.doNotShowAgain()]; appShellMock - .setup(ap => + .setup((ap) => ap.showInformationMessage( TypeMoq.It.isValue(Linters.enablePylint().format(linterInfo.id)), TypeMoq.It.isValue(Linters.enableLinter().format(linterInfo.id)), @@ -421,15 +421,15 @@ suite('Linter Availability Provider tests', () => { const workspaceFolder = { uri: Uri.parse('full/path/to/workspace'), name: '', index: 0 }; workspaceServiceMock - .setup(c => c.hasWorkspaceFolders) + .setup((c) => c.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.once()); workspaceServiceMock - .setup(c => c.workspaceFolders) + .setup((c) => c.workspaceFolders) .returns(() => [workspaceFolder]) .verifiable(TypeMoq.Times.once()); fsMock - .setup(fs => fs.fileExists(TypeMoq.It.isAny())) + .setup((fs) => fs.fileExists(TypeMoq.It.isAny())) .returns(async () => options.linterIsInstalled) .verifiable(TypeMoq.Times.atLeastOnce()); @@ -443,9 +443,9 @@ suite('Linter Availability Provider tests', () => { const notificationPromptEnabled = TypeMoq.Mock.ofType>(); factoryMock - .setup(f => f.createWorkspacePersistentState(TypeMoq.It.isAny(), true)) + .setup((f) => f.createWorkspacePersistentState(TypeMoq.It.isAny(), true)) .returns(() => notificationPromptEnabled.object); - notificationPromptEnabled.setup(n => n.value).returns(() => true); + notificationPromptEnabled.setup((n) => n.value).returns(() => true); // perform test const availabilityProvider: IAvailableLinterActivator = new AvailableLinterActivator( appShellMock.object, @@ -678,7 +678,7 @@ suite('Linter Availability Provider tests', () => { }); [undefined, { uri: Uri.file(path.join('c', 'd', 'resource')), name: 'another', index: 10 }].forEach( - workspaceFolderRelatedToResource => { + (workspaceFolderRelatedToResource) => { const testSuffix = workspaceFolderRelatedToResource ? '(has a corresponding workspace)' : '(use default workspace)'; @@ -782,7 +782,7 @@ function setupWorkspaceMockForLinterConfiguredTests( } const workspaceConfiguration = TypeMoq.Mock.ofType(); workspaceConfiguration - .setup(wc => wc.inspect(TypeMoq.It.isValue('pylintEnabled'))) + .setup((wc) => wc.inspect(TypeMoq.It.isValue('pylintEnabled'))) .returns(() => { return { key: '', @@ -795,7 +795,7 @@ function setupWorkspaceMockForLinterConfiguredTests( .verifiable(TypeMoq.Times.once()); workspaceServiceMock - .setup(ws => ws.getConfiguration(TypeMoq.It.isValue('python.linting'), TypeMoq.It.isAny())) + .setup((ws) => ws.getConfiguration(TypeMoq.It.isValue('python.linting'), TypeMoq.It.isAny())) .returns(() => workspaceConfiguration.object) .verifiable(TypeMoq.Times.once()); @@ -810,9 +810,9 @@ function setupConfigurationServiceForJediSettingsTest( configServiceMock = TypeMoq.Mock.ofType(); } const pythonSettings = TypeMoq.Mock.ofType(); - pythonSettings.setup(ps => ps.jediEnabled).returns(() => jediEnabledValue); + pythonSettings.setup((ps) => ps.jediEnabled).returns(() => jediEnabledValue); - configServiceMock.setup(cs => cs.getSettings()).returns(() => pythonSettings.object); + configServiceMock.setup((cs) => cs.getSettings()).returns(() => pythonSettings.object); return [configServiceMock, pythonSettings]; } @@ -827,13 +827,13 @@ function setupInstallerForAvailabilityTest( } const workspaceFolder = { uri: Uri.parse('full/path/to/workspace'), name: '', index: 0 }; workspaceServiceMock - .setup(c => c.hasWorkspaceFolders) + .setup((c) => c.hasWorkspaceFolders) .returns(() => true) .verifiable(TypeMoq.Times.once()); - workspaceServiceMock.setup(c => c.workspaceFolders).returns(() => [workspaceFolder]); - workspaceServiceMock.setup(c => c.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder); + workspaceServiceMock.setup((c) => c.workspaceFolders).returns(() => [workspaceFolder]); + workspaceServiceMock.setup((c) => c.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder); fsMock - .setup(fs => fs.fileExists(TypeMoq.It.isAny())) + .setup((fs) => fs.fileExists(TypeMoq.It.isAny())) .returns(() => Promise.resolve(linterIsInstalled)) .verifiable(TypeMoq.Times.atLeastOnce()); diff --git a/src/test/linters/linterManager.unit.test.ts b/src/test/linters/linterManager.unit.test.ts index b16db9c2b1da..efdc927eaa9d 100644 --- a/src/test/linters/linterManager.unit.test.ts +++ b/src/test/linters/linterManager.unit.test.ts @@ -69,14 +69,14 @@ suite('Linting - Linter Manager', () => { const productService = new ProductService(); const linterProducts = getNamesAndValues(Product) - .filter(product => productService.getProductType(product.value) === ProductType.Linter) - .map(item => ProductNames.get(item.value)); - expect(linters.map(item => item.id).sort()).to.be.deep.equal(linterProducts.sort()); + .filter((product) => productService.getProductType(product.value) === ProductType.Linter) + .map((item) => ProductNames.get(item.value)); + expect(linters.map((item) => item.id).sort()).to.be.deep.equal(linterProducts.sort()); }); test('Get linter info for non-linter product should throw an exception', () => { const productService = new ProductService(); - getNamesAndValues(Product).forEach(prod => { + getNamesAndValues(Product).forEach((prod) => { if (productService.getProductType(prod.value) === ProductType.Linter) { const info = linterManager.getLinterInfo(prod.value); expect(info.id).to.equal(ProductNames.get(prod.value)); @@ -93,9 +93,9 @@ suite('Linting - Linter Manager', () => { assert.notEqual(pylint.configFileNames.indexOf('.pylintrc'), -1, 'Pylint configuration files miss .pylintrc.'); }); - [undefined, Uri.parse('something')].forEach(resource => { + [undefined, Uri.parse('something')].forEach((resource) => { const testResourceSuffix = `(${resource ? 'with a resource' : 'without a resource'})`; - [true, false].forEach(enabled => { + [true, false].forEach((enabled) => { const testSuffix = `(${enabled ? 'enable' : 'disable'}) & ${testResourceSuffix}`; test(`Enable linting should update config ${testSuffix}`, async () => { when(configService.updateSetting('linting.enabled', enabled, resource)).thenResolve(); @@ -162,15 +162,17 @@ suite('Linting - Linter Manager', () => { const linters = new Map(); const linterInstances = new Map(); linterManager.linters = []; - [Product.flake8, Product.mypy, Product.prospector, Product.bandit, Product.pydocstyle].forEach(product => { - const linterInfo = mock(LinterInfo); - const instanceOfLinterInfo = instance(linterInfo); - linterManager.linters.push(instanceOfLinterInfo); - linters.set(product, linterInfo); - linterInstances.set(product, instanceOfLinterInfo); - when(linterInfo.product).thenReturn(product); - when(linterInfo.enableAsync(anything(), resource)).thenResolve(); - }); + [Product.flake8, Product.mypy, Product.prospector, Product.bandit, Product.pydocstyle].forEach( + (product) => { + const linterInfo = mock(LinterInfo); + const instanceOfLinterInfo = instance(linterInfo); + linterManager.linters.push(instanceOfLinterInfo); + linters.set(product, linterInfo); + linterInstances.set(product, instanceOfLinterInfo); + when(linterInfo.product).thenReturn(product); + when(linterInfo.enableAsync(anything(), resource)).thenResolve(); + } + ); linterManager.getActiveLinters = () => Promise.resolve(Array.from(linterInstances.values())); linterManager.enableLintingAsync = () => Promise.resolve(); diff --git a/src/test/linters/linterinfo.unit.test.ts b/src/test/linters/linterinfo.unit.test.ts index c2af5158b534..e989361336f8 100644 --- a/src/test/linters/linterinfo.unit.test.ts +++ b/src/test/linters/linterinfo.unit.test.ts @@ -75,7 +75,7 @@ suite('Linter Info - Pylint', () => { ]; suite('Test is enabled when using Language Server and Pylint is configured', () => { - testsForisEnabled.forEach(testParams => { + testsForisEnabled.forEach((testParams) => { test(testParams.testName, async () => { const config = mock(ConfigurationService); const workspaceService = mock(WorkspaceService); diff --git a/src/test/linters/pylint.test.ts b/src/test/linters/pylint.test.ts index 3c44ae14f22c..c8017d136970 100644 --- a/src/test/linters/pylint.test.ts +++ b/src/test/linters/pylint.test.ts @@ -39,11 +39,11 @@ suite('Linting - Pylint', () => { setup(() => { fileSystem = TypeMoq.Mock.ofType(); fileSystem - .setup(x => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) + .setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) .returns((a, b) => a === b); platformService = TypeMoq.Mock.ofType(); - platformService.setup(x => x.isWindows).returns(() => false); + platformService.setup((x) => x.isWindows).returns(() => false); workspace = TypeMoq.Mock.ofType(); execService = TypeMoq.Mock.ofType(); @@ -76,11 +76,11 @@ suite('Linting - Pylint', () => { }); test('pylintrc in the file folder', async () => { - fileSystem.setup(x => x.fileExists(path.join(basePath, pylintrc))).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(path.join(basePath, pylintrc))).returns(() => Promise.resolve(true)); let result = await Pylint.hasConfigurationFile(fileSystem.object, basePath, platformService.object); expect(result).to.be.equal(true, `'${pylintrc}' not detected in the file folder.`); - fileSystem.setup(x => x.fileExists(path.join(basePath, dotPylintrc))).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(path.join(basePath, dotPylintrc))).returns(() => Promise.resolve(true)); result = await Pylint.hasConfigurationFile(fileSystem.object, basePath, platformService.object); expect(result).to.be.equal(true, `'${dotPylintrc}' not detected in the file folder.`); }); @@ -90,10 +90,10 @@ suite('Linting - Pylint', () => { const module3 = path.join('/user/a/b', '__init__.py'); const rc = path.join('/user/a/b/c', pylintrc); - fileSystem.setup(x => x.fileExists(module1)).returns(() => Promise.resolve(true)); - fileSystem.setup(x => x.fileExists(module2)).returns(() => Promise.resolve(true)); - fileSystem.setup(x => x.fileExists(module3)).returns(() => Promise.resolve(true)); - fileSystem.setup(x => x.fileExists(rc)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(module1)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(module2)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(module3)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(rc)).returns(() => Promise.resolve(true)); const result = await Pylint.hasConfigurationFile(fileSystem.object, basePath, platformService.object); expect(result).to.be.equal(true, `'${pylintrc}' not detected in the module tree.`); @@ -105,10 +105,10 @@ suite('Linting - Pylint', () => { const module3 = path.join('/user/a/b', '__init__.py'); const rc = path.join('/user/a/b/c', pylintrc); - fileSystem.setup(x => x.fileExists(module1)).returns(() => Promise.resolve(true)); - fileSystem.setup(x => x.fileExists(module2)).returns(() => Promise.resolve(true)); - fileSystem.setup(x => x.fileExists(module3)).returns(() => Promise.resolve(true)); - fileSystem.setup(x => x.fileExists(rc)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(module1)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(module2)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(module3)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(rc)).returns(() => Promise.resolve(true)); const result = await Pylint.hasConfigurationFile(fileSystem.object, basePath, platformService.object); expect(result).to.be.equal(true, `'${dotPylintrc}' not detected in the module tree.`); @@ -116,7 +116,7 @@ suite('Linting - Pylint', () => { test('.pylintrc up the ~ folder', async () => { const home = os.homedir(); const rc = path.join(home, dotPylintrc); - fileSystem.setup(x => x.fileExists(rc)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(rc)).returns(() => Promise.resolve(true)); const result = await Pylint.hasConfigurationFile(fileSystem.object, basePath, platformService.object); expect(result).to.be.equal(true, `'${dotPylintrc}' not detected in the ~ folder.`); @@ -124,14 +124,14 @@ suite('Linting - Pylint', () => { test('pylintrc up the ~/.config folder', async () => { const home = os.homedir(); const rc = path.join(home, '.config', pylintrc); - fileSystem.setup(x => x.fileExists(rc)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(rc)).returns(() => Promise.resolve(true)); const result = await Pylint.hasConfigurationFile(fileSystem.object, basePath, platformService.object); expect(result).to.be.equal(true, `'${pylintrc}' not detected in the ~/.config folder.`); }); test('pylintrc in the /etc folder', async () => { const rc = path.join('/etc', pylintrc); - fileSystem.setup(x => x.fileExists(rc)).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(rc)).returns(() => Promise.resolve(true)); const result = await Pylint.hasConfigurationFile(fileSystem.object, basePath, platformService.object); expect(result).to.be.equal(true, `'${pylintrc}' not detected in the /etc folder.`); @@ -139,14 +139,14 @@ suite('Linting - Pylint', () => { test('pylintrc between file and workspace root', async () => { const root = '/user/a'; const midFolder = '/user/a/b'; - fileSystem.setup(x => x.fileExists(path.join(midFolder, pylintrc))).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(path.join(midFolder, pylintrc))).returns(() => Promise.resolve(true)); const result = await Pylint.hasConfigurationFileInWorkspace(fileSystem.object, basePath, root); expect(result).to.be.equal(true, `'${pylintrc}' not detected in the workspace tree.`); }); test('minArgs - pylintrc between the file and the workspace root', async () => { - fileSystem.setup(x => x.fileExists(path.join('/user/a/b', pylintrc))).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(path.join('/user/a/b', pylintrc))).returns(() => Promise.resolve(true)); await testPylintArguments('/user/a/b/c', '/user/a', false); }); @@ -157,14 +157,14 @@ suite('Linting - Pylint', () => { test('minArgs - pylintrc next to the file', async () => { const fileFolder = '/user/a/b/c'; - fileSystem.setup(x => x.fileExists(path.join(fileFolder, pylintrc))).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(path.join(fileFolder, pylintrc))).returns(() => Promise.resolve(true)); await testPylintArguments(fileFolder, '/user/a', false); }); test('minArgs - pylintrc at the workspace root', async () => { const root = '/user/a'; - fileSystem.setup(x => x.fileExists(path.join(root, pylintrc))).returns(() => Promise.resolve(true)); + fileSystem.setup((x) => x.fileExists(path.join(root, pylintrc))).returns(() => Promise.resolve(true)); await testPylintArguments('/user/a/b/c', root, false); }); @@ -174,16 +174,16 @@ suite('Linting - Pylint', () => { const pylinter = new Pylint(outputChannel.object, serviceContainer); const document = TypeMoq.Mock.ofType(); - document.setup(x => x.uri).returns(() => Uri.file(path.join(fileFolder, 'test.py'))); + document.setup((x) => x.uri).returns(() => Uri.file(path.join(fileFolder, 'test.py'))); const wsf = TypeMoq.Mock.ofType(); - wsf.setup(x => x.uri).returns(() => Uri.file(wsRoot)); + wsf.setup((x) => x.uri).returns(() => Uri.file(wsRoot)); - workspace.setup(x => x.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => wsf.object); + workspace.setup((x) => x.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => wsf.object); let execInfo: ExecutionInfo | undefined; execService - .setup(x => x.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((x) => x.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .callback((e: ExecutionInfo, _b, _c) => { execInfo = e; }) @@ -197,12 +197,12 @@ suite('Linting - Pylint', () => { lintSettings['pylintEnabled'] = true; const settings = TypeMoq.Mock.ofType(); - settings.setup(x => x.linting).returns(() => lintSettings); - config.setup(x => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + settings.setup((x) => x.linting).returns(() => lintSettings); + config.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); await pylinter.lint(document.object, new CancellationTokenSource().token); expect( - execInfo!.args.findIndex(x => x.indexOf('--disable=all') >= 0), + execInfo!.args.findIndex((x) => x.indexOf('--disable=all') >= 0), 'Minimal args passed to pylint while pylintrc exists.' ).to.be.eq(expectedMinArgs ? 0 : -1); } @@ -212,12 +212,12 @@ suite('Linting - Pylint', () => { const pylinter = new Pylint(outputChannel.object, serviceContainer); const document = TypeMoq.Mock.ofType(); - document.setup(x => x.uri).returns(() => Uri.file(path.join(fileFolder, 'test.py'))); + document.setup((x) => x.uri).returns(() => Uri.file(path.join(fileFolder, 'test.py'))); const wsf = TypeMoq.Mock.ofType(); - wsf.setup(x => x.uri).returns(() => Uri.file(fileFolder)); + wsf.setup((x) => x.uri).returns(() => Uri.file(fileFolder)); - workspace.setup(x => x.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => wsf.object); + workspace.setup((x) => x.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => wsf.object); const linterOutput = [ 'No config file found, using default configuration', @@ -226,7 +226,7 @@ suite('Linting - Pylint', () => { '3,-1,error,E1305:Too many arguments for format string' ].join(os.EOL); execService - .setup(x => x.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((x) => x.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ stdout: linterOutput, stderr: '' })); const lintSettings = new MockLintingSettings(); @@ -243,8 +243,8 @@ suite('Linting - Pylint', () => { }; const settings = TypeMoq.Mock.ofType(); - settings.setup(x => x.linting).returns(() => lintSettings); - config.setup(x => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + settings.setup((x) => x.linting).returns(() => lintSettings); + config.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); const messages = await pylinter.lint(document.object, new CancellationTokenSource().token); expect(messages).to.be.lengthOf(2); diff --git a/src/test/linters/pylint.unit.test.ts b/src/test/linters/pylint.unit.test.ts index 864425a49bb8..b09bc1feecb3 100644 --- a/src/test/linters/pylint.unit.test.ts +++ b/src/test/linters/pylint.unit.test.ts @@ -32,11 +32,13 @@ suite('Pylint - Function hasConfigurationFile()', () => { setup(() => { serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPlatformService))).returns(() => platformService.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); + serviceContainer + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) + .returns(() => platformService.object); fileSystem = TypeMoq.Mock.ofType(); fileSystem - .setup(x => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) + .setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) .returns((a, b) => a === b); platformService = TypeMoq.Mock.ofType(); @@ -50,13 +52,13 @@ suite('Pylint - Function hasConfigurationFile()', () => { } }); - pylintrcFiles.forEach(pylintrcFile => { + pylintrcFiles.forEach((pylintrcFile) => { test(`If ${pylintrcFile} exists in the current working directory, return true`, async () => { fileSystem - .setup(x => x.fileExists(path.join(folder, pylintrc))) + .setup((x) => x.fileExists(path.join(folder, pylintrc))) .returns(() => Promise.resolve(pylintrc === pylintrcFile)); fileSystem - .setup(x => x.fileExists(path.join(folder, dotPylintrc))) + .setup((x) => x.fileExists(path.join(folder, dotPylintrc))) .returns(() => Promise.resolve(dotPylintrc === pylintrcFile)); const hasConfig = await Pylint.hasConfigurationFile(fileSystem.object, folder, platformService.object); expect(hasConfig).to.equal(true, 'Should return true'); @@ -64,27 +66,27 @@ suite('Pylint - Function hasConfigurationFile()', () => { test(`If the current working directory is in a Python module, Pylint searches up the hierarchy of Python modules until it finds a ${pylintrcFile} file. And if ${pylintrcFile} exists, return true`, async () => { fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a', 'b', 'c', 'd'), '__init__.py'))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a', 'b', 'c', 'd'), '__init__.py'))) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a', 'b', 'c', 'd'), pylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a', 'b', 'c', 'd'), pylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.atLeastOnce()); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a', 'b', 'c', 'd'), dotPylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a', 'b', 'c', 'd'), dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.atLeastOnce()); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a', 'b', 'c'), '__init__.py'))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a', 'b', 'c'), '__init__.py'))) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a', 'b', 'c'), pylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a', 'b', 'c'), pylintrc))) .returns(() => Promise.resolve(pylintrc === pylintrcFile)); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a', 'b', 'c'), dotPylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a', 'b', 'c'), dotPylintrc))) .returns(() => Promise.resolve(dotPylintrc === pylintrcFile)); const hasConfig = await Pylint.hasConfigurationFile(fileSystem.object, folder, platformService.object); expect(hasConfig).to.equal(true, 'Should return true'); @@ -94,17 +96,17 @@ suite('Pylint - Function hasConfigurationFile()', () => { test(`If ${pylintrcFile} exists in the home directory, return true`, async () => { const home = os.homedir(); - fileSystem.setup(x => x.fileExists(path.join(folder, pylintrc))).returns(() => Promise.resolve(false)); - fileSystem.setup(x => x.fileExists(path.join(folder, dotPylintrc))).returns(() => Promise.resolve(false)); + fileSystem.setup((x) => x.fileExists(path.join(folder, pylintrc))).returns(() => Promise.resolve(false)); + fileSystem.setup((x) => x.fileExists(path.join(folder, dotPylintrc))).returns(() => Promise.resolve(false)); fileSystem - .setup(x => x.fileExists(path.join(folder, '__init__.py'))) + .setup((x) => x.fileExists(path.join(folder, '__init__.py'))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(home, '.config', pylintrc))) + .setup((x) => x.fileExists(path.join(home, '.config', pylintrc))) .returns(() => Promise.resolve(pylintrc === pylintrcFile)); fileSystem - .setup(x => x.fileExists(path.join(home, dotPylintrc))) + .setup((x) => x.fileExists(path.join(home, dotPylintrc))) .returns(() => Promise.resolve(dotPylintrc === pylintrcFile)); const hasConfig = await Pylint.hasConfigurationFile(fileSystem.object, folder, platformService.object); expect(hasConfig).to.equal(true, 'Should return true'); @@ -113,34 +115,34 @@ suite('Pylint - Function hasConfigurationFile()', () => { }); }); - test('If /etc/pylintrc exists in non-Windows platform, return true', async function() { + test('If /etc/pylintrc exists in non-Windows platform, return true', async function () { if (new PlatformService().isWindows) { // tslint:disable-next-line:no-invalid-this return this.skip(); } const home = os.homedir(); fileSystem - .setup(x => x.fileExists(path.join(folder, pylintrc))) + .setup((x) => x.fileExists(path.join(folder, pylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(folder, dotPylintrc))) + .setup((x) => x.fileExists(path.join(folder, dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(folder, '__init__.py'))) + .setup((x) => x.fileExists(path.join(folder, '__init__.py'))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(home, '.config', pylintrc))) + .setup((x) => x.fileExists(path.join(home, '.config', pylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(home, dotPylintrc))) + .setup((x) => x.fileExists(path.join(home, dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); - platformService.setup(x => x.isWindows).returns(() => false); - fileSystem.setup(x => x.fileExists(path.join('/etc', pylintrc))).returns(() => Promise.resolve(true)); + platformService.setup((x) => x.isWindows).returns(() => false); + fileSystem.setup((x) => x.fileExists(path.join('/etc', pylintrc))).returns(() => Promise.resolve(true)); const hasConfig = await Pylint.hasConfigurationFile(fileSystem.object, folder, platformService.object); expect(hasConfig).to.equal(true, 'Should return true'); fileSystem.verifyAll(); @@ -150,31 +152,31 @@ suite('Pylint - Function hasConfigurationFile()', () => { test('If none of the pylintrc configuration files exist anywhere, return false', async () => { const home = os.homedir(); fileSystem - .setup(x => x.fileExists(path.join(folder, pylintrc))) + .setup((x) => x.fileExists(path.join(folder, pylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(folder, dotPylintrc))) + .setup((x) => x.fileExists(path.join(folder, dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(folder, '__init__.py'))) + .setup((x) => x.fileExists(path.join(folder, '__init__.py'))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(home, '.config', pylintrc))) + .setup((x) => x.fileExists(path.join(home, '.config', pylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(home, dotPylintrc))) + .setup((x) => x.fileExists(path.join(home, dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); platformService - .setup(x => x.isWindows) + .setup((x) => x.isWindows) .returns(() => false) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join('/etc', pylintrc))) + .setup((x) => x.fileExists(path.join('/etc', pylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); const hasConfig = await Pylint.hasConfigurationFile(fileSystem.object, folder, platformService.object); @@ -200,11 +202,13 @@ suite('Pylint - Function hasConfigurationFileInWorkspace()', () => { setup(() => { serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPlatformService))).returns(() => platformService.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); + serviceContainer + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) + .returns(() => platformService.object); fileSystem = TypeMoq.Mock.ofType(); fileSystem - .setup(x => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) + .setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) .returns((a, b) => a === b); platformService = TypeMoq.Mock.ofType(); @@ -217,27 +221,27 @@ suite('Pylint - Function hasConfigurationFileInWorkspace()', () => { const rootPathItems = ['user', 'a']; const folderPathItems = ['b', 'c']; // full folder path will be prefixed by root path let rootPath = ''; - rootPathItems.forEach(item => { + rootPathItems.forEach((item) => { rootPath = path.join(rootPath, item); fileSystem - .setup(x => x.fileExists(path.join(rootPath, pylintrc))) + .setup((x) => x.fileExists(path.join(rootPath, pylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.never()); fileSystem - .setup(x => x.fileExists(path.join(rootPath, dotPylintrc))) + .setup((x) => x.fileExists(path.join(rootPath, dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.never()); }); let relativeFolderPath = ''; - folderPathItems.forEach(item => { + folderPathItems.forEach((item) => { relativeFolderPath = path.join(relativeFolderPath, item); const absoluteFolderPath = path.join(rootPath, relativeFolderPath); fileSystem - .setup(x => x.fileExists(path.join(absoluteFolderPath, pylintrc))) + .setup((x) => x.fileExists(path.join(absoluteFolderPath, pylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(absoluteFolderPath, dotPylintrc))) + .setup((x) => x.fileExists(path.join(absoluteFolderPath, dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); }); @@ -247,42 +251,42 @@ suite('Pylint - Function hasConfigurationFileInWorkspace()', () => { fileSystem.verifyAll(); }); - [pylintrc, dotPylintrc].forEach(pylintrcFile => { + [pylintrc, dotPylintrc].forEach((pylintrcFile) => { test(`If ${pylintrcFile} exists while traversing up to the workspace root, return true`, async () => { const folder = path.join('user', 'a', 'b', 'c'); const root = path.join('user', 'a'); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a', 'b', 'c'), pylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a', 'b', 'c'), pylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a', 'b', 'c'), dotPylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a', 'b', 'c'), dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.once()); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a', 'b'), pylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a', 'b'), pylintrc))) .returns(() => Promise.resolve(pylintrc === pylintrcFile)); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a', 'b'), dotPylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a', 'b'), dotPylintrc))) .returns(() => Promise.resolve(dotPylintrc === pylintrcFile)); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a'), pylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a'), pylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.never()); fileSystem - .setup(x => x.fileExists(path.join(path.join('user', 'a'), dotPylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user', 'a'), dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.never()); fileSystem - .setup(x => x.fileExists(path.join(path.join('user'), dotPylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user'), dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.never()); fileSystem - .setup(x => x.fileExists(path.join(path.join('user'), dotPylintrc))) + .setup((x) => x.fileExists(path.join(path.join('user'), dotPylintrc))) .returns(() => Promise.resolve(false)) .verifiable(TypeMoq.Times.never()); @@ -370,20 +374,22 @@ suite('Pylint - Function runLinter()', () => { workspaceService = TypeMoq.Mock.ofType(); configService = TypeMoq.Mock.ofType(); manager = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ILinterManager))).returns(() => manager.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ILinterManager))).returns(() => manager.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPlatformService))).returns(() => platformService.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); + serviceContainer + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) + .returns(() => platformService.object); fileSystem = TypeMoq.Mock.ofType(); fileSystem - .setup(x => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) + .setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) .returns((a, b) => a === b); - manager.setup(m => m.getLinterInfo(TypeMoq.It.isAny())).returns(() => undefined as any); + manager.setup((m) => m.getLinterInfo(TypeMoq.It.isAny())).returns(() => undefined as any); }); teardown(() => { @@ -398,8 +404,8 @@ suite('Pylint - Function runLinter()', () => { pylintUseMinimalCheckers: true } }; - configService.setup(c => c.getSettings(doc.uri)).returns(() => settings as any); - _info.setup(info => info.linterArgs(doc.uri)).returns(() => []); + configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as any); + _info.setup((info) => info.linterArgs(doc.uri)).returns(() => []); Pylint.hasConfigurationFileInWorkspace = () => Promise.resolve(false); Pylint.hasConfigurationFile = () => Promise.resolve(false); run = sinon.stub(PylintTest.prototype, 'run'); @@ -419,8 +425,8 @@ suite('Pylint - Function runLinter()', () => { pylintUseMinimalCheckers: false } }; - configService.setup(c => c.getSettings(doc.uri)).returns(() => settings as any); - _info.setup(info => info.linterArgs(doc.uri)).returns(() => []); + configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as any); + _info.setup((info) => info.linterArgs(doc.uri)).returns(() => []); Pylint.hasConfigurationFileInWorkspace = () => Promise.resolve(false); Pylint.hasConfigurationFile = () => Promise.resolve(false); run = sinon.stub(PylintTest.prototype, 'run'); @@ -440,8 +446,8 @@ suite('Pylint - Function runLinter()', () => { pylintUseMinimalCheckers: true } }; - configService.setup(c => c.getSettings(doc.uri)).returns(() => settings as any); - _info.setup(info => info.linterArgs(doc.uri)).returns(() => ['customArg1', 'customArg2']); + configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as any); + _info.setup((info) => info.linterArgs(doc.uri)).returns(() => ['customArg1', 'customArg2']); Pylint.hasConfigurationFileInWorkspace = () => Promise.resolve(false); Pylint.hasConfigurationFile = () => Promise.resolve(false); run = sinon.stub(PylintTest.prototype, 'run'); @@ -461,8 +467,8 @@ suite('Pylint - Function runLinter()', () => { pylintUseMinimalCheckers: true } }; - configService.setup(c => c.getSettings(doc.uri)).returns(() => settings as any); - _info.setup(info => info.linterArgs(doc.uri)).returns(() => []); + configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as any); + _info.setup((info) => info.linterArgs(doc.uri)).returns(() => []); Pylint.hasConfigurationFileInWorkspace = () => Promise.resolve(true); // This implies method hasConfigurationFileInWorkspace() returns true Pylint.hasConfigurationFile = () => Promise.resolve(false); run = sinon.stub(PylintTest.prototype, 'run'); @@ -482,8 +488,8 @@ suite('Pylint - Function runLinter()', () => { pylintUseMinimalCheckers: true } }; - configService.setup(c => c.getSettings(doc.uri)).returns(() => settings as any); - _info.setup(info => info.linterArgs(doc.uri)).returns(() => []); + configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as any); + _info.setup((info) => info.linterArgs(doc.uri)).returns(() => []); Pylint.hasConfigurationFileInWorkspace = () => Promise.resolve(false); Pylint.hasConfigurationFile = () => Promise.resolve(true); // This implies method hasConfigurationFile() returns true run = sinon.stub(PylintTest.prototype, 'run'); @@ -514,8 +520,8 @@ suite('Pylint - Function runLinter()', () => { pylintUseMinimalCheckers: true } }; - configService.setup(c => c.getSettings(doc.uri)).returns(() => settings as any); - _info.setup(info => info.linterArgs(doc.uri)).returns(() => []); + configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as any); + _info.setup((info) => info.linterArgs(doc.uri)).returns(() => []); Pylint.hasConfigurationFileInWorkspace = () => Promise.resolve(false); Pylint.hasConfigurationFile = () => Promise.resolve(false); run = sinon.stub(PylintTest.prototype, 'run'); diff --git a/src/test/linters/serviceRegistry.unit.test.ts b/src/test/linters/serviceRegistry.unit.test.ts index 1e185f94b5cc..4f63e9fae940 100644 --- a/src/test/linters/serviceRegistry.unit.test.ts +++ b/src/test/linters/serviceRegistry.unit.test.ts @@ -1,35 +1,35 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { instance, mock, verify } from 'ts-mockito'; -import { IExtensionActivationService } from '../../client/activation/types'; -import { ServiceManager } from '../../client/ioc/serviceManager'; -import { IServiceManager } from '../../client/ioc/types'; -import { AvailableLinterActivator } from '../../client/linters/linterAvailability'; -import { LinterManager } from '../../client/linters/linterManager'; -import { LintingEngine } from '../../client/linters/lintingEngine'; -import { registerTypes } from '../../client/linters/serviceRegistry'; -import { IAvailableLinterActivator, ILinterManager, ILintingEngine } from '../../client/linters/types'; -import { LinterProvider } from '../../client/providers/linterProvider'; - -suite('Linters Service Registry', () => { - let serviceManager: IServiceManager; - - setup(() => { - serviceManager = mock(ServiceManager); - }); - - test('Ensure services are registered', async () => { - registerTypes(instance(serviceManager)); - verify(serviceManager.addSingleton(ILintingEngine, LintingEngine)).once(); - verify(serviceManager.addSingleton(ILinterManager, LinterManager)).once(); - verify( - serviceManager.add(IAvailableLinterActivator, AvailableLinterActivator) - ).once(); - verify( - serviceManager.addSingleton(IExtensionActivationService, LinterProvider) - ).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { instance, mock, verify } from 'ts-mockito'; +import { IExtensionActivationService } from '../../client/activation/types'; +import { ServiceManager } from '../../client/ioc/serviceManager'; +import { IServiceManager } from '../../client/ioc/types'; +import { AvailableLinterActivator } from '../../client/linters/linterAvailability'; +import { LinterManager } from '../../client/linters/linterManager'; +import { LintingEngine } from '../../client/linters/lintingEngine'; +import { registerTypes } from '../../client/linters/serviceRegistry'; +import { IAvailableLinterActivator, ILinterManager, ILintingEngine } from '../../client/linters/types'; +import { LinterProvider } from '../../client/providers/linterProvider'; + +suite('Linters Service Registry', () => { + let serviceManager: IServiceManager; + + setup(() => { + serviceManager = mock(ServiceManager); + }); + + test('Ensure services are registered', async () => { + registerTypes(instance(serviceManager)); + verify(serviceManager.addSingleton(ILintingEngine, LintingEngine)).once(); + verify(serviceManager.addSingleton(ILinterManager, LinterManager)).once(); + verify( + serviceManager.add(IAvailableLinterActivator, AvailableLinterActivator) + ).once(); + verify( + serviceManager.addSingleton(IExtensionActivationService, LinterProvider) + ).once(); + }); +}); diff --git a/src/test/mocks/proc.ts b/src/test/mocks/proc.ts index 67a4e3ccce12..59f069cd4d33 100644 --- a/src/test/mocks/proc.ts +++ b/src/test/mocks/proc.ts @@ -37,7 +37,7 @@ export class MockProcessService extends EventEmitter implements IProcessService if (valueReturned) { const output = value as Output; - if (['stderr', 'stdout'].some(source => source === output.source)) { + if (['stderr', 'stdout'].some((source) => source === output.source)) { return { // tslint:disable-next-line:no-any proc: {} as any, diff --git a/src/test/mocks/vsc/arrays.ts b/src/test/mocks/vsc/arrays.ts index 9381f67d575e..9291745a066f 100644 --- a/src/test/mocks/vsc/arrays.ts +++ b/src/test/mocks/vsc/arrays.ts @@ -232,7 +232,7 @@ export namespace vscMockArrays { const element = array[i]; if (compare(element, result[n - 1]) < 0) { result.pop(); - const j = findFirst(result, e => compare(element, e) < 0); + const j = findFirst(result, (e) => compare(element, e) < 0); result.splice(j, 0, element); } } @@ -246,7 +246,7 @@ export namespace vscMockArrays { return array; } - return array.filter(e => !!e); + return array.filter((e) => !!e); } /** @@ -276,7 +276,7 @@ export namespace vscMockArrays { } const seen: Record = Object.create(null); - return array.filter(elem => { + return array.filter((elem) => { const key = keyFn(elem); if (seen[key]) { return false; @@ -291,7 +291,7 @@ export namespace vscMockArrays { export function uniqueFilter(keyFn: (t: T) => string): (t: T) => boolean { const seen: Record = Object.create(null); - return element => { + return (element) => { const key = keyFn(element); if (seen[key]) { @@ -379,7 +379,7 @@ export namespace vscMockArrays { export function index( array: T[], indexer: (t: T) => string, - merger: (t: T, r: R) => R = t => t as any + merger: (t: T, r: R) => R = (t) => t as any ): Record { return array.reduce((r, t) => { const key = indexer(t); diff --git a/src/test/mocks/vsc/extHostedTypes.ts b/src/test/mocks/vsc/extHostedTypes.ts index e7c2dd66a0b6..a4e2bd71a024 100644 --- a/src/test/mocks/vsc/extHostedTypes.ts +++ b/src/test/mocks/vsc/extHostedTypes.ts @@ -27,7 +27,7 @@ export namespace vscMockExtHostedTypes { export class Disposable { static from(...disposables: { dispose(): any }[]): Disposable { - return new Disposable(function() { + return new Disposable(function () { if (disposables) { for (let disposable of disposables) { if (disposable && typeof disposable.dispose === 'function') { @@ -614,7 +614,7 @@ export namespace vscMockExtHostedTypes { entries(): [vscUri.URI, TextEdit[]][] { const res: [vscUri.URI, TextEdit[]][] = []; - this._textEdits.forEach(value => res.push([value.uri, value.edits])); + this._textEdits.forEach((value) => res.push([value.uri, value.edits])); return res.slice(); } diff --git a/src/test/multiRootTest.ts b/src/test/multiRootTest.ts index 9feb50446a93..dc9a77f32466 100644 --- a/src/test/multiRootTest.ts +++ b/src/test/multiRootTest.ts @@ -16,7 +16,7 @@ function start() { launchArgs: [workspacePath], version: 'stable', extensionTestsEnv: { ...process.env, UITEST_DISABLE_INSIDERS: '1' } - }).catch(ex => { + }).catch((ex) => { console.error('End Multiroot tests (with errors)', ex); process.exit(1); }); diff --git a/src/test/performance/load.perf.test.ts b/src/test/performance/load.perf.test.ts index 872957324a56..5521cb11566f 100644 --- a/src/test/performance/load.perf.test.ts +++ b/src/test/performance/load.perf.test.ts @@ -19,10 +19,7 @@ suite('Activation Times', () => { if (process.env.ACTIVATION_TIMES_LOG_FILE_PATH) { const logFile = process.env.ACTIVATION_TIMES_LOG_FILE_PATH; const sampleCounter = fs.existsSync(logFile) - ? fs - .readFileSync(logFile, { encoding: 'utf8' }) - .toString() - .split(/\r?\n/g).length + ? fs.readFileSync(logFile, { encoding: 'utf8' }).toString().split(/\r?\n/g).length : 1; if (sampleCounter > 5) { return; @@ -56,10 +53,10 @@ suite('Activation Times', () => { fs.readFileSync(file, { encoding: 'utf8' }) .toString() .split(/\r?\n/g) - .map(line => line.trim()) - .filter(line => line.length > 0) - .map(line => parseInt(line, 10)) - .forEach(item => activationTimes.push(item)); + .map((line) => line.trim()) + .filter((line) => line.length > 0) + .map((line) => parseInt(line, 10)) + .forEach((item) => activationTimes.push(item)); } return activationTimes; } diff --git a/src/test/performanceTest.ts b/src/test/performanceTest.ts index 6599c7eb0a8b..d489c76dd011 100644 --- a/src/test/performanceTest.ts +++ b/src/test/performanceTest.ts @@ -111,7 +111,7 @@ class TestRunner { proc.stdout.pipe(process.stdout); proc.stderr.pipe(process.stderr); proc.on('error', reject); - proc.on('close', code => { + proc.on('close', (code) => { if (code === 0) { resolve(); } else { @@ -162,4 +162,4 @@ class TestRunner { } } -new TestRunner().start().catch(ex => console.error('Error in running Performance Tests', ex)); +new TestRunner().start().catch((ex) => console.error('Error in running Performance Tests', ex)); diff --git a/src/test/providers/codeActionProvider/launchJsonCodeActionProvider.unit.test.ts b/src/test/providers/codeActionProvider/launchJsonCodeActionProvider.unit.test.ts index ceae05d4bbd0..136271b3e4e5 100644 --- a/src/test/providers/codeActionProvider/launchJsonCodeActionProvider.unit.test.ts +++ b/src/test/providers/codeActionProvider/launchJsonCodeActionProvider.unit.test.ts @@ -22,14 +22,14 @@ suite('LaunchJson CodeAction Provider', () => { range = TypeMoq.Mock.ofType(); context = TypeMoq.Mock.ofType(); diagnostic = TypeMoq.Mock.ofType(); - document.setup(d => d.getText(TypeMoq.It.isAny())).returns(() => 'Diagnostic text'); - document.setup(d => d.uri).returns(() => documentUri); - context.setup(c => c.diagnostics).returns(() => [diagnostic.object]); + document.setup((d) => d.getText(TypeMoq.It.isAny())).returns(() => 'Diagnostic text'); + document.setup((d) => d.uri).returns(() => documentUri); + context.setup((c) => c.diagnostics).returns(() => [diagnostic.object]); }); test('Ensure correct code action is returned if diagnostic message equals `Incorrect type. Expected "string".`', async () => { - diagnostic.setup(d => d.message).returns(() => 'Incorrect type. Expected "string".'); - diagnostic.setup(d => d.range).returns(() => new Range(2, 0, 7, 8)); + diagnostic.setup((d) => d.message).returns(() => 'Incorrect type. Expected "string".'); + diagnostic.setup((d) => d.range).returns(() => new Range(2, 0, 7, 8)); const codeActions = codeActionsProvider.provideCodeActions(document.object, range.object, context.object); @@ -50,7 +50,7 @@ suite('LaunchJson CodeAction Provider', () => { }); test('Ensure no code action is returned if diagnostic message does not equal `Incorrect type. Expected "string".`', async () => { - diagnostic.setup(d => d.message).returns(() => 'Random diagnostic message'); + diagnostic.setup((d) => d.message).returns(() => 'Random diagnostic message'); const codeActions = codeActionsProvider.provideCodeActions(document.object, range.object, context.object); diff --git a/src/test/providers/codeActionProvider/pythonCodeActionsProvider.unit.test.ts b/src/test/providers/codeActionProvider/pythonCodeActionsProvider.unit.test.ts index 454dd2bc27ad..5638bb32d96b 100644 --- a/src/test/providers/codeActionProvider/pythonCodeActionsProvider.unit.test.ts +++ b/src/test/providers/codeActionProvider/pythonCodeActionsProvider.unit.test.ts @@ -36,7 +36,7 @@ suite('Python CodeAction Provider', () => { } const organizeImportsCodeAction = codeActions.filter( - codeAction => codeAction.kind === CodeActionKind.SourceOrganizeImports + (codeAction) => codeAction.kind === CodeActionKind.SourceOrganizeImports ); expect(organizeImportsCodeAction).to.have.length(1); expect(organizeImportsCodeAction[0].kind).to.eq(CodeActionKind.SourceOrganizeImports); diff --git a/src/test/providers/foldingProvider.test.ts b/src/test/providers/foldingProvider.test.ts index 1686605bffd9..b4fec804ea39 100644 --- a/src/test/providers/foldingProvider.test.ts +++ b/src/test/providers/foldingProvider.test.ts @@ -69,15 +69,15 @@ suite('Provider - Folding Provider', () => { } ]; - docStringFileAndExpectedFoldingRanges.forEach(item => { + docStringFileAndExpectedFoldingRanges.forEach((item) => { test(`Test Docstring folding regions '${path.basename(item.file)}'`, async () => { const document = await workspace.openTextDocument(item.file); const provider = new DocStringFoldingProvider(); const ranges = await provider.provideFoldingRanges(document, {}, new CancellationTokenSource().token); expect(ranges).to.be.lengthOf(item.ranges.length); - ranges!.forEach(range => { + ranges!.forEach((range) => { const index = item.ranges.findIndex( - searchItem => searchItem.start === range.start && searchItem.end === range.end + (searchItem) => searchItem.start === range.start && searchItem.end === range.end ); expect(index).to.be.greaterThan(-1, `${range.start}, ${range.end} not found`); }); diff --git a/src/test/providers/importSortProvider.unit.test.ts b/src/test/providers/importSortProvider.unit.test.ts index e85e44b19f1c..3d7208b21200 100644 --- a/src/test/providers/importSortProvider.unit.test.ts +++ b/src/test/providers/importSortProvider.unit.test.ts @@ -55,23 +55,23 @@ suite('Import Sort Provider', () => { pythonSettings = TypeMoq.Mock.ofType(); editorUtils = TypeMoq.Mock.ofType(); fs = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(ICommandManager)).returns(() => commandManager.object); - serviceContainer.setup(c => c.get(IDocumentManager)).returns(() => documentManager.object); - serviceContainer.setup(c => c.get(IApplicationShell)).returns(() => shell.object); - serviceContainer.setup(c => c.get(IConfigurationService)).returns(() => configurationService.object); - serviceContainer.setup(c => c.get(IPythonExecutionFactory)).returns(() => pythonExecFactory.object); - serviceContainer.setup(c => c.get(IProcessServiceFactory)).returns(() => processServiceFactory.object); - serviceContainer.setup(c => c.get(IEditorUtils)).returns(() => editorUtils.object); - serviceContainer.setup(c => c.get(IDisposableRegistry)).returns(() => []); - serviceContainer.setup(c => c.get(IFileSystem)).returns(() => fs.object); - configurationService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + serviceContainer.setup((c) => c.get(ICommandManager)).returns(() => commandManager.object); + serviceContainer.setup((c) => c.get(IDocumentManager)).returns(() => documentManager.object); + serviceContainer.setup((c) => c.get(IApplicationShell)).returns(() => shell.object); + serviceContainer.setup((c) => c.get(IConfigurationService)).returns(() => configurationService.object); + serviceContainer.setup((c) => c.get(IPythonExecutionFactory)).returns(() => pythonExecFactory.object); + serviceContainer.setup((c) => c.get(IProcessServiceFactory)).returns(() => processServiceFactory.object); + serviceContainer.setup((c) => c.get(IEditorUtils)).returns(() => editorUtils.object); + serviceContainer.setup((c) => c.get(IDisposableRegistry)).returns(() => []); + serviceContainer.setup((c) => c.get(IFileSystem)).returns(() => fs.object); + configurationService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); sortProvider = new SortImportsEditingProvider(serviceContainer.object); }); test('Ensure command is registered', () => { commandManager - .setup(c => + .setup((c) => c.registerCommand( TypeMoq.It.isValue(Commands.Sort_Imports), TypeMoq.It.isAny(), @@ -85,11 +85,11 @@ suite('Import Sort Provider', () => { }); test("Ensure message is displayed when no doc is opened and uri isn't provided", async () => { documentManager - .setup(d => d.activeTextEditor) + .setup((d) => d.activeTextEditor) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); shell - .setup(s => s.showErrorMessage(TypeMoq.It.isValue('Please open a Python file to sort the imports.'))) + .setup((s) => s.showErrorMessage(TypeMoq.It.isValue('Please open a Python file to sort the imports.'))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); await sortProvider.sortImports(); @@ -101,20 +101,20 @@ suite('Import Sort Provider', () => { const mockEditor = TypeMoq.Mock.ofType(); const mockDoc = TypeMoq.Mock.ofType(); mockDoc - .setup(d => d.languageId) + .setup((d) => d.languageId) .returns(() => 'xyz') .verifiable(TypeMoq.Times.atLeastOnce()); mockEditor - .setup(d => d.document) + .setup((d) => d.document) .returns(() => mockDoc.object) .verifiable(TypeMoq.Times.atLeastOnce()); documentManager - .setup(d => d.activeTextEditor) + .setup((d) => d.activeTextEditor) .returns(() => mockEditor.object) .verifiable(TypeMoq.Times.once()); shell - .setup(s => s.showErrorMessage(TypeMoq.It.isValue('Please open a Python file to sort the imports.'))) + .setup((s) => s.showErrorMessage(TypeMoq.It.isValue('Please open a Python file to sort the imports.'))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); await sortProvider.sortImports(); @@ -127,10 +127,12 @@ suite('Import Sort Provider', () => { test('Ensure document is opened', async () => { const uri = Uri.file('TestDoc'); - documentManager.setup(d => d.openTextDocument(TypeMoq.It.isValue(uri))).verifiable(TypeMoq.Times.atLeastOnce()); - documentManager.setup(d => d.activeTextEditor).verifiable(TypeMoq.Times.never()); + documentManager + .setup((d) => d.openTextDocument(TypeMoq.It.isValue(uri))) + .verifiable(TypeMoq.Times.atLeastOnce()); + documentManager.setup((d) => d.activeTextEditor).verifiable(TypeMoq.Times.never()); shell - .setup(s => s.showErrorMessage(TypeMoq.It.isAny())) + .setup((s) => s.showErrorMessage(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); await sortProvider.sortImports(uri).catch(noop); @@ -144,15 +146,15 @@ suite('Import Sort Provider', () => { // tslint:disable-next-line:no-any mockDoc.setup((d: any) => d.then).returns(() => undefined); mockDoc - .setup(d => d.lineCount) + .setup((d) => d.lineCount) .returns(() => 1) .verifiable(TypeMoq.Times.atLeastOnce()); documentManager - .setup(d => d.openTextDocument(TypeMoq.It.isValue(uri))) + .setup((d) => d.openTextDocument(TypeMoq.It.isValue(uri))) .returns(() => Promise.resolve(mockDoc.object)) .verifiable(TypeMoq.Times.atLeastOnce()); shell - .setup(s => s.showErrorMessage(TypeMoq.It.isAny())) + .setup((s) => s.showErrorMessage(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); const edit = await sortProvider.sortImports(uri); @@ -167,15 +169,15 @@ suite('Import Sort Provider', () => { // tslint:disable-next-line:no-any mockDoc.setup((d: any) => d.then).returns(() => undefined); mockDoc - .setup(d => d.lineCount) + .setup((d) => d.lineCount) .returns(() => 0) .verifiable(TypeMoq.Times.atLeastOnce()); documentManager - .setup(d => d.openTextDocument(TypeMoq.It.isValue(uri))) + .setup((d) => d.openTextDocument(TypeMoq.It.isValue(uri))) .returns(() => Promise.resolve(mockDoc.object)) .verifiable(TypeMoq.Times.atLeastOnce()); shell - .setup(s => s.showErrorMessage(TypeMoq.It.isAny())) + .setup((s) => s.showErrorMessage(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); const edit = await sortProvider.sortImports(uri); @@ -189,35 +191,35 @@ suite('Import Sort Provider', () => { const mockDoc = TypeMoq.Mock.ofType(); mockDoc.setup((d: any) => d.then).returns(() => undefined); mockDoc - .setup(d => d.lineCount) + .setup((d) => d.lineCount) .returns(() => 10) .verifiable(TypeMoq.Times.atLeastOnce()); const lastLine = TypeMoq.Mock.ofType(); let editApplied: WorkspaceEdit | undefined; lastLine - .setup(l => l.text) + .setup((l) => l.text) .returns(() => '1234') .verifiable(TypeMoq.Times.atLeastOnce()); lastLine - .setup(l => l.range) + .setup((l) => l.range) .returns(() => new Range(1, 0, 10, 1)) .verifiable(TypeMoq.Times.atLeastOnce()); mockDoc - .setup(d => d.lineAt(TypeMoq.It.isValue(9))) + .setup((d) => d.lineAt(TypeMoq.It.isValue(9))) .returns(() => lastLine.object) .verifiable(TypeMoq.Times.atLeastOnce()); documentManager - .setup(d => d.applyEdit(TypeMoq.It.isAny())) - .callback(e => (editApplied = e)) + .setup((d) => d.applyEdit(TypeMoq.It.isAny())) + .callback((e) => (editApplied = e)) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.atLeastOnce()); documentManager - .setup(d => d.openTextDocument(TypeMoq.It.isValue(uri))) + .setup((d) => d.openTextDocument(TypeMoq.It.isValue(uri))) .returns(() => Promise.resolve(mockDoc.object)) .verifiable(TypeMoq.Times.atLeastOnce()); shell - .setup(s => s.showErrorMessage(TypeMoq.It.isAny())) + .setup((s) => s.showErrorMessage(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); @@ -236,15 +238,15 @@ suite('Import Sort Provider', () => { const mockDoc = TypeMoq.Mock.ofType(); mockDoc.setup((d: any) => d.then).returns(() => undefined); mockDoc - .setup(d => d.lineCount) + .setup((d) => d.lineCount) .returns(() => 1) .verifiable(TypeMoq.Times.atLeastOnce()); documentManager - .setup(d => d.openTextDocument(TypeMoq.It.isValue(uri))) + .setup((d) => d.openTextDocument(TypeMoq.It.isValue(uri))) .returns(() => Promise.resolve(mockDoc.object)) .verifiable(TypeMoq.Times.atLeastOnce()); shell - .setup(s => s.showErrorMessage(TypeMoq.It.isAny())) + .setup((s) => s.showErrorMessage(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); const edit = await sortProvider.provideDocumentSortImportsEdits(uri); @@ -258,15 +260,15 @@ suite('Import Sort Provider', () => { const mockDoc = TypeMoq.Mock.ofType(); mockDoc.setup((d: any) => d.then).returns(() => undefined); mockDoc - .setup(d => d.lineCount) + .setup((d) => d.lineCount) .returns(() => 0) .verifiable(TypeMoq.Times.atLeastOnce()); documentManager - .setup(d => d.openTextDocument(TypeMoq.It.isValue(uri))) + .setup((d) => d.openTextDocument(TypeMoq.It.isValue(uri))) .returns(() => Promise.resolve(mockDoc.object)) .verifiable(TypeMoq.Times.atLeastOnce()); shell - .setup(s => s.showErrorMessage(TypeMoq.It.isAny())) + .setup((s) => s.showErrorMessage(TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.never()); const edit = await sortProvider.provideDocumentSortImportsEdits(uri); @@ -284,45 +286,45 @@ suite('Import Sort Provider', () => { processService.setup((d: any) => d.then).returns(() => undefined); mockDoc.setup((d: any) => d.then).returns(() => undefined); mockDoc - .setup(d => d.lineCount) + .setup((d) => d.lineCount) .returns(() => 10) .verifiable(TypeMoq.Times.atLeastOnce()); mockDoc - .setup(d => d.getText(TypeMoq.It.isAny())) + .setup((d) => d.getText(TypeMoq.It.isAny())) .returns(() => 'Hello') .verifiable(TypeMoq.Times.atLeastOnce()); mockDoc - .setup(d => d.isDirty) + .setup((d) => d.isDirty) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); mockDoc - .setup(d => d.uri) + .setup((d) => d.uri) .returns(() => uri) .verifiable(TypeMoq.Times.atLeastOnce()); documentManager - .setup(d => d.openTextDocument(TypeMoq.It.isValue(uri))) + .setup((d) => d.openTextDocument(TypeMoq.It.isValue(uri))) .returns(() => Promise.resolve(mockDoc.object)) .verifiable(TypeMoq.Times.atLeastOnce()); - fs.setup(f => f.createTemporaryFile(TypeMoq.It.isValue('.py'))) + fs.setup((f) => f.createTemporaryFile(TypeMoq.It.isValue('.py'))) .returns(() => Promise.resolve(tmpFile)) .verifiable(TypeMoq.Times.once()); - fs.setup(f => f.writeFile(TypeMoq.It.isValue(tmpFile.filePath), TypeMoq.It.isValue('Hello'))) + fs.setup((f) => f.writeFile(TypeMoq.It.isValue(tmpFile.filePath), TypeMoq.It.isValue('Hello'))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); pythonSettings - .setup(s => s.sortImports) + .setup((s) => s.sortImports) .returns(() => { return ({ path: 'CUSTOM_ISORT', args: ['1', '2'] } as any) as ISortImportSettings; }) .verifiable(TypeMoq.Times.once()); processServiceFactory - .setup(p => p.create(TypeMoq.It.isAny())) + .setup((p) => p.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(processService.object)) .verifiable(TypeMoq.Times.once()); const expectedArgs = [tmpFile.filePath, '--diff', '1', '2']; processService - .setup(p => + .setup((p) => p.exec( TypeMoq.It.isValue('CUSTOM_ISORT'), TypeMoq.It.isValue(expectedArgs), @@ -333,7 +335,7 @@ suite('Import Sort Provider', () => { .verifiable(TypeMoq.Times.once()); const expectedEdit = new WorkspaceEdit(); editorUtils - .setup(e => + .setup((e) => e.getWorkspaceEditsFromPatch( TypeMoq.It.isValue('Hello'), TypeMoq.It.isValue('DIFF'), @@ -359,33 +361,33 @@ suite('Import Sort Provider', () => { processService.setup((d: any) => d.then).returns(() => undefined); mockDoc.setup((d: any) => d.then).returns(() => undefined); mockDoc - .setup(d => d.lineCount) + .setup((d) => d.lineCount) .returns(() => 10) .verifiable(TypeMoq.Times.atLeastOnce()); mockDoc - .setup(d => d.getText(TypeMoq.It.isAny())) + .setup((d) => d.getText(TypeMoq.It.isAny())) .returns(() => 'Hello') .verifiable(TypeMoq.Times.atLeastOnce()); mockDoc - .setup(d => d.isDirty) + .setup((d) => d.isDirty) .returns(() => true) .verifiable(TypeMoq.Times.atLeastOnce()); mockDoc - .setup(d => d.uri) + .setup((d) => d.uri) .returns(() => uri) .verifiable(TypeMoq.Times.atLeastOnce()); documentManager - .setup(d => d.openTextDocument(TypeMoq.It.isValue(uri))) + .setup((d) => d.openTextDocument(TypeMoq.It.isValue(uri))) .returns(() => Promise.resolve(mockDoc.object)) .verifiable(TypeMoq.Times.atLeastOnce()); - fs.setup(f => f.createTemporaryFile(TypeMoq.It.isValue('.py'))) + fs.setup((f) => f.createTemporaryFile(TypeMoq.It.isValue('.py'))) .returns(() => Promise.resolve(tmpFile)) .verifiable(TypeMoq.Times.once()); - fs.setup(f => f.writeFile(TypeMoq.It.isValue(tmpFile.filePath), TypeMoq.It.isValue('Hello'))) + fs.setup((f) => f.writeFile(TypeMoq.It.isValue(tmpFile.filePath), TypeMoq.It.isValue('Hello'))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); pythonSettings - .setup(s => s.sortImports) + .setup((s) => s.sortImports) .returns(() => { return ({ args: ['1', '2'] } as any) as ISortImportSettings; }) @@ -394,20 +396,20 @@ suite('Import Sort Provider', () => { const processExeService = TypeMoq.Mock.ofType(); processExeService.setup((p: any) => p.then).returns(() => undefined); pythonExecFactory - .setup(p => p.create(TypeMoq.It.isAny())) + .setup((p) => p.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(processExeService.object)) .verifiable(TypeMoq.Times.once()); const importScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'sortImports.py'); const expectedArgs = [importScript, tmpFile.filePath, '--diff', '1', '2']; processExeService - .setup(p => + .setup((p) => p.exec(TypeMoq.It.isValue(expectedArgs), TypeMoq.It.isValue({ throwOnStdErr: true, token: undefined })) ) .returns(() => Promise.resolve({ stdout: 'DIFF' })) .verifiable(TypeMoq.Times.once()); const expectedEdit = new WorkspaceEdit(); editorUtils - .setup(e => + .setup((e) => e.getWorkspaceEditsFromPatch( TypeMoq.It.isValue('Hello'), TypeMoq.It.isValue('DIFF'), diff --git a/src/test/providers/repl.unit.test.ts b/src/test/providers/repl.unit.test.ts index 98d172850f28..31a213f3ac33 100644 --- a/src/test/providers/repl.unit.test.ts +++ b/src/test/providers/repl.unit.test.ts @@ -31,13 +31,13 @@ suite('REPL Provider', () => { codeExecutionService = TypeMoq.Mock.ofType(); documentManager = TypeMoq.Mock.ofType(); activeResourceService = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(ICommandManager)).returns(() => commandManager.object); - serviceContainer.setup(c => c.get(IWorkspaceService)).returns(() => workspace.object); + serviceContainer.setup((c) => c.get(ICommandManager)).returns(() => commandManager.object); + serviceContainer.setup((c) => c.get(IWorkspaceService)).returns(() => workspace.object); serviceContainer - .setup(c => c.get(ICodeExecutionService, TypeMoq.It.isValue('repl'))) + .setup((c) => c.get(ICodeExecutionService, TypeMoq.It.isValue('repl'))) .returns(() => codeExecutionService.object); - serviceContainer.setup(c => c.get(IDocumentManager)).returns(() => documentManager.object); - serviceContainer.setup(c => c.get(IActiveResourceService)).returns(() => activeResourceService.object); + serviceContainer.setup((c) => c.get(IDocumentManager)).returns(() => documentManager.object); + serviceContainer.setup((c) => c.get(IActiveResourceService)).returns(() => activeResourceService.object); }); teardown(() => { try { @@ -49,7 +49,7 @@ suite('REPL Provider', () => { test('Ensure command is registered', () => { replProvider = new ReplProvider(serviceContainer.object); commandManager.verify( - c => c.registerCommand(TypeMoq.It.isValue(Commands.Start_REPL), TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (c) => c.registerCommand(TypeMoq.It.isValue(Commands.Start_REPL), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once() ); }); @@ -57,7 +57,7 @@ suite('REPL Provider', () => { test('Ensure command handler is disposed', () => { const disposable = TypeMoq.Mock.ofType(); commandManager - .setup(c => + .setup((c) => c.registerCommand(TypeMoq.It.isValue(Commands.Start_REPL), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => disposable.object); @@ -65,7 +65,7 @@ suite('REPL Provider', () => { replProvider = new ReplProvider(serviceContainer.object); replProvider.dispose(); - disposable.verify(d => d.dispose(), TypeMoq.Times.once()); + disposable.verify((d) => d.dispose(), TypeMoq.Times.once()); }); test('Ensure execution is carried smoothly in the handler if there are no errors', () => { @@ -73,7 +73,7 @@ suite('REPL Provider', () => { const disposable = TypeMoq.Mock.ofType(); let commandHandler: undefined | (() => void); commandManager - .setup(c => + .setup((c) => c.registerCommand(TypeMoq.It.isValue(Commands.Start_REPL), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns((_cmd, callback) => { @@ -81,7 +81,7 @@ suite('REPL Provider', () => { return disposable.object; }); activeResourceService - .setup(a => a.getActiveResource()) + .setup((a) => a.getActiveResource()) .returns(() => resource) .verifiable(TypeMoq.Times.once()); @@ -90,9 +90,9 @@ suite('REPL Provider', () => { commandHandler!.call(replProvider); serviceContainer.verify( - c => c.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue('repl')), + (c) => c.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue('repl')), TypeMoq.Times.once() ); - codeExecutionService.verify(c => c.initializeRepl(TypeMoq.It.isValue(resource)), TypeMoq.Times.once()); + codeExecutionService.verify((c) => c.initializeRepl(TypeMoq.It.isValue(resource)), TypeMoq.Times.once()); }); }); diff --git a/src/test/providers/serviceRegistry.unit.test.ts b/src/test/providers/serviceRegistry.unit.test.ts index fe4da7a03b12..7c55645bd473 100644 --- a/src/test/providers/serviceRegistry.unit.test.ts +++ b/src/test/providers/serviceRegistry.unit.test.ts @@ -1,37 +1,37 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { instance, mock, verify } from 'ts-mockito'; -import { IExtensionSingleActivationService } from '../../client/activation/types'; -import { ServiceManager } from '../../client/ioc/serviceManager'; -import { IServiceManager } from '../../client/ioc/types'; -import { CodeActionProviderService } from '../../client/providers/codeActionProvider/main'; -import { SortImportsEditingProvider } from '../../client/providers/importSortProvider'; -import { registerTypes } from '../../client/providers/serviceRegistry'; -import { ISortImportsEditingProvider } from '../../client/providers/types'; - -suite('Common Providers Service Registry', () => { - let serviceManager: IServiceManager; - - setup(() => { - serviceManager = mock(ServiceManager); - }); - - test('Ensure services are registered', async () => { - registerTypes(instance(serviceManager)); - verify( - serviceManager.addSingleton( - ISortImportsEditingProvider, - SortImportsEditingProvider - ) - ).once(); - verify( - serviceManager.addSingleton( - IExtensionSingleActivationService, - CodeActionProviderService - ) - ).once(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { instance, mock, verify } from 'ts-mockito'; +import { IExtensionSingleActivationService } from '../../client/activation/types'; +import { ServiceManager } from '../../client/ioc/serviceManager'; +import { IServiceManager } from '../../client/ioc/types'; +import { CodeActionProviderService } from '../../client/providers/codeActionProvider/main'; +import { SortImportsEditingProvider } from '../../client/providers/importSortProvider'; +import { registerTypes } from '../../client/providers/serviceRegistry'; +import { ISortImportsEditingProvider } from '../../client/providers/types'; + +suite('Common Providers Service Registry', () => { + let serviceManager: IServiceManager; + + setup(() => { + serviceManager = mock(ServiceManager); + }); + + test('Ensure services are registered', async () => { + registerTypes(instance(serviceManager)); + verify( + serviceManager.addSingleton( + ISortImportsEditingProvider, + SortImportsEditingProvider + ) + ).once(); + verify( + serviceManager.addSingleton( + IExtensionSingleActivationService, + CodeActionProviderService + ) + ).once(); + }); +}); diff --git a/src/test/providers/shebangCodeLenseProvider.unit.test.ts b/src/test/providers/shebangCodeLenseProvider.unit.test.ts index dbbd109dc4db..76edd0facae4 100644 --- a/src/test/providers/shebangCodeLenseProvider.unit.test.ts +++ b/src/test/providers/shebangCodeLenseProvider.unit.test.ts @@ -34,7 +34,7 @@ suite('Shebang detection', () => { processService = typemoq.Mock.ofType(); platformService = typemoq.Mock.ofType(); // tslint:disable-next-line:no-any - processService.setup(p => (p as any).then).returns(() => undefined); + processService.setup((p) => (p as any).then).returns(() => undefined); when(configurationService.getSettings(anything())).thenReturn(pythonSettings.object); when(factory.create(anything())).thenResolve(processService.object); provider = new ShebangCodeLensProvider( @@ -51,15 +51,15 @@ suite('Shebang detection', () => { const doc = typemoq.Mock.ofType(); const line = typemoq.Mock.ofType(); - line.setup(l => l.isEmptyOrWhitespace) + line.setup((l) => l.isEmptyOrWhitespace) .returns(() => firstLine.length === 0) .verifiable(typemoq.Times.once()); - line.setup(l => l.text).returns(() => firstLine); + line.setup((l) => l.text).returns(() => firstLine); - doc.setup(d => d.lineAt(typemoq.It.isValue(0))) + doc.setup((d) => d.lineAt(typemoq.It.isValue(0))) .returns(() => line.object) .verifiable(typemoq.Times.once()); - doc.setup(d => d.uri).returns(() => uri); + doc.setup((d) => d.uri).returns(() => uri); return [doc, line]; } @@ -76,7 +76,7 @@ suite('Shebang detection', () => { const [document, line] = createDocument('#!HELLO'); processService - .setup(p => p.exec(typemoq.It.isValue('HELLO'), typemoq.It.isAny())) + .setup((p) => p.exec(typemoq.It.isValue('HELLO'), typemoq.It.isAny())) .returns(() => Promise.reject()) .verifiable(typemoq.Times.once()); @@ -91,7 +91,7 @@ suite('Shebang detection', () => { const [document, line] = createDocument('#!HELLO'); processService - .setup(p => p.exec(typemoq.It.isValue('HELLO'), typemoq.It.isAny())) + .setup((p) => p.exec(typemoq.It.isValue('HELLO'), typemoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'THIS_IS_IT' })) .verifiable(typemoq.Times.once()); @@ -105,11 +105,11 @@ suite('Shebang detection', () => { test("Shebang should be returned when python path is valid and text is'/usr/bin/env python'", async () => { const [document, line] = createDocument('#!/usr/bin/env python'); platformService - .setup(p => p.isWindows) + .setup((p) => p.isWindows) .returns(() => false) .verifiable(typemoq.Times.once()); processService - .setup(p => p.exec(typemoq.It.isValue('/usr/bin/env'), typemoq.It.isAny())) + .setup((p) => p.exec(typemoq.It.isValue('/usr/bin/env'), typemoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'THIS_IS_IT' })) .verifiable(typemoq.Times.once()); @@ -124,11 +124,11 @@ suite('Shebang detection', () => { test("Shebang should be returned when python path is valid and text is'/usr/bin/env python' and is windows", async () => { const [document, line] = createDocument('#!/usr/bin/env python'); platformService - .setup(p => p.isWindows) + .setup((p) => p.isWindows) .returns(() => true) .verifiable(typemoq.Times.once()); processService - .setup(p => p.exec(typemoq.It.isValue('/usr/bin/env python'), typemoq.It.isAny())) + .setup((p) => p.exec(typemoq.It.isValue('/usr/bin/env python'), typemoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'THIS_IS_IT' })) .verifiable(typemoq.Times.once()); @@ -143,9 +143,9 @@ suite('Shebang detection', () => { test("No code lens when there's no shebang", async () => { const [document] = createDocument(''); - pythonSettings.setup(p => p.pythonPath).returns(() => 'python'); + pythonSettings.setup((p) => p.pythonPath).returns(() => 'python'); processService - .setup(p => p.exec(typemoq.It.isValue('python'), typemoq.It.isAny())) + .setup((p) => p.exec(typemoq.It.isValue('python'), typemoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'python' })) .verifiable(typemoq.Times.once()); @@ -157,9 +157,9 @@ suite('Shebang detection', () => { }); test('No code lens when shebang is an empty string', async () => { const [document] = createDocument('#!'); - pythonSettings.setup(p => p.pythonPath).returns(() => 'python'); + pythonSettings.setup((p) => p.pythonPath).returns(() => 'python'); processService - .setup(p => p.exec(typemoq.It.isValue('python'), typemoq.It.isAny())) + .setup((p) => p.exec(typemoq.It.isValue('python'), typemoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'python' })) .verifiable(typemoq.Times.once()); @@ -171,9 +171,9 @@ suite('Shebang detection', () => { }); test('No code lens when python path in settings is the same as that in shebang', async () => { const [document] = createDocument('#!python'); - pythonSettings.setup(p => p.pythonPath).returns(() => 'python'); + pythonSettings.setup((p) => p.pythonPath).returns(() => 'python'); processService - .setup(p => p.exec(typemoq.It.isValue('python'), typemoq.It.isAny())) + .setup((p) => p.exec(typemoq.It.isValue('python'), typemoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'python' })) .verifiable(typemoq.Times.once()); @@ -185,9 +185,9 @@ suite('Shebang detection', () => { }); test('Code lens returned when python path in settings is different to one in shebang', async () => { const [document] = createDocument('#!python'); - pythonSettings.setup(p => p.pythonPath).returns(() => 'different'); + pythonSettings.setup((p) => p.pythonPath).returns(() => 'different'); processService - .setup(p => p.exec(typemoq.It.isValue('different'), typemoq.It.isAny())) + .setup((p) => p.exec(typemoq.It.isValue('different'), typemoq.It.isAny())) .returns(() => Promise.resolve({ stdout: 'different' })) .verifiable(typemoq.Times.once()); diff --git a/src/test/providers/terminal.unit.test.ts b/src/test/providers/terminal.unit.test.ts index 73bd20c675d9..3e852b63dc2d 100644 --- a/src/test/providers/terminal.unit.test.ts +++ b/src/test/providers/terminal.unit.test.ts @@ -26,9 +26,9 @@ suite('Terminal Provider', () => { commandManager = TypeMoq.Mock.ofType(); activeResourceService = TypeMoq.Mock.ofType(); workspace = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(ICommandManager)).returns(() => commandManager.object); - serviceContainer.setup(c => c.get(IWorkspaceService)).returns(() => workspace.object); - serviceContainer.setup(c => c.get(IActiveResourceService)).returns(() => activeResourceService.object); + serviceContainer.setup((c) => c.get(ICommandManager)).returns(() => commandManager.object); + serviceContainer.setup((c) => c.get(IWorkspaceService)).returns(() => workspace.object); + serviceContainer.setup((c) => c.get(IActiveResourceService)).returns(() => activeResourceService.object); }); teardown(() => { try { @@ -40,7 +40,7 @@ suite('Terminal Provider', () => { test('Ensure command is registered', () => { terminalProvider = new TerminalProvider(serviceContainer.object); commandManager.verify( - c => + (c) => c.registerCommand(TypeMoq.It.isValue(Commands.Create_Terminal), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once() ); @@ -49,7 +49,7 @@ suite('Terminal Provider', () => { test('Ensure command handler is disposed', () => { const disposable = TypeMoq.Mock.ofType(); commandManager - .setup(c => + .setup((c) => c.registerCommand(TypeMoq.It.isValue(Commands.Create_Terminal), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => disposable.object); @@ -57,14 +57,14 @@ suite('Terminal Provider', () => { terminalProvider = new TerminalProvider(serviceContainer.object); terminalProvider.dispose(); - disposable.verify(d => d.dispose(), TypeMoq.Times.once()); + disposable.verify((d) => d.dispose(), TypeMoq.Times.once()); }); test('Ensure terminal is created and displayed when command is invoked', () => { const disposable = TypeMoq.Mock.ofType(); let commandHandler: undefined | (() => void); commandManager - .setup(c => + .setup((c) => c.registerCommand(TypeMoq.It.isValue(Commands.Create_Terminal), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns((_cmd, callback) => { @@ -72,26 +72,26 @@ suite('Terminal Provider', () => { return disposable.object; }); activeResourceService - .setup(a => a.getActiveResource()) + .setup((a) => a.getActiveResource()) .returns(() => resource) .verifiable(TypeMoq.Times.once()); - workspace.setup(w => w.workspaceFolders).returns(() => undefined); + workspace.setup((w) => w.workspaceFolders).returns(() => undefined); terminalProvider = new TerminalProvider(serviceContainer.object); expect(commandHandler).not.to.be.equal(undefined, 'Handler not set'); const terminalServiceFactory = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ITerminalServiceFactory))) + .setup((c) => c.get(TypeMoq.It.isValue(ITerminalServiceFactory))) .returns(() => terminalServiceFactory.object); const terminalService = TypeMoq.Mock.ofType(); terminalServiceFactory - .setup(t => t.createTerminalService(TypeMoq.It.isValue(resource), TypeMoq.It.isValue('Python'))) + .setup((t) => t.createTerminalService(TypeMoq.It.isValue(resource), TypeMoq.It.isValue('Python'))) .returns(() => terminalService.object); commandHandler!.call(terminalProvider); activeResourceService.verifyAll(); - terminalService.verify(t => t.show(false), TypeMoq.Times.once()); + terminalService.verify((t) => t.show(false), TypeMoq.Times.once()); }); // tslint:disable-next-line: max-func-body-length @@ -105,33 +105,33 @@ suite('Terminal Provider', () => { setup(() => { configService = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); pythonSettings = TypeMoq.Mock.ofType(); activeResourceService = TypeMoq.Mock.ofType(); terminalSettings = TypeMoq.Mock.ofType(); - pythonSettings.setup(s => s.terminal).returns(() => terminalSettings.object); + pythonSettings.setup((s) => s.terminal).returns(() => terminalSettings.object); terminalActivator = TypeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ITerminalActivator))) + .setup((c) => c.get(TypeMoq.It.isValue(ITerminalActivator))) .returns(() => terminalActivator.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IActiveResourceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IActiveResourceService))) .returns(() => activeResourceService.object); terminal = TypeMoq.Mock.ofType(); }); test('If terminal.activateCurrentTerminal setting is set, provided terminal should be activated', async () => { - terminalSettings.setup(t => t.activateEnvInCurrentTerminal).returns(() => true); + terminalSettings.setup((t) => t.activateEnvInCurrentTerminal).returns(() => true); configService - .setup(c => c.getSettings(resource)) + .setup((c) => c.getSettings(resource)) .returns(() => pythonSettings.object) .verifiable(TypeMoq.Times.once()); activeResourceService - .setup(a => a.getActiveResource()) + .setup((a) => a.getActiveResource()) .returns(() => resource) .verifiable(TypeMoq.Times.once()); @@ -139,7 +139,7 @@ suite('Terminal Provider', () => { await terminalProvider.initialize(terminal.object); terminalActivator.verify( - a => a.activateEnvironmentInTerminal(terminal.object, TypeMoq.It.isAny()), + (a) => a.activateEnvironmentInTerminal(terminal.object, TypeMoq.It.isAny()), TypeMoq.Times.once() ); configService.verifyAll(); @@ -147,13 +147,13 @@ suite('Terminal Provider', () => { }); test('If terminal.activateCurrentTerminal setting is not set, provided terminal should not be activated', async () => { - terminalSettings.setup(t => t.activateEnvInCurrentTerminal).returns(() => false); + terminalSettings.setup((t) => t.activateEnvInCurrentTerminal).returns(() => false); configService - .setup(c => c.getSettings(resource)) + .setup((c) => c.getSettings(resource)) .returns(() => pythonSettings.object) .verifiable(TypeMoq.Times.once()); activeResourceService - .setup(a => a.getActiveResource()) + .setup((a) => a.getActiveResource()) .returns(() => resource) .verifiable(TypeMoq.Times.once()); @@ -161,7 +161,7 @@ suite('Terminal Provider', () => { await terminalProvider.initialize(terminal.object); terminalActivator.verify( - a => a.activateEnvironmentInTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (a) => a.activateEnvironmentInTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); activeResourceService.verifyAll(); @@ -169,13 +169,13 @@ suite('Terminal Provider', () => { }); test('terminal.activateCurrentTerminal setting is set but provided terminal is undefined', async () => { - terminalSettings.setup(t => t.activateEnvInCurrentTerminal).returns(() => true); + terminalSettings.setup((t) => t.activateEnvInCurrentTerminal).returns(() => true); configService - .setup(c => c.getSettings(resource)) + .setup((c) => c.getSettings(resource)) .returns(() => pythonSettings.object) .verifiable(TypeMoq.Times.once()); activeResourceService - .setup(a => a.getActiveResource()) + .setup((a) => a.getActiveResource()) .returns(() => resource) .verifiable(TypeMoq.Times.once()); @@ -183,7 +183,7 @@ suite('Terminal Provider', () => { await terminalProvider.initialize(undefined); terminalActivator.verify( - a => a.activateEnvironmentInTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny()), + (a) => a.activateEnvironmentInTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); activeResourceService.verifyAll(); @@ -191,9 +191,9 @@ suite('Terminal Provider', () => { }); test('Exceptions are swallowed if initializing terminal provider fails', async () => { - terminalSettings.setup(t => t.activateEnvInCurrentTerminal).returns(() => true); - configService.setup(c => c.getSettings(resource)).throws(new Error('Kaboom')); - activeResourceService.setup(a => a.getActiveResource()).returns(() => resource); + terminalSettings.setup((t) => t.activateEnvInCurrentTerminal).returns(() => true); + configService.setup((c) => c.getSettings(resource)).throws(new Error('Kaboom')); + activeResourceService.setup((a) => a.getActiveResource()).returns(() => resource); terminalProvider = new TerminalProvider(serviceContainer.object); try { diff --git a/src/test/refactor/extension.refactor.extract.method.test.ts b/src/test/refactor/extension.refactor.extract.method.test.ts index 2baed2fc4728..3e3e64d2f7ee 100644 --- a/src/test/refactor/extension.refactor.extract.method.test.ts +++ b/src/test/refactor/extension.refactor.extract.method.test.ts @@ -126,9 +126,9 @@ suite('Method Extraction', () => { const textEdits = getTextEditsFromPatch(mockTextDoc.getText(), DIFF); assert.equal(response.results.length, 1, 'Invalid number of items in response'); assert.equal(textEdits.length, expectedTextEdits.length, 'Invalid number of Text Edits'); - textEdits.forEach(edit => { + textEdits.forEach((edit) => { const foundEdit = expectedTextEdits.filter( - item => item.newText === edit.newText && item.range.isEqual(edit.range) + (item) => item.newText === edit.newText && item.range.isEqual(edit.range) ); assert.equal(foundEdit.length, 1, 'Edit not found'); }); diff --git a/src/test/refactor/extension.refactor.extract.var.test.ts b/src/test/refactor/extension.refactor.extract.var.test.ts index 89d55723428b..4f082824cc5b 100644 --- a/src/test/refactor/extension.refactor.extract.var.test.ts +++ b/src/test/refactor/extension.refactor.extract.var.test.ts @@ -127,9 +127,9 @@ suite('Variable Extraction', () => { const textEdits = getTextEditsFromPatch(mockTextDoc.getText(), DIFF); assert.equal(response.results.length, 1, 'Invalid number of items in response'); assert.equal(textEdits.length, expectedTextEdits.length, 'Invalid number of Text Edits'); - textEdits.forEach(edit => { + textEdits.forEach((edit) => { const foundEdit = expectedTextEdits.filter( - item => item.newText === edit.newText && item.range.isEqual(edit.range) + (item) => item.newText === edit.newText && item.range.isEqual(edit.range) ); assert.equal(foundEdit.length, 1, 'Edit not found'); }); @@ -141,7 +141,7 @@ suite('Variable Extraction', () => { } // tslint:disable-next-line:no-function-expression - test('Extract Variable', async function() { + test('Extract Variable', async function () { if (isPythonVersion('3.7')) { // tslint:disable-next-line:no-invalid-this return this.skip(); diff --git a/src/test/refactor/rename.test.ts b/src/test/refactor/rename.test.ts index a0f380e685b7..fdd5a1604c68 100644 --- a/src/test/refactor/rename.test.ts +++ b/src/test/refactor/rename.test.ts @@ -43,42 +43,44 @@ suite('Refactor Rename', () => { suiteSetup(initialize); setup(async () => { pythonSettings = typeMoq.Mock.ofType(); - pythonSettings.setup(p => p.pythonPath).returns(() => PYTHON_PATH); + pythonSettings.setup((p) => p.pythonPath).returns(() => PYTHON_PATH); const configService = typeMoq.Mock.ofType(); - configService.setup(c => c.getSettings(typeMoq.It.isAny())).returns(() => pythonSettings.object); + configService.setup((c) => c.getSettings(typeMoq.It.isAny())).returns(() => pythonSettings.object); const condaService = typeMoq.Mock.ofType(); const processServiceFactory = typeMoq.Mock.ofType(); processServiceFactory - .setup(p => p.create(typeMoq.It.isAny())) + .setup((p) => p.create(typeMoq.It.isAny())) .returns(() => Promise.resolve(new ProcessService(new BufferDecoder()))); const interpreterService = typeMoq.Mock.ofType(); - interpreterService.setup(i => i.hasInterpreters).returns(() => Promise.resolve(true)); + interpreterService.setup((i) => i.hasInterpreters).returns(() => Promise.resolve(true)); const envActivationService = typeMoq.Mock.ofType(); envActivationService - .setup(e => e.getActivatedEnvironmentVariables(typeMoq.It.isAny())) + .setup((e) => e.getActivatedEnvironmentVariables(typeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); envActivationService - .setup(e => e.getActivatedEnvironmentVariables(typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((e) => e.getActivatedEnvironmentVariables(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)); envActivationService - .setup(e => e.getActivatedEnvironmentVariables(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((e) => + e.getActivatedEnvironmentVariables(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny()) + ) .returns(() => Promise.resolve(undefined)); serviceContainer = typeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IConfigurationService), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IConfigurationService), typeMoq.It.isAny())) .returns(() => configService.object); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IProcessServiceFactory), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IProcessServiceFactory), typeMoq.It.isAny())) .returns(() => processServiceFactory.object); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IInterpreterService), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IInterpreterService), typeMoq.It.isAny())) .returns(() => interpreterService.object); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IEnvironmentActivationService), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IEnvironmentActivationService), typeMoq.It.isAny())) .returns(() => envActivationService.object); const windowsStoreInterpreter = mock(WindowsStoreInterpreter); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IPythonExecutionFactory), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IPythonExecutionFactory), typeMoq.It.isAny())) .returns( () => new PythonExecutionFactory( @@ -93,12 +95,12 @@ suite('Refactor Rename', () => { ); const processLogger = typeMoq.Mock.ofType(); processLogger - .setup(p => p.logProcess(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((p) => p.logProcess(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny())) .returns(() => { return; }); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IProcessLogger), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IProcessLogger), typeMoq.It.isAny())) .returns(() => processLogger.object); await initializeTest(); }); diff --git a/src/test/serviceRegistry.ts b/src/test/serviceRegistry.ts index 280538257167..f9f86f9f38a9 100644 --- a/src/test/serviceRegistry.ts +++ b/src/test/serviceRegistry.ts @@ -127,13 +127,13 @@ class FakeVSCodeFileSystemAPI { } public async readDirectory(uri: Uri): Promise<[string, FileType][]> { const names: string[] = await fsextra.readdir(uri.fsPath); - const promises = names.map(name => { + const promises = names.map((name) => { const filename = path.join(uri.fsPath, name); return ( fsextra // Get the lstat info and deal with symlinks if necessary. .lstat(filename) - .then(async stat => { + .then(async (stat) => { let filetype = FileType.Unknown; if (stat.isFile()) { filetype = FileType.File; @@ -163,13 +163,13 @@ class FakeVSCodeFileSystemAPI { const rs = fsextra // Set an error handler on the stream. .createReadStream(src.fsPath) - .on('error', err => { + .on('error', (err) => { deferred.reject(err); }); const ws = fsextra .createWriteStream(dest.fsPath) // Set an error & close handler on the stream. - .on('error', err => { + .on('error', (err) => { deferred.reject(err); }) .on('close', () => { @@ -306,7 +306,7 @@ export class IocContainer { const processServiceFactory = TypeMoq.Mock.ofType(); // tslint:disable-next-line:no-any const processService = new MockProcessService(new ProcessService(new BufferDecoder(), process.env as any)); - processServiceFactory.setup(f => f.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService)); + processServiceFactory.setup((f) => f.create(TypeMoq.It.isAny())).returns(() => Promise.resolve(processService)); this.serviceManager.addSingletonInstance( IProcessServiceFactory, processServiceFactory.object diff --git a/src/test/smoke/common.ts b/src/test/smoke/common.ts index 45b9e3a43992..da02ff92131d 100644 --- a/src/test/smoke/common.ts +++ b/src/test/smoke/common.ts @@ -21,12 +21,12 @@ export async function updateSetting(setting: string, value: any) { } export async function removeLanguageServerFiles() { const folders = await getLanaguageServerFolders(); - await Promise.all(folders.map(item => fs.remove(item).catch(noop))); + await Promise.all(folders.map((item) => fs.remove(item).catch(noop))); } async function getLanaguageServerFolders(): Promise { return new Promise((resolve, reject) => { glob('languageServer.*', { cwd: SMOKE_TEST_EXTENSIONS_DIR }, (ex, matches) => { - ex ? reject(ex) : resolve(matches.map(item => path.join(SMOKE_TEST_EXTENSIONS_DIR, item))); + ex ? reject(ex) : resolve(matches.map((item) => path.join(SMOKE_TEST_EXTENSIONS_DIR, item))); }); }); } diff --git a/src/test/smoke/datascience.smoke.test.ts b/src/test/smoke/datascience.smoke.test.ts index 5c2650f363f1..0712239654e5 100644 --- a/src/test/smoke/datascience.smoke.test.ts +++ b/src/test/smoke/datascience.smoke.test.ts @@ -16,7 +16,7 @@ import { closeActiveWindows, initialize, initializeTest } from '../initialize'; const timeoutForCellToRun = 3 * 60 * 1_000; suite('Smoke Test: Interactive Window', () => { - suiteSetup(async function() { + suiteSetup(async function () { if (!IS_SMOKE_TEST) { return this.skip(); } diff --git a/src/test/smoke/debugger.smoke.test.ts b/src/test/smoke/debugger.smoke.test.ts index f207bc3fc1a3..4be62b9f7894 100644 --- a/src/test/smoke/debugger.smoke.test.ts +++ b/src/test/smoke/debugger.smoke.test.ts @@ -14,7 +14,7 @@ import { EXTENSION_ROOT_DIR_FOR_TESTS, IS_SMOKE_TEST } from '../constants'; import { closeActiveWindows, initialize, initializeTest } from '../initialize'; suite('Smoke Test: Debug file', () => { - suiteSetup(async function() { + suiteSetup(async function () { if (!IS_SMOKE_TEST) { return this.skip(); } diff --git a/src/test/smoke/languageServer.smoke.test.ts b/src/test/smoke/languageServer.smoke.test.ts index 72a072f78b31..1e21bda47b94 100644 --- a/src/test/smoke/languageServer.smoke.test.ts +++ b/src/test/smoke/languageServer.smoke.test.ts @@ -24,7 +24,7 @@ const fileDefinitions = path.join( ); suite('Smoke Test: Language Server', () => { - suiteSetup(async function() { + suiteSetup(async function () { if (!IS_SMOKE_TEST) { return this.skip(); } diff --git a/src/test/smoke/runInTerminal.smoke.test.ts b/src/test/smoke/runInTerminal.smoke.test.ts index 088ae1a2ac2b..013e6bff396d 100644 --- a/src/test/smoke/runInTerminal.smoke.test.ts +++ b/src/test/smoke/runInTerminal.smoke.test.ts @@ -13,7 +13,7 @@ import { EXTENSION_ROOT_DIR_FOR_TESTS, IS_SMOKE_TEST } from '../constants'; import { closeActiveWindows, initialize, initializeTest } from '../initialize'; suite('Smoke Test: Run Python File In Terminal', () => { - suiteSetup(async function() { + suiteSetup(async function () { if (!IS_SMOKE_TEST) { return this.skip(); } diff --git a/src/test/smokeTest.ts b/src/test/smokeTest.ts index 4310fe800642..815648c22706 100644 --- a/src/test/smokeTest.ts +++ b/src/test/smokeTest.ts @@ -69,7 +69,7 @@ class TestRunner { proc.stdout.pipe(process.stdout); proc.stderr.pipe(process.stderr); proc.on('error', reject); - proc.on('exit', code => { + proc.on('exit', (code) => { console.log(`Tests Exited with code ${code}`); if (code === 0) { resolve(); @@ -88,7 +88,7 @@ class TestRunner { } } -new TestRunner().start().catch(ex => { +new TestRunner().start().catch((ex) => { console.error('Error in running Smoke Tests', ex); // Exit with non zero exit code, so CI fails. process.exit(1); diff --git a/src/test/sourceMapSupport.test.ts b/src/test/sourceMapSupport.test.ts index 323a2ab58881..db7148b20831 100644 --- a/src/test/sourceMapSupport.test.ts +++ b/src/test/sourceMapSupport.test.ts @@ -56,7 +56,7 @@ suite('Source Map Support', () => { const disposables: Disposable[] = []; teardown(() => { - disposables.forEach(disposable => { + disposables.forEach((disposable) => { try { disposable.dispose(); } catch { diff --git a/src/test/sourceMapSupport.unit.test.ts b/src/test/sourceMapSupport.unit.test.ts index 637b711a4edf..437a7463269e 100644 --- a/src/test/sourceMapSupport.unit.test.ts +++ b/src/test/sourceMapSupport.unit.test.ts @@ -60,7 +60,7 @@ suite('Source Map Support', () => { const disposables: Disposable[] = []; teardown(() => { rewiremock.disable(); - disposables.forEach(disposable => { + disposables.forEach((disposable) => { try { disposable.dispose(); } catch { diff --git a/src/test/standardTest.ts b/src/test/standardTest.ts index 8bdeb8db3f74..b728eb10a779 100644 --- a/src/test/standardTest.ts +++ b/src/test/standardTest.ts @@ -22,7 +22,7 @@ function start() { launchArgs: [workspacePath], version: 'stable', extensionTestsEnv: { ...process.env, UITEST_DISABLE_INSIDERS: '1' } - }).catch(ex => { + }).catch((ex) => { console.error('End Standard tests (with errors)', ex); process.exit(1); }); diff --git a/src/test/telemetry/importTracker.unit.test.ts b/src/test/telemetry/importTracker.unit.test.ts index ed05d42a3256..8c702ea11151 100644 --- a/src/test/telemetry/importTracker.unit.test.ts +++ b/src/test/telemetry/importTracker.unit.test.ts @@ -1,313 +1,286 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -//tslint:disable:max-func-body-length match-default-export-name no-any no-multiline-string no-trailing-whitespace -import { expect } from 'chai'; -import rewiremock from 'rewiremock'; -import * as TypeMoq from 'typemoq'; -import { EventEmitter, TextDocument } from 'vscode'; - -import { IDocumentManager } from '../../client/common/application/types'; -import { generateCells } from '../../client/datascience/cellFactory'; -import { INotebookEditor, INotebookEditorProvider, INotebookModel } from '../../client/datascience/types'; -import { EventName } from '../../client/telemetry/constants'; -import { ImportTracker } from '../../client/telemetry/importTracker'; -import { createDocument } from '../datascience/editor-integration/helpers'; - -suite('Import Tracker', () => { - const oldValueOfVSC_PYTHON_UNIT_TEST = process.env.VSC_PYTHON_UNIT_TEST; - const oldValueOfVSC_PYTHON_CI_TEST = process.env.VSC_PYTHON_CI_TEST; - // tslint:disable-next-line:no-require-imports - const hashJs = require('hash.js'); - let importTracker: ImportTracker; - let documentManager: TypeMoq.IMock; - let nativeProvider: TypeMoq.IMock; - let openedEventEmitter: EventEmitter; - let savedEventEmitter: EventEmitter; - let openedNotebookEmitter: EventEmitter; - let closedNotebookEmitter: EventEmitter; - const pandasHash: string = hashJs - .sha256() - .update('pandas') - .digest('hex'); - const elephasHash: string = hashJs - .sha256() - .update('elephas') - .digest('hex'); - const kerasHash: string = hashJs - .sha256() - .update('keras') - .digest('hex'); - const pysparkHash: string = hashJs - .sha256() - .update('pyspark') - .digest('hex'); - const sparkdlHash: string = hashJs - .sha256() - .update('sparkdl') - .digest('hex'); - const numpyHash: string = hashJs - .sha256() - .update('numpy') - .digest('hex'); - const scipyHash: string = hashJs - .sha256() - .update('scipy') - .digest('hex'); - const sklearnHash: string = hashJs - .sha256() - .update('sklearn') - .digest('hex'); - const randomHash: string = hashJs - .sha256() - .update('random') - .digest('hex'); - - class Reporter { - public static eventNames: string[] = []; - public static properties: Record[] = []; - public static measures: {}[] = []; - - public static expectHashes(...hashes: string[]) { - expect(Reporter.eventNames).to.contain(EventName.HASHED_PACKAGE_PERF); - if (hashes.length > 0) { - expect(Reporter.eventNames).to.contain(EventName.HASHED_PACKAGE_NAME); - } - - Reporter.properties.pop(); // HASHED_PACKAGE_PERF - expect(Reporter.properties).to.deep.equal(hashes.map(hash => ({ hashedName: hash }))); - } - - public sendTelemetryEvent(eventName: string, properties?: {}, measures?: {}) { - Reporter.eventNames.push(eventName); - Reporter.properties.push(properties!); - Reporter.measures.push(measures!); - } - } - - setup(() => { - process.env.VSC_PYTHON_UNIT_TEST = undefined; - process.env.VSC_PYTHON_CI_TEST = undefined; - - openedEventEmitter = new EventEmitter(); - savedEventEmitter = new EventEmitter(); - openedNotebookEmitter = new EventEmitter(); - closedNotebookEmitter = new EventEmitter(); - - documentManager = TypeMoq.Mock.ofType(); - documentManager.setup(a => a.onDidOpenTextDocument).returns(() => openedEventEmitter.event); - documentManager.setup(a => a.onDidSaveTextDocument).returns(() => savedEventEmitter.event); - - nativeProvider = TypeMoq.Mock.ofType(); - nativeProvider.setup(n => n.onDidOpenNotebookEditor).returns(() => openedNotebookEmitter.event); - nativeProvider.setup(n => n.onDidCloseNotebookEditor).returns(() => closedNotebookEmitter.event); - nativeProvider.setup(n => n.editors).returns(() => []); - - rewiremock.enable(); - rewiremock('vscode-extension-telemetry').with({ default: Reporter }); - - importTracker = new ImportTracker(documentManager.object, nativeProvider.object); - }); - teardown(() => { - process.env.VSC_PYTHON_UNIT_TEST = oldValueOfVSC_PYTHON_UNIT_TEST; - process.env.VSC_PYTHON_CI_TEST = oldValueOfVSC_PYTHON_CI_TEST; - Reporter.properties = []; - Reporter.eventNames = []; - Reporter.measures = []; - rewiremock.disable(); - }); - - function emitDocEvent(code: string, ev: EventEmitter) { - const textDoc = createDocument(code, 'foo.py', 1, TypeMoq.Times.atMost(100), true); - ev.fire(textDoc.object); - } - - function emitNotebookEvent(code: string, ev: EventEmitter) { - const notebook = TypeMoq.Mock.ofType(); - const model = TypeMoq.Mock.ofType(); - notebook.setup(n => n.model).returns(() => model.object); - model.setup(m => m.cells).returns(() => generateCells(undefined, code, 'foo.py', 0, false, '1')); - ev.fire(notebook.object); - } - - test('Open document', () => { - emitDocEvent('import pandas\r\n', openedEventEmitter); - - Reporter.expectHashes(pandasHash); - }); - - test('Already opened documents', async () => { - const doc = createDocument('import pandas\r\n', 'foo.py', 1, TypeMoq.Times.atMost(100), true); - documentManager.setup(d => d.textDocuments).returns(() => [doc.object]); - await importTracker.activate(); - - Reporter.expectHashes(pandasHash); - }); - - test('Open notebook', () => { - emitNotebookEvent('import pandas\r\n', openedNotebookEmitter); - Reporter.expectHashes(pandasHash); - }); - - test('Close notebook', () => { - emitNotebookEvent('import pandas\r\n', closedNotebookEmitter); - Reporter.expectHashes(pandasHash); - }); - - test('Execute notebook', async () => { - await importTracker.postExecute( - generateCells(undefined, 'import pandas\r\n', 'foo.py', 0, false, '1')[0], - false - ); - Reporter.expectHashes(pandasHash); - }); - - test('Save document', () => { - emitDocEvent('import pandas\r\n', savedEventEmitter); - - Reporter.expectHashes(pandasHash); - }); - - test('from ._ import _, _', () => { - const elephas = ` - from elephas.java import java_classes, adapter - from keras.models import Sequential - from keras.layers import Dense - - - model = Sequential() - model.add(Dense(units=64, activation='relu', input_dim=100)) - model.add(Dense(units=10, activation='softmax')) - model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy']) - - model.save('test.h5') - - - kmi = java_classes.KerasModelImport - file = java_classes.File("test.h5") - - java_model = kmi.importKerasSequentialModelAndWeights(file.absolutePath) - - weights = adapter.retrieve_keras_weights(java_model) - model.set_weights(weights)`; - - emitDocEvent(elephas, savedEventEmitter); - Reporter.expectHashes(elephasHash, kerasHash); - }); - - test('from ._ import _', () => { - const pyspark = `from pyspark.ml.classification import LogisticRegression - from pyspark.ml.evaluation import MulticlassClassificationEvaluator - from pyspark.ml import Pipeline - from sparkdl import DeepImageFeaturizer - - featurizer = DeepImageFeaturizer(inputCol="image", outputCol="features", modelName="InceptionV3") - lr = LogisticRegression(maxIter=20, regParam=0.05, elasticNetParam=0.3, labelCol="label") - p = Pipeline(stages=[featurizer, lr]) - - model = p.fit(train_images_df) # train_images_df is a dataset of images and labels - - # Inspect training error - df = model.transform(train_images_df.limit(10)).select("image", "probability", "uri", "label") - predictionAndLabels = df.select("prediction", "label") - evaluator = MulticlassClassificationEvaluator(metricName="accuracy") - print("Training set accuracy = " + str(evaluator.evaluate(predictionAndLabels)))`; - - emitDocEvent(pyspark, savedEventEmitter); - Reporter.expectHashes(pysparkHash, sparkdlHash); - }); - - test('import as _', () => { - const code = `import pandas as pd -import numpy as np -import random as rnd - -def simplify_ages(df): - df.Age = df.Age.fillna(-0.5) - bins = (-1, 0, 5, 12, 18, 25, 35, 60, 120) - group_names = ['Unknown', 'Baby', 'Child', 'Teenager', 'Student', 'Young Adult', 'Adult', 'Senior'] - categories = pd.cut(df.Age, bins, labels=group_names) - df.Age = categories - return df`; - emitDocEvent(code, savedEventEmitter); - Reporter.expectHashes(pandasHash, numpyHash, randomHash); - }); - - test('from import _', () => { - const code = `from scipy import special -def drumhead_height(n, k, distance, angle, t): - kth_zero = special.jn_zeros(n, k)[-1] - return np.cos(t) * np.cos(n*angle) * special.jn(n, distance*kth_zero) -theta = np.r_[0:2*np.pi:50j] -radius = np.r_[0:1:50j] -x = np.array([r * np.cos(theta) for r in radius]) -y = np.array([r * np.sin(theta) for r in radius]) -z = np.array([drumhead_height(1, 1, r, theta, 0.5) for r in radius])`; - emitDocEvent(code, savedEventEmitter); - Reporter.expectHashes(scipyHash); - }); - - test('from import _ as _', () => { - const code = `from pandas import DataFrame as df`; - emitDocEvent(code, savedEventEmitter); - Reporter.expectHashes(pandasHash); - }); - - test('import , ', () => { - const code = ` -def drumhead_height(n, k, distance, angle, t): - import sklearn, pandas - return np.cos(t) * np.cos(n*angle) * special.jn(n, distance*kth_zero) -theta = np.r_[0:2*np.pi:50j] -radius = np.r_[0:1:50j] -x = np.array([r * np.cos(theta) for r in radius]) -y = np.array([r * np.sin(theta) for r in radius]) -z = np.array([drumhead_height(1, 1, r, theta, 0.5) for r in radius])`; - emitDocEvent(code, savedEventEmitter); - Reporter.expectHashes(sklearnHash, pandasHash); - }); - - test('Import from within a function', () => { - const code = ` -def drumhead_height(n, k, distance, angle, t): - import sklearn as sk - return np.cos(t) * np.cos(n*angle) * special.jn(n, distance*kth_zero) -theta = np.r_[0:2*np.pi:50j] -radius = np.r_[0:1:50j] -x = np.array([r * np.cos(theta) for r in radius]) -y = np.array([r * np.sin(theta) for r in radius]) -z = np.array([drumhead_height(1, 1, r, theta, 0.5) for r in radius])`; - emitDocEvent(code, savedEventEmitter); - Reporter.expectHashes(sklearnHash); - }); - - test('Do not send the same package twice', () => { - const code = ` -import pandas -import pandas`; - emitDocEvent(code, savedEventEmitter); - Reporter.expectHashes(pandasHash); - }); - - test('Ignore relative imports', () => { - const code = 'from .pandas import not_real'; - emitDocEvent(code, savedEventEmitter); - Reporter.expectHashes(); - }); - - test('Ignore docstring for `from` imports', () => { - const code = `""" -from numpy import the random function -"""`; - emitDocEvent(code, savedEventEmitter); - Reporter.expectHashes(); - }); - - test('Ignore docstring for `import` imports', () => { - const code = `""" -import numpy for all the things -"""`; - emitDocEvent(code, savedEventEmitter); - Reporter.expectHashes(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +//tslint:disable:max-func-body-length match-default-export-name no-any no-multiline-string no-trailing-whitespace +import { expect } from 'chai'; +import rewiremock from 'rewiremock'; +import * as TypeMoq from 'typemoq'; +import { EventEmitter, TextDocument } from 'vscode'; + +import { IDocumentManager } from '../../client/common/application/types'; +import { generateCells } from '../../client/datascience/cellFactory'; +import { INotebookEditor, INotebookEditorProvider, INotebookModel } from '../../client/datascience/types'; +import { EventName } from '../../client/telemetry/constants'; +import { ImportTracker } from '../../client/telemetry/importTracker'; +import { createDocument } from '../datascience/editor-integration/helpers'; + +suite('Import Tracker', () => { + const oldValueOfVSC_PYTHON_UNIT_TEST = process.env.VSC_PYTHON_UNIT_TEST; + const oldValueOfVSC_PYTHON_CI_TEST = process.env.VSC_PYTHON_CI_TEST; + // tslint:disable-next-line:no-require-imports + const hashJs = require('hash.js'); + let importTracker: ImportTracker; + let documentManager: TypeMoq.IMock; + let nativeProvider: TypeMoq.IMock; + let openedEventEmitter: EventEmitter; + let savedEventEmitter: EventEmitter; + let openedNotebookEmitter: EventEmitter; + let closedNotebookEmitter: EventEmitter; + const pandasHash: string = hashJs.sha256().update('pandas').digest('hex'); + const elephasHash: string = hashJs.sha256().update('elephas').digest('hex'); + const kerasHash: string = hashJs.sha256().update('keras').digest('hex'); + const pysparkHash: string = hashJs.sha256().update('pyspark').digest('hex'); + const sparkdlHash: string = hashJs.sha256().update('sparkdl').digest('hex'); + const numpyHash: string = hashJs.sha256().update('numpy').digest('hex'); + const scipyHash: string = hashJs.sha256().update('scipy').digest('hex'); + const sklearnHash: string = hashJs.sha256().update('sklearn').digest('hex'); + const randomHash: string = hashJs.sha256().update('random').digest('hex'); + + class Reporter { + public static eventNames: string[] = []; + public static properties: Record[] = []; + public static measures: {}[] = []; + + public static expectHashes(...hashes: string[]) { + expect(Reporter.eventNames).to.contain(EventName.HASHED_PACKAGE_PERF); + if (hashes.length > 0) { + expect(Reporter.eventNames).to.contain(EventName.HASHED_PACKAGE_NAME); + } + + Reporter.properties.pop(); // HASHED_PACKAGE_PERF + expect(Reporter.properties).to.deep.equal(hashes.map((hash) => ({ hashedName: hash }))); + } + + public sendTelemetryEvent(eventName: string, properties?: {}, measures?: {}) { + Reporter.eventNames.push(eventName); + Reporter.properties.push(properties!); + Reporter.measures.push(measures!); + } + } + + setup(() => { + process.env.VSC_PYTHON_UNIT_TEST = undefined; + process.env.VSC_PYTHON_CI_TEST = undefined; + + openedEventEmitter = new EventEmitter(); + savedEventEmitter = new EventEmitter(); + openedNotebookEmitter = new EventEmitter(); + closedNotebookEmitter = new EventEmitter(); + + documentManager = TypeMoq.Mock.ofType(); + documentManager.setup((a) => a.onDidOpenTextDocument).returns(() => openedEventEmitter.event); + documentManager.setup((a) => a.onDidSaveTextDocument).returns(() => savedEventEmitter.event); + + nativeProvider = TypeMoq.Mock.ofType(); + nativeProvider.setup((n) => n.onDidOpenNotebookEditor).returns(() => openedNotebookEmitter.event); + nativeProvider.setup((n) => n.onDidCloseNotebookEditor).returns(() => closedNotebookEmitter.event); + nativeProvider.setup((n) => n.editors).returns(() => []); + + rewiremock.enable(); + rewiremock('vscode-extension-telemetry').with({ default: Reporter }); + + importTracker = new ImportTracker(documentManager.object, nativeProvider.object); + }); + teardown(() => { + process.env.VSC_PYTHON_UNIT_TEST = oldValueOfVSC_PYTHON_UNIT_TEST; + process.env.VSC_PYTHON_CI_TEST = oldValueOfVSC_PYTHON_CI_TEST; + Reporter.properties = []; + Reporter.eventNames = []; + Reporter.measures = []; + rewiremock.disable(); + }); + + function emitDocEvent(code: string, ev: EventEmitter) { + const textDoc = createDocument(code, 'foo.py', 1, TypeMoq.Times.atMost(100), true); + ev.fire(textDoc.object); + } + + function emitNotebookEvent(code: string, ev: EventEmitter) { + const notebook = TypeMoq.Mock.ofType(); + const model = TypeMoq.Mock.ofType(); + notebook.setup((n) => n.model).returns(() => model.object); + model.setup((m) => m.cells).returns(() => generateCells(undefined, code, 'foo.py', 0, false, '1')); + ev.fire(notebook.object); + } + + test('Open document', () => { + emitDocEvent('import pandas\r\n', openedEventEmitter); + + Reporter.expectHashes(pandasHash); + }); + + test('Already opened documents', async () => { + const doc = createDocument('import pandas\r\n', 'foo.py', 1, TypeMoq.Times.atMost(100), true); + documentManager.setup((d) => d.textDocuments).returns(() => [doc.object]); + await importTracker.activate(); + + Reporter.expectHashes(pandasHash); + }); + + test('Open notebook', () => { + emitNotebookEvent('import pandas\r\n', openedNotebookEmitter); + Reporter.expectHashes(pandasHash); + }); + + test('Close notebook', () => { + emitNotebookEvent('import pandas\r\n', closedNotebookEmitter); + Reporter.expectHashes(pandasHash); + }); + + test('Execute notebook', async () => { + await importTracker.postExecute( + generateCells(undefined, 'import pandas\r\n', 'foo.py', 0, false, '1')[0], + false + ); + Reporter.expectHashes(pandasHash); + }); + + test('Save document', () => { + emitDocEvent('import pandas\r\n', savedEventEmitter); + + Reporter.expectHashes(pandasHash); + }); + + test('from ._ import _, _', () => { + const elephas = ` + from elephas.java import java_classes, adapter + from keras.models import Sequential + from keras.layers import Dense + + + model = Sequential() + model.add(Dense(units=64, activation='relu', input_dim=100)) + model.add(Dense(units=10, activation='softmax')) + model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy']) + + model.save('test.h5') + + + kmi = java_classes.KerasModelImport + file = java_classes.File("test.h5") + + java_model = kmi.importKerasSequentialModelAndWeights(file.absolutePath) + + weights = adapter.retrieve_keras_weights(java_model) + model.set_weights(weights)`; + + emitDocEvent(elephas, savedEventEmitter); + Reporter.expectHashes(elephasHash, kerasHash); + }); + + test('from ._ import _', () => { + const pyspark = `from pyspark.ml.classification import LogisticRegression + from pyspark.ml.evaluation import MulticlassClassificationEvaluator + from pyspark.ml import Pipeline + from sparkdl import DeepImageFeaturizer + + featurizer = DeepImageFeaturizer(inputCol="image", outputCol="features", modelName="InceptionV3") + lr = LogisticRegression(maxIter=20, regParam=0.05, elasticNetParam=0.3, labelCol="label") + p = Pipeline(stages=[featurizer, lr]) + + model = p.fit(train_images_df) # train_images_df is a dataset of images and labels + + # Inspect training error + df = model.transform(train_images_df.limit(10)).select("image", "probability", "uri", "label") + predictionAndLabels = df.select("prediction", "label") + evaluator = MulticlassClassificationEvaluator(metricName="accuracy") + print("Training set accuracy = " + str(evaluator.evaluate(predictionAndLabels)))`; + + emitDocEvent(pyspark, savedEventEmitter); + Reporter.expectHashes(pysparkHash, sparkdlHash); + }); + + test('import as _', () => { + const code = `import pandas as pd +import numpy as np +import random as rnd + +def simplify_ages(df): + df.Age = df.Age.fillna(-0.5) + bins = (-1, 0, 5, 12, 18, 25, 35, 60, 120) + group_names = ['Unknown', 'Baby', 'Child', 'Teenager', 'Student', 'Young Adult', 'Adult', 'Senior'] + categories = pd.cut(df.Age, bins, labels=group_names) + df.Age = categories + return df`; + emitDocEvent(code, savedEventEmitter); + Reporter.expectHashes(pandasHash, numpyHash, randomHash); + }); + + test('from import _', () => { + const code = `from scipy import special +def drumhead_height(n, k, distance, angle, t): + kth_zero = special.jn_zeros(n, k)[-1] + return np.cos(t) * np.cos(n*angle) * special.jn(n, distance*kth_zero) +theta = np.r_[0:2*np.pi:50j] +radius = np.r_[0:1:50j] +x = np.array([r * np.cos(theta) for r in radius]) +y = np.array([r * np.sin(theta) for r in radius]) +z = np.array([drumhead_height(1, 1, r, theta, 0.5) for r in radius])`; + emitDocEvent(code, savedEventEmitter); + Reporter.expectHashes(scipyHash); + }); + + test('from import _ as _', () => { + const code = `from pandas import DataFrame as df`; + emitDocEvent(code, savedEventEmitter); + Reporter.expectHashes(pandasHash); + }); + + test('import , ', () => { + const code = ` +def drumhead_height(n, k, distance, angle, t): + import sklearn, pandas + return np.cos(t) * np.cos(n*angle) * special.jn(n, distance*kth_zero) +theta = np.r_[0:2*np.pi:50j] +radius = np.r_[0:1:50j] +x = np.array([r * np.cos(theta) for r in radius]) +y = np.array([r * np.sin(theta) for r in radius]) +z = np.array([drumhead_height(1, 1, r, theta, 0.5) for r in radius])`; + emitDocEvent(code, savedEventEmitter); + Reporter.expectHashes(sklearnHash, pandasHash); + }); + + test('Import from within a function', () => { + const code = ` +def drumhead_height(n, k, distance, angle, t): + import sklearn as sk + return np.cos(t) * np.cos(n*angle) * special.jn(n, distance*kth_zero) +theta = np.r_[0:2*np.pi:50j] +radius = np.r_[0:1:50j] +x = np.array([r * np.cos(theta) for r in radius]) +y = np.array([r * np.sin(theta) for r in radius]) +z = np.array([drumhead_height(1, 1, r, theta, 0.5) for r in radius])`; + emitDocEvent(code, savedEventEmitter); + Reporter.expectHashes(sklearnHash); + }); + + test('Do not send the same package twice', () => { + const code = ` +import pandas +import pandas`; + emitDocEvent(code, savedEventEmitter); + Reporter.expectHashes(pandasHash); + }); + + test('Ignore relative imports', () => { + const code = 'from .pandas import not_real'; + emitDocEvent(code, savedEventEmitter); + Reporter.expectHashes(); + }); + + test('Ignore docstring for `from` imports', () => { + const code = `""" +from numpy import the random function +"""`; + emitDocEvent(code, savedEventEmitter); + Reporter.expectHashes(); + }); + + test('Ignore docstring for `import` imports', () => { + const code = `""" +import numpy for all the things +"""`; + emitDocEvent(code, savedEventEmitter); + Reporter.expectHashes(); + }); +}); diff --git a/src/test/telemetry/index.unit.test.ts b/src/test/telemetry/index.unit.test.ts index 452d80af241b..aad20386af82 100644 --- a/src/test/telemetry/index.unit.test.ts +++ b/src/test/telemetry/index.unit.test.ts @@ -62,12 +62,12 @@ suite('Telemetry', () => { ]; suite('Function isTelemetryDisabled()', () => { - testsForisTelemetryDisabled.forEach(testParams => { + testsForisTelemetryDisabled.forEach((testParams) => { test(testParams.testName, async () => { const workspaceConfig = TypeMoq.Mock.ofType(); when(workspaceService.getConfiguration('telemetry')).thenReturn(workspaceConfig.object); workspaceConfig - .setup(c => c.inspect('enableTelemetry')) + .setup((c) => c.inspect('enableTelemetry')) .returns(() => testParams.settings as any) .verifiable(TypeMoq.Times.once()); diff --git a/src/test/terminals/activation.unit.test.ts b/src/test/terminals/activation.unit.test.ts index 514f47222ade..3c8f5a075698 100644 --- a/src/test/terminals/activation.unit.test.ts +++ b/src/test/terminals/activation.unit.test.ts @@ -89,7 +89,7 @@ suite('Terminal', () => { commands = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); extensions = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); extensionsChangeEvent = new EventEmitter(); - extensions.setup(e => e.onDidChange).returns(() => extensionsChangeEvent.event); + extensions.setup((e) => e.onDidChange).returns(() => extensionsChangeEvent.event); }); teardown(() => { @@ -105,17 +105,17 @@ suite('Terminal', () => { // tslint:disable-next-line:no-any const extension = TypeMoq.Mock.ofType>(undefined, TypeMoq.MockBehavior.Strict); extensions - .setup(e => e.getExtension(CODE_RUNNER_EXTENSION_ID)) + .setup((e) => e.getExtension(CODE_RUNNER_EXTENSION_ID)) .returns(() => extension.object) .verifiable(TypeMoq.Times.once()); activation = new ExtensionActivationForTerminalActivation(commands.object, extensions.object, []); commands - .setup(c => c.executeCommand('setContext', 'python.showPlayIcon', true)) + .setup((c) => c.executeCommand('setContext', 'python.showPlayIcon', true)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); commands - .setup(c => c.executeCommand('setContext', 'python.showPlayIcon', false)) + .setup((c) => c.executeCommand('setContext', 'python.showPlayIcon', false)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); @@ -126,17 +126,17 @@ suite('Terminal', () => { test('If code runner extension is not installed, show the play icon', async () => { extensions - .setup(e => e.getExtension(CODE_RUNNER_EXTENSION_ID)) + .setup((e) => e.getExtension(CODE_RUNNER_EXTENSION_ID)) .returns(() => undefined) .verifiable(TypeMoq.Times.once()); activation = new ExtensionActivationForTerminalActivation(commands.object, extensions.object, []); commands - .setup(c => c.executeCommand('setContext', 'python.showPlayIcon', true)) + .setup((c) => c.executeCommand('setContext', 'python.showPlayIcon', true)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.once()); commands - .setup(c => c.executeCommand('setContext', 'python.showPlayIcon', false)) + .setup((c) => c.executeCommand('setContext', 'python.showPlayIcon', false)) .returns(() => Promise.resolve()) .verifiable(TypeMoq.Times.never()); diff --git a/src/test/terminals/codeExecution/codeExecutionManager.unit.test.ts b/src/test/terminals/codeExecution/codeExecutionManager.unit.test.ts index 91f800b366a9..1765fa1e8d1b 100644 --- a/src/test/terminals/codeExecution/codeExecutionManager.unit.test.ts +++ b/src/test/terminals/codeExecution/codeExecutionManager.unit.test.ts @@ -24,16 +24,16 @@ suite('Terminal - Code Execution Manager', () => { let fileSystem: TypeMoq.IMock; setup(() => { fileSystem = TypeMoq.Mock.ofType(); - fileSystem.setup(f => f.readFile(TypeMoq.It.isAny())).returns(() => Promise.resolve('')); + fileSystem.setup((f) => f.readFile(TypeMoq.It.isAny())).returns(() => Promise.resolve('')); workspace = TypeMoq.Mock.ofType(); shiftEnterBanner = TypeMoq.Mock.ofType(); shiftEnterBanner - .setup(b => b.showBanner()) + .setup((b) => b.showBanner()) .returns(() => { return Promise.resolve(); }); workspace - .setup(c => c.onDidChangeWorkspaceFolders(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((c) => c.onDidChangeWorkspaceFolders(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => { return { dispose: () => void 0 @@ -52,7 +52,7 @@ suite('Terminal - Code Execution Manager', () => { ); }); teardown(() => { - disposables.forEach(disposable => { + disposables.forEach((disposable) => { if (disposable) { disposable.dispose(); } @@ -64,7 +64,7 @@ suite('Terminal - Code Execution Manager', () => { test('Ensure commands are registered', async () => { const registered: string[] = []; commandManager - .setup(c => c.registerCommand) + .setup((c) => c.registerCommand) .returns(() => { return (command: string, _callback: (...args: any[]) => any, _thisArg?: any) => { registered.push(command); @@ -86,7 +86,7 @@ suite('Terminal - Code Execution Manager', () => { test('Ensure executeFileInterTerminal will do nothing if no file is avialble', async () => { let commandHandler: undefined | (() => Promise); commandManager - .setup(c => c.registerCommand as any) + .setup((c) => c.registerCommand as any) .returns(() => { return (command: string, callback: (...args: any[]) => any, _thisArg?: any) => { if (command === Commands.Exec_In_Terminal) { @@ -100,16 +100,16 @@ suite('Terminal - Code Execution Manager', () => { expect(commandHandler).not.to.be.an('undefined', 'Command handler not initialized'); const helper = TypeMoq.Mock.ofType(); - serviceContainer.setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); + serviceContainer.setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); await commandHandler!(); - helper.verify(async h => h.getFileToExecute(), TypeMoq.Times.once()); + helper.verify(async (h) => h.getFileToExecute(), TypeMoq.Times.once()); }); test('Ensure executeFileInterTerminal will use provided file', async () => { let commandHandler: undefined | ((file: Uri) => Promise); commandManager - .setup(c => c.registerCommand as any) + .setup((c) => c.registerCommand as any) .returns(() => { return (command: string, callback: (...args: any[]) => any, _thisArg?: any) => { if (command === Commands.Exec_In_Terminal) { @@ -123,23 +123,23 @@ suite('Terminal - Code Execution Manager', () => { expect(commandHandler).not.to.be.an('undefined', 'Command handler not initialized'); const helper = TypeMoq.Mock.ofType(); - serviceContainer.setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); + serviceContainer.setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); const executionService = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue('standard'))) + .setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue('standard'))) .returns(() => executionService.object); const fileToExecute = Uri.file('x'); await commandHandler!(fileToExecute); - helper.verify(async h => h.getFileToExecute(), TypeMoq.Times.never()); - executionService.verify(async e => e.executeFile(TypeMoq.It.isValue(fileToExecute)), TypeMoq.Times.once()); + helper.verify(async (h) => h.getFileToExecute(), TypeMoq.Times.never()); + executionService.verify(async (e) => e.executeFile(TypeMoq.It.isValue(fileToExecute)), TypeMoq.Times.once()); }); test('Ensure executeFileInterTerminal will use active file', async () => { let commandHandler: undefined | ((file: Uri) => Promise); commandManager - .setup(c => c.registerCommand as any) + .setup((c) => c.registerCommand as any) .returns(() => { return (command: string, callback: (...args: any[]) => any, _thisArg?: any) => { if (command === Commands.Exec_In_Terminal) { @@ -154,21 +154,21 @@ suite('Terminal - Code Execution Manager', () => { const fileToExecute = Uri.file('x'); const helper = TypeMoq.Mock.ofType(); - serviceContainer.setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); - helper.setup(async h => h.getFileToExecute()).returns(() => Promise.resolve(fileToExecute)); + serviceContainer.setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); + helper.setup(async (h) => h.getFileToExecute()).returns(() => Promise.resolve(fileToExecute)); const executionService = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue('standard'))) + .setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue('standard'))) .returns(() => executionService.object); await commandHandler!(fileToExecute); - executionService.verify(async e => e.executeFile(TypeMoq.It.isValue(fileToExecute)), TypeMoq.Times.once()); + executionService.verify(async (e) => e.executeFile(TypeMoq.It.isValue(fileToExecute)), TypeMoq.Times.once()); }); async function testExecutionOfSelectionWithoutAnyActiveDocument(commandId: string, executionSericeId: string) { let commandHandler: undefined | (() => Promise); commandManager - .setup(c => c.registerCommand as any) + .setup((c) => c.registerCommand as any) .returns(() => { return (command: string, callback: (...args: any[]) => any, _thisArg?: any) => { if (command === commandId) { @@ -182,15 +182,15 @@ suite('Terminal - Code Execution Manager', () => { expect(commandHandler).not.to.be.an('undefined', 'Command handler not initialized'); const helper = TypeMoq.Mock.ofType(); - serviceContainer.setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); + serviceContainer.setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); const executionService = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue(executionSericeId))) + .setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue(executionSericeId))) .returns(() => executionService.object); - documentManager.setup(d => d.activeTextEditor).returns(() => undefined); + documentManager.setup((d) => d.activeTextEditor).returns(() => undefined); await commandHandler!(); - executionService.verify(async e => e.execute(TypeMoq.It.isAny()), TypeMoq.Times.never()); + executionService.verify(async (e) => e.execute(TypeMoq.It.isAny()), TypeMoq.Times.never()); } test('Ensure executeSelectionInTerminal will do nothing if theres no active document', async () => { @@ -204,7 +204,7 @@ suite('Terminal - Code Execution Manager', () => { async function testExecutionOfSlectionWithoutAnythingSelected(commandId: string, executionServiceId: string) { let commandHandler: undefined | (() => Promise); commandManager - .setup(c => c.registerCommand as any) + .setup((c) => c.registerCommand as any) .returns(() => { return (command: string, callback: (...args: any[]) => any, _thisArg?: any) => { if (command === commandId) { @@ -218,20 +218,20 @@ suite('Terminal - Code Execution Manager', () => { expect(commandHandler).not.to.be.an('undefined', 'Command handler not initialized'); const helper = TypeMoq.Mock.ofType(); - serviceContainer.setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); - helper.setup(h => h.getSelectedTextToExecute).returns(() => () => Promise.resolve('')); + serviceContainer.setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); + helper.setup((h) => h.getSelectedTextToExecute).returns(() => () => Promise.resolve('')); const executionService = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue(executionServiceId))) + .setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue(executionServiceId))) .returns(() => executionService.object); documentManager - .setup(d => d.activeTextEditor) + .setup((d) => d.activeTextEditor) .returns(() => { return {} as any; }); await commandHandler!(); - executionService.verify(async e => e.execute(TypeMoq.It.isAny()), TypeMoq.Times.never()); + executionService.verify(async (e) => e.execute(TypeMoq.It.isAny()), TypeMoq.Times.never()); } test('Ensure executeSelectionInTerminal will do nothing if no text is selected', async () => { @@ -245,7 +245,7 @@ suite('Terminal - Code Execution Manager', () => { async function testExecutionOfSelectionIsSentToTerminal(commandId: string, executionServiceId: string) { let commandHandler: undefined | (() => Promise); commandManager - .setup(c => c.registerCommand as any) + .setup((c) => c.registerCommand as any) .returns(() => { return (command: string, callback: (...args: any[]) => any, _thisArg?: any) => { if (command === commandId) { @@ -261,25 +261,25 @@ suite('Terminal - Code Execution Manager', () => { const textSelected = 'abcd'; const activeDocumentUri = Uri.file('abc'); const helper = TypeMoq.Mock.ofType(); - serviceContainer.setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); - helper.setup(h => h.getSelectedTextToExecute).returns(() => () => Promise.resolve(textSelected)); + serviceContainer.setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionHelper))).returns(() => helper.object); + helper.setup((h) => h.getSelectedTextToExecute).returns(() => () => Promise.resolve(textSelected)); helper - .setup(h => h.normalizeLines) + .setup((h) => h.normalizeLines) .returns(() => () => Promise.resolve(textSelected)) .verifiable(TypeMoq.Times.once()); const executionService = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue(executionServiceId))) + .setup((s) => s.get(TypeMoq.It.isValue(ICodeExecutionService), TypeMoq.It.isValue(executionServiceId))) .returns(() => executionService.object); const document = TypeMoq.Mock.ofType(); - document.setup(d => d.uri).returns(() => activeDocumentUri); + document.setup((d) => d.uri).returns(() => activeDocumentUri); const activeEditor = TypeMoq.Mock.ofType(); - activeEditor.setup(e => e.document).returns(() => document.object); - documentManager.setup(d => d.activeTextEditor).returns(() => activeEditor.object); + activeEditor.setup((e) => e.document).returns(() => document.object); + documentManager.setup((d) => d.activeTextEditor).returns(() => activeEditor.object); await commandHandler!(); executionService.verify( - async e => e.execute(TypeMoq.It.isValue(textSelected), TypeMoq.It.isValue(activeDocumentUri)), + async (e) => e.execute(TypeMoq.It.isValue(textSelected), TypeMoq.It.isValue(activeDocumentUri)), TypeMoq.Times.once() ); helper.verifyAll(); diff --git a/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts b/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts index b769484c6209..4e759faa7ab7 100644 --- a/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts +++ b/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts @@ -35,7 +35,7 @@ suite('Terminal - Django Shell Code Execution', () => { const configService = TypeMoq.Mock.ofType(); workspace = TypeMoq.Mock.ofType(); workspace - .setup(c => c.onDidChangeWorkspaceFolders(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((c) => c.onDidChangeWorkspaceFolders(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => { return { dispose: () => void 0 @@ -57,14 +57,14 @@ suite('Terminal - Django Shell Code Execution', () => { disposables ); - terminalFactory.setup(f => f.getTerminalService(TypeMoq.It.isAny())).returns(() => terminalService.object); + terminalFactory.setup((f) => f.getTerminalService(TypeMoq.It.isAny())).returns(() => terminalService.object); settings = TypeMoq.Mock.ofType(); - settings.setup(s => s.terminal).returns(() => terminalSettings.object); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + settings.setup((s) => s.terminal).returns(() => terminalSettings.object); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); }); teardown(() => { - disposables.forEach(disposable => { + disposables.forEach((disposable) => { if (disposable) { disposable.dispose(); } @@ -81,9 +81,9 @@ suite('Terminal - Django Shell Code Execution', () => { expectedTerminalArgs: string[], resource?: Uri ) { - platform.setup(p => p.isWindows).returns(() => isWindows); - settings.setup(s => s.pythonPath).returns(() => pythonPath); - terminalSettings.setup(t => t.launchArgs).returns(() => terminalArgs); + platform.setup((p) => p.isWindows).returns(() => isWindows); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs); const replCommandArgs = await (executor as DjangoShellCodeExecutionProvider).getExecutableInfo(resource); expect(replCommandArgs).not.to.be.an('undefined', 'Command args is undefined'); @@ -142,7 +142,7 @@ suite('Terminal - Django Shell Code Execution', () => { const terminalArgs = ['-a', 'b', 'c']; const workspaceUri = Uri.file(path.join('c', 'usr', 'program files')); const workspaceFolder: WorkspaceFolder = { index: 0, name: 'blah', uri: workspaceUri }; - workspace.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder); + workspace.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder); const expectedTerminalArgs = terminalArgs.concat( `${path.join(workspaceUri.fsPath, 'manage.py').fileToCommandArgument()}`, 'shell' @@ -156,7 +156,7 @@ suite('Terminal - Django Shell Code Execution', () => { const terminalArgs = ['-a', 'b', 'c']; const workspaceUri = Uri.file(path.join('c', 'usr', 'programfiles')); const workspaceFolder: WorkspaceFolder = { index: 0, name: 'blah', uri: workspaceUri }; - workspace.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder); + workspace.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder); const expectedTerminalArgs = terminalArgs.concat( path.join(workspaceUri.fsPath, 'manage.py').fileToCommandArgument(), 'shell' @@ -170,8 +170,8 @@ suite('Terminal - Django Shell Code Execution', () => { const terminalArgs = ['-a', 'b', 'c']; const workspaceUri = Uri.file(path.join('c', 'usr', 'program files')); const workspaceFolder: WorkspaceFolder = { index: 0, name: 'blah', uri: workspaceUri }; - workspace.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); - workspace.setup(w => w.workspaceFolders).returns(() => [workspaceFolder]); + workspace.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); + workspace.setup((w) => w.workspaceFolders).returns(() => [workspaceFolder]); const expectedTerminalArgs = terminalArgs.concat( `${path.join(workspaceUri.fsPath, 'manage.py').fileToCommandArgument()}`, 'shell' @@ -185,8 +185,8 @@ suite('Terminal - Django Shell Code Execution', () => { const terminalArgs = ['-a', 'b', 'c']; const workspaceUri = Uri.file(path.join('c', 'usr', 'programfiles')); const workspaceFolder: WorkspaceFolder = { index: 0, name: 'blah', uri: workspaceUri }; - workspace.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); - workspace.setup(w => w.workspaceFolders).returns(() => [workspaceFolder]); + workspace.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); + workspace.setup((w) => w.workspaceFolders).returns(() => [workspaceFolder]); const expectedTerminalArgs = terminalArgs.concat( path.join(workspaceUri.fsPath, 'manage.py').fileToCommandArgument(), 'shell' @@ -201,8 +201,8 @@ suite('Terminal - Django Shell Code Execution', () => { condaEnv: { name: string; path: string }, resource?: Uri ) { - settings.setup(s => s.pythonPath).returns(() => pythonPath); - terminalSettings.setup(t => t.launchArgs).returns(() => terminalArgs); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs); const condaFile = 'conda'; const serviceContainer = TypeMoq.Mock.ofType(); @@ -216,7 +216,7 @@ suite('Terminal - Django Shell Code Execution', () => { ); const expectedTerminalArgs = [...terminalArgs, 'manage.py', 'shell']; pythonExecutionFactory - .setup(p => p.createCondaExecutionService(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.createCondaExecutionService(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(condaExecutionService)); const replCommandArgs = await (executor as DjangoShellCodeExecutionProvider).getExecutableInfo(resource); diff --git a/src/test/terminals/codeExecution/helper.test.ts b/src/test/terminals/codeExecution/helper.test.ts index 13670ec38875..8b1c4fc23152 100644 --- a/src/test/terminals/codeExecution/helper.test.ts +++ b/src/test/terminals/codeExecution/helper.test.ts @@ -55,41 +55,41 @@ suite('Terminal - Code Execution Helper', () => { // tslint:disable-next-line:no-any processService.setup((x: any) => x.then).returns(() => undefined); interpreterService - .setup(i => i.getActiveInterpreter(TypeMoq.It.isAny())) + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(workingPython)); const processServiceFactory = TypeMoq.Mock.ofType(); processServiceFactory - .setup(p => p.create(TypeMoq.It.isAny())) + .setup((p) => p.create(TypeMoq.It.isAny())) .returns(() => Promise.resolve(processService.object)); envVariablesProvider - .setup(e => e.getEnvironmentVariables(TypeMoq.It.isAny())) + .setup((e) => e.getEnvironmentVariables(TypeMoq.It.isAny())) .returns(() => Promise.resolve({})); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IProcessServiceFactory), TypeMoq.It.isAny())) .returns(() => processServiceFactory.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) .returns(() => interpreterService.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IDocumentManager), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IDocumentManager), TypeMoq.It.isAny())) .returns(() => documentManager.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) .returns(() => applicationShell.object); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider), TypeMoq.It.isAny())) + .setup((c) => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider), TypeMoq.It.isAny())) .returns(() => envVariablesProvider.object); helper = new CodeExecutionHelper(serviceContainer.object); document = TypeMoq.Mock.ofType(); editor = TypeMoq.Mock.ofType(); - editor.setup(e => e.document).returns(() => document.object); + editor.setup((e) => e.document).returns(() => document.object); }); async function ensureBlankLinesAreRemoved(source: string, expectedSource: string) { const actualProcessService = new ProcessService(new BufferDecoder()); processService - .setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((file, args, options) => { return actualProcessService.exec.apply(actualProcessService, [file, args, options]); }); @@ -98,7 +98,7 @@ suite('Terminal - Code Execution Helper', () => { expectedSource = expectedSource.splitLines({ removeEmptyEntries: false, trim: false }).join(EOL); expect(normalizedZCode).to.be.equal(expectedSource); } - test('Ensure blank lines are NOT removed when code is not indented (simple)', async function() { + test('Ensure blank lines are NOT removed when code is not indented (simple)', async function () { // This test has not been working for many months in Python 2.7 under // Windows.Tracked by #2544. if (isOs(OSType.Windows) && (await isPythonVersion('2.7'))) { @@ -119,7 +119,7 @@ suite('Terminal - Code Execution Helper', () => { 'print(1)', 'print(2)' ]; - const expectedCode = code.filter(line => line.trim().length > 0).join(EOL); + const expectedCode = code.filter((line) => line.trim().length > 0).join(EOL); await ensureBlankLinesAreRemoved(code.join(EOL), expectedCode); }); test('Ensure there are no multiple-CR elements in the normalized code.', async () => { @@ -138,7 +138,7 @@ suite('Terminal - Code Execution Helper', () => { ]; const actualProcessService = new ProcessService(new BufferDecoder()); processService - .setup(p => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((p) => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns((_file, args, options) => { return actualProcessService.exec.apply(actualProcessService, [PYTHON_PATH, args, options]); }); @@ -146,8 +146,8 @@ suite('Terminal - Code Execution Helper', () => { const doubleCrIndex = normalizedCode.indexOf('\r\r'); expect(doubleCrIndex).to.be.equal(-1, 'Double CR (CRCRLF) line endings detected in normalized code snippet.'); }); - ['', '1', '2', '3', '4', '5', '6', '7', '8'].forEach(fileNameSuffix => { - test(`Ensure blank lines are removed (Sample${fileNameSuffix})`, async function() { + ['', '1', '2', '3', '4', '5', '6', '7', '8'].forEach((fileNameSuffix) => { + test(`Ensure blank lines are removed (Sample${fileNameSuffix})`, async function () { // This test has not been working for many months in Python 2.7 under // Windows.Tracked by #2544. if (isOs(OSType.Windows) && (await isPythonVersion('2.7'))) { @@ -162,7 +162,7 @@ suite('Terminal - Code Execution Helper', () => { ); await ensureBlankLinesAreRemoved(code, expectedCode); }); - test(`Ensure last two blank lines are preserved (Sample${fileNameSuffix})`, async function() { + test(`Ensure last two blank lines are preserved (Sample${fileNameSuffix})`, async function () { // This test has not been working for many months in Python 2.7 under // Windows.Tracked by #2544. if (isOs(OSType.Windows) && (await isPythonVersion('2.7'))) { @@ -177,7 +177,7 @@ suite('Terminal - Code Execution Helper', () => { ); await ensureBlankLinesAreRemoved(code + EOL, expectedCode + EOL); }); - test(`Ensure last two blank lines are preserved even if we have more than 2 trailing blank lines (Sample${fileNameSuffix})`, async function() { + test(`Ensure last two blank lines are preserved even if we have more than 2 trailing blank lines (Sample${fileNameSuffix})`, async function () { // This test has not been working for many months in Python 2.7 under // Windows.Tracked by #2544. if (isOs(OSType.Windows) && (await isPythonVersion('2.7'))) { @@ -194,88 +194,88 @@ suite('Terminal - Code Execution Helper', () => { }); }); test("Display message if there's no active file", async () => { - documentManager.setup(doc => doc.activeTextEditor).returns(() => undefined); + documentManager.setup((doc) => doc.activeTextEditor).returns(() => undefined); const uri = await helper.getFileToExecute(); expect(uri).to.be.an('undefined'); - applicationShell.verify(a => a.showErrorMessage(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); + applicationShell.verify((a) => a.showErrorMessage(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); }); test('Display message if active file is unsaved', async () => { - documentManager.setup(doc => doc.activeTextEditor).returns(() => editor.object); - document.setup(doc => doc.isUntitled).returns(() => true); + documentManager.setup((doc) => doc.activeTextEditor).returns(() => editor.object); + document.setup((doc) => doc.isUntitled).returns(() => true); const uri = await helper.getFileToExecute(); expect(uri).to.be.an('undefined'); - applicationShell.verify(a => a.showErrorMessage(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); + applicationShell.verify((a) => a.showErrorMessage(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); }); test('Display message if active file is non-python', async () => { - document.setup(doc => doc.isUntitled).returns(() => false); - document.setup(doc => doc.languageId).returns(() => 'html'); - documentManager.setup(doc => doc.activeTextEditor).returns(() => editor.object); + document.setup((doc) => doc.isUntitled).returns(() => false); + document.setup((doc) => doc.languageId).returns(() => 'html'); + documentManager.setup((doc) => doc.activeTextEditor).returns(() => editor.object); const uri = await helper.getFileToExecute(); expect(uri).to.be.an('undefined'); - applicationShell.verify(a => a.showErrorMessage(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); + applicationShell.verify((a) => a.showErrorMessage(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); }); test('Returns file uri', async () => { - document.setup(doc => doc.isUntitled).returns(() => false); - document.setup(doc => doc.languageId).returns(() => PYTHON_LANGUAGE); + document.setup((doc) => doc.isUntitled).returns(() => false); + document.setup((doc) => doc.languageId).returns(() => PYTHON_LANGUAGE); const expectedUri = Uri.file('one.py'); - document.setup(doc => doc.uri).returns(() => expectedUri); - documentManager.setup(doc => doc.activeTextEditor).returns(() => editor.object); + document.setup((doc) => doc.uri).returns(() => expectedUri); + documentManager.setup((doc) => doc.activeTextEditor).returns(() => editor.object); const uri = await helper.getFileToExecute(); expect(uri).to.be.deep.equal(expectedUri); }); test('Returns file uri even if saving fails', async () => { - document.setup(doc => doc.isUntitled).returns(() => false); - document.setup(doc => doc.isDirty).returns(() => true); - document.setup(doc => doc.languageId).returns(() => PYTHON_LANGUAGE); - document.setup(doc => doc.save()).returns(() => Promise.resolve(false)); + document.setup((doc) => doc.isUntitled).returns(() => false); + document.setup((doc) => doc.isDirty).returns(() => true); + document.setup((doc) => doc.languageId).returns(() => PYTHON_LANGUAGE); + document.setup((doc) => doc.save()).returns(() => Promise.resolve(false)); const expectedUri = Uri.file('one.py'); - document.setup(doc => doc.uri).returns(() => expectedUri); - documentManager.setup(doc => doc.activeTextEditor).returns(() => editor.object); + document.setup((doc) => doc.uri).returns(() => expectedUri); + documentManager.setup((doc) => doc.activeTextEditor).returns(() => editor.object); const uri = await helper.getFileToExecute(); expect(uri).to.be.deep.equal(expectedUri); }); test('Dirty files are saved', async () => { - document.setup(doc => doc.isUntitled).returns(() => false); - document.setup(doc => doc.isDirty).returns(() => true); - document.setup(doc => doc.languageId).returns(() => PYTHON_LANGUAGE); + document.setup((doc) => doc.isUntitled).returns(() => false); + document.setup((doc) => doc.isDirty).returns(() => true); + document.setup((doc) => doc.languageId).returns(() => PYTHON_LANGUAGE); const expectedUri = Uri.file('one.py'); - document.setup(doc => doc.uri).returns(() => expectedUri); - documentManager.setup(doc => doc.activeTextEditor).returns(() => editor.object); + document.setup((doc) => doc.uri).returns(() => expectedUri); + documentManager.setup((doc) => doc.activeTextEditor).returns(() => editor.object); const uri = await helper.getFileToExecute(); expect(uri).to.be.deep.equal(expectedUri); - document.verify(doc => doc.save(), TypeMoq.Times.once()); + document.verify((doc) => doc.save(), TypeMoq.Times.once()); }); test('Non-Dirty files are not-saved', async () => { - document.setup(doc => doc.isUntitled).returns(() => false); - document.setup(doc => doc.isDirty).returns(() => false); - document.setup(doc => doc.languageId).returns(() => PYTHON_LANGUAGE); + document.setup((doc) => doc.isUntitled).returns(() => false); + document.setup((doc) => doc.isDirty).returns(() => false); + document.setup((doc) => doc.languageId).returns(() => PYTHON_LANGUAGE); const expectedUri = Uri.file('one.py'); - document.setup(doc => doc.uri).returns(() => expectedUri); - documentManager.setup(doc => doc.activeTextEditor).returns(() => editor.object); + document.setup((doc) => doc.uri).returns(() => expectedUri); + documentManager.setup((doc) => doc.activeTextEditor).returns(() => editor.object); const uri = await helper.getFileToExecute(); expect(uri).to.be.deep.equal(expectedUri); - document.verify(doc => doc.save(), TypeMoq.Times.never()); + document.verify((doc) => doc.save(), TypeMoq.Times.never()); }); test('Returns current line if nothing is selected', async () => { const lineContents = 'Line Contents'; - editor.setup(e => e.selection).returns(() => new Selection(3, 0, 3, 0)); + editor.setup((e) => e.selection).returns(() => new Selection(3, 0, 3, 0)); const textLine = TypeMoq.Mock.ofType(); - textLine.setup(t => t.text).returns(() => lineContents); - document.setup(d => d.lineAt(TypeMoq.It.isAny())).returns(() => textLine.object); + textLine.setup((t) => t.text).returns(() => lineContents); + document.setup((d) => d.lineAt(TypeMoq.It.isAny())).returns(() => textLine.object); const content = await helper.getSelectedTextToExecute(editor.object); expect(content).to.be.equal(lineContents); @@ -283,11 +283,11 @@ suite('Terminal - Code Execution Helper', () => { test('Returns selected text', async () => { const lineContents = 'Line Contents'; - editor.setup(e => e.selection).returns(() => new Selection(3, 0, 10, 5)); + editor.setup((e) => e.selection).returns(() => new Selection(3, 0, 10, 5)); const textLine = TypeMoq.Mock.ofType(); - textLine.setup(t => t.text).returns(() => lineContents); + textLine.setup((t) => t.text).returns(() => lineContents); document - .setup(d => d.getText(TypeMoq.It.isAny())) + .setup((d) => d.getText(TypeMoq.It.isAny())) .returns((r: Range) => `${r.start.line}.${r.start.character}.${r.end.line}.${r.end.character}`); const content = await helper.getSelectedTextToExecute(editor.object); @@ -296,7 +296,7 @@ suite('Terminal - Code Execution Helper', () => { test('saveFileIfDirty will not fail if file is not opened', async () => { documentManager - .setup(d => d.textDocuments) + .setup((d) => d.textDocuments) .returns(() => []) .verifiable(TypeMoq.Times.once()); @@ -306,33 +306,33 @@ suite('Terminal - Code Execution Helper', () => { test('File will be saved if file is dirty', async () => { documentManager - .setup(d => d.textDocuments) + .setup((d) => d.textDocuments) .returns(() => [document.object]) .verifiable(TypeMoq.Times.once()); - document.setup(doc => doc.isUntitled).returns(() => false); - document.setup(doc => doc.isDirty).returns(() => true); - document.setup(doc => doc.languageId).returns(() => PYTHON_LANGUAGE); + document.setup((doc) => doc.isUntitled).returns(() => false); + document.setup((doc) => doc.isDirty).returns(() => true); + document.setup((doc) => doc.languageId).returns(() => PYTHON_LANGUAGE); const expectedUri = Uri.file('one.py'); - document.setup(doc => doc.uri).returns(() => expectedUri); + document.setup((doc) => doc.uri).returns(() => expectedUri); await helper.saveFileIfDirty(expectedUri); documentManager.verifyAll(); - document.verify(doc => doc.save(), TypeMoq.Times.once()); + document.verify((doc) => doc.save(), TypeMoq.Times.once()); }); test('File will be not saved if file is not dirty', async () => { documentManager - .setup(d => d.textDocuments) + .setup((d) => d.textDocuments) .returns(() => [document.object]) .verifiable(TypeMoq.Times.once()); - document.setup(doc => doc.isUntitled).returns(() => false); - document.setup(doc => doc.isDirty).returns(() => false); - document.setup(doc => doc.languageId).returns(() => PYTHON_LANGUAGE); + document.setup((doc) => doc.isUntitled).returns(() => false); + document.setup((doc) => doc.isDirty).returns(() => false); + document.setup((doc) => doc.languageId).returns(() => PYTHON_LANGUAGE); const expectedUri = Uri.file('one.py'); - document.setup(doc => doc.uri).returns(() => expectedUri); + document.setup((doc) => doc.uri).returns(() => expectedUri); await helper.saveFileIfDirty(expectedUri); documentManager.verifyAll(); - document.verify(doc => doc.save(), TypeMoq.Times.never()); + document.verify((doc) => doc.save(), TypeMoq.Times.never()); }); }); diff --git a/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts b/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts index 5d338b2cb8a0..d34efffed80c 100644 --- a/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts +++ b/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts @@ -22,7 +22,7 @@ import { ICodeExecutionService } from '../../../client/terminals/types'; import { PYTHON_PATH } from '../../common'; suite('Terminal - Code Execution', () => { - ['Terminal Execution', 'Repl Execution', 'Django Execution'].forEach(testSuiteName => { + ['Terminal Execution', 'Repl Execution', 'Django Execution'].forEach((testSuiteName) => { let terminalSettings: TypeMoq.IMock; let terminalService: TypeMoq.IMock; let workspace: TypeMoq.IMock; @@ -40,7 +40,7 @@ suite('Terminal - Code Execution', () => { let isDjangoRepl: boolean; teardown(() => { - disposables.forEach(disposable => { + disposables.forEach((disposable) => { if (disposable) { disposable.dispose(); } @@ -62,8 +62,8 @@ suite('Terminal - Code Execution', () => { fileSystem = TypeMoq.Mock.ofType(); pythonExecutionFactory = TypeMoq.Mock.ofType(); settings = TypeMoq.Mock.ofType(); - settings.setup(s => s.terminal).returns(() => terminalSettings.object); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + settings.setup((s) => s.terminal).returns(() => terminalSettings.object); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); switch (testSuiteName) { case 'Terminal Execution': { @@ -90,7 +90,7 @@ suite('Terminal - Code Execution', () => { case 'Django Execution': { isDjangoRepl = true; workspace - .setup(w => + .setup((w) => w.onDidChangeWorkspaceFolders(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => { @@ -118,7 +118,7 @@ suite('Terminal - Code Execution', () => { suite(`${testSuiteName} (validation of title)`, () => { setup(() => { terminalFactory - .setup(f => f.getTerminalService(TypeMoq.It.isAny(), TypeMoq.It.isValue(expectedTerminalTitle))) + .setup((f) => f.getTerminalService(TypeMoq.It.isAny(), TypeMoq.It.isValue(expectedTerminalTitle))) .returns(() => terminalService.object); }); @@ -127,11 +127,11 @@ suite('Terminal - Code Execution', () => { isOsx: boolean, isLinux: boolean ): Promise { - platform.setup(p => p.isWindows).returns(() => isWindows); - platform.setup(p => p.isMac).returns(() => isOsx); - platform.setup(p => p.isLinux).returns(() => isLinux); - settings.setup(s => s.pythonPath).returns(() => PYTHON_PATH); - terminalSettings.setup(t => t.launchArgs).returns(() => []); + platform.setup((p) => p.isWindows).returns(() => isWindows); + platform.setup((p) => p.isMac).returns(() => isOsx); + platform.setup((p) => p.isLinux).returns(() => isLinux); + settings.setup((s) => s.pythonPath).returns(() => PYTHON_PATH); + terminalSettings.setup((t) => t.launchArgs).returns(() => []); await executor.initializeRepl(); } @@ -149,28 +149,28 @@ suite('Terminal - Code Execution', () => { }); }); - suite(testSuiteName, async function() { + suite(testSuiteName, async function () { // tslint:disable-next-line:no-invalid-this this.timeout(5000); // Activation of terminals take some time (there's a delay in the code to account for VSC Terminal issues). setup(() => { terminalFactory - .setup(f => f.getTerminalService(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((f) => f.getTerminalService(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => terminalService.object); }); async function ensureWeSetCurrentDirectoryBeforeExecutingAFile(_isWindows: boolean): Promise { const file = Uri.file(path.join('c', 'path', 'to', 'file', 'one.py')); - terminalSettings.setup(t => t.executeInFileDir).returns(() => true); - workspace.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder.object); - workspaceFolder.setup(w => w.uri).returns(() => Uri.file(path.join('c', 'path', 'to'))); - platform.setup(p => p.isWindows).returns(() => false); - settings.setup(s => s.pythonPath).returns(() => PYTHON_PATH); - terminalSettings.setup(t => t.launchArgs).returns(() => []); + terminalSettings.setup((t) => t.executeInFileDir).returns(() => true); + workspace.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder.object); + workspaceFolder.setup((w) => w.uri).returns(() => Uri.file(path.join('c', 'path', 'to'))); + platform.setup((p) => p.isWindows).returns(() => false); + settings.setup((s) => s.pythonPath).returns(() => PYTHON_PATH); + terminalSettings.setup((t) => t.launchArgs).returns(() => []); await executor.executeFile(file); terminalService.verify( - async t => + async (t) => t.sendText(TypeMoq.It.isValue(`cd ${path.dirname(file.fsPath).fileToCommandArgument()}`)), TypeMoq.Times.once() ); @@ -184,16 +184,16 @@ suite('Terminal - Code Execution', () => { async function ensureWeWetCurrentDirectoryAndQuoteBeforeExecutingFile(isWindows: boolean): Promise { const file = Uri.file(path.join('c', 'path', 'to', 'file with spaces in path', 'one.py')); - terminalSettings.setup(t => t.executeInFileDir).returns(() => true); - workspace.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder.object); - workspaceFolder.setup(w => w.uri).returns(() => Uri.file(path.join('c', 'path', 'to'))); - platform.setup(p => p.isWindows).returns(() => isWindows); - settings.setup(s => s.pythonPath).returns(() => PYTHON_PATH); - terminalSettings.setup(t => t.launchArgs).returns(() => []); + terminalSettings.setup((t) => t.executeInFileDir).returns(() => true); + workspace.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder.object); + workspaceFolder.setup((w) => w.uri).returns(() => Uri.file(path.join('c', 'path', 'to'))); + platform.setup((p) => p.isWindows).returns(() => isWindows); + settings.setup((s) => s.pythonPath).returns(() => PYTHON_PATH); + terminalSettings.setup((t) => t.launchArgs).returns(() => []); await executor.executeFile(file); const dir = path.dirname(file.fsPath).fileToCommandArgument(); - terminalService.verify(async t => t.sendText(TypeMoq.It.isValue(`cd ${dir}`)), TypeMoq.Times.once()); + terminalService.verify(async (t) => t.sendText(TypeMoq.It.isValue(`cd ${dir}`)), TypeMoq.Times.once()); } test('Ensure we set current directory (and quote it when containing spaces) before executing file (non windows)', async () => { @@ -208,18 +208,18 @@ suite('Terminal - Code Execution', () => { isWindows: boolean ): Promise { const file = Uri.file(path.join('c', 'path', 'to', 'file with spaces in path', 'one.py')); - terminalSettings.setup(t => t.executeInFileDir).returns(() => true); - workspace.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder.object); + terminalSettings.setup((t) => t.executeInFileDir).returns(() => true); + workspace.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolder.object); workspaceFolder - .setup(w => w.uri) + .setup((w) => w.uri) .returns(() => Uri.file(path.join('c', 'path', 'to', 'file with spaces in path'))); - platform.setup(p => p.isWindows).returns(() => isWindows); - settings.setup(s => s.pythonPath).returns(() => PYTHON_PATH); - terminalSettings.setup(t => t.launchArgs).returns(() => []); + platform.setup((p) => p.isWindows).returns(() => isWindows); + settings.setup((s) => s.pythonPath).returns(() => PYTHON_PATH); + terminalSettings.setup((t) => t.launchArgs).returns(() => []); await executor.executeFile(file); - terminalService.verify(async t => t.sendText(TypeMoq.It.isAny()), TypeMoq.Times.never()); + terminalService.verify(async (t) => t.sendText(TypeMoq.It.isAny()), TypeMoq.Times.never()); } test('Ensure we do not set current directory before executing file if in the same directory (non windows)', async () => { await ensureWeDoNotSetCurrentDirectoryBeforeExecutingFileInSameDirectory(false); @@ -232,15 +232,15 @@ suite('Terminal - Code Execution', () => { isWindows: boolean ): Promise { const file = Uri.file(path.join('c', 'path', 'to', 'file with spaces in path', 'one.py')); - terminalSettings.setup(t => t.executeInFileDir).returns(() => true); - workspace.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); - platform.setup(p => p.isWindows).returns(() => isWindows); - settings.setup(s => s.pythonPath).returns(() => PYTHON_PATH); - terminalSettings.setup(t => t.launchArgs).returns(() => []); + terminalSettings.setup((t) => t.executeInFileDir).returns(() => true); + workspace.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); + platform.setup((p) => p.isWindows).returns(() => isWindows); + settings.setup((s) => s.pythonPath).returns(() => PYTHON_PATH); + terminalSettings.setup((t) => t.launchArgs).returns(() => []); await executor.executeFile(file); - terminalService.verify(async t => t.sendText(TypeMoq.It.isAny()), TypeMoq.Times.once()); + terminalService.verify(async (t) => t.sendText(TypeMoq.It.isAny()), TypeMoq.Times.once()); } test('Ensure we set current directory before executing file if file is not in a workspace (non windows)', async () => { await ensureWeSetCurrentDirectoryBeforeExecutingFileNotInSameDirectory(false); @@ -255,13 +255,13 @@ suite('Terminal - Code Execution', () => { terminalArgs: string[], file: Uri ): Promise { - platform.setup(p => p.isWindows).returns(() => isWindows); - settings.setup(s => s.pythonPath).returns(() => pythonPath); - terminalSettings.setup(t => t.launchArgs).returns(() => terminalArgs); - terminalSettings.setup(t => t.executeInFileDir).returns(() => false); - workspace.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); + platform.setup((p) => p.isWindows).returns(() => isWindows); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs); + terminalSettings.setup((t) => t.executeInFileDir).returns(() => false); + workspace.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); pythonExecutionFactory - .setup(p => + .setup((p) => p.createCondaExecutionService(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve(undefined)); @@ -270,7 +270,8 @@ suite('Terminal - Code Execution', () => { const expectedPythonPath = isWindows ? pythonPath.replace(/\\/g, '/') : pythonPath; const expectedArgs = terminalArgs.concat(file.fsPath.fileToCommandArgument()); terminalService.verify( - async t => t.sendCommand(TypeMoq.It.isValue(expectedPythonPath), TypeMoq.It.isValue(expectedArgs)), + async (t) => + t.sendCommand(TypeMoq.It.isValue(expectedPythonPath), TypeMoq.It.isValue(expectedArgs)), TypeMoq.Times.once() ); } @@ -301,10 +302,10 @@ suite('Terminal - Code Execution', () => { file: Uri, condaEnv: { name: string; path: string } ): Promise { - settings.setup(s => s.pythonPath).returns(() => pythonPath); - terminalSettings.setup(t => t.launchArgs).returns(() => terminalArgs); - terminalSettings.setup(t => t.executeInFileDir).returns(() => false); - workspace.setup(w => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs); + terminalSettings.setup((t) => t.executeInFileDir).returns(() => false); + workspace.setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => undefined); const condaFile = 'conda'; const serviceContainer = TypeMoq.Mock.ofType(); @@ -317,7 +318,7 @@ suite('Terminal - Code Execution', () => { condaEnv ); pythonExecutionFactory - .setup(p => + .setup((p) => p.createCondaExecutionService(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve(condaExecutionService)); @@ -327,7 +328,7 @@ suite('Terminal - Code Execution', () => { const expectedArgs = [...terminalArgs, file.fsPath.fileToCommandArgument()]; terminalService.verify( - async t => t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedArgs)), + async (t) => t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedArgs)), TypeMoq.Times.once() ); } @@ -355,13 +356,13 @@ suite('Terminal - Code Execution', () => { terminalArgs: string[] ) { pythonExecutionFactory - .setup(p => + .setup((p) => p.createCondaExecutionService(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve(undefined)); - platform.setup(p => p.isWindows).returns(() => isWindows); - settings.setup(s => s.pythonPath).returns(() => pythonPath); - terminalSettings.setup(t => t.launchArgs).returns(() => terminalArgs); + platform.setup((p) => p.isWindows).returns(() => isWindows); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs); const expectedTerminalArgs = isDjangoRepl ? terminalArgs.concat(['manage.py', 'shell']) : terminalArgs; const replCommandArgs = await (executor as TerminalCodeExecutionProvider).getExecutableInfo(); @@ -410,8 +411,8 @@ suite('Terminal - Code Execution', () => { terminalArgs: string[], condaEnv: { name: string; path: string } ) { - settings.setup(s => s.pythonPath).returns(() => pythonPath); - terminalSettings.setup(t => t.launchArgs).returns(() => terminalArgs); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs); const condaFile = 'conda'; const serviceContainer = TypeMoq.Mock.ofType(); @@ -424,7 +425,7 @@ suite('Terminal - Code Execution', () => { condaEnv ); pythonExecutionFactory - .setup(p => + .setup((p) => p.createCondaExecutionService(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()) ) .returns(() => Promise.resolve(condaExecutionService)); @@ -460,19 +461,19 @@ suite('Terminal - Code Execution', () => { await executor.execute((undefined as any) as string); terminalService.verify( - async t => t.sendCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny()), + async (t) => t.sendCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never() ); - terminalService.verify(async t => t.sendText(TypeMoq.It.isAny()), TypeMoq.Times.never()); + terminalService.verify(async (t) => t.sendText(TypeMoq.It.isAny()), TypeMoq.Times.never()); }); test('Ensure repl is initialized once before sending text to the repl', async () => { const pythonPath = 'usr/bin/python1234'; const terminalArgs = ['-a', 'b', 'c']; - platform.setup(p => p.isWindows).returns(() => false); - settings.setup(s => s.pythonPath).returns(() => pythonPath); - terminalSettings.setup(t => t.launchArgs).returns(() => terminalArgs); + platform.setup((p) => p.isWindows).returns(() => false); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs); await executor.execute('cmd1'); await executor.execute('cmd2'); @@ -480,7 +481,8 @@ suite('Terminal - Code Execution', () => { const expectedTerminalArgs = isDjangoRepl ? terminalArgs.concat(['manage.py', 'shell']) : terminalArgs; terminalService.verify( - async t => t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), + async (t) => + t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), TypeMoq.Times.once() ); }); @@ -488,14 +490,14 @@ suite('Terminal - Code Execution', () => { test('Ensure repl is re-initialized when terminal is closed', async () => { const pythonPath = 'usr/bin/python1234'; const terminalArgs = ['-a', 'b', 'c']; - platform.setup(p => p.isWindows).returns(() => false); - settings.setup(s => s.pythonPath).returns(() => pythonPath); - terminalSettings.setup(t => t.launchArgs).returns(() => terminalArgs); + platform.setup((p) => p.isWindows).returns(() => false); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs); let closeTerminalCallback: undefined | (() => void); terminalService - .setup(t => t.onDidCloseTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(callback => { + .setup((t) => t.onDidCloseTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns((callback) => { closeTerminalCallback = callback; return { dispose: noop @@ -510,21 +512,24 @@ suite('Terminal - Code Execution', () => { expect(closeTerminalCallback).not.to.be.an('undefined', 'Callback not initialized'); terminalService.verify( - async t => t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), + async (t) => + t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), TypeMoq.Times.once() ); closeTerminalCallback!.call(terminalService.object); await executor.execute('cmd4'); terminalService.verify( - async t => t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), + async (t) => + t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), TypeMoq.Times.exactly(2) ); closeTerminalCallback!.call(terminalService.object); await executor.execute('cmd5'); terminalService.verify( - async t => t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), + async (t) => + t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), TypeMoq.Times.exactly(3) ); }); @@ -532,15 +537,15 @@ suite('Terminal - Code Execution', () => { test('Ensure code is sent to terminal', async () => { const pythonPath = 'usr/bin/python1234'; const terminalArgs = ['-a', 'b', 'c']; - platform.setup(p => p.isWindows).returns(() => false); - settings.setup(s => s.pythonPath).returns(() => pythonPath); - terminalSettings.setup(t => t.launchArgs).returns(() => terminalArgs); + platform.setup((p) => p.isWindows).returns(() => false); + settings.setup((s) => s.pythonPath).returns(() => pythonPath); + terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs); await executor.execute('cmd1'); - terminalService.verify(async t => t.sendText('cmd1'), TypeMoq.Times.once()); + terminalService.verify(async (t) => t.sendText('cmd1'), TypeMoq.Times.once()); await executor.execute('cmd2'); - terminalService.verify(async t => t.sendText('cmd2'), TypeMoq.Times.once()); + terminalService.verify(async (t) => t.sendText('cmd2'), TypeMoq.Times.once()); }); }); }); diff --git a/src/test/terminals/serviceRegistry.unit.test.ts b/src/test/terminals/serviceRegistry.unit.test.ts index c71a22227935..67446f44e601 100644 --- a/src/test/terminals/serviceRegistry.unit.test.ts +++ b/src/test/terminals/serviceRegistry.unit.test.ts @@ -29,24 +29,24 @@ suite('Terminal - Service Registry', () => { [ICodeExecutionService, ReplProvider, 'repl'], [ITerminalAutoActivation, TerminalAutoActivation], [ICodeExecutionService, TerminalCodeExecutionProvider, 'standard'] - ].forEach(args => { + ].forEach((args) => { if (args.length === 2) { services - .setup(s => + .setup((s) => s.addSingleton( // tslint:disable-next-line:no-any typemoq.It.isValue(args[0] as any), - typemoq.It.is(value => args[1] === value) + typemoq.It.is((value) => args[1] === value) ) ) .verifiable(typemoq.Times.once()); } else { services - .setup(s => + .setup((s) => s.addSingleton( // tslint:disable-next-line:no-any typemoq.It.isValue(args[0] as any), - typemoq.It.is(value => args[1] === value), + typemoq.It.is((value) => args[1] === value), // tslint:disable-next-line:no-any typemoq.It.isValue(args[2] as any) ) diff --git a/src/test/testBootstrap.ts b/src/test/testBootstrap.ts index 8fa4423bd012..905f3fdf45b7 100644 --- a/src/test/testBootstrap.ts +++ b/src/test/testBootstrap.ts @@ -73,15 +73,15 @@ async function end(exitCode: number) { } async function startSocketServer() { - return new Promise(resolve => { - server = createServer(socket => { - socket.on('data', buffer => { + return new Promise((resolve) => { + server = createServer((socket) => { + socket.on('data', (buffer) => { const data = buffer.toString('utf8'); console.log(`Exit code from Tests is ${data}`); const code = parseInt(data.substring(0, 1), 10); end(code).catch(noop); }); - socket.on('error', ex => { + socket.on('error', (ex) => { // Just log it, no need to do anything else. console.error(ex); }); @@ -94,7 +94,7 @@ async function startSocketServer() { await fs.writeFile(portFile, port.toString()); resolve(); }); - server.on('error', ex => { + server.on('error', (ex) => { // Just log it, no need to do anything else. console.error(ex); }); @@ -108,7 +108,7 @@ async function start() { proc.once('close', end); } -start().catch(ex => { +start().catch((ex) => { console.error('File testBootstrap.ts failed with Errors', ex); process.exit(1); }); diff --git a/src/test/testRunner.ts b/src/test/testRunner.ts index a846965b4e14..12788f9c93dd 100644 --- a/src/test/testRunner.ts +++ b/src/test/testRunner.ts @@ -1,102 +1,102 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// tslint:disable:no-require-imports no-var-requires import-name no-function-expression no-any prefer-template no-console no-var-self -// Most of the source is in node_modules/vscode/lib/testrunner.js - -'use strict'; -import * as glob from 'glob'; -import * as Mocha from 'mocha'; -import * as path from 'path'; -import { IS_SMOKE_TEST, MAX_EXTENSION_ACTIVATION_TIME } from './constants'; -import { initialize } from './initialize'; - -// Linux: prevent a weird NPE when mocha on Linux requires the window size from the TTY. -// Since we are not running in a tty environment, we just implement the method statically. -const tty = require('tty'); -if (!tty.getWindowSize) { - tty.getWindowSize = function(): number[] { - return [80, 75]; - }; -} - -let mocha = new Mocha({ - ui: 'tdd', - colors: true -}); - -export type SetupOptions = Mocha.MochaOptions & { - testFilesSuffix?: string; - reporterOptions?: { - mochaFile?: string; - properties?: string; - }; -}; - -let testFilesGlob = 'test'; - -export function configure(setupOptions: SetupOptions): void { - if (setupOptions.testFilesSuffix) { - testFilesGlob = setupOptions.testFilesSuffix; - } - // Force Mocha to exit. - (setupOptions as any).exit = true; - mocha = new Mocha(setupOptions); -} - -export async function run(): Promise { - const testsRoot = path.join(__dirname); - // Enable source map support. - require('source-map-support').install(); - - // nteract/transforms-full expects to run in the browser so we have to fake - // parts of the browser here. - if (!IS_SMOKE_TEST) { - const reactHelpers = require('./datascience/reactHelpers') as typeof import('./datascience/reactHelpers'); - reactHelpers.setUpDomEnvironment(); - } - - /** - * Waits until the Python Extension completes loading or a timeout. - * When running tests within VSC, we need to wait for the Python Extension to complete loading, - * this is where `initialize` comes in, we load the PVSC extension using VSC API, wait for it - * to complete. - * That's when we know out PVSC extension specific code is ready for testing. - * So, this code needs to run always for every test running in VS Code (what we call these `system test`) . - * @returns - */ - function initializationScript() { - const ex = new Error('Failed to initialize Python extension for tests after 3 minutes'); - let timer: NodeJS.Timer | undefined; - const failed = new Promise((_, reject) => { - timer = setTimeout(() => reject(ex), MAX_EXTENSION_ACTIVATION_TIME); - }); - const promise = Promise.race([initialize(), failed]); - promise.then(() => clearTimeout(timer!)).catch(() => clearTimeout(timer!)); - return promise; - } - // Run the tests. - await new Promise((resolve, reject) => { - glob( - `**/**.${testFilesGlob}.js`, - { ignore: ['**/**.unit.test.js', '**/**.functional.test.js'], cwd: testsRoot }, - (error, files) => { - if (error) { - return reject(error); - } - try { - files.forEach(file => mocha.addFile(path.join(testsRoot, file))); - initializationScript() - .then(() => - mocha.run(failures => - failures > 0 ? reject(new Error(`${failures} total failures`)) : resolve() - ) - ) - .catch(reject); - } catch (error) { - return reject(error); - } - } - ); - }); -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// tslint:disable:no-require-imports no-var-requires import-name no-function-expression no-any prefer-template no-console no-var-self +// Most of the source is in node_modules/vscode/lib/testrunner.js + +'use strict'; +import * as glob from 'glob'; +import * as Mocha from 'mocha'; +import * as path from 'path'; +import { IS_SMOKE_TEST, MAX_EXTENSION_ACTIVATION_TIME } from './constants'; +import { initialize } from './initialize'; + +// Linux: prevent a weird NPE when mocha on Linux requires the window size from the TTY. +// Since we are not running in a tty environment, we just implement the method statically. +const tty = require('tty'); +if (!tty.getWindowSize) { + tty.getWindowSize = function (): number[] { + return [80, 75]; + }; +} + +let mocha = new Mocha({ + ui: 'tdd', + colors: true +}); + +export type SetupOptions = Mocha.MochaOptions & { + testFilesSuffix?: string; + reporterOptions?: { + mochaFile?: string; + properties?: string; + }; +}; + +let testFilesGlob = 'test'; + +export function configure(setupOptions: SetupOptions): void { + if (setupOptions.testFilesSuffix) { + testFilesGlob = setupOptions.testFilesSuffix; + } + // Force Mocha to exit. + (setupOptions as any).exit = true; + mocha = new Mocha(setupOptions); +} + +export async function run(): Promise { + const testsRoot = path.join(__dirname); + // Enable source map support. + require('source-map-support').install(); + + // nteract/transforms-full expects to run in the browser so we have to fake + // parts of the browser here. + if (!IS_SMOKE_TEST) { + const reactHelpers = require('./datascience/reactHelpers') as typeof import('./datascience/reactHelpers'); + reactHelpers.setUpDomEnvironment(); + } + + /** + * Waits until the Python Extension completes loading or a timeout. + * When running tests within VSC, we need to wait for the Python Extension to complete loading, + * this is where `initialize` comes in, we load the PVSC extension using VSC API, wait for it + * to complete. + * That's when we know out PVSC extension specific code is ready for testing. + * So, this code needs to run always for every test running in VS Code (what we call these `system test`) . + * @returns + */ + function initializationScript() { + const ex = new Error('Failed to initialize Python extension for tests after 3 minutes'); + let timer: NodeJS.Timer | undefined; + const failed = new Promise((_, reject) => { + timer = setTimeout(() => reject(ex), MAX_EXTENSION_ACTIVATION_TIME); + }); + const promise = Promise.race([initialize(), failed]); + promise.then(() => clearTimeout(timer!)).catch(() => clearTimeout(timer!)); + return promise; + } + // Run the tests. + await new Promise((resolve, reject) => { + glob( + `**/**.${testFilesGlob}.js`, + { ignore: ['**/**.unit.test.js', '**/**.functional.test.js'], cwd: testsRoot }, + (error, files) => { + if (error) { + return reject(error); + } + try { + files.forEach((file) => mocha.addFile(path.join(testsRoot, file))); + initializationScript() + .then(() => + mocha.run((failures) => + failures > 0 ? reject(new Error(`${failures} total failures`)) : resolve() + ) + ) + .catch(reject); + } catch (error) { + return reject(error); + } + } + ); + }); +} diff --git a/src/test/testing/argsService.test.ts b/src/test/testing/argsService.test.ts index faaa73ce68b3..95874e69ad51 100644 --- a/src/test/testing/argsService.test.ts +++ b/src/test/testing/argsService.test.ts @@ -21,16 +21,16 @@ import { ArgumentsService as UnitTestArgumentsService } from '../../client/testi import { PYTHON_PATH } from '../common'; suite('ArgsService: Common', () => { - UNIT_TEST_PRODUCTS.forEach(product => { + UNIT_TEST_PRODUCTS.forEach((product) => { const productNames = getNamesAndValues(Product); - const productName = productNames.find(item => item.value === product)!.name; + const productName = productNames.find((item) => item.value === product)!.name; suite(productName, () => { let argumentsService: IArgumentsService; let moduleName = ''; let expectedWithArgs: string[] = []; let expectedWithoutArgs: string[] = []; - setup(function() { + setup(function () { // Take the spawning of process into account. // tslint:disable-next-line:no-invalid-this this.timeout(5000); @@ -39,7 +39,7 @@ suite('ArgsService: Common', () => { const argsHelper = new ArgumentsHelper(); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IArgumentsHelper), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IArgumentsHelper), typeMoq.It.isAny())) .returns(() => argsHelper); switch (product) { @@ -69,7 +69,7 @@ suite('ArgsService: Common', () => { test('Check for new/unrecognized options with values', () => { const options = argumentsService.getKnownOptions(); - const optionsNotFound = expectedWithArgs.filter(item => options.withArgs.indexOf(item) === -1); + const optionsNotFound = expectedWithArgs.filter((item) => options.withArgs.indexOf(item) === -1); if (optionsNotFound.length > 0) { fail('', optionsNotFound.join(', '), 'Options not found'); @@ -77,7 +77,7 @@ suite('ArgsService: Common', () => { }); test('Check for new/unrecognized options without values', () => { const options = argumentsService.getKnownOptions(); - const optionsNotFound = expectedWithoutArgs.filter(item => options.withoutArgs.indexOf(item) === -1); + const optionsNotFound = expectedWithoutArgs.filter((item) => options.withoutArgs.indexOf(item) === -1); if (optionsNotFound.length > 0) { fail('', optionsNotFound.join(', '), 'Options not found'); @@ -150,15 +150,15 @@ function getOptions(product: Product, moduleName: string, withValues: boolean) { if (withValues) { return getOptionsWithArguments(output) .concat(...knownOptionsWithArgs) - .filter(item => knownOptionsWithoutArgs.indexOf(item) === -1) + .filter((item) => knownOptionsWithoutArgs.indexOf(item) === -1) .sort(); } else { return ( getOptionsWithoutArguments(output) .concat(...knownOptionsWithoutArgs) - .filter(item => knownOptionsWithArgs.indexOf(item) === -1) + .filter((item) => knownOptionsWithArgs.indexOf(item) === -1) // In pytest, any option beginning with --log- is known to have args. - .filter(item => (product === Product.pytest ? !item.startsWith('--log-') : true)) + .filter((item) => (product === Product.pytest ? !item.startsWith('--log-') : true)) .sort() ); } diff --git a/src/test/testing/banners/languageServerSurvey.unit.test.ts b/src/test/testing/banners/languageServerSurvey.unit.test.ts index b19bbae436d3..34554cd8448e 100644 --- a/src/test/testing/banners/languageServerSurvey.unit.test.ts +++ b/src/test/testing/banners/languageServerSurvey.unit.test.ts @@ -48,7 +48,7 @@ suite('Language Server Survey Banner', () => { }); test('Do not show banner when it is disabled', () => { appShell - .setup(a => + .setup((a) => a.showInformationMessage(typemoq.It.isValue(message), typemoq.It.isValue(yes), typemoq.It.isValue(no)) ) .verifiable(typemoq.Times.never()); @@ -113,7 +113,7 @@ suite('Language Server Survey Banner', () => { // Server directory installed. This in turn will give the tested // code the version via the .version member of lsFolder. lsService - .setup(f => f.getCurrentLanguageServerDirectory()) + .setup((f) => f.getCurrentLanguageServerDirectory()) .returns(() => { return Promise.resolve(lsFolder); }) @@ -125,7 +125,7 @@ suite('Language Server Survey Banner', () => { // to launch. let receivedUri: string = ''; browser - .setup(b => + .setup((b) => b.launch( typemoq.It.is((a: string) => { receivedUri = a; @@ -178,52 +178,52 @@ function preparePopup( IPersistentState >(); enabledValState - .setup(a => a.updateValue(typemoq.It.isValue(true))) + .setup((a) => a.updateValue(typemoq.It.isValue(true))) .returns(() => { enabledValue = true; return Promise.resolve(); }); enabledValState - .setup(a => a.updateValue(typemoq.It.isValue(false))) + .setup((a) => a.updateValue(typemoq.It.isValue(false))) .returns(() => { enabledValue = false; return Promise.resolve(); }); attemptCountState - .setup(a => a.updateValue(typemoq.It.isAnyNumber())) + .setup((a) => a.updateValue(typemoq.It.isAnyNumber())) .returns(() => { attemptCounter += 1; return Promise.resolve(); }); completionCountState - .setup(a => a.updateValue(typemoq.It.isAnyNumber())) + .setup((a) => a.updateValue(typemoq.It.isAnyNumber())) .returns(() => { completionsCount += 1; return Promise.resolve(); }); - enabledValState.setup(a => a.value).returns(() => enabledValue); - attemptCountState.setup(a => a.value).returns(() => attemptCounter); - completionCountState.setup(a => a.value).returns(() => completionsCount); + enabledValState.setup((a) => a.value).returns(() => enabledValue); + attemptCountState.setup((a) => a.value).returns(() => attemptCounter); + completionCountState.setup((a) => a.value).returns(() => completionsCount); myfactory - .setup(a => + .setup((a) => a.createGlobalPersistentState(typemoq.It.isValue(LSSurveyStateKeys.ShowBanner), typemoq.It.isValue(true)) ) .returns(() => { return enabledValState.object; }); myfactory - .setup(a => + .setup((a) => a.createGlobalPersistentState(typemoq.It.isValue(LSSurveyStateKeys.ShowBanner), typemoq.It.isValue(false)) ) .returns(() => { return enabledValState.object; }); myfactory - .setup(a => + .setup((a) => a.createGlobalPersistentState( typemoq.It.isValue(LSSurveyStateKeys.ShowAttemptCounter), typemoq.It.isAnyNumber() @@ -233,7 +233,7 @@ function preparePopup( return attemptCountState.object; }); myfactory - .setup(a => + .setup((a) => a.createGlobalPersistentState( typemoq.It.isValue(LSSurveyStateKeys.ShowAfterCompletionCount), typemoq.It.isAnyNumber() diff --git a/src/test/testing/banners/proposeNewLanguageServerBanner.unit.test.ts b/src/test/testing/banners/proposeNewLanguageServerBanner.unit.test.ts index 8b6525aca9b1..b1732b9b4562 100644 --- a/src/test/testing/banners/proposeNewLanguageServerBanner.unit.test.ts +++ b/src/test/testing/banners/proposeNewLanguageServerBanner.unit.test.ts @@ -34,7 +34,7 @@ suite('Propose New Language Server Banner', () => { }); test('Do not show banner when it is disabled', () => { appShell - .setup(a => + .setup((a) => a.showInformationMessage( typemoq.It.isValue(message), typemoq.It.isValue(yes), @@ -76,26 +76,26 @@ function preparePopup( ): ProposeLanguageServerBanner { const myfactory: typemoq.IMock = typemoq.Mock.ofType(); const val: typemoq.IMock> = typemoq.Mock.ofType>(); - val.setup(a => a.updateValue(typemoq.It.isValue(true))).returns(() => { + val.setup((a) => a.updateValue(typemoq.It.isValue(true))).returns(() => { enabledValue = true; return Promise.resolve(); }); - val.setup(a => a.updateValue(typemoq.It.isValue(false))).returns(() => { + val.setup((a) => a.updateValue(typemoq.It.isValue(false))).returns(() => { enabledValue = false; return Promise.resolve(); }); - val.setup(a => a.value).returns(() => { + val.setup((a) => a.value).returns(() => { return enabledValue; }); myfactory - .setup(a => + .setup((a) => a.createGlobalPersistentState(typemoq.It.isValue(ProposeLSStateKeys.ShowBanner), typemoq.It.isValue(true)) ) .returns(() => { return val.object; }); myfactory - .setup(a => + .setup((a) => a.createGlobalPersistentState(typemoq.It.isValue(ProposeLSStateKeys.ShowBanner), typemoq.It.isValue(false)) ) .returns(() => { diff --git a/src/test/testing/codeLenses/testFiles.unit.test.ts b/src/test/testing/codeLenses/testFiles.unit.test.ts index b49318f17521..861263d2b575 100644 --- a/src/test/testing/codeLenses/testFiles.unit.test.ts +++ b/src/test/testing/codeLenses/testFiles.unit.test.ts @@ -1,157 +1,157 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -// tslint:disable:no-any - -import { assert, expect } from 'chai'; -import { mock } from 'ts-mockito'; -import * as typemoq from 'typemoq'; -import { DocumentSymbolProvider, EventEmitter, Uri } from 'vscode'; -import { IWorkspaceService } from '../../../client/common/application/types'; -import { IFileSystem } from '../../../client/common/platform/types'; -import { IServiceContainer } from '../../../client/ioc/types'; -import { LanguageServerSymbolProvider } from '../../../client/providers/symbolProvider'; -import { TestFileCodeLensProvider } from '../../../client/testing/codeLenses/testFiles'; -import { ITestCollectionStorageService } from '../../../client/testing/common/types'; - -// tslint:disable-next-line: max-func-body-length -suite('Code lenses - Test files', () => { - let testCollectionStorage: typemoq.IMock; - let workspaceService: typemoq.IMock; - let fileSystem: typemoq.IMock; - let serviceContainer: typemoq.IMock; - let symbolProvider: DocumentSymbolProvider; - let onDidChange: EventEmitter; - let codeLensProvider: TestFileCodeLensProvider; - setup(() => { - workspaceService = typemoq.Mock.ofType(); - fileSystem = typemoq.Mock.ofType(); - testCollectionStorage = typemoq.Mock.ofType(); - serviceContainer = typemoq.Mock.ofType(); - symbolProvider = mock(LanguageServerSymbolProvider); - onDidChange = new EventEmitter(); - serviceContainer - .setup(c => c.get(typemoq.It.isValue(IWorkspaceService))) - .returns(() => workspaceService.object); - serviceContainer.setup(c => c.get(typemoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); - codeLensProvider = new TestFileCodeLensProvider( - onDidChange, - symbolProvider, - testCollectionStorage.object, - serviceContainer.object - ); - }); - - teardown(() => { - onDidChange.dispose(); - }); - - test('Function getTestFileWhichNeedsCodeLens() returns `undefined` if there are no workspace corresponding to document', async () => { - const document = { - uri: Uri.file('path/to/document') - }; - workspaceService - .setup(w => w.getWorkspaceFolder(document.uri)) - .returns(() => undefined) - .verifiable(typemoq.Times.once()); - testCollectionStorage - .setup(w => w.getTests(typemoq.It.isAny())) - .returns(() => undefined) - .verifiable(typemoq.Times.never()); - const files = codeLensProvider.getTestFileWhichNeedsCodeLens(document as any); - expect(files).to.equal(undefined, 'No files should be returned'); - workspaceService.verifyAll(); - testCollectionStorage.verifyAll(); - }); - - test('Function getTestFileWhichNeedsCodeLens() returns `undefined` if test storage is empty', async () => { - const document = { - uri: Uri.file('path/to/document') - }; - const workspaceUri = Uri.file('path/to/workspace'); - const workspace = { uri: workspaceUri }; - workspaceService - .setup(w => w.getWorkspaceFolder(document.uri)) - .returns(() => workspace as any) - .verifiable(typemoq.Times.once()); - testCollectionStorage - .setup(w => w.getTests(workspaceUri)) - .returns(() => undefined) - .verifiable(typemoq.Times.once()); - const files = codeLensProvider.getTestFileWhichNeedsCodeLens(document as any); - expect(files).to.equal(undefined, 'No files should be returned'); - workspaceService.verifyAll(); - testCollectionStorage.verifyAll(); - }); - - test('Function getTestFileWhichNeedsCodeLens() returns `undefined` if tests returned from storage does not contain document', async () => { - const document = { - uri: Uri.file('path/to/document5') - }; - const workspaceUri = Uri.file('path/to/workspace'); - const workspace = { uri: workspaceUri }; - const tests = { - testFiles: [ - { - fullPath: 'path/to/document1' - }, - { - fullPath: 'path/to/document2' - } - ] - }; - workspaceService - .setup(w => w.getWorkspaceFolder(document.uri)) - .returns(() => workspace as any) - .verifiable(typemoq.Times.once()); - testCollectionStorage - .setup(w => w.getTests(workspaceUri)) - .returns(() => tests as any) - .verifiable(typemoq.Times.once()); - fileSystem.setup(f => f.arePathsSame('path/to/document1', 'path/to/document5')).returns(() => false); - fileSystem.setup(f => f.arePathsSame('path/to/document2', 'path/to/document5')).returns(() => false); - const files = codeLensProvider.getTestFileWhichNeedsCodeLens(document as any); - expect(files).to.equal(undefined, 'No files should be returned'); - workspaceService.verifyAll(); - testCollectionStorage.verifyAll(); - }); - - test('Function getTestFileWhichNeedsCodeLens() returns test file if tests returned from storage contains document', async () => { - const document = { - uri: Uri.file('path/to/document2') - }; - const workspaceUri = Uri.file('path/to/workspace'); - const workspace = { uri: workspaceUri }; - const testFile2 = { - fullPath: Uri.file('path/to/document2').fsPath - }; - const tests = { - testFiles: [ - { - fullPath: Uri.file('path/to/document1').fsPath - }, - testFile2 - ] - }; - workspaceService - .setup(w => w.getWorkspaceFolder(typemoq.It.isValue(document.uri))) - .returns(() => workspace as any) - .verifiable(typemoq.Times.once()); - testCollectionStorage - .setup(w => w.getTests(typemoq.It.isValue(workspaceUri))) - .returns(() => tests as any) - .verifiable(typemoq.Times.once()); - fileSystem - .setup(f => f.arePathsSame(Uri.file('/path/to/document1').fsPath, Uri.file('/path/to/document2').fsPath)) - .returns(() => false); - fileSystem - .setup(f => f.arePathsSame(Uri.file('/path/to/document2').fsPath, Uri.file('/path/to/document2').fsPath)) - .returns(() => true); - const files = codeLensProvider.getTestFileWhichNeedsCodeLens(document as any); - assert.deepEqual(files, testFile2 as any); - workspaceService.verifyAll(); - testCollectionStorage.verifyAll(); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// tslint:disable:no-any + +import { assert, expect } from 'chai'; +import { mock } from 'ts-mockito'; +import * as typemoq from 'typemoq'; +import { DocumentSymbolProvider, EventEmitter, Uri } from 'vscode'; +import { IWorkspaceService } from '../../../client/common/application/types'; +import { IFileSystem } from '../../../client/common/platform/types'; +import { IServiceContainer } from '../../../client/ioc/types'; +import { LanguageServerSymbolProvider } from '../../../client/providers/symbolProvider'; +import { TestFileCodeLensProvider } from '../../../client/testing/codeLenses/testFiles'; +import { ITestCollectionStorageService } from '../../../client/testing/common/types'; + +// tslint:disable-next-line: max-func-body-length +suite('Code lenses - Test files', () => { + let testCollectionStorage: typemoq.IMock; + let workspaceService: typemoq.IMock; + let fileSystem: typemoq.IMock; + let serviceContainer: typemoq.IMock; + let symbolProvider: DocumentSymbolProvider; + let onDidChange: EventEmitter; + let codeLensProvider: TestFileCodeLensProvider; + setup(() => { + workspaceService = typemoq.Mock.ofType(); + fileSystem = typemoq.Mock.ofType(); + testCollectionStorage = typemoq.Mock.ofType(); + serviceContainer = typemoq.Mock.ofType(); + symbolProvider = mock(LanguageServerSymbolProvider); + onDidChange = new EventEmitter(); + serviceContainer + .setup((c) => c.get(typemoq.It.isValue(IWorkspaceService))) + .returns(() => workspaceService.object); + serviceContainer.setup((c) => c.get(typemoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); + codeLensProvider = new TestFileCodeLensProvider( + onDidChange, + symbolProvider, + testCollectionStorage.object, + serviceContainer.object + ); + }); + + teardown(() => { + onDidChange.dispose(); + }); + + test('Function getTestFileWhichNeedsCodeLens() returns `undefined` if there are no workspace corresponding to document', async () => { + const document = { + uri: Uri.file('path/to/document') + }; + workspaceService + .setup((w) => w.getWorkspaceFolder(document.uri)) + .returns(() => undefined) + .verifiable(typemoq.Times.once()); + testCollectionStorage + .setup((w) => w.getTests(typemoq.It.isAny())) + .returns(() => undefined) + .verifiable(typemoq.Times.never()); + const files = codeLensProvider.getTestFileWhichNeedsCodeLens(document as any); + expect(files).to.equal(undefined, 'No files should be returned'); + workspaceService.verifyAll(); + testCollectionStorage.verifyAll(); + }); + + test('Function getTestFileWhichNeedsCodeLens() returns `undefined` if test storage is empty', async () => { + const document = { + uri: Uri.file('path/to/document') + }; + const workspaceUri = Uri.file('path/to/workspace'); + const workspace = { uri: workspaceUri }; + workspaceService + .setup((w) => w.getWorkspaceFolder(document.uri)) + .returns(() => workspace as any) + .verifiable(typemoq.Times.once()); + testCollectionStorage + .setup((w) => w.getTests(workspaceUri)) + .returns(() => undefined) + .verifiable(typemoq.Times.once()); + const files = codeLensProvider.getTestFileWhichNeedsCodeLens(document as any); + expect(files).to.equal(undefined, 'No files should be returned'); + workspaceService.verifyAll(); + testCollectionStorage.verifyAll(); + }); + + test('Function getTestFileWhichNeedsCodeLens() returns `undefined` if tests returned from storage does not contain document', async () => { + const document = { + uri: Uri.file('path/to/document5') + }; + const workspaceUri = Uri.file('path/to/workspace'); + const workspace = { uri: workspaceUri }; + const tests = { + testFiles: [ + { + fullPath: 'path/to/document1' + }, + { + fullPath: 'path/to/document2' + } + ] + }; + workspaceService + .setup((w) => w.getWorkspaceFolder(document.uri)) + .returns(() => workspace as any) + .verifiable(typemoq.Times.once()); + testCollectionStorage + .setup((w) => w.getTests(workspaceUri)) + .returns(() => tests as any) + .verifiable(typemoq.Times.once()); + fileSystem.setup((f) => f.arePathsSame('path/to/document1', 'path/to/document5')).returns(() => false); + fileSystem.setup((f) => f.arePathsSame('path/to/document2', 'path/to/document5')).returns(() => false); + const files = codeLensProvider.getTestFileWhichNeedsCodeLens(document as any); + expect(files).to.equal(undefined, 'No files should be returned'); + workspaceService.verifyAll(); + testCollectionStorage.verifyAll(); + }); + + test('Function getTestFileWhichNeedsCodeLens() returns test file if tests returned from storage contains document', async () => { + const document = { + uri: Uri.file('path/to/document2') + }; + const workspaceUri = Uri.file('path/to/workspace'); + const workspace = { uri: workspaceUri }; + const testFile2 = { + fullPath: Uri.file('path/to/document2').fsPath + }; + const tests = { + testFiles: [ + { + fullPath: Uri.file('path/to/document1').fsPath + }, + testFile2 + ] + }; + workspaceService + .setup((w) => w.getWorkspaceFolder(typemoq.It.isValue(document.uri))) + .returns(() => workspace as any) + .verifiable(typemoq.Times.once()); + testCollectionStorage + .setup((w) => w.getTests(typemoq.It.isValue(workspaceUri))) + .returns(() => tests as any) + .verifiable(typemoq.Times.once()); + fileSystem + .setup((f) => f.arePathsSame(Uri.file('/path/to/document1').fsPath, Uri.file('/path/to/document2').fsPath)) + .returns(() => false); + fileSystem + .setup((f) => f.arePathsSame(Uri.file('/path/to/document2').fsPath, Uri.file('/path/to/document2').fsPath)) + .returns(() => true); + const files = codeLensProvider.getTestFileWhichNeedsCodeLens(document as any); + assert.deepEqual(files, testFile2 as any); + workspaceService.verifyAll(); + testCollectionStorage.verifyAll(); + }); +}); diff --git a/src/test/testing/common/debugLauncher.unit.test.ts b/src/test/testing/common/debugLauncher.unit.test.ts index 865e82a8af40..488c4dfe7714 100644 --- a/src/test/testing/common/debugLauncher.unit.test.ts +++ b/src/test/testing/common/debugLauncher.unit.test.ts @@ -50,43 +50,45 @@ suite('Unit Tests - Debug Launcher', () => { serviceContainer = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); const configService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) .returns(() => configService.object); debugService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDebugService))).returns(() => debugService.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IDebugService))).returns(() => debugService.object); hasWorkspaceFolders = true; workspaceService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - workspaceService.setup(u => u.hasWorkspaceFolders).returns(() => hasWorkspaceFolders); + workspaceService.setup((u) => u.hasWorkspaceFolders).returns(() => hasWorkspaceFolders); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); platformService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IPlatformService))).returns(() => platformService.object); + serviceContainer + .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) + .returns(() => platformService.object); filesystem = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => filesystem.object); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => filesystem.object); const appShell = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - appShell.setup(a => a.showErrorMessage(TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined)); - serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); + appShell.setup((a) => a.showErrorMessage(TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined)); + serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); settings = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); + configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); unitTestSettings = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - settings.setup(p => p.testing).returns(() => unitTestSettings.object); + settings.setup((p) => p.testing).returns(() => unitTestSettings.object); debugEnvHelper = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(IDebugEnvironmentVariablesService))) + .setup((c) => c.get(TypeMoq.It.isValue(IDebugEnvironmentVariablesService))) .returns(() => debugEnvHelper.object); configExperiment = TypeMoq.Mock.ofType(undefined); serviceContainer - .setup(c => c.get(TypeMoq.It.isValue(ILaunchDebugConfigurationResolverExperiment))) + .setup((c) => c.get(TypeMoq.It.isValue(ILaunchDebugConfigurationResolverExperiment))) .returns(() => configExperiment.object); debugLauncher = new DebugLauncher(serviceContainer.object, getNewResolver(configService.object)); @@ -97,10 +99,10 @@ suite('Unit Tests - Debug Launcher', () => { TypeMoq.MockBehavior.Strict ); validator - .setup(v => v.validatePythonPath(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((v) => v.validatePythonPath(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(true)); configExperiment - .setup(c => c.modifyConfigurationBasedOnExperiment(TypeMoq.It.isAny())) + .setup((c) => c.modifyConfigurationBasedOnExperiment(TypeMoq.It.isAny())) .returns(() => { return; }); @@ -119,20 +121,20 @@ suite('Unit Tests - Debug Launcher', () => { expected: DebugConfiguration, testProvider: TestProvider ) { - platformService.setup(p => p.isWindows).returns(() => /^win/.test(process.platform)); - settings.setup(p => p.pythonPath).returns(() => 'python'); - settings.setup(p => p.envFile).returns(() => __filename); + platformService.setup((p) => p.isWindows).returns(() => /^win/.test(process.platform)); + settings.setup((p) => p.pythonPath).returns(() => 'python'); + settings.setup((p) => p.envFile).returns(() => __filename); const args = expected.args; const debugArgs = testProvider === 'unittest' ? args.filter((item: string) => item !== '--debug') : args; expected.args = debugArgs; debugEnvHelper - .setup(d => d.getEnvironmentVariables(TypeMoq.It.isAny())) + .setup((d) => d.getEnvironmentVariables(TypeMoq.It.isAny())) .returns(() => Promise.resolve(expected.env)); //debugService.setup(d => d.startDebugging(TypeMoq.It.isValue(workspaceFolder), TypeMoq.It.isValue(expected))) debugService - .setup(d => d.startDebugging(TypeMoq.It.isValue(workspaceFolder), TypeMoq.It.isValue(expected))) + .setup((d) => d.startDebugging(TypeMoq.It.isValue(workspaceFolder), TypeMoq.It.isValue(expected))) .returns((_wspc: WorkspaceFolder, _expectedParam: DebugConfiguration) => { return Promise.resolve(undefined as any); }) @@ -183,13 +185,13 @@ suite('Unit Tests - Debug Launcher', () => { const testLaunchScript = getTestLauncherScript(testProvider); const workspaceFolders = [createWorkspaceFolder(options.cwd), createWorkspaceFolder('five/six/seven')]; - workspaceService.setup(u => u.workspaceFolders).returns(() => workspaceFolders); - workspaceService.setup(u => u.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolders[0]); + workspaceService.setup((u) => u.workspaceFolders).returns(() => workspaceFolders); + workspaceService.setup((u) => u.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => workspaceFolders[0]); if (!debugConfigs) { - filesystem.setup(fs => fs.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); + filesystem.setup((fs) => fs.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(false)); } else { - filesystem.setup(fs => fs.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); + filesystem.setup((fs) => fs.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); if (typeof debugConfigs !== 'string') { debugConfigs = JSON.stringify({ version: '0.1.0', @@ -197,7 +199,7 @@ suite('Unit Tests - Debug Launcher', () => { }); } filesystem - .setup(fs => fs.readFile(TypeMoq.It.isAny())) + .setup((fs) => fs.readFile(TypeMoq.It.isAny())) .returns(() => Promise.resolve(debugConfigs as string)); } @@ -245,7 +247,7 @@ suite('Unit Tests - Debug Launcher', () => { const testProviders: TestProvider[] = ['nosetest', 'pytest', 'unittest']; // tslint:disable-next-line:max-func-body-length - testProviders.forEach(testProvider => { + testProviders.forEach((testProvider) => { const testTitleSuffix = `(Test Framework '${testProvider}')`; test(`Must launch debugger ${testTitleSuffix}`, async () => { @@ -274,7 +276,7 @@ suite('Unit Tests - Debug Launcher', () => { }); test(`Must not launch debugger if cancelled ${testTitleSuffix}`, async () => { debugService - .setup(d => d.startDebugging(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((d) => d.startDebugging(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => { return Promise.resolve(undefined as any); }) @@ -292,7 +294,7 @@ suite('Unit Tests - Debug Launcher', () => { test(`Must throw an exception if there are no workspaces ${testTitleSuffix}`, async () => { hasWorkspaceFolders = false; debugService - .setup(d => d.startDebugging(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .setup((d) => d.startDebugging(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => Promise.resolve(undefined as any)) .verifiable(TypeMoq.Times.never()); @@ -432,10 +434,7 @@ suite('Unit Tests - Debug Launcher', () => { ' ]; for (const text of malformedFiles) { - const testID = text - .split('\n')[0] - .substring(3) - .trim(); + const testID = text.split('\n')[0].substring(3).trim(); test(`Handles malformed launch.json - ${testID}`, async () => { const options: LaunchOptions = { cwd: 'one/two/three', @@ -581,8 +580,8 @@ suite('Unit Tests - Debug Launcher', () => { const workspaceFolder = { name: 'abc', index: 0, uri: Uri.file(__filename) }; const filename = path.join(workspaceFolder.uri.fsPath, '.vscode', 'launch.json'); const jsonc = '{"version":"1234", "configurations":[1,2,],}'; - filesystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(filename))).returns(() => Promise.resolve(true)); - filesystem.setup(fs => fs.readFile(TypeMoq.It.isValue(filename))).returns(() => Promise.resolve(jsonc)); + filesystem.setup((fs) => fs.fileExists(TypeMoq.It.isValue(filename))).returns(() => Promise.resolve(true)); + filesystem.setup((fs) => fs.readFile(TypeMoq.It.isValue(filename))).returns(() => Promise.resolve(jsonc)); const configs = await debugLauncher.readAllDebugConfigs(workspaceFolder); @@ -593,8 +592,8 @@ suite('Unit Tests - Debug Launcher', () => { const filename = path.join(workspaceFolder.uri.fsPath, '.vscode', 'launch.json'); const jsonc = '{"version":"1234"'; - filesystem.setup(fs => fs.fileExists(TypeMoq.It.isValue(filename))).returns(() => Promise.resolve(true)); - filesystem.setup(fs => fs.readFile(TypeMoq.It.isValue(filename))).returns(() => Promise.resolve(jsonc)); + filesystem.setup((fs) => fs.fileExists(TypeMoq.It.isValue(filename))).returns(() => Promise.resolve(true)); + filesystem.setup((fs) => fs.readFile(TypeMoq.It.isValue(filename))).returns(() => Promise.resolve(jsonc)); const configs = await debugLauncher.readAllDebugConfigs(workspaceFolder); diff --git a/src/test/testing/common/managers/baseTestManager.unit.test.ts b/src/test/testing/common/managers/baseTestManager.unit.test.ts index e2c8aa42a304..47adcb9809c8 100644 --- a/src/test/testing/common/managers/baseTestManager.unit.test.ts +++ b/src/test/testing/common/managers/baseTestManager.unit.test.ts @@ -56,7 +56,7 @@ suite('Unit Tests - Base Test Manager', () => { { name: 'nose', class: NoseTestManager }, { name: 'pytest', class: PyTestTestManager }, { name: 'unittest', class: UnitTestTestManager } - ].forEach(item => { + ].forEach((item) => { suite(item.name, () => { let testManager: ITestManager; const workspaceFolder = Uri.file(__dirname); @@ -147,7 +147,7 @@ suite('Unit Tests - Base Test Manager', () => { verify(commandManager.executeCommand('setContext', 'testsDiscovered', true)).once(); }); - test('When failing to discover tests prompt to install test framework', async function() { + test('When failing to discover tests prompt to install test framework', async function () { if (item.name === 'unittest') { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -164,7 +164,7 @@ suite('Unit Tests - Base Test Manager', () => { verify(installer.isInstalled(anything(), anything())).once(); verify(installer.promptToInstall(anything(), anything())).once(); }); - test('When failing to discover tests do not prompt to install test framework', async function() { + test('When failing to discover tests do not prompt to install test framework', async function () { if (item.name === 'unittest') { // tslint:disable-next-line: no-invalid-this return this.skip(); @@ -181,7 +181,7 @@ suite('Unit Tests - Base Test Manager', () => { verify(installer.isInstalled(anything(), anything())).never(); verify(installer.promptToInstall(anything(), anything())).never(); }); - test('When failing to discover tests do not prompt to install test framework if installed', async function() { + test('When failing to discover tests do not prompt to install test framework if installed', async function () { if (item.name === 'unittest') { // tslint:disable-next-line: no-invalid-this return this.skip(); diff --git a/src/test/testing/common/managers/testConfigurationManager.unit.test.ts b/src/test/testing/common/managers/testConfigurationManager.unit.test.ts index ef894650a100..7baa68ef07e4 100644 --- a/src/test/testing/common/managers/testConfigurationManager.unit.test.ts +++ b/src/test/testing/common/managers/testConfigurationManager.unit.test.ts @@ -25,9 +25,9 @@ class MockTestConfigurationManager extends TestConfigurationManager { } suite('Unit Test Configuration Manager (unit)', () => { - UNIT_TEST_PRODUCTS.forEach(product => { + UNIT_TEST_PRODUCTS.forEach((product) => { const prods = getNamesAndValues(Product); - const productName = prods.filter(item => item.value === product)[0]; + const productName = prods.filter((item) => item.value === product)[0]; suite(productName.name, () => { const workspaceUri = Uri.file(__dirname); let manager: TestConfigurationManager; @@ -39,12 +39,12 @@ suite('Unit Test Configuration Manager (unit)', () => { const installer = TypeMoq.Mock.ofType().object; const serviceContainer = TypeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isValue(TEST_OUTPUT_CHANNEL))) + .setup((s) => s.get(TypeMoq.It.isValue(IOutputChannel), TypeMoq.It.isValue(TEST_OUTPUT_CHANNEL))) .returns(() => outputChannel); serviceContainer - .setup(s => s.get(TypeMoq.It.isValue(ITestConfigSettingsService))) + .setup((s) => s.get(TypeMoq.It.isValue(ITestConfigSettingsService))) .returns(() => configService.object); - serviceContainer.setup(s => s.get(TypeMoq.It.isValue(IInstaller))).returns(() => installer); + serviceContainer.setup((s) => s.get(TypeMoq.It.isValue(IInstaller))).returns(() => installer); manager = new MockTestConfigurationManager( workspaceUri, product as UnitTestProduct, @@ -53,14 +53,14 @@ suite('Unit Test Configuration Manager (unit)', () => { }); test('Enabling a test product shoud disable other products', async () => { - UNIT_TEST_PRODUCTS.filter(item => item !== product).forEach(productToDisable => { + UNIT_TEST_PRODUCTS.filter((item) => item !== product).forEach((productToDisable) => { configService - .setup(c => c.disable(TypeMoq.It.isValue(workspaceUri), TypeMoq.It.isValue(productToDisable))) + .setup((c) => c.disable(TypeMoq.It.isValue(workspaceUri), TypeMoq.It.isValue(productToDisable))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); }); configService - .setup(c => c.enable(TypeMoq.It.isValue(workspaceUri), TypeMoq.It.isValue(product))) + .setup((c) => c.enable(TypeMoq.It.isValue(workspaceUri), TypeMoq.It.isValue(product))) .returns(() => Promise.resolve(undefined)) .verifiable(TypeMoq.Times.once()); diff --git a/src/test/testing/common/services/configSettingService.unit.test.ts b/src/test/testing/common/services/configSettingService.unit.test.ts index 06e8896b9794..09e947efb25f 100644 --- a/src/test/testing/common/services/configSettingService.unit.test.ts +++ b/src/test/testing/common/services/configSettingService.unit.test.ts @@ -30,11 +30,11 @@ const updateMethods: (keyof Omit { - UNIT_TEST_PRODUCTS.forEach(product => { + UNIT_TEST_PRODUCTS.forEach((product) => { const prods = getNamesAndValues(Product); - const productName = prods.filter(item => item.value === product)[0]; + const productName = prods.filter((item) => item.value === product)[0]; const workspaceUri = Uri.file(__filename); - updateMethods.forEach(updateMethod => { + updateMethods.forEach((updateMethod) => { suite(`Test '${updateMethod}' method with ${productName.name}`, () => { let testConfigSettingsService: ITestConfigSettingsService; let workspaceService: typeMoq.IMock; @@ -43,7 +43,7 @@ suite('Unit Tests - ConfigSettingsService', () => { workspaceService = typeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(typeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); testConfigSettingsService = new TestConfigSettingsService(serviceContainer.object); }); @@ -89,20 +89,20 @@ suite('Unit Tests - ConfigSettingsService', () => { } test('Update Test Arguments with workspace Uri without workspaces', async () => { workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => false) .verifiable(typeMoq.Times.atLeastOnce()); const pythonConfig = typeMoq.Mock.ofType(); workspaceService - .setup(w => w.getConfiguration(typeMoq.It.isValue('python'))) + .setup((w) => w.getConfiguration(typeMoq.It.isValue('python'))) .returns(() => pythonConfig.object) .verifiable(typeMoq.Times.once()); const { configValue, configName } = getExpectedValueAndSettings(); pythonConfig - .setup(p => p.update(typeMoq.It.isValue(configName), typeMoq.It.isValue(configValue))) + .setup((p) => p.update(typeMoq.It.isValue(configName), typeMoq.It.isValue(configValue))) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); @@ -116,29 +116,31 @@ suite('Unit Tests - ConfigSettingsService', () => { }); test('Update Test Arguments with workspace Uri with one workspace', async () => { workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(typeMoq.Times.atLeastOnce()); const workspaceFolder = typeMoq.Mock.ofType(); workspaceFolder - .setup(w => w.uri) + .setup((w) => w.uri) .returns(() => workspaceUri) .verifiable(typeMoq.Times.atLeastOnce()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder.object]) .verifiable(typeMoq.Times.atLeastOnce()); const pythonConfig = typeMoq.Mock.ofType(); workspaceService - .setup(w => w.getConfiguration(typeMoq.It.isValue('python'), typeMoq.It.isValue(workspaceUri))) + .setup((w) => + w.getConfiguration(typeMoq.It.isValue('python'), typeMoq.It.isValue(workspaceUri)) + ) .returns(() => pythonConfig.object) .verifiable(typeMoq.Times.once()); const { configValue, configName } = getExpectedValueAndSettings(); pythonConfig - .setup(p => p.update(typeMoq.It.isValue(configName), typeMoq.It.isValue(configValue))) + .setup((p) => p.update(typeMoq.It.isValue(configName), typeMoq.It.isValue(configValue))) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); @@ -153,33 +155,35 @@ suite('Unit Tests - ConfigSettingsService', () => { }); test('Update Test Arguments with workspace Uri with more than one workspace and uri belongs to a workspace', async () => { workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(typeMoq.Times.atLeastOnce()); const workspaceFolder = typeMoq.Mock.ofType(); workspaceFolder - .setup(w => w.uri) + .setup((w) => w.uri) .returns(() => workspaceUri) .verifiable(typeMoq.Times.atLeastOnce()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder.object, workspaceFolder.object]) .verifiable(typeMoq.Times.atLeastOnce()); workspaceService - .setup(w => w.getWorkspaceFolder(typeMoq.It.isValue(workspaceUri))) + .setup((w) => w.getWorkspaceFolder(typeMoq.It.isValue(workspaceUri))) .returns(() => workspaceFolder.object) .verifiable(typeMoq.Times.once()); const pythonConfig = typeMoq.Mock.ofType(); workspaceService - .setup(w => w.getConfiguration(typeMoq.It.isValue('python'), typeMoq.It.isValue(workspaceUri))) + .setup((w) => + w.getConfiguration(typeMoq.It.isValue('python'), typeMoq.It.isValue(workspaceUri)) + ) .returns(() => pythonConfig.object) .verifiable(typeMoq.Times.once()); const { configValue, configName } = getExpectedValueAndSettings(); pythonConfig - .setup(p => p.update(typeMoq.It.isValue(configName), typeMoq.It.isValue(configValue))) + .setup((p) => p.update(typeMoq.It.isValue(configName), typeMoq.It.isValue(configValue))) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); @@ -194,21 +198,21 @@ suite('Unit Tests - ConfigSettingsService', () => { }); test('Expect an exception when updating Test Arguments with workspace Uri with more than one workspace and uri does not belong to a workspace', async () => { workspaceService - .setup(w => w.hasWorkspaceFolders) + .setup((w) => w.hasWorkspaceFolders) .returns(() => true) .verifiable(typeMoq.Times.atLeastOnce()); const workspaceFolder = typeMoq.Mock.ofType(); workspaceFolder - .setup(w => w.uri) + .setup((w) => w.uri) .returns(() => workspaceUri) .verifiable(typeMoq.Times.atLeastOnce()); workspaceService - .setup(w => w.workspaceFolders) + .setup((w) => w.workspaceFolders) .returns(() => [workspaceFolder.object, workspaceFolder.object]) .verifiable(typeMoq.Times.atLeastOnce()); workspaceService - .setup(w => w.getWorkspaceFolder(typeMoq.It.isValue(workspaceUri))) + .setup((w) => w.getWorkspaceFolder(typeMoq.It.isValue(workspaceUri))) .returns(() => undefined) .verifiable(typeMoq.Times.once()); @@ -228,7 +232,7 @@ suite('Unit Tests - BufferedTestConfigSettingsService', () => { const testDir = '/my/project'; const newArgs: string[] = ['-x', '--spam=42']; const cfg = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); - cfg.setup(c => + cfg.setup((c) => c.updateTestArgs( typeMoq.It.isValue(testDir), typeMoq.It.isValue(Product.pytest), @@ -237,13 +241,13 @@ suite('Unit Tests - BufferedTestConfigSettingsService', () => { ) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); - cfg.setup(c => c.disable(typeMoq.It.isValue(testDir), typeMoq.It.isValue(Product.unittest))) + cfg.setup((c) => c.disable(typeMoq.It.isValue(testDir), typeMoq.It.isValue(Product.unittest))) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); - cfg.setup(c => c.disable(typeMoq.It.isValue(testDir), typeMoq.It.isValue(Product.nosetest))) + cfg.setup((c) => c.disable(typeMoq.It.isValue(testDir), typeMoq.It.isValue(Product.nosetest))) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); - cfg.setup(c => c.enable(typeMoq.It.isValue(testDir), typeMoq.It.isValue(Product.pytest))) + cfg.setup((c) => c.enable(typeMoq.It.isValue(testDir), typeMoq.It.isValue(Product.pytest))) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); @@ -262,7 +266,7 @@ suite('Unit Tests - BufferedTestConfigSettingsService', () => { test('applied changes are cleared', async () => { const cfg = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); - cfg.setup(c => c.enable(typeMoq.It.isAny(), typeMoq.It.isAny())) + cfg.setup((c) => c.enable(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); diff --git a/src/test/testing/common/services/discoveredTestParser.unit.test.ts b/src/test/testing/common/services/discoveredTestParser.unit.test.ts index 0542d5c41acb..9238e75822db 100644 --- a/src/test/testing/common/services/discoveredTestParser.unit.test.ts +++ b/src/test/testing/common/services/discoveredTestParser.unit.test.ts @@ -1,120 +1,120 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as assert from 'assert'; -import * as sinon from 'sinon'; -import * as typemoq from 'typemoq'; -import { Uri } from 'vscode'; -import { IWorkspaceService } from '../../../../client/common/application/types'; -import { TestDiscoveredTestParser } from '../../../../client/testing/common/services/discoveredTestParser'; -import { Tests } from '../../../../client/testing/common/types'; - -// tslint:disable:no-any max-func-body-length -suite('Services - Discovered test parser', () => { - let workspaceService: typemoq.IMock; - let parser: TestDiscoveredTestParser; - setup(() => { - workspaceService = typemoq.Mock.ofType(); - }); - - teardown(() => { - sinon.restore(); - }); - - test('Parse returns empty tests if resource does not belong to workspace', () => { - // That is, getWorkspaceFolder() returns undefined. - const expectedTests: Tests = { - rootTestFolders: [], - summary: { errors: 0, failures: 0, passed: 0, skipped: 0 }, - testFiles: [], - testFolders: [], - testFunctions: [], - testSuites: [] - }; - const discoveredTests = [ - { - root: 'path/to/testDataRoot' - } - ]; - const buildChildren = sinon.stub(TestDiscoveredTestParser.prototype, 'buildChildren'); - buildChildren.callsFake(() => undefined); - workspaceService - .setup(w => w.getWorkspaceFolder(typemoq.It.isAny())) - .returns(() => undefined) - .verifiable(typemoq.Times.once()); - parser = new TestDiscoveredTestParser(workspaceService.object); - const result = parser.parse(Uri.file('path/to/resource'), discoveredTests as any); - assert.ok(buildChildren.notCalled); - assert.deepEqual(expectedTests, result); - workspaceService.verifyAll(); - }); - - test('Parse returns expected tests otherwise', () => { - const discoveredTests = [ - { - root: 'path/to/testDataRoot1', - rootid: 'rootId1' - }, - { - root: 'path/to/testDataRoot2', - rootid: 'rootId2' - } - ]; - const workspaceUri = Uri.file('path/to/workspace'); - const workspace = { uri: workspaceUri }; - const expectedTests: Tests = { - rootTestFolders: [ - { - name: 'path/to/testDataRoot1', - folders: [], - time: 0, - testFiles: [], - resource: workspaceUri, - nameToRun: 'rootId1' - }, - { - name: 'path/to/testDataRoot2', - folders: [], - time: 0, - testFiles: [], - resource: workspaceUri, - nameToRun: 'rootId2' - } - ], - summary: { errors: 0, failures: 0, passed: 0, skipped: 0 }, - testFiles: [], - testFolders: [ - { - name: 'path/to/testDataRoot1', - folders: [], - time: 0, - testFiles: [], - resource: workspaceUri, - nameToRun: 'rootId1' - }, - { - name: 'path/to/testDataRoot2', - folders: [], - time: 0, - testFiles: [], - resource: workspaceUri, - nameToRun: 'rootId2' - } - ], - testFunctions: [], - testSuites: [] - }; - const buildChildren = sinon.stub(TestDiscoveredTestParser.prototype, 'buildChildren'); - buildChildren.callsFake(() => undefined); - workspaceService - .setup(w => w.getWorkspaceFolder(typemoq.It.isAny())) - .returns(() => workspace as any) - .verifiable(typemoq.Times.once()); - parser = new TestDiscoveredTestParser(workspaceService.object); - const result = parser.parse(workspaceUri, discoveredTests as any); - assert.ok(buildChildren.calledTwice); - assert.deepEqual(expectedTests, result); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import * as typemoq from 'typemoq'; +import { Uri } from 'vscode'; +import { IWorkspaceService } from '../../../../client/common/application/types'; +import { TestDiscoveredTestParser } from '../../../../client/testing/common/services/discoveredTestParser'; +import { Tests } from '../../../../client/testing/common/types'; + +// tslint:disable:no-any max-func-body-length +suite('Services - Discovered test parser', () => { + let workspaceService: typemoq.IMock; + let parser: TestDiscoveredTestParser; + setup(() => { + workspaceService = typemoq.Mock.ofType(); + }); + + teardown(() => { + sinon.restore(); + }); + + test('Parse returns empty tests if resource does not belong to workspace', () => { + // That is, getWorkspaceFolder() returns undefined. + const expectedTests: Tests = { + rootTestFolders: [], + summary: { errors: 0, failures: 0, passed: 0, skipped: 0 }, + testFiles: [], + testFolders: [], + testFunctions: [], + testSuites: [] + }; + const discoveredTests = [ + { + root: 'path/to/testDataRoot' + } + ]; + const buildChildren = sinon.stub(TestDiscoveredTestParser.prototype, 'buildChildren'); + buildChildren.callsFake(() => undefined); + workspaceService + .setup((w) => w.getWorkspaceFolder(typemoq.It.isAny())) + .returns(() => undefined) + .verifiable(typemoq.Times.once()); + parser = new TestDiscoveredTestParser(workspaceService.object); + const result = parser.parse(Uri.file('path/to/resource'), discoveredTests as any); + assert.ok(buildChildren.notCalled); + assert.deepEqual(expectedTests, result); + workspaceService.verifyAll(); + }); + + test('Parse returns expected tests otherwise', () => { + const discoveredTests = [ + { + root: 'path/to/testDataRoot1', + rootid: 'rootId1' + }, + { + root: 'path/to/testDataRoot2', + rootid: 'rootId2' + } + ]; + const workspaceUri = Uri.file('path/to/workspace'); + const workspace = { uri: workspaceUri }; + const expectedTests: Tests = { + rootTestFolders: [ + { + name: 'path/to/testDataRoot1', + folders: [], + time: 0, + testFiles: [], + resource: workspaceUri, + nameToRun: 'rootId1' + }, + { + name: 'path/to/testDataRoot2', + folders: [], + time: 0, + testFiles: [], + resource: workspaceUri, + nameToRun: 'rootId2' + } + ], + summary: { errors: 0, failures: 0, passed: 0, skipped: 0 }, + testFiles: [], + testFolders: [ + { + name: 'path/to/testDataRoot1', + folders: [], + time: 0, + testFiles: [], + resource: workspaceUri, + nameToRun: 'rootId1' + }, + { + name: 'path/to/testDataRoot2', + folders: [], + time: 0, + testFiles: [], + resource: workspaceUri, + nameToRun: 'rootId2' + } + ], + testFunctions: [], + testSuites: [] + }; + const buildChildren = sinon.stub(TestDiscoveredTestParser.prototype, 'buildChildren'); + buildChildren.callsFake(() => undefined); + workspaceService + .setup((w) => w.getWorkspaceFolder(typemoq.It.isAny())) + .returns(() => workspace as any) + .verifiable(typemoq.Times.once()); + parser = new TestDiscoveredTestParser(workspaceService.object); + const result = parser.parse(workspaceUri, discoveredTests as any); + assert.ok(buildChildren.calledTwice); + assert.deepEqual(expectedTests, result); + }); +}); diff --git a/src/test/testing/common/services/discovery.unit.test.ts b/src/test/testing/common/services/discovery.unit.test.ts index b0bed2c86088..972cbfe6acb8 100644 --- a/src/test/testing/common/services/discovery.unit.test.ts +++ b/src/test/testing/common/services/discovery.unit.test.ts @@ -80,7 +80,7 @@ suite('Unit Tests - Common Discovery', () => { when(executionFactory.createActivatedEnvironment(deepEqual(creationOptions))).thenResolve(execService.object); const executionResult = { stdout: discoveredTests }; execService - .setup(e => e.exec(typemoq.It.isValue([pythonFile, ...options.args]), typemoq.It.isValue(spawnOptions))) + .setup((e) => e.exec(typemoq.It.isValue([pythonFile, ...options.args]), typemoq.It.isValue(spawnOptions))) .returns(() => Promise.resolve(executionResult)); const result = await discovery.exec(options); diff --git a/src/test/testing/common/services/testStatusService.unit.test.ts b/src/test/testing/common/services/testStatusService.unit.test.ts index b4952b266568..f2a689c3d5a2 100644 --- a/src/test/testing/common/services/testStatusService.unit.test.ts +++ b/src/test/testing/common/services/testStatusService.unit.test.ts @@ -132,8 +132,8 @@ suite('Unit Tests - Tests Status Updater', () => { } tests.testFolders.forEach(validate); tests.testFiles.forEach(validate); - tests.testFunctions.forEach(func => validate(func.testFunction)); - tests.testSuites.forEach(suite => validate(suite.testSuite)); + tests.testFunctions.forEach((func) => validate(func.testFunction)); + tests.testSuites.forEach((suite) => validate(suite.testSuite)); }); test('Updating unknown status will recursively update all items and triggers an update for each', () => { updater.updateStatusAsUnknown(workspaceUri, tests); @@ -144,8 +144,8 @@ suite('Unit Tests - Tests Status Updater', () => { } tests.testFolders.forEach(validate); tests.testFiles.forEach(validate); - tests.testFunctions.forEach(func => validate(func.testFunction)); - tests.testSuites.forEach(suite => validate(suite.testSuite)); + tests.testFunctions.forEach((func) => validate(func.testFunction)); + tests.testSuites.forEach((suite) => validate(suite.testSuite)); }); test('Updating running status will recursively update all items and triggers an update for each', () => { updater.updateStatusAsRunning(workspaceUri, tests); @@ -156,8 +156,8 @@ suite('Unit Tests - Tests Status Updater', () => { } tests.testFolders.forEach(validate); tests.testFiles.forEach(validate); - tests.testFunctions.forEach(func => validate(func.testFunction)); - tests.testSuites.forEach(suite => validate(suite.testSuite)); + tests.testFunctions.forEach((func) => validate(func.testFunction)); + tests.testSuites.forEach((suite) => validate(suite.testSuite)); }); test('Updating running status for failed tests will recursively update all items and triggers an update for each', () => { tests.testFolders[1].status = TestStatus.Fail; @@ -191,7 +191,7 @@ suite('Unit Tests - Tests Status Updater', () => { tests.testSuites[1].testSuite, tests.testSuites[2].testSuite ]; - failedItems.forEach(failedItem => visitRecursive(tests, failedItem, visitor)); + failedItems.forEach((failedItem) => visitRecursive(tests, failedItem, visitor)); for (const item of updatedItems) { assert.equal(item.status, TestStatus.Running); @@ -240,8 +240,8 @@ suite('Unit Tests - Tests Status Updater', () => { const updatedItems: TestDataItem[] = [ ...tests.testFolders, ...tests.testFiles, - ...tests.testFunctions.map(item => item.testFunction), - ...tests.testSuites.map(item => item.testSuite) + ...tests.testFunctions.map((item) => item.testFunction), + ...tests.testSuites.map((item) => item.testSuite) ]; for (const item of updatedItems) { diff --git a/src/test/testing/common/testUtils.unit.test.ts b/src/test/testing/common/testUtils.unit.test.ts index e5153c6e8d24..82822609856f 100644 --- a/src/test/testing/common/testUtils.unit.test.ts +++ b/src/test/testing/common/testUtils.unit.test.ts @@ -35,7 +35,7 @@ import { TestDataItem, TestDataItemType, TestWorkspaceFolder } from '../../../cl function longestCommonSubstring(strings: string[]): string { strings = strings.concat().sort(); let substr = strings.shift() || ''; - strings.forEach(str => { + strings.forEach((str) => { for (const [idx, ch] of [...substr].entries()) { if (str[idx] !== ch) { substr = substr.substring(0, idx); @@ -105,8 +105,8 @@ export function createMockTestDataItem( } export function createSubtestParent(funcs: TestFunction[]): SubtestParent { - const name = longestCommonSubstring(funcs.map(func => func.name)); - const nameToRun = longestCommonSubstring(funcs.map(func => func.nameToRun)); + const name = longestCommonSubstring(funcs.map((func) => func.name)); + const nameToRun = longestCommonSubstring(funcs.map((func) => func.nameToRun)); const subtestParent: SubtestParent = { name: name, nameToRun: nameToRun, @@ -123,7 +123,7 @@ export function createSubtestParent(funcs: TestFunction[]): SubtestParent { }, time: 0 }; - funcs.forEach(func => { + funcs.forEach((func) => { func.subtestParent = subtestParent; }); return subtestParent; @@ -141,13 +141,13 @@ export function createTests( rootTestFolders: folders.length > 0 ? [folders[0]] : [], testFolders: folders, testFiles: files, - testSuites: suites.map(suite => { + testSuites: suites.map((suite) => { return { testSuite: suite, xmlClassName: suite.xmlName } as any; }), - testFunctions: funcs.map(func => { + testFunctions: funcs.map((func) => { return { testFunction: func, xmlClassName: func.name diff --git a/src/test/testing/common/testVisitors/resultResetVisitor.unit.test.ts b/src/test/testing/common/testVisitors/resultResetVisitor.unit.test.ts index 7fd06c90b450..cb09323e87b2 100644 --- a/src/test/testing/common/testVisitors/resultResetVisitor.unit.test.ts +++ b/src/test/testing/common/testVisitors/resultResetVisitor.unit.test.ts @@ -1,112 +1,112 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { assert } from 'chai'; -import { TestResultResetVisitor } from '../../../../client/testing/common/testVisitors/resultResetVisitor'; -import { TestStatus } from '../../../../client/testing/common/types'; - -// tslint:disable-next-line: max-func-body-length -suite('Result reset visitor', async () => { - let resultResetVisitor: TestResultResetVisitor; - setup(() => { - resultResetVisitor = new TestResultResetVisitor(); - }); - - test('Method visitTestFunction() resets visited function nodes', async () => { - const testFunction = { - passed: true, - time: 102, - message: 'yo', - traceback: 'sd', - status: TestStatus.Fail, - functionsDidNotRun: 12, - functionsPassed: 1, - functionsFailed: 5 - }; - const expectedTestFunction = { - passed: undefined, - time: 0, - message: '', - traceback: '', - status: TestStatus.Unknown, - functionsDidNotRun: 0, - functionsPassed: 0, - functionsFailed: 0 - }; - // tslint:disable-next-line: no-any - resultResetVisitor.visitTestFunction(testFunction as any); - // tslint:disable-next-line: no-any - assert.deepEqual(testFunction, expectedTestFunction as any); - }); - - test('Method visitTestSuite() resets visited suite nodes', async () => { - const testSuite = { - passed: true, - time: 102, - status: TestStatus.Fail, - functionsDidNotRun: 12, - functionsPassed: 1, - functionsFailed: 5 - }; - const expectedTestSuite = { - passed: undefined, - time: 0, - status: TestStatus.Unknown, - functionsDidNotRun: 0, - functionsPassed: 0, - functionsFailed: 0 - }; - // tslint:disable-next-line: no-any - resultResetVisitor.visitTestSuite(testSuite as any); - // tslint:disable-next-line: no-any - assert.deepEqual(testSuite, expectedTestSuite as any); - }); - - test('Method visitTestFile() resets visited file nodes', async () => { - const testFile = { - passed: true, - time: 102, - status: TestStatus.Fail, - functionsDidNotRun: 12, - functionsPassed: 1, - functionsFailed: 5 - }; - const expectedTestFile = { - passed: undefined, - time: 0, - status: TestStatus.Unknown, - functionsDidNotRun: 0, - functionsPassed: 0, - functionsFailed: 0 - }; - // tslint:disable-next-line: no-any - resultResetVisitor.visitTestFile(testFile as any); - // tslint:disable-next-line: no-any - assert.deepEqual(testFile, expectedTestFile as any); - }); - - test('Method visitTestFolder() resets visited folder nodes', async () => { - const testFolder = { - passed: true, - time: 102, - status: TestStatus.Fail, - functionsDidNotRun: 12, - functionsPassed: 1, - functionsFailed: 5 - }; - const expectedTestFolder = { - passed: undefined, - time: 0, - status: TestStatus.Unknown, - functionsDidNotRun: 0, - functionsPassed: 0, - functionsFailed: 0 - }; - // tslint:disable-next-line: no-any - resultResetVisitor.visitTestFolder(testFolder as any); - // tslint:disable-next-line: no-any - assert.deepEqual(testFolder, expectedTestFolder as any); - }); -}); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { assert } from 'chai'; +import { TestResultResetVisitor } from '../../../../client/testing/common/testVisitors/resultResetVisitor'; +import { TestStatus } from '../../../../client/testing/common/types'; + +// tslint:disable-next-line: max-func-body-length +suite('Result reset visitor', async () => { + let resultResetVisitor: TestResultResetVisitor; + setup(() => { + resultResetVisitor = new TestResultResetVisitor(); + }); + + test('Method visitTestFunction() resets visited function nodes', async () => { + const testFunction = { + passed: true, + time: 102, + message: 'yo', + traceback: 'sd', + status: TestStatus.Fail, + functionsDidNotRun: 12, + functionsPassed: 1, + functionsFailed: 5 + }; + const expectedTestFunction = { + passed: undefined, + time: 0, + message: '', + traceback: '', + status: TestStatus.Unknown, + functionsDidNotRun: 0, + functionsPassed: 0, + functionsFailed: 0 + }; + // tslint:disable-next-line: no-any + resultResetVisitor.visitTestFunction(testFunction as any); + // tslint:disable-next-line: no-any + assert.deepEqual(testFunction, expectedTestFunction as any); + }); + + test('Method visitTestSuite() resets visited suite nodes', async () => { + const testSuite = { + passed: true, + time: 102, + status: TestStatus.Fail, + functionsDidNotRun: 12, + functionsPassed: 1, + functionsFailed: 5 + }; + const expectedTestSuite = { + passed: undefined, + time: 0, + status: TestStatus.Unknown, + functionsDidNotRun: 0, + functionsPassed: 0, + functionsFailed: 0 + }; + // tslint:disable-next-line: no-any + resultResetVisitor.visitTestSuite(testSuite as any); + // tslint:disable-next-line: no-any + assert.deepEqual(testSuite, expectedTestSuite as any); + }); + + test('Method visitTestFile() resets visited file nodes', async () => { + const testFile = { + passed: true, + time: 102, + status: TestStatus.Fail, + functionsDidNotRun: 12, + functionsPassed: 1, + functionsFailed: 5 + }; + const expectedTestFile = { + passed: undefined, + time: 0, + status: TestStatus.Unknown, + functionsDidNotRun: 0, + functionsPassed: 0, + functionsFailed: 0 + }; + // tslint:disable-next-line: no-any + resultResetVisitor.visitTestFile(testFile as any); + // tslint:disable-next-line: no-any + assert.deepEqual(testFile, expectedTestFile as any); + }); + + test('Method visitTestFolder() resets visited folder nodes', async () => { + const testFolder = { + passed: true, + time: 102, + status: TestStatus.Fail, + functionsDidNotRun: 12, + functionsPassed: 1, + functionsFailed: 5 + }; + const expectedTestFolder = { + passed: undefined, + time: 0, + status: TestStatus.Unknown, + functionsDidNotRun: 0, + functionsPassed: 0, + functionsFailed: 0 + }; + // tslint:disable-next-line: no-any + resultResetVisitor.visitTestFolder(testFolder as any); + // tslint:disable-next-line: no-any + assert.deepEqual(testFolder, expectedTestFolder as any); + }); +}); diff --git a/src/test/testing/common/trackEnablement.unit.test.ts b/src/test/testing/common/trackEnablement.unit.test.ts index 53c16664e3e3..d09c8bdd68e7 100644 --- a/src/test/testing/common/trackEnablement.unit.test.ts +++ b/src/test/testing/common/trackEnablement.unit.test.ts @@ -158,7 +158,7 @@ suite('Unit Tests - Track Enablement', () => { const affectsConfiguration = sinon.stub().returns(true); const getConfigSettings = sinon .stub<[string], boolean>() - .callsFake(setting => setting.includes(sendForProvider)); + .callsFake((setting) => setting.includes(sendForProvider)); when(workspaceService.workspaceFolders).thenReturn([]); // tslint:disable-next-line: no-any diff --git a/src/test/testing/common/xUnitParser.unit.test.ts b/src/test/testing/common/xUnitParser.unit.test.ts index bfc9a5aa8237..07a45a9f8e29 100644 --- a/src/test/testing/common/xUnitParser.unit.test.ts +++ b/src/test/testing/common/xUnitParser.unit.test.ts @@ -51,7 +51,7 @@ suite('Testing - parse JUnit XML file', () => { `); fixResult(expected.testFunctions[0].testFunction, 'test_spam.py', 3); const filename = 'x/y/z/results.xml'; - fs.setup(f => f.readFile(filename)).returns(() => + fs.setup((f) => f.readFile(filename)).returns(() => Promise.resolve(` @@ -82,7 +82,7 @@ suite('Testing - parse JUnit XML file', () => { `); fixResult(expected.testFunctions[0].testFunction, 'test_spam.py', 3); const filename = 'x/y/z/results.xml'; - fs.setup(f => f.readFile(filename)).returns(() => + fs.setup((f) => f.readFile(filename)).returns(() => Promise.resolve(` @@ -105,7 +105,7 @@ suite('Testing - parse JUnit XML file', () => { const expected: Tests = createEmptyResults(); expected.summary.passed = 1; // That's a little strange... const filename = 'x/y/z/results.xml'; - fs.setup(f => f.readFile(filename)).returns(() => + fs.setup((f) => f.readFile(filename)).returns(() => Promise.resolve(` @@ -125,7 +125,7 @@ suite('Testing - parse JUnit XML file', () => { const tests: Tests = createEmptyResults(); const expected: Tests = createEmptyResults(); const filename = 'x/y/z/results.xml'; - fs.setup(f => f.readFile(filename)).returns(() => + fs.setup((f) => f.readFile(filename)).returns(() => Promise.resolve(` diff --git a/src/test/testing/configuration.unit.test.ts b/src/test/testing/configuration.unit.test.ts index 383150b8c459..396d24221252 100644 --- a/src/test/testing/configuration.unit.test.ts +++ b/src/test/testing/configuration.unit.test.ts @@ -31,9 +31,9 @@ import { } from '../../client/testing/types'; suite('Unit Tests - ConfigurationService', () => { - UNIT_TEST_PRODUCTS.forEach(product => { + UNIT_TEST_PRODUCTS.forEach((product) => { const prods = getNamesAndValues(Product); - const productName = prods.filter(item => item.value === product)[0]; + const productName = prods.filter((item) => item.value === product)[0]; const workspaceUri = Uri.file(__filename); suite(productName.name, () => { let testConfigService: typeMoq.IMock; @@ -60,33 +60,35 @@ suite('Unit Tests - ConfigurationService', () => { unitTestSettings = typeMoq.Mock.ofType(); const pythonSettings = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); - pythonSettings.setup(p => p.testing).returns(() => unitTestSettings.object); - configurationService.setup(c => c.getSettings(workspaceUri)).returns(() => pythonSettings.object); + pythonSettings.setup((p) => p.testing).returns(() => unitTestSettings.object); + configurationService.setup((c) => c.getSettings(workspaceUri)).returns(() => pythonSettings.object); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(IOutputChannel), typeMoq.It.isValue(TEST_OUTPUT_CHANNEL))) + .setup((c) => c.get(typeMoq.It.isValue(IOutputChannel), typeMoq.It.isValue(TEST_OUTPUT_CHANNEL))) .returns(() => outputChannel.object); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(IInstaller))).returns(() => installer.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(IInstaller))).returns(() => installer.object); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(typeMoq.It.isValue(IConfigurationService))) .returns(() => configurationService.object); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(IApplicationShell))) + .setup((c) => c.get(typeMoq.It.isValue(IApplicationShell))) .returns(() => appShell.object); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(IWorkspaceService))) + .setup((c) => c.get(typeMoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(ITestConfigurationManagerFactory))) + .setup((c) => c.get(typeMoq.It.isValue(ITestConfigurationManagerFactory))) .returns(() => factory.object); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(ITestConfigSettingsService))) + .setup((c) => c.get(typeMoq.It.isValue(ITestConfigSettingsService))) .returns(() => testSettingsService.object); const commands = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(ICommandManager))).returns(() => commands.object); + serviceContainer + .setup((c) => c.get(typeMoq.It.isValue(ICommandManager))) + .returns(() => commands.object); const flattener = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(ITestsHelper))) + .setup((c) => c.get(typeMoq.It.isValue(ITestsHelper))) .returns(() => new TestsHelper(flattener.object, serviceContainer.object)); testConfigService = typeMoq.Mock.ofType( UnitTestConfigurationService, @@ -101,12 +103,12 @@ suite('Unit Tests - ConfigurationService', () => { typeMoq.MockBehavior.Strict ); configMgr - .setup(c => c.enable()) + .setup((c) => c.enable()) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); factory - .setup(f => f.create(workspaceUri, product)) + .setup((f) => f.create(workspaceUri, product)) .returns(() => configMgr.object) .verifiable(typeMoq.Times.once()); @@ -115,12 +117,12 @@ suite('Unit Tests - ConfigurationService', () => { typeMoq.MockBehavior.Strict ); workspaceService - .setup(w => w.getConfiguration(typeMoq.It.isValue('python'), workspaceUri)) + .setup((w) => w.getConfiguration(typeMoq.It.isValue('python'), workspaceUri)) .returns(() => workspaceConfig.object) .verifiable(typeMoq.Times.once()); workspaceConfig - .setup(w => w.get(typeMoq.It.isValue('testing.promptToConfigure'))) + .setup((w) => w.get(typeMoq.It.isValue('testing.promptToConfigure'))) .returns(() => true) .verifiable(typeMoq.Times.once()); @@ -137,12 +139,12 @@ suite('Unit Tests - ConfigurationService', () => { typeMoq.MockBehavior.Strict ); configMgr - .setup(c => c.enable()) + .setup((c) => c.enable()) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); factory - .setup(f => f.create(workspaceUri, product)) + .setup((f) => f.create(workspaceUri, product)) .returns(() => configMgr.object) .verifiable(typeMoq.Times.once()); @@ -151,17 +153,17 @@ suite('Unit Tests - ConfigurationService', () => { typeMoq.MockBehavior.Strict ); workspaceService - .setup(w => w.getConfiguration(typeMoq.It.isValue('python'), workspaceUri)) + .setup((w) => w.getConfiguration(typeMoq.It.isValue('python'), workspaceUri)) .returns(() => workspaceConfig.object) .verifiable(typeMoq.Times.once()); workspaceConfig - .setup(w => w.get(typeMoq.It.isValue('testing.promptToConfigure'))) + .setup((w) => w.get(typeMoq.It.isValue('testing.promptToConfigure'))) .returns(() => false) .verifiable(typeMoq.Times.once()); workspaceConfig - .setup(w => + .setup((w) => w.update(typeMoq.It.isValue('testing.promptToConfigure'), typeMoq.It.isValue(undefined)) ) .returns(() => Promise.resolve()) @@ -180,12 +182,12 @@ suite('Unit Tests - ConfigurationService', () => { typeMoq.MockBehavior.Strict ); configMgr - .setup(c => c.enable()) + .setup((c) => c.enable()) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); factory - .setup(f => f.create(workspaceUri, product)) + .setup((f) => f.create(workspaceUri, product)) .returns(() => configMgr.object) .verifiable(typeMoq.Times.once()); @@ -194,19 +196,19 @@ suite('Unit Tests - ConfigurationService', () => { typeMoq.MockBehavior.Strict ); workspaceService - .setup(w => w.getConfiguration(typeMoq.It.isValue('python'), workspaceUri)) + .setup((w) => w.getConfiguration(typeMoq.It.isValue('python'), workspaceUri)) .returns(() => workspaceConfig.object) .verifiable(typeMoq.Times.once()); workspaceConfig - .setup(w => w.get(typeMoq.It.isValue('testing.promptToConfigure'))) + .setup((w) => w.get(typeMoq.It.isValue('testing.promptToConfigure'))) .returns(() => false) .verifiable(typeMoq.Times.once()); const errorMessage = 'Update Failed'; const updateFailError = new Error(errorMessage); workspaceConfig - .setup(w => + .setup((w) => w.update(typeMoq.It.isValue('testing.promptToConfigure'), typeMoq.It.isValue(undefined)) ) .returns(() => Promise.reject(updateFailError)) @@ -223,8 +225,8 @@ suite('Unit Tests - ConfigurationService', () => { test('Select Test runner displays 3 items', async () => { const placeHolder = 'Some message'; appShell - .setup(s => s.showQuickPick(typeMoq.It.isAny(), typeMoq.It.isObjectWith({ placeHolder }))) - .callback(items => expect(items).be.lengthOf(3)) + .setup((s) => s.showQuickPick(typeMoq.It.isAny(), typeMoq.It.isObjectWith({ placeHolder }))) + .callback((items) => expect(items).be.lengthOf(3)) .verifiable(typeMoq.Times.once()); await testConfigService.target.selectTestRunner(placeHolder); @@ -234,9 +236,9 @@ suite('Unit Tests - ConfigurationService', () => { const placeHolder = 'Some message'; const indexes = [Product.unittest, Product.pytest, Product.nosetest]; appShell - .setup(s => s.showQuickPick(typeMoq.It.isAny(), typeMoq.It.isObjectWith({ placeHolder }))) - .callback(items => expect(items).be.lengthOf(3)) - .returns(items => items[indexes.indexOf(product)]) + .setup((s) => s.showQuickPick(typeMoq.It.isAny(), typeMoq.It.isObjectWith({ placeHolder }))) + .callback((items) => expect(items).be.lengthOf(3)) + .returns((items) => items[indexes.indexOf(product)]) .verifiable(typeMoq.Times.once()); const selectedItem = await testConfigService.target.selectTestRunner(placeHolder); @@ -246,7 +248,7 @@ suite('Unit Tests - ConfigurationService', () => { test('Ensure undefined is returned when nothing is seleted', async () => { const placeHolder = 'Some message'; appShell - .setup(s => s.showQuickPick(typeMoq.It.isAny(), typeMoq.It.isObjectWith({ placeHolder }))) + .setup((s) => s.showQuickPick(typeMoq.It.isAny(), typeMoq.It.isObjectWith({ placeHolder }))) .returns(() => Promise.resolve(undefined)) .verifiable(typeMoq.Times.once()); @@ -255,12 +257,12 @@ suite('Unit Tests - ConfigurationService', () => { appShell.verifyAll(); }); test('Prompt to enable a test if a test framework is not enabled', async () => { - unitTestSettings.setup(u => u.pytestEnabled).returns(() => false); - unitTestSettings.setup(u => u.unittestEnabled).returns(() => false); - unitTestSettings.setup(u => u.nosetestsEnabled).returns(() => false); + unitTestSettings.setup((u) => u.pytestEnabled).returns(() => false); + unitTestSettings.setup((u) => u.unittestEnabled).returns(() => false); + unitTestSettings.setup((u) => u.nosetestsEnabled).returns(() => false); appShell - .setup(s => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((s) => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(typeMoq.Times.once()); @@ -278,12 +280,12 @@ suite('Unit Tests - ConfigurationService', () => { appShell.verifyAll(); }); test('Prompt to select a test if a test framework is not enabled', async () => { - unitTestSettings.setup(u => u.pytestEnabled).returns(() => false); - unitTestSettings.setup(u => u.unittestEnabled).returns(() => false); - unitTestSettings.setup(u => u.nosetestsEnabled).returns(() => false); + unitTestSettings.setup((u) => u.pytestEnabled).returns(() => false); + unitTestSettings.setup((u) => u.unittestEnabled).returns(() => false); + unitTestSettings.setup((u) => u.nosetestsEnabled).returns(() => false); appShell - .setup(s => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((s) => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns((_msg, option) => Promise.resolve(option)) .verifiable(typeMoq.Times.once()); @@ -292,7 +294,7 @@ suite('Unit Tests - ConfigurationService', () => { try { testConfigService.callBase = false; testConfigService - .setup(t => t.selectTestRunner(typeMoq.It.isAny())) + .setup((t) => t.selectTestRunner(typeMoq.It.isAny())) .returns(() => { selectTestRunnerInvoked = true; return Promise.resolve(undefined); @@ -310,32 +312,32 @@ suite('Unit Tests - ConfigurationService', () => { appShell.verifyAll(); }); test('Configure selected test framework and disable others', async () => { - unitTestSettings.setup(u => u.pytestEnabled).returns(() => false); - unitTestSettings.setup(u => u.unittestEnabled).returns(() => false); - unitTestSettings.setup(u => u.nosetestsEnabled).returns(() => false); + unitTestSettings.setup((u) => u.pytestEnabled).returns(() => false); + unitTestSettings.setup((u) => u.unittestEnabled).returns(() => false); + unitTestSettings.setup((u) => u.nosetestsEnabled).returns(() => false); const workspaceConfig = typeMoq.Mock.ofType( undefined, typeMoq.MockBehavior.Strict ); workspaceConfig - .setup(w => w.get(typeMoq.It.isAny())) + .setup((w) => w.get(typeMoq.It.isAny())) .returns(() => true) .verifiable(typeMoq.Times.once()); workspaceService - .setup(w => w.getConfiguration(typeMoq.It.isValue('python'), workspaceUri)) + .setup((w) => w.getConfiguration(typeMoq.It.isValue('python'), workspaceUri)) .returns(() => workspaceConfig.object) .verifiable(typeMoq.Times.once()); appShell - .setup(s => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((s) => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns((_msg, option) => Promise.resolve(option)) .verifiable(typeMoq.Times.once()); let selectTestRunnerInvoked = false; testConfigService.callBase = false; testConfigService - .setup(t => t.selectTestRunner(typeMoq.It.isAny())) + .setup((t) => t.selectTestRunner(typeMoq.It.isAny())) .returns(() => { selectTestRunnerInvoked = true; return Promise.resolve(product as any); @@ -346,18 +348,18 @@ suite('Unit Tests - ConfigurationService', () => { typeMoq.MockBehavior.Strict ); factory - .setup(f => + .setup((f) => f.create(typeMoq.It.isValue(workspaceUri), typeMoq.It.isValue(product), typeMoq.It.isAny()) ) .returns(() => configMgr.object) .verifiable(typeMoq.Times.once()); configMgr - .setup(c => c.configure(typeMoq.It.isValue(workspaceUri))) + .setup((c) => c.configure(typeMoq.It.isValue(workspaceUri))) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); configMgr - .setup(c => c.enable()) + .setup((c) => c.enable()) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); @@ -370,16 +372,16 @@ suite('Unit Tests - ConfigurationService', () => { workspaceConfig.verifyAll(); }); test('If more than one test framework is enabled, then prompt to select a test framework', async () => { - unitTestSettings.setup(u => u.pytestEnabled).returns(() => true); - unitTestSettings.setup(u => u.unittestEnabled).returns(() => true); - unitTestSettings.setup(u => u.nosetestsEnabled).returns(() => true); + unitTestSettings.setup((u) => u.pytestEnabled).returns(() => true); + unitTestSettings.setup((u) => u.unittestEnabled).returns(() => true); + unitTestSettings.setup((u) => u.nosetestsEnabled).returns(() => true); appShell - .setup(s => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((s) => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(typeMoq.Times.never()); appShell - .setup(s => s.showQuickPick(typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((s) => s.showQuickPick(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns(() => Promise.resolve(undefined)) .verifiable(typeMoq.Times.once()); @@ -397,19 +399,19 @@ suite('Unit Tests - ConfigurationService', () => { appShell.verifyAll(); }); test('If more than one test framework is enabled, then prompt to select a test framework and enable test, but do not configure', async () => { - unitTestSettings.setup(u => u.pytestEnabled).returns(() => true); - unitTestSettings.setup(u => u.unittestEnabled).returns(() => true); - unitTestSettings.setup(u => u.nosetestsEnabled).returns(() => true); + unitTestSettings.setup((u) => u.pytestEnabled).returns(() => true); + unitTestSettings.setup((u) => u.unittestEnabled).returns(() => true); + unitTestSettings.setup((u) => u.nosetestsEnabled).returns(() => true); appShell - .setup(s => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((s) => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns((_msg, option) => Promise.resolve(option)) .verifiable(typeMoq.Times.never()); let selectTestRunnerInvoked = false; testConfigService.callBase = false; testConfigService - .setup(t => t.selectTestRunner(typeMoq.It.isAny())) + .setup((t) => t.selectTestRunner(typeMoq.It.isAny())) .returns(() => { selectTestRunnerInvoked = true; return Promise.resolve(product as any); @@ -417,7 +419,7 @@ suite('Unit Tests - ConfigurationService', () => { let enableTestInvoked = false; testConfigService - .setup(t => t.enableTest(typeMoq.It.isValue(workspaceUri), typeMoq.It.isValue(product))) + .setup((t) => t.enableTest(typeMoq.It.isValue(workspaceUri), typeMoq.It.isValue(product))) .returns(() => { enableTestInvoked = true; return Promise.resolve(); @@ -428,18 +430,18 @@ suite('Unit Tests - ConfigurationService', () => { typeMoq.MockBehavior.Strict ); factory - .setup(f => + .setup((f) => f.create(typeMoq.It.isValue(workspaceUri), typeMoq.It.isValue(product), typeMoq.It.isAny()) ) .returns(() => configMgr.object) .verifiable(typeMoq.Times.once()); configMgr - .setup(c => c.configure(typeMoq.It.isValue(workspaceUri))) + .setup((c) => c.configure(typeMoq.It.isValue(workspaceUri))) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.never()); configMgr - .setup(c => c.enable()) + .setup((c) => c.enable()) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); @@ -453,31 +455,31 @@ suite('Unit Tests - ConfigurationService', () => { }); test('Prompt to enable and configure selected test framework', async () => { - unitTestSettings.setup(u => u.pytestEnabled).returns(() => false); - unitTestSettings.setup(u => u.unittestEnabled).returns(() => false); - unitTestSettings.setup(u => u.nosetestsEnabled).returns(() => false); + unitTestSettings.setup((u) => u.pytestEnabled).returns(() => false); + unitTestSettings.setup((u) => u.unittestEnabled).returns(() => false); + unitTestSettings.setup((u) => u.nosetestsEnabled).returns(() => false); const workspaceConfig = typeMoq.Mock.ofType( undefined, typeMoq.MockBehavior.Strict ); workspaceConfig - .setup(w => w.get(typeMoq.It.isAny())) + .setup((w) => w.get(typeMoq.It.isAny())) .returns(() => true) .verifiable(typeMoq.Times.once()); workspaceService - .setup(w => w.getConfiguration(typeMoq.It.isValue('python'), workspaceUri)) + .setup((w) => w.getConfiguration(typeMoq.It.isValue('python'), workspaceUri)) .returns(() => workspaceConfig.object) .verifiable(typeMoq.Times.once()); appShell - .setup(s => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((s) => s.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny())) .verifiable(typeMoq.Times.never()); let selectTestRunnerInvoked = false; testConfigService.callBase = false; testConfigService - .setup(t => t.selectTestRunner(typeMoq.It.isAny())) + .setup((t) => t.selectTestRunner(typeMoq.It.isAny())) .returns(() => { selectTestRunnerInvoked = true; return Promise.resolve(product as any); @@ -485,18 +487,18 @@ suite('Unit Tests - ConfigurationService', () => { const configMgr = typeMoq.Mock.ofType(); factory - .setup(f => + .setup((f) => f.create(typeMoq.It.isValue(workspaceUri), typeMoq.It.isValue(product), typeMoq.It.isAny()) ) .returns(() => configMgr.object) .verifiable(typeMoq.Times.once()); configMgr - .setup(c => c.configure(typeMoq.It.isValue(workspaceUri))) + .setup((c) => c.configure(typeMoq.It.isValue(workspaceUri))) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); configMgr - .setup(c => c.enable()) + .setup((c) => c.enable()) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); const configManagersToVerify: typeof configMgr[] = [configMgr]; diff --git a/src/test/testing/configurationFactory.unit.test.ts b/src/test/testing/configurationFactory.unit.test.ts index 6d011b7b725e..d19484cdb32b 100644 --- a/src/test/testing/configurationFactory.unit.test.ts +++ b/src/test/testing/configurationFactory.unit.test.ts @@ -27,11 +27,11 @@ suite('Unit Tests - ConfigurationManagerFactory', () => { const testConfigService = typeMoq.Mock.ofType(); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(IOutputChannel), typeMoq.It.isValue(TEST_OUTPUT_CHANNEL))) + .setup((c) => c.get(typeMoq.It.isValue(IOutputChannel), typeMoq.It.isValue(TEST_OUTPUT_CHANNEL))) .returns(() => outputChannel.object); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(IInstaller))).returns(() => installer.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(IInstaller))).returns(() => installer.object); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(ITestConfigSettingsService))) + .setup((c) => c.get(typeMoq.It.isValue(ITestConfigSettingsService))) .returns(() => testConfigService.object); factory = new TestConfigurationManagerFactory(serviceContainer.object); }); diff --git a/src/test/testing/debugger.test.ts b/src/test/testing/debugger.test.ts index eaea110dd753..8687a8e9ab67 100644 --- a/src/test/testing/debugger.test.ts +++ b/src/test/testing/debugger.test.ts @@ -48,7 +48,7 @@ const defaultUnitTestArgs = ['-v', '-s', '.', '-p', '*test*.py']; suite('Unit Tests - debugging', () => { let ioc: UnitTestIocContainer; const configTarget = IS_MULTI_ROOT_TEST ? ConfigurationTarget.WorkspaceFolder : ConfigurationTarget.Workspace; - suiteSetup(async function() { + suiteSetup(async function () { // tslint:disable-next-line:no-invalid-this this.timeout(TEST_TIMEOUT * 2); // Test discovery is where the delay is, hence give 10 seconds (as we discover tests at least twice in each test). @@ -59,7 +59,7 @@ suite('Unit Tests - debugging', () => { updateSetting('testing.pytestArgs', [], rootWorkspaceUri, configTarget) ]); }); - setup(async function() { + setup(async function () { // tslint:disable-next-line:no-invalid-this this.timeout(TEST_TIMEOUT * 2); // This hook requires more timeout as we're deleting files as well await deleteDirectory(path.join(testFilesPath, '.cache')); @@ -128,17 +128,17 @@ suite('Unit Tests - debugging', () => { // This promise should never resolve nor reject. runningPromise .then(() => deferred.reject("Debugger stopped when it shouldn't have")) - .catch(error => deferred.reject(error)); + .catch((error) => deferred.reject(error)); mockDebugLauncher.launched - .then(launched => { + .then((launched) => { if (launched) { deferred.resolve(''); } else { deferred.reject('Debugger not launched'); } }) - .catch(error => deferred.reject(error)); + .catch((error) => deferred.reject(error)); await deferred.promise; } @@ -226,14 +226,14 @@ suite('Unit Tests - debugging', () => { // tslint:disable-next-line:no-unsafe-any .then(() => deferred.resolve('')) // tslint:disable-next-line:no-unsafe-any - .catch(ex => deferred.reject(ex)); + .catch((ex) => deferred.reject(ex)); // This promise should never resolve nor reject. runningPromise .then(() => "Debugger stopped when it shouldn't have") .catch(() => "Debugger crashed when it shouldn't have") // tslint:disable-next-line: no-floating-promises - .then(error => { + .then((error) => { deferred.reject(error); }); diff --git a/src/test/testing/display/main.unit.test.ts b/src/test/testing/display/main.unit.test.ts index dc5a8059ff67..d529dc25b183 100644 --- a/src/test/testing/display/main.unit.test.ts +++ b/src/test/testing/display/main.unit.test.ts @@ -39,15 +39,15 @@ suite('Unit Tests - TestResultDisplay', () => { testsHelper = typeMoq.Mock.ofType(); cmdManager = typeMoq.Mock.ofType(); - pythonSettings.setup(p => p.testing).returns(() => unitTestSettings.object); - configurationService.setup(c => c.getSettings(workspaceUri)).returns(() => pythonSettings.object); + pythonSettings.setup((p) => p.testing).returns(() => unitTestSettings.object); + configurationService.setup((c) => c.getSettings(workspaceUri)).returns(() => pythonSettings.object); serviceContainer - .setup(c => c.get(typeMoq.It.isValue(IConfigurationService))) + .setup((c) => c.get(typeMoq.It.isValue(IConfigurationService))) .returns(() => configurationService.object); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(ITestsHelper))).returns(() => testsHelper.object); - serviceContainer.setup(c => c.get(typeMoq.It.isValue(ICommandManager))).returns(() => cmdManager.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(IApplicationShell))).returns(() => appShell.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(ITestsHelper))).returns(() => testsHelper.object); + serviceContainer.setup((c) => c.get(typeMoq.It.isValue(ICommandManager))).returns(() => cmdManager.object); }); teardown(() => { try { @@ -62,7 +62,7 @@ suite('Unit Tests - TestResultDisplay', () => { test('Should create a status bar item upon instantiation', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); @@ -72,7 +72,7 @@ suite('Unit Tests - TestResultDisplay', () => { test('Should be disabled upon instantiation', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); @@ -83,11 +83,11 @@ suite('Unit Tests - TestResultDisplay', () => { test('Enable display should show the statusbar', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); display.enabled = true; @@ -96,11 +96,11 @@ suite('Unit Tests - TestResultDisplay', () => { test('Disable display should hide the statusbar', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.hide()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.hide()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); display.enabled = false; @@ -109,30 +109,30 @@ suite('Unit Tests - TestResultDisplay', () => { test('Ensure status bar is displayed and updated with progress with ability to stop tests', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); display.displayProgressStatus(createDeferred().promise, false); statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Test)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Test)), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Running Tests')), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.text = typeMoq.It.isValue('$(stop) Running Tests')), typeMoq.Times.atLeastOnce()); }); test('Ensure status bar is updated with success with ability to view ui without any results', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); const def = createDeferred(); @@ -141,22 +141,22 @@ suite('Unit Tests - TestResultDisplay', () => { statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Test)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Test)), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Running Tests')), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.text = typeMoq.It.isValue('$(stop) Running Tests')), typeMoq.Times.atLeastOnce()); const tests = typeMoq.Mock.ofType(); tests.setup((t: any) => t.then).returns(() => undefined); tests - .setup(t => t.summary) + .setup((t) => t.summary) .returns(() => { return { errors: 0, failures: 0, passed: 0, skipped: 0 }; }) .verifiable(typeMoq.Times.atLeastOnce()); appShell - .setup(a => + .setup((a) => a.showWarningMessage(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny()) ) .returns(() => Promise.resolve(undefined)) @@ -167,16 +167,16 @@ suite('Unit Tests - TestResultDisplay', () => { tests.verifyAll(); appShell.verifyAll(); - statusBar.verify(s => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); }); test('Ensure status bar is updated with success with ability to view ui with results', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); const def = createDeferred(); @@ -185,22 +185,22 @@ suite('Unit Tests - TestResultDisplay', () => { statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Test)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Test)), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Running Tests')), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.text = typeMoq.It.isValue('$(stop) Running Tests')), typeMoq.Times.atLeastOnce()); const tests = typeMoq.Mock.ofType(); tests.setup((t: any) => t.then).returns(() => undefined); tests - .setup(t => t.summary) + .setup((t) => t.summary) .returns(() => { return { errors: 0, failures: 0, passed: 1, skipped: 0 }; }) .verifiable(typeMoq.Times.atLeastOnce()); appShell - .setup(a => + .setup((a) => a.showWarningMessage(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny()) ) .returns(() => Promise.resolve(undefined)) @@ -211,16 +211,16 @@ suite('Unit Tests - TestResultDisplay', () => { tests.verifyAll(); appShell.verifyAll(); - statusBar.verify(s => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); }); test('Ensure status bar is updated with error when cancelled by user with ability to view ui with results', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); const def = createDeferred(); @@ -229,28 +229,28 @@ suite('Unit Tests - TestResultDisplay', () => { statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Test)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Test)), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Running Tests')), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.text = typeMoq.It.isValue('$(stop) Running Tests')), typeMoq.Times.atLeastOnce()); - testsHelper.setup(t => t.displayTestErrorMessage(typeMoq.It.isAny())).verifiable(typeMoq.Times.never()); + testsHelper.setup((t) => t.displayTestErrorMessage(typeMoq.It.isAny())).verifiable(typeMoq.Times.never()); def.reject(CANCELLATION_REASON); await sleep(1); appShell.verifyAll(); - statusBar.verify(s => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); testsHelper.verifyAll(); }); test('Ensure status bar is updated, and error message display with error in running tests, with ability to view ui with results', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); const def = createDeferred(); @@ -259,48 +259,51 @@ suite('Unit Tests - TestResultDisplay', () => { statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Test)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Test)), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Running Tests')), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.text = typeMoq.It.isValue('$(stop) Running Tests')), typeMoq.Times.atLeastOnce()); - testsHelper.setup(t => t.displayTestErrorMessage(typeMoq.It.isAny())).verifiable(typeMoq.Times.once()); + testsHelper.setup((t) => t.displayTestErrorMessage(typeMoq.It.isAny())).verifiable(typeMoq.Times.once()); def.reject('Some other reason'); await sleep(1); appShell.verifyAll(); - statusBar.verify(s => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); testsHelper.verifyAll(); }); test('Ensure status bar is displayed and updated with progress with ability to stop test discovery', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); display.displayDiscoverStatus(createDeferred().promise, false).ignoreErrors(); statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + typeMoq.Times.atLeastOnce() + ); + statusBar.verify( + (s) => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce()); }); test('Ensure status bar is displayed and updated with success and no tests, with ability to view ui to view results of test discovery', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); const def = createDeferred(); @@ -309,14 +312,17 @@ suite('Unit Tests - TestResultDisplay', () => { statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + typeMoq.Times.atLeastOnce() + ); + statusBar.verify( + (s) => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce()); const tests = typeMoq.Mock.ofType(); appShell - .setup(a => + .setup((a) => a.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny()) ) .returns(() => Promise.resolve(undefined)) @@ -327,18 +333,18 @@ suite('Unit Tests - TestResultDisplay', () => { tests.verifyAll(); appShell.verifyAll(); - statusBar.verify(s => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); }); test('Ensure tests are disabled when there are errors and user choses to disable tests', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); cmdManager - .setup(c => + .setup((c) => c.executeCommand( typeMoq.It.isValue('setContext'), typeMoq.It.isValue('testsDiscovered'), @@ -353,14 +359,17 @@ suite('Unit Tests - TestResultDisplay', () => { statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + typeMoq.Times.atLeastOnce() + ); + statusBar.verify( + (s) => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce()); const tests = typeMoq.Mock.ofType(); appShell - .setup(a => + .setup((a) => a.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny()) ) .returns(() => Promise.resolve(Testing.disableTests())) @@ -373,7 +382,7 @@ suite('Unit Tests - TestResultDisplay', () => { 'testing.nosetestsEnabled' ]) { configurationService - .setup(c => c.updateSetting(typeMoq.It.isValue(setting), typeMoq.It.isValue(false))) + .setup((c) => c.updateSetting(typeMoq.It.isValue(setting), typeMoq.It.isValue(false))) .returns(() => Promise.resolve()) .verifiable(typeMoq.Times.once()); } @@ -382,18 +391,18 @@ suite('Unit Tests - TestResultDisplay', () => { tests.verifyAll(); appShell.verifyAll(); - statusBar.verify(s => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); configurationService.verifyAll(); cmdManager.verifyAll(); }); test('Ensure corresponding command is executed when there are errors and user choses to configure test framework', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); const def = createDeferred(); @@ -402,14 +411,17 @@ suite('Unit Tests - TestResultDisplay', () => { statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + typeMoq.Times.atLeastOnce() + ); + statusBar.verify( + (s) => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce()); const tests = typeMoq.Mock.ofType(); appShell - .setup(a => + .setup((a) => a.showInformationMessage(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny()) ) .returns(() => Promise.resolve(Testing.configureTests())) @@ -417,7 +429,7 @@ suite('Unit Tests - TestResultDisplay', () => { const undefinedArg = typeMoq.It.isValue(undefined); cmdManager - .setup(c => + .setup((c) => c.executeCommand( typeMoq.It.isValue(Commands.Tests_Configure as any), undefinedArg, @@ -432,17 +444,17 @@ suite('Unit Tests - TestResultDisplay', () => { tests.verifyAll(); appShell.verifyAll(); - statusBar.verify(s => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.command = typeMoq.It.isValue(Commands.Tests_View_UI)), typeMoq.Times.atLeastOnce()); cmdManager.verifyAll(); }); test('Ensure status bar is displayed and updated with error info when test discovery is cancelled by the user', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); const def = createDeferred(); @@ -451,28 +463,31 @@ suite('Unit Tests - TestResultDisplay', () => { statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + typeMoq.Times.atLeastOnce() + ); + statusBar.verify( + (s) => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce()); - appShell.setup(a => a.showErrorMessage(typeMoq.It.isAny())).verifiable(typeMoq.Times.never()); + appShell.setup((a) => a.showErrorMessage(typeMoq.It.isAny())).verifiable(typeMoq.Times.never()); def.reject(CANCELLATION_REASON); await sleep(1); appShell.verifyAll(); - statusBar.verify(s => (s.command = typeMoq.It.isValue(Commands.Tests_Discover)), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.command = typeMoq.It.isValue(Commands.Tests_Discover)), typeMoq.Times.atLeastOnce()); configurationService.verifyAll(); }); test('Ensure status bar is displayed and updated with error info, and message is displayed when test discovery is fails due to errors', async () => { const statusBar = typeMoq.Mock.ofType(); appShell - .setup(a => a.createStatusBarItem(typeMoq.It.isAny())) + .setup((a) => a.createStatusBarItem(typeMoq.It.isAny())) .returns(() => statusBar.object) .verifiable(typeMoq.Times.once()); - statusBar.setup(s => s.show()).verifiable(typeMoq.Times.once()); + statusBar.setup((s) => s.show()).verifiable(typeMoq.Times.once()); createTestResultDisplay(); const def = createDeferred(); @@ -481,18 +496,21 @@ suite('Unit Tests - TestResultDisplay', () => { statusBar.verifyAll(); statusBar.verify( - s => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + (s) => (s.command = typeMoq.It.isValue(Commands.Tests_Ask_To_Stop_Discovery)), + typeMoq.Times.atLeastOnce() + ); + statusBar.verify( + (s) => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce() ); - statusBar.verify(s => (s.text = typeMoq.It.isValue('$(stop) Discovering Tests')), typeMoq.Times.atLeastOnce()); - appShell.setup(a => a.showErrorMessage(typeMoq.It.isAny())).verifiable(typeMoq.Times.once()); + appShell.setup((a) => a.showErrorMessage(typeMoq.It.isAny())).verifiable(typeMoq.Times.once()); def.reject('some weird error'); await sleep(1); appShell.verifyAll(); - statusBar.verify(s => (s.command = typeMoq.It.isValue(Commands.Tests_Discover)), typeMoq.Times.atLeastOnce()); + statusBar.verify((s) => (s.command = typeMoq.It.isValue(Commands.Tests_Discover)), typeMoq.Times.atLeastOnce()); configurationService.verifyAll(); }); }); diff --git a/src/test/testing/display/picker.functional.test.ts b/src/test/testing/display/picker.functional.test.ts index fee572c3d308..14de3b7f66e4 100644 --- a/src/test/testing/display/picker.functional.test.ts +++ b/src/test/testing/display/picker.functional.test.ts @@ -70,7 +70,7 @@ suite('Testing - TestDisplay', () => { return [{ ...anything() }]; } const functions: TestFunction[] = []; - testfunctions.forEach(fn => functions.push(fn)); + testfunctions.forEach((fn) => functions.push(fn)); return functions; } @@ -111,7 +111,7 @@ suite('Testing - TestDisplay', () => { verify(mockedAppShell.showQuickPick(anything(), anything())).never(); }); - test(`Test that clicking a codelens on parametrized tests opens a dropdown picker on windows (#8627)`, function() { + test(`Test that clicking a codelens on parametrized tests opens a dropdown picker on windows (#8627)`, function () { if (process.platform !== 'win32') { // tslint:disable-next-line: no-invalid-this this.skip(); diff --git a/src/test/testing/display/picker.unit.test.ts b/src/test/testing/display/picker.unit.test.ts index bb9b45f9a5d1..d0b77e003890 100644 --- a/src/test/testing/display/picker.unit.test.ts +++ b/src/test/testing/display/picker.unit.test.ts @@ -24,9 +24,9 @@ import { createEmptyResults } from '../results'; // tslint:disable:max-func-body-length suite('Unit Tests - Picker (execution of commands)', () => { - getNamesAndValues(Type).forEach(item => { - getNamesAndValues(CommandSource).forEach(commandSource => { - [true, false].forEach(debug => { + getNamesAndValues(Type).forEach((item) => { + getNamesAndValues(CommandSource).forEach((commandSource) => { + [true, false].forEach((debug) => { test(`Invoking command for selection ${item.name} from ${commandSource.name} (${ debug ? 'Debug' : 'No debug' })`, async () => { @@ -220,7 +220,7 @@ suite('Testing - TestDisplay', () => { return [{ ...anything() }]; } const functions: TestFunction[] = []; - testfunctions.forEach(fn => functions.push(fn)); + testfunctions.forEach((fn) => functions.push(fn)); return functions; } diff --git a/src/test/testing/explorer/explorerTestData.ts b/src/test/testing/explorer/explorerTestData.ts index 5f2b0d8ba2f8..6034beb617d6 100644 --- a/src/test/testing/explorer/explorerTestData.ts +++ b/src/test/testing/explorer/explorerTestData.ts @@ -143,10 +143,10 @@ export function getTestHelperInstance(): TestsHelper { const serviceContainerMoq = typemoq.Mock.ofType(); serviceContainerMoq - .setup(a => a.get(typemoq.It.isValue(IApplicationShell), typemoq.It.isAny())) + .setup((a) => a.get(typemoq.It.isValue(IApplicationShell), typemoq.It.isAny())) .returns(() => appShellMoq.object); serviceContainerMoq - .setup(a => a.get(typemoq.It.isValue(ICommandManager), typemoq.It.isAny())) + .setup((a) => a.get(typemoq.It.isValue(ICommandManager), typemoq.It.isAny())) .returns(() => commMgrMoq.object); return new TestsHelper(new TestFlatteningVisitor(), serviceContainerMoq.object); @@ -175,7 +175,7 @@ export function createMockTestStorageService(testData?: Tests): typemoq.IMock t.getTests(typemoq.It.isAny())).returns(() => testData); + testStoreMoq.setup((t) => t.getTests(typemoq.It.isAny())).returns(() => testData); return testStoreMoq; } @@ -187,7 +187,9 @@ export function createMockTestStorageService(testData?: Tests): typemoq.IMock { const unitTestMgmtSrvMoq = typemoq.Mock.ofType(); - unitTestMgmtSrvMoq.setup(u => u.onDidStatusChange(typemoq.It.isAny())).returns(() => new ExplorerTestsDisposable()); + unitTestMgmtSrvMoq + .setup((u) => u.onDidStatusChange(typemoq.It.isAny())) + .returns(() => new ExplorerTestsDisposable()); return unitTestMgmtSrvMoq; } @@ -210,7 +212,7 @@ export function createMockWorkspaceService(): typemoq.IMock { return 0; } } - workspcSrvMoq.setup(w => w.workspaceFolders).returns(() => [new ExplorerTestsWorkspaceFolder()]); + workspcSrvMoq.setup((w) => w.workspaceFolders).returns(() => [new ExplorerTestsWorkspaceFolder()]); return workspcSrvMoq; } @@ -246,7 +248,7 @@ export function createMockTestExplorer( } const dispRegMoq = typemoq.Mock.ofType(); - dispRegMoq.setup(d => d.push(typemoq.It.isAny())); + dispRegMoq.setup((d) => d.push(typemoq.It.isAny())); return new TestTreeViewProvider( testStore, diff --git a/src/test/testing/explorer/testExplorerCommandHandler.unit.test.ts b/src/test/testing/explorer/testExplorerCommandHandler.unit.test.ts index f9d44711e0d0..403c3a8438ee 100644 --- a/src/test/testing/explorer/testExplorerCommandHandler.unit.test.ts +++ b/src/test/testing/explorer/testExplorerCommandHandler.unit.test.ts @@ -53,9 +53,9 @@ suite('Unit Tests - Test Explorer Command Handler', () => { commandHandler.register(); commandHandler.dispose(); - disposable1.verify(d => d.dispose(), typemoq.Times.once()); - disposable2.verify(d => d.dispose(), typemoq.Times.once()); - disposable3.verify(d => d.dispose(), typemoq.Times.once()); + disposable1.verify((d) => d.dispose(), typemoq.Times.once()); + disposable2.verify((d) => d.dispose(), typemoq.Times.once()); + disposable3.verify((d) => d.dispose(), typemoq.Times.once()); }); async function testOpeningTestNode( data: TestFile | TestSuite | TestFunction, diff --git a/src/test/testing/explorer/testTreeViewProvider.unit.test.ts b/src/test/testing/explorer/testTreeViewProvider.unit.test.ts index 2fdf059602e5..e38dedec2f8c 100644 --- a/src/test/testing/explorer/testTreeViewProvider.unit.test.ts +++ b/src/test/testing/explorer/testTreeViewProvider.unit.test.ts @@ -153,7 +153,7 @@ suite('Unit Tests Test Explorer TestTreeViewProvider', () => { let testData = originalTestData; const testStoreMoq = typemoq.Mock.ofType(); - testStoreMoq.setup(a => a.getTests(typemoq.It.isAny())).returns(() => testData); + testStoreMoq.setup((a) => a.getTests(typemoq.It.isAny())).returns(() => testData); const testTreeProvider = createMockTestTreeProvider(testStoreMoq.object); @@ -210,7 +210,7 @@ suite('Unit Tests Test Explorer TestTreeViewProvider', () => { let testData = originalTestData; const testStoreMoq = typemoq.Mock.ofType(); - testStoreMoq.setup(a => a.getTests(typemoq.It.isAny())).returns(() => testData); + testStoreMoq.setup((a) => a.getTests(typemoq.It.isAny())).returns(() => testData); const testTreeProvider = createMockTestTreeProvider(testStoreMoq.object); @@ -260,7 +260,7 @@ suite('Unit Tests Test Explorer TestTreeViewProvider', () => { let testData = originalTestData; const testStoreMoq = typemoq.Mock.ofType(); - testStoreMoq.setup(a => a.getTests(typemoq.It.isAny())).returns(() => testData); + testStoreMoq.setup((a) => a.getTests(typemoq.It.isAny())).returns(() => testData); const testTreeProvider = createMockTestTreeProvider(testStoreMoq.object); diff --git a/src/test/testing/explorer/treeView.unit.test.ts b/src/test/testing/explorer/treeView.unit.test.ts index f6ee238e61fd..4c9642b7483d 100644 --- a/src/test/testing/explorer/treeView.unit.test.ts +++ b/src/test/testing/explorer/treeView.unit.test.ts @@ -57,7 +57,7 @@ suite('Unit Tests Test Explorer Tree View', () => { test('Invoking the command handler will reveal the node in the tree', async () => { const data = {} as any; treeView - .setup(t => t.reveal(typemoq.It.isAny())) + .setup((t) => t.reveal(typemoq.It.isAny())) .returns(() => Promise.resolve()) .verifiable(typemoq.Times.once()); when(appShell.createTreeView('python_tests', anything())).thenReturn(treeView.object); diff --git a/src/test/testing/helper.ts b/src/test/testing/helper.ts index 38e8d47d1268..d973f28d07d6 100644 --- a/src/test/testing/helper.ts +++ b/src/test/testing/helper.ts @@ -16,13 +16,13 @@ export function lookForTestFile(tests: Tests, testFile: string) { // In the mock output, we'd have paths separated using '/' (but on windows, path separators are '\') const testFileToSearch = testFile.split(sep).join('/'); found = tests.testFiles.some( - t => + (t) => (t.name.toUpperCase() === testFile.toUpperCase() || t.name.toUpperCase() === testFileToSearch.toUpperCase()) && t.nameToRun.toUpperCase() === t.name.toUpperCase() ); } else { - found = tests.testFiles.some(t => t.name === testFile && t.nameToRun === t.name); + found = tests.testFiles.some((t) => t.name === testFile && t.nameToRun === t.name); } assert.equal(found, true, `Test File not found '${testFile}'`); } diff --git a/src/test/testing/mocks.ts b/src/test/testing/mocks.ts index 767e60eb0795..b41fc3e2da6a 100644 --- a/src/test/testing/mocks.ts +++ b/src/test/testing/mocks.ts @@ -123,7 +123,7 @@ export class MockUnitTestSocketServer extends EventEmitter implements IUnitTestS this.results.push(...results); } public async start(options: { port: number; host: string } = { port: 0, host: 'localhost' }): Promise { - this.results.forEach(result => { + this.results.forEach((result) => { this.emit('result', result); }); this.results = []; diff --git a/src/test/testing/navigation/commandHandlers.unit.test.ts b/src/test/testing/navigation/commandHandlers.unit.test.ts index 6c4283d7a7e6..f9f8a9f1a61d 100644 --- a/src/test/testing/navigation/commandHandlers.unit.test.ts +++ b/src/test/testing/navigation/commandHandlers.unit.test.ts @@ -91,9 +91,9 @@ suite('Unit Tests - Navigation Command Handler', () => { commandHandler.register(); commandHandler.dispose(); - disposable1.verify(d => d.dispose(), typemoq.Times.once()); - disposable2.verify(d => d.dispose(), typemoq.Times.once()); - disposable3.verify(d => d.dispose(), typemoq.Times.once()); + disposable1.verify((d) => d.dispose(), typemoq.Times.once()); + disposable2.verify((d) => d.dispose(), typemoq.Times.once()); + disposable3.verify((d) => d.dispose(), typemoq.Times.once()); }); test('Ensure command handler is reigstered to be disposed', async () => { commandHandler.register(); diff --git a/src/test/testing/navigation/functionNavigator.unit.test.ts b/src/test/testing/navigation/functionNavigator.unit.test.ts index 65929a90fae0..c883a3fb97a4 100644 --- a/src/test/testing/navigation/functionNavigator.unit.test.ts +++ b/src/test/testing/navigation/functionNavigator.unit.test.ts @@ -90,7 +90,7 @@ suite('Unit Tests - Navigation Function', () => { docManager.showTextDocument(doc.object, deepEqual({ preserveFocus: false, selection: range })) ).once(); } else { - editor.verify(e => e.revealRange(typemoq.It.isAny(), TextEditorRevealType.Default), typemoq.Times.once()); + editor.verify((e) => e.revealRange(typemoq.It.isAny(), TextEditorRevealType.Default), typemoq.Times.once()); } } test('Ensure we use line number from test function when navigating in file (without focusing code)', async () => { @@ -111,6 +111,6 @@ suite('Unit Tests - Navigation Function', () => { verify(helper.openFile(anything())).once(); expect(capture(helper.openFile).first()[0]!.fsPath).to.equal(filePath.fsPath); - editor.verify(e => e.revealRange(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); + editor.verify((e) => e.revealRange(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); }); }); diff --git a/src/test/testing/navigation/suiteNavigator.unit.test.ts b/src/test/testing/navigation/suiteNavigator.unit.test.ts index 92c068e7b94f..df06fba2b6b4 100644 --- a/src/test/testing/navigation/suiteNavigator.unit.test.ts +++ b/src/test/testing/navigation/suiteNavigator.unit.test.ts @@ -90,7 +90,7 @@ suite('Unit Tests - Navigation Suite', () => { docManager.showTextDocument(doc.object, deepEqual({ preserveFocus: false, selection: range })) ).once(); } else { - editor.verify(e => e.revealRange(range, TextEditorRevealType.Default), typemoq.Times.once()); + editor.verify((e) => e.revealRange(range, TextEditorRevealType.Default), typemoq.Times.once()); } } test('Ensure we use line number from test suite when navigating in file (without focusing code)', async () => { @@ -117,7 +117,7 @@ suite('Unit Tests - Navigation Suite', () => { docManager.showTextDocument(doc.object, deepEqual({ preserveFocus: false, selection: range })) ).once(); } else { - editor.verify(e => e.revealRange(range, TextEditorRevealType.Default), typemoq.Times.once()); + editor.verify((e) => e.revealRange(range, TextEditorRevealType.Default), typemoq.Times.once()); } } test('Navigating in file (without focusing code)', async () => { @@ -138,6 +138,6 @@ suite('Unit Tests - Navigation Suite', () => { verify(helper.openFile(anything())).once(); expect(capture(helper.openFile).first()[0]!.fsPath).to.equal(filePath.fsPath); - editor.verify(e => e.revealRange(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); + editor.verify((e) => e.revealRange(typemoq.It.isAny(), typemoq.It.isAny()), typemoq.Times.never()); }); }); diff --git a/src/test/testing/navigation/symbolNavigator.unit.test.ts b/src/test/testing/navigation/symbolNavigator.unit.test.ts index b7f2a67b6795..cbc008b66911 100644 --- a/src/test/testing/navigation/symbolNavigator.unit.test.ts +++ b/src/test/testing/navigation/symbolNavigator.unit.test.ts @@ -44,14 +44,14 @@ suite('Unit Tests - Navigation Command Handler', () => { pythonService.setup((x: any) => x.then).returns(() => undefined); pythonExecFactory - .setup(factory => factory.create(typemoq.It.isAny())) + .setup((factory) => factory.create(typemoq.It.isAny())) .returns(async () => pythonService.object); doc = typemoq.Mock.ofType(); token = new CancellationTokenSource().token; }); test('Ensure no symbols are returned when file has not been saved', async () => { - doc.setup(d => d.isUntitled) + doc.setup((d) => d.isUntitled) .returns(() => true) .verifiable(typemoq.Times.once()); @@ -62,18 +62,18 @@ suite('Unit Tests - Navigation Command Handler', () => { doc.verifyAll(); }); test('Ensure no symbols are returned when there are errors in running the code', async () => { - doc.setup(d => d.isUntitled) + doc.setup((d) => d.isUntitled) .returns(() => false) .verifiable(typemoq.Times.once()); - doc.setup(d => d.isDirty) + doc.setup((d) => d.isDirty) .returns(() => false) .verifiable(typemoq.Times.once()); - doc.setup(d => d.uri) + doc.setup((d) => d.uri) .returns(() => Uri.file(__filename)) .verifiable(typemoq.Times.atLeastOnce()); pythonService - .setup(service => service.exec(typemoq.It.isAny(), typemoq.It.isAny())) + .setup((service) => service.exec(typemoq.It.isAny(), typemoq.It.isAny())) .returns(async () => { return { stdout: '' }; }); @@ -90,18 +90,18 @@ suite('Unit Tests - Navigation Command Handler', () => { const proc: ExecutionResult = { stdout: JSON.stringify({ classes: [], methods: [], functions: [] }) }; - doc.setup(d => d.isUntitled) + doc.setup((d) => d.isUntitled) .returns(() => false) .verifiable(typemoq.Times.once()); - doc.setup(d => d.isDirty) + doc.setup((d) => d.isDirty) .returns(() => false) .verifiable(typemoq.Times.once()); - doc.setup(d => d.uri) + doc.setup((d) => d.uri) .returns(() => docUri) .verifiable(typemoq.Times.atLeastOnce()); pythonService - .setup(service => service.exec(typemoq.It.isValue(args), typemoq.It.isAny())) + .setup((service) => service.exec(typemoq.It.isValue(args), typemoq.It.isAny())) .returns(async () => proc) .verifiable(typemoq.Times.once()); @@ -143,18 +143,18 @@ suite('Unit Tests - Navigation Command Handler', () => { ] }) }; - doc.setup(d => d.isUntitled) + doc.setup((d) => d.isUntitled) .returns(() => false) .verifiable(typemoq.Times.once()); - doc.setup(d => d.isDirty) + doc.setup((d) => d.isDirty) .returns(() => false) .verifiable(typemoq.Times.once()); - doc.setup(d => d.uri) + doc.setup((d) => d.uri) .returns(() => docUri) .verifiable(typemoq.Times.atLeastOnce()); pythonService - .setup(service => service.exec(typemoq.It.isValue(args), typemoq.It.isAny())) + .setup((service) => service.exec(typemoq.It.isValue(args), typemoq.It.isAny())) .returns(async () => proc) .verifiable(typemoq.Times.once()); diff --git a/src/test/testing/nosetest/nosetest.argsService.unit.test.ts b/src/test/testing/nosetest/nosetest.argsService.unit.test.ts index 1be692a01e20..0b5224176b1a 100644 --- a/src/test/testing/nosetest/nosetest.argsService.unit.test.ts +++ b/src/test/testing/nosetest/nosetest.argsService.unit.test.ts @@ -20,7 +20,7 @@ suite('ArgsService: nosetest', () => { const argsHelper = new ArgumentsHelper(); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IArgumentsHelper), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IArgumentsHelper), typeMoq.It.isAny())) .returns(() => argsHelper); argumentsService = new NoseTestArgumentsService(serviceContainer.object); diff --git a/src/test/testing/nosetest/nosetest.discovery.unit.test.ts b/src/test/testing/nosetest/nosetest.discovery.unit.test.ts index 848ad7bc54a3..a6dfb25e46cb 100644 --- a/src/test/testing/nosetest/nosetest.discovery.unit.test.ts +++ b/src/test/testing/nosetest/nosetest.discovery.unit.test.ts @@ -37,10 +37,10 @@ suite('Unit Tests - nose - Discovery', () => { runner = typeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IArgumentsService), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IArgumentsService), typeMoq.It.isAny())) .returns(() => argsService.object); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(ITestRunner), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(ITestRunner), typeMoq.It.isAny())) .returns(() => runner.object); discoveryService = new TestDiscoveryService(serviceContainer.object, testParser.object); @@ -57,11 +57,11 @@ suite('Unit Tests - nose - Discovery', () => { testFolders: [] }; argsService - .setup(a => a.filterArguments(typeMoq.It.isValue(args), typeMoq.It.isValue(TestFilter.discovery))) + .setup((a) => a.filterArguments(typeMoq.It.isValue(args), typeMoq.It.isValue(TestFilter.discovery))) .returns(() => []) .verifiable(typeMoq.Times.once()); runner - .setup(r => r.run(typeMoq.It.isValue(NOSETEST_PROVIDER), typeMoq.It.isAny())) + .setup((r) => r.run(typeMoq.It.isValue(NOSETEST_PROVIDER), typeMoq.It.isAny())) .callback((_, opts: Options) => { expect(opts.args).to.include('--collect-only'); expect(opts.args).to.include('-vvv'); @@ -69,15 +69,15 @@ suite('Unit Tests - nose - Discovery', () => { .returns(() => Promise.resolve(runOutput)) .verifiable(typeMoq.Times.once()); testParser - .setup(t => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) + .setup((t) => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) .returns(() => tests) .verifiable(typeMoq.Times.once()); const options = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); - options.setup(o => o.args).returns(() => args); - options.setup(o => o.token).returns(() => token.object); - token.setup(t => t.isCancellationRequested).returns(() => false); + options.setup((o) => o.args).returns(() => args); + options.setup((o) => o.token).returns(() => token.object); + token.setup((t) => t.isCancellationRequested).returns(() => false); const result = await discoveryService.discoverTests(options.object); @@ -98,11 +98,11 @@ suite('Unit Tests - nose - Discovery', () => { testFolders: [] }; argsService - .setup(a => a.filterArguments(typeMoq.It.isValue(args), typeMoq.It.isValue(TestFilter.discovery))) + .setup((a) => a.filterArguments(typeMoq.It.isValue(args), typeMoq.It.isValue(TestFilter.discovery))) .returns(() => []) .verifiable(typeMoq.Times.once()); runner - .setup(r => r.run(typeMoq.It.isValue(NOSETEST_PROVIDER), typeMoq.It.isAny())) + .setup((r) => r.run(typeMoq.It.isValue(NOSETEST_PROVIDER), typeMoq.It.isAny())) .callback((_, opts: Options) => { expect(opts.args).to.include('--collect-only'); expect(opts.args).to.include('-vvv'); @@ -110,19 +110,19 @@ suite('Unit Tests - nose - Discovery', () => { .returns(() => Promise.resolve(runOutput)) .verifiable(typeMoq.Times.once()); testParser - .setup(t => t.parse(typeMoq.It.isAny(), typeMoq.It.isAny())) + .setup((t) => t.parse(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns(() => tests) .verifiable(typeMoq.Times.never()); const options = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); token - .setup(t => t.isCancellationRequested) + .setup((t) => t.isCancellationRequested) .returns(() => true) .verifiable(typeMoq.Times.once()); - options.setup(o => o.args).returns(() => args); - options.setup(o => o.token).returns(() => token.object); + options.setup((o) => o.args).returns(() => args); + options.setup((o) => o.token).returns(() => token.object); const promise = discoveryService.discoverTests(options.object); await expect(promise).to.eventually.be.rejectedWith('cancelled'); diff --git a/src/test/testing/nosetest/nosetest.disovery.test.ts b/src/test/testing/nosetest/nosetest.disovery.test.ts index b370d9fb6ac0..db8858bb756d 100644 --- a/src/test/testing/nosetest/nosetest.disovery.test.ts +++ b/src/test/testing/nosetest/nosetest.disovery.test.ts @@ -46,7 +46,7 @@ suite('Unit Tests - nose - discovery with mocked process output', () => { : vscode.ConfigurationTarget.Workspace; suiteSetup(async () => { - filesToDelete.forEach(file => { + filesToDelete.forEach((file) => { if (fs.existsSync(file)) { fs.unlinkSync(file); } @@ -56,7 +56,7 @@ suite('Unit Tests - nose - discovery with mocked process output', () => { }); suiteTeardown(async () => { await updateSetting('testing.nosetestArgs', [], rootWorkspaceUri, configTarget); - filesToDelete.forEach(file => { + filesToDelete.forEach((file) => { if (fs.existsSync(file)) { fs.unlinkSync(file); } @@ -133,7 +133,7 @@ suite('Unit Tests - nose - discovery with mocked process output', () => { assert.equal(tests.testFunctions.length, 6, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 2, 'Incorrect number of test suites'); assert.equal( - tests.testSuites.every(t => t.testSuite.name === t.testSuite.nameToRun.split(':')[1]), + tests.testSuites.every((t) => t.testSuite.name === t.testSuite.nameToRun.split(':')[1]), true, 'Suite name does not match class name' ); diff --git a/src/test/testing/nosetest/nosetest.run.test.ts b/src/test/testing/nosetest/nosetest.run.test.ts index 5ac04eeb4dbe..539049219624 100644 --- a/src/test/testing/nosetest/nosetest.run.test.ts +++ b/src/test/testing/nosetest/nosetest.run.test.ts @@ -42,7 +42,7 @@ suite('Unit Tests - nose - run against actual python process', () => { : vscode.ConfigurationTarget.Workspace; suiteSetup(async () => { - filesToDelete.forEach(file => { + filesToDelete.forEach((file) => { if (fs.existsSync(file)) { fs.unlinkSync(file); } @@ -52,7 +52,7 @@ suite('Unit Tests - nose - run against actual python process', () => { }); suiteTeardown(async () => { await updateSetting('testing.nosetestArgs', [], rootWorkspaceUri, configTarget); - filesToDelete.forEach(file => { + filesToDelete.forEach((file) => { if (fs.existsSync(file)) { fs.unlinkSync(file); } @@ -114,7 +114,7 @@ suite('Unit Tests - nose - run against actual python process', () => { return; } - const index = args.findIndex(arg => arg.startsWith('--xunit-file=')); + const index = args.findIndex((arg) => arg.startsWith('--xunit-file=')); if (index >= 0) { const fileName = args[index].substr('--xunit-file='.length); const contents = fs.readFileSync(path.join(UNITTEST_TEST_FILES_PATH, outputFileName), 'utf8'); @@ -164,7 +164,7 @@ suite('Unit Tests - nose - run against actual python process', () => { const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri!, UNITTEST_TEST_FILES_PATH); const tests = await testManager.discoverTests(CommandSource.ui, true, true); - const testFileToRun = tests.testFiles.find(t => t.fullPath.endsWith('test_root.py')); + const testFileToRun = tests.testFiles.find((t) => t.fullPath.endsWith('test_root.py')); assert.ok(testFileToRun, 'Test file not found'); // tslint:disable-next-line:no-non-null-assertion const testFile: TestsToRun = { testFile: [testFileToRun!], testFolder: [], testFunction: [], testSuite: [] }; @@ -182,7 +182,7 @@ suite('Unit Tests - nose - run against actual python process', () => { const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri!, UNITTEST_TEST_FILES_PATH); const tests = await testManager.discoverTests(CommandSource.ui, true, true); - const testSuiteToRun = tests.testSuites.find(s => s.xmlClassName === 'test_root.Test_Root_test1'); + const testSuiteToRun = tests.testSuites.find((s) => s.xmlClassName === 'test_root.Test_Root_test1'); assert.ok(testSuiteToRun, 'Test suite not found'); // tslint:disable-next-line:no-non-null-assertion const testSuite: TestsToRun = { @@ -205,7 +205,7 @@ suite('Unit Tests - nose - run against actual python process', () => { const factory = ioc.serviceContainer.get(ITestManagerFactory); const testManager = factory('nosetest', rootWorkspaceUri!, UNITTEST_TEST_FILES_PATH); const tests = await testManager.discoverTests(CommandSource.ui, true, true); - const testFnToRun = tests.testFunctions.find(f => f.xmlClassName === 'test_root.Test_Root_test1'); + const testFnToRun = tests.testFunctions.find((f) => f.xmlClassName === 'test_root.Test_Root_test1'); assert.ok(testFnToRun, 'Test function not found'); // tslint:disable-next-line:no-non-null-assertion const testFn: TestsToRun = { diff --git a/src/test/testing/nosetest/nosetest.test.ts b/src/test/testing/nosetest/nosetest.test.ts index fcf9b665fa0c..70aeb981757e 100644 --- a/src/test/testing/nosetest/nosetest.test.ts +++ b/src/test/testing/nosetest/nosetest.test.ts @@ -34,7 +34,7 @@ suite('Unit Tests - nose - discovery against actual python process', () => { : vscode.ConfigurationTarget.Workspace; suiteSetup(async () => { - filesToDelete.forEach(file => { + filesToDelete.forEach((file) => { if (fs.existsSync(file)) { fs.unlinkSync(file); } @@ -44,7 +44,7 @@ suite('Unit Tests - nose - discovery against actual python process', () => { }); suiteTeardown(async () => { await updateSetting('testing.nosetestArgs', [], rootWorkspaceUri, configTarget); - filesToDelete.forEach(file => { + filesToDelete.forEach((file) => { if (fs.existsSync(file)) { fs.unlinkSync(file); } diff --git a/src/test/testing/pytest/pytest.argsService.unit.test.ts b/src/test/testing/pytest/pytest.argsService.unit.test.ts index 658ee99931ae..86d6a0878758 100644 --- a/src/test/testing/pytest/pytest.argsService.unit.test.ts +++ b/src/test/testing/pytest/pytest.argsService.unit.test.ts @@ -20,7 +20,7 @@ suite('ArgsService: pytest', () => { const argsHelper = new ArgumentsHelper(); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IArgumentsHelper), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IArgumentsHelper), typeMoq.It.isAny())) .returns(() => argsHelper); argumentsService = new PyTestArgumentsService(serviceContainer.object); @@ -91,14 +91,14 @@ suite('ArgsService: pytest', () => { }); test('Test calling ArgumentsService.getOptionValue with the option followed by the value', () => { const knownOptionsWithValues = argumentsService.getKnownOptions().withArgs; - knownOptionsWithValues.forEach(option => { + knownOptionsWithValues.forEach((option) => { const args = ['--foo', '--bar', 'arg1', option, 'value1']; expect(argumentsService.getOptionValue(args, option)).to.deep.equal('value1'); }); }); test('Test calling ArgumentsService.getOptionValue with the inline option syntax', () => { const knownOptionsWithValues = argumentsService.getKnownOptions().withArgs; - knownOptionsWithValues.forEach(option => { + knownOptionsWithValues.forEach((option) => { const args = ['--foo', '--bar', 'arg1', `${option}=value1`]; expect(argumentsService.getOptionValue(args, option)).to.deep.equal('value1'); }); diff --git a/src/test/testing/pytest/pytest.discovery.test.ts b/src/test/testing/pytest/pytest.discovery.test.ts index 4b49f3817060..1325339311e8 100644 --- a/src/test/testing/pytest/pytest.discovery.test.ts +++ b/src/test/testing/pytest/pytest.discovery.test.ts @@ -239,7 +239,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { const testManager = factory('pytest', rootWorkspaceUri!, UNITTEST_SINGLE_TEST_FILE_PATH); const tests = await testManager.discoverTests(CommandSource.ui, true, true); const diagnosticCollectionUris: vscode.Uri[] = []; - testManager.diagnosticCollection.forEach(uri => { + testManager.diagnosticCollection.forEach((uri) => { diagnosticCollectionUris.push(uri); }); assert.equal(diagnosticCollectionUris.length, 0, 'Should not have diagnostics yet'); @@ -247,12 +247,12 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { assert.equal(tests.testFunctions.length, 6, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 2, 'Incorrect number of test suites'); assert.equal( - tests.testFiles.some(t => t.name === 'test_one.py'), + tests.testFiles.some((t) => t.name === 'test_one.py'), true, 'Test File not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_root.py'), + tests.testFiles.some((t) => t.name === 'test_root.py'), true, 'Test File not found' ); @@ -649,7 +649,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { const testManager = factory('pytest', rootWorkspaceUri!, UNITTEST_TEST_FILES_PATH); const tests = await testManager.discoverTests(CommandSource.ui, true, true); const diagnosticCollectionUris: vscode.Uri[] = []; - testManager.diagnosticCollection.forEach(uri => { + testManager.diagnosticCollection.forEach((uri) => { diagnosticCollectionUris.push(uri); }); assert.equal(diagnosticCollectionUris.length, 0, 'Should not have diagnostics yet'); @@ -657,37 +657,37 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { assert.equal(tests.testFunctions.length, 33, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 11, 'Incorrect number of test suites'); assert.equal( - tests.testFiles.some(t => t.name === 'test_foreign_nested_tests.py'), + tests.testFiles.some((t) => t.name === 'test_foreign_nested_tests.py'), true, 'Test File not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_unittest_one.py'), + tests.testFiles.some((t) => t.name === 'test_unittest_one.py'), true, 'Test File not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_unittest_two.py'), + tests.testFiles.some((t) => t.name === 'test_unittest_two.py'), true, 'Test File not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'unittest_three_test.py'), + tests.testFiles.some((t) => t.name === 'unittest_three_test.py'), true, 'Test File not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_pytest.py'), + tests.testFiles.some((t) => t.name === 'test_pytest.py'), true, 'Test File not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_another_pytest.py'), + tests.testFiles.some((t) => t.name === 'test_another_pytest.py'), true, 'Test File not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_root.py'), + tests.testFiles.some((t) => t.name === 'test_root.py'), true, 'Test File not found' ); @@ -746,7 +746,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { const testManager = factory('pytest', rootWorkspaceUri!, UNITTEST_TEST_FILES_PATH); const tests = await testManager.discoverTests(CommandSource.ui, true, true); const diagnosticCollectionUris: vscode.Uri[] = []; - testManager.diagnosticCollection.forEach(uri => { + testManager.diagnosticCollection.forEach((uri) => { diagnosticCollectionUris.push(uri); }); assert.equal(diagnosticCollectionUris.length, 0, 'Should not have diagnostics yet'); @@ -754,7 +754,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { assert.equal(tests.testFunctions.length, 2, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 1, 'Incorrect number of test suites'); assert.equal( - tests.testFiles.some(t => t.name === 'unittest_three_test.py'), + tests.testFiles.some((t) => t.name === 'unittest_three_test.py'), true, 'Test File not found' ); @@ -930,7 +930,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { const testManager = factory('pytest', rootWorkspaceUri!, UNITTEST_TEST_FILES_PATH_WITH_CONFIGS); const tests = await testManager.discoverTests(CommandSource.ui, true, true); const diagnosticCollectionUris: vscode.Uri[] = []; - testManager.diagnosticCollection.forEach(uri => { + testManager.diagnosticCollection.forEach((uri) => { diagnosticCollectionUris.push(uri); }); assert.equal(diagnosticCollectionUris.length, 0, 'Should not have diagnostics yet'); @@ -938,12 +938,12 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { assert.equal(tests.testFunctions.length, 14, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 4, 'Incorrect number of test suites'); assert.equal( - tests.testFiles.some(t => t.name === 'test_unittest_one.py'), + tests.testFiles.some((t) => t.name === 'test_unittest_one.py'), true, 'Test File not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_pytest.py'), + tests.testFiles.some((t) => t.name === 'test_pytest.py'), true, 'Test File not found' ); @@ -996,7 +996,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => { const tests = await testManager.discoverTests(CommandSource.ui, true, true); const diagnosticCollectionUris: vscode.Uri[] = []; - testManager.diagnosticCollection.forEach(uri => { + testManager.diagnosticCollection.forEach((uri) => { diagnosticCollectionUris.push(uri); }); assert.equal(diagnosticCollectionUris.length, 0, 'Should not have diagnostics yet'); diff --git a/src/test/testing/pytest/pytest.run.test.ts b/src/test/testing/pytest/pytest.run.test.ts index 0bd404c67a5e..94891c4b2797 100644 --- a/src/test/testing/pytest/pytest.run.test.ts +++ b/src/test/testing/pytest/pytest.run.test.ts @@ -131,7 +131,7 @@ function getExpectedSummaryCount(testDetails: ITestDetails[], failedRun: boolean failures: 0, errors: 0 }; - testDetails.forEach(td => { + testDetails.forEach((td) => { let tStatus = td.status; if (failedRun && td.passOnFailedRun) { tStatus = TestStatus.Pass; @@ -168,7 +168,7 @@ function getExpectedSummaryCount(testDetails: ITestDetails[], failedRun: boolean * @param fileName The name of the file to find test details for. */ function getRelevantTestDetailsForFile(testDetails: ITestDetails[], fileName: string): ITestDetails[] { - return testDetails.filter(td => { + return testDetails.filter((td) => { return td.fileName === fileName; }); } @@ -191,7 +191,7 @@ function getIssueCountFromRelevantTestDetails( skippedTestDetails: ITestDetails[], failedRun: boolean = false ): number { - const relevantIssueDetails = testDetails.filter(td => { + const relevantIssueDetails = testDetails.filter((td) => { return td.status !== TestStatus.Pass && !(failedRun && td.passOnFailedRun); }); // If it's a failed run, the skipped tests won't be included in testDetails, but should still be included as they still aren't passing. @@ -208,7 +208,7 @@ function getDiagnosticForTestFunc( diagnostics: readonly vscode.Diagnostic[], testFunc: FlattenedTestFunction ): vscode.Diagnostic { - return diagnostics.find(diag => { + return diagnostics.find((diag) => { return testFunc.testFunction.nameToRun === diag.code; })!; } @@ -234,7 +234,7 @@ function getUniqueIssueFilesFromTestDetails(testDetails: ITestDetails[]): string * @param fileName The location of a file that had tests run. */ function getRelevantSkippedIssuesFromTestDetailsForFile(testDetails: ITestDetails[], fileName: string): ITestDetails[] { - return testDetails.filter(td => { + return testDetails.filter((td) => { return td.fileName === fileName && td.status === TestStatus.Skipped; }); } @@ -253,7 +253,7 @@ function getTestFuncFromResultsByTestFileAndName( testDetails: ITestDetails ): FlattenedTestFunction { const fileSystem = ioc.serviceContainer.get(IFileSystem); - return results.testFunctions.find(test => { + return results.testFunctions.find((test) => { return ( fileSystem.arePathsSame(vscode.Uri.file(test.parentTestFile.fullPath).fsPath, testFileUri.fsPath) && test.testFunction.name === testDetails.testName @@ -468,7 +468,7 @@ suite('Unit Tests - pytest - run with mocked process output', () => { if (failedOutput && args.indexOf('--last-failed') === -1) { return; } - const index = args.findIndex(arg => arg.startsWith('--junitxml=')); + const index = args.findIndex((arg) => arg.startsWith('--junitxml=')); if (index >= 0) { const fileName = args[index].substr('--junitxml='.length); const contents = fs.readFileSync(path.join(PYTEST_RESULTS_PATH, outputFileName), 'utf8'); @@ -479,13 +479,13 @@ suite('Unit Tests - pytest - run with mocked process output', () => { } function getScenarioTestDetails(scenario: ITestScenarioDetails, failedRun: boolean): ITestDetails[] { if (scenario.shouldRunFailed && failedRun) { - return scenario.testDetails!.filter(td => { + return scenario.testDetails!.filter((td) => { return td.status === TestStatus.Fail; })!; } return scenario.testDetails!; } - testScenarios.forEach(scenario => { + testScenarios.forEach((scenario) => { suite(scenario.scenarioName, () => { let testDetails: ITestDetails[]; let factory: ITestManagerFactory; @@ -524,7 +524,7 @@ suite('Unit Tests - pytest - run with mocked process output', () => { test('Test results summary', async () => { await testResultsSummary(results, expectedSummaryCount); }); - uniqueIssueFiles.forEach(fileName => { + uniqueIssueFiles.forEach((fileName) => { suite(fileName, () => { let testFileUri: vscode.Uri; const relevantTestDetails = getRelevantTestDetailsForFile(testDetails, fileName); diff --git a/src/test/testing/pytest/pytest.test.ts b/src/test/testing/pytest/pytest.test.ts index c5b43c9ec131..00e06217019b 100644 --- a/src/test/testing/pytest/pytest.test.ts +++ b/src/test/testing/pytest/pytest.test.ts @@ -56,12 +56,12 @@ suite('Unit Tests - pytest - discovery against actual python process', () => { assert.equal(tests.testFunctions.length, 6, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 2, 'Incorrect number of test suites'); assert.equal( - tests.testFiles.some(t => t.name === 'test_one.py'), + tests.testFiles.some((t) => t.name === 'test_one.py'), true, 'Test File not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_root.py'), + tests.testFiles.some((t) => t.name === 'test_root.py'), true, 'Test File not found' ); diff --git a/src/test/testing/pytest/pytest.testMessageService.test.ts b/src/test/testing/pytest/pytest.testMessageService.test.ts index 6334c696ec96..76946d48b077 100644 --- a/src/test/testing/pytest/pytest.testMessageService.test.ts +++ b/src/test/testing/pytest/pytest.testMessageService.test.ts @@ -46,7 +46,7 @@ const PYTEST_RESULTS_PATH = path.join( 'results' ); -const filterdTestScenarios = testScenarios.filter(ts => { +const filterdTestScenarios = testScenarios.filter((ts) => { return !ts.shouldRunFailed; }); @@ -164,7 +164,7 @@ suite('Unit Tests - PyTest - TestMessageService', () => { ); } // Build tests for the test data that is relevant for this platform. - filterdTestScenarios.forEach(scenario => { + filterdTestScenarios.forEach((scenario) => { suite(scenario.scenarioName, async () => { let testMessages: IPythonTestMessage[]; suiteSetup(async () => { @@ -174,7 +174,7 @@ suite('Unit Tests - PyTest - TestMessageService', () => { const testVisitor = typeMoq.Mock.ofType(); const outChannel = typeMoq.Mock.ofType(); const cancelToken = typeMoq.Mock.ofType(); - cancelToken.setup(c => c.isCancellationRequested).returns(() => false); + cancelToken.setup((c) => c.isCancellationRequested).returns(() => false); const options: TestDiscoveryOptions = { args: [], cwd: UNITTEST_TEST_FILES_PATH, @@ -210,7 +210,7 @@ suite('Unit Tests - PyTest - TestMessageService', () => { await ioc.dispose(); await updateSetting('testing.pytestArgs', [], rootWorkspaceUri, configTarget); }); - scenario.testDetails!.forEach(td => { + scenario.testDetails!.forEach((td) => { suite(td.nameToRun, () => { let testMessage: IPythonTestMessage; let expectedMessage: IPythonTestMessage; @@ -234,7 +234,7 @@ suite('Unit Tests - PyTest - TestMessageService', () => { locationStack: expectedLocationStack, testFilePath: path.join(UNITTEST_TEST_FILES_PATH, td.fileName) }; - testMessage = testMessages.find(tm => tm.code === td.nameToRun)!; + testMessage = testMessages.find((tm) => tm.code === td.nameToRun)!; }); test('Message', async () => { await testMessageProperties(testMessage, expectedMessage, td.imported, td.status); diff --git a/src/test/testing/pytest/pytest_run_tests_data.ts b/src/test/testing/pytest/pytest_run_tests_data.ts index f2c48b7bdc66..33114b43569a 100644 --- a/src/test/testing/pytest/pytest_run_tests_data.ts +++ b/src/test/testing/pytest/pytest_run_tests_data.ts @@ -434,7 +434,7 @@ export const testScenarios: ITestScenarioDetails[] = [ testFunction: [], testSuite: [] }, - testDetails: allTestDetails.filter(td => { + testDetails: allTestDetails.filter((td) => { return td.fileName === path.join('tests', 'test_another_pytest.py'); }) }, @@ -444,7 +444,7 @@ export const testScenarios: ITestScenarioDetails[] = [ runOutput: 'four.xml', testsToRun: undefined as any, testSuiteIndex: 0, - testDetails: allTestDetails.filter(td => { + testDetails: allTestDetails.filter((td) => { return td.className === 'test_root.Test_Root_test1'; }) }, @@ -454,7 +454,7 @@ export const testScenarios: ITestScenarioDetails[] = [ runOutput: 'five.xml', testsToRun: undefined as any, testFunctionIndex: 0, - testDetails: allTestDetails.filter(td => { + testDetails: allTestDetails.filter((td) => { return td.testName === 'test_Root_A'; }) }, @@ -463,7 +463,7 @@ export const testScenarios: ITestScenarioDetails[] = [ discoveryOutput: 'two.output', runOutput: 'two.xml', testsToRun: undefined as any, - testDetails: allTestDetails.filter(_td => { + testDetails: allTestDetails.filter((_td) => { return true; }), shouldRunFailed: true, diff --git a/src/test/testing/pytest/services/discoveryService.unit.test.ts b/src/test/testing/pytest/services/discoveryService.unit.test.ts index 8e9907a31a3a..ea46e70c4bb4 100644 --- a/src/test/testing/pytest/services/discoveryService.unit.test.ts +++ b/src/test/testing/pytest/services/discoveryService.unit.test.ts @@ -80,7 +80,7 @@ suite('Unit Tests - PyTest - Discovery', () => { const args = ['1', '2', '3']; discoveryService.buildTestCollectionArgs = () => args; const directories = ['a', 'b']; - discoveryService.discoverTestsInTestDirectory = async opts => { + discoveryService.discoverTestsInTestDirectory = async (opts) => { const dir = opts.args[opts.args.length - 1]; if (dir === 'a') { return ('Result A' as any) as Tests; diff --git a/src/test/testing/rediscover.test.ts b/src/test/testing/rediscover.test.ts index 50d656f6d58d..70394949426e 100644 --- a/src/test/testing/rediscover.test.ts +++ b/src/test/testing/rediscover.test.ts @@ -25,7 +25,7 @@ suite('Unit Tests re-discovery', () => { suiteSetup(async () => { await initialize(); }); - setup(async function() { + setup(async function () { // tslint:disable-next-line:no-invalid-this this.timeout(TEST_TIMEOUT * 2); // This hook requires more timeout as we're dealing with files as well await fs.copy(testFileWithFewTests, testFile, { overwrite: true }); diff --git a/src/test/testing/serviceRegistry.ts b/src/test/testing/serviceRegistry.ts index 81acf2414fc2..a79bc840831c 100644 --- a/src/test/testing/serviceRegistry.ts +++ b/src/test/testing/serviceRegistry.ts @@ -131,7 +131,7 @@ export class UnitTestIocContainer extends IocContainer { } public registerTestManagers() { - this.serviceManager.addFactory(ITestManagerFactory, context => { + this.serviceManager.addFactory(ITestManagerFactory, (context) => { return (testProvider: TestProvider, workspaceFolder: Uri, rootDirectory: string) => { const serviceContainer = context.container.get(IServiceContainer); @@ -154,7 +154,7 @@ export class UnitTestIocContainer extends IocContainer { } public registerTestManagerService() { - this.serviceManager.addFactory(ITestManagerServiceFactory, context => { + this.serviceManager.addFactory(ITestManagerServiceFactory, (context) => { return (workspaceFolder: Uri) => { const serviceContainer = context.container.get(IServiceContainer); const testsHelper = context.container.get(ITestsHelper); diff --git a/src/test/testing/stoppingDiscoverAndTest.test.ts b/src/test/testing/stoppingDiscoverAndTest.test.ts index 0d6297e73e8e..466941ae50ec 100644 --- a/src/test/testing/stoppingDiscoverAndTest.test.ts +++ b/src/test/testing/stoppingDiscoverAndTest.test.ts @@ -77,17 +77,17 @@ suite('Unit Tests Stopping Discovery and Runner', () => { // This promise should never resolve nor reject. runningPromise .then(() => Promise.reject("Debugger stopped when it shouldn't have")) - .catch(error => deferred.reject(error)); + .catch((error) => deferred.reject(error)); discoveryPromise - .then(result => { + .then((result) => { if (result === EmptyTests) { deferred.resolve(''); } else { deferred.reject('tests not empty'); } }) - .catch(error => deferred.reject(error)); + .catch((error) => deferred.reject(error)); await deferred.promise; }); @@ -109,7 +109,7 @@ suite('Unit Tests Stopping Discovery and Runner', () => { await mockTestManager.discoverTests(CommandSource.auto); const runPromise = mockTestManager.runTest(CommandSource.ui); // tslint:disable-next-line:no-string-based-set-timeout - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); // User manually discovering tests will kill the existing test runner. await mockTestManager.discoverTests(CommandSource.ui, true, false, true); diff --git a/src/test/testing/unittest/unittest.argsService.unit.test.ts b/src/test/testing/unittest/unittest.argsService.unit.test.ts index e96399667713..8c8ad61ae446 100644 --- a/src/test/testing/unittest/unittest.argsService.unit.test.ts +++ b/src/test/testing/unittest/unittest.argsService.unit.test.ts @@ -20,7 +20,7 @@ suite('ArgsService: unittest', () => { const argsHelper = new ArgumentsHelper(); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IArgumentsHelper), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IArgumentsHelper), typeMoq.It.isAny())) .returns(() => argsHelper); argumentsService = new UnittestArgumentsService(serviceContainer.object); diff --git a/src/test/testing/unittest/unittest.discovery.test.ts b/src/test/testing/unittest/unittest.discovery.test.ts index a9609bb04dc3..cd5fc08e1f34 100644 --- a/src/test/testing/unittest/unittest.discovery.test.ts +++ b/src/test/testing/unittest/unittest.discovery.test.ts @@ -90,7 +90,7 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => { // Ensure any spaces added during code formatting or the like are removed. out: output .split(/\r?\n/g) - .map(item => item.trim()) + .map((item) => item.trim()) .join(EOL), source: 'stdout' }); @@ -113,13 +113,13 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => { assert.equal(tests.testFunctions.length, 3, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 1, 'Incorrect number of test suites'); assert.equal( - tests.testFiles.some(t => t.name === 'test_one.py' && t.nameToRun === 'test_one'), + tests.testFiles.some((t) => t.name === 'test_one.py' && t.nameToRun === 'test_one'), true, 'Test File not found' ); assert.equal( tests.testFunctions.some( - t => t.testFunction.name === 'test_A' && t.testFunction.nameToRun === 'test_one.Test_test1.test_A' + (t) => t.testFunction.name === 'test_A' && t.testFunction.nameToRun === 'test_one.Test_test1.test_A' ), true, 'Test File not found' @@ -147,18 +147,18 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => { assert.equal(tests.testFunctions.length, 9, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 3, 'Incorrect number of test suites'); assert.equal( - tests.testFiles.some(t => t.name === 'test_unittest_one.py' && t.nameToRun === 'test_unittest_one'), + tests.testFiles.some((t) => t.name === 'test_unittest_one.py' && t.nameToRun === 'test_unittest_one'), true, 'Test File not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_unittest_two.py' && t.nameToRun === 'test_unittest_two'), + tests.testFiles.some((t) => t.name === 'test_unittest_two.py' && t.nameToRun === 'test_unittest_two'), true, 'Test File not found' ); assert.equal( tests.testFunctions.some( - t => + (t) => t.testFunction.name === 'test_A' && t.testFunction.nameToRun === 'test_unittest_one.Test_test1.test_A' ), @@ -167,7 +167,7 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => { ); assert.equal( tests.testFunctions.some( - t => + (t) => t.testFunction.name === 'test_A2' && t.testFunction.nameToRun === 'test_unittest_two.Test_test2.test_A2' ), @@ -190,13 +190,13 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => { assert.equal(tests.testFunctions.length, 2, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 1, 'Incorrect number of test suites'); assert.equal( - tests.testFiles.some(t => t.name === 'unittest_three_test.py' && t.nameToRun === 'unittest_three_test'), + tests.testFiles.some((t) => t.name === 'unittest_three_test.py' && t.nameToRun === 'unittest_three_test'), true, 'Test File not found' ); assert.equal( tests.testFunctions.some( - t => + (t) => t.testFunction.name === 'test_A' && t.testFunction.nameToRun === 'unittest_three_test.Test_test3.test_A' ), diff --git a/src/test/testing/unittest/unittest.discovery.unit.test.ts b/src/test/testing/unittest/unittest.discovery.unit.test.ts index a9faf27e98d7..1d03baada13e 100644 --- a/src/test/testing/unittest/unittest.discovery.unit.test.ts +++ b/src/test/testing/unittest/unittest.discovery.unit.test.ts @@ -44,10 +44,10 @@ suite('Unit Tests - Unittest - Discovery', () => { runner = typeMoq.Mock.ofType(); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(IArgumentsHelper), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(IArgumentsHelper), typeMoq.It.isAny())) .returns(() => argsHelper.object); serviceContainer - .setup(s => s.get(typeMoq.It.isValue(ITestRunner), typeMoq.It.isAny())) + .setup((s) => s.get(typeMoq.It.isValue(ITestRunner), typeMoq.It.isAny())) .returns(() => runner.object); discoveryService = new TestDiscoveryService(serviceContainer.object, testParser.object); @@ -64,11 +64,11 @@ suite('Unit Tests - Unittest - Discovery', () => { testFolders: [] }; argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-s'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-s'))) .returns(() => dir) .verifiable(typeMoq.Times.atLeastOnce()); runner - .setup(r => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) + .setup((r) => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) .callback((_, opts: Options) => { expect(opts.args).to.include('-c'); expect(opts.args[1]).to.contain(dir); @@ -77,15 +77,15 @@ suite('Unit Tests - Unittest - Discovery', () => { .returns(() => Promise.resolve(runOutput)) .verifiable(typeMoq.Times.once()); testParser - .setup(t => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) + .setup((t) => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) .returns(() => tests) .verifiable(typeMoq.Times.once()); const options = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); - options.setup(o => o.args).returns(() => args); - options.setup(o => o.token).returns(() => token.object); - token.setup(t => t.isCancellationRequested).returns(() => false); + options.setup((o) => o.args).returns(() => args); + options.setup((o) => o.token).returns(() => token.object); + token.setup((t) => t.isCancellationRequested).returns(() => false); const result = await discoveryService.discoverTests(options.object); @@ -106,15 +106,15 @@ suite('Unit Tests - Unittest - Discovery', () => { testFolders: [] }; argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-s'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-s'))) .returns(() => undefined) .verifiable(typeMoq.Times.atLeastOnce()); argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('--start-directory'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('--start-directory'))) .returns(() => dir) .verifiable(typeMoq.Times.atLeastOnce()); runner - .setup(r => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) + .setup((r) => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) .callback((_, opts: Options) => { expect(opts.args).to.include('-c'); expect(opts.args[1]).to.contain(dir); @@ -123,15 +123,15 @@ suite('Unit Tests - Unittest - Discovery', () => { .returns(() => Promise.resolve(runOutput)) .verifiable(typeMoq.Times.once()); testParser - .setup(t => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) + .setup((t) => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) .returns(() => tests) .verifiable(typeMoq.Times.once()); const options = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); - options.setup(o => o.args).returns(() => args); - options.setup(o => o.token).returns(() => token.object); - token.setup(t => t.isCancellationRequested).returns(() => false); + options.setup((o) => o.args).returns(() => args); + options.setup((o) => o.token).returns(() => token.object); + token.setup((t) => t.isCancellationRequested).returns(() => false); const result = await discoveryService.discoverTests(options.object); @@ -152,15 +152,15 @@ suite('Unit Tests - Unittest - Discovery', () => { testFolders: [] }; argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-s'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-s'))) .returns(() => undefined) .verifiable(typeMoq.Times.atLeastOnce()); argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('--start-directory'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('--start-directory'))) .returns(() => undefined) .verifiable(typeMoq.Times.atLeastOnce()); runner - .setup(r => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) + .setup((r) => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) .callback((_, opts: Options) => { expect(opts.args).to.include('-c'); expect(opts.args[1]).to.not.contain(dir); @@ -169,15 +169,15 @@ suite('Unit Tests - Unittest - Discovery', () => { .returns(() => Promise.resolve(runOutput)) .verifiable(typeMoq.Times.once()); testParser - .setup(t => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) + .setup((t) => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) .returns(() => tests) .verifiable(typeMoq.Times.once()); const options = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); - options.setup(o => o.args).returns(() => args); - options.setup(o => o.token).returns(() => token.object); - token.setup(t => t.isCancellationRequested).returns(() => false); + options.setup((o) => o.args).returns(() => args); + options.setup((o) => o.token).returns(() => token.object); + token.setup((t) => t.isCancellationRequested).returns(() => false); const result = await discoveryService.discoverTests(options.object); @@ -198,11 +198,11 @@ suite('Unit Tests - Unittest - Discovery', () => { testFolders: [] }; argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-p'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-p'))) .returns(() => pattern) .verifiable(typeMoq.Times.atLeastOnce()); runner - .setup(r => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) + .setup((r) => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) .callback((_, opts: Options) => { expect(opts.args).to.include('-c'); expect(opts.args[1]).to.contain(pattern); @@ -211,15 +211,15 @@ suite('Unit Tests - Unittest - Discovery', () => { .returns(() => Promise.resolve(runOutput)) .verifiable(typeMoq.Times.once()); testParser - .setup(t => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) + .setup((t) => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) .returns(() => tests) .verifiable(typeMoq.Times.once()); const options = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); - options.setup(o => o.args).returns(() => args); - options.setup(o => o.token).returns(() => token.object); - token.setup(t => t.isCancellationRequested).returns(() => false); + options.setup((o) => o.args).returns(() => args); + options.setup((o) => o.token).returns(() => token.object); + token.setup((t) => t.isCancellationRequested).returns(() => false); const result = await discoveryService.discoverTests(options.object); @@ -240,15 +240,15 @@ suite('Unit Tests - Unittest - Discovery', () => { testFolders: [] }; argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-p'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-p'))) .returns(() => undefined) .verifiable(typeMoq.Times.atLeastOnce()); argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('--pattern'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('--pattern'))) .returns(() => pattern) .verifiable(typeMoq.Times.atLeastOnce()); runner - .setup(r => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) + .setup((r) => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) .callback((_, opts: Options) => { expect(opts.args).to.include('-c'); expect(opts.args[1]).to.contain(pattern); @@ -257,15 +257,15 @@ suite('Unit Tests - Unittest - Discovery', () => { .returns(() => Promise.resolve(runOutput)) .verifiable(typeMoq.Times.once()); testParser - .setup(t => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) + .setup((t) => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) .returns(() => tests) .verifiable(typeMoq.Times.once()); const options = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); - options.setup(o => o.args).returns(() => args); - options.setup(o => o.token).returns(() => token.object); - token.setup(t => t.isCancellationRequested).returns(() => false); + options.setup((o) => o.args).returns(() => args); + options.setup((o) => o.token).returns(() => token.object); + token.setup((t) => t.isCancellationRequested).returns(() => false); const result = await discoveryService.discoverTests(options.object); @@ -286,15 +286,15 @@ suite('Unit Tests - Unittest - Discovery', () => { testFolders: [] }; argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-p'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-p'))) .returns(() => undefined) .verifiable(typeMoq.Times.atLeastOnce()); argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('--pattern'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('--pattern'))) .returns(() => undefined) .verifiable(typeMoq.Times.atLeastOnce()); runner - .setup(r => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) + .setup((r) => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) .callback((_, opts: Options) => { expect(opts.args).to.include('-c'); expect(opts.args[1]).to.not.contain(pattern); @@ -303,15 +303,15 @@ suite('Unit Tests - Unittest - Discovery', () => { .returns(() => Promise.resolve(runOutput)) .verifiable(typeMoq.Times.once()); testParser - .setup(t => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) + .setup((t) => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) .returns(() => tests) .verifiable(typeMoq.Times.once()); const options = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); - options.setup(o => o.args).returns(() => args); - options.setup(o => o.token).returns(() => token.object); - token.setup(t => t.isCancellationRequested).returns(() => false); + options.setup((o) => o.args).returns(() => args); + options.setup((o) => o.token).returns(() => token.object); + token.setup((t) => t.isCancellationRequested).returns(() => false); const result = await discoveryService.discoverTests(options.object); @@ -332,27 +332,27 @@ suite('Unit Tests - Unittest - Discovery', () => { testFolders: [] }; argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-p'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('-p'))) .returns(() => undefined) .verifiable(typeMoq.Times.atLeastOnce()); argsHelper - .setup(a => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('--pattern'))) + .setup((a) => a.getOptionValues(typeMoq.It.isValue(args), typeMoq.It.isValue('--pattern'))) .returns(() => undefined) .verifiable(typeMoq.Times.atLeastOnce()); runner - .setup(r => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) + .setup((r) => r.run(typeMoq.It.isValue(UNITTEST_PROVIDER), typeMoq.It.isAny())) .returns(() => Promise.resolve(runOutput)) .verifiable(typeMoq.Times.once()); testParser - .setup(t => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) + .setup((t) => t.parse(typeMoq.It.isValue(runOutput), typeMoq.It.isAny())) .returns(() => tests) .verifiable(typeMoq.Times.never()); const options = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); - options.setup(o => o.args).returns(() => args); - options.setup(o => o.token).returns(() => token.object); - token.setup(t => t.isCancellationRequested).returns(() => true); + options.setup((o) => o.args).returns(() => args); + options.setup((o) => o.token).returns(() => token.object); + token.setup((t) => t.isCancellationRequested).returns(() => true); const promise = discoveryService.discoverTests(options.object); @@ -369,11 +369,11 @@ suite('Unit Tests - Unittest - Discovery', () => { const opts = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); const wspace = typeMoq.Mock.ofType(); - opts.setup(o => o.token).returns(() => token.object); - opts.setup(o => o.workspaceFolder).returns(() => wspace.object); - token.setup(t => t.isCancellationRequested).returns(() => true); - opts.setup(o => o.cwd).returns(() => '/home/user/dev'); - opts.setup(o => o.startDirectory).returns(() => '/home/user/dev/tests'); + opts.setup((o) => o.token).returns(() => token.object); + opts.setup((o) => o.workspaceFolder).returns(() => wspace.object); + token.setup((t) => t.isCancellationRequested).returns(() => true); + opts.setup((o) => o.cwd).returns(() => '/home/user/dev'); + opts.setup((o) => o.startDirectory).returns(() => '/home/user/dev/tests'); const discoveryOutput: string = [ 'start', @@ -394,7 +394,7 @@ suite('Unit Tests - Unittest - Discovery', () => { expect(tests.testFolders.length).to.be.equal(5); // now ensure that each test function belongs within a single test suite... - tests.testFunctions.forEach(fn => { + tests.testFunctions.forEach((fn) => { if (fn.parentTestSuite) { const testPrefix: boolean = fn.testFunction.nameToRun.startsWith(fn.parentTestSuite.nameToRun); expect(testPrefix).to.equal( @@ -416,11 +416,11 @@ suite('Unit Tests - Unittest - Discovery', () => { const opts = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); const wspace = typeMoq.Mock.ofType(); - opts.setup(o => o.token).returns(() => token.object); - opts.setup(o => o.workspaceFolder).returns(() => wspace.object); - token.setup(t => t.isCancellationRequested).returns(() => true); - opts.setup(o => o.cwd).returns(() => '/home/user/dev'); - opts.setup(o => o.startDirectory).returns(() => '/home/user/dev/tests'); + opts.setup((o) => o.token).returns(() => token.object); + opts.setup((o) => o.workspaceFolder).returns(() => wspace.object); + token.setup((t) => t.isCancellationRequested).returns(() => true); + opts.setup((o) => o.cwd).returns(() => '/home/user/dev'); + opts.setup((o) => o.startDirectory).returns(() => '/home/user/dev/tests'); const discoveryOutput: string = [ 'start', @@ -441,7 +441,7 @@ suite('Unit Tests - Unittest - Discovery', () => { expect(tests.testFolders.length).to.be.equal(5); // now ensure that the 'nameToRun' for each test function begins with its file's a single test suite... - tests.testFunctions.forEach(fn => { + tests.testFunctions.forEach((fn) => { if (fn.parentTestSuite) { const testPrefix: boolean = fn.testFunction.nameToRun.startsWith(fn.parentTestFile.nameToRun); expect(testPrefix).to.equal( @@ -463,11 +463,11 @@ suite('Unit Tests - Unittest - Discovery', () => { const opts = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); const wspace = typeMoq.Mock.ofType(); - opts.setup(o => o.token).returns(() => token.object); - opts.setup(o => o.workspaceFolder).returns(() => wspace.object); - token.setup(t => t.isCancellationRequested).returns(() => true); - opts.setup(o => o.cwd).returns(() => '/home/user/dev'); - opts.setup(o => o.startDirectory).returns(() => ''); + opts.setup((o) => o.token).returns(() => token.object); + opts.setup((o) => o.workspaceFolder).returns(() => wspace.object); + token.setup((t) => t.isCancellationRequested).returns(() => true); + opts.setup((o) => o.cwd).returns(() => '/home/user/dev'); + opts.setup((o) => o.startDirectory).returns(() => ''); const discoveryOutput: string = [ 'start', @@ -488,7 +488,7 @@ suite('Unit Tests - Unittest - Discovery', () => { expect(tests.testFolders.length).to.be.equal(4); // now ensure that each test function belongs within a single test suite... - tests.testFunctions.forEach(fn => { + tests.testFunctions.forEach((fn) => { if (fn.parentTestSuite) { const testPrefix: boolean = fn.testFunction.nameToRun.startsWith(fn.parentTestSuite.nameToRun); expect(testPrefix).to.equal( @@ -510,11 +510,11 @@ suite('Unit Tests - Unittest - Discovery', () => { const opts = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); const wspace = typeMoq.Mock.ofType(); - opts.setup(o => o.token).returns(() => token.object); - opts.setup(o => o.workspaceFolder).returns(() => wspace.object); - token.setup(t => t.isCancellationRequested).returns(() => true); - opts.setup(o => o.cwd).returns(() => '/home/user/dev'); - opts.setup(o => o.startDirectory).returns(() => './tests'); + opts.setup((o) => o.token).returns(() => token.object); + opts.setup((o) => o.workspaceFolder).returns(() => wspace.object); + token.setup((t) => t.isCancellationRequested).returns(() => true); + opts.setup((o) => o.cwd).returns(() => '/home/user/dev'); + opts.setup((o) => o.startDirectory).returns(() => './tests'); const discoveryOutput: string = [ 'start', @@ -535,7 +535,7 @@ suite('Unit Tests - Unittest - Discovery', () => { expect(tests.testFolders.length).to.be.equal(5); // now ensure that each test function belongs within a single test suite... - tests.testFunctions.forEach(fn => { + tests.testFunctions.forEach((fn) => { if (fn.parentTestSuite) { const testPrefix: boolean = fn.testFunction.nameToRun.startsWith(fn.parentTestSuite.nameToRun); expect(testPrefix).to.equal( @@ -557,11 +557,11 @@ suite('Unit Tests - Unittest - Discovery', () => { const opts = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); const wspace = typeMoq.Mock.ofType(); - opts.setup(o => o.token).returns(() => token.object); - opts.setup(o => o.workspaceFolder).returns(() => wspace.object); - token.setup(t => t.isCancellationRequested).returns(() => true); - opts.setup(o => o.cwd).returns(() => '/home/user/dev'); - opts.setup(o => o.startDirectory).returns(() => './tests'); + opts.setup((o) => o.token).returns(() => token.object); + opts.setup((o) => o.workspaceFolder).returns(() => wspace.object); + token.setup((t) => t.isCancellationRequested).returns(() => true); + opts.setup((o) => o.cwd).returns(() => '/home/user/dev'); + opts.setup((o) => o.startDirectory).returns(() => './tests'); const tests: Tests = testsParser.parse('', opts.object); @@ -578,11 +578,11 @@ suite('Unit Tests - Unittest - Discovery', () => { const opts = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); const wspace = typeMoq.Mock.ofType(); - opts.setup(o => o.token).returns(() => token.object); - opts.setup(o => o.workspaceFolder).returns(() => wspace.object); - token.setup(t => t.isCancellationRequested).returns(() => true); - opts.setup(o => o.cwd).returns(() => '/home/user/dev'); - opts.setup(o => o.startDirectory).returns(() => './tests'); + opts.setup((o) => o.token).returns(() => token.object); + opts.setup((o) => o.workspaceFolder).returns(() => wspace.object); + token.setup((t) => t.isCancellationRequested).returns(() => true); + opts.setup((o) => o.cwd).returns(() => '/home/user/dev'); + opts.setup((o) => o.startDirectory).returns(() => './tests'); const discoveryOutput: string = [ 'a;lskdjfa', @@ -607,11 +607,11 @@ suite('Unit Tests - Unittest - Discovery', () => { const opts = typeMoq.Mock.ofType(); const token = typeMoq.Mock.ofType(); const wspace = typeMoq.Mock.ofType(); - opts.setup(o => o.token).returns(() => token.object); - opts.setup(o => o.workspaceFolder).returns(() => wspace.object); - token.setup(t => t.isCancellationRequested).returns(() => true); - opts.setup(o => o.cwd).returns(() => '/home/user/dev'); - opts.setup(o => o.startDirectory).returns(() => './tests'); + opts.setup((o) => o.token).returns(() => token.object); + opts.setup((o) => o.workspaceFolder).returns(() => wspace.object); + token.setup((t) => t.isCancellationRequested).returns(() => true); + opts.setup((o) => o.cwd).returns(() => '/home/user/dev'); + opts.setup((o) => o.startDirectory).returns(() => './tests'); const discoveryOutput: string = 'start'; diff --git a/src/test/testing/unittest/unittest.run.test.ts b/src/test/testing/unittest/unittest.run.test.ts index 3b588fb74393..df97d22554cd 100644 --- a/src/test/testing/unittest/unittest.run.test.ts +++ b/src/test/testing/unittest/unittest.run.test.ts @@ -77,8 +77,8 @@ suite('Unit Tests - unittest - run with mocked process output', () => { function buildTestCliSwitches(): ITestConfiguration[] { const switches: ITestConfiguration[] = []; - ['-p', '--pattern'].forEach(p => { - ['-s', '--start - directory'].forEach(s => { + ['-p', '--pattern'].forEach((p) => { + ['-s', '--start - directory'].forEach((s) => { switches.push({ patternSwitch: p, startDirSwitch: s @@ -153,7 +153,7 @@ suite('Unit Tests - unittest - run with mocked process output', () => { // Ensure any spaces added during code formatting or the like are removed out: output .split(/\r?\n/g) - .map(item => item.trim()) + .map((item) => item.trim()) .join(EOL), source: 'stdout' }); @@ -168,7 +168,7 @@ suite('Unit Tests - unittest - run with mocked process output', () => { } // tslint:disable-next-line:max-func-body-length - cliSwitches.forEach(cfg => { + cliSwitches.forEach((cfg) => { test(`Run Tests [${cfg.startDirSwitch}, ${cfg.patternSwitch}]`, async () => { await updateSetting( 'testing.unittestArgs', @@ -396,7 +396,7 @@ suite('Unit Tests - unittest - run with mocked process output', () => { const tests = await testManager.discoverTests(CommandSource.ui, true, true); // tslint:disable-next-line:no-non-null-assertion - const testFileToTest = tests.testFiles.find(f => f.name === 'test_unittest_one.py')!; + const testFileToTest = tests.testFiles.find((f) => f.name === 'test_unittest_one.py')!; const testFile: TestsToRun = { testFile: [testFileToTest], testFolder: [], @@ -463,7 +463,7 @@ suite('Unit Tests - unittest - run with mocked process output', () => { const tests = await testManager.discoverTests(CommandSource.ui, true, true); // tslint:disable-next-line:no-non-null-assertion - const testSuiteToTest = tests.testSuites.find(s => s.testSuite.name === 'Test_test_one_1')!.testSuite; + const testSuiteToTest = tests.testSuites.find((s) => s.testSuite.name === 'Test_test_one_1')!.testSuite; const testSuite: TestsToRun = { testFile: [], testFolder: [], diff --git a/src/test/testing/unittest/unittest.test.ts b/src/test/testing/unittest/unittest.test.ts index 19b4b67a7123..0b8ae0b9b337 100644 --- a/src/test/testing/unittest/unittest.test.ts +++ b/src/test/testing/unittest/unittest.test.ts @@ -78,13 +78,13 @@ suite('Unit Tests - unittest - discovery against actual python process', () => { assert.equal(tests.testFunctions.length, 3, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 1, 'Incorrect number of test suites'); assert.equal( - tests.testFiles.some(t => t.name === 'test_one.py' && t.nameToRun === 'test_one'), + tests.testFiles.some((t) => t.name === 'test_one.py' && t.nameToRun === 'test_one'), true, 'Test File not found' ); assert.equal( tests.testFunctions.some( - t => t.testFunction.name === 'test_A' && t.testFunction.nameToRun === 'test_one.Test_test1.test_A' + (t) => t.testFunction.name === 'test_A' && t.testFunction.nameToRun === 'test_one.Test_test1.test_A' ), true, 'Test File not found' @@ -100,37 +100,37 @@ suite('Unit Tests - unittest - discovery against actual python process', () => { assert.equal(tests.testFunctions.length, 9, 'Incorrect number of test functions'); assert.equal(tests.testSuites.length, 3, 'Incorrect number of test suites'); assert.equal( - tests.testFiles.some(t => t.name === 'test_one.py' && t.nameToRun === 'test_one'), + tests.testFiles.some((t) => t.name === 'test_one.py' && t.nameToRun === 'test_one'), true, 'Test File one not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_two.py' && t.nameToRun === 'test_two'), + tests.testFiles.some((t) => t.name === 'test_two.py' && t.nameToRun === 'test_two'), true, 'Test File two not found' ); assert.equal( - tests.testFiles.some(t => t.name === 'test_three.py' && t.nameToRun === 'more_tests.test_three'), + tests.testFiles.some((t) => t.name === 'test_three.py' && t.nameToRun === 'more_tests.test_three'), true, 'Test File three not found' ); assert.equal( tests.testFunctions.some( - t => t.testFunction.name === 'test_A' && t.testFunction.nameToRun === 'test_one.Test_test1.test_A' + (t) => t.testFunction.name === 'test_A' && t.testFunction.nameToRun === 'test_one.Test_test1.test_A' ), true, 'Test File one not found' ); assert.equal( tests.testFunctions.some( - t => t.testFunction.name === 'test_2A' && t.testFunction.nameToRun === 'test_two.Test_test2.test_2A' + (t) => t.testFunction.name === 'test_2A' && t.testFunction.nameToRun === 'test_two.Test_test2.test_2A' ), true, 'Test File two not found' ); assert.equal( tests.testFunctions.some( - t => + (t) => t.testFunction.name === 'test_3A' && t.testFunction.nameToRun === 'more_tests.test_three.Test_test3.test_3A' ), diff --git a/src/test/unittests.ts b/src/test/unittests.ts index b57e17d241a1..fabbd76238e9 100644 --- a/src/test/unittests.ts +++ b/src/test/unittests.ts @@ -1,84 +1,84 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; - -// Not sure why but on windows, if you execute a process from the System32 directory, it will just crash Node. -// Not throw an exception, just make node exit. -// However if a system32 process is run first, everything works. -import * as child_process from 'child_process'; -import * as os from 'os'; -if (os.platform() === 'win32') { - const proc = child_process.spawn('C:\\Windows\\System32\\Reg.exe', ['/?']); - proc.on('error', () => { - // tslint:disable-next-line: no-console - console.error('error during reg.exe'); - }); -} - -// tslint:disable:no-any no-require-imports no-var-requires -if ((Reflect as any).metadata === undefined) { - require('reflect-metadata'); -} - -process.env.VSC_PYTHON_CI_TEST = '1'; -process.env.VSC_PYTHON_UNIT_TEST = '1'; - -import { setUpDomEnvironment, setupTranspile } from './datascience/reactHelpers'; -import { initialize } from './vscode-mock'; - -// Custom module loader so we skip .css files that break non webpack wrapped compiles -// tslint:disable-next-line:no-var-requires no-require-imports -const Module = require('module'); - -// Required for DS functional tests. -// tslint:disable-next-line:no-function-expression -(function() { - const origRequire = Module.prototype.require; - const _require = (context: any, filepath: any) => { - return origRequire.call(context, filepath); - }; - Module.prototype.require = function(filepath: string) { - if (filepath.endsWith('.css') || filepath.endsWith('.svg')) { - return ''; - } - if (filepath.startsWith('expose-loader?')) { - // Pull out the thing to expose - const queryEnd = filepath.indexOf('!'); - if (queryEnd >= 0) { - const query = filepath.substring('expose-loader?'.length, queryEnd); - // tslint:disable-next-line:no-invalid-this - (global as any)[query] = _require(this, filepath.substring(queryEnd + 1)); - return ''; - } - } - if (filepath.startsWith('slickgrid/slick.core')) { - // Special case. This module sticks something into the global 'window' object. - // tslint:disable-next-line:no-invalid-this - const result = _require(this, filepath); - - // However it doesn't look in the 'window' object later. we have to move it to - // the globals when in node.js - if ((window as any).Slick) { - (global as any).Slick = (window as any).Slick; - } - - return result; - } - // tslint:disable-next-line:no-invalid-this - return _require(this, filepath); - }; -})(); - -// Setting up DOM env and transpile is required for the react & monaco related tests. -// However this takes around 40s to setup on Mac, hence slowing down testing/development. -// Allowing ability to disable this (faster local development & testing, saving minutes). -if (process.argv.indexOf('--fast') === -1) { - // nteract/transforms-full expects to run in the browser so we have to fake - // parts of the browser here. - setUpDomEnvironment(); - - // Also have to setup babel to get the monaco editor to work. - setupTranspile(); -} - -initialize(); +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +// Not sure why but on windows, if you execute a process from the System32 directory, it will just crash Node. +// Not throw an exception, just make node exit. +// However if a system32 process is run first, everything works. +import * as child_process from 'child_process'; +import * as os from 'os'; +if (os.platform() === 'win32') { + const proc = child_process.spawn('C:\\Windows\\System32\\Reg.exe', ['/?']); + proc.on('error', () => { + // tslint:disable-next-line: no-console + console.error('error during reg.exe'); + }); +} + +// tslint:disable:no-any no-require-imports no-var-requires +if ((Reflect as any).metadata === undefined) { + require('reflect-metadata'); +} + +process.env.VSC_PYTHON_CI_TEST = '1'; +process.env.VSC_PYTHON_UNIT_TEST = '1'; + +import { setUpDomEnvironment, setupTranspile } from './datascience/reactHelpers'; +import { initialize } from './vscode-mock'; + +// Custom module loader so we skip .css files that break non webpack wrapped compiles +// tslint:disable-next-line:no-var-requires no-require-imports +const Module = require('module'); + +// Required for DS functional tests. +// tslint:disable-next-line:no-function-expression +(function () { + const origRequire = Module.prototype.require; + const _require = (context: any, filepath: any) => { + return origRequire.call(context, filepath); + }; + Module.prototype.require = function (filepath: string) { + if (filepath.endsWith('.css') || filepath.endsWith('.svg')) { + return ''; + } + if (filepath.startsWith('expose-loader?')) { + // Pull out the thing to expose + const queryEnd = filepath.indexOf('!'); + if (queryEnd >= 0) { + const query = filepath.substring('expose-loader?'.length, queryEnd); + // tslint:disable-next-line:no-invalid-this + (global as any)[query] = _require(this, filepath.substring(queryEnd + 1)); + return ''; + } + } + if (filepath.startsWith('slickgrid/slick.core')) { + // Special case. This module sticks something into the global 'window' object. + // tslint:disable-next-line:no-invalid-this + const result = _require(this, filepath); + + // However it doesn't look in the 'window' object later. we have to move it to + // the globals when in node.js + if ((window as any).Slick) { + (global as any).Slick = (window as any).Slick; + } + + return result; + } + // tslint:disable-next-line:no-invalid-this + return _require(this, filepath); + }; +})(); + +// Setting up DOM env and transpile is required for the react & monaco related tests. +// However this takes around 40s to setup on Mac, hence slowing down testing/development. +// Allowing ability to disable this (faster local development & testing, saving minutes). +if (process.argv.indexOf('--fast') === -1) { + // nteract/transforms-full expects to run in the browser so we have to fake + // parts of the browser here. + setUpDomEnvironment(); + + // Also have to setup babel to get the monaco editor to work. + setupTranspile(); +} + +initialize(); diff --git a/src/test/vscode-mock.ts b/src/test/vscode-mock.ts index 48b7c0adf495..573d9d31594e 100644 --- a/src/test/vscode-mock.ts +++ b/src/test/vscode-mock.ts @@ -43,10 +43,10 @@ export function initialize() { // Use mock clipboard fo testing purposes. const clipboard = new MockClipboard(); - mockedVSCodeNamespaces.env?.setup(e => e.clipboard).returns(() => clipboard); + mockedVSCodeNamespaces.env?.setup((e) => e.clipboard).returns(() => clipboard); // When upgrading to npm 9-10, this might have to change, as we could have explicit imports (named imports). - Module._load = function(request: any, _parent: any) { + Module._load = function (request: any, _parent: any) { if (request === 'vscode') { return mockedVSCode; } @@ -101,12 +101,12 @@ mockedVSCode.FileSystemError = vscodeMocks.vscMockExtHostedTypes.FileSystemError // This API is used in src/client/telemetry/telemetry.ts const extensions = TypeMoq.Mock.ofType(); -extensions.setup(e => e.all).returns(() => []); +extensions.setup((e) => e.all).returns(() => []); const extension = TypeMoq.Mock.ofType>(); const packageJson = TypeMoq.Mock.ofType(); const contributes = TypeMoq.Mock.ofType(); -extension.setup(e => e.packageJSON).returns(() => packageJson.object); -packageJson.setup(p => p.contributes).returns(() => contributes.object); -contributes.setup(p => p.debuggers).returns(() => [{ aiKey: '' }]); -extensions.setup(e => e.getExtension(TypeMoq.It.isAny())).returns(() => extension.object); +extension.setup((e) => e.packageJSON).returns(() => packageJson.object); +packageJson.setup((p) => p.contributes).returns(() => contributes.object); +contributes.setup((p) => p.debuggers).returns(() => [{ aiKey: '' }]); +extensions.setup((e) => e.getExtension(TypeMoq.It.isAny())).returns(() => extension.object); mockedVSCode.extensions = extensions.object; diff --git a/src/test/workspaceSymbols/generator.unit.test.ts b/src/test/workspaceSymbols/generator.unit.test.ts index 1086e932aee3..6bfa1d77301a 100644 --- a/src/test/workspaceSymbols/generator.unit.test.ts +++ b/src/test/workspaceSymbols/generator.unit.test.ts @@ -39,7 +39,7 @@ suite('Workspace Symbols Generator', () => { shell = mock(ApplicationShell); fs = mock(FileSystem); processService = mock(ProcessService); - factory.setup(f => f.create(typemoq.It.isAny())).returns(() => Promise.resolve(instance(processService))); + factory.setup((f) => f.create(typemoq.It.isAny())).returns(() => Promise.resolve(instance(processService))); when(configurationService.getSettings(anything())).thenReturn(pythonSettings.object); const outputChannel = typemoq.Mock.ofType(); generator = new Generator( @@ -53,19 +53,19 @@ suite('Workspace Symbols Generator', () => { }); test('should be disabled', () => { const workspaceSymbols = { enabled: false } as any; - pythonSettings.setup(p => p.workspaceSymbols).returns(() => workspaceSymbols); + pythonSettings.setup((p) => p.workspaceSymbols).returns(() => workspaceSymbols); expect(generator.enabled).to.be.equal(false, 'not disabled'); }); test('should be enabled', () => { const workspaceSymbols = { enabled: true } as any; - pythonSettings.setup(p => p.workspaceSymbols).returns(() => workspaceSymbols); + pythonSettings.setup((p) => p.workspaceSymbols).returns(() => workspaceSymbols); expect(generator.enabled).to.be.equal(true, 'not enabled'); }); test('Check tagFilePath', () => { const workspaceSymbols = { tagFilePath: '1234' } as any; - pythonSettings.setup(p => p.workspaceSymbols).returns(() => workspaceSymbols); + pythonSettings.setup((p) => p.workspaceSymbols).returns(() => workspaceSymbols); expect(generator.tagFilePath).to.be.equal('1234'); }); @@ -77,7 +77,7 @@ suite('Workspace Symbols Generator', () => { exclusionPatterns: [], ctagsPath } as any; - pythonSettings.setup(p => p.workspaceSymbols).returns(() => workspaceSymbols); + pythonSettings.setup((p) => p.workspaceSymbols).returns(() => workspaceSymbols); when(fs.directoryExists(anything())).thenResolve(true); const observable = { out: { @@ -101,7 +101,7 @@ suite('Workspace Symbols Generator', () => { exclusionPatterns: [], ctagsPath } as any; - pythonSettings.setup(p => p.workspaceSymbols).returns(() => workspaceSymbols); + pythonSettings.setup((p) => p.workspaceSymbols).returns(() => workspaceSymbols); when(fs.directoryExists(anything())).thenResolve(true); const observable = { out: { diff --git a/src/test/workspaceSymbols/provider.unit.test.ts b/src/test/workspaceSymbols/provider.unit.test.ts index 40e136f32d69..3cd753443a5f 100644 --- a/src/test/workspaceSymbols/provider.unit.test.ts +++ b/src/test/workspaceSymbols/provider.unit.test.ts @@ -106,12 +106,12 @@ suite('Workspace Symbols Provider', () => { assert.equal(symbols.length >= 2, true, 'Incorrect number of symbols returned'); assert.notEqual( - symbols.findIndex(sym => sym.location.uri.fsPath.endsWith('childFile.py')), + symbols.findIndex((sym) => sym.location.uri.fsPath.endsWith('childFile.py')), -1, 'File with symbol not found in child workspace folder' ); assert.notEqual( - symbols.findIndex(sym => sym.location.uri.fsPath.endsWith('workspace2File.py')), + symbols.findIndex((sym) => sym.location.uri.fsPath.endsWith('workspace2File.py')), -1, 'File with symbol not found in child workspace folder' ); @@ -119,17 +119,17 @@ suite('Workspace Symbols Provider', () => { const symbolsForMeth = await provider.provideWorkspaceSymbols('meth', new CancellationTokenSource().token); assert.equal(symbolsForMeth.length >= 10, true, 'Incorrect number of symbols returned'); assert.notEqual( - symbolsForMeth.findIndex(sym => sym.location.uri.fsPath.endsWith('childFile.py')), + symbolsForMeth.findIndex((sym) => sym.location.uri.fsPath.endsWith('childFile.py')), -1, 'Symbols not returned for childFile.py' ); assert.notEqual( - symbolsForMeth.findIndex(sym => sym.location.uri.fsPath.endsWith('workspace2File.py')), + symbolsForMeth.findIndex((sym) => sym.location.uri.fsPath.endsWith('workspace2File.py')), -1, 'Symbols not returned for workspace2File.py' ); assert.notEqual( - symbolsForMeth.findIndex(sym => sym.location.uri.fsPath.endsWith('file.py')), + symbolsForMeth.findIndex((sym) => sym.location.uri.fsPath.endsWith('file.py')), -1, 'Symbols not returned for file.py' );