Path: blob/main/extensions/copilot/test/testVisualizationRunner.ts
13383 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/4import { enableHotReload, hotRequire } from '@hediet/node-reload';5import { Module } from 'module';6import { IDebugValueEditorGlobals, IPlaygroundRunnerGlobals } from '../src/util/common/debugValueEditorGlobals';78/** See {@link file://./../.vscode/extensions/visualization-runner/README.md} */910enableHotReload({ loggingEnabled: false });1112const r = Module.prototype.require;13(Module as any).prototype.require = function (this: { filename: string }, path: string) {14if (path === 'vitest') {15return createVitestModule(this.filename);16}17return r.call(this, path);18};1920function run(args: { fileName: string; path: string[] }) {21console.log('> Running test: ' + args.path.join(' > '));22setTestFile(args.fileName);23setTest(args.path);24runCurrentTest();25}2627const g = globalThis as unknown as IDebugValueEditorGlobals & IPlaygroundRunnerGlobals;28g.$$playgroundRunner_data = { currentPath: [] };2930// The timeout seems to fix a deadlock-issue of tsx, when the run function is called from the debugger.31g.$$debugValueEditor_run = args => (setTimeout(() => { run(args); }, 0));32(g.$$debugValueEditor_debugChannels ?? (g.$$debugValueEditor_debugChannels = {}))['run'] = host => ({33handleRequest: (args) => { setTimeout(() => run(args as any), 0); }34});3536let hotRequireDisposable: any;37let currentFileName: string | undefined = undefined;38function setTestFile(fileName: string) {39if (currentFileName === fileName) {40return;41}42currentFileName = fileName;43if (hotRequireDisposable) { hotRequireDisposable.dispose(); }44let isFirst = true;45hotRequireDisposable = hotRequire(module, fileName, cur => {46if (isFirst) {47console.log('> Loading tests');48isFirst = false;49} else {50console.log('> Running test: ' + currentPath.join(' > '));51runCurrentTest();52}53return {54dispose: () => {55testsPerFileName.get(fileName)?.clearCache();56}57};58});59}6061let currentPath: string[] = [];62function setTest(path: string[]) {63currentPath = path;64g.$$playgroundRunner_data.currentPath = path;65}66setTest([]);6768async function runCurrentTest() {69const t = testsPerFileName.get(currentFileName!)?.findTest(currentPath);70if (!t) {71console.error('Test not found', currentPath);72return;73}74try {75const startTime = Date.now();76g.$$debugValueEditor_properties = [];77await t?.runner();78const endTime = Date.now();79const duration = endTime - startTime;80console.log('> Test finished (' + duration + 'ms).');81} catch (e) {82console.error('Test failed:', e);83}84}858687const testsPerFileName = new Map<string, TestContainer>();8889function createVitestModule(filename: string) {90let items: (Test | TestContainer)[] = [];91function getDiscoverFn(fn: () => void) {92return () => {93items = [];94const i = items;95fn();96items = [];97return i;98};99}100101let currentTestContainer: TestContainer;102103const vitest = {} as any;104vitest.describe = function (name: string, fn: () => void) {105currentTestContainer = new TestContainer(name, getDiscoverFn(fn));106items.push(currentTestContainer);107108};109vitest.test = function (name: string, fn: () => void) {110items.push(new Test(name, fn));111};112113vitest.expect = function () {114return {115toBe: function () { },116toMatchInlineSnapshot: function () { },117toMatchFileSnapshot: function () { },118};119};120121testsPerFileName.set(filename, new TestContainer(filename, () => {122const i = items;123items = [];124return i;125}));126127return vitest;128}129130class TestContainer {131private readonly _tests = new Map<string, Test>();132private readonly _containers = new Map<string, TestContainer>();133134private _discovered = false;135136constructor(137public readonly name: string,138private readonly _discoverFn: () => (Test | TestContainer)[],139) {140}141142private _discover(): void {143if (this._discovered) {144return;145}146this._discovered = true;147for (const t of this._discoverFn()) {148if (t instanceof Test) {149this._tests.set(t.name, t);150} else {151this._containers.set(t.name, t);152}153}154}155156getTest(name: string): Test | undefined {157this._discover();158return this._tests.get(name);159}160161getContainer(name: string): TestContainer | undefined {162this._discover();163return this._containers.get(name);164}165166findTest(path: string[]): Test | undefined {167if (path.length === 0) {168throw new Error('Invalid path');169}170let cur: TestContainer = this;171for (let i = 0; i < path.length - 1; i++) {172const c = cur.getContainer(path[i]);173if (!c) { return undefined; }174cur = c;175}176return cur.getTest(path[path.length - 1]);177}178179clearCache(): void {180this._discovered = false;181this._tests.clear();182this._containers.clear();183}184}185186class Test {187constructor(188public readonly name: string,189public readonly runner: () => Promise<void> | void,190) { }191}192193console.log('> Playground runner ready.');194195setTimeout(() => {196if (currentPath.length === 0) {197console.error('Did not run a test after 5 seconds. Probably a bug in the extension?');198}199}, 5000);200201202