@flint.fyi/rule-tester 0.16.3 → 0.17.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.
- package/dist/index.d.mts +81 -0
- package/dist/index.mjs +245 -0
- package/package.json +11 -11
- package/lib/RuleTester.d.ts +0 -42
- package/lib/RuleTester.js +0 -99
- package/lib/createOutput.d.ts +0 -5
- package/lib/createOutput.js +0 -10
- package/lib/createReportSnapshot.d.ts +0 -3
- package/lib/createReportSnapshot.js +0 -78
- package/lib/index.d.ts +0 -3
- package/lib/index.js +0 -2
- package/lib/normalizeTestCase.d.ts +0 -6
- package/lib/normalizeTestCase.js +0 -10
- package/lib/predicates.d.ts +0 -3
- package/lib/predicates.js +0 -8
- package/lib/resolveReportedSuggestions.d.ts +0 -15
- package/lib/resolveReportedSuggestions.js +0 -34
- package/lib/runTestCaseRule.d.ts +0 -9
- package/lib/runTestCaseRule.js +0 -40
- package/lib/types.d.ts +0 -43
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { AnyOptionalSchema, AnyRule, InferredInputObject, RuleAbout } from "@flint.fyi/core";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
interface InvalidTestCase<Options extends object | undefined = object | undefined> extends TestCase<Options> {
|
|
5
|
+
output?: string;
|
|
6
|
+
snapshot: string;
|
|
7
|
+
suggestions?: TestSuggestion[];
|
|
8
|
+
}
|
|
9
|
+
interface TestCase<Options extends object | undefined = object | undefined> {
|
|
10
|
+
code: string;
|
|
11
|
+
fileName?: string | undefined;
|
|
12
|
+
files?: Record<string, string> | undefined;
|
|
13
|
+
name?: string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Run only this test case. Useful for debugging.
|
|
16
|
+
*
|
|
17
|
+
* Do not commit code with this flag set.
|
|
18
|
+
*/
|
|
19
|
+
only?: boolean;
|
|
20
|
+
options?: Options | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Skip running this test case. Useful for work-in-progress tests.
|
|
23
|
+
*
|
|
24
|
+
* Do not commit code with this flag set.
|
|
25
|
+
*/
|
|
26
|
+
skip?: boolean;
|
|
27
|
+
}
|
|
28
|
+
type TestSuggestion = TestSuggestionForFile | TestSuggestionForFiles;
|
|
29
|
+
interface TestSuggestionFileCase {
|
|
30
|
+
original: string;
|
|
31
|
+
updated: string;
|
|
32
|
+
}
|
|
33
|
+
interface TestSuggestionForFile {
|
|
34
|
+
id: string;
|
|
35
|
+
updated: string;
|
|
36
|
+
}
|
|
37
|
+
interface TestSuggestionForFiles {
|
|
38
|
+
files: Record<string, TestSuggestionFileCase[]>;
|
|
39
|
+
id: string;
|
|
40
|
+
}
|
|
41
|
+
type ValidTestCase<Options extends object | undefined> = string | ValidTestCaseObject<Options>;
|
|
42
|
+
type ValidTestCaseObject<Options extends object | undefined> = TestCase<Options>;
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/RuleTester.d.ts
|
|
45
|
+
interface RuleTesterDefaults {
|
|
46
|
+
fileName?: string;
|
|
47
|
+
files?: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
interface RuleTesterOptions {
|
|
50
|
+
defaults?: RuleTesterDefaults;
|
|
51
|
+
describe?: TesterSetupDescribe;
|
|
52
|
+
diskBackedFSRoot?: string;
|
|
53
|
+
it?: TesterSetupIt;
|
|
54
|
+
only?: TesterSetupIt;
|
|
55
|
+
scope?: Record<string, unknown>;
|
|
56
|
+
skip?: TesterSetupIt;
|
|
57
|
+
}
|
|
58
|
+
interface TestCases<Options extends object | undefined> {
|
|
59
|
+
invalid: InvalidTestCase<Options>[];
|
|
60
|
+
valid: ValidTestCase<Options>[];
|
|
61
|
+
}
|
|
62
|
+
type TesterSetupDescribe = (description: string, setup: () => void) => void;
|
|
63
|
+
type TesterSetupIt = (description: string, setup: () => Promise<void>) => void;
|
|
64
|
+
declare class RuleTester {
|
|
65
|
+
#private;
|
|
66
|
+
constructor({
|
|
67
|
+
defaults,
|
|
68
|
+
describe,
|
|
69
|
+
diskBackedFSRoot,
|
|
70
|
+
it,
|
|
71
|
+
only,
|
|
72
|
+
scope,
|
|
73
|
+
skip
|
|
74
|
+
}?: RuleTesterOptions);
|
|
75
|
+
describe<OptionsSchema extends AnyOptionalSchema | undefined>(rule: AnyRule<RuleAbout, OptionsSchema>, {
|
|
76
|
+
invalid,
|
|
77
|
+
valid
|
|
78
|
+
}: TestCases<InferredInputObject<OptionsSchema>>): void;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { type InvalidTestCase, RuleTester, type TestCase, type TestSuggestion, type TestSuggestionFileCase, type TestSuggestionForFile, type TestSuggestionForFiles, type ValidTestCase, type ValidTestCaseObject };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CachedFactory } from "cached-factory";
|
|
4
|
+
import { applyChangesToText, createDiskBackedLinterHost, createEphemeralLinterHost, createVFSLinterHost, formatReport, getPositionOfColumnAndLine, isSuggestionForFiles, parseOptions, processRuleReport } from "@flint.fyi/core";
|
|
5
|
+
import { isTruthy, normalizePath, nullThrows, pathKey } from "@flint.fyi/utils";
|
|
6
|
+
//#region src/createOutput.ts
|
|
7
|
+
function createOutput(reports, testCaseNormalized) {
|
|
8
|
+
const changes = reports.filter((report) => report.fix !== void 0).flatMap((report) => report.fix);
|
|
9
|
+
return changes.length ? applyChangesToText(changes, testCaseNormalized.code) : void 0;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/createReportSnapshot.ts
|
|
13
|
+
function createReportSnapshot(sourceText, reports) {
|
|
14
|
+
let result = sourceText;
|
|
15
|
+
for (const report of reports.toReversed()) result = createReportSnapshotAt(result, report);
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
function createReportSnapshotAt(sourceText, report) {
|
|
19
|
+
const { begin, end } = getDisplayedRange(sourceText, report.range);
|
|
20
|
+
const lineStartIndex = begin.raw - begin.column;
|
|
21
|
+
const lineEndIndex = getLineBounds(sourceText, end.line).end;
|
|
22
|
+
const lines = sourceText.slice(lineStartIndex, lineEndIndex).split("\n");
|
|
23
|
+
const output = [];
|
|
24
|
+
for (let i = begin.line; i <= end.line; i++) {
|
|
25
|
+
const line = nullThrows(lines[i - begin.line], "Line is expected to be present by the loop condition");
|
|
26
|
+
output.push(line);
|
|
27
|
+
const prevLineIndent = /^[\t ]*/.exec(line)?.[0] ?? "";
|
|
28
|
+
if (i === begin.line) {
|
|
29
|
+
const indent = prevLineIndent.padEnd(begin.column, " ");
|
|
30
|
+
const squiggleEnd = begin.line === end.line ? end.column : line.length;
|
|
31
|
+
output.push(indent.padEnd(squiggleEnd, "~"));
|
|
32
|
+
for (const errorMessageLine of formatReport(report.data, report.message.primary).split("\n")) output.push(indent + errorMessageLine);
|
|
33
|
+
} else {
|
|
34
|
+
const squiggleEnd = i === end.line ? end.column : line.length;
|
|
35
|
+
output.push(prevLineIndent.padEnd(squiggleEnd, "~"));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return sourceText.slice(0, lineStartIndex) + output.join("\n") + sourceText.slice(lineEndIndex);
|
|
39
|
+
}
|
|
40
|
+
function getDisplayedRange(sourceText, { begin, end }) {
|
|
41
|
+
if (end.column > 0 || end.line === begin.line) return {
|
|
42
|
+
begin,
|
|
43
|
+
end
|
|
44
|
+
};
|
|
45
|
+
const previousLine = getLineBounds(sourceText, end.line - 1);
|
|
46
|
+
if (begin.line === previousLine.line && previousLine.text === "") {
|
|
47
|
+
const currentLine = getLineBounds(sourceText, end.line);
|
|
48
|
+
if (currentLine.text !== "") return {
|
|
49
|
+
begin: {
|
|
50
|
+
column: 0,
|
|
51
|
+
line: currentLine.line,
|
|
52
|
+
raw: currentLine.start
|
|
53
|
+
},
|
|
54
|
+
end: {
|
|
55
|
+
column: 1,
|
|
56
|
+
line: currentLine.line,
|
|
57
|
+
raw: currentLine.start + 1
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
begin,
|
|
63
|
+
end: {
|
|
64
|
+
column: previousLine.text.length,
|
|
65
|
+
line: previousLine.line,
|
|
66
|
+
raw: previousLine.end
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function getLineBounds(sourceText, line) {
|
|
71
|
+
const start = getPositionOfColumnAndLine(sourceText, {
|
|
72
|
+
column: 0,
|
|
73
|
+
line
|
|
74
|
+
});
|
|
75
|
+
let end = sourceText.indexOf("\n", start);
|
|
76
|
+
if (end < 0) end = sourceText.length;
|
|
77
|
+
return {
|
|
78
|
+
end,
|
|
79
|
+
line,
|
|
80
|
+
start,
|
|
81
|
+
text: sourceText.slice(start, end)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/normalizeTestCase.ts
|
|
86
|
+
function normalizeTestCase(testCase, fileName) {
|
|
87
|
+
const rv = { ...testCase };
|
|
88
|
+
rv.fileName ??= fileName ?? "file.ts";
|
|
89
|
+
return rv;
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/predicates.ts
|
|
93
|
+
function isTestSuggestionForFiles(suggestion) {
|
|
94
|
+
return "files" in suggestion;
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/resolveReportedSuggestions.ts
|
|
98
|
+
function resolveReportedSuggestions(reports, testCaseNormalized) {
|
|
99
|
+
const suggestionsReported = reports.flatMap((report) => report.suggestions).filter(isTruthy);
|
|
100
|
+
if (!suggestionsReported.length) return;
|
|
101
|
+
return suggestionsReported.map((suggestionReported) => isSuggestionForFiles(suggestionReported) ? {
|
|
102
|
+
files: resolveReportedSuggestionForFiles(suggestionReported, testCaseNormalized),
|
|
103
|
+
id: suggestionReported.id
|
|
104
|
+
} : {
|
|
105
|
+
id: suggestionReported.id,
|
|
106
|
+
updated: applyChangesToText([suggestionReported], testCaseNormalized.code)
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function resolveReportedSuggestionForFiles(suggestionReported, testCaseNormalized) {
|
|
110
|
+
if (!testCaseNormalized.suggestions) return {};
|
|
111
|
+
if (!testCaseNormalized.suggestions.every(isTestSuggestionForFiles)) throw new Error("This test case describes suggestions across files, but the rule is only reporting changes to its own file.");
|
|
112
|
+
return Object.fromEntries(testCaseNormalized.suggestions.flatMap((suggestionExpected) => {
|
|
113
|
+
return Object.entries(suggestionExpected.files).map(([filePath, suggestionCasesExpected]) => {
|
|
114
|
+
return [filePath, suggestionCasesExpected.map((suggestionCaseExpected) => {
|
|
115
|
+
const changes = suggestionReported.files[filePath];
|
|
116
|
+
return {
|
|
117
|
+
original: suggestionCaseExpected.original,
|
|
118
|
+
updated: changes ? applyChangesToText(changes, suggestionCaseExpected.original) : suggestionCaseExpected.original
|
|
119
|
+
};
|
|
120
|
+
})];
|
|
121
|
+
});
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/runTestCaseRule.ts
|
|
126
|
+
async function runTestCaseRule(fileFactories, linterHost, { options, rule }, { code, fileName, files }) {
|
|
127
|
+
const filePathAbsolute = normalizePath(path.resolve(linterHost.getCurrentDirectory(), fileName));
|
|
128
|
+
const caseSensitive = linterHost.isCaseSensitiveFS();
|
|
129
|
+
const targetKey = pathKey(filePathAbsolute, caseSensitive);
|
|
130
|
+
for (const oldFile of linterHost.vfsListFiles().keys()) if (pathKey(oldFile, caseSensitive) !== targetKey) linterHost.vfsDeleteFile(oldFile);
|
|
131
|
+
for (const [name, content] of Object.entries(files ?? {})) {
|
|
132
|
+
const filePath = normalizePath(path.resolve(linterHost.getCurrentDirectory(), name));
|
|
133
|
+
assert.notEqual(filePath, filePathAbsolute, `Expected 'files' not to shadow '${fileName}'`);
|
|
134
|
+
linterHost.vfsUpsertFile(filePath, content);
|
|
135
|
+
}
|
|
136
|
+
linterHost.vfsUpsertFile(filePathAbsolute, code);
|
|
137
|
+
using file = fileFactories.get(rule.language).createFile({
|
|
138
|
+
filePath: fileName,
|
|
139
|
+
filePathAbsolute,
|
|
140
|
+
sourceText: code
|
|
141
|
+
});
|
|
142
|
+
const reports = [];
|
|
143
|
+
const ruleRuntime = await rule.setup({
|
|
144
|
+
host: linterHost,
|
|
145
|
+
report(ruleReport) {
|
|
146
|
+
const processedReport = processRuleReport(file, rule, ruleReport);
|
|
147
|
+
if (processedReport == null) return;
|
|
148
|
+
reports.push(processedReport);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
if (ruleRuntime) {
|
|
152
|
+
rule.language.runFileVisitors(file, options, ruleRuntime);
|
|
153
|
+
await ruleRuntime.teardown?.();
|
|
154
|
+
}
|
|
155
|
+
return reports.toSorted((a, b) => a.range.begin.raw - b.range.begin.raw || a.range.end.raw - b.range.end.raw);
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/RuleTester.ts
|
|
159
|
+
var RuleTester = class {
|
|
160
|
+
#fileFactories;
|
|
161
|
+
#linterHost;
|
|
162
|
+
#testerOptions;
|
|
163
|
+
constructor({ defaults = {}, describe, diskBackedFSRoot, it, only, scope = globalThis, skip } = {}) {
|
|
164
|
+
let baseHost = diskBackedFSRoot != null ? createEphemeralLinterHost(createDiskBackedLinterHost(path.resolve(process.cwd(), diskBackedFSRoot, "_flint-rule-tester-virtual"))) : void 0;
|
|
165
|
+
const { files: defaultFiles = {} } = defaults;
|
|
166
|
+
if (Object.keys(defaultFiles).length) {
|
|
167
|
+
const vfs = createVFSLinterHost(baseHost == null ? { cwd: process.cwd() } : { baseHost });
|
|
168
|
+
for (const [name, content] of Object.entries(defaultFiles)) {
|
|
169
|
+
const filePath = path.resolve(vfs.getCurrentDirectory(), name);
|
|
170
|
+
vfs.vfsUpsertFile(filePath, content);
|
|
171
|
+
}
|
|
172
|
+
baseHost = vfs;
|
|
173
|
+
}
|
|
174
|
+
this.#linterHost = createVFSLinterHost(baseHost == null ? { cwd: process.cwd() } : { baseHost });
|
|
175
|
+
this.#fileFactories = new CachedFactory((language) => language.createFileFactory(this.#linterHost));
|
|
176
|
+
it = defaultTo(it, scope, "it");
|
|
177
|
+
if (!skip && "skip" in it && typeof it.skip === "function") skip = it.skip;
|
|
178
|
+
if (!only && "only" in it && typeof it.only === "function") only = it.only;
|
|
179
|
+
if (!skip) throw new TypeError("RuleTester needs a `skip` function");
|
|
180
|
+
if (!only) throw new TypeError("RuleTester needs a `only` function");
|
|
181
|
+
this.#testerOptions = {
|
|
182
|
+
defaults,
|
|
183
|
+
describe: defaultTo(describe, scope, "describe"),
|
|
184
|
+
it,
|
|
185
|
+
only,
|
|
186
|
+
scope,
|
|
187
|
+
skip
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
describe(rule, { invalid, valid }) {
|
|
191
|
+
this.#testerOptions.describe(rule.about.id, () => {
|
|
192
|
+
this.#testerOptions.describe("invalid", () => {
|
|
193
|
+
for (const testCase of invalid) this.#itInvalidCase(rule, testCase);
|
|
194
|
+
});
|
|
195
|
+
this.#testerOptions.describe("valid", () => {
|
|
196
|
+
for (const testCase of valid) this.#itValidCase(rule, testCase);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
#itInvalidCase(rule, testCase) {
|
|
201
|
+
const testCaseNormalized = normalizeTestCase(testCase, this.#testerOptions.defaults.fileName);
|
|
202
|
+
this.#itTestCase(testCaseNormalized, async () => {
|
|
203
|
+
const reports = await runTestCaseRule(this.#fileFactories, this.#linterHost, {
|
|
204
|
+
options: parseOptions(rule.options, testCase.options),
|
|
205
|
+
rule
|
|
206
|
+
}, testCaseNormalized);
|
|
207
|
+
const actualSnapshot = createReportSnapshot(testCase.code, reports);
|
|
208
|
+
assert.equal(actualSnapshot, testCase.snapshot);
|
|
209
|
+
const actualOutput = createOutput(reports, testCaseNormalized);
|
|
210
|
+
assert.equal(testCase.output, actualOutput, "Expected `output` property to equal:");
|
|
211
|
+
const actualSuggestions = resolveReportedSuggestions(reports, testCaseNormalized);
|
|
212
|
+
assert.deepStrictEqual(actualSuggestions, testCase.suggestions);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
#itTestCase(testCase, setup) {
|
|
216
|
+
let test = testCase.only ? this.#testerOptions.only : this.#testerOptions.it;
|
|
217
|
+
if (testCase.skip) if ("skip" in test && typeof test.skip === "function") test = test.skip;
|
|
218
|
+
else test = this.#testerOptions.skip;
|
|
219
|
+
test(testCase.name ?? ("files" in testCase ? JSON.stringify({
|
|
220
|
+
[testCase.fileName]: testCase.code,
|
|
221
|
+
...testCase.files
|
|
222
|
+
}, null, 2) : testCase.code), () => {
|
|
223
|
+
if (testCase.files != null) assert.notEqual(Object.keys(testCase.files).length, 0, `'files' must have at least one file`);
|
|
224
|
+
return setup();
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
#itValidCase(rule, testCaseRaw) {
|
|
228
|
+
const testCase = typeof testCaseRaw === "string" ? { code: testCaseRaw } : testCaseRaw;
|
|
229
|
+
const testCaseNormalized = normalizeTestCase(testCase, this.#testerOptions.defaults.fileName);
|
|
230
|
+
this.#itTestCase(testCaseNormalized, async () => {
|
|
231
|
+
const reports = await runTestCaseRule(this.#fileFactories, this.#linterHost, {
|
|
232
|
+
options: parseOptions(rule.options, testCase.options),
|
|
233
|
+
rule
|
|
234
|
+
}, testCaseNormalized);
|
|
235
|
+
if (reports.length) assert.deepStrictEqual(createReportSnapshot(testCaseNormalized.code, reports), testCaseNormalized.code);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
function defaultTo(provided, scope, scopeKey) {
|
|
240
|
+
if (provided) return provided;
|
|
241
|
+
if (scopeKey in scope && typeof scope[scopeKey] === "function") return scope[scopeKey];
|
|
242
|
+
throw new Error(`No ${scopeKey} function found`);
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
export { RuleTester };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flint.fyi/rule-tester",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "[Experimental] Rule unit tests for Flint.",
|
|
5
5
|
"homepage": "https://flint.fyi",
|
|
6
6
|
"repository": {
|
|
@@ -10,26 +10,26 @@
|
|
|
10
10
|
},
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"author": {
|
|
13
|
-
"name": "
|
|
14
|
-
"
|
|
13
|
+
"name": "Flint Team",
|
|
14
|
+
"url": "https://flint.fyi/team"
|
|
15
15
|
},
|
|
16
16
|
"sideEffects": false,
|
|
17
17
|
"type": "module",
|
|
18
18
|
"exports": {
|
|
19
|
-
".": "./
|
|
19
|
+
".": "./dist/index.mjs"
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
|
-
"
|
|
23
|
-
"!lib/**/*.map"
|
|
22
|
+
"dist/"
|
|
24
23
|
],
|
|
25
24
|
"dependencies": {
|
|
26
|
-
"cached-factory": "^0.
|
|
27
|
-
"@flint.fyi/core": "^0.
|
|
28
|
-
"@flint.fyi/utils": "^0.
|
|
25
|
+
"cached-factory": "^0.3.0",
|
|
26
|
+
"@flint.fyi/core": "^0.24.0",
|
|
27
|
+
"@flint.fyi/utils": "^0.15.0"
|
|
29
28
|
},
|
|
30
29
|
"devDependencies": {
|
|
31
|
-
"tsdown": "0.
|
|
32
|
-
"vitest": "4.1.0"
|
|
30
|
+
"tsdown": "0.22.3",
|
|
31
|
+
"vitest": "4.1.0",
|
|
32
|
+
"@flint.fyi/build": "^0.1.0"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
|
35
35
|
"node": ">=24.0.0"
|
package/lib/RuleTester.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { InvalidTestCase, ValidTestCase } from "./types.js";
|
|
2
|
-
import { AnyOptionalSchema, AnyRule, InferredInputObject, RuleAbout } from "@flint.fyi/core";
|
|
3
|
-
|
|
4
|
-
//#region src/RuleTester.d.ts
|
|
5
|
-
interface RuleTesterDefaults {
|
|
6
|
-
fileName?: string;
|
|
7
|
-
files?: Record<string, string>;
|
|
8
|
-
}
|
|
9
|
-
interface RuleTesterOptions {
|
|
10
|
-
defaults?: RuleTesterDefaults;
|
|
11
|
-
describe?: TesterSetupDescribe;
|
|
12
|
-
diskBackedFSRoot?: string;
|
|
13
|
-
it?: TesterSetupIt;
|
|
14
|
-
only?: TesterSetupIt;
|
|
15
|
-
scope?: Record<string, unknown>;
|
|
16
|
-
skip?: TesterSetupIt;
|
|
17
|
-
}
|
|
18
|
-
interface TestCases<Options extends object | undefined> {
|
|
19
|
-
invalid: InvalidTestCase<Options>[];
|
|
20
|
-
valid: ValidTestCase<Options>[];
|
|
21
|
-
}
|
|
22
|
-
type TesterSetupDescribe = (description: string, setup: () => void) => void;
|
|
23
|
-
type TesterSetupIt = (description: string, setup: () => Promise<void>) => void;
|
|
24
|
-
declare class RuleTester {
|
|
25
|
-
#private;
|
|
26
|
-
constructor({
|
|
27
|
-
defaults,
|
|
28
|
-
describe,
|
|
29
|
-
diskBackedFSRoot,
|
|
30
|
-
it,
|
|
31
|
-
only,
|
|
32
|
-
scope,
|
|
33
|
-
skip
|
|
34
|
-
}?: RuleTesterOptions);
|
|
35
|
-
describe<OptionsSchema extends AnyOptionalSchema | undefined>(rule: AnyRule<RuleAbout, OptionsSchema>, {
|
|
36
|
-
invalid,
|
|
37
|
-
valid
|
|
38
|
-
}: TestCases<InferredInputObject<OptionsSchema>>): void;
|
|
39
|
-
}
|
|
40
|
-
//#endregion
|
|
41
|
-
export { RuleTester };
|
|
42
|
-
//# sourceMappingURL=RuleTester.d.ts.map
|
package/lib/RuleTester.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { createOutput } from "./createOutput.js";
|
|
2
|
-
import { createReportSnapshot } from "./createReportSnapshot.js";
|
|
3
|
-
import { normalizeTestCase } from "./normalizeTestCase.js";
|
|
4
|
-
import { resolveReportedSuggestions } from "./resolveReportedSuggestions.js";
|
|
5
|
-
import { runTestCaseRule } from "./runTestCaseRule.js";
|
|
6
|
-
import { createDiskBackedLinterHost, createEphemeralLinterHost, createVFSLinterHost, parseOptions } from "@flint.fyi/core";
|
|
7
|
-
import { CachedFactory } from "cached-factory";
|
|
8
|
-
import assert from "node:assert/strict";
|
|
9
|
-
import path from "node:path";
|
|
10
|
-
//#region src/RuleTester.ts
|
|
11
|
-
var RuleTester = class {
|
|
12
|
-
#fileFactories;
|
|
13
|
-
#linterHost;
|
|
14
|
-
#testerOptions;
|
|
15
|
-
constructor({ defaults = {}, describe, diskBackedFSRoot, it, only, scope = globalThis, skip } = {}) {
|
|
16
|
-
let baseHost = diskBackedFSRoot != null ? createEphemeralLinterHost(createDiskBackedLinterHost(path.resolve(process.cwd(), diskBackedFSRoot, "_flint-rule-tester-virtual"))) : void 0;
|
|
17
|
-
const { files: defaultFiles = {} } = defaults;
|
|
18
|
-
if (Object.keys(defaultFiles).length) {
|
|
19
|
-
const vfs = createVFSLinterHost(baseHost == null ? { cwd: process.cwd() } : { baseHost });
|
|
20
|
-
for (const [name, content] of Object.entries(defaultFiles)) {
|
|
21
|
-
const filePath = path.resolve(vfs.getCurrentDirectory(), name);
|
|
22
|
-
vfs.vfsUpsertFile(filePath, content);
|
|
23
|
-
}
|
|
24
|
-
baseHost = vfs;
|
|
25
|
-
}
|
|
26
|
-
this.#linterHost = createVFSLinterHost(baseHost == null ? { cwd: process.cwd() } : { baseHost });
|
|
27
|
-
this.#fileFactories = new CachedFactory((language) => language.createFileFactory(this.#linterHost));
|
|
28
|
-
it = defaultTo(it, scope, "it");
|
|
29
|
-
if (!skip && "skip" in it && typeof it.skip === "function") skip = it.skip;
|
|
30
|
-
if (!only && "only" in it && typeof it.only === "function") only = it.only;
|
|
31
|
-
if (!skip) throw new TypeError("RuleTester needs a `skip` function");
|
|
32
|
-
if (!only) throw new TypeError("RuleTester needs a `only` function");
|
|
33
|
-
this.#testerOptions = {
|
|
34
|
-
defaults,
|
|
35
|
-
describe: defaultTo(describe, scope, "describe"),
|
|
36
|
-
it,
|
|
37
|
-
only,
|
|
38
|
-
scope,
|
|
39
|
-
skip
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
describe(rule, { invalid, valid }) {
|
|
43
|
-
this.#testerOptions.describe(rule.about.id, () => {
|
|
44
|
-
this.#testerOptions.describe("invalid", () => {
|
|
45
|
-
for (const testCase of invalid) this.#itInvalidCase(rule, testCase);
|
|
46
|
-
});
|
|
47
|
-
this.#testerOptions.describe("valid", () => {
|
|
48
|
-
for (const testCase of valid) this.#itValidCase(rule, testCase);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
#itInvalidCase(rule, testCase) {
|
|
53
|
-
const testCaseNormalized = normalizeTestCase(testCase, this.#testerOptions.defaults.fileName);
|
|
54
|
-
this.#itTestCase(testCaseNormalized, async () => {
|
|
55
|
-
const reports = await runTestCaseRule(this.#fileFactories, this.#linterHost, {
|
|
56
|
-
options: parseOptions(rule.options, testCase.options),
|
|
57
|
-
rule
|
|
58
|
-
}, testCaseNormalized);
|
|
59
|
-
const actualSnapshot = createReportSnapshot(testCase.code, reports);
|
|
60
|
-
assert.equal(actualSnapshot, testCase.snapshot);
|
|
61
|
-
const actualOutput = createOutput(reports, testCaseNormalized);
|
|
62
|
-
assert.equal(testCase.output, actualOutput, "Expected `output` property to equal:");
|
|
63
|
-
const actualSuggestions = resolveReportedSuggestions(reports, testCaseNormalized);
|
|
64
|
-
assert.deepStrictEqual(actualSuggestions, testCase.suggestions);
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
#itTestCase(testCase, setup) {
|
|
68
|
-
let test = testCase.only ? this.#testerOptions.only : this.#testerOptions.it;
|
|
69
|
-
if (testCase.skip) if ("skip" in test && typeof test.skip === "function") test = test.skip;
|
|
70
|
-
else test = this.#testerOptions.skip;
|
|
71
|
-
test(testCase.name ?? ("files" in testCase ? JSON.stringify({
|
|
72
|
-
[testCase.fileName]: testCase.code,
|
|
73
|
-
...testCase.files
|
|
74
|
-
}, null, 2) : testCase.code), () => {
|
|
75
|
-
if (testCase.files != null) assert.notEqual(Object.keys(testCase.files), 0, `'files' must have at least one file`);
|
|
76
|
-
return setup();
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
#itValidCase(rule, testCaseRaw) {
|
|
80
|
-
const testCase = typeof testCaseRaw === "string" ? { code: testCaseRaw } : testCaseRaw;
|
|
81
|
-
const testCaseNormalized = normalizeTestCase(testCase, this.#testerOptions.defaults.fileName);
|
|
82
|
-
this.#itTestCase(testCaseNormalized, async () => {
|
|
83
|
-
const reports = await runTestCaseRule(this.#fileFactories, this.#linterHost, {
|
|
84
|
-
options: parseOptions(rule.options, testCase.options),
|
|
85
|
-
rule
|
|
86
|
-
}, testCaseNormalized);
|
|
87
|
-
if (reports.length) assert.deepStrictEqual(createReportSnapshot(testCaseNormalized.code, reports), testCaseNormalized.code);
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
function defaultTo(provided, scope, scopeKey) {
|
|
92
|
-
if (provided) return provided;
|
|
93
|
-
if (scopeKey in scope && typeof scope[scopeKey] === "function") return scope[scopeKey];
|
|
94
|
-
throw new Error(`No ${scopeKey} function found`);
|
|
95
|
-
}
|
|
96
|
-
//#endregion
|
|
97
|
-
export { RuleTester };
|
|
98
|
-
|
|
99
|
-
//# sourceMappingURL=RuleTester.js.map
|
package/lib/createOutput.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { type NormalizedReport } from "@flint.fyi/core";
|
|
2
|
-
import type { TestCaseNormalized } from "./normalizeTestCase.ts";
|
|
3
|
-
import type { InvalidTestCase } from "./types.ts";
|
|
4
|
-
export declare function createOutput(reports: NormalizedReport[], testCaseNormalized: InvalidTestCase & TestCaseNormalized): string | undefined;
|
|
5
|
-
//# sourceMappingURL=createOutput.d.ts.map
|
package/lib/createOutput.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { applyChangesToText } from "@flint.fyi/core";
|
|
2
|
-
//#region src/createOutput.ts
|
|
3
|
-
function createOutput(reports, testCaseNormalized) {
|
|
4
|
-
const changes = reports.filter((report) => report.fix !== void 0).flatMap((report) => report.fix);
|
|
5
|
-
return changes.length ? applyChangesToText(changes, testCaseNormalized.code) : void 0;
|
|
6
|
-
}
|
|
7
|
-
//#endregion
|
|
8
|
-
export { createOutput };
|
|
9
|
-
|
|
10
|
-
//# sourceMappingURL=createOutput.js.map
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { formatReport, getPositionOfColumnAndLine } from "@flint.fyi/core";
|
|
2
|
-
import { nullThrows } from "@flint.fyi/utils";
|
|
3
|
-
//#region src/createReportSnapshot.ts
|
|
4
|
-
function createReportSnapshot(sourceText, reports) {
|
|
5
|
-
let result = sourceText;
|
|
6
|
-
for (const report of reports.toReversed()) result = createReportSnapshotAt(result, report);
|
|
7
|
-
return result;
|
|
8
|
-
}
|
|
9
|
-
function createReportSnapshotAt(sourceText, report) {
|
|
10
|
-
const { begin, end } = getDisplayedRange(sourceText, report.range);
|
|
11
|
-
const lineStartIndex = begin.raw - begin.column;
|
|
12
|
-
const lineEndIndex = getLineBounds(sourceText, end.line).end;
|
|
13
|
-
const lines = sourceText.slice(lineStartIndex, lineEndIndex).split("\n");
|
|
14
|
-
const output = [];
|
|
15
|
-
for (let i = begin.line; i <= end.line; i++) {
|
|
16
|
-
const line = nullThrows(lines[i - begin.line], "Line is expected to be present by the loop condition");
|
|
17
|
-
output.push(line);
|
|
18
|
-
const prevLineIndent = /^[\t ]*/.exec(line)?.[0] ?? "";
|
|
19
|
-
if (i === begin.line) {
|
|
20
|
-
const indent = prevLineIndent.padEnd(begin.column, " ");
|
|
21
|
-
const squiggleEnd = begin.line === end.line ? end.column : line.length;
|
|
22
|
-
output.push(indent.padEnd(squiggleEnd, "~"));
|
|
23
|
-
for (const errorMessageLine of formatReport(report.data, report.message.primary).split("\n")) output.push(indent + errorMessageLine);
|
|
24
|
-
} else {
|
|
25
|
-
const squiggleEnd = i === end.line ? end.column : line.length;
|
|
26
|
-
output.push(prevLineIndent.padEnd(squiggleEnd, "~"));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return sourceText.slice(0, lineStartIndex) + output.join("\n") + sourceText.slice(lineEndIndex);
|
|
30
|
-
}
|
|
31
|
-
function getDisplayedRange(sourceText, { begin, end }) {
|
|
32
|
-
if (end.column > 0 || end.line === begin.line) return {
|
|
33
|
-
begin,
|
|
34
|
-
end
|
|
35
|
-
};
|
|
36
|
-
const previousLine = getLineBounds(sourceText, end.line - 1);
|
|
37
|
-
if (begin.line === previousLine.line && previousLine.text === "") {
|
|
38
|
-
const currentLine = getLineBounds(sourceText, end.line);
|
|
39
|
-
if (currentLine.text !== "") return {
|
|
40
|
-
begin: {
|
|
41
|
-
column: 0,
|
|
42
|
-
line: currentLine.line,
|
|
43
|
-
raw: currentLine.start
|
|
44
|
-
},
|
|
45
|
-
end: {
|
|
46
|
-
column: 1,
|
|
47
|
-
line: currentLine.line,
|
|
48
|
-
raw: currentLine.start + 1
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
begin,
|
|
54
|
-
end: {
|
|
55
|
-
column: previousLine.text.length,
|
|
56
|
-
line: previousLine.line,
|
|
57
|
-
raw: previousLine.end
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function getLineBounds(sourceText, line) {
|
|
62
|
-
const start = getPositionOfColumnAndLine(sourceText, {
|
|
63
|
-
column: 0,
|
|
64
|
-
line
|
|
65
|
-
});
|
|
66
|
-
let end = sourceText.indexOf("\n", start);
|
|
67
|
-
if (end < 0) end = sourceText.length;
|
|
68
|
-
return {
|
|
69
|
-
end,
|
|
70
|
-
line,
|
|
71
|
-
start,
|
|
72
|
-
text: sourceText.slice(start, end)
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
//#endregion
|
|
76
|
-
export { createReportSnapshot };
|
|
77
|
-
|
|
78
|
-
//# sourceMappingURL=createReportSnapshot.js.map
|
package/lib/index.d.ts
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { InvalidTestCase, TestCase, TestSuggestion, TestSuggestionFileCase, TestSuggestionForFile, TestSuggestionForFiles, ValidTestCase, ValidTestCaseObject } from "./types.js";
|
|
2
|
-
import { RuleTester } from "./RuleTester.js";
|
|
3
|
-
export { InvalidTestCase, RuleTester, TestCase, TestSuggestion, TestSuggestionFileCase, TestSuggestionForFile, TestSuggestionForFiles, ValidTestCase, ValidTestCaseObject };
|
package/lib/index.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { TestCase } from "./types.ts";
|
|
2
|
-
export interface TestCaseNormalized extends TestCase {
|
|
3
|
-
fileName: string;
|
|
4
|
-
}
|
|
5
|
-
export declare function normalizeTestCase<T extends TestCase>(testCase: T, fileName: string | undefined): T & TestCaseNormalized;
|
|
6
|
-
//# sourceMappingURL=normalizeTestCase.d.ts.map
|
package/lib/normalizeTestCase.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
//#region src/normalizeTestCase.ts
|
|
2
|
-
function normalizeTestCase(testCase, fileName) {
|
|
3
|
-
const rv = { ...testCase };
|
|
4
|
-
rv.fileName ??= fileName ?? "file.ts";
|
|
5
|
-
return rv;
|
|
6
|
-
}
|
|
7
|
-
//#endregion
|
|
8
|
-
export { normalizeTestCase };
|
|
9
|
-
|
|
10
|
-
//# sourceMappingURL=normalizeTestCase.js.map
|
package/lib/predicates.d.ts
DELETED
package/lib/predicates.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { type NormalizedReport } from "@flint.fyi/core";
|
|
2
|
-
import type { TestCaseNormalized } from "./normalizeTestCase.ts";
|
|
3
|
-
import type { InvalidTestCase, TestSuggestionFileCase } from "./types.ts";
|
|
4
|
-
export declare function resolveReportedSuggestions(reports: NormalizedReport[], testCaseNormalized: InvalidTestCase & TestCaseNormalized): ({
|
|
5
|
-
files: {
|
|
6
|
-
[k: string]: TestSuggestionFileCase[];
|
|
7
|
-
};
|
|
8
|
-
id: string;
|
|
9
|
-
updated?: never;
|
|
10
|
-
} | {
|
|
11
|
-
id: string;
|
|
12
|
-
updated: string;
|
|
13
|
-
files?: never;
|
|
14
|
-
})[] | undefined;
|
|
15
|
-
//# sourceMappingURL=resolveReportedSuggestions.d.ts.map
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { isTestSuggestionForFiles } from "./predicates.js";
|
|
2
|
-
import { applyChangesToText, isSuggestionForFiles } from "@flint.fyi/core";
|
|
3
|
-
import { isTruthy } from "@flint.fyi/utils";
|
|
4
|
-
//#region src/resolveReportedSuggestions.ts
|
|
5
|
-
function resolveReportedSuggestions(reports, testCaseNormalized) {
|
|
6
|
-
const suggestionsReported = reports.flatMap((report) => report.suggestions).filter(isTruthy);
|
|
7
|
-
if (!suggestionsReported.length) return;
|
|
8
|
-
return suggestionsReported.map((suggestionReported) => isSuggestionForFiles(suggestionReported) ? {
|
|
9
|
-
files: resolveReportedSuggestionForFiles(suggestionReported, testCaseNormalized),
|
|
10
|
-
id: suggestionReported.id
|
|
11
|
-
} : {
|
|
12
|
-
id: suggestionReported.id,
|
|
13
|
-
updated: applyChangesToText([suggestionReported], testCaseNormalized.code)
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
function resolveReportedSuggestionForFiles(suggestionReported, testCaseNormalized) {
|
|
17
|
-
if (!testCaseNormalized.suggestions) return {};
|
|
18
|
-
if (!testCaseNormalized.suggestions.every(isTestSuggestionForFiles)) throw new Error("This test case describes suggestions across files, but the rule is only reporting changes to its own file.");
|
|
19
|
-
return Object.fromEntries(testCaseNormalized.suggestions.flatMap((suggestionExpected) => {
|
|
20
|
-
return Object.entries(suggestionExpected.files).map(([filePath, suggestionCasesExpected]) => {
|
|
21
|
-
return [filePath, suggestionCasesExpected.map((suggestionCaseExpected) => {
|
|
22
|
-
const changes = suggestionReported.files[filePath];
|
|
23
|
-
return {
|
|
24
|
-
original: suggestionCaseExpected.original,
|
|
25
|
-
updated: changes ? applyChangesToText(changes, suggestionCaseExpected.original) : suggestionCaseExpected.original
|
|
26
|
-
};
|
|
27
|
-
})];
|
|
28
|
-
});
|
|
29
|
-
}));
|
|
30
|
-
}
|
|
31
|
-
//#endregion
|
|
32
|
-
export { resolveReportedSuggestions };
|
|
33
|
-
|
|
34
|
-
//# sourceMappingURL=resolveReportedSuggestions.js.map
|
package/lib/runTestCaseRule.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type AnyLanguage, type AnyLanguageFileFactory, type AnyOptionalSchema, type AnyRule, type InferredOutputObject, type NormalizedReport, type RuleAbout, type VFSLinterHost } from "@flint.fyi/core";
|
|
2
|
-
import type { CachedFactory } from "cached-factory";
|
|
3
|
-
import type { TestCaseNormalized } from "./normalizeTestCase.ts";
|
|
4
|
-
export interface TestCaseRuleConfiguration<OptionsSchema extends AnyOptionalSchema | undefined> {
|
|
5
|
-
options?: InferredOutputObject<OptionsSchema | undefined>;
|
|
6
|
-
rule: AnyRule<RuleAbout, OptionsSchema>;
|
|
7
|
-
}
|
|
8
|
-
export declare function runTestCaseRule<OptionsSchema extends AnyOptionalSchema | undefined>(fileFactories: CachedFactory<AnyLanguage, AnyLanguageFileFactory>, linterHost: VFSLinterHost, { options, rule }: Required<TestCaseRuleConfiguration<OptionsSchema>>, { code, fileName, files }: TestCaseNormalized): Promise<NormalizedReport[]>;
|
|
9
|
-
//# sourceMappingURL=runTestCaseRule.d.ts.map
|
package/lib/runTestCaseRule.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { processRuleReport } from "@flint.fyi/core";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { normalizePath, pathKey } from "@flint.fyi/utils";
|
|
5
|
-
//#region src/runTestCaseRule.ts
|
|
6
|
-
async function runTestCaseRule(fileFactories, linterHost, { options, rule }, { code, fileName, files }) {
|
|
7
|
-
const filePathAbsolute = normalizePath(path.resolve(linterHost.getCurrentDirectory(), fileName));
|
|
8
|
-
const caseSensitive = linterHost.isCaseSensitiveFS();
|
|
9
|
-
const targetKey = pathKey(filePathAbsolute, caseSensitive);
|
|
10
|
-
for (const oldFile of linterHost.vfsListFiles().keys()) if (pathKey(oldFile, caseSensitive) !== targetKey) linterHost.vfsDeleteFile(oldFile);
|
|
11
|
-
for (const [name, content] of Object.entries(files ?? {})) {
|
|
12
|
-
const filePath = normalizePath(path.resolve(linterHost.getCurrentDirectory(), name));
|
|
13
|
-
assert.notEqual(filePath, filePathAbsolute, `Expected 'files' not to shadow '${fileName}'`);
|
|
14
|
-
linterHost.vfsUpsertFile(filePath, content);
|
|
15
|
-
}
|
|
16
|
-
linterHost.vfsUpsertFile(filePathAbsolute, code);
|
|
17
|
-
using file = fileFactories.get(rule.language).createFile({
|
|
18
|
-
filePath: fileName,
|
|
19
|
-
filePathAbsolute,
|
|
20
|
-
sourceText: code
|
|
21
|
-
});
|
|
22
|
-
const reports = [];
|
|
23
|
-
const ruleRuntime = await rule.setup({
|
|
24
|
-
host: linterHost,
|
|
25
|
-
report(ruleReport) {
|
|
26
|
-
const processedReport = processRuleReport(file, rule, ruleReport);
|
|
27
|
-
if (processedReport == null) return;
|
|
28
|
-
reports.push(processedReport);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
if (ruleRuntime) {
|
|
32
|
-
rule.language.runFileVisitors(file, options, ruleRuntime);
|
|
33
|
-
await ruleRuntime.teardown?.();
|
|
34
|
-
}
|
|
35
|
-
return reports.toSorted((a, b) => a.range.begin.raw - b.range.begin.raw || a.range.end.raw - b.range.end.raw);
|
|
36
|
-
}
|
|
37
|
-
//#endregion
|
|
38
|
-
export { runTestCaseRule };
|
|
39
|
-
|
|
40
|
-
//# sourceMappingURL=runTestCaseRule.js.map
|
package/lib/types.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
//#region src/types.d.ts
|
|
2
|
-
interface InvalidTestCase<Options extends object | undefined = object | undefined> extends TestCase<Options> {
|
|
3
|
-
output?: string;
|
|
4
|
-
snapshot: string;
|
|
5
|
-
suggestions?: TestSuggestion[];
|
|
6
|
-
}
|
|
7
|
-
interface TestCase<Options extends object | undefined = object | undefined> {
|
|
8
|
-
code: string;
|
|
9
|
-
fileName?: string | undefined;
|
|
10
|
-
files?: Record<string, string> | undefined;
|
|
11
|
-
name?: string | undefined;
|
|
12
|
-
/**
|
|
13
|
-
* Run only this test case. Useful for debugging.
|
|
14
|
-
*
|
|
15
|
-
* Do not commit code with this flag set.
|
|
16
|
-
*/
|
|
17
|
-
only?: boolean;
|
|
18
|
-
options?: Options | undefined;
|
|
19
|
-
/**
|
|
20
|
-
* Skip running this test case. Useful for work-in-progress tests.
|
|
21
|
-
*
|
|
22
|
-
* Do not commit code with this flag set.
|
|
23
|
-
*/
|
|
24
|
-
skip?: boolean;
|
|
25
|
-
}
|
|
26
|
-
type TestSuggestion = TestSuggestionForFile | TestSuggestionForFiles;
|
|
27
|
-
interface TestSuggestionFileCase {
|
|
28
|
-
original: string;
|
|
29
|
-
updated: string;
|
|
30
|
-
}
|
|
31
|
-
interface TestSuggestionForFile {
|
|
32
|
-
id: string;
|
|
33
|
-
updated: string;
|
|
34
|
-
}
|
|
35
|
-
interface TestSuggestionForFiles {
|
|
36
|
-
files: Record<string, TestSuggestionFileCase[]>;
|
|
37
|
-
id: string;
|
|
38
|
-
}
|
|
39
|
-
type ValidTestCase<Options extends object | undefined> = string | ValidTestCaseObject<Options>;
|
|
40
|
-
type ValidTestCaseObject<Options extends object | undefined> = TestCase<Options>;
|
|
41
|
-
//#endregion
|
|
42
|
-
export { InvalidTestCase, TestCase, TestSuggestion, TestSuggestionFileCase, TestSuggestionForFile, TestSuggestionForFiles, ValidTestCase, ValidTestCaseObject };
|
|
43
|
-
//# sourceMappingURL=types.d.ts.map
|