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
8 changes: 8 additions & 0 deletions .chronus/changes/fix_vscode_e2e_test-2025-8-15-10-18-14.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: internal
packages:
- typespec-vscode
---

Fix vscode e2e test when making a release pr
1 change: 1 addition & 0 deletions packages/typespec-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@
"package-vsix": "vsce package",
"deploy": "vsce publish",
"open-in-browser": "vscode-test-web --extensionDevelopmentPath=. .",
"test:e2e": "pnpm test:web && pnpm test:extension",
"test:web": "vscode-test-web --quality stable --extensionDevelopmentPath=. --headless --extensionTestsPath=dist/test/web/suite.js ./test/web/data",
"test:extension": "vitest run --root test/extension"
},
Expand Down
76 changes: 74 additions & 2 deletions packages/typespec-vscode/test/extension/common/common-steps.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { readdirSync } from "fs";
import { rm } from "fs/promises";
import fs from "node:fs";
import fs, { rmSync, writeFileSync } from "node:fs";
import path from "node:path";
import { Locator, Page } from "playwright";
import { CaseScreenshot, retry } from "./utils";
import { RunOptions, runOrExit } from "../../../../internal-build-utils/dist/src/index.js";
import { CaseScreenshot, npxCmd, repoRoot, retry, tempDir } from "./utils";

/**
* Waits for the specified text to appear on the page before proceeding.
Expand Down Expand Up @@ -136,3 +138,73 @@ export function restoreTspConfigFile(folderName: string, lines: string) {
const filePath = path.join(folderName, "tspconfig.yaml");
fs.writeFileSync(filePath, lines, "utf-8");
}

/**
* Pack those packages in the repoRoot needed for testing and prepare to be linked
* @returns packages path map
*/
export async function packPackages() {
await runOrExit("pnpm", ["-w", "pack:all"], { cwd: repoRoot, stdio: "ignore" });
const outputFolder = path.join(repoRoot, "/temp/artifacts");
const files = readdirSync(outputFolder);

function resolvePackage(start: string) {
const pkgName = files.find((x: string) => x.startsWith(start));
if (pkgName === undefined) {
throw new Error(`Cannot resolve package starting with "${start}"`);
}
return path.join(outputFolder, pkgName);
}

return {
"@typespec/compiler": resolvePackage("typespec-compiler-"),
"@typespec/openapi3": resolvePackage("typespec-openapi3-"),
"@typespec/http": resolvePackage("typespec-http-"),
"@typespec/http-client-js": resolvePackage("typespec-http-client-js-"),
};
}

/**
* Install those packages needed for testing in the EmitTypespecProject folder
* @param packages packages path map
*/
export async function packagesInstall(packages: { [x: string]: string }, testType: string) {
let testCurrentDir: string;
if (testType === "Emit") {
testCurrentDir = path.join(tempDir, "EmitTypespecProject");
const outputDir = path.join(testCurrentDir, "tsp-output");
rmSync(outputDir, { recursive: true, force: true });
} else if (testType === "Import") {
testCurrentDir = path.join(tempDir, "ImportTypespecProjectOpenApi3");
} else if (testType === "Preview") {
testCurrentDir = path.join(tempDir, "PreviewTypespecProject");
} else {
throw new Error(`Unknown testType: ${testType}`);
}
const packageJson = {
name: "@typespec/e2e-test-typespec-vscode",
dependencies: {
"@typespec/compiler": packages["@typespec/compiler"],
"@typespec/http": packages["@typespec/http"],
"@typespec/openapi3": packages["@typespec/openapi3"],
"@typespec/http-client-js": packages["@typespec/http-client-js"],
},
private: true,
};
writeFileSync(path.join(testCurrentDir, "package.json"), JSON.stringify(packageJson, null, 2));

await runTypeSpec(packages["@typespec/compiler"], ["install"], { cwd: testCurrentDir });
await runTypeSpec(packages["@typespec/http"], ["install"], { cwd: testCurrentDir });
await runTypeSpec(packages["@typespec/openapi3"], ["install"], { cwd: testCurrentDir });
await runTypeSpec(packages["@typespec/http-client-js"], ["install"], { cwd: testCurrentDir });
}

