Skip to content

Commit

Permalink
feat: watcher works. now
Browse files Browse the repository at this point in the history
need more works to improve dx
  • Loading branch information
zxch3n committed May 16, 2022
1 parent 6a28047 commit 87071ee
Show file tree
Hide file tree
Showing 10 changed files with 469 additions and 62 deletions.
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@
"devDependencies": {
"@types/fs-extra": "^9.0.13",
"@types/glob": "^7.2.0",
"@types/mocha": "^9.1.0",
"@types/node": "14.x",
"@types/semver": "^7.3.9",
"@types/vscode": "^1.59.0",
"@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.12.1",
"@typescript-eslint/parser": "^5.12.1",
"@vitest/ws-client": "^0.12.6",
"@vscode/test-electron": "^2.1.2",
"@vueuse/core": "^8.4.2",
"eslint": "^8.9.0",
Expand All @@ -133,8 +133,7 @@
"tsup": "^5.12.7",
"typescript": "^4.5.5",
"vite": "^2.8.6",
"vitest": "latest",
"@vitest/ws-client": "^0.12.6"
"vitest": "latest"
},
"dependencies": {
"@babel/parser": "^7.17.3",
Expand Down
5 changes: 4 additions & 1 deletion samples/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"license": "ISC",
"devDependencies": {
"vite": "^2.8.6",
"vitest": "^0.8.0"
"vitest": "latest"
},
"dependencies": {
"birpc": "^0.2.2"
}
}
2 changes: 1 addition & 1 deletion samples/basic/test/add.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe("addition", () => {

it("should failed", async () => {
await new Promise((r) => setTimeout(r, 100));
expect(1 + 2).toBe(2);
expect(1 + 2).toBe(3);
});

it.skip("skipped", () => {
Expand Down
3 changes: 2 additions & 1 deletion samples/basic/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { defineConfig } from "vite";

export default defineConfig({
test: {
include: ["src/should_included_test.ts", "test/**"],
include: ["src/should_included_test.ts", "test/**/*.test.ts"],
exclude: ["test/ignored.test.ts"],
},
});
196 changes: 179 additions & 17 deletions samples/basic/yarn.lock

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions src/discover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ export class TestFileDiscoverer extends vscode.Disposable {
path: string,
) {
const { data } = this.getOrCreateFile(controller, vscode.Uri.file(path));
data.updateFromDisk(controller);
if (!data.resolved) {
data.updateFromDisk(controller);
}
return data;
}

Expand Down Expand Up @@ -210,7 +212,9 @@ export function discoverTestFromFileContent(
const top = ancestors.pop();
if (top) {
top.item.children.replace(top.children);
(top.data as (TestFile | TestDescribe)).children = top.dataChildren;
(top.data as (TestFile | TestDescribe)).children = [
...top.dataChildren,
];
}

parent = ancestors[ancestors.length - 1];
Expand Down Expand Up @@ -279,6 +283,9 @@ export function discoverTestFromFileContent(
const top = ancestors.pop();
if (top) {
top.item.children.replace(top.children);
(top.data as (TestFile | TestDescribe)).children = [
...top.dataChildren,
];
}
}
}
2 changes: 1 addition & 1 deletion src/pure/watch/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createClient } from "@vitest/ws-client";
import WebSocket from "ws";
import { computed, effect, reactive, ref, shallowRef } from "@vue/reactivity";
import type { ResolvedConfig, WebSocketEvents } from "vitest";
import { createClient } from "./ws-client";

type WebSocketStatus = "OPEN" | "CONNECTING" | "CLOSED";
(globalThis as any).WebSocket = WebSocket;
Expand Down
189 changes: 189 additions & 0 deletions src/pure/watch/ws-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// this file is copied from vitest with a few modifications;
import { shallowReactive } from "@vue/reactivity";
import type { BirpcReturn } from "birpc";
import { createBirpc } from "birpc";
import { parse, stringify } from "flatted";
// eslint-disable-next-line no-restricted-imports
import type { WebSocketEvents, WebSocketHandlers } from "vitest";
import type {
ErrorWithDiff,
File,
Task,
TaskResultPack,
UserConsoleLog,
} from "vitest";

class StateManager {
filesMap = new Map<string, File>();
idMap = new Map<string, Task>();
taskFileMap = new WeakMap<Task, File>();
errorsSet = new Set<unknown>();

catchError(err: unknown, type: string) {
(err as any).type = type;
this.errorsSet.add(err);
}

clearErrors() {
this.errorsSet.clear();
}

getUnhandledErrors() {
return Array.from(this.errorsSet.values());
}

getFiles(keys?: string[]): File[] {
if (keys) {
return keys.map((key) => this.filesMap.get(key)!);
}
return Array.from(this.filesMap.values());
}

getFilepaths(): string[] {
return Array.from(this.filesMap.keys());
}

getFailedFilepaths() {
return this.getFiles()
.filter((i) => i.result?.state === "fail")
.map((i) => i.filepath);
}

collectFiles(files: File[] = []) {
files.forEach((file) => {
this.filesMap.set(file.filepath, file);
this.updateId(file);
});
}

updateId(task: Task) {
if (this.idMap.get(task.id) === task) {
return;
}
this.idMap.set(task.id, task);
if (task.type === "suite") {
task.tasks.forEach((task) => {
this.updateId(task);
});
}
}

updateTasks(packs: TaskResultPack[]) {
for (const [id, result] of packs) {
if (this.idMap.has(id)) {
this.idMap.get(id)!.result = result;
}
}
}

updateUserLog(log: UserConsoleLog) {
const task = log.taskId && this.idMap.get(log.taskId);
if (task) {
if (!task.logs) {
task.logs = [];
}
task.logs.push(log);
}
}
}
export interface VitestClientOptions {
handlers?: Partial<WebSocketEvents>;
autoReconnect?: boolean;
reconnectInterval?: number;
reconnectTries?: number;
reactive?: <T>(v: T) => T;
ref?: <T>(v: T) => { value: T };
WebSocketConstructor?: typeof WebSocket;
}

export interface VitestClient {
ws: WebSocket;
state: StateManager;
rpc: BirpcReturn<WebSocketHandlers>;
waitForConnection(): Promise<void>;
reconnect(): Promise<void>;
}

export function createClient(url: string, options: VitestClientOptions = {}) {
const {
handlers = {},
autoReconnect = true,
reconnectInterval = 2000,
reconnectTries = 10,
reactive = (v) => v,
WebSocketConstructor = globalThis.WebSocket,
} = options;

let tries = reconnectTries;
const ctx = reactive({
ws: shallowReactive(new WebSocketConstructor(url)),
state: new StateManager(),
waitForConnection,
reconnect,
}) as VitestClient;

ctx.state.filesMap = reactive(ctx.state.filesMap);
ctx.state.idMap = reactive(ctx.state.idMap);

let onMessage: Function;
ctx.rpc = createBirpc<WebSocketHandlers, WebSocketEvents>(
{
onCollected(files) {
ctx.state.collectFiles(files);
handlers.onCollected?.(files);
},
onTaskUpdate(packs) {
ctx.state.updateTasks(packs);
handlers.onTaskUpdate?.(packs);
},
onUserConsoleLog(log) {
ctx.state.updateUserLog(log);
},
onFinished(files) {
handlers.onFinished?.(files);
},
},
{
post: (msg) => ctx.ws.send(msg),
on: (fn) => onMessage = fn,
serialize: stringify,
deserialize: parse,
},
);

let openPromise: Promise<void>;

function reconnect(reset = false) {
if (reset) {
tries = reconnectTries;
}
ctx.ws = shallowReactive(new WebSocketConstructor(url));
registerWS();
}

function registerWS() {
openPromise = new Promise((resolve) => {
ctx.ws.addEventListener("open", () => {
tries = reconnectTries;
resolve();
});
});
ctx.ws.addEventListener("message", (v) => {
onMessage(v.data);
});
ctx.ws.addEventListener("close", () => {
tries -= 1;
if (autoReconnect && tries > 0) {
setTimeout(reconnect, reconnectInterval);
}
});
}

registerWS();

function waitForConnection() {
return openPromise;
}

return ctx;
}

0 comments on commit 87071ee

Please sign in to comment.