Public
Edited
Oct 9, 2023
Importers
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
it('does the thing', () => {
expect('the thing').toBe('the thing')
})
Insert cell
it('should do the thing', async () => {
await Promises.delay(300)
expect('not the thing').toBe('the thing')
})
Insert cell
describe('a complex gizmo', () => {
it('does thing 1', () => {
expect(1).toBe(1)
})

it('has property 2 that takes long to compute', async () => {
await Promises.delay(750)
expect(2).toBe(2)
})

it('has property 3 that takes long to compute', async () => {
await Promises.delay(1500)
expect(2).toBe(2)
})

it('has property 4 that takes long to compute', async () => {
await Promises.delay(2500)
expect(2).toBe(2)
})
})
Insert cell
describe('another very complex system', () => {
describe('the larger subsystem', () => {
it('does thing 5', async () => {
expect(null).toBeNull()
})
it('should be able to do thing 6', async () => {
await Promises.delay(1000)
expect('could not do it 😥').toBe('did it 👍')
})
})

describe('the smaller subsystem', () => {
it('is pretty solid', async () => {
await Promises.delay(2000)
expect(true).toBeTruthy()
})
})

it('has another unrelated feature', () => {
})
})
Insert cell
Insert cell
renderLabel = (type, name) => htl.html.fragment`<span>
<strong>
<span style=${{opacity: 0.4}}>${type}</span> ${name}
</strong>
${type === 'describe' ? htl.html.fragment`<span style=${{opacity: 0.4}}>with</span>` : undefined}
</span>`
Insert cell
renderDuration = (duration) => htl.html.fragment`
<span>
<span style=${{opacity: 0.4}}>in</span>
<span style=${{opacity: 0.4, /*color: '#6a9eff'*/}}>${durationFormat.format(duration)}ms</span>
</span>
`
Insert cell
renderPassingStatistics = ({passingTests, totalTests}) => htl.html.fragment`
<span style=${{color: !passingTests ? 'grey' : passingTests.length === totalTests.length ? 'green' : 'red'}}>
${passingTests === undefined ? `${totalTests.length} running` : undefined}
${passingTests && `${passingTests?.length ?? '?'} of ${totalTests.length} passing`}
</span>
`
Insert cell
renderMessage = message => message
? htl.html.fragment`
<pre style=${{color: 'red', margin: 0, gridColumnStart: 2}}>
<code>${message}</code>
</pre>
`
: undefined
Insert cell
renderTest = (task, {noFragment} = {}) => {
const {name, promise} = task
const render = ({pass, message, expected, actual, duration} = {}) => (noFragment ? htl.html : htl.html.fragment)`<div style=${{
display: 'grid',
gridTemplateColumns: 'auto 1fr',
gap: '0.25em',
border: '1px solid #ddd',
borderRadius: '0.15em',
padding: '0.2em',
paddingBottom: '0.05em'
}}>
<span style=${{width: '24px', display: 'flex', justifyContent: 'center', alignItems: 'center'}}>${pass === undefined ? spinnerIcon(19)/*'🔁'*/ : pass ? '✅' : '❌'}</span>
<span>
${renderLabel('it', name)}
${duration && renderDuration(duration)}
</span>
${message && renderMessage(message)}
</div>`

const parent = document.createElement('div')
const element = render()
parent.appendChild(element)

promise.then((result) => {
parent.replaceChildren(render(result))
})

return parent
}
Insert cell
renderSuite = {
const renderHeader = (task, guid, result = {}, results) => {
const {name, promise, tasks, isNested} = task
const {duration} = result

const totalTests = tasks.reduce(flattenTasks, [])
const passingTests = results?.filter(x => x.pass)
const success = results?.every(x => x.pass)
const aggregatedColor = success === undefined ? 'gray' : success ? 'green' : 'red'

return htl.html.fragment`<span class="describe-header-${guid}" style=${{gridArea: '1 / 2 / 2 / 3', paddingTop: '0.1em'}}>
${renderLabel('describe', name)}
${renderPassingStatistics({passingTests, totalTests})}
${duration && renderDuration(duration)}
</span>`
}

const renderSuiteInternal = (task, guid, {noFragment} = {}) => {
const {tasks, isNested} = task
return (noFragment ? htl.html : htl.html.fragment)`
<div style=${{display: 'grid', gridTemplateColumns: 'auto 1fr auto', gap: '0.25em', alignItems: 'center'}}>
${isNested ? undefined : htl.html.fragment`<style>
.icon-spinning-${hash} {
animation: spinning-${hash} 1.5s infinite linear;
}
@keyframes spinning-${hash} {
0% {transform: rotate(0deg)}
100% {transform: rotate(360deg)}
}
</style>`}
<span style=${{gridArea: '1 / 1 / 2 / 2', padding: '0.3em', display: 'flex', alignItems: 'center', color: isNested ? '#00a1df' : '#006edf'}}>
${true ? undefined : isNested ? '🔹' : '🔷'}${systemIcon(20)}
</span>
${renderHeader(task, guid)}
<div style=${{
alignSelf: 'stretch',
gridArea: '1 / 1 / 2 / span 3',
border: '1px solid #ddd',
borderRadius: '0.15em',
padding: '0.2em',
paddingBottom: '0.05em'
}}></div>
<div style=${{gridColumn: '2 / span 2', display: 'flex', flexDirection: 'column', gap: '0.25em'}}>
${tasks.map(task => renderTestOrSuite(task))}
</div>
</div>
`
}

const renderSuiteTask = (task, {noFragment} = {}) => {
const guid = crypto.randomUUID()

const parent = document.createElement('div')
parent.appendChild(renderSuiteInternal(task, guid, {noFragment}))

const resultsPromise = Promise.all(task.tasks.reduce(flattenTasks, []).map(task => task.promise))
Promise.all([task.promise, resultsPromise]).then(async ([result, results]) => {
const currentHeader = parent.querySelector(`.describe-header-${guid}`)
currentHeader.replaceWith(renderHeader(task, guid, result, results))
})

return parent
}

const renderTestOrSuite = task => task.tasks ? renderSuiteTask(task) : renderTest(task)
return renderSuiteTask
}
Insert cell
systemIcon = (pixelSize) => htl.svg`<svg width="${pixelSize}px" height="${pixelSize}px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;stroke: none;fill: currentColor;">
<g transform="matrix(1,0,0,1,-81,-344)">
<g transform="matrix(0.204634,0,0,0.213903,66.3397,272.195)">
<path d="M115.019,339.258L76.002,339.258L76.002,376.584L115.019,376.584L115.019,339.258ZM105.245,348.608L105.245,367.234C105.245,367.234 85.776,367.234 85.776,367.234C85.776,367.234 85.776,348.608 85.776,348.608L105.245,348.608Z"/>
</g>
<g transform="matrix(0.204634,0,0,0.213903,76.2889,272.195)">
<path d="M115.019,339.258L76.002,339.258L76.002,376.584L115.019,376.584L115.019,339.258ZM105.245,348.608L105.245,367.234C105.245,367.234 85.776,367.234 85.776,367.234C85.776,367.234 85.776,348.608 85.776,348.608L105.245,348.608Z"/>
</g>
<g transform="matrix(0.204634,0,0,0.213903,76.2889,282.144)">
<path d="M115.019,339.258L76.002,339.258L76.002,376.584L115.019,376.584L115.019,339.258ZM105.245,348.608L105.245,367.234C105.245,367.234 85.776,367.234 85.776,367.234C85.776,367.234 85.776,348.608 85.776,348.608L105.245,348.608Z"/>
</g>
<g transform="matrix(0.204634,0,0,0.213903,66.3397,282.144)">
<path d="M115.019,339.258L76.002,339.258L76.002,376.584L115.019,376.584L115.019,339.258ZM105.245,348.608L105.245,367.234C105.245,367.234 85.776,367.234 85.776,367.234C85.776,367.234 85.776,348.608 85.776,348.608L105.245,348.608Z"/>
</g>
</g>
</svg>`
Insert cell
spinnerIcon = (pixelSize = 19, color = 'gray') => htl.svg`<svg width="${pixelSize}px" height="${pixelSize}px" viewBox="0 0 19 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-miterlimit:5;transform-origin: ${(pixelSize - 1) / 2}px ${(pixelSize - 1) / 2}px;stroke:${color}" class="icon-spinning-${hash}">
<path d="M9,0.5L9,4.492" style="fill:none;stroke-width:2px;"/>
<path d="M9,13.508L9,17.5" style="fill:none;stroke-width:2px;"/>
<path d="M17.5,9L13.508,9" style="fill:none;stroke-width:2px;"/>
<path d="M4.492,9L0.5,9" style="fill:none;stroke-width:2px;"/>
<path d="M15.01,2.99L12.188,5.812" style="fill:none;stroke-width:2px;"/>
<path d="M5.812,12.188L2.99,15.01" style="fill:none;stroke-width:2px;"/>
<path d="M15.01,15.01L12.188,12.188" style="fill:none;stroke-width:2px;"/>
<path d="M5.812,5.812L2.99,2.99" style="fill:none;stroke-width:2px;"/>
</svg>`
Insert cell
Insert cell
hash = crypto.randomUUID()
Insert cell
durationFormat = new Intl.NumberFormat({ maximumSignificantDigits: 2 })
Insert cell
flattenResults = {
const flattenResultsRecursive = (acc, result) => acc.concat(result.results ? result.results.reduce(flattenResultsRecursive, []) : result)

return flattenResultsRecursive
}
Insert cell
flattenTasks = {
const flattenTasksRecursive = (acc, task) => acc.concat(task.tasks?.reduce(flattenTasksRecursive, []) ?? task)

return flattenTasksRecursive
}
Insert cell
Insert cell
it = {
const it = (testName, testFunction) => {
const scope = describe.getCurrentScope()
const start = performance.now()

const promise = new Promise(async (resolve) => {
let result
try {
await testFunction()
result = {pass: true}
} catch(error) {
if (error.matcherResult) {
result = error.matcherResult
} else {
result = {message: error.message, pass: false}
}
}

const end = performance.now()

result.duration = end - start

resolve(result)
})

const task = {name: testName, promise}

scope?.register(task)

if (scope) {
return task
} else {
return renderTest(task, {noFragment: true})
}
}

return it
}
Insert cell
describe = {
const scopes = []

const describe = async (suiteName, testFunction) => {
const scope = describe.getCurrentScope()
const start = performance.now()

const tasks = []

scopes.push({
registerNestedScope: (suiteName, promise) => tasks.push({suiteName, promise}),
registerTest: (testName, promise) => tasks.push({testName, promise}),
register: task => tasks.push(task)
})

const promise = new Promise(async (resolve) => {
testFunction()

scopes.pop()
const results = await Promise.all(tasks.map(task => task.promise))

const end = performance.now()

const result = {duration: end - start}
resolve(result)
})

const task = {name: suiteName, promise, tasks, isNested: !!scope}

scope?.register(task)

if (scope) {
return task
} else {
return renderSuite(task, {noFragment: true})
}
}

describe.getCurrentScope = () => scopes[scopes.length - 1]

return describe
}
Insert cell
expect = import('https://cdn.skypack.dev/@storybook/expect@28.1.3-5?min').then(x => x.default)
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more