Skip to content

Commit

Permalink
test: add global snapshot serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
LingyuCoder committed Oct 16, 2024
1 parent a97e105 commit 1b5b094
Show file tree
Hide file tree
Showing 58 changed files with 4,139 additions and 3,970 deletions.
39 changes: 18 additions & 21 deletions packages/rspack-test-tools/etc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,6 @@ export function createWatchNewIncrementalCase(name: string, src: string, dist: s
export class DefaultsConfigProcessor<T extends ECompilerType> extends SimpleTaskProcessor<T> {
constructor(_defaultsConfigOptions: IDefaultsConfigProcessorOptions<T>);
// (undocumented)
static addSnapshotSerializer(expectImpl: jest.Expect): void;
// (undocumented)
after(context: ITestContext): Promise<void>;
// (undocumented)
afterAll(context: ITestContext): Promise<void>;
Expand Down Expand Up @@ -253,13 +251,6 @@ export class DiagnosticProcessor<T extends ECompilerType> extends BasicProcessor
protected _diagnosticOptions: IDiagnosticProcessorOptions<T>;
}

// @public (undocumented)
class Diff {
constructor(value: string);
// (undocumented)
value: string;
}

// @public (undocumented)
export class DiffComparator {
constructor(options: IDiffComparatorOptions);
Expand Down Expand Up @@ -350,8 +341,6 @@ export enum EEsmMode {
export class ErrorProcessor<T extends ECompilerType> extends SimpleTaskProcessor<T> {
constructor(_errorOptions: IErrorProcessorOptions<T>);
// (undocumented)
static addSnapshotSerializer(expectImpl: jest.Expect): void;
// (undocumented)
check(env: ITestEnv, context: ITestContext): Promise<void>;
// (undocumented)
compiler(context: ITestContext): Promise<void>;
Expand Down Expand Up @@ -609,7 +598,7 @@ export interface IDefaultsConfigProcessorOptions<T extends ECompilerType> {
// (undocumented)
cwd?: string;
// (undocumented)
diff: (diff: jest.JestMatchers<Diff>, defaults: jest.JestMatchers<TCompilerOptions<T>>) => Promise<void>;
diff: (diff: jest.JestMatchers<RspackTestDiff>, defaults: jest.JestMatchers<TCompilerOptions<T>>) => Promise<void>;
// (undocumented)
name: string;
// (undocumented)
Expand Down Expand Up @@ -699,7 +688,7 @@ export interface IErrorProcessorOptions<T extends ECompilerType> {
// (undocumented)
build?: (context: ITestContext, compiler: TCompiler<T>) => Promise<void>;
// (undocumented)
check?: (stats: TStatsDiagnostics) => Promise<void>;
check?: (stats: RspackStatsDiagnostics) => Promise<void>;
// (undocumented)
compilerType: T;
// (undocumented)
Expand Down Expand Up @@ -1154,6 +1143,22 @@ export class RspackDiffConfigPlugin implements RspackPluginInstance {
name: string;
}

// @public (undocumented)
class RspackStatsDiagnostics {
constructor(errors: StatsError[], warnings: StatsError[]);
// (undocumented)
errors: StatsError[];
// (undocumented)
warnings: StatsError[];
}

// @public (undocumented)
class RspackTestDiff {
constructor(value: string);
// (undocumented)
value: string;
}

// @public (undocumented)
export class SimpleTaskProcessor<T extends ECompilerType> implements ITestProcessor {
constructor(_options: ISimpleProcessorOptions<T>);
Expand Down Expand Up @@ -1194,8 +1199,6 @@ export class SnapshotProcessor<T extends ECompilerType> extends BasicProcessor<T
export class StatsAPIProcessor<T extends ECompilerType> extends SimpleTaskProcessor<T> {
constructor(_statsAPIOptions: IStatsAPIProcessorOptions<T>);
// (undocumented)
static addSnapshotSerializer(expectImpl: jest.Expect): void;
// (undocumented)
check(env: ITestEnv, context: ITestContext): Promise<void>;
// (undocumented)
compiler(context: ITestContext): Promise<void>;
Expand Down Expand Up @@ -1449,12 +1452,6 @@ export type TStatsAPICaseConfig = Omit<IStatsAPIProcessorOptions<ECompilerType.R
description: string;
};

// @public (undocumented)
type TStatsDiagnostics = {
errors: StatsError[];
warnings: StatsError[];
};

// @public (undocumented)
export type TTestConfig<T extends ECompilerType> = {
documentType?: EDocumentType;
Expand Down
1 change: 1 addition & 0 deletions packages/rspack-test-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"jsdom": "^25.0.0",
"memfs": "4.8.1",
"mkdirp": "0.5.6",
"path-serializer": "0.0.6",
"pretty-format": "29.7.0",
"rimraf": "3.0.2",
"strip-ansi": "6.0.1",
Expand Down
1 change: 0 additions & 1 deletion packages/rspack-test-tools/src/case/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export function createErrorCase(
testConfig: string
) {
if (!addedSerializer) {
ErrorProcessor.addSnapshotSerializer(expect);
addedSerializer = true;
}
const caseConfig = require(testConfig);
Expand Down
1 change: 0 additions & 1 deletion packages/rspack-test-tools/src/case/stats-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export function createStatsAPICase(
testConfig: string
) {
if (!addedSerializer) {
StatsAPIProcessor.addSnapshotSerializer(expect);
addedSerializer = true;
}
const caseConfig: TStatsAPICaseConfig = require(testConfig);
Expand Down
19 changes: 19 additions & 0 deletions packages/rspack-test-tools/src/helper/expect/char.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const normalizeCRLF = (str: string): string => {
return str.replace(/\r\n?/g, "\n");
};

export const normalizeCLR = (str: string): string => {
return (
str
.replace(/\u001b\[1m\u001b\[([0-9;]*)m/g, "<CLR=$1,BOLD>")
.replace(/\u001b\[1m/g, "<CLR=BOLD>")
.replace(/\u001b\[39m\u001b\[22m/g, "</CLR>")
.replace(/\u001b\[([0-9;]*)m/g, "<CLR=$1>")
// CHANGE: The time unit display in Rspack is second
.replace(/[.0-9]+(<\/CLR>)?(\s?s)/g, "X$1$2")
);
};

export const normalizeColor = (str: string): string => {
return str.replace(/\u001b\[[0-9;]*m/g, "");
};
36 changes: 36 additions & 0 deletions packages/rspack-test-tools/src/helper/expect/diff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const CURRENT_CWD = process.cwd();

const quoteMeta = (str: string) => str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&");
const cwdRegExp = new RegExp(
`${quoteMeta(CURRENT_CWD)}((?:\\\\)?(?:[a-zA-Z.\\-_]+\\\\)*)`,
"g"
);
const escapedCwd = JSON.stringify(CURRENT_CWD).slice(1, -1);
const escapedCwdRegExp = new RegExp(
`${quoteMeta(escapedCwd)}((?:\\\\\\\\)?(?:[a-zA-Z.\\-_]+\\\\\\\\)*)`,
"g"
);

export const normalizeDiff = (diff: { value: string }) => {
let normalizedStr: string = diff.value;
if (CURRENT_CWD.startsWith("/")) {
normalizedStr = normalizedStr.replace(
new RegExp(quoteMeta(CURRENT_CWD), "g"),
"<cwd>"
);
} else {
normalizedStr = normalizedStr.replace(
cwdRegExp,
(_, g) => `<cwd>${g.replace(/\\/g, "/")}`
);
normalizedStr = normalizedStr.replace(
escapedCwdRegExp,
(_, g) => `<cwd>${g.replace(/\\\\/g, "/")}`
);
}
normalizedStr = normalizedStr.replace(
/@@ -\d+,\d+ \+\d+,\d+ @@/g,
"@@ ... @@"
);
return normalizedStr;
};
59 changes: 59 additions & 0 deletions packages/rspack-test-tools/src/helper/expect/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import prettyFormat from "pretty-format";

const ERROR_STACK_PATTERN = /(│.* at ).*/g;

const prettyFormatOptions = {
escapeRegex: false,
printFunctionName: false,
plugins: [
{
test(val: any) {
return typeof val === "string";
},
print(val: any) {
return `"${val
.replace(/\\/gm, "/")
.replace(/"/gm, '\\"')
.replace(/\r?\n/gm, "\\n")}"`;
}
}
]
};

function cleanErrorStack(message: string) {
return message.replace(ERROR_STACK_PATTERN, "$1xxx");
}

function cleanError(err: Error) {
const result: Partial<Record<keyof Error, any>> = {};
for (const key of Object.getOwnPropertyNames(err)) {
result[key as keyof Error] = err[key as keyof Error];
}

if (result.message) {
result.message = cleanErrorStack(err.message);
}

if (result.stack) {
result.stack = cleanErrorStack(result.stack);
}

return result;
}

export function normalizeDignostics(received: {
errors: Error[];
warnings: Error[];
}): string {
return prettyFormat(
{
errors: received.errors.map(e => cleanError(e)),
warnings: received.warnings.map(e => cleanError(e))
},
prettyFormatOptions
).trim();
}

export function normalizeError(received: Error): string {
return prettyFormat(cleanError(received), prettyFormatOptions).trim();
}
26 changes: 26 additions & 0 deletions packages/rspack-test-tools/src/helper/expect/placeholder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import path from "node:path";
const { createSnapshotSerializer } = require("path-serializer");

const placeholderSerializer = createSnapshotSerializer({
workspace: path.resolve(__dirname, "../../../../"),
replace: [
{
match: path.resolve(__dirname, "../../../rspack"),
mark: "rspack"
},
{
match: path.resolve(__dirname, "../../"),
mark: "test_tools"
},
{
match: /:\d+:\d+-\d+:\d+/g,
mark: "line_col_range"
},
{
match: /:\d+:\d+/g,
mark: "line_col"
}
]
});

export const normalizePlaceholder = placeholderSerializer.print;
12 changes: 12 additions & 0 deletions packages/rspack-test-tools/src/helper/expect/rspack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const normalizeStats = (stats: { value: string }): string => {
return (
stats.value
// CHANGE: Remove potential line break and "|" caused by long text
.replace(/((ERROR|WARNING)([\s\S](?!╭|├))*?)(\n {2}│ )/g, "$1")
// CHANGE: Update the regular expression to replace the 'Rspack' version string
.replace(/Rspack [^ )]+(\)?) compiled/g, "Rspack x.x.x$1 compiled")
.replace(/(\w)\\(\w)/g, "$1/$2")
.replace(/, additional resolving: X ms/g, "")
.replace(/Unexpected identifier '.+?'/g, "Unexpected identifier")
);
};
72 changes: 70 additions & 2 deletions packages/rspack-test-tools/src/helper/setup-expect.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,80 @@
// @ts-nocheck

import { normalizeCLR, normalizeCRLF } from "./expect/char";
import { normalizeDiff } from "./expect/diff";
import { normalizeDignostics, normalizeError } from "./expect/error";
import { normalizePlaceholder } from "./expect/placeholder";
import { normalizeStats } from "./expect/rspack";
import { toBeTypeOf } from "./expect/to-be-typeof";
import { toEndWith } from "./expect/to-end-with";
import { toMatchFileSnapshot } from "./expect/to-match-file-snapshot";
const { normalizePaths } = require("jest-serializer-path");

expect.extend({
// CHANGE: new test matcher for `rspack-test-tools`
// @ts-ignore
toMatchFileSnapshot,
toBeTypeOf,
toEndWith
});

const pipes = [
normalizeCLR,
normalizeCRLF,
normalizePlaceholder,
normalizePaths
];

const serialize = (
str: string,
extra: Array<(str: string) => string> = []
): string =>
[...pipes, ...extra].reduce((res, transform) => transform(res), str);

expect.addSnapshotSerializer({
test(received) {
return typeof received === "string";
},
print(received) {
return serialize(received as string);
}
});

// for diff
expect.addSnapshotSerializer({
test(received) {
return received?.constructor?.name === "RspackTestDiff";
},
print(received, next) {
return next(normalizeDiff(received as { value: string }));
}
});

// for errors
expect.addSnapshotSerializer({
test(received) {
return received?.constructor?.name === "RspackStatsDiagnostics";
},
print(received, next) {
return next(
normalizeDignostics(received as { errors: Error[]; warnings: Error[] })
);
}
});

expect.addSnapshotSerializer({
test(received) {
return typeof received?.message === "string";
},
print(received, next) {
return next(normalizeError(received as Error));
}
});

// for stats
expect.addSnapshotSerializer({
test(received) {
return received?.constructor?.name === "RspackStats";
},
print(received, next) {
return next(normalizeStats(received as { value: string }));
}
});
Loading

0 comments on commit 1b5b094

Please sign in to comment.