Migrate test framework to vitest (#2769)

Get rid of mocha and upgrade to vitest which is a more modern
alternative providing, watch, direct typescript compilation out of the
box, expect library and more.

Advantage over mocha:
- Much better cli
  -  watch mode
  - better diff
- Better extension:
  -  tree organization for files too (not everything flattened)
- update in real time the test(no more need to refresh manually to
discover where are the tests)
  - just a little buggy
- Compiles typescript directly
- provides more expectation apis(like jest)

Cons over mocha: 
- Slower(about 2x) but that means we don't need to build the test as
part of build which would speed up that part(not as much as is lost)

Todo: 
- typespec-azure migration
This commit is contained in:
Timothee Guerin 2024-01-02 11:40:29 -08:00 коммит произвёл GitHub
Родитель da99aa955b
Коммит 9c7bf80187
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
317 изменённых файлов: 3924 добавлений и 4486 удалений

3
.vscode/extensions.json поставляемый
Просмотреть файл

@ -2,6 +2,7 @@
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"streetsidesoftware.code-spell-checker"
"streetsidesoftware.code-spell-checker",
"ZixuanChen.vitest-explorer"
]
}

20
.vscode/launch.json поставляемый
Просмотреть файл

@ -20,26 +20,6 @@
"order": 999
}
},
{
"type": "node",
"request": "attach",
"name": "Tests",
"port": 9229,
"smartStep": true,
"sourceMaps": true,
"continueOnAttach": true,
"skipFiles": [
"<node_internals>/**/*.js",
"**/.vscode/extensions/hbenl.vscode-mocha-test-adapter-*/**.js"
],
"outFiles": [
"${workspaceFolder}/packages/*/dist/**/*.js",
"${workspaceFolder}/packages/*/dist-dev/**/*.js"
],
"presentation": {
"hidden": true
}
},
{
"name": "Attach to Language Server",
"type": "node",

19
.vscode/settings.json поставляемый
Просмотреть файл

@ -54,23 +54,8 @@
},
"typescript.tsdk": "./packages/compiler/node_modules/typescript/lib",
"git.ignoreLimitWarning": true,
"testExplorer.useNativeTesting": true,
"mochaExplorer.parallel": false,
"mochaExplorer.files": [
"./packages/*/dist/test/**/*.test.js",
"./packages/*/dist/test/**/*.e2e.js",
"./packages/*/dist-dev/test/**/*.test.js",
"./packages/*/test/**/*.test.js"
],
"mochaExplorer.ignore": "./packages/*/dist/test/manual/**/*.js",
"mochaExplorer.mochaPath": "./packages/compiler/node_modules/mocha",
"mochaExplorer.timeout": 500000,
"mochaExplorer.require": "source-map-support/register",
"mochaExplorer.debuggerConfig": "Tests",
"mochaExplorer.env": {
"TYPESPEC_VERBOSE_TEST_OUTPUT": "true",
"NODE_OPTIONS": "--stack-trace-limit=50"
},
"vitest.enable": true,
"vitest.commandLine": "node ./packages/compiler/node_modules/vitest/vitest.mjs",
"prettier.prettierPath": "./packages/compiler/node_modules/prettier/index.cjs",
"prettier.documentSelectors": ["**/*.tsp"],
"testExplorer.errorDecoration": false,

Просмотреть файл

@ -154,8 +154,8 @@ PR validation will ensure that reference docs are up to date.
## Recommended extensions
1. [Mocha Test Explorer](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-mocha-test-adapter):
Run tests from the IDE.
1. [Vitest Test Explorer](https://marketplace.visualstudio.com/items?itemName=ZixuanChen.vitest-explorer):
Run tests from the IDE. (Version `0.2.43` is bugged on OSX, use `0.2.42` instead)
2. [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode):
Automatically keep code formatted correctly on save.
3. [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint):
@ -191,17 +191,13 @@ Terminal pane will have three parallel watch tasks running:
## Testing
With [Mocha Test
Explorer](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-mocha-test-adapter)
installed, click on its icon in the sidebar, then click on the play
button at the top or on any individual test or test group to run just
one test or just one group. You can also click on the bug icon next to
an individual test to debug it.
```bash
# Run all the tests
rush test
You can see additional information logged by each test using
`logVerboseTestOutput` by clicking on the test and looking at the
output pane. Unlike the command line, no environment variable is
needed.
# Run in a specific package tests in watch mode
npm run test:watch
```
## Debugging

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/bundler",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/bundler"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/compiler",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/compiler"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/eslint-config-typespec",
"comment": "Migrate test rules to vitest",
"type": "none"
}
],
"packageName": "@typespec/eslint-config-typespec"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/eslint-plugin",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/eslint-plugin"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/html-program-viewer",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/html-program-viewer"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/http",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/http"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/internal-build-utils",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/internal-build-utils"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/json-schema",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/json-schema"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/library-linter",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/library-linter"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/openapi",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/openapi"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/openapi3",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/openapi3"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/playground",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/playground"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/prettier-plugin-typespec",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/prettier-plugin-typespec"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/protobuf",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/protobuf"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/rest",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/rest"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/versioning",
"comment": "",
"type": "none"
}
],
"packageName": "@typespec/versioning"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "tmlanguage-generator",
"comment": "",
"type": "none"
}
],
"packageName": "tmlanguage-generator"
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "typespec-vscode",
"comment": "",
"type": "none"
}
],
"packageName": "typespec-vscode"
}

