Skip to content

Commit 913ac55

Browse files
committed
Populate the sidebar
1 parent 7606a2e commit 913ac55

File tree

4 files changed

+214
-0
lines changed

4 files changed

+214
-0
lines changed

src/commands/tests_sidebar.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { nova, Process, wrapCommand } from "../nova_utils.ts";
2+
import TestsDataProvider, { PathFile } from "../tests/TestsDataProvider.ts";
3+
4+
export function registerLearnMore() {
5+
return nova.commands.register(
6+
"co.gwil.deno.sidebars.tests.commands.learn",
7+
wrapCommand(learnMore),
8+
);
9+
10+
function learnMore() {
11+
const options = {
12+
args: ["https://deno.land/manual/testing#running-tests"],
13+
};
14+
const process = new Process("/usr/bin/open", options);
15+
process.start();
16+
}
17+
}
18+
19+
export function registerRefresh(testsDataProvider: TestsDataProvider) {
20+
return nova.commands.register(
21+
"co.gwil.deno.sidebars.tests.commands.refresh",
22+
wrapCommand(refresh),
23+
);
24+
25+
function refresh() {
26+
testsDataProvider.refresh();
27+
}
28+
}
29+
30+
export function registerRunAll(testsDataProvider: TestsDataProvider) {
31+
return nova.commands.register(
32+
"co.gwil.deno.sidebars.tests.commands.runAll",
33+
wrapCommand(runAll),
34+
);
35+
36+
function runAll() {
37+
testsDataProvider.runTests();
38+
}
39+
}
40+
41+
export function registerRun(testsDataProvider: TestsDataProvider) {
42+
return nova.commands.register(
43+
"co.gwil.deno.sidebars.tests.commands.run",
44+
wrapCommand(run),
45+
);
46+
47+
function run() {
48+
testsDataProvider.runTests(
49+
testsDataProvider.treeView.selection.filter((item) =>
50+
item instanceof PathFile
51+
) as PathFile[],
52+
);
53+
}
54+
}

src/nova_deno.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
CanNotEnsureError,
1616
makeClientDisposable,
1717
} from "./client_disposable.ts";
18+
import { registerTestsSidebar } from "./tests/register.ts";
1819

1920
const compositeDisposable = new CompositeDisposable();
2021
const taskDisposable = new CompositeDisposable();
@@ -53,6 +54,8 @@ export async function activate() {
5354

5455
compositeDisposable.add(registerEditorWatcher());
5556

57+
compositeDisposable.add(registerTestsSidebar());
58+
5659
const configFileWatchingDisposables = watchConfigFiles(
5760
nova.workspace.path,
5861
configFilenames,

src/tests/TestsDataProvider.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import {
2+
nova,
3+
Process,
4+
TreeDataProvider,
5+
TreeItem,
6+
TreeItemCollapsibleState,
7+
TreeView,
8+
} from "../nova_utils.ts";
9+
10+
// This is duplicate code.
11+
interface Element {
12+
toTreeItem: () => TreeItem;
13+
children: Element[];
14+
}
15+
// This is also duplicate code.
16+
class Header implements Element {
17+
content: string;
18+
children: [];
19+
constructor(content: string) {
20+
this.content = content;
21+
this.children = [];
22+
}
23+
24+
toTreeItem() {
25+
const item = new TreeItem(this.content);
26+
return item;
27+
}
28+
}
29+
export class PathFile implements Element {
30+
path: string;
31+
children: [];
32+
shouldDisambiguate: boolean;
33+
34+
constructor(path: string, children: [], shouldDisambiguate: boolean) {
35+
this.path = path;
36+
this.children = children;
37+
this.shouldDisambiguate = shouldDisambiguate;
38+
}
39+
40+
get filename(): string {
41+
return nova.path.basename(this.path);
42+
}
43+
44+
get extension(): string {
45+
return nova.path.extname(this.path);
46+
}
47+
48+
toTreeItem() {
49+
const relativePath: string = nova.workspace.relativizePath(this.path);
50+
const item = new TreeItem(
51+
this.shouldDisambiguate ? relativePath : this.filename,
52+
);
53+
54+
item.image = "__filetype" + this.extension;
55+
item.collapsibleState = TreeItemCollapsibleState.Expanded;
56+
item.contextValue = "file";
57+
return item;
58+
}
59+
}
60+
61+
export default class TestsDataProvider implements TreeDataProvider<Element> {
62+
// See https://deno.land/manual/testing#running-tests.
63+
static FILE_REGEXP = /^.*[_.]?test\.(ts|tsx|mts|js|mjs|jsx|cjs|cts)$/;
64+
treeView: TreeView<Element>;
65+
files: Element[];
66+
67+
constructor() {
68+
this.treeView = new TreeView("co.gwil.deno.sidebars.tests.sections.1", {
69+
dataProvider: this,
70+
});
71+
this.files = [];
72+
}
73+
74+
static findTests(directoryPath: string): string[] {
75+
function isAccessibleDirectory(path: string) {
76+
try {
77+
nova.fs.listdir(path);
78+
} catch {
79+
return false;
80+
}
81+
return true;
82+
}
83+
84+
const tests: string[] = [];
85+
for (const name of nova.fs.listdir(directoryPath)) {
86+
const path = nova.path.join(directoryPath, name);
87+
if (isAccessibleDirectory(path)) {
88+
let internalTests = null;
89+
try {
90+
internalTests = TestsDataProvider.findTests(path);
91+
} catch {
92+
continue;
93+
}
94+
tests.push(...internalTests);
95+
} else if (name.match(TestsDataProvider.FILE_REGEXP)) {
96+
tests.push(path);
97+
}
98+
}
99+
return tests;
100+
}
101+
102+
runTests(files?: PathFile[]) {
103+
files ??= [];
104+
const paths = files.map((file) => file.path);
105+
106+
const options = {
107+
args: ["deno", ...paths],
108+
};
109+
const denoProcess = new Process("/usr/bin/env", options);
110+
denoProcess.onStdout(console.log);
111+
denoProcess.onStderr(console.log);
112+
113+
denoProcess.start();
114+
}
115+
116+
getChildren(element: Element | null): Element[] {
117+
if (element == null) {
118+
if (nova.workspace.path) {
119+
return TestsDataProvider.findTests(nova.workspace.path).map((path) =>
120+
new PathFile(path, [], false)
121+
);
122+
} else {
123+
return [new Header("Open a Deno project to see test files.")];
124+
}
125+
}
126+
return element.children;
127+
}
128+
129+
getTreeItem(element: Element): TreeItem {
130+
return element.toTreeItem();
131+
}
132+
133+
refresh() {
134+
return this.treeView.reload();
135+
}
136+
}

src/tests/register.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { CompositeDisposable } from "../nova_utils.ts";
2+
import {
3+
registerLearnMore,
4+
registerRefresh,
5+
} from "../commands/tests_sidebar.ts";
6+
import TestsDataProvider from "./TestsDataProvider.ts";
7+
8+
let testsDataProvider: TestsDataProvider | null = null;
9+
10+
export function registerTestsSidebar() {
11+
const testsDisposable = new CompositeDisposable();
12+
13+
const dataProvider = new TestsDataProvider();
14+
testsDataProvider = dataProvider;
15+
testsDisposable.add(dataProvider.treeView);
16+
17+
testsDisposable.add(registerLearnMore());
18+
testsDisposable.add(registerRefresh(testsDataProvider));
19+
20+
return testsDisposable;
21+
}

0 commit comments

Comments
 (0)