@flint.fyi/rule-tester 0.14.3 → 0.16.1
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/lib/RuleTester.d.ts +38 -16
- package/lib/RuleTester.js +93 -92
- package/lib/createOutput.d.ts +5 -0
- package/lib/createOutput.js +10 -0
- package/lib/createReportSnapshot.d.ts +2 -1
- package/lib/createReportSnapshot.js +75 -35
- package/lib/index.d.ts +3 -2
- package/lib/index.js +2 -1
- package/lib/normalizeTestCase.d.ts +3 -2
- package/lib/normalizeTestCase.js +9 -5
- package/lib/predicates.d.ts +2 -1
- package/lib/predicates.js +7 -2
- package/lib/resolveReportedSuggestions.d.ts +6 -5
- package/lib/resolveReportedSuggestions.js +30 -42
- package/lib/runTestCaseRule.d.ts +6 -5
- package/lib/runTestCaseRule.js +39 -8
- package/lib/types.d.ts +38 -32
- package/package.json +22 -8
- package/CHANGELOG.md +0 -53
- package/lib/resolveReportedSuggestions.test.d.ts +0 -1
- package/lib/resolveReportedSuggestions.test.js +0 -172
- package/lib/types.js +0 -1
- package/src/RuleTester.ts +0 -186
- package/src/createReportSnapshot.ts +0 -50
- package/src/index.ts +0 -2
- package/src/normalizeTestCase.ts +0 -14
- package/src/predicates.ts +0 -7
- package/src/resolveReportedSuggestions.test.ts +0 -201
- package/src/resolveReportedSuggestions.ts +0 -85
- package/src/runTestCaseRule.ts +0 -35
- package/src/types.ts +0 -54
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
package/lib/types.d.ts
CHANGED
|
@@ -1,37 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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[];
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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;
|
|
22
25
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
type TestSuggestion = TestSuggestionForFile | TestSuggestionForFiles;
|
|
27
|
+
interface TestSuggestionFileCase {
|
|
28
|
+
original: string;
|
|
29
|
+
updated: string;
|
|
27
30
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
interface TestSuggestionForFile {
|
|
32
|
+
id: string;
|
|
33
|
+
updated: string;
|
|
31
34
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
interface TestSuggestionForFiles {
|
|
36
|
+
files: Record<string, TestSuggestionFileCase[]>;
|
|
37
|
+
id: string;
|
|
35
38
|
}
|
|
36
|
-
|
|
37
|
-
|
|
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
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flint.fyi/rule-tester",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "[Experimental] Rule unit tests for Flint.",
|
|
5
|
+
"homepage": "https://flint.fyi",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
7
|
-
"url": "https://github.com/
|
|
8
|
+
"url": "git+https://github.com/flint-fyi/flint.git",
|
|
8
9
|
"directory": "packages/rule-tester"
|
|
9
10
|
},
|
|
10
11
|
"license": "MIT",
|
|
@@ -12,18 +13,31 @@
|
|
|
12
13
|
"name": "JoshuaKGoldberg",
|
|
13
14
|
"email": "npm@joshuakgoldberg.com"
|
|
14
15
|
},
|
|
16
|
+
"sideEffects": false,
|
|
15
17
|
"type": "module",
|
|
16
|
-
"
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"lib/",
|
|
23
|
+
"!lib/**/*.map"
|
|
24
|
+
],
|
|
17
25
|
"dependencies": {
|
|
18
|
-
"
|
|
19
|
-
"@flint.fyi/
|
|
20
|
-
"
|
|
26
|
+
"cached-factory": "^0.1.0",
|
|
27
|
+
"@flint.fyi/core": "^0.21.0",
|
|
28
|
+
"@flint.fyi/utils": "^0.14.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"tsdown": "0.21.0",
|
|
32
|
+
"vitest": "4.1.0"
|
|
21
33
|
},
|
|
22
34
|
"engines": {
|
|
23
35
|
"node": ">=24.0.0"
|
|
24
36
|
},
|
|
25
37
|
"publishConfig": {
|
|
26
|
-
"access": "public"
|
|
27
|
-
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"test": "vitest --project rule-tester"
|
|
28
42
|
}
|
|
29
43
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# @flint/rule-tester
|
|
2
|
-
|
|
3
|
-
## 0.14.3
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- 738fe36: fix: support multiline error ranges in RuleTester
|
|
8
|
-
|
|
9
|
-
## 0.14.2
|
|
10
|
-
|
|
11
|
-
### Patch Changes
|
|
12
|
-
|
|
13
|
-
- a3f9043: fix: respect \`only\` and \`skip\` options in valid RuleTester cases
|
|
14
|
-
- Updated dependencies [3d19082]
|
|
15
|
-
- @flint.fyi/core@0.15.1
|
|
16
|
-
|
|
17
|
-
## 0.14.1
|
|
18
|
-
|
|
19
|
-
### Patch Changes
|
|
20
|
-
|
|
21
|
-
- b58d145: corrected logic and types for current-file suggestions
|
|
22
|
-
|
|
23
|
-
## 0.14.0
|
|
24
|
-
|
|
25
|
-
### Minor Changes
|
|
26
|
-
|
|
27
|
-
- 7d0d873: add // flint-\* comment directives
|
|
28
|
-
|
|
29
|
-
### Patch Changes
|
|
30
|
-
|
|
31
|
-
- Updated dependencies [b48f4a9]
|
|
32
|
-
- Updated dependencies [7d0d873]
|
|
33
|
-
- Updated dependencies [79f15da]
|
|
34
|
-
- @flint.fyi/core@0.15.0
|
|
35
|
-
|
|
36
|
-
## 0.13.1
|
|
37
|
-
|
|
38
|
-
### Patch Changes
|
|
39
|
-
|
|
40
|
-
- 9909b48: add README.md
|
|
41
|
-
- Updated dependencies [9909b48]
|
|
42
|
-
- @flint.fyi/core@0.13.1
|
|
43
|
-
|
|
44
|
-
## 0.13.0
|
|
45
|
-
|
|
46
|
-
### Minor Changes
|
|
47
|
-
|
|
48
|
-
- 72ed00b: feat: split into a monorepo
|
|
49
|
-
|
|
50
|
-
### Patch Changes
|
|
51
|
-
|
|
52
|
-
- Updated dependencies [72ed00b]
|
|
53
|
-
- @flint/core@0.13.0
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { resolveReportedSuggestions } from "./resolveReportedSuggestions.js";
|
|
3
|
-
const mockReport = {
|
|
4
|
-
message: { primary: "", secondary: [], suggestions: [] },
|
|
5
|
-
range: {
|
|
6
|
-
begin: { column: 0, line: 1, raw: 0 },
|
|
7
|
-
end: { column: 3, line: 1, raw: 3 },
|
|
8
|
-
},
|
|
9
|
-
};
|
|
10
|
-
const mockTestCaseNormalized = {
|
|
11
|
-
code: "xyz",
|
|
12
|
-
fileName: "file.ts",
|
|
13
|
-
snapshot: "",
|
|
14
|
-
};
|
|
15
|
-
describe("resolveReportedSuggestions", () => {
|
|
16
|
-
it("returns undefined when reports is empty", () => {
|
|
17
|
-
const result = resolveReportedSuggestions([], mockTestCaseNormalized);
|
|
18
|
-
expect(result).toEqual(undefined);
|
|
19
|
-
});
|
|
20
|
-
it("returns undefined when given one report with no suggestions", () => {
|
|
21
|
-
const report = {
|
|
22
|
-
...mockReport,
|
|
23
|
-
suggestions: [],
|
|
24
|
-
};
|
|
25
|
-
const result = resolveReportedSuggestions([report], mockTestCaseNormalized);
|
|
26
|
-
expect(result).toEqual(undefined);
|
|
27
|
-
});
|
|
28
|
-
it("returns id and updated text when given a single file suggestion", () => {
|
|
29
|
-
const suggestion = {
|
|
30
|
-
id: "suggestion",
|
|
31
|
-
range: { begin: 0, end: 3 },
|
|
32
|
-
text: "abc",
|
|
33
|
-
};
|
|
34
|
-
const report = {
|
|
35
|
-
...mockReport,
|
|
36
|
-
suggestions: [suggestion],
|
|
37
|
-
};
|
|
38
|
-
const result = resolveReportedSuggestions([report], mockTestCaseNormalized);
|
|
39
|
-
expect(result).toEqual([
|
|
40
|
-
{
|
|
41
|
-
id: suggestion.id,
|
|
42
|
-
updated: suggestion.text,
|
|
43
|
-
},
|
|
44
|
-
]);
|
|
45
|
-
});
|
|
46
|
-
it("throws when given a test case with no suggestions", () => {
|
|
47
|
-
const report = {
|
|
48
|
-
...mockReport,
|
|
49
|
-
suggestions: [
|
|
50
|
-
{
|
|
51
|
-
files: {
|
|
52
|
-
"file.ts": () => [{ range: { begin: 0, end: 3 }, text: "def" }],
|
|
53
|
-
},
|
|
54
|
-
id: "suggestion-report",
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
};
|
|
58
|
-
expect(() => resolveReportedSuggestions([report], {
|
|
59
|
-
...mockTestCaseNormalized,
|
|
60
|
-
suggestions: [
|
|
61
|
-
{
|
|
62
|
-
id: "suggestion-result",
|
|
63
|
-
updated: "...",
|
|
64
|
-
},
|
|
65
|
-
],
|
|
66
|
-
})).toThrowErrorMatchingInlineSnapshot(`[Error: This test case describes suggestions across files, but the rule is only reporting changes to its own file.]`);
|
|
67
|
-
});
|
|
68
|
-
it("throws when given a test case that doesn't have cross-file suggestions", () => {
|
|
69
|
-
const report = {
|
|
70
|
-
...mockReport,
|
|
71
|
-
suggestions: [
|
|
72
|
-
{
|
|
73
|
-
files: {
|
|
74
|
-
"file.ts": () => [{ range: { begin: 0, end: 3 }, text: "def" }],
|
|
75
|
-
},
|
|
76
|
-
id: "suggestion-report",
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
};
|
|
80
|
-
expect(() => resolveReportedSuggestions([report], {
|
|
81
|
-
...mockTestCaseNormalized,
|
|
82
|
-
suggestions: [
|
|
83
|
-
{
|
|
84
|
-
id: "suggestion-result",
|
|
85
|
-
updated: "...",
|
|
86
|
-
},
|
|
87
|
-
],
|
|
88
|
-
})).toThrowErrorMatchingInlineSnapshot(`[Error: This test case describes suggestions across files, but the rule is only reporting changes to its own file.]`);
|
|
89
|
-
});
|
|
90
|
-
it("returns id and a files object when given multi-file suggestions with a single file", () => {
|
|
91
|
-
const report = {
|
|
92
|
-
...mockReport,
|
|
93
|
-
suggestions: [
|
|
94
|
-
{
|
|
95
|
-
files: {
|
|
96
|
-
"file.ts": () => [{ range: { begin: 0, end: 3 }, text: "def" }],
|
|
97
|
-
},
|
|
98
|
-
id: "suggestion-report",
|
|
99
|
-
},
|
|
100
|
-
],
|
|
101
|
-
};
|
|
102
|
-
const result = resolveReportedSuggestions([report], {
|
|
103
|
-
...mockTestCaseNormalized,
|
|
104
|
-
suggestions: [
|
|
105
|
-
{
|
|
106
|
-
files: {
|
|
107
|
-
"file.ts": [{ original: "abc", updated: "def" }],
|
|
108
|
-
},
|
|
109
|
-
id: "suggestion-result",
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
});
|
|
113
|
-
expect(result).toEqual([
|
|
114
|
-
{
|
|
115
|
-
files: {
|
|
116
|
-
"file.ts": [
|
|
117
|
-
{
|
|
118
|
-
original: "abc",
|
|
119
|
-
updated: "def",
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
},
|
|
123
|
-
id: "suggestion-report",
|
|
124
|
-
},
|
|
125
|
-
]);
|
|
126
|
-
});
|
|
127
|
-
it("returns id and a files object when given multi-file suggestions with multiple file", () => {
|
|
128
|
-
const report = {
|
|
129
|
-
...mockReport,
|
|
130
|
-
suggestions: [
|
|
131
|
-
{
|
|
132
|
-
files: {
|
|
133
|
-
"fileA.ts": () => [{ range: { begin: 0, end: 5 }, text: "def-A" }],
|
|
134
|
-
"fileB.ts": () => [{ range: { begin: 0, end: 5 }, text: "def-B" }],
|
|
135
|
-
},
|
|
136
|
-
id: "suggestion-report",
|
|
137
|
-
},
|
|
138
|
-
],
|
|
139
|
-
};
|
|
140
|
-
const result = resolveReportedSuggestions([report], {
|
|
141
|
-
...mockTestCaseNormalized,
|
|
142
|
-
suggestions: [
|
|
143
|
-
{
|
|
144
|
-
files: {
|
|
145
|
-
"fileA.ts": [{ original: "abc-A", updated: "def-A" }],
|
|
146
|
-
"fileB.ts": [{ original: "abc-B", updated: "def-B" }],
|
|
147
|
-
},
|
|
148
|
-
id: "suggestion-result",
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
});
|
|
152
|
-
expect(result).toEqual([
|
|
153
|
-
{
|
|
154
|
-
files: {
|
|
155
|
-
"fileA.ts": [
|
|
156
|
-
{
|
|
157
|
-
original: "abc-A",
|
|
158
|
-
updated: "def-A",
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
"fileB.ts": [
|
|
162
|
-
{
|
|
163
|
-
original: "abc-B",
|
|
164
|
-
updated: "def-B",
|
|
165
|
-
},
|
|
166
|
-
],
|
|
167
|
-
},
|
|
168
|
-
id: "suggestion-report",
|
|
169
|
-
},
|
|
170
|
-
]);
|
|
171
|
-
});
|
|
172
|
-
});
|
package/lib/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/src/RuleTester.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AnyLanguage,
|
|
3
|
-
AnyOptionalSchema,
|
|
4
|
-
AnyRule,
|
|
5
|
-
InferredObject,
|
|
6
|
-
LanguageFileFactory,
|
|
7
|
-
RuleAbout,
|
|
8
|
-
} from "@flint.fyi/core";
|
|
9
|
-
import { CachedFactory } from "cached-factory";
|
|
10
|
-
import assert from "node:assert";
|
|
11
|
-
|
|
12
|
-
import { createReportSnapshot } from "./createReportSnapshot.js";
|
|
13
|
-
import { normalizeTestCase } from "./normalizeTestCase.js";
|
|
14
|
-
import { resolveReportedSuggestions } from "./resolveReportedSuggestions.js";
|
|
15
|
-
import { runTestCaseRule } from "./runTestCaseRule.js";
|
|
16
|
-
import { InvalidTestCase, TestCase, ValidTestCase } from "./types.js";
|
|
17
|
-
|
|
18
|
-
export interface RuleTesterOptions {
|
|
19
|
-
describe?: TesterSetupDescribe;
|
|
20
|
-
it?: TesterSetupIt;
|
|
21
|
-
only?: TesterSetupIt;
|
|
22
|
-
scope?: Record<string, unknown>;
|
|
23
|
-
skip?: TesterSetupIt;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface TestCases<Options extends object | undefined> {
|
|
27
|
-
invalid: InvalidTestCase<Options>[];
|
|
28
|
-
valid: ValidTestCase<Options>[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type TesterSetupDescribe = (
|
|
32
|
-
description: string,
|
|
33
|
-
setup: () => void,
|
|
34
|
-
) => void;
|
|
35
|
-
|
|
36
|
-
export type TesterSetupIt = (
|
|
37
|
-
description: string,
|
|
38
|
-
setup: () => Promise<void>,
|
|
39
|
-
) => void;
|
|
40
|
-
|
|
41
|
-
export class RuleTester {
|
|
42
|
-
#fileFactories: CachedFactory<AnyLanguage, LanguageFileFactory>;
|
|
43
|
-
#testerOptions: Required<RuleTesterOptions>;
|
|
44
|
-
|
|
45
|
-
constructor({
|
|
46
|
-
describe,
|
|
47
|
-
it,
|
|
48
|
-
only,
|
|
49
|
-
scope = globalThis,
|
|
50
|
-
skip,
|
|
51
|
-
}: RuleTesterOptions = {}) {
|
|
52
|
-
this.#fileFactories = new CachedFactory((language: AnyLanguage) =>
|
|
53
|
-
language.prepare(),
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
it = defaultTo(it, scope, "it");
|
|
57
|
-
|
|
58
|
-
if (!skip && "skip" in it && typeof it.skip === "function") {
|
|
59
|
-
skip = it.skip as TesterSetupIt;
|
|
60
|
-
}
|
|
61
|
-
if (!only && "only" in it && typeof it.only === "function") {
|
|
62
|
-
only = it.only as TesterSetupIt;
|
|
63
|
-
}
|
|
64
|
-
if (!skip) {
|
|
65
|
-
throw new TypeError("RuleTester needs a `skip` function");
|
|
66
|
-
}
|
|
67
|
-
if (!only) {
|
|
68
|
-
throw new TypeError("RuleTester needs a `only` function");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
this.#testerOptions = {
|
|
72
|
-
describe: defaultTo(describe, scope, "describe"),
|
|
73
|
-
it,
|
|
74
|
-
only,
|
|
75
|
-
scope,
|
|
76
|
-
skip,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
describe<OptionsSchema extends AnyOptionalSchema | undefined>(
|
|
81
|
-
rule: AnyRule<RuleAbout, OptionsSchema>,
|
|
82
|
-
{ invalid, valid }: TestCases<InferredObject<OptionsSchema>>,
|
|
83
|
-
) {
|
|
84
|
-
this.#testerOptions.describe(rule.about.id, () => {
|
|
85
|
-
this.#testerOptions.describe("invalid", () => {
|
|
86
|
-
for (const testCase of invalid) {
|
|
87
|
-
this.#itInvalidCase(rule, testCase);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
this.#testerOptions.describe("valid", () => {
|
|
92
|
-
for (const testCase of valid) {
|
|
93
|
-
this.#itValidCase(rule, testCase);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
#itInvalidCase<OptionsSchema extends AnyOptionalSchema | undefined>(
|
|
100
|
-
rule: AnyRule<RuleAbout, OptionsSchema>,
|
|
101
|
-
testCase: InvalidTestCase<InferredObject<OptionsSchema>>,
|
|
102
|
-
) {
|
|
103
|
-
const testCaseNormalized = normalizeTestCase(testCase);
|
|
104
|
-
|
|
105
|
-
this.#itTestCase(testCaseNormalized, async () => {
|
|
106
|
-
const reports = await runTestCaseRule(
|
|
107
|
-
this.#fileFactories,
|
|
108
|
-
{
|
|
109
|
-
// TODO: Figure out a way around the type assertion...
|
|
110
|
-
options: testCase.options ?? ({} as InferredObject<OptionsSchema>),
|
|
111
|
-
rule,
|
|
112
|
-
},
|
|
113
|
-
testCaseNormalized,
|
|
114
|
-
);
|
|
115
|
-
const actualSnapshot = createReportSnapshot(testCase.code, reports);
|
|
116
|
-
|
|
117
|
-
assert.equal(actualSnapshot, testCase.snapshot);
|
|
118
|
-
|
|
119
|
-
const actualSuggestions = resolveReportedSuggestions(
|
|
120
|
-
reports,
|
|
121
|
-
testCaseNormalized,
|
|
122
|
-
);
|
|
123
|
-
assert.deepStrictEqual(actualSuggestions, testCase.suggestions);
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
#itTestCase(testCase: TestCase, setup: () => Promise<void>) {
|
|
128
|
-
let test = testCase.only
|
|
129
|
-
? this.#testerOptions.only
|
|
130
|
-
: this.#testerOptions.it;
|
|
131
|
-
|
|
132
|
-
if (testCase.skip) {
|
|
133
|
-
if ("skip" in test && typeof test.skip === "function") {
|
|
134
|
-
test = test.skip as TesterSetupIt;
|
|
135
|
-
} else {
|
|
136
|
-
test = this.#testerOptions.skip;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
test(testCase.code, setup);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
#itValidCase<OptionsSchema extends AnyOptionalSchema | undefined>(
|
|
144
|
-
rule: AnyRule<RuleAbout, OptionsSchema>,
|
|
145
|
-
testCaseRaw: ValidTestCase<InferredObject<OptionsSchema>>,
|
|
146
|
-
) {
|
|
147
|
-
const testCase =
|
|
148
|
-
typeof testCaseRaw === "string" ? { code: testCaseRaw } : testCaseRaw;
|
|
149
|
-
const testCaseNormalized = normalizeTestCase(testCase);
|
|
150
|
-
|
|
151
|
-
this.#itTestCase(testCaseNormalized, async () => {
|
|
152
|
-
const reports = await runTestCaseRule(
|
|
153
|
-
this.#fileFactories,
|
|
154
|
-
{
|
|
155
|
-
// TODO: Figure out a way around the type assertion...
|
|
156
|
-
options: (testCase.options ?? {}) as InferredObject<OptionsSchema>,
|
|
157
|
-
rule,
|
|
158
|
-
},
|
|
159
|
-
testCaseNormalized,
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
if (reports.length) {
|
|
163
|
-
assert.deepStrictEqual(
|
|
164
|
-
createReportSnapshot(testCaseNormalized.code, reports),
|
|
165
|
-
testCaseNormalized.code,
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function defaultTo<TesterSetup extends TesterSetupDescribe | TesterSetupIt>(
|
|
173
|
-
provided: TesterSetup | undefined,
|
|
174
|
-
scope: Record<string, unknown>,
|
|
175
|
-
scopeKey: string,
|
|
176
|
-
): TesterSetup {
|
|
177
|
-
if (provided) {
|
|
178
|
-
return provided;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (scopeKey in scope && typeof scope[scopeKey] === "function") {
|
|
182
|
-
return scope[scopeKey] as TesterSetup;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
throw new Error(`No ${scopeKey} function found`);
|
|
186
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { formatReportPrimary, NormalizedReport } from "@flint.fyi/core";
|
|
2
|
-
|
|
3
|
-
export function createReportSnapshot(
|
|
4
|
-
sourceText: string,
|
|
5
|
-
reports: NormalizedReport[],
|
|
6
|
-
) {
|
|
7
|
-
let result = sourceText;
|
|
8
|
-
|
|
9
|
-
for (const report of reports.toReversed()) {
|
|
10
|
-
result = createReportSnapshotAt(result, report);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return result;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function createReportSnapshotAt(sourceText: string, report: NormalizedReport) {
|
|
17
|
-
const { begin, end } = report.range;
|
|
18
|
-
const lineStartIndex = sourceText.lastIndexOf("\n", begin.raw) + 1;
|
|
19
|
-
let lineEndIndex = sourceText.indexOf("\n", end.raw);
|
|
20
|
-
if (lineEndIndex < 0) {
|
|
21
|
-
lineEndIndex = sourceText.length;
|
|
22
|
-
}
|
|
23
|
-
const lines = sourceText.slice(lineStartIndex, lineEndIndex).split("\n");
|
|
24
|
-
const output: string[] = [];
|
|
25
|
-
|
|
26
|
-
for (let i = begin.line; i <= end.line; i++) {
|
|
27
|
-
const line = lines[i - begin.line];
|
|
28
|
-
output.push(line);
|
|
29
|
-
|
|
30
|
-
const prevLineIndent = /^[\t ]*/.exec(line)?.[0] ?? "";
|
|
31
|
-
|
|
32
|
-
if (i === begin.line) {
|
|
33
|
-
const indent = prevLineIndent.padEnd(begin.column, " ");
|
|
34
|
-
const squiggleEnd = begin.line === end.line ? end.column : line.length;
|
|
35
|
-
output.push(indent.padEnd(squiggleEnd, "~"));
|
|
36
|
-
for (const errorMessageLine of formatReportPrimary(report).split("\n")) {
|
|
37
|
-
output.push(indent + errorMessageLine);
|
|
38
|
-
}
|
|
39
|
-
} else {
|
|
40
|
-
const squiggleEnd = i === end.line ? end.column : line.length;
|
|
41
|
-
output.push(prevLineIndent.padEnd(squiggleEnd, "~"));
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
sourceText.slice(0, lineStartIndex) +
|
|
47
|
-
output.join("\n") +
|
|
48
|
-
sourceText.slice(lineEndIndex)
|
|
49
|
-
);
|
|
50
|
-
}
|
package/src/index.ts
DELETED
package/src/normalizeTestCase.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { TestCase } from "./types.js";
|
|
2
|
-
|
|
3
|
-
export interface TestCaseNormalized extends TestCase {
|
|
4
|
-
fileName: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function normalizeTestCase<T extends TestCase>(
|
|
8
|
-
testCase: T,
|
|
9
|
-
): T & TestCaseNormalized {
|
|
10
|
-
return {
|
|
11
|
-
fileName: "file.ts",
|
|
12
|
-
...testCase,
|
|
13
|
-
};
|
|
14
|
-
}
|