orphic-cypress
Version:
Set of utilities and typescript transformers to cover storybook stories with cypress component tests
195 lines • 10.1 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeCyTests = void 0;
const csf_1 = require("@storybook/csf");
const testing_react_1 = require("@storybook/testing-react");
const react_1 = __importDefault(require("react"));
const ts = __importStar(require("typescript"));
const actions_1 = require("./actions");
const intercept_1 = require("./intercept");
const UnitTest_1 = require("./storybook/UnitTest");
/* istanbul ignore next */
class CyTestConfigError extends Error {
constructor(format, storyTitle) {
super(`Opted out of allowing the ${format} format. See your setupNodeEvents` +
` and/or env["orphic-cypress"].format.${format} to alter the config, or update the test${storyTitle ? ` for ${storyTitle}` : ""} by changing it to a supported format or moving it to an external file`);
Object.setPrototypeOf(this, new.target.prototype);
}
}
const evalTranspile = (code) => {
const transformed = eval(ts.transpile(`(React) => ${code}`, {
jsx: ts.JsxEmit.React,
module: ts.ModuleKind.ES2022,
target: ts.ScriptTarget.ES2022,
alwaysStrict: false,
}, "test.cy.tsx"))(react_1.default);
return eval(transformed);
};
/**
* Execute standard cypress tests against a set of storybook components.
* If the storybook story or object is normal, then it will perform a simple
* 'mount' and expect no errors to throw. If the story or object has a `cy`
* property, then the keys of that object will be used as 'it' descriptions
* and each test there will be executed.
*
* @throws CyTestConfigError
*/
const executeCyTests = (stories, describeText) => {
var _a, _b;
const describeFn = stories.default.cyOnly || ((_a = stories.default.parameters) === null || _a === void 0 ? void 0 : _a.cyOnly)
? describe.only
: stories.default.cySkip || ((_b = stories.default.parameters) === null || _b === void 0 ? void 0 : _b.cySkip)
? describe.skip
: describe;
describeFn(describeText || stories.default.title || "CyTest", () => {
var _a, _b;
// adding `cy` property to default is a way to add hooks like `beforeEach`
// for all tests in the file. I guess you could even write a test here.
const defaultCy = stories.default.cy || ((_a = stories.default.parameters) === null || _a === void 0 ? void 0 : _a.cy);
if (defaultCy) {
if (typeof defaultCy === "string")
evalTranspile(defaultCy)();
else
defaultCy();
}
const config = Cypress.env("orphic-cypress");
const cyIncludeStories = stories.default.cyIncludeStories ||
((_b = stories.default.parameters) === null || _b === void 0 ? void 0 : _b.cyIncludeStories);
if (cyIncludeStories) {
if (cyIncludeStories === true) {
delete stories.default.includeStories;
}
else {
stories.default.includeStories = cyIncludeStories;
}
}
const composed = (0, testing_react_1.composeStories)(stories);
const composedEntries = Object.entries(composed);
composedEntries.forEach(([name, Comp]) => {
/* istanbul ignore next */
if (typeof Comp !== "function")
return;
describe((0, csf_1.storyNameFromExport)(name), () => {
const story = stories[name];
const parameters = story.parameters || {};
const cyTest = story.cyTest || parameters.cyTest;
beforeEach(() => {
var _a, _b, _c;
/* istanbul ignore next */
if (cyTest && ((_a = config === null || config === void 0 ? void 0 : config.format) === null || _a === void 0 ? void 0 : _a.cyTest) === false) {
throw new CyTestConfigError("cyTest", stories.default.title);
}
(0, actions_1.stubStoryActions)(Comp, stories);
const mockData = [
...(((_b = stories.default.parameters) === null || _b === void 0 ? void 0 : _b.mockData) || []),
...(((_c = Comp.parameters) === null || _c === void 0 ? void 0 : _c.mockData) || []),
];
if (mockData.length > 0)
(0, intercept_1.mockToCyIntercept)(mockData);
});
// cy test format where a function can then contain 'it's and 'before' etc
// actions will be available at `cy.get("@actions")` or `this.actions`
// and you can skip/only in the test
if (cyTest) {
if (typeof cyTest === "string") {
return evalTranspile(cyTest)(Comp);
}
if (cyTest === true) {
if (!parameters.cyCodeBlock) {
throw new Error("Provided `cyTest: true` but did not provide `cyCodeBlock`, which is required");
}
const [description, cyTestFromBlock] = Object.entries((0, UnitTest_1.getStoryCyFromMdxCodeBlock)(stories.default.parameters, story.storyName, true))[0];
story.cyTest = cyTestFromBlock;
if (description) {
return describe(description, () => {
evalTranspile(cyTestFromBlock)(Comp);
});
}
return evalTranspile(cyTestFromBlock)(Comp);
}
return cyTest(Comp);
}
// cy object format for a more streamlined test that does the mount for you
const itFn = story.cyOnly || parameters.cyOnly
? it.only
: story.cySkip || parameters.cySkip
? it.skip
: it;
if (parameters.cyCodeBlock) {
// MUTATE STORY
story.cy = (0, UnitTest_1.getStoryCyFromMdxCodeBlock)(stories.default.parameters, story.storyName, true);
}
const storyCy = story.cy || parameters.cy;
if (storyCy) {
// cy is a function directly
if (typeof storyCy === "function" || typeof storyCy === "string") {
return itFn("should satisfy a cy test expectation", function () {
var _a;
/* istanbul ignore next */
if (((_a = config === null || config === void 0 ? void 0 : config.format) === null || _a === void 0 ? void 0 : _a.function) === false) {
throw new CyTestConfigError("function", stories.default.title);
}
cy.mount(react_1.default.createElement(Comp, { ...this.actions }));
if (typeof storyCy === "function")
return storyCy.bind(this)();
evalTranspile(storyCy).bind(this)();
});
}
// otherwise cy is an object with story descriptions as keys and test
// functions as values
return Object.entries(storyCy).forEach(([desc, cyTest]) => {
itFn(desc, function () {
var _a;
/* istanbul ignore next */
if (((_a = config === null || config === void 0 ? void 0 : config.format) === null || _a === void 0 ? void 0 : _a.object) === false) {
throw new CyTestConfigError("object", stories.default.title);
}
cy.mount(react_1.default.createElement(Comp, { ...this.actions }));
if (typeof cyTest === "string") {
evalTranspile(cyTest).bind(this)();
}
else {
cyTest.bind(this)();
}
});
});
}
// mdx files with no stories are docs only and will intentionally throw an error if rendered
if (name !== "__page") {
// no test defined, just check that it renders ok
itFn(`${name} should render ok`, function () {
cy.mount(react_1.default.createElement(Comp, { ...this.actions }));
});
}
});
});
});
};
exports.executeCyTests = executeCyTests;
//# sourceMappingURL=execute.js.map
;