Skip to content

Enable the Test Activity in VS Code #4282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 5, 2019
Merged
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 news/1 Enhancements/4272.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Enable the Test Activity view, and add a view.
16 changes: 15 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,12 @@
"category": "Python",
"when": "python.datascience.haveinteractive && python.datascience.featureenabled"
}
],
"view/title": [
{
"command": "python.runtests",
"group": "navigation"
}
]
},
"debuggers": [
Expand Down Expand Up @@ -1867,7 +1873,15 @@
"fileMatch": "meta.yaml",
"url": "./schemas/conda-meta.json"
}
]
],
"views": {
"test": [
{
"id": "python_tests",
"name": "PYTHON"
}
]
}
},
"scripts": {
"package": "gulp clean && gulp prePublishBundle && vsce package",
Expand Down
5 changes: 4 additions & 1 deletion src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ if ((Reflect as any).metadata === undefined) {
}

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

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

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

context.subscriptions.push(window.registerTreeDataProvider('python_tests', new PythonTestTreeViewProvider()));

serviceContainer.getAll<DebugConfigurationProvider>(IDebugConfigurationService).forEach(debugConfigProvider => {
context.subscriptions.push(debug.registerDebugConfigurationProvider(DebuggerTypeName, debugConfigProvider));
});
Expand Down
46 changes: 46 additions & 0 deletions src/client/providers/testTreeViewItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

import {
TreeItem, TreeItemCollapsibleState
} from 'vscode';
import { TestStatus } from '../unittests/common/types';

export enum PythonTestTreeItemType {
Root = 'Root',
Package = 'Package',
File = 'File',
Suite = 'Suite',
Function = 'Function'
}

export class PythonTestTreeItem extends TreeItem {

constructor(
kind: PythonTestTreeItemType,
private myParent: PythonTestTreeItem,
private myChildren: PythonTestTreeItem[],
runId: string,
name: string,
testStatus: TestStatus = TestStatus.Unknown) {

super(
`[${kind}] ${name}`,
kind === PythonTestTreeItemType.Function ? TreeItemCollapsibleState.None : TreeItemCollapsibleState.Collapsed
);

this.contextValue = kind;
this.id = runId;
this.tooltip = `Status: ${testStatus}`;
}

public get children(): PythonTestTreeItem[] {
return this.myChildren;
}

public get parent(): PythonTestTreeItem {
return this.myParent;
}
}
105 changes: 105 additions & 0 deletions src/client/providers/testTreeViewProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

import {
Event, EventEmitter,
ProviderResult, TreeDataProvider
} from 'vscode';
import {
PythonTestTreeItem,
PythonTestTreeItemType
} from './testTreeViewItem';

export class PythonTestTreeViewProvider implements TreeDataProvider<PythonTestTreeItem> {
/**
* This will trigger the view to update the changed element/root and its children recursively (if shown).
* To signal that root has changed, do not pass any argument or pass `undefined` or `null`.
*/
public readonly onDidChangeTreeData: Event<PythonTestTreeItem | undefined>;

private _onDidChangeTreeData: EventEmitter<PythonTestTreeItem | undefined> = new EventEmitter<PythonTestTreeItem | undefined>();
private root: PythonTestTreeItem[];

constructor() {
this.onDidChangeTreeData = this._onDidChangeTreeData.event;
// set up some dummy data to just show that the test explorer loads.
this.root = this.getTestTree();
}

/**
* Get [TreeItem](#TreeItem) representation of the `element`
*
* @param element The element for which [TreeItem](#TreeItem) representation is asked for.
* @return [TreeItem](#TreeItem) representation of the element
*/
public async getTreeItem(element: PythonTestTreeItem): Promise<PythonTestTreeItem> {
return element;
}

/**
* Get the children of `element` or root if no element is passed.
*
* @param element The element from which the provider gets children. Can be `undefined`.
* @return Children of `element` or root if no element is passed.
*/
public getChildren(element?: PythonTestTreeItem): ProviderResult<PythonTestTreeItem[]> {
if (element === undefined) {
return this.root;
}
return element.children;
}

/**
* Optional method to return the parent of `element`.
* Return `null` or `undefined` if `element` is a child of root.
*
* **NOTE:** This method should be implemented in order to access [reveal](#TreeView.reveal) API.
*
* @param element The element for which the parent has to be returned.
* @return Parent of `element`.
*/
public getParent?(element: PythonTestTreeItem): ProviderResult<PythonTestTreeItem> {
return element.parent;
}

private getTestTree(): PythonTestTreeItem[] {
// create a sample tree just to get the feature up and running
const roots: PythonTestTreeItem[] = [];
const root1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Root, undefined, [], '/test', '/test');
roots.push(root1);

const root1_pkg1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Package, root1, [], '/test/module1', 'module1');
root1.children.push(root1_pkg1);

const root1_pkg1_file1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.File, root1_pkg1, [], '/test/module1/test_file1.py', 'test_file1.py');
root1_pkg1.children.push(root1_pkg1_file1);

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');
root1_pkg1_file1.children.push(root1_pkg1_file1_fn1);

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');
root1_pkg1_file1.children.push(root1_pkg1_file1_fn2);

const root1_pkg1_file1_suite1: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Suite, root1_pkg1_file1, [], '/test/module1/test_file1.py::TestSuite1', 'TestSuite1');
root1_pkg1_file1.children.push(root1_pkg1_file1_suite1);

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');
root1_pkg1_file1_suite1.children.push(root1_pkg1_file1_suite1_fn1);

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');
root1_pkg1_file1_suite1.children.push(root1_pkg1_file1_suite1_fn2);

const root1_pkg1_file1_suite2: PythonTestTreeItem = new PythonTestTreeItem(PythonTestTreeItemType.Suite, root1_pkg1_file1, [], '/test/module1/test_file1.py::TestSuite2', 'TestSuite2');
root1_pkg1_file1.children.push(root1_pkg1_file1_suite2);

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');
root1_pkg1_file1_suite2.children.push(root1_pkg1_file1_suite2_fn1);

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');
root1_pkg1_file1_suite2.children.push(root1_pkg1_file1_suite2_fn2);

return roots;
}
}
16 changes: 8 additions & 8 deletions src/client/unittests/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,14 @@ export type Tests = {
};

export enum TestStatus {
Unknown,
Discovering,
Idle,
Running,
Fail,
Error,
Skipped,
Pass
Unknown = 'Unknown',
Discovering = 'Discovering',
Idle = 'Idle',
Running = 'Running',
Fail = 'Fail',
Error = 'Error',
Skipped = 'Skipped',
Pass = 'Pass'
}

export type TestsToRun = {
Expand Down