forked from microsoft/typespec
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add e2e test for the CLI (microsoft#2878)
fix microsoft#489 e2e test were also not running at all and the `emitter-ts` template was failing due to importing `vitest` instead of `node:test`
- Loading branch information
1 parent
afd3772
commit 639d899
Showing
17 changed files
with
252 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/compiler" | ||
--- | ||
|
||
Add e2e test for the CLI |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
packages/compiler/templates/__snapshots__/emitter-ts/test/hello.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import { ChildProcess, SpawnOptions, spawn } from "child_process"; | ||
import { access, readFile, rm } from "fs/promises"; | ||
import { beforeEach, describe, expect, it } from "vitest"; | ||
import { resolvePath } from "../../../src/index.js"; | ||
import { findTestPackageRoot } from "../../../src/testing/test-utils.js"; | ||
|
||
const pkgRoot = await findTestPackageRoot(import.meta.url); | ||
const scenarioRoot = resolvePath(pkgRoot, "test/e2e/cli/scenarios"); | ||
|
||
function getScenarioDir(name: string) { | ||
return resolvePath(scenarioRoot, name); | ||
} | ||
interface ExecCliOptions { | ||
cwd?: string; | ||
} | ||
|
||
async function execCli(args: string[], { cwd }: ExecCliOptions) { | ||
const node = process.platform === "win32" ? "node.exe" : "node"; | ||
return execAsync(node, [resolvePath(pkgRoot, "entrypoints/cli.js"), ...args], { cwd }); | ||
} | ||
async function execCliSuccess(args: string[], { cwd }: ExecCliOptions) { | ||
const result = await execCli(args, { cwd }); | ||
if (result.exitCode !== 0) { | ||
throw new Error(`Failed to execute cli: ${result.stdio}`); | ||
} | ||
|
||
return result; | ||
} | ||
async function execCliFail(args: string[], { cwd }: ExecCliOptions) { | ||
const result = await execCli(args, { cwd }); | ||
if (result.exitCode === 0) { | ||
throw new Error(`Cli succeeded but expected failure: ${result.stdio}`); | ||
} | ||
return result; | ||
} | ||
|
||
export interface ExecResult { | ||
exitCode: number; | ||
stdout: string; | ||
stderr: string; | ||
stdio: string; | ||
proc: ChildProcess; | ||
} | ||
export async function execAsync( | ||
command: string, | ||
args: string[], | ||
options: SpawnOptions | ||
): Promise<ExecResult> { | ||
const child = spawn(command, args, options); | ||
|
||
return new Promise((resolve, reject) => { | ||
child.on("error", (error) => { | ||
reject(error); | ||
}); | ||
const stdio: Buffer[] = []; | ||
const stdout: Buffer[] = []; | ||
const stderr: Buffer[] = []; | ||
child.stdout?.on("data", (data) => { | ||
stdout.push(data); | ||
stdio.push(data); | ||
}); | ||
child.stderr?.on("data", (data) => { | ||
stderr.push(data); | ||
stdio.push(data); | ||
}); | ||
|
||
child.on("exit", (exitCode) => { | ||
resolve({ | ||
exitCode: exitCode ?? -1, | ||
stdout: Buffer.concat(stdout).toString(), | ||
stderr: Buffer.concat(stderr).toString(), | ||
stdio: Buffer.concat(stdio).toString(), | ||
proc: child, | ||
}); | ||
}); | ||
}); | ||
} | ||
|
||
async function cleanOutputDir(scenarioName: string) { | ||
const dir = resolvePath(getScenarioDir(scenarioName), "tsp-output"); | ||
await rm(dir, { recursive: true, force: true }); | ||
} | ||
describe("cli", () => { | ||
it("shows help", async () => { | ||
const { stdout } = await execCliSuccess(["--help"], { | ||
cwd: getScenarioDir("simple"), | ||
}); | ||
expect(stdout).toContain("tsp <command>"); | ||
expect(stdout).toContain("tsp compile <path> Compile TypeSpec source."); | ||
expect(stdout).toContain("tsp format <include...> Format given list of TypeSpec files."); | ||
}); | ||
|
||
describe("compiling spec with warning", () => { | ||
it("logs warning and succeed", async () => { | ||
const { stdout } = await execCliSuccess(["compile", ".", "--pretty", "false"], { | ||
cwd: getScenarioDir("warn"), | ||
}); | ||
|
||
// eslint-disable-next-line no-console | ||
console.log("Stdout", stdout); | ||
expect(stdout).toContain("main.tsp:5:8 - warning deprecated: Deprecated: Deprecated"); | ||
expect(stdout).toContain("Found 1 warning."); | ||
}); | ||
|
||
it("logs warning as error(and fail) when using --warn-as-error", async () => { | ||
const { stdout } = await execCliFail( | ||
["compile", ".", "--warn-as-error", "--pretty", "false"], | ||
{ | ||
cwd: getScenarioDir("warn"), | ||
} | ||
); | ||
// eslint-disable-next-line no-console | ||
console.log("Stdout", stdout); | ||
expect(stdout).toContain("main.tsp:5:8 - error deprecated: Deprecated: Deprecated"); | ||
expect(stdout).toContain("Found 1 error."); | ||
}); | ||
}); | ||
|
||
describe("compiling with an emitter", () => { | ||
beforeEach(async () => { | ||
await cleanOutputDir("with-emitter"); | ||
}); | ||
|
||
it("emits output", async () => { | ||
const { stdout } = await execCliSuccess(["compile", ".", "--emit", "./emitter.js"], { | ||
cwd: getScenarioDir("with-emitter"), | ||
}); | ||
expect(stdout).toContain("Compilation completed successfully."); | ||
const file = await readFile( | ||
resolvePath(getScenarioDir("with-emitter"), "tsp-output/out.txt") | ||
); | ||
expect(file.toString()).toEqual("Hello, world!"); | ||
}); | ||
|
||
it("doesn't emit output when --noEmit is set", async () => { | ||
const { stdout } = await execCliSuccess( | ||
["compile", ".", "--emit", "./emitter.js", "--no-emit"], | ||
{ | ||
cwd: getScenarioDir("with-emitter"), | ||
} | ||
); | ||
expect(stdout).toContain("Compilation completed successfully."); | ||
await expect(() => | ||
access(resolvePath(getScenarioDir("with-emitter"), "tsp-output/out.txt")) | ||
).rejects.toEqual(expect.any(Error)); | ||
}); | ||
}); | ||
|
||
describe("compiling with no emitter", () => { | ||
it("logs warnings", async () => { | ||
const { stdout } = await execCliSuccess(["compile", "."], { | ||
cwd: getScenarioDir("simple"), | ||
}); | ||
expect(stdout).toContain( | ||
"No emitter was configured, no output was generated. Use `--emit <emitterName>` to pick emitter or specify it in the TypeSpec config." | ||
); | ||
}); | ||
|
||
it("doesn't log warning when --noEmit is set", async () => { | ||
const { stdout } = await execCliSuccess(["compile", ".", "--no-emit"], { | ||
cwd: getScenarioDir("simple"), | ||
}); | ||
expect(stdout).not.toContain( | ||
"No emitter was configured, no output was generated. Use `--emit <emitterName>` to pick emitter or specify it in the TypeSpec config." | ||
); | ||
}); | ||
}); | ||
|
||
it("can provide emitter options", async () => { | ||
const { stdout } = await execCliSuccess( | ||
["compile", ".", "--emit", "./emitter.js", "--option", "test-emitter.text=foo"], | ||
{ | ||
cwd: getScenarioDir("with-emitter"), | ||
} | ||
); | ||
expect(stdout).toContain("Compilation completed successfully."); | ||
const file = await readFile(resolvePath(getScenarioDir("with-emitter"), "tsp-output/out.txt")); | ||
expect(file.toString()).toEqual("foo"); | ||
}); | ||
|
||
it("set config parmaeter with --arg", async () => { | ||
await cleanOutputDir("with-config"); | ||
|
||
const { stdout } = await execCliSuccess( | ||
["compile", ".", "--emit", "./emitter.js", "--arg", "custom-dir=custom-dir-name"], | ||
{ | ||
cwd: getScenarioDir("with-config"), | ||
} | ||
); | ||
expect(stdout).toContain("Compilation completed successfully."); | ||
await access(resolvePath(getScenarioDir("with-config"), "tsp-output/custom-dir-name/out.txt")); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
model Foo {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#deprecated "Deprecated" | ||
model Foo {} | ||
|
||
model Bar { | ||
foo: Foo; | ||
} |
6 changes: 6 additions & 0 deletions
6
packages/compiler/test/e2e/cli/scenarios/with-config/emitter.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { mkdir, writeFile } from "fs/promises"; | ||
|
||
export async function $onEmit(context) { | ||
await mkdir(context.program.compilerOptions.outputDir, { recursive: true }); | ||
await writeFile(context.program.compilerOptions.outputDir + "/out.txt", ""); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
model Foo {} |
8 changes: 8 additions & 0 deletions
8
packages/compiler/test/e2e/cli/scenarios/with-config/tspconfig.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
parameters: | ||
custom-dir: | ||
default: default-value | ||
|
||
emit: | ||
- ./emitter.js | ||
|
||
output-dir: "{project-root}/tsp-output/{custom-dir}" |
15 changes: 15 additions & 0 deletions
15
packages/compiler/test/e2e/cli/scenarios/with-emitter/emitter.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { mkdir, writeFile } from "fs/promises"; | ||
|
||
export async function $onEmit(context) { | ||
if (!context.program.compilerOptions.noEmit) { | ||
await mkdir(context.program.compilerOptions.outputDir, { recursive: true }); | ||
await writeFile( | ||
context.program.compilerOptions.outputDir + "/out.txt", | ||
context.options["text"] ?? "Hello, world!" | ||
); | ||
} | ||
} | ||
|
||
export const $lib = { | ||
name: "test-emitter", | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
model Foo {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters