- Notifications
You must be signed in to change notification settings - Fork35
Add package scripts and cli library, enable integration testing#536
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes from9 commits
7e1bce9c693a46240b6490e58a31872b7e801c2d808ddbf269b74df4adec211d9b543aa7afdd63097d8fa2d2bc832dfda412b01243ee92feFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| vitest.config.ts |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -18,7 +18,7 @@ jobs: | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "22" | ||
| - run: yarn | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { defineConfig } from "@vscode/test-cli"; | ||
| export default defineConfig({ | ||
| files: "out/test/**/*.test.js", | ||
| extensionDevelopmentPath: ".", | ||
| extensionTestsPath: "./out/test", | ||
| launchArgs: ["--enable-proposed-api", "coder.coder-remote"], | ||
| mocha: { | ||
| ui: "tdd", | ||
| timeout: 20000, | ||
| }, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -12,4 +12,4 @@ node_modules/** | ||
| **/.editorconfig | ||
| **/*.map | ||
| **/*.ts | ||
| *.gif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -278,8 +278,11 @@ | ||
| "package:prerelease":"npx vsce package --pre-release", | ||
| "lint":"eslint . --ext ts,md", | ||
| "lint:fix":"yarn lint --fix", | ||
| "test":"vitest", | ||
| "test:ci":"CI=true yarn test", | ||
| "test:integration":"vscode-test", | ||
| "pretest":"yarn run compile-tests && yarn run build && yarn run lint", | ||
| "compile-tests":"tsc -p . --outDir out" | ||
jaggederest marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| }, | ||
| "devDependencies": { | ||
| "@types/eventsource":"^3.0.0", | ||
| @@ -311,7 +314,8 @@ | ||
| "vitest":"^0.34.6", | ||
| "vscode-test":"^1.5.0", | ||
| "webpack":"^5.99.6", | ||
| "webpack-cli":"^5.1.4", | ||
| "@vscode/test-cli":"^0.0.10" | ||
jaggederest marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| }, | ||
| "dependencies": { | ||
| "axios":"1.8.4", | ||
| @@ -326,7 +330,7 @@ | ||
| "semver":"^7.7.1", | ||
| "ua-parser-js":"1.0.40", | ||
| "ws":"^8.18.2", | ||
| "zod":"^3.25.65" | ||
| }, | ||
| "resolutions": { | ||
| "semver":"7.7.1", | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -21,25 +21,36 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> { | ||
| // | ||
| // Cursor and VSCode are covered by ms remote, and the only other is windsurf for now | ||
| // Means that vscodium is not supported by this for now | ||
| const isTestMode = | ||
| process.env.NODE_ENV === "test" || | ||
| ctx.extensionMode === vscode.ExtensionMode.Test; | ||
| const remoteSSHExtension = | ||
| vscode.extensions.getExtension("jeanp413.open-remote-ssh") || | ||
| vscode.extensions.getExtension("codeium.windsurf-remote-openssh") || | ||
| vscode.extensions.getExtension("anysphere.remote-ssh") || | ||
| vscode.extensions.getExtension("ms-vscode-remote.remote-ssh"); | ||
| let vscodeProposed: typeof vscode = vscode; | ||
| if (!remoteSSHExtension) { | ||
| if (!isTestMode) { | ||
| vscode.window.showErrorMessage( | ||
| "Remote SSH extension not found, cannot activate Coder extension", | ||
| ); | ||
| throw new Error("Remote SSH extension not found"); | ||
| } | ||
| // In test mode, use regular vscode API | ||
jaggederest marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| } else { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| vscodeProposed = (module as any)._load( | ||
| "vscode", | ||
| { | ||
| filename: remoteSSHExtension.extensionPath, | ||
| }, | ||
| false, | ||
| ); | ||
Comment on lines +40 to 47 CopilotAI | ||
| } | ||
| const output = vscode.window.createOutputChannel("Coder"); | ||
| const storage = new Storage( | ||
| @@ -278,7 +289,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> { | ||
| // Since the "onResolveRemoteAuthority:ssh-remote" activation event exists | ||
| // in package.json we're able to perform actions before the authority is | ||
| // resolved by the remote SSH extension. | ||
| if (!isTestMode &&vscodeProposed.env.remoteAuthority) { | ||
| const remote = new Remote( | ||
| vscodeProposed, | ||
| storage, | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -6,7 +6,9 @@ import * as fs from "fs/promises"; | ||
| import * as jsonc from "jsonc-parser"; | ||
| import * as os from "os"; | ||
| import * as path from "path"; | ||
| // Dynamic import for ESM module | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| let prettyBytes: any; | ||
jaggederest marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| import * as semver from "semver"; | ||
| import * as vscode from "vscode"; | ||
| import { | ||
| @@ -841,7 +843,7 @@ export class Remote { | ||
| `${sshPid}.json`, | ||
| ); | ||
| const updateStatus =async(network: { | ||
jaggederest marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| p2p: boolean; | ||
| latency: number; | ||
| preferred_derp: string; | ||
| @@ -850,6 +852,10 @@ export class Remote { | ||
| download_bytes_sec: number; | ||
| using_coder_connect: boolean; | ||
| }) => { | ||
| // Load ESM module if not already loaded | ||
| if (!prettyBytes) { | ||
| prettyBytes = (await import("pretty-bytes")).default; | ||
jaggederest marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| } | ||
| let statusText = "$(globe) "; | ||
| // Coder Connect doesn't populate any other stats | ||
| @@ -910,9 +916,9 @@ export class Remote { | ||
| .then((content) => { | ||
| return JSON.parse(content); | ||
| }) | ||
| .then(async(parsed) => { | ||
| try { | ||
| awaitupdateStatus(parsed); | ||
jaggederest marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| } catch (ex) { | ||
| // Ignore | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -3,7 +3,9 @@ import { createWriteStream } from "fs"; | ||
| import fs from "fs/promises"; | ||
| import { IncomingMessage } from "http"; | ||
| import path from "path"; | ||
| // Dynamic import for ESM module | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
jaggederest marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| let prettyBytes: any; | ||
| import * as vscode from "vscode"; | ||
| import { errToStr } from "./api-helper"; | ||
| import * as cli from "./cliManager"; | ||
| @@ -122,6 +124,10 @@ export class Storage { | ||
| * downloads being disabled. | ||
| */ | ||
| public async fetchBinary(restClient: Api, label: string): Promise<string> { | ||
| // Load ESM module if not already loaded | ||
| if (!prettyBytes) { | ||
| prettyBytes = (await import("pretty-bytes")).default; | ||
| } | ||
jaggederest marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| const baseUrl = restClient.getAxiosInstance().defaults.baseURL; | ||
| // Settings can be undefined when set to their defaults (true in this case), | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import*asassertfrom"assert"; | ||
| import*asvscodefrom"vscode"; | ||
| suite("Extension Test Suite",()=>{ | ||
jaggederest marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| vscode.window.showInformationMessage("Start all tests."); | ||
| test("Extension should be present",()=>{ | ||
| assert.ok(vscode.extensions.getExtension("coder.coder-remote")); | ||
| }); | ||
| test("Extension should activate",async()=>{ | ||
| constextension=vscode.extensions.getExtension("coder.coder-remote"); | ||
| assert.ok(extension); | ||
| if(!extension.isActive){ | ||
| awaitextension.activate(); | ||
| } | ||
| assert.ok(extension.isActive); | ||
| }); | ||
| test("Extension should export activate function",async()=>{ | ||
| constextension=vscode.extensions.getExtension("coder.coder-remote"); | ||
| assert.ok(extension); | ||
| awaitextension.activate(); | ||
| // The extension doesn't export anything, which is fine | ||
| // The test was expecting exports.activate but the extension | ||
| // itself is the activate function | ||
| assert.ok(extension.isActive); | ||
| }); | ||
| test("Commands should be registered",async()=>{ | ||
| constextension=vscode.extensions.getExtension("coder.coder-remote"); | ||
| assert.ok(extension); | ||
| if(!extension.isActive){ | ||
| awaitextension.activate(); | ||
| } | ||
| // Give a small delay for commands to register | ||
| awaitnewPromise((resolve)=>setTimeout(resolve,100)); | ||
| constcommands=awaitvscode.commands.getCommands(true); | ||
| constcoderCommands=commands.filter((cmd)=>cmd.startsWith("coder.")); | ||
| assert.ok( | ||
| coderCommands.length>0, | ||
| "Should have registered Coder commands", | ||
| ); | ||
| assert.ok( | ||
| coderCommands.includes("coder.login"), | ||
| "Should have coder.login command", | ||
| ); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,13 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "module": "Node16", | ||
| "target": "ES2022", | ||
jaggederest marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| "outDir": "out", | ||
| // "dom" is required for importing the API from coder/coder. | ||
| "lib": ["ES2022", "dom"], | ||
| "sourceMap": true, | ||
| "strict": true, | ||
| "skipLibCheck": true | ||
| }, | ||
| "exclude": ["vitest.config.ts"] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { defineConfig } from "vitest/config"; | ||
| export default defineConfig({ | ||
| test: { | ||
| include: ["src/**/*.test.ts"], | ||
| exclude: [ | ||
| "**/node_modules/**", | ||
| "**/dist/**", | ||
| "**/build/**", | ||
| "**/out/**", | ||
| "**/src/test/**", | ||
| "src/test/**", | ||
| "./src/test/**", | ||
| ], | ||
| environment: "node", | ||
| }, | ||
| }); |
Uh oh!
There was an error while loading.Please reload this page.