UNPKG

orphic-cypress

Version:

Set of utilities and typescript transformers to cover storybook stories with cypress component tests

195 lines 10.1 kB
"use strict"; 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