microsoft/typespec

Public

mirrored from https://github.com/microsoft/typespecAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
4d92e0825d81d6432d29b0fade7b161a2e2aafb7

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

packages/compiler/testing/expect.ts

159lines · modecode

1import assert, { fail, match, strictEqual } from "assert";
2import { Diagnostic, formatDiagnostic, getSourceLocation, NoTarget, Type } from "../core/index.js";
3import { isArray } from "../core/util.js";
4import { resolveVirtualPath } from "./test-host.js";
5
6/**
7 * Assert there is no diagnostics.
8 * @param diagnostics Diagnostics
9 */
10export function expectDiagnosticEmpty(diagnostics: readonly Diagnostic[]) {
11 if (diagnostics.length > 0) {
12 assert.fail(`Unexpected diagnostics:\n${formatDiagnostics(diagnostics)}`);
13 }
14}
15
16function formatDiagnostics(diagnostics: readonly Diagnostic[]) {
17 return diagnostics.map(formatDiagnostic).join("\n");
18}
19/**
20 * Condition to match
21 */
22export interface DiagnosticMatch {
23 /**
24 * Match the code.
25 */
26 code?: string;
27
28 /**
29 * Match the message.
30 */
31 message?: string | RegExp;
32
33 /**
34 * Match the severity.
35 */
36 severity?: "error" | "warning";
37
38 /**
39 * Name of the file for this diagnostic.
40 */
41 file?: string | RegExp;
42
43 /**
44 * Start position of the diagnostic
45 */
46 pos?: number;
47
48 /**
49 * End position of the diagnostic
50 */
51 end?: number;
52}
53
54/**
55 * Validate the diagnostic array contains exactly the given diagnostics.
56 * @param diagnostics Array of the diagnostics
57 */
58export function expectDiagnostics(
59 diagnostics: readonly Diagnostic[],
60 match: DiagnosticMatch | DiagnosticMatch[],
61 options = {
62 strict: true,
63 }
64) {
65 const array = isArray(match) ? match : [match];
66
67 if (options.strict && array.length !== diagnostics.length) {
68 assert.fail(
69 `Expected ${array.length} diagnostics but found ${diagnostics.length}:\n ${formatDiagnostics(
70 diagnostics
71 )}`
72 );
73 }
74 for (let i = 0; i < array.length; i++) {
75 const diagnostic = diagnostics[i];
76 const expectation = array[i];
77 const sep = "-".repeat(100);
78 const message = `Diagnostics found:\n${sep}\n${formatDiagnostics(diagnostics)}\n${sep}`;
79 if (expectation.code !== undefined) {
80 strictEqual(
81 diagnostic.code,
82 expectation.code,
83 `Diagnostic at index ${i} has non matching code.\n${message}`
84 );
85 }
86
87 if (expectation.message !== undefined) {
88 matchStrOrRegex(
89 diagnostic.message,
90 expectation.message,
91 `Diagnostic at index ${i} has non matching message.\n${message}`
92 );
93 }
94 if (expectation.severity !== undefined) {
95 strictEqual(
96 diagnostic.severity,
97 expectation.severity,
98 `Diagnostic at index ${i} has non matching severity.\n${message}`
99 );
100 }
101 if (
102 expectation.file !== undefined ||
103 expectation.pos !== undefined ||
104 expectation.end !== undefined
105 ) {
106 if (diagnostic.target === NoTarget) {
107 fail(`Diagnostics at index ${i} expected to have a target.\n${message}`);
108 }
109 const source = getSourceLocation(diagnostic.target);
110
111 if (expectation.file !== undefined) {
112 matchStrOrRegex(
113 source.file.path,
114 typeof expectation.file === "string"
115 ? resolveVirtualPath(expectation.file)
116 : expectation.file,
117 `Diagnostics at index ${i} has non matching file.\n${message}`
118 );
119 }
120
121 if (expectation.pos !== undefined) {
122 strictEqual(
123 source.pos,
124 expectation.pos,
125 `Diagnostic at index ${i} has non-matching start position.`
126 );
127 }
128
129 if (expectation.end !== undefined) {
130 strictEqual(
131 source.end,
132 expectation.end,
133 `Diagnostic at index ${i} has non-matching end position.`
134 );
135 }
136 }
137 }
138}
139
140function matchStrOrRegex(value: string, expectation: string | RegExp, assertMessage: string) {
141 if (typeof expectation === "string") {
142 strictEqual(value, expectation, assertMessage);
143 } else {
144 match(value, expectation, assertMessage);
145 }
146}
147
148/**
149 * Replacement for strictEqual for identity check against types. strictEqual
150 * does a really slow deep comparison for the error message when it fails in
151 * order to show the diff. Just show the type names instead.
152 */
153export function expectIdenticalTypes(a: Type, b: Type) {
154 if (a !== b) {
155 // Note: `||` instead of `??` is intentional to allow for anonymous types with name = `""`
156 strictEqual((a as any).name || "(anonymous type 1)", (b as any).name || "(anonymous type 2)");
157 fail(`Types are both named "${(a as any).name}", but they are not identical.`);
158 }
159}
160