6654
common/config/rush/pnpm-lock.yaml сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -87,6 +87,7 @@ ignorePaths:
- "**/dist-dev/**"
- "**/.docusaurus/**"
- "**/CHANGELOG.md"
- "**/coverage/**"
- packages/website/build/**
- common/scripts/*
enableFiletypes:

Просмотреть файл

@ -139,7 +139,7 @@ Alternatively, you can add these as scripts in your `package.json` to make them
"clean": "rimraf ./dist ./temp",
"build": "tsc -p .",
"watch": "tsc -p . --watch",
"test": "mocha"
"test": "node --test ./dist/test"
}
```
@ -201,27 +201,31 @@ TypeSpec libraries are defined using `peerDependencies` so we don't end-up with
## 4. Testing your TypeSpec library
TypeSpec provides a testing framework to help testing libraries. Examples here are shown using `mocha` but any other JS test framework can be used.
TypeSpec provides a testing framework to help testing libraries. Examples here are shown using node built-in test framework(Available node 20+) but any other JS test framework can be used that will provide more advanced features like vitest which is used in this project.
### a. Add devDependencies
Verify that you have the following in your `package.json`:
```
```json
"devDependencies": {
"@types/node": "~18.11.9",
"@types/mocha": "~10.0.1",
"mocha": "~10.2.0",
"source-map-support": "^0.5.21"
}
```
Also add a `.mocharc.yaml` file at the root of your project.
Also add a `vitest.config.ts` file at the root of your project.
```yaml
timeout: 5000
require: source-map-support/register
spec: "dist/test/**/*.test.js"
```ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "node",
// testTimeout: 10000, // Uncomment to increase the default timeout
isolate: false, // Your test shouldn't have side effects to this will improve performance.
},
});
```
### b. Define the testing library
@ -238,7 +242,7 @@ import { fileURLToPath } from "url";
export const MyTestLibrary = createTestLibrary({
name: "<name-of-npm-pkg>",
// Set this to the absolute path to the root of the package. (e.g. in this case this file would be compiled to ./dist/src/testing/index.js)
packageRoot: resolvePath(fileURLToPath(import.meta.url), "../../../../"),
packageRoot: await findTestPackageRoot(import.meta.url),
});
```
@ -288,10 +292,22 @@ export async function createMyTestRunner() {
### d. Write tests
After setting up that infrastructure you can start writing tests. For tests to be recognized by mocha the file names must follow the following format: `<name>.test.ts`
After setting up that infrastructure you can start writing tests. By default Node.js will run all files matching these patterns:
```
**/*.test.?(c|m)js
**/*-test.?(c|m)js
**/*_test.?(c|m)js
**/test-*.?(c|m)js
**/test.?(c|m)js
**/test/**/*.?(c|m)js
```
[See nodejs doc](https://nodejs.org/api/test.html)
```ts
import { createMyTestRunner } from "./test-host.js";
import { describe, beforeEach, it } from "node:test";
describe("my library", () => {
let runner: BasicTestRunner;
@ -336,9 +352,9 @@ Foo; // type of: model Foo {}
CustomName; // type of : Bar.name
```
#### f. Install the mocha test explorer for VSCode (optional)
#### f. Install vscode extension for the test framework
If you are using VSCode, you can install the [mocha test explorer](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-mocha-test-adapter) to run your tests from the editor. This will also allow you easily debug into your tests.
If you are using VSCode, you can install the [Node test runner](https://marketplace.visualstudio.com/items?itemName=connor4312.nodejs-testing) to run your tests from the editor. This will also allow you easily debug into your tests.
You should now be able to discover, run and debug into your tests from the test explorer.

Просмотреть файл

@ -8,7 +8,7 @@ mkdirSync(rootCoverageTmp, { recursive: true });
// Copy coverage files from each project to common folder
forEachProject((name, location, project) => {
const coverageTmp = join(location, "coverage", "tmp");
const coverageTmp = join(location, "coverage", ".tmp");
if (existsSync(coverageTmp)) {
const files = readdirSync(coverageTmp);
for (const file of files) {

Просмотреть файл

@ -1,2 +1,2 @@
import { repoRoot, run, tsc } from "./helpers.js";
run(tsc, ["--build", "--watch"], { cwd: repoRoot, sync: false });
run(tsc, ["--build", "./tsconfig.ws.json", "--watch"], { cwd: repoRoot, sync: false });

Просмотреть файл

@ -1,3 +0,0 @@
{
"reporter": ["cobertura", "json", "text"]
}

Просмотреть файл

@ -1,7 +0,0 @@
timeout: 5000
require: source-map-support/register
spec: "dist/test/**/*.test.js"
ignore: "dist/test/manual/**/*.js"
# Config for https://www.npmjs.com/package/mocha-multi-reporters
reporterOptions: "configFile=mocha.reporter.config.json"

Просмотреть файл

@ -1,4 +0,0 @@
{
"reporterEnabled": "spec, mocha-junit-reporter",
"maxDiffSize": 16384
}

Просмотреть файл

@ -28,8 +28,9 @@
"clean": "rimraf ./dist ./temp",
"build": "tsc -p .",
"watch": "tsc -p . --watch",
"test": "mocha",
"test-official": "c8 mocha --forbid-only --reporter mocha-multi-reporters",
"test": "vitest run",
"test:watch": "vitest -w",
"test-official": "vitest run --coverage --reporter=junit --reporter=default --no-file-parallelism",
"lint": "eslint . --ext .ts --max-warnings=0",
"lint:fix": "eslint . --fix --ext .ts"
},
@ -42,15 +43,13 @@
"@typespec/compiler": "workspace:~0.51.0"
},
"devDependencies": {
"@types/mocha": "~10.0.6",
"@types/node": "~18.11.9",
"@typespec/compiler": "workspace:~0.51.0",
"@typespec/eslint-config-typespec": "workspace:~0.51.0",
"@typespec/eslint-plugin": "workspace:~0.51.0",
"eslint": "^8.55.0",
"mocha": "~10.2.0",
"mocha-junit-reporter": "~2.2.1",
"mocha-multi-reporters": "~1.5.1",
"vitest": "^1.1.0",
"@vitest/coverage-v8": "^1.1.0",
"c8": "~8.0.1",
"rimraf": "~5.0.1",
"typescript": "~5.3.3"

Просмотреть файл

@ -3,6 +3,7 @@ import {
createLinterRuleTester,
createTestRunner,
} from "@typespec/compiler/testing";
import { beforeEach, describe, it } from "vitest";
import { casingRule } from "../../src/rules/casing.rule.js";
describe("casing rule", () => {

Просмотреть файл

@ -1,11 +1,10 @@
{
"extends": "../tsconfig.json",
"extends": "../../tsconfig.base.json",
"references": [{ "path": "../compiler/tsconfig.json" }],
"compilerOptions": {
"outDir": "dist",
"rootDir": ".",
"tsBuildInfoFile": "temp/tsconfig.tsbuildinfo",
"types": ["node", "mocha"]
"tsBuildInfoFile": "temp/tsconfig.tsbuildinfo"
},
"include": ["src/**/*.ts", "test/**/*.ts"]
"include": ["src/**/*.ts", "test/**/*.ts", "vitest.config.ts"]
}

Просмотреть файл

@ -0,0 +1,14 @@
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "node",
isolate: false,
coverage: {
reporter: ["cobertura", "json", "text"],
},
outputFile: {
junit: "./test-results.xml",
},
},
});

Просмотреть файл

@ -45,14 +45,12 @@
"json5": "^2.2.3"
},
"devDependencies": {
"@types/mocha": "~10.0.6",
"@types/node": "~18.11.9",
"@typespec/eslint-config-typespec": "workspace:~0.51.0",
"@types/semver": "^7.5.6",
"eslint": "^8.55.0",
"mocha": "~10.2.0",
"mocha-junit-reporter": "~2.2.1",
"mocha-multi-reporters": "~1.5.1",
"vitest": "^1.1.0",
"@vitest/coverage-v8": "^1.1.0",
"c8": "~8.0.1",
"rimraf": "~5.0.1",
"typescript": "~5.3.3"

Просмотреть файл

@ -1,10 +1,9 @@
{
"extends": "../tsconfig.json",
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": ".",
"tsBuildInfoFile": "temp/tsconfig.tsbuildinfo",
"types": ["node", "mocha"]
"tsBuildInfoFile": "temp/tsconfig.tsbuildinfo"
},
"include": ["src/**/*.ts", "test/**/*.ts"]
"include": ["src/**/*.ts", "test/**/*.ts", "vitest.config.ts"]
}

Просмотреть файл

@ -1,3 +0,0 @@
{
"reporter": ["cobertura", "json", "text"]
}

Просмотреть файл

@ -1,7 +0,0 @@
timeout: 5000
require: source-map-support/register
spec: "dist/test/**/*.test.js"
ignore: "dist/test/manual/**/*.js"
# Config for https://www.npmjs.com/package/mocha-multi-reporters
reporterOptions: "configFile=mocha.reporter.config.json"

Просмотреть файл

@ -1,4 +0,0 @@
{
"reporterEnabled": "spec, mocha-junit-reporter",
"maxDiffSize": 16384
}

Просмотреть файл

@ -33,8 +33,9 @@
"clean": "rimraf ./dist ./temp",
"build": "tsc -p .",
"watch": "tsc -p . --watch",
"test": "mocha",
"test-official": "c8 mocha --forbid-only --reporter mocha-multi-reporters",
"test": "vitest run",
"test:watch": "vitest -w",
"test-official": "vitest run --coverage --reporter=junit --reporter=default --no-file-parallelism",
"lint": "eslint . --ext .ts --max-warnings=0",
"lint:fix": "eslint . --fix --ext .ts"
},
@ -56,13 +57,11 @@
"picocolors": "~1.0.0"
},
"devDependencies": {
"@types/mocha": "~10.0.6",
"@types/node": "~18.11.9",
"@typespec/eslint-config-typespec": "workspace:~0.51.0",
"eslint": "^8.55.0",
"mocha": "~10.2.0",
"mocha-junit-reporter": "~2.2.1",
"mocha-multi-reporters": "~1.5.1",
"vitest": "^1.1.0",
"@vitest/coverage-v8": "^1.1.0",
"c8": "~8.0.1",
"rimraf": "~5.0.1",
"typescript": "~5.3.3",

Просмотреть файл

@ -1,4 +1,5 @@
import { ok } from "assert";
import { describe, it } from "vitest";
describe("bundler", () => {
it("works", () => {

Просмотреть файл

@ -1,12 +1,11 @@
{
"extends": "../tsconfig.json",
"extends": "../../tsconfig.base.json",
"references": [{ "path": "../compiler/tsconfig.json" }, { "path": "../rest/tsconfig.json" }],
"compilerOptions": {
"outDir": "dist",
"rootDir": ".",
"tsBuildInfoFile": "temp/tsconfig.tsbuildinfo",
"skipLibCheck": true,
"types": ["node", "mocha"]
"skipLibCheck": true
},
"include": ["src/**/*.ts", "test/**/*.ts"]
"include": ["src/**/*.ts", "test/**/*.ts", "vitest.config.ts"]
}

Просмотреть файл

@ -0,0 +1,14 @@
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "node",
isolate: false,
coverage: {
reporter: ["cobertura", "json", "text"],
},
outputFile: {
junit: "./test-results.xml",
},
},
});

Просмотреть файл

@ -1,3 +0,0 @@
{
"reporter": ["cobertura", "json", "text"]
}

Просмотреть файл

@ -1,6 +0,0 @@
timeout: 60_000
require: source-map-support/register
spec: "dist/test/**/*.e2e.js"
# Config for https://www.npmjs.com/package/mocha-multi-reporters
reporterOptions: "configFile=mocha.reporter.config.json"

Просмотреть файл

@ -1,7 +0,0 @@
timeout: 5000
require: source-map-support/register
spec: "dist/test/**/*.test.js"
ignore: "dist/test/manual/**/*.js"
# Config for https://www.npmjs.com/package/mocha-multi-reporters
reporterOptions: "configFile=mocha.reporter.config.json"

Просмотреть файл

@ -1,4 +0,0 @@
{
"reporterEnabled": "spec, mocha-junit-reporter",
"maxDiffSize": 16384
}

Просмотреть файл

@ -65,9 +65,10 @@
"watch-tmlanguage": "node scripts/watch-tmlanguage.js",
"generate-tmlanguage": "node scripts/generate-tmlanguage.js",
"dogfood": "node scripts/dogfood.js",
"test": "mocha",
"test-official": "c8 mocha --forbid-only --reporter mocha-multi-reporters",
"e2e": "mocha --config ./.mocharc.e2e.yaml",
"test": "vitest run",
"test:watch": "vitest -w",
"test-official": "vitest run --coverage --reporter=junit --reporter=default --no-file-parallelism",
"e2e": "vitest run --config ./vitest.config.e2e.ts",
"gen-manifest": "node scripts/generate-manifest.js",
"regen-nonascii": "node scripts/regen-nonascii.js",
"fuzz": "node dist/test/manual/fuzz.js run",
@ -91,7 +92,6 @@
},
"devDependencies": {
"@types/babel__code-frame": "~7.0.6",
"@types/mocha": "~10.0.6",
"@types/mustache": "~4.2.5",
"@types/node": "~18.11.9",
"@types/prompts": "~2.4.9",
@ -101,9 +101,8 @@
"@typespec/internal-build-utils": "workspace:~0.51.0",
"eslint": "^8.55.0",
"grammarkdown": "~3.3.2",
"mocha": "~10.2.0",
"mocha-junit-reporter": "~2.2.1",
"mocha-multi-reporters": "~1.5.1",
"vitest": "^1.1.0",
"@vitest/coverage-v8": "^1.1.0",
"c8": "~8.0.1",
"prettier-plugin-organize-imports": "~3.2.4",
"source-map-support": "~0.5.21",

Просмотреть файл

@ -3,7 +3,6 @@ import { compilerAssert } from "./diagnostics.js";
import { Program } from "./program.js";
import { createJSONSchemaValidator } from "./schema-validator.js";
import {
CallableMessage,
DiagnosticMessages,
JSONSchemaValidator,
LinterDefinition,
@ -13,6 +12,8 @@ import {
TypeSpecLibraryDef,
} from "./types.js";
export { paramMessage } from "./param-message.js";
const globalLibraryUrlsLoadedSym = Symbol.for("TYPESPEC_LIBRARY_URLS_LOADED");
if ((globalThis as any)[globalLibraryUrlsLoadedSym] === undefined) {
(globalThis as any)[globalLibraryUrlsLoadedSym] = new Set<string>();
@ -105,25 +106,6 @@ export function defineLinter(def: LinterDefinition): LinterDefinition {
return def;
}
export function paramMessage<T extends string[]>(
strings: readonly string[],
...keys: T
): CallableMessage<T> {
const template = (dict: Record<T[number], string>) => {
const result = [strings[0]];
keys.forEach((key, i) => {
const value = (dict as any)[key];
if (value !== undefined) {
result.push(value);
}
result.push(strings[i + 1]);
});
return result.join("");
};
template.keys = keys;
return template;
}
/** Create a new linter rule. */
export function createLinterRule<const N extends string, const T extends DiagnosticMessages>(
definition: LinterRuleDefinition<N, T>

Просмотреть файл

@ -1,6 +1,6 @@
// Static assert: this won't compile if one of the entries above is invalid.
import { createDiagnosticCreator } from "./diagnostic-creator.js";
import { paramMessage } from "./library.js";
import { paramMessage } from "./param-message.js";
import type { TypeOfDiagnostics } from "./types.js";
const diagnostics = {

Просмотреть файл

@ -3,9 +3,11 @@ import { fileURLToPath, pathToFileURL } from "url";
import { createSourceFile } from "./diagnostics.js";
import { fetch } from "./fetch.js";
import { createConsoleSink } from "./logger/index.js";
import { joinPaths, resolvePath } from "./path-utils.js";
import { joinPaths } from "./path-utils.js";
import { CompilerHost, RmOptions } from "./types.js";
import { getSourceFileKindFromExt } from "./util.js";
import { findProjectRoot, getSourceFileKindFromExt } from "./util.js";
const root = (await findProjectRoot(stat, fileURLToPath(import.meta.url)))!;
/**
* Implementation of the @see CompilerHost using the real file system.
@ -21,7 +23,7 @@ export const NodeHost: CompilerHost = {
writeFile: (path: string, content: string) => writeFile(path, content, { encoding: "utf-8" }),
readDir: (path: string) => readdir(path),
rm: (path: string, options: RmOptions) => rm(path, options),
getExecutionRoot: () => resolvePath(fileURLToPath(import.meta.url), "../../../../"),
getExecutionRoot: () => root,
getJsImport: (path: string) => import(pathToFileURL(path).href),
getLibDirs() {
const rootDir = this.getExecutionRoot();

Просмотреть файл

@ -0,0 +1,20 @@
import type { CallableMessage } from "./types.js";
export function paramMessage<T extends string[]>(
strings: readonly string[],
...keys: T
): CallableMessage<T> {
const template = (dict: Record<T[number], string>) => {
const result = [strings[0]];
keys.forEach((key, i) => {
const value = (dict as any)[key];
if (value !== undefined) {
result.push(value);
}
result.push(strings[i + 1]);
});
return result.join("");
};
template.keys = keys;
return template;
}

Просмотреть файл

@ -23,7 +23,7 @@ import {
} from "./module-resolver.js";
import { CompilerOptions } from "./options.js";
import { isImportStatement, parse, parseStandaloneTypeReference } from "./parser.js";
import { getDirectoryPath, joinPaths, resolvePath } from "./path-utils.js";
import { getDirectoryPath, joinPaths } from "./path-utils.js";
import { createProjector } from "./projector.js";
import {
CompilerHost,
@ -420,9 +420,11 @@ export async function compile(
const loadedRoots = new Set<string>();
// Check all the files that were loaded
for (const fileUrl of getLibraryUrlsLoaded()) {
const root = await findProjectRoot(host, host.fileURLToPath(fileUrl));
if (root) {
loadedRoots.add(root);
if (fileUrl.startsWith("file:")) {
const root = await findProjectRoot(host.stat, host.fileURLToPath(fileUrl));
if (root) {
loadedRoots.add(root);
}
}
}
@ -1008,10 +1010,7 @@ export async function compile(
throw err;
}
const expected = resolvePath(
await host.realpath(host.fileURLToPath(import.meta.url)),
"../../../.."
);
const expected = host.getExecutionRoot();
if (actual.path !== expected && MANIFEST.version !== actual.manifest.version) {
const betterTypeSpecServerPath = actual.path;

Просмотреть файл

@ -22,7 +22,6 @@ import {
} from "./types.js";
export { typespecVersion } from "../manifest.js";
export { NodeHost } from "./node-host.js";
/**
* Recursively calls Object.freeze such that all objects and arrays
@ -264,14 +263,14 @@ export function omitUndefined<T extends Record<string, unknown>>(data: T): T {
* @param lookIn
*/
export async function findProjectRoot(
host: CompilerHost,
statFn: CompilerHost["stat"],
path: string
): Promise<string | undefined> {
let current = path;
while (true) {
const pkgPath = joinPaths(current, "package.json");
const stat = await doIO(
() => host.stat(pkgPath),
() => statFn(pkgPath),
pkgPath,
() => {}
);

Просмотреть файл

@ -1,6 +1,12 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import manifest from "../manifest.js";
let manifest;
try {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
manifest = (await import("../manifest.js")).default;
} catch {
const name = "../dist/manifest.js";
manifest = (await import(/* @vite-ignore */ /* webpackIgnore: true */ name)).default;
}
export const typespecVersion = manifest.version;

Просмотреть файл

@ -118,7 +118,7 @@ async function addLibraryImportCompletion(
node: StringLiteralNode
) {
const documentPath = file.file.path;
const projectRoot = await findProjectRoot(program.host, documentPath);
const projectRoot = await findProjectRoot(program.host.stat, documentPath);
if (projectRoot !== undefined) {
const [packagejson] = await loadFile(
program.host,

Просмотреть файл

@ -12,7 +12,7 @@ import { Program, compile as compileProgram } from "../core/program.js";
import { CompilerHost, Diagnostic, StringLiteral, Type } from "../core/types.js";
import { createStringMap, getSourceFileKindFromExt } from "../core/util.js";
import { expectDiagnosticEmpty } from "./expect.js";
import { createTestWrapper } from "./test-utils.js";
import { createTestWrapper, findTestPackageRoot } from "./test-utils.js";
import {
BasicTestRunner,
TestFileSystem,
@ -220,7 +220,7 @@ export async function createTestFileSystem(options?: TestHostOptions): Promise<T
export const StandardTestLibrary: TypeSpecTestLibrary = {
name: "@typespec/compiler",
packageRoot: resolvePath(fileURLToPath(import.meta.url), "../../../../"),
packageRoot: await findTestPackageRoot(import.meta.url),
files: [
{ virtualPath: "./.tsp/dist/src/lib", realDir: "./dist/src/lib", pattern: "*" },
{ virtualPath: "./.tsp/lib", realDir: "./lib", pattern: "*" },

Просмотреть файл

@ -1,6 +1,7 @@
import { resolvePath } from "../core/index.js";
import { fileURLToPath } from "url";
import { NodeHost, resolvePath } from "../core/index.js";
import { CompilerOptions } from "../core/options.js";
import { StandardTestLibrary } from "./test-host.js";
import { findProjectRoot } from "../core/util.js";
import {
BasicTestRunner,
TestHost,
@ -8,6 +9,10 @@ import {
TypeSpecTestLibraryInit,
} from "./types.js";
/** Find the package root from the provided file */
export function findTestPackageRoot(fileUrl: string): Promise<string> {
return findProjectRoot(NodeHost.stat, fileURLToPath(fileUrl)) as Promise<string>;
}
/**
* Define a test library defaulting to the most common library structure.
* @param init Library configuration.
@ -63,7 +68,8 @@ export function createTestWrapper(
} = testWrapperOptions;
const autoCode = [
...(
autoImports ?? host.libraries.filter((x) => x !== StandardTestLibrary).map((x) => x.name)
autoImports ??
host.libraries.filter((x) => x.name !== "@typespec/compiler").map((x) => x.name)
).map((x) => `import "${x}";`),
...(autoUsings ?? []).map((x) => `using ${x};`),
].join("\n");

Просмотреть файл

@ -1,8 +1,10 @@
import { resolvePath } from "@typespec/compiler";
import { createTestLibrary, TypeSpecTestLibrary } from "@typespec/compiler/testing";
import { fileURLToPath } from "url";
import {
createTestLibrary,
findTestPackageRoot,
TypeSpecTestLibrary,
} from "@typespec/compiler/testing";
export const TestLibrary: TypeSpecTestLibrary = createTestLibrary({
name: "emitter-ts",
packageRoot: resolvePath(fileURLToPath(import.meta.url), "../../../../"),
packageRoot: await findTestPackageRoot(import.meta.url),
});

Просмотреть файл

@ -1,5 +1,5 @@
import { strictEqual } from "node:assert";
import { describe, it } from "node:test";
import { describe, it } from "vitest";
import { emit } from "./test-host.js";
describe("hello", () => {

Просмотреть файл

@ -1,8 +1,10 @@
import { resolvePath } from "@typespec/compiler";
import { createTestLibrary, TypeSpecTestLibrary } from "@typespec/compiler/testing";
import { fileURLToPath } from "url";
import {
createTestLibrary,
findTestPackageRoot,
TypeSpecTestLibrary,
} from "@typespec/compiler/testing";
export const TestLibrary: TypeSpecTestLibrary = createTestLibrary({
name: "{{name}}",
packageRoot: resolvePath(fileURLToPath(import.meta.url), "../../../../"),
packageRoot: await findTestPackageRoot(import.meta.url),
});

Просмотреть файл

@ -1,5 +1,5 @@
import { strictEqual } from "node:assert";
import { describe, it } from "node:test";
import { describe, it } from "vitest";
import { emit } from "./test-host.js";
describe("hello", () => {

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Binder, createBinder } from "../src/core/binder.js";
import { createSourceFile } from "../src/core/diagnostics.js";
import { createLogger } from "../src/core/logger/logger.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model, Type, Union } from "../../src/core/types.js";
import {
TestHost,

Просмотреть файл

@ -1,4 +1,5 @@
import { deepEqual, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model, Operation, StringLiteral, Type } from "../../src/core/types.js";
import { TestHost, createTestHost, expectDiagnosticEmpty } from "../../src/testing/index.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { createTestHost, expectDiagnostics, TestHost } from "../../src/testing/index.js";
import { beforeEach, describe, it } from "vitest";
import { TestHost, createTestHost, expectDiagnostics } from "../../src/testing/index.js";
describe("compiler: semantic checks on source with parse errors", () => {
let testHost: TestHost;

Просмотреть файл

@ -1,4 +1,5 @@
import { deepStrictEqual, ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Program } from "../../src/core/program.js";
import { DecoratorContext, Type } from "../../src/core/types.js";
import { createRekeyableMap } from "../../src/core/util.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { setTypeSpecNamespace } from "../../src/core/index.js";
import {
BasicTestRunner,

Просмотреть файл

@ -1,14 +1,15 @@
import { beforeEach, describe, it } from "vitest";
import { Diagnostic } from "../../src/index.js";
import {
BasicTestRunner,
DiagnosticMatch,
TestHost,
createTestHost,
createTestRunner,
createTestWrapper,
DiagnosticMatch,
expectDiagnosticEmpty,
expectDiagnostics,
extractCursor,
TestHost,
} from "../../src/testing/index.js";
describe("compiler: checker: deprecation", () => {

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model, Operation } from "../../src/core/index.js";
import { getDoc, getErrorsDoc, getReturnsDoc } from "../../src/lib/decorators.js";
import { BasicTestRunner, createTestRunner } from "../../src/testing/index.js";

Просмотреть файл

@ -1,5 +1,6 @@
import { beforeEach, describe, it } from "vitest";
import { Diagnostic } from "../../src/core/types.js";
import { createTestHost, expectDiagnostics, TestHost } from "../../src/testing/index.js";
import { TestHost, createTestHost, expectDiagnostics } from "../../src/testing/index.js";
describe("compiler: duplicate declarations", () => {
let testHost: TestHost;
@ -95,7 +96,7 @@ describe("compiler: duplicate declarations", () => {
});
describe("reports duplicate namespace/non-namespace", () => {
context("in same file", () => {
describe("in same file", () => {
it("with namespace first", async () => {
testHost.addTypeSpecFile(
"main.tsp",
@ -123,7 +124,7 @@ describe("compiler: duplicate declarations", () => {
});
});
context("across multiple files", () => {
describe("across multiple files", () => {
// NOTE: Different order of declarations triggers different code paths, so test both
it("with namespace first", async () => {
testHost.addTypeSpecFile(

Просмотреть файл

@ -1,4 +1,5 @@
import { strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { filterModelProperties, getEffectiveModelType } from "../../src/core/checker.js";
import { DecoratorContext, Model, ModelProperty, Type } from "../../src/core/types.js";
import { TestHost, createTestHost, expectIdenticalTypes } from "../../src/testing/index.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { DecoratorContext, Enum, EnumMember, Model, Type } from "../../src/core/types.js";
import { getDoc } from "../../src/index.js";
import { TestHost, createTestHost, expectDiagnostics } from "../../src/testing/index.js";

Просмотреть файл

@ -1,6 +1,7 @@
import assert, { notStrictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model } from "../../src/core/types.js";
import { createTestHost, TestHost } from "../../src/testing/index.js";
import { TestHost, createTestHost } from "../../src/testing/index.js";
describe("compiler: global namespace", () => {
let testHost: TestHost;

Просмотреть файл

@ -1,4 +1,5 @@
import { deepStrictEqual, ok } from "assert";
import { beforeEach, describe, it } from "vitest";
import {
LibraryLocationContext,
LocationContext,

Просмотреть файл

@ -1,4 +1,5 @@
import { deepStrictEqual, notStrictEqual, ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { isTemplateDeclaration } from "../../src/core/type-utils.js";
import { Interface, Model, Operation, Type } from "../../src/core/types.js";
import { getDoc } from "../../src/index.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model } from "../../src/core/index.js";
import {
BasicTestRunner,

Просмотреть файл

@ -1,4 +1,5 @@
import assert, { strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model, Type } from "../../src/core/types.js";
import { TestHost, createTestHost } from "../../src/testing/index.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { deepStrictEqual, match, ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { isTemplateDeclaration } from "../../src/core/type-utils.js";
import { Model, ModelProperty, Type } from "../../src/core/types.js";
import { Operation, getDoc, isArrayModelType } from "../../src/index.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { getTypeName } from "../../src/core/index.js";
import { Program } from "../../src/core/program.js";
import { Model, Namespace, Type } from "../../src/core/types.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { deepStrictEqual, notStrictEqual, ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { DecoratorContext, IntrinsicType, Operation, Type } from "../../src/core/types.js";
import { getDoc } from "../../src/index.js";
import { TestHost, createTestHost, expectDiagnostics } from "../../src/testing/index.js";

Просмотреть файл

@ -1,4 +1,6 @@
/* eslint-disable vitest/valid-describe-callback */
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Enum, Interface, Model, Operation, Type } from "../../src/core/types.js";
import { TestHost, createTestHost, expectDiagnostics } from "../../src/testing/index.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { deepStrictEqual, ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Diagnostic, FunctionParameterNode, Model, Type } from "../../src/core/index.js";
import {
BasicTestRunner,

Просмотреть файл

@ -1,4 +1,5 @@
import { strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import {
BasicTestRunner,
createTestHost,

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model, NumericLiteral } from "../../src/core/index.js";
import {
BasicTestRunner,

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model, Type } from "../../src/core/types.js";
import {
BasicTestRunner,

Просмотреть файл

@ -1,4 +1,5 @@
import { strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model, StringTemplate } from "../../src/index.js";
import { BasicTestRunner, createTestRunner } from "../../src/testing/index.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { deepStrictEqual, fail, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { getSourceLocation } from "../../src/core/diagnostics.js";
import { Diagnostic, Model, StringLiteral, Type } from "../../src/core/types.js";
import {
@ -516,7 +517,7 @@ describe("compiler: templates", () => {
});
});
describe("named template argument instantiations", async () => {
describe("named template argument instantiations", () => {
it("with named arguments", async () => {
testHost.addTypeSpecFile(
"main.tsp",

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model, Union, UnionVariant } from "../../src/core/types.js";
import { TestHost, createTestHost } from "../../src/testing/index.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { rejects, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { Model } from "../../src/core/types.js";
import {
TestHost,

Просмотреть файл

@ -1,4 +1,5 @@
import { deepStrictEqual, ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { stringify } from "yaml";
import { TypeSpecRawConfig } from "../src/config/types.js";
import { CompileCliArgs, getCompilerOptions } from "../src/core/cli/actions/compile/args.js";
@ -53,7 +54,7 @@ describe("compiler: cli", () => {
deepStrictEqual(options?.options, {});
});
context("config file with emitters", () => {
describe("config file with emitters", () => {
beforeEach(() => {
host.addTypeSpecFile(
"ws/tspconfig.yaml",

Просмотреть файл

@ -1,14 +1,14 @@
import { deepStrictEqual } from "assert";
import { WatchEventType, mkdirSync } from "fs";
import { appendFile, mkdir, rm } from "fs/promises";
import { dirname } from "path";
import { fileURLToPath } from "url";
import { afterEach, beforeAll, describe, it } from "vitest";
import { ProjectWatcher, createWatcher } from "../../../src/core/cli/actions/compile/watch.js";
import { getDirectoryPath, resolvePath } from "../../../src/index.js";
import { findTestPackageRoot } from "../../../src/testing/test-utils.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const pkgRoot = await findTestPackageRoot(import.meta.url);
const fixtureRoot = resolvePath(__dirname, "../../../../temp/test/cli/watcher");
const fixtureRoot = resolvePath(pkgRoot, "temp/test/cli/watcher");
function fixturePath(path: string) {
return resolvePath(fixtureRoot, path);
@ -54,8 +54,8 @@ class FixtureFS {
}
}
describe("compiler: node host", () => {
before(async () => {
describe.skip("compiler: watch", () => {
beforeAll(async () => {
try {
await rm(fixtureRoot, { recursive: true });
} catch {}
@ -66,7 +66,7 @@ describe("compiler: node host", () => {
let fixtures: FixtureFS;
let changes: [WatchEventType, string][];
beforeEach(() => {
beforeAll(() => {
fixtures = new FixtureFS();
changes = [];
watcher = createWatcher((evt, name) => {
@ -101,6 +101,8 @@ describe("compiler: node host", () => {
const file2 = await fixtures.modify("multiple/sub-folder1/file2.txt");
const file3 = await fixtures.modify("multiple/sub-folder2/file3.txt");
watcher.updateWatchedFiles([file1, file2, file3]);
await delay(100);
changes = [];
deepStrictEqual(changes, [], "Should not report change in initial load.");
await fixtures.modify("multiple/sub-folder1/file2.txt", 100);

Просмотреть файл

@ -1,4 +1,5 @@
import { deepStrictEqual } from "assert";
import { describe, it } from "vitest";
import {
ExpandConfigOptions,
expandConfigVariables,

Просмотреть файл

@ -1,16 +1,17 @@
import { deepStrictEqual, strictEqual } from "assert";
import { dirname, join } from "path";
import { fileURLToPath } from "url";
import { join } from "path";
import { describe, it } from "vitest";
import { TypeSpecConfigJsonSchema } from "../../src/config/config-schema.js";
import { TypeSpecRawConfig, loadTypeSpecConfigForPath } from "../../src/config/index.js";
import { createSourceFile } from "../../src/core/diagnostics.js";
import { NodeHost } from "../../src/core/node-host.js";
import { createJSONSchemaValidator } from "../../src/core/schema-validator.js";
import { resolvePath } from "../../src/index.js";
import { findTestPackageRoot } from "../../src/testing/test-utils.js";
const scenarioRoot = resolvePath(
dirname(fileURLToPath(import.meta.url)),
"../../../test/config/scenarios"
await findTestPackageRoot(import.meta.url),
"test/config/scenarios"
);
describe("compiler: config file loading", () => {

Просмотреть файл

@ -1,14 +1,14 @@
import { deepStrictEqual } from "assert";
import { dirname } from "path";
import { fileURLToPath } from "url";
import { describe, it } from "vitest";
import { resolveCompilerOptions } from "../../src/config/index.js";
import { NodeHost } from "../../src/core/node-host.js";
import { normalizePath, resolvePath } from "../../src/index.js";
import { expectDiagnosticEmpty, expectDiagnostics } from "../../src/testing/expect.js";
import { findTestPackageRoot } from "../../src/testing/test-utils.js";
const scenarioRoot = resolvePath(
dirname(fileURLToPath(import.meta.url)),
"../../../test/config/scenarios"
await findTestPackageRoot(import.meta.url),
"test/config/scenarios"
);
describe("compiler: resolve compiler options", () => {

Просмотреть файл

@ -1,4 +1,5 @@
import { strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { SourceLocationOptions, getSourceLocation } from "../../src/index.js";
import { createTestRunner } from "../../src/testing/test-host.js";
import { extractSquiggles } from "../../src/testing/test-server-host.js";

Просмотреть файл

@ -1,4 +1,5 @@
import { ok, strictEqual } from "assert";
import { describe, it } from "vitest";
import { Diagnostic, EmitContext, createTypeSpecLibrary } from "../../src/index.js";
import { expectDiagnosticEmpty, expectDiagnostics } from "../../src/testing/expect.js";
import { createTestHost } from "../../src/testing/test-host.js";

Просмотреть файл

@ -1,6 +1,8 @@
import { describe, it } from "vitest";
import { createLinterRule, createTypeSpecLibrary } from "../../src/core/library.js";
import { Linter, createLinter } from "../../src/core/linter.js";
import { LibraryInstance, LinterDefinition } from "../../src/index.js";
import type { LibraryInstance, LinterDefinition } from "../../src/index.js";
import {
createTestHost,
expectDiagnosticEmpty,

Просмотреть файл

@ -2,6 +2,7 @@ import { rejects, strictEqual } from "assert";
import { mkdir, readFile, rm, writeFile } from "fs/promises";
import { dirname } from "path";
import { fileURLToPath } from "url";
import { beforeAll, describe, it } from "vitest";
import { InvalidEncodingError, NodeHost } from "../../../src/core/node-host.js";
import { getDirectoryPath, resolvePath } from "../../../src/index.js";
@ -37,7 +38,7 @@ describe("compiler: node host", () => {
return resolvedPath;
}
before(async () => {
beforeAll(async () => {
try {
await rm(fixtureRoot, { recursive: true });
} catch {}

Просмотреть файл

@ -1,4 +1,5 @@
import { deepStrictEqual, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import {
DecoratorContext,
TypeSpecValue,

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше