Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/managers/builtin/pipUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export async function getProjectInstallable(
{
location: ProgressLocation.Notification,
title: VenvManagerStrings.searchingDependencies,
cancellable: true,
},
async (_progress, token) => {
const results: Uri[] = (
Expand Down
52 changes: 52 additions & 0 deletions src/test/managers/builtin/pipUtils.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,56 @@ suite('Pip Utils - getProjectInstallable', () => {
assert.ok(firstResult.uri, 'Should have a URI');
assert.ok(firstResult.uri.fsPath.startsWith(workspacePath), 'Should be in workspace directory');
});

test('should show cancellable progress notification', async () => {
// Arrange: Mock findFiles to return empty results
findFilesStub.resolves([]);

// Act: Call getProjectInstallable
const workspacePath = Uri.file('/test/path/root').fsPath;
const projects = [{ name: 'workspace', uri: Uri.file(workspacePath) }];
await getProjectInstallable(mockApi as PythonEnvironmentApi, projects);

// Assert: Verify withProgress was called with cancellable option
assert.ok(withProgressStub.calledOnce, 'Should call withProgress once');
const progressOptions = withProgressStub.firstCall.args[0] as ProgressOptions;
assert.strictEqual(progressOptions.cancellable, true, 'Progress should be cancellable');
});

test('should handle cancellation during file search', async () => {
// Arrange: Create a cancellation token that is already cancelled
const cancelledToken: CancellationToken = {
isCancellationRequested: true,
onCancellationRequested: () => ({ dispose: () => {} }),
};

// Mock withProgress to immediately call the callback with the cancelled token
withProgressStub.callsFake(
async (
_options: ProgressOptions,
callback: (
progress: Progress<{ message?: string; increment?: number }>,
token: CancellationToken,
) => Thenable<unknown>,
) => {
return await callback({} as Progress<{ message?: string; increment?: number }>, cancelledToken);
},
);

// Mock findFiles to check that token is passed
findFilesStub.callsFake((_pattern: string, _exclude: string, _maxResults: number, token: CancellationToken) => {
// Verify the cancellation token is passed to findFiles
assert.strictEqual(token, cancelledToken, 'Cancellation token should be passed to findFiles');
return Promise.resolve([]);
});

// Act: Call getProjectInstallable
const workspacePath = Uri.file('/test/path/root').fsPath;
const projects = [{ name: 'workspace', uri: Uri.file(workspacePath) }];
await getProjectInstallable(mockApi as PythonEnvironmentApi, projects);

// Assert: Verify findFiles was called with the cancellation token
assert.ok(findFilesStub.called, 'findFiles should be called');
assert.strictEqual(findFilesStub.callCount, 4, 'Should call findFiles 4 times for different patterns');
});
});