Skip to content

Commit

Permalink
fix(component-testing): correctly import components with css from thi…
Browse files Browse the repository at this point in the history
…rd party libraries
  • Loading branch information
DudaGod authored and KuznetsovRoman committed Nov 13, 2024
1 parent 7f5240f commit c368b1b
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 14 deletions.
29 changes: 29 additions & 0 deletions src/bundle/test-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import * as nodePath from "node:path";
import * as babel from "@babel/core";
import { addHook } from "pirates";
import { TRANSFORM_EXTENSIONS, JS_EXTENSION_RE } from "./constants";
import { requireModuleSync } from "../utils/module";

import type { NodePath, PluginObj, TransformOptions } from "@babel/core";
import type { ImportDeclaration } from "@babel/types";

const STYLE_EXTESTION_RE = /\.(css|less|scss|sass|styl|stylus|pcss)$/;
const IGNORE_STYLE_ERRORS = ["Unexpected token"];

export const setupTransformHook = (opts: { removeNonJsImports?: boolean } = {}): VoidFunction => {
const transformOptions: TransformOptions = {
browserslistConfigFile: false,
Expand Down Expand Up @@ -34,6 +38,15 @@ export const setupTransformHook = (opts: { removeNonJsImports?: boolean } = {}):

if (extname && !extname.match(JS_EXTENSION_RE)) {
path.remove();
return;
}

try {
requireModuleSync(path.node.source.value);
} catch (err) {
if (shouldIgnoreImportError(err as Error)) {
path.remove();
}
}
},
},
Expand All @@ -52,3 +65,19 @@ export const setupTransformHook = (opts: { removeNonJsImports?: boolean } = {}):

return revertTransformHook;
};

function shouldIgnoreImportError(err: Error): boolean {
const shouldIgnoreImport = IGNORE_STYLE_ERRORS.some(ignoreImportErr => {
return (err as Error).message.startsWith(ignoreImportErr);
});

if (!shouldIgnoreImport) {
return false;
}

const firstStackFrame = (err as Error).stack?.split("\n")[0] || "";
const filePath = firstStackFrame.split(":")[0];
const isStyleFilePath = STYLE_EXTESTION_RE.test(filePath);

return isStyleFilePath;
}
4 changes: 4 additions & 0 deletions src/utils/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ export const requireModule = async <T = unknown>(modulePath: string): Promise<T>

return require(isModuleLocal ? path.resolve(modulePath) : modulePath);
};

export const requireModuleSync = (modulePath: string): unknown => {
return require(modulePath);
};
81 changes: 67 additions & 14 deletions test/src/test-reader/test-transformer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as pirates from "pirates";
import sinon, { SinonStub } from "sinon";
import proxyquire from "proxyquire";
import { setupTransformHook, TRANSFORM_EXTENSIONS } from "../../../src/test-reader/test-transformer";

describe("test-transformer", () => {
Expand Down Expand Up @@ -59,29 +60,81 @@ describe("test-transformer", () => {
});
});

[true, false].forEach(removeNonJsImports => {
describe(`should ${removeNonJsImports ? "" : "not "}remove non-js imports`, () => {
[".css", ".less", ".scss", ".jpg", ".png", ".woff"].forEach(extName => {
it(`asset with extension: "${extName}"`, () => {
describe("'removeNonJsImports' option", () => {
[true, false].forEach(removeNonJsImports => {
describe(`should ${removeNonJsImports ? "" : "not "}remove non-js imports`, () => {
[".css", ".less", ".scss", ".jpg", ".png", ".woff"].forEach(extName => {
it(`asset with extension: "${extName}"`, () => {
let transformedCode;
const fileName = `some${extName}`;
(pirates.addHook as SinonStub).callsFake(cb => {
transformedCode = cb(`import "${fileName}"`, fileName);
});

setupTransformHook({ removeNonJsImports });

const expectedCode = ['"use strict";'];

if (!removeNonJsImports) {
expectedCode.push("", `require("some${extName}");`);
}

expectedCode.push("//# sourceMappingURL=");

assert.match(transformedCode, expectedCode.join("\n"));
});
});
});
});

describe("should remove third party import with error from", () => {
[".css", ".less", ".scss", ".sass", ".styl", ".stylus", ".pcss"].forEach(extName => {
it(`${extName} style file`, () => {
const moduleName = "some-module";
const error = { message: "Unexpected token {", stack: `foo${extName}:100500\nbar\nqux` };

const { setupTransformHook } = proxyquire("../../../src/test-reader/test-transformer", {
"../bundle": proxyquire.noCallThru().load("../../../src/bundle/test-transformer", {
"../utils/module": {
requireModuleSync: sandbox.stub().withArgs(moduleName).throws(error),
},
}),
});

let transformedCode;
const fileName = `some${extName}`;

(pirates.addHook as SinonStub).callsFake(cb => {
transformedCode = cb(`import "${fileName}"`, fileName);
transformedCode = cb(`import "${moduleName}"`, moduleName);
});

setupTransformHook({ removeNonJsImports });
setupTransformHook({ removeNonJsImports: true });

const expectedCode = ['"use strict";'];
assert.notMatch(transformedCode, new RegExp(`require\\("${moduleName}"\\)`));
});
});
});

if (!removeNonJsImports) {
expectedCode.push("", `require("some${extName}");`);
}
it("should not remove third party import with error not from style file", () => {
const moduleName = "some-module";
const error = { message: "Some error", stack: `foo.js:100500\nbar\nqux` };

const { setupTransformHook } = proxyquire("../../../src/test-reader/test-transformer", {
"../bundle": proxyquire.noCallThru().load("../../../src/bundle/test-transformer", {
"../utils/module": {
requireModuleSync: sandbox.stub().withArgs(moduleName).throws(error),
},
}),
});

expectedCode.push("//# sourceMappingURL=");
let transformedCode;

assert.match(transformedCode, expectedCode.join("\n"));
});
(pirates.addHook as SinonStub).callsFake(cb => {
transformedCode = cb(`import "${moduleName}"`, moduleName);
});

setupTransformHook({ removeNonJsImports: true });

assert.match(transformedCode, new RegExp(`require\\("${moduleName}"\\)`));
});
});
});
Expand Down

0 comments on commit c368b1b

Please sign in to comment.