Skip to content

Commit db11fa0

Browse files
authored
Basic unit test for workspace viewer (#1305)
- Introduces basic unit testing for the workspace viewer - Proposes unit testing through mocking the vscode API via the sinon library
1 parent 0e49417 commit db11fa0

9 files changed

Lines changed: 3020 additions & 2627 deletions

File tree

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2011,6 +2011,7 @@
20112011
"@types/mocha": "^8.2.2",
20122012
"@types/node": "^16.11.7",
20132013
"@types/node-fetch": "^2.5.10",
2014+
"@types/sinon": "^10.0.13",
20142015
"@types/vscode": "^1.67.0",
20152016
"@types/winreg": "^1.2.31",
20162017
"@typescript-eslint/eslint-plugin": "^5.30.0",
@@ -2021,6 +2022,7 @@
20212022
"eslint": "^7.28.0",
20222023
"eslint-plugin-jsdoc": "^35.1.3",
20232024
"mocha": "^9.1.0",
2025+
"sinon": "^15.0.1",
20242026
"ts-loader": "^9.3.1",
20252027
"typescript": "^4.7.2",
20262028
"webpack": "^5.38.1",

src/rmarkdown/manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export abstract class RMarkdownManager {
177177
let childProcess: DisposableProcess | undefined = undefined;
178178
await util.doWithProgress(
179179
(async (
180-
token: vscode.CancellationToken | undefined,
180+
token: vscode.CancellationToken | undefined,
181181
progress: vscode.Progress<{
182182
message?: string | undefined;
183183
increment?: number | undefined;

src/test/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './mockvscode';

src/test/common/mockvscode.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as vscode from 'vscode';
2+
import sinon = require('sinon');
3+
import path = require('path');
4+
import * as ext from '../../extension';
5+
6+
export function mockActiveTextEditor(document: vscode.TextDocument, sandbox: sinon.SinonSandbox) {
7+
return sandbox.stub(vscode.window, 'activeTextEditor').value({
8+
document
9+
});
10+
}
11+
12+
export function mockExtensionContext(extension_root: string, sandbox: sinon.SinonSandbox) {
13+
const mockExtensionContext = {
14+
environmentVariableCollection: sandbox.stub(),
15+
extension: sandbox.stub(),
16+
extensionMode: sandbox.stub(),
17+
extensionPath: sandbox.stub(),
18+
extensionUri: sandbox.stub(),
19+
globalState: {
20+
get: sinon.stub(),
21+
set: sinon.stub()
22+
},
23+
globalStorageUri: sandbox.stub(),
24+
logUri: sandbox.stub(),
25+
secrets: sandbox.stub(),
26+
storageUri: sandbox.stub(),
27+
subscriptions: [],
28+
workspaceState: {
29+
get: sinon.stub(),
30+
update: sinon.stub()
31+
},
32+
asAbsolutePath: (relativePath: string) => {
33+
return path.join(extension_root, relativePath);
34+
}
35+
};
36+
return sandbox.stub(ext, 'extensionContext').value(mockExtensionContext);
37+
}

src/test/suite/sesson.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import vscode = require('vscode');
2+
import sinon = require('sinon');
3+
import path = require('path');
4+
import * as assert from 'assert';
5+
import * as fs from 'fs-extra';
6+
7+
import { mockExtensionContext } from '../common';
8+
import * as session from '../../session';
9+
import * as workspace from '../../workspaceViewer';
10+
11+
const extension_root: string = path.join(__dirname, '..', '..', '..');
12+
const workspaceFile = path.join(extension_root, 'test', 'rFiles', 'session', 'workspace.json');
13+
14+
function mockWorkspaceData(sandbox: sinon.SinonSandbox) {
15+
const content = fs.readFileSync(workspaceFile, 'utf8');
16+
const workspaceData = JSON.parse(content) as session.WorkspaceData;
17+
return sandbox.stub(session, 'workspaceData').value(workspaceData);
18+
}
19+
20+
suite('Workspace Viewer', () => {
21+
let sandbox: sinon.SinonSandbox;
22+
let workspaceViewer: workspace.WorkspaceDataProvider;
23+
let nodes: vscode.TreeItem[];
24+
25+
setup(() => {
26+
sandbox = sinon.createSandbox();
27+
});
28+
teardown(() => {
29+
sandbox.restore();
30+
});
31+
32+
test('has 3 nodes', async () => {
33+
mockExtensionContext(extension_root, sandbox);
34+
mockWorkspaceData(sandbox);
35+
workspaceViewer = new workspace.WorkspaceDataProvider();
36+
workspaceViewer.refresh();
37+
nodes = await workspaceViewer.getChildren();
38+
assert.strictEqual(nodes.length, 3);
39+
});
40+
41+
test('search node', async () => {
42+
const search = await workspaceViewer.getChildren(nodes[0]);
43+
assert.strictEqual(search.length, 10);
44+
});
45+
46+
test('attached node', async () => {
47+
const attached = await workspaceViewer.getChildren(nodes[1]);
48+
assert.strictEqual(attached.length, 14);
49+
});
50+
51+
test('env node', async () => {
52+
const env: workspace.GlobalEnvItem[] = await workspaceViewer.getChildren(nodes[2]) as workspace.GlobalEnvItem[];
53+
assert.strictEqual(env.length, 9);
54+
});
55+
});

src/util.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ export async function spawnAsync(command: string, args?: ReadonlyArray<string>,
472472
status: null,
473473
signal: null
474474
};
475-
475+
476476
try {
477477
const childProcess = spawn(command, args, options, onDisposed);
478478
if (childProcess.pid !== undefined) {
@@ -536,7 +536,7 @@ export async function promptToInstallRPackage(name: string, section: string, cwd
536536

537537
/**
538538
* Create temporary directory. Will avoid name clashes. Caller must delete directory after use.
539-
*
539+
*
540540
* @param root Parent folder.
541541
* @param hidden If set to true, directory will be prefixed with a '.' (ignored on windows).
542542
* @returns Path to the temporary directory.
@@ -551,18 +551,18 @@ export function createTempDir(root: string, hidden?: boolean): string {
551551

552552
/**
553553
* Utility function for converting 'unknown' types to errors.
554-
*
554+
*
555555
* Usage:
556-
*
556+
*
557557
* ```ts
558-
* try { ... }
559-
* catch (e) {
560-
* const err: Error = catchAsError(e);
558+
* try { ... }
559+
* catch (e) {
560+
* const err: Error = catchAsError(e);
561561
* }
562562
* ```
563-
* @param err
564-
* @param fallbackMessage
565-
* @returns
563+
* @param err
564+
* @param fallbackMessage
565+
* @returns
566566
*/
567567
export function catchAsError(err: unknown, fallbackMessage?: string): Error {
568568
return (err instanceof Error) ? err : Error(fallbackMessage ?? 'Unknown error');
@@ -623,7 +623,7 @@ export function statSyncSafe(path: fs.PathLike): fs.Stats | undefined {
623623
try {
624624
return fs.statSync(path);
625625
} catch (e) {
626-
626+
627627
}
628628
}
629629

test/rFiles/rmarkdown/basic.rmd

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: Hello World
3+
---
4+
5+
Hello world

test/rFiles/session/workspace.json

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
{
2+
"search": [
3+
"tools:vscode",
4+
"package:stats",
5+
"package:graphics",
6+
"package:grDevices",
7+
"package:datasets",
8+
"tools:custom",
9+
"package:utils",
10+
"package:methods",
11+
"Autoloads",
12+
"package:base"
13+
],
14+
"loaded_namespaces": [
15+
"compiler",
16+
"cli",
17+
"graphics",
18+
"tools",
19+
"utils",
20+
"grDevices",
21+
"crayon",
22+
"stats",
23+
"datasets",
24+
"methods",
25+
"jsonlite",
26+
"rlang",
27+
"base",
28+
"pak"
29+
],
30+
"globalenv": {
31+
"a": {
32+
"class": [
33+
"character"
34+
],
35+
"type": "character",
36+
"length": 1,
37+
"size": 112,
38+
"str": "chr \"a\""
39+
},
40+
"b": {
41+
"class": [
42+
"integer"
43+
],
44+
"type": "integer",
45+
"length": 1,
46+
"size": 56,
47+
"str": "int 1"
48+
},
49+
"c": {
50+
"class": [
51+
"numeric"
52+
],
53+
"type": "double",
54+
"length": 1,
55+
"size": 56,
56+
"str": "num 1.5"
57+
},
58+
"d": {
59+
"class": [
60+
"complex"
61+
],
62+
"type": "complex",
63+
"length": 1,
64+
"size": 64,
65+
"str": "cplx 0+0i"
66+
},
67+
"e": {
68+
"class": [
69+
"logical"
70+
],
71+
"type": "logical",
72+
"length": 1,
73+
"size": 56,
74+
"str": "logi TRUE"
75+
},
76+
"f": {
77+
"class": [
78+
"function"
79+
],
80+
"type": "closure",
81+
"length": 1,
82+
"size": 1240,
83+
"str": "function (x, ...)"
84+
},
85+
"g": {
86+
"class": [
87+
"environment"
88+
],
89+
"type": "environment",
90+
"length": 11,
91+
"size": 56,
92+
"str": "<environment: R_GlobalEnv>",
93+
"names": [
94+
"a",
95+
"b",
96+
"c",
97+
"d",
98+
"e",
99+
"f",
100+
".local.name",
101+
"g",
102+
".local",
103+
"h",
104+
"i"
105+
]
106+
},
107+
"h": {
108+
"class": [
109+
"list"
110+
],
111+
"type": "list",
112+
"length": 2,
113+
"size": 520,
114+
"str": "List of 2\n $ a: num 1\n $ b: chr \"foo\"",
115+
"names": [
116+
"a",
117+
"b"
118+
]
119+
},
120+
"i": {
121+
"class": [
122+
"data.frame"
123+
],
124+
"type": "list",
125+
"length": 11,
126+
"size": 7208,
127+
"str": "'data.frame':\t32 obs. of 11 variables:\n $ mpg : num 21 21 ...\n $ cyl : num 6 6 ...\n $ disp: num 160 160 ...\n $ hp : num 110 110 ...\n $ drat: num 3.9 3.9 ...\n $ wt : num 2.62 ...\n $ qsec: num 16.5 ...\n $ vs : num 0 0 ...\n $ am : num 1 1 ...\n $ gear: num 4 4 ...\n $ carb: num 4 4 ...",
128+
"names": [
129+
"mpg",
130+
"cyl",
131+
"disp",
132+
"hp",
133+
"drat",
134+
"wt",
135+
"qsec",
136+
"vs",
137+
"am",
138+
"gear",
139+
"carb"
140+
],
141+
"dim": [
142+
32,
143+
11
144+
]
145+
}
146+
}
147+
}

0 commit comments

Comments
 (0)