Tldraw/config/setupJest.ts

84 wiersze
2.1 KiB
TypeScript
Czysty Zwykły widok Historia

2023-04-25 11:01:25 +00:00
import { equals, getObjectSubset, iterableEquality, subsetEquality } from '@jest/expect-utils'
import {
matcherHint,
printDiffOrStringify,
printExpected,
printReceived,
stringify,
} from 'jest-matcher-utils'
React-powered SVG exports (#3117) ## Migration path 1. If any of your shapes implement `toSvg` for exports, you'll need to replace your implementation with a new version that returns JSX (it's a react component) instead of manually constructing SVG DOM nodes 2. `editor.getSvg` is deprecated. It still works, but will be going away in a future release. If you still need SVGs as DOM elements rather than strings, use `new DOMParser().parseFromString(svgString, 'image/svg+xml').firstElementChild` ## The change in detail At the moment, our SVG exports very carefully try to recreate the visuals of our shapes by manually constructing SVG DOM nodes. On its own this is really painful, but it also results in a lot of duplicated logic between the `component` and `getSvg` methods of shape utils. In #3020, we looked at using string concatenation & DOMParser to make this a bit less painful. This works, but requires specifying namespaces everywhere, is still pretty painful (no syntax highlighting or formatting), and still results in all that duplicated logic. I briefly experimented with creating my own version of the javascript language that let you embed XML like syntax directly. I was going to call it EXTREME JAVASCRIPT or XJS for short, but then I noticed that we already wrote the whole of tldraw in this thing called react and a (imo much worse named) version of the javascript xml thing already existed. Given the entire library already depends on react, what would it look like if we just used react directly for these exports? Turns out things get a lot simpler! Take a look at lmk what you think This diff was intended as a proof of concept, but is actually pretty close to being landable. The main thing is that here, I've deliberately leant into this being a big breaking change to see just how much code we could delete (turns out: lots). We could if we wanted to make this without making it a breaking change at all, but it would add back a lot of complexity on our side and run a fair bit slower --------- Co-authored-by: huppy-bot[bot] <128400622+huppy-bot[bot]@users.noreply.github.com>
2024-03-25 14:16:55 +00:00
import { TextDecoder, TextEncoder } from 'util'
global.TextEncoder = TextEncoder
global.TextDecoder = TextDecoder
2023-04-25 11:01:25 +00:00
Image.prototype.decode = async function () {
return true
}
2023-04-25 11:01:25 +00:00
function convertNumbersInObject(obj: any, roundToNearest: number) {
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 isExpand = (expand?: boolean): boolean => expand !== false
const newActualObj = convertNumbersInObject(actual, roundToNearest)
const newExpectedObj = convertNumbersInObject(expected, roundToNearest)
const pass = equals(newActualObj, newExpectedObj, [iterableEquality, subsetEquality])
const message = pass
? () =>
// eslint-disable-next-line prefer-template
matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
`Expected: not ${printExpected(expected)}` +
(stringify(expected) !== stringify(actual)
? `\nReceived: ${printReceived(actual)}`
: '')
: () =>
// eslint-disable-next-line prefer-template
matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
printDiffOrStringify(
expected,
getObjectSubset(actual, expected),
EXPECTED_LABEL,
RECEIVED_LABEL,
isExpand(this.expand)
)
return { message, pass }
},
})