@flint.fyi/rule-tester 0.20.1 → 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 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, createDiskBackedLinterHost, createEphemeralLinterHost, createVFSLinterHost, formatReport, getPositionOfColumnAndLine, isFileSystemCaseSensitive, isSuggestionForFiles, parseOptions, processRuleReport, withRepositoryRoot } from "@flint.fyi/core";
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).toSorted(), Object.keys(suggestionExpected.files).toSorted(), "Reported suggestion target paths must exactly match expected target paths.");
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,
@@ -174,24 +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
- const virtualRoot = diskBackedFSRoot == null ? void 0 : resolve(process.cwd(), diskBackedFSRoot, "_flint-rule-tester-virtual");
178
- let baseHost = virtualRoot == null ? void 0 : createEphemeralLinterHost(withRepositoryRoot(createDiskBackedLinterHost(virtualRoot), virtualRoot));
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 ? {
182
- caseSensitive: isFileSystemCaseSensitive(),
183
- cwd: process.cwd()
184
- } : { baseHost });
185
189
  for (const [name, content] of Object.entries(defaultFiles)) {
186
- const filePath = resolve(vfs.getCurrentDirectory(), name);
187
- vfs.vfsUpsertFile(filePath, content);
190
+ const filePath = resolve(host.getCurrentDirectory(), name);
191
+ host.vfsUpsertFile(filePath, content);
188
192
  }
189
- baseHost = vfs;
193
+ host = createVFSLinterHost({ baseHost: host });
190
194
  }
191
- this.#linterHost = createVFSLinterHost(baseHost == null ? {
192
- caseSensitive: isFileSystemCaseSensitive(),
193
- cwd: process.cwd()
194
- } : { baseHost });
195
+ this.#linterHost = host;
195
196
  this.#fileFactories = new CachedFactory((language) => language.createFileFactory(this.#linterHost));
196
197
  it = defaultTo(it, scope, "it");
197
198
  if (!skip && "skip" in it && typeof it.skip === "function") skip = it.skip;
@@ -232,7 +233,7 @@ var RuleTester = class {
232
233
  assert.equal(actualSnapshot, testCase.snapshot);
233
234
  const actualOutput = createOutput(reports, testCaseNormalized);
234
235
  assert.equal(testCase.output, actualOutput, "Expected `output` property to equal:");
235
- const actualSuggestions = resolveReportedSuggestions(reports, testCaseNormalized);
236
+ const actualSuggestions = resolveReportedSuggestions(reports, testCaseNormalized, this.#linterHost.getCurrentDirectory());
236
237
  assert.deepStrictEqual(actualSuggestions, testCase.suggestions);
237
238
  });
238
239
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flint.fyi/rule-tester",
3
- "version": "0.20.1",
3
+ "version": "0.21.0",
4
4
  "description": "[Experimental] Rule unit tests for Flint.",
5
5
  "homepage": "https://flint.fyi",
6
6
  "repository": {
@@ -23,15 +23,15 @@
23
23
  "dist/"
24
24
  ],
25
25
  "dependencies": {
26
- "@flint.fyi/core": "^0.28.0",
27
- "@flint.fyi/utils": "^0.16.1",
26
+ "@flint.fyi/core": "^0.29.0",
27
+ "@flint.fyi/utils": "^0.17.0",
28
28
  "cached-factory": "^0.3.0",
29
29
  "pathe": "^2.0.3"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@flint.fyi/build": "^0.1.0",
33
33
  "tsdown": "0.23.0",
34
- "vitest": "5.0.1"
34
+ "vitest": "5.0.3"
35
35
  },
36
36
  "engines": {
37
37
  "node": ">=26.1.0"