UNPKG

@typed/test

Version:
162 lines (124 loc) 4.63 kB
import { errorToString } from 'assertion-error-diff' import { strip } from 'typed-colors' import { cross, tick } from 'typed-figures' import { FailedTestResult, GroupResult, TestResult } from '../types' export function resultsToDom(results: TestResult[]): HTMLElement { return withChildren(flexColumn(), results.map(x => resultToDom(x))) } export function resultToDom(result: TestResult, nested: boolean = false): HTMLElement { if (result.type === 'pass') { return formatPassingResult(result, nested) } if (result.type === 'fail') { return formatFailingResult(result, nested) } if (result.type === 'skip') { return formatSkippedResult(result, nested) } return formatGroupResult(result) } function formatPassingResult({ name }: TestResult, nested: boolean): HTMLElement { const element = withChildren(flexContainer(), [greenTick(), testName(name)]) return marginTopIfNotNested(element, nested) } function formatFailingResult({ name, error }: FailedTestResult, nested: boolean): HTMLElement { const testNameElement = withChildren(flexContainer(), [redCross(), testName(name)]) const errorElement = errorToDom(error) const element = marginTopIfNotNested( withChildren(flexColumn(), [testNameElement, errorElement]), nested, ) return nested ? withStyle(element, { marginBottom: '0.5rem' }) : element } function errorToDom(error: Error) { const message = errorMessage(error) return withStyle(message, { marginLeft: '1.25rem' }) } function errorMessage(error: Error) { const message = strip(errorToString(error)).replace('- expected + actual\n', '') const parts = message.split(/\n/g) return withChildren( flexColumn(), parts.map((x, i) => (i > 0 ? withStyle(text(x), { marginLeft: '1rem' }) : text(x))), ) } function formatSkippedResult({ name }: TestResult, nested: boolean): HTMLElement { const element = withChildren(flexContainer(), [blueText('(Skipped) '), testName(name)]) return marginTopIfNotNested(element, nested) } function formatGroupResult(result: GroupResult): HTMLElement { const { results, name } = result const container = flexColumn() return withChildren(withStyle(container, { marginTop: '1rem' }), [ testName(name, true), withChildren( withStyle(flexColumn(), { paddingLeft: '1rem' }), results.map((x, i) => { const r = resultToDom(x, true) if (i > 0 && x.type !== 'group' && results[i - 1].type === 'group') { return withStyle(r, { marginTop: '1rem' }) } return r }), ), ]) } function testName(name: string, bold: boolean = false): HTMLElement { const itRegex = /^it\s/ const givenRegex = /^given\s/ if (itRegex.test(name)) { return withChildren(flexContainer(), [blueText('it '), text(name.replace(itRegex, '').trim())]) } if (givenRegex.test(name)) { const testNameParsed = name.replace(givenRegex, '').trim() return withChildren(flexContainer(), [ blueText('given ', bold), bold ? boldText(testNameParsed) : text(testNameParsed), ]) } return text(name) } function withChildren<A extends Element>(element: A, children: Element[]): A { children.forEach(child => element.appendChild(child)) return element } function boldText(txt: string): HTMLParagraphElement { return withStyle(text(txt), { fontWeight: '700' }) } function blueText(txt: string, bold: boolean = false): HTMLParagraphElement { return withStyle(bold ? boldText(txt) : text(txt), { color: 'blue' }) } function text(s: string): HTMLParagraphElement { const element = document.createElement('p') element.textContent = s return withStyle(element, { margin: '0 0.25rem' }) } function redCross(): HTMLParagraphElement { return withStyle(text(cross), { color: 'red' }) } function greenTick(): HTMLParagraphElement { return withStyle(text(tick), { color: 'green' }) } function flexColumn(): HTMLDivElement { return withStyle(document.createElement('div'), { display: 'inline-flex', flexDirection: 'column', }) } function flexContainer(): HTMLDivElement { return withStyle(document.createElement('div'), { display: 'inline-flex', flexDirection: 'row', }) } function marginTopIfNotNested<A extends HTMLElement>(el: A, nested: boolean): A { return nested ? el : withStyle(el, { marginTop: '1rem' } as any) } function withStyle<A extends HTMLElement>( el: A, styles: { [K in keyof A['style']]?: A['style'][K] }, ): A { const keys = Object.keys(styles) as Array<keyof A['style'] & keyof CSSStyleDeclaration> keys.forEach(key => (el.style[key] = styles[key] as any)) return el }