@typespec/emitter-framework 0.9.0-dev.8 → 0.10.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/src/testing/scenario-test/code-block-expectation.d.ts +33 -0
  3. package/dist/src/testing/scenario-test/code-block-expectation.d.ts.map +1 -0
  4. package/dist/src/testing/scenario-test/code-block-expectation.js +69 -0
  5. package/dist/src/testing/scenario-test/code-block-expectation.test.d.ts +2 -0
  6. package/dist/src/testing/scenario-test/code-block-expectation.test.d.ts.map +1 -0
  7. package/dist/src/testing/scenario-test/code-block-expectation.test.js +80 -0
  8. package/dist/src/testing/scenario-test/harness.d.ts +2 -2
  9. package/dist/src/testing/scenario-test/harness.d.ts.map +1 -1
  10. package/dist/src/testing/scenario-test/harness.js +69 -158
  11. package/dist/src/testing/scenario-test/index.d.ts +0 -1
  12. package/dist/src/testing/scenario-test/index.d.ts.map +1 -1
  13. package/dist/src/testing/scenario-test/index.js +1 -2
  14. package/dist/src/testing/scenario-test/snippet-extractor.d.ts +1 -1
  15. package/dist/src/testing/scenario-test/snippet-extractor.js +1 -1
  16. package/dist/test/testing/snippet-extractor-csharp.test.js +3 -3
  17. package/dist/test/testing/snippet-extractor-java.test.js +3 -3
  18. package/dist/test/testing/snippet-extractor-python.test.js +2 -2
  19. package/dist/test/testing/snippet-extractor-typescript.test.js +3 -3
  20. package/package.json +20 -12
  21. package/src/testing/scenario-test/code-block-expectation.test.ts +95 -0
  22. package/src/testing/scenario-test/code-block-expectation.ts +115 -0
  23. package/src/testing/scenario-test/harness.ts +91 -236
  24. package/src/testing/scenario-test/index.ts +0 -1
  25. package/src/testing/scenario-test/snippet-extractor.ts +1 -1
  26. package/test/testing/snippet-extractor-csharp.test.ts +3 -3
  27. package/test/testing/snippet-extractor-java.test.ts +3 -3
  28. package/test/testing/snippet-extractor-python.test.ts +2 -2
  29. package/test/testing/snippet-extractor-typescript.test.ts +3 -3
  30. package/dist/src/testing/scenario-test/test-host.d.ts +0 -8
  31. package/dist/src/testing/scenario-test/test-host.d.ts.map +0 -1
  32. package/dist/src/testing/scenario-test/test-host.js +0 -49
  33. package/src/testing/scenario-test/test-host.ts +0 -83
@@ -1,176 +1,59 @@
1
- import { normalizePath } from "@typespec/compiler";
2
- import type { TypeSpecTestLibrary } from "@typespec/compiler/testing";
1
+ import { logDiagnostics, NodeHost } from "@typespec/compiler";
2
+ import { expectDiagnosticEmpty, type EmitterTester } from "@typespec/compiler/testing";
3
3
  import { readdirSync, readFileSync, statSync, writeFileSync } from "fs";
4
- import minimist from "minimist";
5
- import path from "path";
4
+ import { join } from "pathe";
6
5
  import { format } from "prettier";
7
- import { afterAll, describe, expect, it } from "vitest";
6
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
7
+ import {
8
+ getExcerptForQuery,
9
+ parseCodeblockExpectation,
10
+ type CodeBlockExpectation,
11
+ } from "./code-block-expectation.js";
8
12
  import type { LanguageConfiguration, SnippetExtractor } from "./snippet-extractor.js";
9
- import { emitWithDiagnostics } from "./test-host.js";
10
-
11
- const rawArgs = process.env.TEST_ARGS ? process.env.TEST_ARGS.split(" ") : [];
12
-
13
- // Parse command-line arguments with minimist
14
- const args = minimist(rawArgs, {
15
- alias: {
16
- filter: "f", // Short alias for `--filter`
17
- },
18
- default: {
19
- filter: undefined, // Default to undefined if no filter is provided
20
- },
21
- });
22
-
23
- // Extract the filter paths from the parsed arguments
24
- const filterPaths = args.filter
25
- ? Array.isArray(args.filter) // Handle single or multiple file paths
26
- ? args.filter
27
- : [args.filter]
28
- : undefined;
29
13
 
