@flint.fyi/rule-tester 0.20.0 → 0.21.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 +4 -0
- package/dist/index.mjs +23 -19
- package/package.json +6 -5
package/dist/index.d.mts
CHANGED
|
@@ -34,6 +34,10 @@ interface TestSuggestionForFile {
|
|
|
34
34
|
updated: string;
|
|
35
35
|
}
|
|
36
36
|
interface TestSuggestionForFiles {
|
|
37
|
+
/**
|
|
38
|
+
* Expected edits keyed by paths relative to the test host's cwd, or absolute paths.
|
|
39
|
+
* Rules must report the corresponding absolute target paths.
|
|
40
|
+
*/
|
|
37
41
|
files: Record<string, TestSuggestionFileCase[]>;
|
|
38
42
|
id: string;
|
|
39
43
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,8 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
3
|
import { CachedFactory } from "cached-factory";
|
|
4
4
|
import { resolve } from "pathe";
|
|
5
|
-
import { applyChangesToText,
|
|
5
|
+
import { applyChangesToText, createEphemeralLinterHost, createVFSLinterHost, formatReport, getPositionOfColumnAndLine, isSuggestionForFiles, parseOptions, processRuleReport, withRepositoryRoot } from "@flint.fyi/core";
|
|
6
|
+
import { createDiskBackedLinterHost, isFileSystemCaseSensitive } from "@flint.fyi/core/node";
|
|
6
7
|
import { isTruthy, normalizePath, nullThrows, pathKey } from "@flint.fyi/utils";
|
|
7
8
|
//#region src/createOutput.ts
|
|
8
9
|
function createOutput(reports, testCaseNormalized) {
|
|
@@ -96,13 +97,13 @@ function isTestSuggestionForFiles(suggestion) {
|
|
|
96
97
|
}
|
|
97
98
|
//#endregion
|
|
98
99
|
//#region src/resolveReportedSuggestions.ts
|
|
99
|
-
function resolveReportedSuggestions(reports, testCaseNormalized) {
|
|
100
|
+
function resolveReportedSuggestions(reports, testCaseNormalized, cwd) {
|
|
100
101
|
const suggestionsReported = reports.flatMap((report) => report.suggestions).filter(isTruthy);
|
|
101
102
|
if (!suggestionsReported.length) return;
|
|
102
103
|
return suggestionsReported.map((suggestionReported, index) => {
|
|
103
104
|
const suggestionExpected = testCaseNormalized.suggestions?.[index];
|
|
104
105
|
if (isSuggestionForFiles(suggestionReported)) return {
|
|
105
|
-
files: resolveReportedSuggestionForFiles(suggestionReported, suggestionExpected),
|
|
106
|
+
files: resolveReportedSuggestionForFiles(suggestionReported, suggestionExpected, cwd),
|
|
106
107
|
id: suggestionReported.id
|
|
107
108
|
};
|
|
108
109
|
if (suggestionExpected && isTestSuggestionForFiles(suggestionExpected)) throw new Error("This test case describes a suggestion across files, but the rule is only reporting changes to its own file.");
|
|
@@ -112,12 +113,12 @@ function resolveReportedSuggestions(reports, testCaseNormalized) {
|
|
|
112
113
|
};
|
|
113
114
|
});
|
|
114
115
|
}
|
|
115
|
-
function resolveReportedSuggestionForFiles(suggestionReported, suggestionExpected) {
|
|
116
|
+
function resolveReportedSuggestionForFiles(suggestionReported, suggestionExpected, cwd) {
|
|
116
117
|
if (!suggestionExpected) return {};
|
|
117
118
|
if (!isTestSuggestionForFiles(suggestionExpected)) throw new Error("This test case describes a suggestion to its own file, but the rule is reporting changes across files.");
|
|
118
|
-
assert.deepStrictEqual(Object.keys(suggestionReported.files).
|
|
119
|
+
assert.deepStrictEqual(Object.keys(suggestionReported.files).toSorted(), Object.keys(suggestionExpected.files).map((filePath) => resolve(cwd, filePath)).toSorted(), "Reported suggestion target paths must exactly match expected target paths.");
|
|
119
120
|
return Object.fromEntries(Object.entries(suggestionExpected.files).map(([filePath, suggestionCasesExpected]) => {
|
|
120
|
-
const changes = suggestionReported.files[filePath];
|
|
121
|
+
const changes = suggestionReported.files[resolve(cwd, filePath)];
|
|
121
122
|
assert.ok(changes, `Expected reported suggestion "${suggestionReported.id}" to provide changes for target "${filePath}".`);
|
|
122
123
|
return [filePath, suggestionCasesExpected.map((suggestionCaseExpected) => ({
|
|
123
124
|
original: suggestionCaseExpected.original,
|
|
@@ -163,7 +164,7 @@ async function runTestCaseRule(fileFactories, linterHost, { options, rule }, { c
|
|
|
163
164
|
await ruleRuntime.teardown?.();
|
|
164
165
|
}
|
|
165
166
|
return {
|
|
166
|
-
languageReports: collectLanguageReports ? rule.language.getLanguageReports?.(file) ?? [] : [],
|
|
167
|
+
languageReports: collectLanguageReports ? rule.language.getLanguageReports?.(file, linterHost) ?? [] : [],
|
|
167
168
|
reports: reports.toSorted((a, b) => a.range.begin.raw - b.range.begin.raw || a.range.end.raw - b.range.end.raw)
|
|
168
169
|
};
|
|
169
170
|
}
|
|
@@ -174,18 +175,24 @@ var RuleTester = class {
|
|
|
174
175
|
#linterHost;
|
|
175
176
|
#testerOptions;
|
|
176
177
|
constructor({ assertNoLanguageReports = true, defaults = {}, describe, diskBackedFSRoot, it, only, scope = globalThis, skip } = {}) {
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
let host;
|
|
179
|
+
if (diskBackedFSRoot === void 0) host = createVFSLinterHost({
|
|
180
|
+
caseSensitive: isFileSystemCaseSensitive(),
|
|
181
|
+
cwd: "/"
|
|
182
|
+
});
|
|
183
|
+
else {
|
|
184
|
+
const cwd = resolve(process.cwd(), diskBackedFSRoot, "_flint-rule-tester-virtual");
|
|
185
|
+
host = createVFSLinterHost({ baseHost: createEphemeralLinterHost(withRepositoryRoot(createDiskBackedLinterHost(cwd), cwd)) });
|
|
186
|
+
}
|
|
179
187
|
const { files: defaultFiles = {} } = defaults;
|
|
180
188
|
if (Object.keys(defaultFiles).length) {
|
|
181
|
-
const vfs = createVFSLinterHost(baseHost == null ? { cwd: process.cwd() } : { baseHost });
|
|
182
189
|
for (const [name, content] of Object.entries(defaultFiles)) {
|
|
183
|
-
const filePath = resolve(
|
|
184
|
-
|
|
190
|
+
const filePath = resolve(host.getCurrentDirectory(), name);
|
|
191
|
+
host.vfsUpsertFile(filePath, content);
|
|
185
192
|
}
|
|
186
|
-
|
|
193
|
+
host = createVFSLinterHost({ baseHost: host });
|
|
187
194
|
}
|
|
188
|
-
this.#linterHost =
|
|
195
|
+
this.#linterHost = host;
|
|
189
196
|
this.#fileFactories = new CachedFactory((language) => language.createFileFactory(this.#linterHost));
|
|
190
197
|
it = defaultTo(it, scope, "it");
|
|
191
198
|
if (!skip && "skip" in it && typeof it.skip === "function") skip = it.skip;
|
|
@@ -226,16 +233,13 @@ var RuleTester = class {
|
|
|
226
233
|
assert.equal(actualSnapshot, testCase.snapshot);
|
|
227
234
|
const actualOutput = createOutput(reports, testCaseNormalized);
|
|
228
235
|
assert.equal(testCase.output, actualOutput, "Expected `output` property to equal:");
|
|
229
|
-
const actualSuggestions = resolveReportedSuggestions(reports, testCaseNormalized);
|
|
236
|
+
const actualSuggestions = resolveReportedSuggestions(reports, testCaseNormalized, this.#linterHost.getCurrentDirectory());
|
|
230
237
|
assert.deepStrictEqual(actualSuggestions, testCase.suggestions);
|
|
231
238
|
});
|
|
232
239
|
}
|
|
233
240
|
#itTestCase(testCase, seenTestCases, setup) {
|
|
234
241
|
let test = testCase.only ? this.#testerOptions.only : this.#testerOptions.it;
|
|
235
|
-
if (testCase.skip)
|
|
236
|
-
if ("skip" in test && typeof test.skip === "function") test = test.skip;
|
|
237
|
-
else test = this.#testerOptions.skip;
|
|
238
|
-
}
|
|
242
|
+
if (testCase.skip) test = "skip" in test && typeof test.skip === "function" ? test.skip : this.#testerOptions.skip;
|
|
239
243
|
test(testCase.name ?? ("files" in testCase ? JSON.stringify({
|
|
240
244
|
[testCase.fileName]: testCase.code,
|
|
241
245
|
...testCase.files
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flint.fyi/rule-tester",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "[Experimental] Rule unit tests for Flint.",
|
|
5
5
|
"homepage": "https://flint.fyi",
|
|
6
6
|
"repository": {
|
|
@@ -16,21 +16,22 @@
|
|
|
16
16
|
"sideEffects": false,
|
|
17
17
|
"type": "module",
|
|
18
18
|
"exports": {
|
|
19
|
-
".": "./dist/index.mjs"
|
|
19
|
+
".": "./dist/index.mjs",
|
|
20
|
+
"./package.json": "./package.json"
|
|
20
21
|
},
|
|
21
22
|
"files": [
|
|
22
23
|
"dist/"
|
|
23
24
|
],
|
|
24
25
|
"dependencies": {
|
|
25
|
-
"@flint.fyi/core": "^0.
|
|
26
|
-
"@flint.fyi/utils": "^0.
|
|
26
|
+
"@flint.fyi/core": "^0.29.0",
|
|
27
|
+
"@flint.fyi/utils": "^0.17.0",
|
|
27
28
|
"cached-factory": "^0.3.0",
|
|
28
29
|
"pathe": "^2.0.3"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@flint.fyi/build": "^0.1.0",
|
|
32
33
|
"tsdown": "0.23.0",
|
|
33
|
-
"vitest": "5.0.
|
|
34
|
+
"vitest": "5.0.3"
|
|
34
35
|
},
|
|
35
36
|
"engines": {
|
|
36
37
|
"node": ">=26.1.0"
|