UNPKG

@workday/canvas-kit-docs

Version:

Documentation components of Canvas Kit components

83 lines (82 loc) 3.14 kB
import fs from 'node:fs/promises'; import path from 'node:path'; import typescript from 'typescript'; /** * Reads a spec file by getting the file contents as a string, passing through the TypeScript * transpiler, setting up custom `describe`, `it`, `beforeEach`, etc functions and runs the file * contents through `eval()` with those custom functions to extract out a JSON object representing * the structure of a Spec file. We use `eval` instead of an AST parser to handle meta coding like * `[a, b].forEach(() => { it('...') })`. Running the contents through a JavaScript context means * the file contents can be extracted via any runtime tricks used in spec files. */ export async function parseSpecFile(file) { const [_, fileName] = file.match(/.+(cypress\/.+)$/) || []; if (fileName) { const contents = await fs .readFile(file) .then(contents => contents.toString()) .then(contents => contents.replace(/import (.+) from .+/g, (substr, imports) => { if (imports.includes('{')) { return `const ${imports.replace(/[{}]/g, '')} = () => {}`; } if (/react/g.test(imports)) { return `const React = {createElement: () => {}}`; } return ''; })) // remove imports .then(contents => typescript.transpile(contents, { jsx: typescript.JsxEmit.React })) .then(contents => { let children = []; // eslint-disable-next-line no-empty-function const noop = () => { }; // define for the eval() // @ts-ignore const before = noop; // @ts-ignore const beforeEach = noop; // @ts-ignore const after = noop; // @ts-ignore const afterEach = noop; const describe = (name, optionsOrCb, cb) => { const childrenBefore = children; const obj = { type: 'describe', name, children: [], }; children.push(obj); children = obj.children; if (typeof optionsOrCb === 'function') { optionsOrCb(); } else if (typeof cb === 'function') { cb(); } children = childrenBefore; }; describe.skip = noop; describe.only = noop; // @ts-ignore const context = describe; const it = (name) => { const obj = { type: 'it', name, }; children.push(obj); }; it.skip = noop; it.only = noop; // eslint-disable-next-line no-eval eval(contents); return { type: 'file', name: path.basename(file).replace(`.spec${path.extname(file)}`, ''), children, }; }); return contents; } return null; }