30
14
  const SCENARIOS_UPDATE =
31
15
  process.env["RECORD"] === "true" || process.env["SCENARIOS_UPDATE"] === "true";
32
16
 
33
- type EmitterFunction = (tsp: string, namedArgs: Record<string, string>) => Promise<string>;
34
-
35
- async function assertGetEmittedFile(
36
- testLibrary: TypeSpecTestLibrary,
37
- emitterOutputDir: string,
38
- file: string,
39
- code: string,
40
- ) {
41
- const [emittedFiles, diagnostics] = await emitWithDiagnostics(
42
- testLibrary,
43
- emitterOutputDir,
44
- code,
45
- );
46
-
47
- const errors = diagnostics.filter((d) => d.severity === "error");
48
- const warnings = diagnostics.filter((d) => d.severity === "warning");
49
- if (warnings.length > 0) {
50
- // eslint-disable-next-line no-console
51
- console.warn(`Warning compiling code:\n ${warnings.map((x) => x.message).join("\n")}`);
52
- }
53
- if (errors.length > 0) {
54
- throw new Error(`Error compiling code:\n ${errors.map((x) => x.message).join("\n")}`);
55
- }
56
-
57
- const normalizedTarget = normalizePath(file);
58
- const sourceFile = emittedFiles.find((x) => normalizePath(x.path) === normalizedTarget);
59
-
60
- if (!sourceFile) {
61
- throw new Error(
62
- `File ${file} not found in emitted files:\n ${emittedFiles.map((f) => f.path).join("\n")}`,
63
- );
64
- }
65
- return sourceFile;
66
- }
67
-
68
- /**
69
- * Mapping of different snapshot types to how to get them.
70
- * Snapshot types can take single-word string arguments templated in curly braces {} and are otherwise regex
71
- */
72
- function getCodeBlockTypes(
73
- testLibrary: TypeSpecTestLibrary,
74
- languageConfiguration: LanguageConfiguration,
75
- emitterOutputDir: string,
76
- snippetExtractor: SnippetExtractor,
77
- ): Record<string, EmitterFunction> {
78
- const languageTags = languageConfiguration.codeBlockTypes.join("|");
79
- return {
80
- // Snapshot of a particular interface named {name} in the models file
81
- [`(${languageTags}) {file} interface {name}`]: async (code, { file, name }) => {
82
- const sourceFile = await assertGetEmittedFile(testLibrary, emitterOutputDir, file, code);
83
- const snippet = snippetExtractor.getInterface(sourceFile.content, name);
84
-
85
- if (!snippet) {
86
- throw new Error(`Interface ${name} not found in ${file}`);
87
- }
88
-
89
- return snippet;
90
- },
91
-
92
- [`(${languageTags}) {file} type {name}`]: async (code, { file, name }) => {
93
- const sourceFile = await assertGetEmittedFile(testLibrary, emitterOutputDir, file, code);
94
- const snippet = snippetExtractor.getTypeAlias(sourceFile.content, name);
95
-
96
- if (!snippet) {
97
- throw new Error(`Type alias ${name} not found in ${file}`);
98
- }
99
-
100
- return snippet;
101
- },
102
-
103
- // Snapshot of a particular function named {name} in the models file
104
- [`(${languageTags}) {file} function {name}`]: async (code, { file, name }) => {
105
- const sourceFile = await assertGetEmittedFile(testLibrary, emitterOutputDir, file, code);
106
- const snippet = snippetExtractor.getFunction(sourceFile.content, name);
107
-
108
- if (!snippet) {
109
- throw new Error(`Function ${name} not found in ${file}`);
110
- }
111
-
112
- return snippet;
113
- },
114
-
115
- // Snapshot of a particular class named {name} in the models file
116
- [`(${languageTags}) {file} class {name}`]: async (code, { file, name }) => {
117
- const sourceFile = await assertGetEmittedFile(testLibrary, emitterOutputDir, file, code);
118
- const snippet = snippetExtractor.getClass(sourceFile.content, name);
119
-
120
- if (!snippet) {
121
- throw new Error(`Class ${name} not found in ${file}`);
122
- }
123
-
124
- return snippet;
125
- },
126
-
127
- // Snapshot of the entire file
128
- [`(${languageTags}) {file}`]: async (code, { file }) => {
129
- const sourceFile = await assertGetEmittedFile(testLibrary, emitterOutputDir, file, code);
130
- return sourceFile.content;
131
- },
132
- };
133
- }
134
-
135
17
  export async function executeScenarios(
136
- testLibrary: TypeSpecTestLibrary,
18
+ tester: EmitterTester,
137
19
  languageConfiguration: LanguageConfiguration,
138
20
  scenariosLocation: string,
139
- emitterOutputDir: string,
140
21
  snippetExtractor: SnippetExtractor,
141
22
  ) {
142
- const scenarioList = filterPaths ?? [];
143
- // eslint-disable-next-line no-console
144
- scenarioList.length && console.log("Filtering scenarios: ", scenarioList);
23
+ const scenarioList = discoverAllScenarios(scenariosLocation);
145
24
 
146
- if (!scenarioList.length) {
147
- // Add all scenarios.
148
- discoverAllScenarios(scenariosLocation, scenarioList);
149
- }
150
-
151
- describeScenarios(
152
- scenarioList,
153
- testLibrary,
154
- languageConfiguration,
155
- emitterOutputDir,
156
- snippetExtractor,
157
- );
25
+ describeScenarios(scenarioList, tester, languageConfiguration, snippetExtractor);
158
26
  }
