Skip to content

Commit 482e7d9

Browse files
committed
add integration tests
1 parent 7b1b3f2 commit 482e7d9

10 files changed

Lines changed: 269 additions & 144 deletions

File tree

.github/workflows/pr-check.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,53 @@ jobs:
223223
- name: Run Integration Tests (non-Linux)
224224
if: runner.os != 'Linux'
225225
run: npm run integration-test
226+
227+
integration-tests-multiroot:
228+
name: Integration Tests (Multi-Root)
229+
runs-on: ${{ matrix.os }}
230+
needs: [smoke-tests]
231+
strategy:
232+
fail-fast: false
233+
matrix:
234+
os: [ubuntu-latest, windows-latest, macos-latest]
235+
python-version: ['3.9', '3.12', '3.14']
236+
237+
steps:
238+
- name: Checkout
239+
uses: actions/checkout@v4
240+
241+
- name: Install Node
242+
uses: actions/setup-node@v4
243+
with:
244+
node-version: ${{ env.NODE_VERSION }}
245+
cache: 'npm'
246+
247+
- name: Install Python
248+
uses: actions/setup-python@v5
249+
with:
250+
python-version: ${{ matrix.python-version }}
251+
252+
- name: Install Dependencies
253+
run: npm ci
254+
255+
- name: Compile Extension
256+
run: npm run compile
257+
258+
- name: Compile Tests
259+
run: npm run compile-tests
260+
261+
- name: Configure Test Settings
262+
run: |
263+
mkdir -p .vscode-test/user-data/User
264+
echo '{"python.useEnvironmentsExtension": true}' > .vscode-test/user-data/User/settings.json
265+
shell: bash
266+
267+
- name: Run Integration Tests Multi-Root (Linux)
268+
if: runner.os == 'Linux'
269+
uses: GabrielBB/xvfb-action@v1
270+
with:
271+
run: npm run integration-test-multiroot
272+
273+
- name: Run Integration Tests Multi-Root (non-Linux)
274+
if: runner.os != 'Linux'
275+
run: npm run integration-test-multiroot

.vscode-test.mjs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ export default defineConfig([
4747
},
4848
{
4949
label: 'integrationTests',
50-
files: 'out/test/integration/**/*.integration.test.js',
51-
workspaceFolder: 'src/test/integration/test-workspace/integration-tests.code-workspace',
50+
files: 'out/test/integration/*.integration.test.js',
51+
workspaceFolder: 'src/test/integration/test-workspace/project-a',
5252
mocha: {
5353
ui: 'tdd',
5454
timeout: 60000,
@@ -65,6 +65,23 @@ export default defineConfig([
6565
// the native Python tools (pet binary). We use inspect() for
6666
// useEnvironmentsExtension check, so Python extension's default is ignored.
6767
},
68+
{
69+
label: 'integrationTestsMultiRoot',
70+
files: 'out/test/integration/multiroot/*.integration.test.js',
71+
workspaceFolder: 'src/test/integration/test-workspace/integration-tests.code-workspace',
72+
mocha: {
73+
ui: 'tdd',
74+
timeout: 60000,
75+
retries: 1,
76+
},
77+
env: {
78+
VSC_PYTHON_INTEGRATION_TEST: '1',
79+
},
80+
launchArgs: [
81+
`--user-data-dir=${userDataDir}`,
82+
'--disable-workspace-trust',
83+
],
84+
},
6885
{
6986
label: 'extensionTests',
7087
files: 'out/test/**/*.test.js',

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"git.branchProtection": ["main"],
3232
"git.branchProtectionPrompt": "alwaysCommitToNewBranch",
3333
"chat.tools.terminal.autoApprove": {
34-
"npx tsc": true
34+
"npx tsc": true,
35+
"mkdir": true
3536
}
3637
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,7 @@
685685
"smoke-test": "vscode-test --label smokeTests",
686686
"e2e-test": "vscode-test --label e2eTests --install-extensions ms-python.python",
687687
"integration-test": "vscode-test --label integrationTests --install-extensions ms-python.python",
688+
"integration-test-multiroot": "vscode-test --label integrationTestsMultiRoot --install-extensions ms-python.python",
688689
"vsce-package": "vsce package -o ms-python-envs-insiders.vsix"
689690
},
690691
"devDependencies": {

src/test/integration/envCreation.integration.test.ts

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -252,44 +252,6 @@ suite('Integration: Environment Creation', function () {
252252
}
253253
});
254254

255-
/**
256-
* Test: Creation with multiple URIs selects manager
257-
*
258-
* When passing multiple URIs, the API should handle manager selection.
259-
*/
260-
test('Multiple URI scope is handled', async function () {
261-
const workspaceFolders = vscode.workspace.workspaceFolders;
262-
263-
if (!workspaceFolders || workspaceFolders.length < 2) {
264-
this.skip();
265-
return;
266-
}
267-
268-
const uris = workspaceFolders.map((f) => f.uri);
269-
let createdEnv: PythonEnvironment | undefined;
270-
271-
try {
272-
// This may prompt for manager selection - quickCreate should handle it
273-
createdEnv = await api.createEnvironment(uris, { quickCreate: true });
274-
275-
if (createdEnv) {
276-
// Verify created environment has valid structure
277-
assert.ok(createdEnv.envId, 'Multi-URI created env must have envId');
278-
assert.ok(createdEnv.environmentPath, 'Multi-URI created env must have environmentPath');
279-
} else {
280-
// quickCreate returned undefined - skip this test as feature not available
281-
console.log('Multi-URI creation not supported with quickCreate, skipping');
282-
this.skip();
283-
return;
284-
}
285-
} finally {
286-
// Cleanup: always try to remove if created
287-
if (createdEnv) {
288-
await api.removeEnvironment(createdEnv);
289-
}
290-
}
291-
});
292-
293255
/**
294256
* Test: Creation returns properly structured environment
295257
*

src/test/integration/interpreterSelection.integration.test.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -299,34 +299,6 @@ suite('Integration: Interpreter Selection Priority', function () {
299299
}
300300
});
301301

302-
/**
303-
* Test: Environment selection works with URI arrays
304-
*
305-
* setEnvironment should handle array of URIs for multi-select scenarios.
306-
*/
307-
test('setEnvironment handles URI array', async function () {
308-
const environments = await api.getEnvironments('all');
309-
const projects = api.getPythonProjects();
310-
311-
if (environments.length === 0 || projects.length < 2) {
312-
this.skip();
313-
return;
314-
}
315-
316-
const env = environments[0];
317-
const uris = projects.slice(0, 2).map((p) => p.uri);
318-
319-
// Set environment for multiple URIs at once
320-
await api.setEnvironment(uris, env);
321-
322-
// Verify both projects have the environment
323-
for (const uri of uris) {
324-
const retrieved = await api.getEnvironment(uri);
325-
assert.ok(retrieved, `URI ${uri.fsPath} should have environment`);
326-
assert.strictEqual(retrieved.envId.id, env.envId.id, `URI ${uri.fsPath} should have set environment`);
327-
}
328-
});
329-
330302
/**
331303
* Test: Clearing selection falls back to auto-discovery
332304
*
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
/**
5+
* Integration Test: Multi-Root Workspace
6+
*
7+
* PURPOSE:
8+
* Verify behavior that requires multiple workspace folders open simultaneously.
9+
* These tests run in a multi-root workspace (.code-workspace) with 2+ folders.
10+
*
11+
* WHAT THIS TESTS:
12+
* 1. Different projects can have independent environment selections
13+
* 2. setEnvironment handles URI arrays across projects
14+
* 3. Settings scope is isolated between workspace folders
15+
* 4. Environment creation with multiple URI scopes
16+
*
17+
* NOTE: These tests require a multi-root workspace with at least 2 workspace folders.
18+
* They will skip if run in a single-folder workspace.
19+
*/
20+
21+
import * as assert from 'assert';
22+
import * as vscode from 'vscode';
23+
import { PythonEnvironment, PythonEnvironmentApi } from '../../../api';
24+
import { ENVS_EXTENSION_ID } from '../../constants';
25+
import { waitForCondition } from '../../testUtils';
26+
27+
suite('Integration: Multi-Root Workspace', function () {
28+
this.timeout(120_000);
29+
30+
let api: PythonEnvironmentApi;
31+
let originalProjectEnvs: Map<string, PythonEnvironment | undefined>;
32+
33+
suiteSetup(async function () {
34+
this.timeout(30_000);
35+
36+
const workspaceFolders = vscode.workspace.workspaceFolders;
37+
if (!workspaceFolders || workspaceFolders.length < 2) {
38+
this.skip();
39+
return;
40+
}
41+
42+
const extension = vscode.extensions.getExtension(ENVS_EXTENSION_ID);
43+
assert.ok(extension, `Extension ${ENVS_EXTENSION_ID} not found`);
44+
45+
if (!extension.isActive) {
46+
await extension.activate();
47+
await waitForCondition(() => extension.isActive, 20_000, 'Extension did not activate');
48+
}
49+
50+
api = extension.exports as PythonEnvironmentApi;
51+
assert.ok(api, 'API not available');
52+
53+
// Save original state for restoration
54+
originalProjectEnvs = new Map();
55+
const projects = api.getPythonProjects();
56+
for (const project of projects) {
57+
const env = await api.getEnvironment(project.uri);
58+
originalProjectEnvs.set(project.uri.toString(), env);
59+
}
60+
});
61+
62+
suiteTeardown(async function () {
63+
if (!originalProjectEnvs) {
64+
return;
65+
}
66+
// Restore original state
67+
for (const [uriStr, env] of originalProjectEnvs) {
68+
try {
69+
const uri = vscode.Uri.parse(uriStr);
70+
await api.setEnvironment(uri, env);
71+
} catch {
72+
// Best effort restore
73+
}
74+
}
75+
});
76+
77+
/**
78+
* Test: Multiple projects can have different environments
79+
*
80+
* In a multi-project workspace, each project can have its own environment.
81+
*/
82+
test('Different projects can have different environments', async function () {
83+
const projects = api.getPythonProjects();
84+
const environments = await api.getEnvironments('all');
85+
86+
if (projects.length < 2 || environments.length < 2) {
87+
this.skip();
88+
return;
89+
}
90+
91+
const project1 = projects[0];
92+
const project2 = projects[1];
93+
const env1 = environments[0];
94+
const env2 = environments[1];
95+
96+
// Set different environments for different projects
97+
await api.setEnvironment(project1.uri, env1);
98+
await api.setEnvironment(project2.uri, env2);
99+
100+
// Verify each project has its assigned environment
101+
const retrieved1 = await api.getEnvironment(project1.uri);
102+
const retrieved2 = await api.getEnvironment(project2.uri);
103+
104+
assert.ok(retrieved1, 'Project 1 should have environment');
105+
assert.ok(retrieved2, 'Project 2 should have environment');
106+
assert.strictEqual(retrieved1.envId.id, env1.envId.id, 'Project 1 should have env1');
107+
assert.strictEqual(retrieved2.envId.id, env2.envId.id, 'Project 2 should have env2');
108+
});
109+
110+
/**
111+
* Test: setEnvironment handles URI arrays across projects
112+
*
113+
* setEnvironment should handle array of URIs for multi-select scenarios.
114+
*/
115+
test('setEnvironment handles URI array', async function () {
116+
const environments = await api.getEnvironments('all');
117+
const projects = api.getPythonProjects();
118+
119+
if (environments.length === 0 || projects.length < 2) {
120+
this.skip();
121+
return;
122+
}
123+
124+
const env = environments[0];
125+
const uris = projects.slice(0, 2).map((p) => p.uri);
126+
127+
// Set environment for multiple URIs at once
128+
await api.setEnvironment(uris, env);
129+
130+
// Verify both projects have the environment
131+
for (const uri of uris) {
132+
const retrieved = await api.getEnvironment(uri);
133+
assert.ok(retrieved, `URI ${uri.fsPath} should have environment`);
134+
assert.strictEqual(retrieved.envId.id, env.envId.id, `URI ${uri.fsPath} should have set environment`);
135+
}
136+
});
137+
138+
/**
139+
* Test: Workspace folder settings scope is respected
140+
*
141+
* Settings at workspace folder level should be independently inspectable
142+
* across different folders.
143+
*/
144+
test('Workspace folder settings scope is respected', async function () {
145+
const workspaceFolders = vscode.workspace.workspaceFolders!;
146+
147+
const config1 = vscode.workspace.getConfiguration('python-envs', workspaceFolders[0].uri);
148+
const config2 = vscode.workspace.getConfiguration('python-envs', workspaceFolders[1].uri);
149+
150+
// Both should be independently inspectable
151+
const inspect1 = config1.inspect('pythonProjects');
152+
const inspect2 = config2.inspect('pythonProjects');
153+
154+
// Assert inspect returns valid results for both folders
155+
assert.ok(inspect1, 'Should be able to inspect settings for folder 1');
156+
assert.ok(inspect2, 'Should be able to inspect settings for folder 2');
157+
158+
// Assert the inspection objects have the expected structure
159+
assert.ok('key' in inspect1, 'Inspection should have key property');
160+
assert.ok('key' in inspect2, 'Inspection should have key property');
161+
assert.strictEqual(inspect1.key, 'python-envs.pythonProjects', 'Key should be python-envs.pythonProjects');
162+
assert.strictEqual(inspect2.key, 'python-envs.pythonProjects', 'Key should be python-envs.pythonProjects');
163+
});
164+
165+
/**
166+
* Test: Creation with multiple URIs selects manager
167+
*
168+
* When passing multiple workspace folder URIs, the API should handle
169+
* manager selection and create an environment.
170+
*/
171+
test('Multiple URI scope creation is handled', async function () {
172+
const workspaceFolders = vscode.workspace.workspaceFolders!;
173+
const uris = workspaceFolders.map((f) => f.uri);
174+
let createdEnv: PythonEnvironment | undefined;
175+
176+
try {
177+
// This may prompt for manager selection - quickCreate should handle it
178+
createdEnv = await api.createEnvironment(uris, { quickCreate: true });
179+
180+
if (createdEnv) {
181+
// Verify created environment has valid structure
182+
assert.ok(createdEnv.envId, 'Multi-URI created env must have envId');
183+
assert.ok(createdEnv.environmentPath, 'Multi-URI created env must have environmentPath');
184+
} else {
185+
// quickCreate returned undefined - skip this test as feature not available
186+
this.skip();
187+
return;
188+
}
189+
} finally {
190+
// Cleanup: always try to remove if created
191+
if (createdEnv) {
192+
await api.removeEnvironment(createdEnv);
193+
}
194+
}
195+
});
196+
});

0 commit comments

Comments
 (0)