Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
"vitest.nodeEnv": {
"ELECTRON_RUN_AS_NODE": "1"
},
"vitest.nodeExecutable": "node_modules/.bin/electron"
"vitest.nodeExecutable": "node_modules/electron/dist/electron"
}
16 changes: 9 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,20 @@
"type": "commonjs",
"main": "./dist/extension.js",
"scripts": {
"build": "concurrently -g -n webviews,extension \"pnpm build:webviews\" \"node esbuild.mjs\"",
"build:production": "NODE_ENV=production pnpm build",
"build": "concurrently -g -n webviews,extension,compile-tests:integration \"pnpm build:webviews\" \"node esbuild.mjs\" \"pnpm compile-tests:integration\"",
"build:production": "cross-env NODE_ENV=production pnpm build",
"build:webviews": "pnpm -r --filter \"./packages/*\" --parallel build",
"compile-tests:integration": "tsc -p test/integration --outDir out --noCheck",
"format": "prettier --write --cache --cache-strategy content .",
"format:check": "prettier --check --cache --cache-strategy content .",
"lint": "eslint --cache --cache-strategy content .",
"lint:fix": "pnpm lint --fix",
"package": "pnpm build:production && vsce package --no-dependencies",
"package:prerelease": "pnpm build:production && vsce package --pre-release --no-dependencies",
"test": "CI=true ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs",
"test:extension": "ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs --project extension",
"test:integration": "tsc -p test/integration --outDir out --noCheck && node esbuild.mjs && vscode-test",
"test:webview": "ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs --project webview",
"test": "cross-env CI=true ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs",
"test:extension": "cross-env ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs --project extension",
"test:integration": "pnpm compile-tests:integration && node esbuild.mjs && vscode-test",
"test:webview": "cross-env ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs --project webview",
"typecheck": "concurrently -g -n extension,tests,packages \"tsc --noEmit\" \"tsc --noEmit -p test\" \"pnpm typecheck:packages\"",
"typecheck:packages": "pnpm -r --filter \"./packages/*\" --parallel typecheck",
"watch": "concurrently -g -n extension,webviews \"pnpm watch:extension\" \"pnpm watch:webviews\"",
Expand Down Expand Up @@ -609,8 +610,9 @@
"bufferutil": "^4.1.0",
"coder": "catalog:",
"concurrently": "^9.2.1",
"cross-env": "^10.1.0",
"dayjs": "^1.11.20",
"electron": "39.8.1",
"electron": "39.8.5",
"esbuild": "^0.28.0",
"eslint": "^10.2.0",
"eslint-config-prettier": "^10.1.8",
Expand Down
44 changes: 31 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions test/unit/core/cliExec.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import fs from "fs/promises";
import os from "os";
import path from "path";
import { beforeAll, describe, expect, it } from "vitest";

import * as cliExec from "@/core/cliExec";
import { beforeAll, describe, expect, it, vi } from "vitest";

import { MockConfigurationProvider } from "../../mocks/testHelpers";
import { isWindows, writeExecutable } from "../../utils/platform";

import type { CliEnv } from "@/core/cliExec";

// Shim execFile so .js test scripts are run through node cross-platform.
vi.mock("node:child_process", async (importOriginal) => {
const { shimExecFile } = await import("../../utils/platform");
return shimExecFile(await importOriginal());
});

// Import after mock so the module picks up the shimmed execFile.
const cliExec = await import("@/core/cliExec");

describe("cliExec", () => {
const tmp = path.join(os.tmpdir(), "vscode-coder-tests-cliExec");

Expand Down
13 changes: 8 additions & 5 deletions test/unit/remote/sshProcess.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import find from "find-process";
import { vol } from "memfs";
import * as fsPromises from "node:fs/promises";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

import {
Expand Down Expand Up @@ -313,8 +314,10 @@ describe("SshProcessMonitor", () => {
});
const logPath = await waitForEvent(monitor.onLogFilePathChange);

expect(logPath).toBe("/proxy-logs/999.log");
expect(monitor.getLogFilePath()).toBe("/proxy-logs/999.log");
expect(logPath).toBe(path.join("/proxy-logs", "999.log"));
expect(monitor.getLogFilePath()).toBe(
path.join("/proxy-logs", "999.log"),
);
});

it("finds log file with prefix pattern", async () => {
Expand All @@ -330,7 +333,7 @@ describe("SshProcessMonitor", () => {
});
const logPath = await waitForEvent(monitor.onLogFilePathChange);

expect(logPath).toBe("/proxy-logs/coder-ssh-999.log");
expect(logPath).toBe(path.join("/proxy-logs", "coder-ssh-999.log"));
});

it("returns undefined when no proxyLogDir set", async () => {
Expand Down Expand Up @@ -373,7 +376,7 @@ describe("SshProcessMonitor", () => {
});
const logPath = await waitForEvent(monitor.onLogFilePathChange);

expect(logPath).toBe("/proxy-logs/2024-01-03-999.log");
expect(logPath).toBe(path.join("/proxy-logs", "2024-01-03-999.log"));
});

it("sorts log files using localeCompare for consistent cross-platform ordering", async () => {
Expand All @@ -395,7 +398,7 @@ describe("SshProcessMonitor", () => {

// With localeCompare: ["a", "Z"] -> reversed -> "Z" first
// With plain sort(): ["Z", "a"] -> reversed -> "a" first (WRONG)
expect(logPath).toBe("/proxy-logs/Z-999.log");
expect(logPath).toBe(path.join("/proxy-logs", "Z-999.log"));
});
});

Expand Down
83 changes: 81 additions & 2 deletions test/utils/platform.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { describe, expect, it } from "vitest";
import * as cp from "node:child_process";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { promisify } from "node:util";
import { beforeAll, describe, expect, it } from "vitest";

import {
expectPathsEqual,
exitCommand,
isWindows,
printCommand,
printEnvCommand,
isWindows,
shimExecFile,
writeExecutable,
} from "./platform";

describe("platform utils", () => {
Expand Down Expand Up @@ -83,4 +90,76 @@ describe("platform utils", () => {
},
);
});

describe("writeExecutable", () => {
const tmp = path.join(os.tmpdir(), "vscode-coder-tests-platform");

beforeAll(async () => {
await fs.rm(tmp, { recursive: true, force: true });
await fs.mkdir(tmp, { recursive: true });
});

it("writes a .js file and returns its path", async () => {
const result = await writeExecutable(tmp, "test-script", "// hello");
expect(result).toBe(path.join(tmp, "test-script.js"));
expect(await fs.readFile(result, "utf-8")).toBe("// hello");
});

it("overwrites existing files", async () => {
await writeExecutable(tmp, "overwrite", "first");
const result = await writeExecutable(tmp, "overwrite", "second");
expect(await fs.readFile(result, "utf-8")).toBe("second");
});
});

describe("shimExecFile", () => {
const tmp = path.join(os.tmpdir(), "vscode-coder-tests-shim");
const mod = shimExecFile(cp);
const shimmedExecFile = promisify(mod.execFile);

beforeAll(async () => {
await fs.rm(tmp, { recursive: true, force: true });
await fs.mkdir(tmp, { recursive: true });
});

it("runs .js files through node", async () => {
const script = await writeExecutable(
tmp,
"echo",
'process.stdout.write("ok");',
);
const { stdout } = await shimmedExecFile(script);
expect(stdout).toBe("ok");
});

it("passes args through to the script", async () => {
const script = await writeExecutable(
tmp,
"echo-args",
"process.stdout.write(process.argv.slice(2).join(','));",
);
const { stdout } = await shimmedExecFile(script, ["a", "b", "c"]);
expect(stdout).toBe("a,b,c");
});

it("does not rewrite non-.js files", async () => {
await expect(shimmedExecFile("/nonexistent/binary")).rejects.toThrow(
"ENOENT",
);
});

it("preserves the callback form", async () => {
const script = path.join(tmp, "echo.js");
const stdout = await new Promise<string>((resolve, reject) => {
mod.execFile(script, (err, out) =>
err ? reject(new Error(err.message)) : resolve(out),
);
});
expect(stdout).toBe("ok");
});

it("does not touch spawn", () => {
expect(mod.spawn).toBe(cp.spawn);
});
});
});
Loading