159
27
 
160
- function discoverAllScenarios(location: string, scenarios: string[]) {
161
- const children = readdirSync(location);
162
- for (const child of children) {
163
- const fullPath = path.join(location, child);
164
- const stat = statSync(fullPath);
165
- if (stat.isDirectory()) {
166
- discoverAllScenarios(fullPath, scenarios);
167
- } else {
168
- scenarios.push(fullPath);
28
+ function discoverAllScenarios(dir: string): ScenarioFileId[] {
29
+ const scenarios: ScenarioFileId[] = [];
30
+
31
+ function recurse(current: string) {
32
+ const children = readdirSync(join(dir, current));
33
+ for (const child of children) {
34
+ const fullPath = join(dir, current, child);
35
+ const stat = statSync(fullPath);
36
+ if (stat.isDirectory()) {
37
+ recurse(join(current, child));
38
+ } else {
39
+ scenarios.push({ path: fullPath, relativePath: join(current, child) });
40
+ }
169
41
  }
170
42
  }
171
43
 
44
+ recurse("");
172
45
  return scenarios;
173
46
  }
47
+
48
+ interface ScenarioFileId {
49
+ path: string;
50
+ relativePath: string;
51
+ }
52
+
53
+ interface ScenarioFile extends ScenarioFileId {
54
+ scenarios: Scenario[];
55
+ }
56
+
174
57
  interface Scenario {
175
58
  // The title of the scenario delimited by H1
176
59
  title: string;
@@ -186,54 +69,32 @@ interface ScenarioContents {
186
69
 
187
70
  interface SpecCodeBlock {
188
71
  kind: "spec" | "test";
189
- content: string[];
72
+ content: string;
190
73
  }
191
74
 
192
75
  interface TestCodeBlock {
193
76
  kind: "test";
194
77
  heading: string;
195
- content: string[];
196
- matchedTemplate: {
197
- template: string;
198
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
199
- fn: Function;
200
- namedArgs: Record<string, string> | null;
201
- };
78
+ content: string;
79
+ expectation: CodeBlockExpectation;
202
80
  }
203
81
 
204
82
  type ScenarioCodeBlock = SpecCodeBlock | TestCodeBlock;
205
83
 
206
- interface ScenarioFile {
207
- path: string;
208
- scenarios: Scenario[];
209
- }
210
-
211
- function parseFile(
212
- path: string,
213
- testLibrary: TypeSpecTestLibrary,
214
- languageConfiguration: LanguageConfiguration,
215
- emitterOutputDir: string,
216
- snippetExtractor: SnippetExtractor,
217
- ): ScenarioFile {
84
+ function parseFile(file: ScenarioFileId): ScenarioFile {
218
85
  // Read the whole file
219
- const rawContent = readFileSync(path, { encoding: "utf-8" });
86
+ const rawContent = readFileSync(file.path, { encoding: "utf-8" });
220
87
 
221
88
  // Split the content by H1
222
89
  const sections = splitByH1(rawContent);
223
90
 
224
91
  const scenarioFile: ScenarioFile = {
225
- path,
92
+ ...file,
226
93
  scenarios: [],
227
94
  };
228
95
 
229
96
  for (const section of sections) {
230
- const scenarioContent = parseScenario(
231
- section.content,
232
- testLibrary,
233
- languageConfiguration,
234
- emitterOutputDir,
235
- snippetExtractor,
236
- );
97
+ const scenarioContent = parseScenario(section.content);
237
98
  const scenario: Scenario = {
238
99
  title: section.title,
239
100
  content: scenarioContent,
@@ -249,59 +110,43 @@ function isTestCodeBlock(codeBlock: ScenarioCodeBlock): codeBlock is TestCodeBlo
249
110
  return codeBlock.kind === "test";
250
111
  }
251
112
 
252
- function parseScenario(
253
- content: string,
254
- testLibrary: TypeSpecTestLibrary,
255
- languageConfiguration: LanguageConfiguration,
256
- emitterOutputDir: string,
257
- snippetExtractor: SnippetExtractor,
258
- ): ScenarioContents {
113
+ function parseScenario(content: string): ScenarioContents {
259
114
  const rawLines = content.split("\n");
260
115
  const scenario: ScenarioContents = {
261
116
  lines: [],
262
- specBlock: { kind: "spec", content: [] },
117
+ specBlock: { kind: "spec", content: "" },
263
118
  testBlocks: [],
264
119
  };
265
120
 
266
- let currentCodeBlock: ScenarioCodeBlock | null = null;
267
-
268
- // Precompute output code block types once
269
- const outputCodeBlockTypes = getCodeBlockTypes(
270
- testLibrary,
271
- languageConfiguration,
272
- emitterOutputDir,
273
- snippetExtractor,
274
- );
121
+ let currentCodeBlock: { heading: string; content: string[] } | null = null;
275
122
 
276
123
  for (const line of rawLines) {
277
124
  if (line.startsWith("```") && currentCodeBlock) {
278
- // Close the code block
279
- scenario.lines.push(currentCodeBlock);
280
- if (!isTestCodeBlock(currentCodeBlock)) {
281
- scenario.specBlock.content = currentCodeBlock.content;
125
+ const heading = currentCodeBlock.heading;
126
+ const codeBlockKind =
127
+ heading.includes("tsp") || heading.includes("typespec") ? "spec" : "test";
128
+ const content = currentCodeBlock.content.join("\n");
129
+ if (codeBlockKind === "spec") {
130
+ const codeblock: SpecCodeBlock = {
131
+ kind: "spec",
132
+ content,
133
+ };
134
+ scenario.lines.push(codeblock);
135
+ scenario.specBlock.content = content;
282
136
  } else {
283
- for (const [template, fn] of Object.entries(outputCodeBlockTypes)) {
284
- const templateRegex = new RegExp(
285
- "^" + template.replace(/\{(\w+)\}/g, "(?<$1>[^\\s]+)") + "$",
286
- );
287
-
288
- const match = currentCodeBlock.heading.match(templateRegex);
289
- if (match) {
290
- currentCodeBlock.matchedTemplate = {
291
- template,
292
- fn,
293
- namedArgs: match.groups ?? null,
294
- };
295
- break;
296
- }
297
- }
298
- scenario.testBlocks.push(currentCodeBlock);
137
+ const codeblock: TestCodeBlock = {
138
+ kind: "test",
139
+ heading: currentCodeBlock.heading,
140
+ content,
141
+ expectation: parseCodeblockExpectation(currentCodeBlock.heading, content),
142
+ };
143
+ scenario.lines.push(codeblock);
144
+ scenario.testBlocks.push(codeblock);
299
145
  }
300
146
  currentCodeBlock = null;
301
147
  } else if (line.startsWith("```")) {
302
- const codeBlockKind = line.includes("tsp") || line.includes("typespec") ? "spec" : "test";
303
148
  // Start a new code block
304
- currentCodeBlock = { kind: codeBlockKind, heading: line.substring(3), content: [] };
149
+ currentCodeBlock = { heading: line.substring(3), content: [] };
305
150
  } else if (currentCodeBlock) {
306
151
  // Append to code block content
307
152
  currentCodeBlock.content.push(line);
@@ -315,37 +160,47 @@ function parseScenario(
315
160
  }
316
161
 
317
162
  function describeScenarios(
318
- scenarioFiles: string[],
319
- testLibrary: TypeSpecTestLibrary,
163
+ scenarioFiles: ScenarioFileId[],
164
+ tester: EmitterTester,
320
165
  languageConfiguration: LanguageConfiguration,
321
- emitterOutputDir: string,
322
166
  snippetExtractor: SnippetExtractor,
323
167
  ) {
324
- const scenarios = scenarioFiles.map((f) =>
325
- parseFile(f, testLibrary, languageConfiguration, emitterOutputDir, snippetExtractor),
326
- );
168
+ const scenarios = scenarioFiles.map((f) => parseFile(f));
327
169
 
328
170
  for (const scenarioFile of scenarios) {
329
- describe(`Scenario File: ${scenarioFile.path}`, () => {
171
+ describe(`${scenarioFile.relativePath}`, () => {
330
172
  for (const scenario of scenarioFile.scenarios) {
331
173
  const isOnly = scenario.title.includes("only:");
332
174
  const isSkip = scenario.title.includes("skip:");
333
-
334
175
  const describeFn = isSkip ? describe.skip : isOnly ? describe.only : describe;
335
176
 
177
+ let outputFiles: Record<string, string>;
178
+ beforeAll(async () => {
179
+ const code = scenario.content.specBlock.content;
180
+ const [{ outputs }, diagnostics] = await tester.compileAndDiagnose(code);
181
+ const errors = diagnostics.filter((d) => d.severity === "error");
182
+ const warnings = diagnostics.filter((d) => d.severity === "warning");
183
+ if (warnings.length > 0) {
184
+ // TODO: this should ideally fail the test or be part of the expectation.
185
+ logDiagnostics(warnings, NodeHost.logSink);
186
+ }
187
+ expectDiagnosticEmpty(errors);
188
+ outputFiles = outputs;
189
+ });
190
+
336
191
  describeFn(`Scenario: ${scenario.title}`, () => {
337
192
  for (const testBlock of scenario.content.testBlocks) {
338
193
  it(`Test: ${testBlock.heading}`, async () => {
339
- const { fn, namedArgs } = testBlock.matchedTemplate;
340
- const result = await fn(
341
- scenario.content.specBlock.content.join("\n"),
342
- namedArgs ?? {},
194
+ const result = getExcerptForQuery(
195
+ snippetExtractor,
196
+ testBlock.expectation,
197
+ outputFiles,
343
198
  );
344
199
 
345
200
  if (SCENARIOS_UPDATE) {
346
- testBlock.content = (await languageConfiguration.format(result)).split("\n");
201
+ testBlock.content = await languageConfiguration.format(result);
347
202
  } else {
348
- const expected = await languageConfiguration.format(testBlock.content.join("\n"));
203
+ const expected = await languageConfiguration.format(testBlock.content);
349
204
  const actual = await languageConfiguration.format(result);
350
205
  expect(actual).toBe(expected);
351
206
  }
@@ -375,7 +230,7 @@ async function updateFile(scenarioFile: ScenarioFile) {
375
230
  } else {
376
231
  const heading = isTestCodeBlock(line) ? line.heading : "tsp";
377
232
  newContent.push("```" + heading);
378
- newContent.push(...line.content);
233
+ newContent.push(line.content);
379
234
  newContent.push("```");
380
235
  }
381
236
  }
@@ -1,3 +1,2 @@
1
1
  export * from "./harness.js";
2
2
  export * from "./snippet-extractor.js";
3
- export * from "./test-host.js";
@@ -92,7 +92,7 @@ export interface LanguageConfiguration {
92
92
 
93
93
  await Parser.init();
94
94
 
95
- export function createSnipperExtractor(
95
+ export function createSnippetExtractor(
96
96
  languageConfiguration: LanguageConfiguration,
97
97
  ): SnippetExtractor {
98
98
  return new SnippetExtractorImpl(languageConfiguration);
@@ -2,14 +2,14 @@ import { d } from "@alloy-js/core/testing";
2
2
  import { beforeEach, describe, expect, it } from "vitest";
3
3
  import {
4
4
  createCSharpExtractorConfig,
5
- createSnipperExtractor,
5
+ createSnippetExtractor,
6
6
  type SnippetExtractor,
7
7
  } from "../../src/testing/index.js";
8
8
  describe("C# Snippet Extractor", () => {
9
9
  let extractor: SnippetExtractor;
10
10
 
11
11
  beforeEach(async () => {
12
- extractor = createSnipperExtractor(await createCSharpExtractorConfig());
12
+ extractor = createSnippetExtractor(await createCSharpExtractorConfig());
13
13
  });
14
14
 
15
15
  it("should extract a class", () => {
@@ -66,7 +66,7 @@ describe("C# Snippet Extractor - Enums", () => {
66
66
  let extractor: SnippetExtractor;
67
67
 
68
68
  beforeEach(async () => {
69
- extractor = createSnipperExtractor(await createCSharpExtractorConfig());
69
+ extractor = createSnippetExtractor(await createCSharpExtractorConfig());
70
70
  });
71
71
 
72
72
  it("should extract a basic enum", async () => {
@@ -2,7 +2,7 @@ import { d } from "@alloy-js/core/testing";
2
2
  import { beforeEach, describe, expect, it } from "vitest";
3
3
  import {
4
4
  createJavaExtractorConfig,
5
- createSnipperExtractor,
5
+ createSnippetExtractor,
6
6
  type SnippetExtractor,
7
7
  } from "../../src/testing/index.js";
8
8
 
@@ -10,7 +10,7 @@ describe("Java Snippet Extractor", () => {
10
10
  let extractor: SnippetExtractor;
11
11
 
12
12
  beforeEach(async () => {
13
- extractor = createSnipperExtractor(await createJavaExtractorConfig());
13
+ extractor = createSnippetExtractor(await createJavaExtractorConfig());
14
14
  });
15
15
 
16
16
  it("should extract a class", () => {
@@ -67,7 +67,7 @@ describe("Java Snippet Extractor - Enums", () => {
67
67
  let extractor: SnippetExtractor;
68
68
 
69
69
  beforeEach(async () => {
70
- extractor = createSnipperExtractor(await createJavaExtractorConfig());
70
+ extractor = createSnippetExtractor(await createJavaExtractorConfig());
71
71
  });
72
72
 
73
73
  it("should extract a basic enum", async () => {
@@ -2,7 +2,7 @@ import { d } from "@alloy-js/core/testing";
2
2
  import { beforeEach, describe, expect, it } from "vitest";
3
3
  import {
4
4
  createPythonExtractorConfig,
5
- createSnipperExtractor,
5
+ createSnippetExtractor,
6
6
  type SnippetExtractor,
7
7
  } from "../../src/testing/index.js";
8
8
 
@@ -10,7 +10,7 @@ describe("Python Snippet Extractor", () => {
10
10
  let extractor: SnippetExtractor;
11
11
 
12
12
  beforeEach(async () => {
13
- extractor = createSnipperExtractor(await createPythonExtractorConfig());
13
+ extractor = createSnippetExtractor(await createPythonExtractorConfig());
14
14
  });
15
15
 
16
16
  it("should extract a class", () => {
@@ -1,7 +1,7 @@
1
1
  import { d } from "@alloy-js/core/testing";
2
2
  import { beforeEach, describe, expect, it } from "vitest";
3
3
  import {
4
- createSnipperExtractor,
4
+ createSnippetExtractor,
5
5
  createTypeScriptExtractorConfig,
6
6
  type SnippetExtractor,
7
7
  } from "../../src/testing/index.js";
@@ -9,7 +9,7 @@ import {
9
9
  describe("TypeScript Snippet Extractor", () => {
10
10
  let extractor: SnippetExtractor;
11
11
  beforeEach(async () => {
12
- extractor = createSnipperExtractor(await createTypeScriptExtractorConfig());
12
+ extractor = createSnippetExtractor(await createTypeScriptExtractorConfig());
13
13
  });
14
14
 
15
15
  it("should extract a class", async () => {
@@ -138,7 +138,7 @@ describe("TypeScript Snippet Extractor", () => {
138
138
  describe("TypeScript Snippet Extractor - Enums", () => {
139
139
  let extractor: SnippetExtractor;
140
140
  beforeEach(async () => {
141
- extractor = createSnipperExtractor(await createTypeScriptExtractorConfig());
141
+ extractor = createSnippetExtractor(await createTypeScriptExtractorConfig());
142
142
  });
143
143
 
144
144
  it("should extract a basic enum", async () => {
@@ -1,8 +0,0 @@
1
- import type { Diagnostic } from "@typespec/compiler";
2
- import { type TypeSpecTestLibrary } from "@typespec/compiler/testing";
3
- export interface EmittedFile {
4
- path: string;
5
- content: string;
6
- }
7
- export declare function emitWithDiagnostics(testLibrary: TypeSpecTestLibrary, emitterOutputDir: string, code: string): Promise<[EmittedFile[], readonly Diagnostic[]]>;
8
- //# sourceMappingURL=test-host.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-host.d.ts","sourceRoot":"","sources":["../../../../src/testing/scenario-test/test-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AAKpC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AA4BD,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,mBAAmB,EAChC,gBAAgB,EAAE,MAAM,EACxB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,SAAS,UAAU,EAAE,CAAC,CAAC,CAOjD"}
@@ -1,49 +0,0 @@
1
- import { createTestHost, createTestWrapper } from "@typespec/compiler/testing";
2
- import { HttpTestLibrary } from "@typespec/http/testing";
3
- import { RestTestLibrary } from "@typespec/rest/testing";
4
- import { join, relative } from "path";
5
- async function createEmitterTestRunner(testLibrary, options = {}) {
6
- const libraries = options.testHostConfig?.libraries ?? [testLibrary, HttpTestLibrary, RestTestLibrary];
7
- const host = await createTestHost({
8
- libraries
9
- });
10
- return createTestWrapper(host, {
11
- autoImports: options.autoImports ?? ["@typespec/http", "@typespec/rest"],
12
- autoUsings: options.autoUsings ?? ["TypeSpec.Http", "TypeSpec.Rest"],
13
- compilerOptions: options.compilerOptions ?? {
14
- noEmit: false,
15
- emit: [testLibrary.name]
16
- }
17
- });
18
- }
19
- export async function emitWithDiagnostics(testLibrary, emitterOutputDir, code) {
20
- const runner = await createEmitterTestRunner(testLibrary);
21
- await runner.compileAndDiagnose(code, {
22
- outputDir: "tsp-output"
23
- });
24
- const result = await readFilesRecursively(emitterOutputDir, emitterOutputDir, runner);
25
- return [result, runner.program.diagnostics];
26
- }
27
- async function readFilesRecursively(currentDir, emitterOutputDir, runner) {
28
- const entries = await runner.program.host.readDir(currentDir);
29
- const result = [];
30
- for (const entry of entries) {
31
- const fullPath = join(currentDir, entry);
32
- const stat = await runner.program.host.stat(fullPath);
33
- if (stat.isDirectory()) {
34
- // Recursively read files in the directory
35
- const nestedFiles = await readFilesRecursively(fullPath, emitterOutputDir, runner);
36
- result.push(...nestedFiles);
37
- } else if (stat.isFile()) {
38
- // Read the file
39
- // Read the file and store it with a relative path
40
- const relativePath = relative(emitterOutputDir, fullPath);
41
- const fileContent = await runner.program.host.readFile(fullPath);
42
- result.push({
43
- path: relativePath,
44
- content: fileContent.text
45
- });
46
- }
47
- }
48
- return result;
49
- }