UNPKG

creevey

Version:

Cross-browser screenshot testing tool for Storybook with fancy UI Runner

111 lines (94 loc) 3.3 kB
import { pathToFileURL } from 'url'; import { CreeveyStoryParams, CreeveyTestFunction } from '../../types.js'; import { loadThroughTSX } from '../utils.js'; // NOTE: Copy-pasted from @storybook/csf function toStartCaseStr(str: string) { return str .replace(/_/g, ' ') .replace(/-/g, ' ') .replace(/\./g, ' ') .replace(/([^\n])([A-Z])([a-z])/g, (_, $1, $2, $3) => `${$1} ${$2}${$3}`) .replace(/([a-z])([A-Z])/g, (_, $1, $2) => `${$1} ${$2}`) .replace(/([a-z])([0-9])/gi, (_, $1, $2) => `${$1} ${$2}`) .replace(/([0-9])([a-z])/gi, (_, $1, $2) => `${$1} ${$2}`) .replace(/(\s|^)(\w)/g, (_, $1, $2: string) => `${$1}${$2.toUpperCase()}`) .replace(/ +/g, ' ') .trim(); } /** * Remove punctuation and illegal characters from a story ID. * * See https://gist.github.com/davidjrice/9d2af51100e41c6c4b4a */ const sanitize = (string: string) => { return ( string .toLowerCase() // eslint-disable-next-line no-useless-escape .replace(/[ ’–—―′¿'`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '-') .replace(/-+/g, '-') .replace(/^-+/, '') .replace(/-+$/, '') ); }; const sanitizeSafe = (string: string, part: string) => { const sanitized = sanitize(string); if (sanitized === '') { throw new Error(`Invalid ${part} '${string}', must include alphanumeric characters`); } return sanitized; }; /** * Generate a storybook ID from a component/kind and story name. */ const toId = (kind: string, name?: string) => `${sanitizeSafe(kind, 'kind')}${name ? `--${sanitizeSafe(name, 'name')}` : ''}`; /** * Transform a CSF named export into a readable story name */ const storyNameFromExport = (key: string) => toStartCaseStr(key); export type CreeveyParamsByStoryId = Record<string, CreeveyStoryParams>; export default async function parse(files: string[]): Promise<CreeveyParamsByStoryId> { result = {}; await loadThroughTSX(async (load) => Promise.all( files.map(async (file) => { const fileUrl = pathToFileURL(file).toString(); await load(fileUrl); }), ), ); return result as CreeveyParamsByStoryId; } let result: Partial<CreeveyParamsByStoryId> = {}; let kindTitle = ''; let storyTitle = ''; let storyParams: CreeveyStoryParams | null = null; const setStoryParameters = (params: CreeveyStoryParams): void => { storyParams = params; }; const getStoryId = (kindTitle: string, storyTitle: string): string => { return toId(kindTitle, storyNameFromExport(storyTitle)); }; export const kind = (title: string, kindFn: () => void): void => { kindTitle = title; kindFn(); kindTitle = ''; }; export const story = ( title: string, storyFn: (arg: { setStoryParameters: (params: CreeveyStoryParams) => void }) => void, ): void => { storyTitle = title; storyParams = null; storyFn({ setStoryParameters }); const storyId = getStoryId(kindTitle, storyTitle); result[storyId] = Object.assign({}, storyParams, { tests: result[storyId]?.tests }); storyTitle = ''; storyParams = null; }; export const test = (title: string, testFn: CreeveyTestFunction): void => { const storyId = getStoryId(kindTitle, storyTitle); result[storyId] ??= {}; result[storyId].tests = Object.assign({}, result[storyId].tests, { [title]: testFn }); };