Skip to content

Commit 9187479

Browse files
authored
Enable the Test Activity in VS Code (#4282)
Initial checkin to enable Test Explorer view - Enables the Test Activity in VS Code - Adds a single view - Adds a single command (no icon) - Adds dummy tree view data
1 parent 11bbf52 commit 9187479

File tree

6 files changed

+179
-10
lines changed

6 files changed

+179
-10
lines changed

news/1 Enhancements/4272.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Enable the Test Activity view, and add a view.

package.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,12 @@
528528
"category": "Python",
529529
"when": "python.datascience.haveinteractive && python.datascience.featureenabled"
530530
}
531+
],
532+
"view/title": [
533+
{
534+
"command": "python.runtests",
535+
"group": "navigation"
536+
}
531537
]
532538
},
533539
"debuggers": [
@@ -1867,7 +1873,15 @@
18671873
"fileMatch": "meta.yaml",
18681874
"url": "./schemas/conda-meta.json"
18691875
}
1870-
]
1876+
],
1877+
"views": {
1878+
"test": [
1879+
{
1880+
"id": "python_tests",
1881+
"name": "PYTHON"
1882+
}
1883+
]
1884+
}
18711885
},
18721886
"scripts": {
18731887
"package": "gulp clean && gulp prePublishBundle && vsce package",

src/client/extension.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ if ((Reflect as any).metadata === undefined) {
88
}
99

1010
// Initialize source maps (this must never be moved up nor further down).
11-
import {initialize } from './sourceMapSupport';
11+
import { initialize } from './sourceMapSupport';
1212
initialize(require('vscode'));
1313

1414
const durations: Record<string, number> = {};
@@ -93,6 +93,7 @@ import { ReplProvider } from './providers/replProvider';
9393
import { registerTypes as providersRegisterTypes } from './providers/serviceRegistry';
9494
import { activateSimplePythonRefactorProvider } from './providers/simpleRefactorProvider';
9595
import { TerminalProvider } from './providers/terminalProvider';
96+
import { PythonTestTreeViewProvider } from './providers/testTreeViewProvider';
9697
import { ISortImportsEditingProvider } from './providers/types';
9798
import { activateUpdateSparkLibraryProvider } from './providers/updateSparkLibraryProvider';
9899
import { sendTelemetryEvent } from './telemetry';
@@ -206,6 +207,8 @@ async function activateUnsafe(context: ExtensionContext): Promise<IExtensionApi>
206207

207208
context.subscriptions.push(languages.registerCodeActionsProvider(PYTHON, new PythonCodeActionProvider(), { providedCodeActionKinds: [CodeActionKind.SourceOrganizeImports] }));
208209

210+
context.subscriptions.push(window.registerTreeDataProvider('python_tests', new PythonTestTreeViewProvider()));
211+
209212
serviceContainer.getAll<DebugConfigurationProvider>(IDebugConfigurationService).forEach(debugConfigProvider => {
210213
context.subscriptions.push(debug.registerDebugConfigurationProvider(DebuggerTypeName, debugConfigProvider));
211214
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import {
7+
TreeItem, TreeItemCollapsibleState
8+
} from 'vscode';
9+
import { TestStatus } from '../unittests/common/types';
10+
11+
export enum PythonTestTreeItemType {
12+
Root = 'Root',
13+
Package = 'Package',
14+
File = 'File',
15+
Suite = 'Suite',
16+
Function = 'Function'
17+
}
18+
19+
export class PythonTestTreeItem extends TreeItem {
20+
21+
constructor(
22+
kind: PythonTestTreeItemType,
23+
private myParent: PythonTestTreeItem,
24+
private myChildren: PythonTestTreeItem[],
25+
runId: string,
26+
name: string,
27+
testStatus: TestStatus = TestStatus.Unknown) {
28+
29+
super(
30+
`[${kind}] ${name}`,
31+
kind === PythonTestTreeItemType.Function ? TreeItemCollapsibleState.None : TreeItemCollapsibleState.Collapsed
32+
);
33+
34+
this.contextValue = kind;
35+
this.id = runId;
36+
this.tooltip = `Status: ${testStatus}`;
37+
}
38+
39+
public get children(): PythonTestTreeItem[] {
40+
return this.myChildren;
41+
}
42+
43+
public get parent(): PythonTestTreeItem {
44+
return this.myParent;
45+
}
46+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import {
7+
Event, EventEmitter,
8+
ProviderResult, TreeDataProvider
9+
} from 'vscode';
10+
import {
11+
PythonTestTreeItem,
12+
PythonTestTreeItemType
13+
} from './testTreeViewItem';
14+
15+
export class PythonTestTreeViewProvider implements TreeDataProvider<PythonTestTreeItem> {
16+
/**
17+
* This will trigger the view to update the changed element/root and its children recursively (if shown).
18+
* To signal that root has changed, do not pass any argument or pass `undefined` or `null`.
19+
*/
20+
public readonly onDidChangeTreeData: Event<PythonTestTreeItem | undefined>;
21+
22+
private _onDidChangeTreeData: EventEmitter<PythonTestTreeItem | undefined> = new EventEmitter<PythonTestTreeItem | undefined>();
23+
private root: PythonTestTreeItem[];
24+
25+
constructor() {
26+
this.onDidChangeTreeData = this._onDidChangeTreeData.event;
27+
// set up some dummy data to just show that the test explorer loads.
28+
this.root = this.getTestTree();
29+
}
30+
31+
/**
32+
* Get [TreeItem](#TreeItem) representation of the `element`
33+
*
34+
* @param element The element for which [TreeItem](#TreeItem) representation is asked for.
35+
* @return [TreeItem](#TreeItem) representation of the element
36+
*/
37+
public async getTreeItem(element: PythonTestTreeItem): Promise<PythonTestTreeItem> {
38+
return element;
39+
}
40+
41+
/**
42+
* Get the children of `element` or root if no element is passed.
43+
*
44+
* @param element The element from which the provider gets children. Can be `undefined`.
45+
* @return Children of `element` or root if no element is passed.
46+
*/
47+
public getChildren(element?: PythonTestTreeItem): ProviderResult<PythonTestTreeItem[]> {
48+
if (element === undefined) {
49+
return this.root;
50+
}
51+
return element.children;
52+
}
53+
54+
/**
55+
* Optional method to return the parent of `element`.
56+
* Return `null` or `undefined` if `element` is a child of root.
57+
*
58+
* **NOTE:** This method should be implemented in order to access [reveal](#TreeView.reveal) API.
59+
*
60+
* @param element The element for which the parent has to be returned.
61+
* @return Parent of `element`.
62+
*/
63+
public getParent?(element: PythonTestTreeItem): ProviderResult<PythonTestTreeItem> {
64+
return element.parent;
65+
}
66+
67+
private getTestTree(): PythonTestTreeItem[] {
68+
// create a sample tree just to get the feature up and running
69+
const roots: PythonTestTreeItem[] = [];
70+
const root1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Root, undefined, [], '/test', '/test');
71+
roots.push(root1);
72+
73+
const root1_pkg1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Package, root1, [], '/test/module1', 'module1');
74+
root1.children.push(root1_pkg1);
75+
76+
const root1_pkg1_file1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.File, root1_pkg1, [], '/test/module1/test_file1.py', 'test_file1.py');
77+
root1_pkg1.children.push(root1_pkg1_file1);
78+
79+
const root1_pkg1_file1_fn1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Function, root1_pkg1_file1, undefined, '/test/module1/test_file1.py::test_function_1', 'test_function_1');
80+
root1_pkg1_file1.children.push(root1_pkg1_file1_fn1);
81+
82+
const root1_pkg1_file1_fn2: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Function, root1_pkg1_file1, undefined, '/test/module1/test_file1.py::test_function_2', 'test_function_2');
83+
root1_pkg1_file1.children.push(root1_pkg1_file1_fn2);
84+
85+
const root1_pkg1_file1_suite1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Suite, root1_pkg1_file1, [], '/test/module1/test_file1.py::TestSuite1', 'TestSuite1');
86+
root1_pkg1_file1.children.push(root1_pkg1_file1_suite1);
87+
88+
const root1_pkg1_file1_suite1_fn1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Function, root1_pkg1_file1_suite1, undefined, '/test/module1/test_file1.py::TestSuite1::test_suite1_fn1', 'test_suite1_fn1');
89+
root1_pkg1_file1_suite1.children.push(root1_pkg1_file1_suite1_fn1);
90+
91+
const root1_pkg1_file1_suite1_fn2: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Function, root1_pkg1_file1_suite1, undefined, '/test/module1/test_file1.py::TestSuite1::test_suite1_fn2', 'test_suite1_fn2');
92+
root1_pkg1_file1_suite1.children.push(root1_pkg1_file1_suite1_fn2);
93+
94+
const root1_pkg1_file1_suite2: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Suite, root1_pkg1_file1, [], '/test/module1/test_file1.py::TestSuite2', 'TestSuite2');
95+
root1_pkg1_file1.children.push(root1_pkg1_file1_suite2);
96+
97+
const root1_pkg1_file1_suite2_fn1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Function, root1_pkg1_file1_suite2, undefined, '/test/module1/test_file1.py::TestSuite2::test_suite2_fn1', 'test_suite2_fn1');
98+
root1_pkg1_file1_suite2.children.push(root1_pkg1_file1_suite2_fn1);
99+
100+
const root1_pkg1_file1_suite2_fn2: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Function, root1_pkg1_file1_suite2, undefined, '/test/module1/test_file1.py::TestSuite2::test_suite2_fn2', 'test_suite2_fn2');
101+
root1_pkg1_file1_suite2.children.push(root1_pkg1_file1_suite2_fn2);
102+
103+
return roots;
104+
}
105+
}

src/client/unittests/common/types.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,14 @@ export type Tests = {
110110
};
111111

112112
export enum TestStatus {
113-
Unknown,
114-
Discovering,
115-
Idle,
116-
Running,
117-
Fail,
118-
Error,
119-
Skipped,
120-
Pass
113+
Unknown = 'Unknown',
114+
Discovering = 'Discovering',
115+
Idle = 'Idle',
116+
Running = 'Running',
117+
Fail = 'Fail',
118+
Error = 'Error',
119+
Skipped = 'Skipped',
120+
Pass = 'Pass'
121121
}
122122

123123
export type TestsToRun = {

0 commit comments

Comments
 (0)