/**
* Run typespec with npx
* @param compilerTgz The path to the TypeSpec compiler package tarball.
* @param args The arguments to pass to the TypeSpec compiler.
* @param options Additional options for running the command.
*/
export async function runTypeSpec(compilerTgz: string, args: any, options: RunOptions | undefined) {
await runOrExit(npxCmd, ["-y", "-p", compilerTgz, "tsp", ...args], { ...options });
}
13 changes: 13 additions & 0 deletions packages/typespec-vscode/test/extension/common/emit-steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ export async function emitSelectType(page: Page, type: string, cs: CaseScreensho
"Emitting Server Stub from TypeSpec files. Supported languages are .NET, JavaScript.",
},
];
await retry(
page,
3,
async () => {
const selectTypeBox = await page.getByRole("textbox", {
name: "Select an emitter type - Emit",
});
return (await selectTypeBox.count()) > 0;
},
`Failed to find the language for code emitting.`,
2,
cs,
);
await retry(
page,
3,
Expand Down
2 changes: 2 additions & 0 deletions packages/typespec-vscode/test/extension/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { test as baseTest, inject } from "vitest";

const __dirname = import.meta.dirname;
export const projectRoot = path.resolve(__dirname, "../../../");
export const repoRoot = path.resolve(projectRoot, "../../");
export const tempDir = path.resolve(projectRoot, "./temp");
export const testfilesDir = path.resolve(projectRoot, "./test/scenarios");
export const imagesPath = path.resolve(tempDir, "./images-linux");
export const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx";

interface Context {
page: Page;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { beforeEach, describe } from "vitest";
import { contrastResult, preContrastResult, startWithCommandPalette } from "./common/common-steps";
import { inputProjectName, selectEmitters, selectTemplate } from "./common/create-steps";
import { mockShowOpenDialog } from "./common/mock-dialogs";
import { CaseScreenshot, test, testfilesDir } from "./common/utils";
import { CaseScreenshot, tempDir, test } from "./common/utils";

enum CreateProjectTriggerType {
Click = "RightClick",
Expand All @@ -21,7 +21,8 @@ type CreateConfigType = {
expectedResults: string[];
};

const CreateTypespecProjectFolderPath = path.resolve(testfilesDir, "CreateTypespecProject");
// Move to the temp directory to execute the test
const CreateTypespecProjectFolderPath = path.resolve(tempDir, "CreateTypespecProject");

const createCase = "CreateTypespecProject";
const templateName = "Generic Rest API";
Expand Down
48 changes: 21 additions & 27 deletions packages/typespec-vscode/test/extension/emit-typespec.test.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import { execSync } from "child_process";
import fs from "node:fs";
import path from "node:path";
import { beforeEach, describe } from "vitest";
import { beforeAll, beforeEach, describe } from "vitest";
import {
contrastResult,
packagesInstall,
packPackages,
preContrastResult,
readTspConfigFile,
restoreTspConfigFile,
startWithCommandPalette,
} from "./common/common-steps";
import { emiChooseEmitter, emitSelectLanguage, emitSelectType } from "./common/emit-steps";
import { CaseScreenshot, test, testfilesDir } from "./common/utils";
import { CaseScreenshot, tempDir, test, testfilesDir } from "./common/utils";

try {
execSync("pnpm install @typespec/http-client-csharp", { stdio: "inherit" });
execSync("pnpm install @typespec/http", { stdio: "inherit" });
} catch (e) {
process.exit(1);
}
// Test files are copied into the temporary directory before tests run
beforeAll(async () => {
const src = path.resolve(testfilesDir, "EmitTypespecProject");
const dest = path.resolve(tempDir, "EmitTypespecProject");
fs.cpSync(src, dest, { recursive: true });

const packages = await packPackages();
// Install those packages locally
await packagesInstall(packages, "Emit");
}, 300000);

enum EmitProjectTriggerType {
Command = "Command",
Expand All @@ -33,24 +38,24 @@ type EmitConfigType = {
expectedResults: string[];
};

const EmitTypespecProjectFolderPath = path.resolve(testfilesDir, "EmitTypespecProject");
const EmitTypespecProjectFolderPath = path.resolve(tempDir, "EmitTypespecProject");

const EmitCasesConfigList: EmitConfigType[] = [
{
caseName: "EmitTypespecProject ClientCode DotNet Trigger CommandPalette TspconfigHasEmit",
caseName: "EmitTypespecProject ClientCode Js Trigger CommandPalette TspconfigHasEmit",
selectType: "Client Code",
selectTypeLanguage: ".NET",
selectTypeLanguage: "JavaScript",
triggerType: EmitProjectTriggerType.Command,
TspConfigHasEmit: true,
expectedResults: ["http-client-csharp"],
expectedResults: ["http-client-js"],
},
{
caseName: "EmitTypespecProject ClientCode DotNet Trigger CommandPalette TspconfigNoEmit",
caseName: "EmitTypespecProject ClientCode Js Trigger CommandPalette TspconfigNoEmit",
selectType: "Client Code",
selectTypeLanguage: ".NET",
selectTypeLanguage: "JavaScript",
triggerType: EmitProjectTriggerType.Command,
TspConfigHasEmit: false,
expectedResults: ["http-client-csharp"],
expectedResults: ["http-client-js"],
},
];

Expand Down Expand Up @@ -100,16 +105,5 @@ describe.each(EmitCasesConfigList)("EmitTypespecProject", async (item) => {
const resultFilePath = path.resolve(workspacePath, "./tsp-output/@typespec");
await contrastResult(expectedResults, resultFilePath, cs);
app.close();

try {
execSync("git restore ./package.json", { stdio: "inherit" });
} catch (e) {
process.exit(1);
}
try {
execSync("git restore ../../pnpm-lock.yaml", { stdio: "inherit" });
} catch (e) {
process.exit(1);
}
});
});
51 changes: 24 additions & 27 deletions packages/typespec-vscode/test/extension/import-typespec.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import { execSync } from "child_process";
import fs from "node:fs";
import path from "node:path";
import { beforeEach, describe } from "vitest";
import { contrastResult, preContrastResult, startWithRightClick } from "./common/common-steps";
import { beforeAll, beforeEach, describe } from "vitest";
import {
contrastResult,
packagesInstall,
packPackages,
preContrastResult,
startWithRightClick,
} from "./common/common-steps";
import { mockShowOpenDialog } from "./common/mock-dialogs";
import { CaseScreenshot, test, testfilesDir } from "./common/utils";
import { CaseScreenshot, tempDir, test, testfilesDir } from "./common/utils";

try {
execSync("pnpm install @typespec/openapi3", { stdio: "inherit" });
execSync("pnpm install @typespec/http", { stdio: "inherit" });
} catch (e) {
process.exit(1);
}
// Test files are copied into the temporary directory before tests run
beforeAll(async () => {
const src = path.resolve(testfilesDir, "ImportTypespecProjectOpenApi3");
const dest = path.resolve(tempDir, "ImportTypespecProjectOpenApi3");
fs.cpSync(src, dest, { recursive: true });

const packages = await packPackages();
// Install those packages locally
await packagesInstall(packages, "Import");
}, 300000);

enum ImportProjectTriggerType {
CommandPalette = "CommandPalette",
Expand All @@ -26,9 +35,9 @@ type ImportConfigType = {
expectedResults: string[];
};

const ImportTypespecProjectFolderPath = path.resolve(testfilesDir, "ImportTypespecProjectOpenApi3");
const ImportTypespecProjectFolderPath = path.resolve(tempDir, "ImportTypespecProjectOpenApi3");
const ImportTypespecProjectEmptyFolderPath = path.resolve(
testfilesDir,
tempDir,
"ImportTypespecProjectOpenApi3/ImportTypespecProjectEmptyFolder",
);

Expand All @@ -38,7 +47,7 @@ ImportCasesConfigList.push({
caseName: "ImportTypespecProject Trigger RightClickOnFolder EmptyFolder",
triggerType: ImportProjectTriggerType.RightClickOnFolder,
selectFolderEmptyOrNonEmpty: "empty",
expectedResults: ["openapi.3.0.yaml", "ImportTypespecProjectEmptyFolder"],
expectedResults: ["main.tsp"],
});

beforeEach(() => {
Expand All @@ -49,9 +58,6 @@ beforeEach(() => {
for (const file of fs.readdirSync(importTypespec)) {
if (file === "openapi.3.0.yaml") {
hasOpenapi3File = true;
} else if (file !== "ImportTypespecProjectEmptyFolder") {
const filePath = path.resolve(importTypespec, file);
fs.rmSync(filePath, { recursive: true, force: true });
}
}
if (!hasOpenapi3File) {
Expand Down Expand Up @@ -91,17 +97,8 @@ describe.each(ImportCasesConfigList)("ImportTypespecFromOpenApi3", async (item)
cs,
app,
);
await contrastResult(expectedResults, workspacePath, cs);
const resultFilePath = path.resolve(workspacePath, "./ImportTypespecProjectEmptyFolder");
await contrastResult(expectedResults, resultFilePath, cs);
app.close();
try {
execSync("git restore ./package.json", { stdio: "inherit" });
} catch (e) {
process.exit(1);
}
try {
execSync("git restore ../../pnpm-lock.yaml", { stdio: "inherit" });
} catch (e) {
process.exit(1);
}
});
});
38 changes: 14 additions & 24 deletions packages/typespec-vscode/test/extension/preview-typespec.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { execSync } from "child_process";
import { rm } from "fs/promises";
import fs from "node:fs";
import path from "node:path";
import { beforeEach, describe } from "vitest";
import { startWithCommandPalette } from "./common/common-steps";
import { CaseScreenshot, retry, test, testfilesDir } from "./common/utils";
import { beforeAll, beforeEach, describe } from "vitest";
import { packagesInstall, packPackages, startWithCommandPalette } from "./common/common-steps";
import { CaseScreenshot, retry, tempDir, test, testfilesDir } from "./common/utils";

try {
execSync("pnpm install @typespec/http", { stdio: "inherit" });
execSync("pnpm install @typespec/openapi3", { stdio: "inherit" });
} catch (e) {
process.exit(1);
}
// Test files are copied into the temporary directory before tests run
beforeAll(async () => {
const src = path.resolve(testfilesDir, "PreviewTypespecProject");
const dest = path.resolve(tempDir, "PreviewTypespecProject");
fs.cpSync(src, dest, { recursive: true });

const packages = await packPackages();
// Install those packages locally
await packagesInstall(packages, "Preview");
}, 300000);

export enum PreviewProjectTriggerType {
Command = "CommandPalette",
Expand All @@ -23,7 +26,7 @@ type PreviewConfigType = {
triggerType: PreviewProjectTriggerType;
};

const PreviewTypespecProjectFolderPath = path.resolve(testfilesDir, "PreviewTypespecProject");
const PreviewTypespecProjectFolderPath = path.resolve(tempDir, "PreviewTypespecProject");

const PreviewCaseName = `PreviewTypespecProject`;
const PreviewCasesConfigList: PreviewConfigType[] = [];
Expand All @@ -40,9 +43,6 @@ beforeEach(() => {
for (const file of fs.readdirSync(previewTypespec)) {
if (file === "main.tsp") {
hasMainTsp = true;
} else {
const filePath = path.resolve(previewTypespec, file);
fs.rmSync(filePath, { recursive: true, force: true });
}
}
if (!hasMainTsp) {
Expand Down Expand Up @@ -81,15 +81,5 @@ describe.each(PreviewCasesConfigList)("PreviewAPIDocument", async (item) => {
);
await rm(cs.caseDir, { recursive: true });
app.close();
try {
execSync("git restore ./package.json", { stdio: "inherit" });
} catch (e) {
process.exit(1);
}
try {
execSync("git restore ../../pnpm-lock.yaml", { stdio: "inherit" });
} catch (e) {
process.exit(1);
}
});
});
Loading
Loading