Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/test/testVisualizationRunner.ts
13383 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
import { enableHotReload, hotRequire } from '@hediet/node-reload';
6
import { Module } from 'module';
7
import { IDebugValueEditorGlobals, IPlaygroundRunnerGlobals } from '../src/util/common/debugValueEditorGlobals';
8
9
/** See {@link file://./../.vscode/extensions/visualization-runner/README.md} */
10
11
enableHotReload({ loggingEnabled: false });
12
13
const r = Module.prototype.require;
14
(Module as any).prototype.require = function (this: { filename: string }, path: string) {
15
if (path === 'vitest') {
16
return createVitestModule(this.filename);
17
}
18
return r.call(this, path);
19
};
20
21
function run(args: { fileName: string; path: string[] }) {
22
console.log('> Running test: ' + args.path.join(' > '));
23
setTestFile(args.fileName);
24
setTest(args.path);
25
runCurrentTest();
26
}
27
28
const g = globalThis as unknown as IDebugValueEditorGlobals & IPlaygroundRunnerGlobals;
29
g.$$playgroundRunner_data = { currentPath: [] };
30
31
// The timeout seems to fix a deadlock-issue of tsx, when the run function is called from the debugger.
32
g.$$debugValueEditor_run = args => (setTimeout(() => { run(args); }, 0));
33
(g.$$debugValueEditor_debugChannels ?? (g.$$debugValueEditor_debugChannels = {}))['run'] = host => ({
34
handleRequest: (args) => { setTimeout(() => run(args as any), 0); }
35
});
36
37
let hotRequireDisposable: any;
38
let currentFileName: string | undefined = undefined;
39
function setTestFile(fileName: string) {
40
if (currentFileName === fileName) {
41
return;
42
}
43
currentFileName = fileName;
44
if (hotRequireDisposable) { hotRequireDisposable.dispose(); }
45
let isFirst = true;
46
hotRequireDisposable = hotRequire(module, fileName, cur => {
47
if (isFirst) {
48
console.log('> Loading tests');
49
isFirst = false;
50
} else {
51
console.log('> Running test: ' + currentPath.join(' > '));
52
runCurrentTest();
53
}
54
return {
55
dispose: () => {
56
testsPerFileName.get(fileName)?.clearCache();
57
}
58
};
59
});
60
}
61
62
let currentPath: string[] = [];
63
function setTest(path: string[]) {
64
currentPath = path;
65
g.$$playgroundRunner_data.currentPath = path;
66
}
67
setTest([]);
68
69
async function runCurrentTest() {
70
const t = testsPerFileName.get(currentFileName!)?.findTest(currentPath);
71
if (!t) {
72
console.error('Test not found', currentPath);
73
return;
74
}
75
try {
76
const startTime = Date.now();
77
g.$$debugValueEditor_properties = [];
78
await t?.runner();
79
const endTime = Date.now();
80
const duration = endTime - startTime;
81
console.log('> Test finished (' + duration + 'ms).');
82
} catch (e) {
83
console.error('Test failed:', e);
84
}
85
}
86
87
88
const testsPerFileName = new Map<string, TestContainer>();
89
90
function createVitestModule(filename: string) {
91
let items: (Test | TestContainer)[] = [];
92
function getDiscoverFn(fn: () => void) {
93
return () => {
94
items = [];
95
const i = items;
96
fn();
97
items = [];
98
return i;
99
};
100
}
101
102
let currentTestContainer: TestContainer;
103
104
const vitest = {} as any;
105
vitest.describe = function (name: string, fn: () => void) {
106
currentTestContainer = new TestContainer(name, getDiscoverFn(fn));
107
items.push(currentTestContainer);
108
109
};
110
vitest.test = function (name: string, fn: () => void) {
111
items.push(new Test(name, fn));
112
};
113
114
vitest.expect = function () {
115
return {
116
toBe: function () { },
117
toMatchInlineSnapshot: function () { },
118
toMatchFileSnapshot: function () { },
119
};
120
};
121
122
testsPerFileName.set(filename, new TestContainer(filename, () => {
123
const i = items;
124
items = [];
125
return i;
126
}));
127
128
return vitest;
129
}
130
131
class TestContainer {
132
private readonly _tests = new Map<string, Test>();
133
private readonly _containers = new Map<string, TestContainer>();
134
135
private _discovered = false;
136
137
constructor(
138
public readonly name: string,
139
private readonly _discoverFn: () => (Test | TestContainer)[],
140
) {
141
}
142
143
private _discover(): void {
144
if (this._discovered) {
145
return;
146
}
147
this._discovered = true;
148
for (const t of this._discoverFn()) {
149
if (t instanceof Test) {
150
this._tests.set(t.name, t);
151
} else {
152
this._containers.set(t.name, t);
153
}
154
}
155
}
156
157
getTest(name: string): Test | undefined {
158
this._discover();
159
return this._tests.get(name);
160
}
161
162
getContainer(name: string): TestContainer | undefined {
163
this._discover();
164
return this._containers.get(name);
165
}
166
167
findTest(path: string[]): Test | undefined {
168
if (path.length === 0) {
169
throw new Error('Invalid path');
170
}
171
let cur: TestContainer = this;
172
for (let i = 0; i < path.length - 1; i++) {
173
const c = cur.getContainer(path[i]);
174
if (!c) { return undefined; }
175
cur = c;
176
}
177
return cur.getTest(path[path.length - 1]);
178
}
179
180
clearCache(): void {
181
this._discovered = false;
182
this._tests.clear();
183
this._containers.clear();
184
}
185
}
186
187
class Test {
188
constructor(
189
public readonly name: string,
190
public readonly runner: () => Promise<void> | void,
191
) { }
192
}
193
194
console.log('> Playground runner ready.');
195
196
setTimeout(() => {
197
if (currentPath.length === 0) {
198
console.error('Did not run a test after 5 seconds. Probably a bug in the extension?');
199
}
200
}, 5000);
201
202