@testplane/storybook
Version:
Testplane plugin that enables runtime screenshot storybook tests autogeneration
270 lines (269 loc) • 11.7 kB
JavaScript
;
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extendStoriesFromStoryFile = void 0;
const module_1 = __importDefault(require("module"));
const inheritable_values_1 = require("./inheritable-values");
const Module = module_1.default; // eslint-disable-line @typescript-eslint/no-explicit-any
const MockedModuleProxySymbol = Symbol("@testplane/storybook:mocked-module-proxy");
let loggedStoryFileRequireError = Boolean(process.env.TESTPLANE_STORYBOOK_DISABLE_STORY_REQUIRE_WARNING);
function extendStoriesFromStoryFile(stories, { requireFn = require } = {}) {
var _a, _b;
const storiesMap = getStoriesMap(stories);
const storyPath = stories[0].absolutePath;
const storyFile = getStoryFile(storyPath, { requireFn });
const withStoryFileExtendedStories = stories;
const storyExtendedBaseConfig = {
skip: false,
browserIds: null,
assertViewOpts: {},
autoScreenshots: null,
autoscreenshotSelector: null,
autoScreenshotStorybookGlobals: {},
extraTests: null,
};
if (!storyFile) {
return withStoryFileExtendedStories.map(story => Object.assign(story, storyExtendedBaseConfig));
}
const storyTestplaneDefaultConfigs = ((_a = storyFile.default) === null || _a === void 0 ? void 0 : _a.testplaneConfig) || ((_b = storyFile.default) === null || _b === void 0 ? void 0 : _b.testplane) || {};
for (const storyName of Object.keys(storyFile)) {
if (storyName === "default") {
continue;
}
const storyMapKey = getStoryNameId(storyName);
if (!storiesMap.has(storyMapKey)) {
continue;
}
const storyContents = removeNestedMockedPropertiesPaths(storyFile[storyName]);
const storyTestlaneConfigs = storyContents.testplaneConfig || {};
const story = storiesMap.get(storyMapKey);
Object.assign(story, storyExtendedBaseConfig, storyTestplaneDefaultConfigs, storyTestlaneConfigs, {
extraTests: storyContents.testplane || null,
});
story.assertViewOpts = (0, inheritable_values_1.extractInheritedValue)((0, inheritable_values_1.inheritValue)(storyTestplaneDefaultConfigs.assertViewOpts, storyTestlaneConfigs.assertViewOpts), storyExtendedBaseConfig.assertViewOpts);
story.autoScreenshotStorybookGlobals =
(0, inheritable_values_1.inheritValue)(storyExtendedBaseConfig.autoScreenshotStorybookGlobals, storyTestplaneDefaultConfigs.autoScreenshotStorybookGlobals, storyTestlaneConfigs.autoScreenshotStorybookGlobals) || {};
}
for (const story of withStoryFileExtendedStories) {
for (const essentialProperty in storyExtendedBaseConfig) {
if (!Object.hasOwn(story, essentialProperty)) {
Object.assign(story, {
[essentialProperty]: storyExtendedBaseConfig[essentialProperty],
});
}
}
}
return withStoryFileExtendedStories;
}
exports.extendStoriesFromStoryFile = extendStoriesFromStoryFile;
function getStoriesMap(stories) {
const storiesMap = new Map();
stories.forEach(story => {
storiesMap.set(getStoryNameId(story.name), story);
});
return storiesMap;
}
function getStoryNameId(storyName) {
// https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L166
// eslint-disable-next-line no-control-regex
const nonAsciiWordRegExp = /[\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
return storyName.replace(nonAsciiWordRegExp, "").toLowerCase();
}
function getStoryFile(storyPath, { requireFn = require } = {}) {
const unmockFn = mockLoaders({ except: storyPath });
const disableWarningsMessage = [
"There could be other story files.",
"Set 'TESTPLANE_STORYBOOK_DISABLE_STORY_REQUIRE_WARNING' environment variable to hide this warning",
].join("\n");
let storyFile;
try {
storyFile = requireFn(storyPath);
}
catch (error) {
if (!loggedStoryFileRequireError) {
loggedStoryFileRequireError = true;
const warningMessage = [
`Testplane custom section is ignored in storyfile "${storyPath}",`,
`because the file could not be read:\n${error}\n${disableWarningsMessage}`,
].join(" ");
console.warn(warningMessage);
}
}
unmockFn();
const isStoryFileFailedToLoad = !storyFile || isMockedModuleProxy(storyFile);
if (isStoryFileFailedToLoad) {
return null;
}
const ignoredProperties = getStoryFilMockedeNestedPropertiesPaths(storyFile);
if (ignoredProperties.length && !loggedStoryFileRequireError) {
loggedStoryFileRequireError = true;
const warningMessage = [
`Following Testplane custom properties are ignored in storyfile "${storyPath}":\n`,
...ignoredProperties.map(property => `- ${property}\n`),
"Reason: Testplane couldn't handle imports in this file.\n",
disableWarningsMessage,
].join("");
console.warn(warningMessage);
}
return storyFile;
}
function mockLoaders(opts) {
const unmockModuleCbs = [".css", ".scss", ".svg", ".png", ".ico", ".jpg", ".jpeg", ".gif", ".yml"].map(mockModule);
const unmockFailedModuleCbs = [".js", ".ts", ".jsx", ".tsx", ".mjs", ".mts"].map(ext => mockFailedModule(ext, opts));
const unmockCb = () => unmockModuleCbs.concat(unmockFailedModuleCbs).forEach(unmock => unmock());
return unmockCb;
}
function mockModule(extension) {
const originalModule = Module._extensions[extension];
Module._extensions[extension] = () => void 0;
return () => {
Module._extensions[extension] = originalModule;
};
}
function isMockedModuleProxy(obj) {
return Boolean(obj && obj[MockedModuleProxySymbol] === true);
}
function getStoryFilMockedeNestedPropertiesPaths(storyFile) {
const results = [];
const customProperties = ["testplane", "testplaneConfig"];
for (const exportName in storyFile) {
if (isMockedModuleProxy(storyFile[exportName])) {
results.push(exportName);
}
else {
for (const customProperty of customProperties) {
const customMockedProperties = getNestedMockedPropertiesPaths(storyFile[exportName][customProperty], `${exportName}.${customProperty}`);
if (customMockedProperties) {
results.push(...customMockedProperties);
}
}
}
}
return results;
}
function getNestedMockedPropertiesPaths(obj, parentProperty = "") {
if (!obj || (typeof obj !== "object" && typeof obj !== "function")) {
return null;
}
if (isMockedModuleProxy(obj)) {
return [parentProperty];
}
const results = [];
for (const property in obj) {
const nestedValue = obj[property];
const nestedKey = parentProperty + "." + property;
const nestedResults = getNestedMockedPropertiesPaths(nestedValue, nestedKey);
if (nestedResults) {
results.push(...nestedResults);
}
}
return results.length ? results : null;
}
function removeNestedMockedPropertiesPaths(obj) {
if (isMockedModuleProxy(obj)) {
return {};
}
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
return obj;
}
const result = {};
for (const prop in obj) {
const property = prop;
if (!isMockedModuleProxy(obj[property])) {
result[property] = removeNestedMockedPropertiesPaths(obj[property]);
}
}
return result;
}
function mockFailedModule(extension, { except }) {
const originalModule = Module._extensions[extension];
const getMockedModuleProxy = () => // eslint-disable-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-empty-function
new Proxy(function () { }, {
get: (_, prop) => {
switch (prop) {
// string conversion
case "toString":
case "valueOf":
case Symbol.toPrimitive:
return () => "[proxy Object]";
case Symbol.toStringTag:
return "Proxy";
// iterators
case Symbol.iterator:
// eslint-disable-next-line @typescript-eslint/no-empty-function
return function* () { };
case Symbol.asyncIterator:
// eslint-disable-next-line @typescript-eslint/no-empty-function
return function () { return __asyncGenerator(this, arguments, function* () { }); };
// string methods
case Symbol.match:
return false;
case Symbol.matchAll:
return () => null;
case Symbol.search:
return () => -1;
case Symbol.split:
return (str) => str;
case Symbol.hasInstance:
return () => false;
case MockedModuleProxySymbol:
return true;
default:
return getMockedModuleProxy();
}
},
// traps
apply: getMockedModuleProxy,
construct: getMockedModuleProxy,
// inspection
getPrototypeOf: () => null,
has: () => false,
ownKeys: () => [],
getOwnPropertyDescriptor: () => void 0,
// extensibility checks
isExtensible: () => true,
preventExtensions: () => false,
// mutation
set: () => true,
defineProperty: () => true,
deleteProperty: () => true,
setPrototypeOf: () => true,
});
const applyMock = (filename) => {
require.cache[filename].loaded = true;
require.cache[filename].exports = getMockedModuleProxy();
};
Module._extensions[extension] = (m, filename) => {
if (filename === except) {
return originalModule(m, filename);
}
if (filename.includes("/node_modules/@storybook/")) {
return applyMock(filename);
}
try {
return originalModule(m, filename);
}
catch (e) {
applyMock(filename);
}
};
return () => {
Module._extensions[extension] = originalModule;
};
}
//# sourceMappingURL=extend-stories.js.map