Skip to content

Commit 7049770

Browse files
authored
Tests for prompting to install missing ipykernel (#14266)
1 parent 749749e commit 7049770

File tree

12 files changed

+333
-60
lines changed

12 files changed

+333
-60
lines changed

.vscode/launch.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,35 @@
177177
"preLaunchTask": "Compile",
178178
"skipFiles": ["<node_internals>/**"]
179179
},
180+
{
181+
"name": "DataScience tests in VS Code (*.vscode.ts)",
182+
"type": "extensionHost",
183+
"request": "launch",
184+
"runtimeExecutable": "${execPath}",
185+
"args": [
186+
"${workspaceFolder}/src/test/datascience",
187+
"--disable-extensions",
188+
"--extensionDevelopmentPath=${workspaceFolder}",
189+
"--extensionTestsPath=${workspaceFolder}/out/test"
190+
],
191+
"env": {
192+
"VSC_PYTHON_CI_TEST_GREP": "", // Modify this to run a subset of the single workspace tests
193+
"VSC_PYTHON_CI_TEST_INVERT_GREP": "", // Initialize this to invert the grep (exclude tests with value defined in grep).
194+
"CI_PYTHON_PATH": "<PythonPath>", // Initialize this to invert the grep (exclude tests with value defined in grep).
195+
"VSC_PYTHON_LOAD_EXPERIMENTS_FROM_FILE": "true",
196+
"TEST_FILES_SUFFIX": "vscode.test"
197+
},
198+
"stopOnEntry": false,
199+
"sourceMaps": true,
200+
"outFiles": [
201+
"${workspaceFolder}/out/**/*.js",
202+
"!${workspaceFolder}/**/node_modules**/*"
203+
],
204+
"preLaunchTask": "Compile",
205+
"skipFiles": [
206+
"<node_internals>/**"
207+
]
208+
},
180209
{
181210
"name": "Tests (Multiroot, VS Code, *.test.ts)",
182211
"type": "extensionHost",

build/ci/templates/test_phases.yml

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,23 @@ steps:
373373
buildPlatform: '$(Agent.Os)-Py$(pythonVersion)'
374374
buildConfiguration: 'SystemTests'
375375

376+
377+
# Slow DataScience tests with VS Code
378+
# Create a venv & register it as a kernel.
379+
# These tests are slow hence will only run on linux.
380+
# This env will be used to install ipykernel & test for prompts if ipykernel is missing & similar tests.
381+
# Ensure this is registered as a kernel.
382+
- bash: |
383+
python -m venv .venvnokernel
384+
source .venvnokernel/bin/activate
385+
386+
python -m pip install ipykernel
387+
python -m ipykernel install --user --name .venvnokernel --display-name .venvnokernel
388+
python -m pip uninstall ipykernel --yes
389+
displayName: 'Prepare Virtual Env for Kernel Tests'
390+
workingDirectory: $(Build.SourcesDirectory)/src/test/datascience
391+
condition: and(succeeded(), contains(variables['TestsToRun'], 'testDataScienceInVSCode'))
392+
376393
# Set the CI_PYTHON_PATH variable that forces VS Code system tests to use
377394
# the specified Python interpreter.
378395
#
@@ -438,18 +455,29 @@ steps:
438455
env:
439456
DISPLAY: :10
440457
441-
# Run the single workspace tests in VS Code Insiders.
458+
# Run the single workspace tests for DS in VS Code Insiders.
442459
- script: |
443460
npm run testDataScience
444461
continueOnError: true
445462
displayName: 'Run DataScience Tests in VSCode Insiders'
446-
condition: and(succeeded(), contains(variables['TestsToRun'], 'testDataScience'))
463+
condition: and(succeeded(), contains(variables['TestsToRun'], 'testDataScience'), not(contains(variables['TestsToRun'], 'testDataScienceInVSCode')))
447464
env:
448465
DISPLAY: :10
449466
VSC_PYTHON_CI_TEST_VSC_CHANNEL: 'insiders'
450467
VSC_PYTHON_LOAD_EXPERIMENTS_FROM_FILE: 'true'
451468
TEST_FILES_SUFFIX: 'ds.test'
452469
470+
# Run the single workspace tests for DS in VS Code Stable.
471+
- script: |
472+
npm run testDataScienceInVSCode
473+
displayName: 'Run DataScience Tests in VSCode Stable'
474+
condition: and(succeeded(), contains(variables['TestsToRun'], 'testDataScienceInVSCode'))
475+
env:
476+
DISPLAY: :10
477+
VSC_PYTHON_CI_TEST_VSC_CHANNEL: 'stable'
478+
VSC_PYTHON_LOAD_EXPERIMENTS_FROM_FILE: 'true'
479+
TEST_FILES_SUFFIX: 'vscode.test'
480+
453481
# Upload the test results to Azure DevOps to facilitate test reporting in their UX.
454482
- task: PublishTestResults@2
455483
displayName: 'Publish single workspace tests results'

build/ci/vscode-python-ci.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ stages:
6666
'Debugger':
6767
TestsToRun: 'testDebugger'
6868
NeedsPythonTestReqs: true
69+
'DataScience in VSCode':
70+
TestsToRun: 'testDataScienceInVSCode'
71+
NeedsPythonTestReqs: true
72+
NeedsPythonFunctionalReqs: true
6973
'Smoke':
7074
TestsToRun: 'testSmoke'
7175
NeedsPythonTestReqs: true

build/ci/vscode-python-pr-validation.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ stages:
5858
TestsToRun: 'testDataScience'
5959
NeedsPythonTestReqs: true
6060
NeedsPythonFunctionalReqs: true
61+
'DataScience in VSCode':
62+
TestsToRun: 'testDataScienceInVSCode'
63+
NeedsPythonTestReqs: true
64+
NeedsPythonFunctionalReqs: true
6165
'Smoke':
6266
TestsToRun: 'testSmoke'
6367
NeedsPythonTestReqs: true

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3472,6 +3472,8 @@
34723472
"testJediLSP": "node ./out/test/languageServers/jedi/lspSetup.js && cross-env CODE_TESTS_WORKSPACE=src/test VSC_PYTHON_CI_TEST_GREP='Language Server:' node ./out/test/testBootstrap.js ./out/test/standardTest.js && node ./out/test/languageServers/jedi/lspTeardown.js",
34733473
"pretestDataScience": "node ./out/test/datascience/dsTestSetup.js",
34743474
"testDataScience": "cross-env CODE_TESTS_WORKSPACE=src/test/datascience VSC_PYTHON_CI_TEST_VSC_CHANNEL=insiders TEST_FILES_SUFFIX=ds.test VSC_PYTHON_FORCE_LOGGING=1 VSC_PYTHON_LOAD_EXPERIMENTS_FROM_FILE=true node ./out/test/testBootstrap.js ./out/test/standardTest.js",
3475+
"pretestDataScienceInVSCode": "node ./out/test/datascience/dsTestSetup.js",
3476+
"testDataScienceInVSCode": "cross-env CODE_TESTS_WORKSPACE=src/test/datascience VSC_PYTHON_CI_TEST_VSC_CHANNEL=stable TEST_FILES_SUFFIX=vscode.test VSC_PYTHON_FORCE_LOGGING=1 VSC_PYTHON_LOAD_EXPERIMENTS_FROM_FILE=true node ./out/test/testBootstrap.js ./out/test/standardTest.js",
34753477
"testMultiWorkspace": "node ./out/test/testBootstrap.js ./out/test/multiRootTest.js",
34763478
"testPerformance": "node ./out/test/testBootstrap.js ./out/test/performanceTest.js",
34773479
"testSmoke": "node ./out/test/smokeTest.js",

src/client/datascience/interactive-ipynb/nativeEditorOldWebView.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import {
5151
import { NativeEditor } from './nativeEditor';
5252
import { NativeEditorSynchronizer } from './nativeEditorSynchronizer';
5353

54-
enum AskForSaveResult {
54+
export enum AskForSaveResult {
5555
Yes,
5656
No,
5757
Cancel

src/test/datascience/.vscode/settings.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
"python.formatting.provider": "yapf",
1414
"python.pythonPath": "python",
1515
"python.experiments.optInto": [
16-
"LocalZMQKernel - experiment",
1716
"NativeNotebook - experiment"
1817
],
1918
// Do not set this to "Microsoft", else it will result in LS being downloaded on CI
@@ -22,8 +21,11 @@
2221
"python.languageServer": "Jedi",
2322
// Ensure auto save is off.
2423
"files.autoSave": "off",
24+
//If enabled, ensure we save immediately.
25+
"files.autoSaveDelay": 1,
2526
// We don't want jupyter to start when testing (DS functionality or anything else).
2627
"python.dataScience.disableJupyterAutoStart": true,
2728
"python.logging.level": "debug",
28-
"python.dataScience.alwaysTrustNotebooks": true // In UI tests we don't want prompts for `Do you want to trust thie nb...`
29+
"python.dataScience.alwaysTrustNotebooks": true, // In UI tests we don't want prompts for `Do you want to trust thie nb...`
30+
"python.dataScience.useNotebookEditor": true
2931
}

src/test/datascience/dsTestSetup.ts

Lines changed: 67 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,64 +2,77 @@
22
// Licensed under the MIT License.
33

44
import * as fs from 'fs-extra';
5+
import { applyEdits, ModificationOptions, modify } from 'jsonc-parser';
56
import * as path from 'path';
7+
import { IS_CI_SERVER } from '../ciConstants';
68
import { EXTENSION_ROOT_DIR_FOR_TESTS } from '../constants';
7-
/**
8-
* Modify package.json to ensure VSC Notebooks have been setup so tests can run.
9-
* This is required because we modify package.json during runtime, hence we need to do the same thing for tests.
10-
*/
119

12-
const packageJsonFile = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'package.json');
13-
const content = JSON.parse(fs.readFileSync(packageJsonFile).toString());
10+
const settingsFile = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src/test/datascience/.vscode/settings.json');
1411

15-
// This code is temporary.
16-
if (
17-
!content.enableProposedApi ||
18-
!Array.isArray(content.contributes.notebookOutputRenderer) ||
19-
!Array.isArray(content.contributes.notebookProvider)
20-
) {
21-
content.enableProposedApi = true;
22-
content.contributes.notebookOutputRenderer = [
23-
{
24-
viewType: 'jupyter-notebook-renderer',
25-
displayName: 'Jupyter Notebook Renderer',
26-
mimeTypes: [
27-
'application/geo+json',
28-
'application/vdom.v1+json',
29-
'application/vnd.dataresource+json',
30-
'application/vnd.plotly.v1+json',
31-
'application/vnd.vega.v2+json',
32-
'application/vnd.vega.v3+json',
33-
'application/vnd.vega.v4+json',
34-
'application/vnd.vega.v5+json',
35-
'application/vnd.vegalite.v1+json',
36-
'application/vnd.vegalite.v2+json',
37-
'application/vnd.vegalite.v3+json',
38-
'application/vnd.vegalite.v4+json',
39-
'application/x-nteract-model-debug+json',
40-
'image/gif',
41-
'image/png',
42-
'image/jpeg',
43-
'text/latex',
44-
'text/vnd.plotly.v1+html'
45-
]
46-
}
47-
];
48-
content.contributes.notebookProvider = [
49-
{
50-
viewType: 'jupyter-notebook',
51-
displayName: 'Jupyter Notebook',
52-
selector: [
53-
{
54-
filenamePattern: '*.ipynb'
55-
}
56-
]
57-
}
58-
];
12+
function updateTestsForNativeNotebooks() {
13+
/**
14+
* Modify package.json to ensure VSC Notebooks have been setup so tests can run.
15+
* This is required because we modify package.json during runtime, hence we need to do the same thing for tests.
16+
*/
17+
const packageJsonFile = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'package.json');
18+
const content = JSON.parse(fs.readFileSync(packageJsonFile).toString());
19+
20+
// This code is temporary.
21+
if (
22+
!content.enableProposedApi ||
23+
!Array.isArray(content.contributes.notebookOutputRenderer) ||
24+
!Array.isArray(content.contributes.notebookProvider)
25+
) {
26+
content.enableProposedApi = true;
27+
content.contributes.notebookProvider = [
28+
{
29+
viewType: 'jupyter-notebook',
30+
displayName: 'Jupyter Notebook',
31+
selector: [
32+
{
33+
filenamePattern: '*.ipynb'
34+
}
35+
]
36+
}
37+
];
38+
}
39+
40+
// Update package.json to pick experiments from our custom settings.json file.
41+
content.contributes.configuration.properties['python.experiments.optInto'].scope = 'resource';
42+
content.contributes.configuration.properties['python.logging.level'].scope = 'resource';
43+
44+
fs.writeFileSync(packageJsonFile, JSON.stringify(content, undefined, 4));
45+
46+
updateSettings(true);
5947
}
6048

61-
// Update package.json to pick experiments from our custom settings.json file.
62-
content.contributes.configuration.properties['python.experiments.optInto'].scope = 'resource';
63-
content.contributes.configuration.properties['python.logging.level'].scope = 'resource';
49+
function updateSettings(useNativeNotebooks: boolean) {
50+
const modificationOptions: ModificationOptions = {
51+
formattingOptions: {
52+
tabSize: 4,
53+
insertSpaces: true
54+
}
55+
};
56+
let settingsJson = fs.readFileSync(settingsFile).toString();
57+
const experiments = useNativeNotebooks ? ['NativeNotebook - experiment'] : [];
58+
const autoSave = useNativeNotebooks ? 'off' : 'afterDelay';
59+
60+
settingsJson = applyEdits(
61+
settingsJson,
62+
modify(settingsJson, ['python.experiments.optInto'], experiments, modificationOptions)
63+
);
64+
settingsJson = applyEdits(settingsJson, modify(settingsJson, ['files.autoSave'], autoSave, modificationOptions));
65+
66+
fs.writeFileSync(settingsFile, settingsJson);
67+
}
68+
function updateTestsForOldNotebooks() {
69+
updateSettings(false);
70+
}
6471

65-
fs.writeFileSync(packageJsonFile, JSON.stringify(content, undefined, 4));
72+
if (!IS_CI_SERVER) {
73+
// Noop.
74+
} else if (process.env.VSC_PYTHON_CI_TEST_VSC_CHANNEL === 'insiders') {
75+
updateTestsForNativeNotebooks();
76+
} else {
77+
updateTestsForOldNotebooks();
78+
}

src/test/datascience/helpers.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,19 @@
44
'use strict';
55

66
import { noop } from 'lodash';
7+
import * as path from 'path';
8+
import { Uri } from 'vscode';
9+
import { ICommandManager } from '../../client/common/application/types';
710
import { IDataScienceSettings } from '../../client/common/types';
11+
import { Commands } from '../../client/datascience/constants';
12+
import {
13+
AskForSaveResult,
14+
NativeEditorOldWebView
15+
} from '../../client/datascience/interactive-ipynb/nativeEditorOldWebView';
16+
import { INotebookEditorProvider } from '../../client/datascience/types';
17+
import { IServiceContainer } from '../../client/ioc/types';
18+
import { CommandSource } from '../../client/testing/common/constants';
19+
import { waitForCondition } from '../common';
820

921
// The default base set of data science settings to use
1022
export function defaultDataScienceSettings(): IDataScienceSettings {
@@ -61,3 +73,23 @@ export function writeDiffSnapshot(_snapshot: any, _prefix: string) {
6173
// snapshotCounter += 1;
6274
// fs.writeFile(file, JSON.stringify(diff), { encoding: 'utf-8' }).ignoreErrors();
6375
}
76+
77+
export async function openNotebook(serviceContainer: IServiceContainer, ipynbFile: string, ignoreSaving = true) {
78+
const cmd = serviceContainer.get<ICommandManager>(ICommandManager);
79+
await cmd.executeCommand(Commands.OpenNotebook, Uri.file(ipynbFile), CommandSource.commandPalette);
80+
const editorProvider = serviceContainer.get<INotebookEditorProvider>(INotebookEditorProvider);
81+
await waitForCondition(
82+
async () =>
83+
editorProvider.editors.length > 0 &&
84+
!!editorProvider.activeEditor &&
85+
editorProvider.activeEditor.file.fsPath.endsWith(path.basename(ipynbFile)),
86+
30_000,
87+
'Notebook not opened'
88+
);
89+
90+
if (ignoreSaving && editorProvider.activeEditor && editorProvider.activeEditor instanceof NativeEditorOldWebView) {
91+
// We don't care about changes, no need to save them.
92+
// tslint:disable-next-line: no-any
93+
(editorProvider.activeEditor as any).askForSave = () => Promise.resolve(AskForSaveResult.No);
94+
}
95+
}

0 commit comments

Comments
 (0)