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
144 lines
4.3 KiB
TypeScript
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 }
|
|
},
|
|
})
|