@samitouri / QOS-React-1 / commits / b25c14feb1

[tests][be] Clean up fixture selection logic

--- Refactor selection logic to be easier to read; add support for .jsx test files (feels a bit weird adding a `.jsx` fixture and not seeing it get run)

Mofei Zhang committed Nov 9, 2023 at 16:50 UTC b25c14feb133ffad247cf5cb8358ecaebd75d962
4 files changed +81 -54
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/tsconfig.json
+1
@@ -20,6 +20,7 @@
20 },
21 "include": [
22 "./compiler/**/*.js",
23 + "./compiler/**/*.jsx",
24 "./compiler/**/*.ts",
25 "./compiler/**/*.tsx"
26 ]
compiler/packages/fixture-test-utils/src/fixture-utils.ts
+75 -46
@@ -7,6 +7,7 @@
7
8 import fs from "fs/promises";
9 import glob from "glob";
10 +import invariant from "invariant";
11 import path from "path";
12 import { FILTER_PATH, FIXTURES_PATH } from "./constants";
13
@@ -32,6 +33,35 @@ async function exists(file: string): Promise<boolean> {
33 }
34 }
35
36 +function stripExtension(filename: string, extensions: Array<string>): string {
37 + for (const ext of extensions) {
38 + if (filename.endsWith(ext)) {
39 + return filename.slice(0, -ext.length);
40 + }
41 + }
42 + return filename;
43 +}
44 +
45 +function shouldSkip(
46 + filter: TestFilter | null,
47 + filterId: string,
48 + filename: string
49 +) {
50 + if (filter) {
51 + if (filter.kind === "only" && filter.paths.indexOf(filterId) === -1) {
52 + return true;
53 + } else if (
54 + filter.kind === "skip" &&
55 + filter.paths.indexOf(filterId) !== -1
56 + ) {
57 + return true;
58 + }
59 + } else if (filename.startsWith("todo.")) {
60 + return true;
61 + }
62 + return false;
63 +}
64 +
65 export async function readTestFilter(): Promise<TestFilter | null> {
66 if (!(await exists(FILTER_PATH))) {
67 throw new Error(`testfilter file not found at ${FILTER_PATH}`);
@@ -71,71 +101,70 @@ export async function readTestFilter(): Promise<TestFilter | null> {
101
102 export type TestFixture = {
103 basename: string;
74 - inputPath: string;
75 - inputExists: boolean;
104 + inputPath: string | null;
105 outputPath: string;
106 outputExists: boolean;
107 };
108
109 +const INPUT_EXTENSIONS = [".js", ".ts", ".jsx", ".tsx"];
110 +const OUTPUT_EXTENSION = ".expect.md";
111 export function getFixtures(
112 filter: TestFilter | null
113 ): Map<string, TestFixture> {
114 // search for fixtures within nested directories
84 - const files = glob.sync(`**/*.{js,ts,tsx,md}`, {
115 + const inputFiles = glob.sync(`**/*{${INPUT_EXTENSIONS.join(",")}}`, {
116 cwd: FIXTURES_PATH,
117 });
118 const fixtures: Map<string, TestFixture> = new Map();
88 -
89 - for (const filePath of files) {
90 - const basename = path.basename(
91 - path.basename(
92 - path.basename(path.basename(filePath, ".js"), ".ts"),
93 - ".tsx"
94 - ),
95 - ".expect.md"
96 - );
97 - // "partial" paths do not include suffixes
98 - const partialRelativePath = path.join(path.dirname(filePath), basename);
99 - // Replicate jest test behavior
100 - if (basename.startsWith("todo.")) {
119 + for (const filePath of inputFiles) {
120 + const filename = path.basename(filePath);
121 + // Do not include extensions in unique identifier for fixture
122 + const partialPath = stripExtension(filePath, INPUT_EXTENSIONS);
123 + if (shouldSkip(filter, partialPath, filename)) {
124 continue;
125 }
103 - if (filter) {
104 - if (
105 - filter.kind === "only" &&
106 - filter.paths.indexOf(partialRelativePath) === -1
107 - ) {
108 - continue;
109 - } else if (
110 - filter.kind === "skip" &&
111 - filter.paths.indexOf(partialRelativePath) !== -1
112 - ) {
113 - continue;
114 - }
115 - }
126
117 - let fixtureInfo = fixtures.get(partialRelativePath);
127 + const fixtureInfo = fixtures.get(partialPath);
128 if (fixtureInfo === undefined) {
119 - const partialAbsolutePath = path.join(FIXTURES_PATH, partialRelativePath);
120 - fixtureInfo = {
121 - basename,
122 - inputPath: `${partialAbsolutePath}.js`,
123 - inputExists: false,
124 - outputPath: `${partialAbsolutePath}.expect.md`,
129 + fixtures.set(partialPath, {
130 + basename: path.basename(partialPath),
131 + inputPath: path.join(FIXTURES_PATH, filePath),
132 + outputPath: path.join(FIXTURES_PATH, partialPath) + OUTPUT_EXTENSION,
133 outputExists: false,
126 - };
127 - fixtures.set(partialRelativePath, fixtureInfo);
134 + });
135 + } else {
136 + console.warn(
137 + "Found duplicate fixture files: ",
138 + fixtureInfo.inputPath,
139 + filePath
140 + );
141 }
142 + }
143
130 - if (
131 - filePath.endsWith(".js") ||
132 - filePath.endsWith(".ts") ||
133 - filePath.endsWith(".tsx")
134 - ) {
135 - // inputPath may have a different file extension than the .js default
136 - fixtureInfo.inputPath = path.join(FIXTURES_PATH, filePath);
137 - fixtureInfo.inputExists = true;
144 + const outputFiles = glob.sync(`**/*${OUTPUT_EXTENSION}`, {
145 + cwd: FIXTURES_PATH,
146 + });
147 + for (const filePath of outputFiles) {
148 + const filename = path.basename(filePath);
149 + // Do not include extensions in unique identifier for fixture
150 + const partialPath = stripExtension(filePath, [OUTPUT_EXTENSION]);
151 + if (shouldSkip(filter, partialPath, filename)) {
152 + continue;
153 + }
154 +
155 + const fixtureInfo = fixtures.get(partialPath);
156 + if (fixtureInfo === undefined) {
157 + fixtures.set(partialPath, {
158 + basename: path.basename(partialPath),
159 + inputPath: null,
160 + outputPath: path.join(FIXTURES_PATH, filePath),
161 + outputExists: true,
162 + });
163 } else {
164 + invariant(
165 + fixtureInfo.outputPath === path.join(FIXTURES_PATH, filePath),
166 + "Unexpected output filepath"
167 + );
168 fixtureInfo.outputExists = true;
169 }
170 }
compiler/packages/snap/src/compiler-worker.ts
+2 -5
@@ -30,7 +30,6 @@ export function clearRequireCache() {
30 }
31
32 export type TestResult = {
33 - inputPath: string;
33 outputPath: string;
34 actual: string | null; // null == input did not exist
35 expected: string | null; // null == output did not exist
@@ -54,16 +53,15 @@ export async function compile(
53 clearRequireCache();
54 }
55 version = compilerVersion;
57 - const { inputPath, inputExists, outputPath, outputExists, basename } =
56 + const { inputPath, outputPath, outputExists, basename } =
57 fixture;
59 - const input = inputExists ? await fs.readFile(inputPath, "utf8") : null;
58 + const input = inputPath != null ? await fs.readFile(inputPath, "utf8") : null;
59 const expected = outputExists ? await fs.readFile(outputPath, "utf8") : null;
60
61 // Input will be null if the input file did not exist, in which case the output file
62 // is stale
63 if (input === null) {
64 return {
66 - inputPath,
65 outputPath,
66 actual: null,
67 expected,
@@ -128,7 +126,6 @@ export async function compile(
126 console.error = originalConsoleError;
127
128 return {
131 - inputPath,
129 outputPath,
130 actual: output,
131 expected,
compiler/packages/sprout/src/runner-worker.ts
+3 -3
@@ -191,12 +191,12 @@ export async function run(fixture: TestFixture): Promise<TestResult> {
191 console.error = (...messages: Array<string>) => {
192 seenConsoleErrors.push(...messages);
193 };
194 - const { inputPath, inputExists } = fixture;
195 - if (!inputExists) {
194 + const { inputPath } = fixture;
195 + if (inputPath == null) {
196 return {
197 nonForgetResult: null,
198 forgetResult: null,
199 - unexpectedError: "file did not exist!",
199 + unexpectedError: "No input for fixture " + fixture.outputPath,
200 };
201 }
202 const inputRaw = await fs.readFile(inputPath, "utf8");