Files
upstream-tldraw/internal/config/vitest/setup.ts
upstream-mirror a774ac673a
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
2026-09-23 16:12:52 +05:00

144 lines
4.3 KiB
TypeScript

import { equals, getObjectSubset, iterableEquality, subsetEquality } from '@jest/expect-utils'
import {
matcherHint,
printDiffOrStringify,
printExpected,
printReceived,
stringify,
} from 'jest-matcher-utils'
if (typeof window !== 'undefined') {
await import('vitest-canvas-mock')
}
// Crypto fallback for environments without a native WebCrypto implementation (e.g. the ai package).
// jsdom provides window.crypto with subtle crypto, so this only kicks in elsewhere.
if (typeof globalThis.crypto === 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { Crypto } = require('@peculiar/webcrypto')
globalThis.crypto = new Crypto()
}
// Image.decode polyfill - this should be handled by individual package setups,
// but including it here as a fallback
if (typeof HTMLImageElement !== 'undefined') {
if (!HTMLImageElement.prototype.decode) {
HTMLImageElement.prototype.decode = function () {
return Promise.resolve()
}
}
}
// Path2D polyfills
if (typeof Path2D !== 'undefined' && typeof Path2D.prototype.roundRect !== 'function') {
Path2D.prototype.roundRect = function (x, y, w, h, _) {
this.rect(x, y, w, h)
}
}
// CSS.supports polyfill for tests that use color spaces (e.g., highlight shapes)
if (typeof CSS === 'undefined') {
;(global as any).CSS = {}
}
if (typeof CSS.supports === 'undefined') {
CSS.supports = () => false
}
// Pointer capture polyfill. jsdom implements the PointerEvent constructor but not the pointer
// capture model, so setPointerCapture/releasePointerCapture/hasPointerCapture are missing
// (https://github.com/jsdom/jsdom/pull/2666). Our canvas event handlers capture the pointer on
// pointerdown/up, so stub them out to avoid throwing.
if (typeof Element !== 'undefined') {
Element.prototype.setPointerCapture ??= function () {
// noop
}
Element.prototype.releasePointerCapture ??= function () {
// noop
}
Element.prototype.hasPointerCapture ??= function () {
return false
}
}
// Captured before any test file installs fake timers.
const realSetTimeout = globalThis.setTimeout
const realSetImmediate = globalThis.setImmediate
// Drain deferred React work before vitest tears down jsdom: exportToSvg unmounts its root in a
// setTimeout(0) and every react-dom commit schedules a setImmediate that reads `window.event`.
// Landing after teardown, those throw "window is not defined" as an unhandled error (flaky CI).
if (typeof window !== 'undefined') {
afterAll(async () => {
// two passes: pass 1 fires pending timeouts and the setImmediate they enqueue; pass 2 covers
// the scheduler re-arming itself when it yields mid-work
for (let i = 0; i < 2; i++) {
await new Promise((resolve) => realSetTimeout(resolve, 0))
await new Promise((resolve) => realSetImmediate(resolve))
}
})
}
function convertNumbersInObject(obj: any, roundToNearest: number): any {
if (!obj) return obj
if (Array.isArray(obj)) {
return obj.map((x) => convertNumbersInObject(x, roundToNearest))
}
if (typeof obj === 'number') {
// || 0 to avoid -0
return Math.round(obj / roundToNearest) * roundToNearest || 0
}
if (typeof obj !== 'object') {
return obj
}
const r: any = {
__converted: true,
}
for (const k of Object.keys(obj)) {
r[k] = convertNumbersInObject(obj[k], roundToNearest)
}
return r
}
expect.extend({
toCloselyMatchObject(actual: any, expected: any, roundToNearest = 0.0001) {
const matcherName = 'toCloselyMatchObject'
const options = {
isNot: this.isNot,
promise: this.promise,
}
const EXPECTED_LABEL = 'Expected'
const RECEIVED_LABEL = 'Received'
const newActualObj = convertNumbersInObject(actual, roundToNearest)
const newExpectedObj = convertNumbersInObject(expected, roundToNearest)
const pass = equals(newActualObj, newExpectedObj, [iterableEquality, subsetEquality])
const message = pass
? () =>
matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
`Expected: not ${printExpected(expected)}` +
(stringify(expected) !== stringify(actual)
? `\nReceived: ${printReceived(actual)}`
: '')
: () =>
matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
printDiffOrStringify(
expected,
getObjectSubset(actual, expected),
EXPECTED_LABEL,
RECEIVED_LABEL,
true
)
return { message, pass }
},
})