Remove jest-canvas-mock (#66854)

This commit is contained in:
Ryan Clark
2026-05-19 12:55:49 +00:00
committed by GitHub
parent 4b63f586c2
commit a3bed6930b
25 changed files with 535 additions and 105 deletions
+1
View File
@@ -52,5 +52,6 @@ module.exports = {
setupFilesAfterEnv: [
'<rootDir>/web/packages/build/jest/setupTests.ts',
'<rootDir>/web/packages/build/jest/customMatchers.ts',
'<rootDir>/web/packages/build/jest/canvasMock.ts',
],
};
-1
View File
@@ -53,7 +53,6 @@
"eslint-plugin-testing-library": "7.16.2",
"eslint-plugin-unused-imports": "^4.4.1",
"jest": "^30.3.0",
"jest-canvas-mock": "^2.5.2",
"jsdom-testing-mocks": "^1.16.0",
"msw": "2.14.2",
"msw-storybook-addon": "^2.0.7",
-28
View File
@@ -6,11 +6,6 @@ settings:
pnpmfileChecksum: sha256-TR6zTaL3sXZMyu+QoRdd27aSUTH6sH0gp+M2XjxgTUI=
patchedDependencies:
[email protected]:
hash: 8329a68f6b8f930f6204c405a071859893962d4a6641b6c2c2bb33122c0441b1
path: web/patches/[email protected]
importers:
.:
@@ -184,9 +179,6 @@ importers:
jest:
specifier: ^30.3.0
version: 30.3.0(@types/[email protected])([email protected])
jest-canvas-mock:
specifier: ^2.5.2
version: 2.5.2(patch_hash=8329a68f6b8f930f6204c405a071859893962d4a6641b6c2c2bb33122c0441b1)
jsdom-testing-mocks:
specifier: ^1.16.0
version: 1.16.0
@@ -4166,9 +4158,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
[email protected]:
resolution: {integrity: sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==}
[email protected]:
resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
@@ -5088,9 +5077,6 @@ packages:
engines: {node: '>=10'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==}
[email protected]:
resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
@@ -5600,9 +5586,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==}
[email protected]:
resolution: {integrity: sha512-5iXz5n9LWQzx/C2WesGFfpE6RLamzdHwsn3KpfzShwbfIqs7stnoEpaNErf/7+3mbxwZ4s8Foq7I0tPxw7BWHg==}
[email protected]:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -10814,8 +10797,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
css-tree: 2.2.1
@@ -11835,11 +11816,6 @@ snapshots:
filelist: 1.0.4
minimatch: 3.1.5
[email protected](patch_hash=8329a68f6b8f930f6204c405a071859893962d4a6641b6c2c2bb33122c0441b1):
dependencies:
cssfontparser: 1.2.1
moo-color: 1.0.2
[email protected]:
dependencies:
execa: 5.1.1
@@ -12518,10 +12494,6 @@ snapshots:
[email protected]: {}
[email protected]:
dependencies:
color-name: 1.1.4
[email protected]: {}
[email protected]([email protected](@types/[email protected])([email protected])):
-3
View File
@@ -39,8 +39,5 @@ shellEmulator: true
# affect performance outside of docker.
# https://github.com/gravitational/teleport/issues/46019
packageImportMethod: clone-or-copy
patchedDependencies:
# Needed for roundRect support which was merged to jest-canvas-mock but not released.
[email protected]: web/patches/[email protected]
# Do not install packages released less than 2 days (2880 minutes) ago
minimumReleaseAge: 2880
+534
View File
@@ -0,0 +1,534 @@
/**
* Teleport
* Copyright (C) 2026 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Minimal in-repo replacement for jest-canvas-mock. jsdom does not implement
// HTMLCanvasElement.getContext('2d'), and a handful of tests inspect the
// recorded canvas events to assert what was drawn (see SessionRecordings
// timeline renderers). This file mocks just enough of CanvasRenderingContext2D
// for those tests, exposing the same __getEvents/__getDrawCalls/__getPath
// helpers that jest-canvas-mock used to provide.
type Transform = [number, number, number, number, number, number];
type CanvasStyle = string | CanvasGradient | CanvasPattern;
type EventProps = { [key: string]: any };
export interface CanvasRenderingContext2DEvent {
type: string;
transform: Transform;
props: EventProps;
}
const IDENTITY: Transform = [1, 0, 0, 1, 0, 0];
class MockTextMetrics implements TextMetrics {
readonly width: number;
readonly actualBoundingBoxLeft = 0;
readonly actualBoundingBoxRight = 0;
readonly fontBoundingBoxAscent = 0;
readonly fontBoundingBoxDescent = 0;
readonly actualBoundingBoxAscent = 0;
readonly actualBoundingBoxDescent = 0;
readonly emHeightAscent = 0;
readonly emHeightDescent = 0;
readonly hangingBaseline = 0;
readonly alphabeticBaseline = 0;
readonly ideographicBaseline = 0;
constructor(text: string) {
this.width = text.length;
}
}
class MockCanvasGradient implements CanvasGradient {
addColorStop(_offset: number, _color: string) {}
}
class MockCanvasPattern implements CanvasPattern {
setTransform(_transform?: DOMMatrix2DInit) {}
}
function createEvent(
type: string,
props: EventProps
): CanvasRenderingContext2DEvent {
return { type, transform: IDENTITY.slice() as Transform, props };
}
class MockCanvasRenderingContext2D {
private _events: CanvasRenderingContext2DEvent[] = [];
private _drawCalls: CanvasRenderingContext2DEvent[] = [];
private _path: CanvasRenderingContext2DEvent[] = [
createEvent('beginPath', {}),
];
private _clip: CanvasRenderingContext2DEvent[] = [];
fillStyle: CanvasStyle = '#000000';
strokeStyle: CanvasStyle = '#000000';
font = '10px sans-serif';
textAlign: CanvasTextAlign = 'start';
textBaseline: CanvasTextBaseline = 'alphabetic';
direction: CanvasDirection = 'inherit';
lineWidth = 1;
lineCap: CanvasLineCap = 'butt';
lineJoin: CanvasLineJoin = 'miter';
miterLimit = 10;
lineDashOffset = 0;
globalAlpha = 1;
globalCompositeOperation: GlobalCompositeOperation = 'source-over';
shadowBlur = 0;
shadowColor = 'rgba(0, 0, 0, 0)';
shadowOffsetX = 0;
shadowOffsetY = 0;
imageSmoothingEnabled = true;
imageSmoothingQuality: ImageSmoothingQuality = 'low';
filter = 'none';
fontKerning: CanvasFontKerning = 'auto';
fontStretch: CanvasFontStretch = 'normal';
fontVariantCaps: CanvasFontVariantCaps = 'normal';
letterSpacing = '0px';
wordSpacing = '0px';
textRendering: CanvasTextRendering = 'auto';
readonly canvas: HTMLCanvasElement;
constructor(canvas: HTMLCanvasElement) {
this.canvas = canvas;
}
private push(
type: string,
props: EventProps = {}
): CanvasRenderingContext2DEvent {
const event = createEvent(type, props);
this._events.push(event);
return event;
}
private pushDraw(
type: string,
props: EventProps = {}
): CanvasRenderingContext2DEvent {
const event = this.push(type, props);
this._drawCalls.push(event);
return event;
}
private pushPath(
type: string,
props: EventProps = {}
): CanvasRenderingContext2DEvent {
const event = this.push(type, props);
this._path.push(event);
return event;
}
__getEvents(): CanvasRenderingContext2DEvent[] {
return this._events.slice();
}
__clearEvents() {
this._events = [];
}
__getDrawCalls(): CanvasRenderingContext2DEvent[] {
return this._drawCalls.slice();
}
__clearDrawCalls() {
this._drawCalls = [];
}
__getPath(): CanvasRenderingContext2DEvent[] {
return this._path.slice();
}
__clearPath() {
this._path = [createEvent('beginPath', {})];
}
__getClippingRegion(): CanvasRenderingContext2DEvent[] {
return this._clip.slice();
}
save() {
this.push('save');
}
restore() {
this.push('restore');
}
scale(x: number, y: number) {
this.push('scale', { x, y });
}
rotate(angle: number) {
this.push('rotate', { angle });
}
translate(x: number, y: number) {
this.push('translate', { x, y });
}
transform(a: number, b: number, c: number, d: number, e: number, f: number) {
this.push('transform', { a, b, c, d, e, f });
}
setTransform(
a?: number | DOMMatrix2DInit,
b?: number,
c?: number,
d?: number,
e?: number,
f?: number
) {
this.push('setTransform', { a, b, c, d, e, f });
}
resetTransform() {
this.push('resetTransform');
}
getTransform(): DOMMatrix {
return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 } as DOMMatrix;
}
beginPath() {
this.__clearPath();
this._events.push(this._path[0]);
}
closePath() {
this.pushPath('closePath');
}
moveTo(x: number, y: number) {
this.pushPath('moveTo', { x, y });
}
lineTo(x: number, y: number) {
this.pushPath('lineTo', { x, y });
}
bezierCurveTo(
cp1x: number,
cp1y: number,
cp2x: number,
cp2y: number,
x: number,
y: number
) {
this.pushPath('bezierCurveTo', { cp1x, cp1y, cp2x, cp2y, x, y });
}
quadraticCurveTo(cpx: number, cpy: number, x: number, y: number) {
this.pushPath('quadraticCurveTo', { cpx, cpy, x, y });
}
arc(
x: number,
y: number,
radius: number,
startAngle: number,
endAngle: number,
anticlockwise = false
) {
this.pushPath('arc', {
x,
y,
radius,
startAngle,
endAngle,
anticlockwise,
});
}
arcTo(x1: number, y1: number, x2: number, y2: number, radius: number) {
this.pushPath('arcTo', { x1, y1, x2, y2, radius });
}
ellipse(
x: number,
y: number,
radiusX: number,
radiusY: number,
rotation: number,
startAngle: number,
endAngle: number,
anticlockwise = false
) {
this.pushPath('ellipse', {
x,
y,
radiusX,
radiusY,
rotation,
startAngle,
endAngle,
anticlockwise,
});
}
rect(x: number, y: number, width: number, height: number) {
this.pushPath('rect', { x, y, width, height });
}
roundRect(
x: number,
y: number,
width: number,
height: number,
radii?: number | DOMPointInit | (number | DOMPointInit)[]
) {
this.pushPath('roundRect', { x, y, width, height, radii });
}
fill() {
this.pushDraw('fill');
}
stroke() {
this.pushDraw('stroke');
}
clip() {
const event = this.push('clip');
this._clip.push(event);
}
fillRect(x: number, y: number, width: number, height: number) {
this.pushDraw('fillRect', { x, y, width, height });
}
strokeRect(x: number, y: number, width: number, height: number) {
this.pushDraw('strokeRect', { x, y, width, height });
}
clearRect(x: number, y: number, width: number, height: number) {
this.pushDraw('clearRect', { x, y, width, height });
}
fillText(text: string, x: number, y: number, maxWidth?: number) {
this.pushDraw('fillText', { text, x, y, maxWidth });
}
strokeText(text: string, x: number, y: number, maxWidth?: number) {
this.pushDraw('strokeText', { text, x, y, maxWidth });
}
drawImage(
image: CanvasImageSource,
sxOrDx: number,
syOrDy: number,
sWidthOrDWidth?: number,
sHeightOrDHeight?: number,
dx?: number,
dy?: number,
dWidth?: number,
dHeight?: number
) {
if (dx === undefined) {
this.pushDraw('drawImage', {
img: image,
dx: sxOrDx,
dy: syOrDy,
dWidth: sWidthOrDWidth,
dHeight: sHeightOrDHeight,
});
return;
}
this.pushDraw('drawImage', {
img: image,
sx: sxOrDx,
sy: syOrDy,
sWidth: sWidthOrDWidth,
sHeight: sHeightOrDHeight,
dx,
dy,
dWidth,
dHeight,
});
}
measureText(text: string): TextMetrics {
const normalized = text == null ? '' : String(text);
this.push('measureText', { text: normalized });
return new MockTextMetrics(normalized);
}
createLinearGradient(
x0: number,
y0: number,
x1: number,
y1: number
): CanvasGradient {
this.push('createLinearGradient', { x0, y0, x1, y1 });
return new MockCanvasGradient();
}
createRadialGradient(
x0: number,
y0: number,
r0: number,
x1: number,
y1: number,
r1: number
): CanvasGradient {
this.push('createRadialGradient', { x0, y0, r0, x1, y1, r1 });
return new MockCanvasGradient();
}
createConicGradient(
startAngle: number,
x: number,
y: number
): CanvasGradient {
this.push('createConicGradient', { startAngle, x, y });
return new MockCanvasGradient();
}
createPattern(
_image: CanvasImageSource,
_repetition: string | null
): CanvasPattern | null {
return new MockCanvasPattern();
}
createImageData(
widthOrImageData: number | ImageData,
height?: number
): ImageData {
const isImageData = typeof widthOrImageData !== 'number';
const w = isImageData ? widthOrImageData.width : widthOrImageData;
const h = isImageData ? widthOrImageData.height : (height ?? 0);
return {
data: new Uint8ClampedArray(w * h * 4),
width: w,
height: h,
colorSpace: 'srgb',
} as ImageData;
}
getImageData(_sx: number, _sy: number, sw: number, sh: number): ImageData {
return {
data: new Uint8ClampedArray(sw * sh * 4),
width: sw,
height: sh,
colorSpace: 'srgb',
} as ImageData;
}
putImageData() {}
setLineDash(_segments: number[]) {}
getLineDash(): number[] {
return [];
}
isPointInPath(): boolean {
return false;
}
isPointInStroke(): boolean {
return false;
}
drawFocusIfNeeded() {}
scrollPathIntoView() {}
getContextAttributes(): CanvasRenderingContext2DSettings {
return {};
}
isContextLost(): boolean {
return false;
}
reset() {
this.__clearEvents();
this.__clearDrawCalls();
this.__clearPath();
this._clip = [];
}
}
const contexts = new WeakMap<HTMLCanvasElement, MockCanvasRenderingContext2D>();
function installCanvasMock() {
if (typeof window === 'undefined') return;
const proto = window.HTMLCanvasElement?.prototype;
if (!proto) return;
// The DOM `getContext` has many context-specific overloads we can't
// express in a single signature. Define it via Object.defineProperty so the
// value slot doesn't have to satisfy every overload simultaneously.
Object.defineProperty(proto, 'getContext', {
configurable: true,
writable: true,
value: function getContext(
this: HTMLCanvasElement,
type: string
): MockCanvasRenderingContext2D | null {
if (type !== '2d') return null;
let ctx = contexts.get(this);
if (!ctx) {
ctx = new MockCanvasRenderingContext2D(this);
contexts.set(this, ctx);
}
return ctx;
},
});
proto.toDataURL = function toDataURL(type?: string): string {
const mime =
type === 'image/jpeg' || type === 'image/webp' ? type : 'image/png';
return `data:${mime};base64,00`;
};
proto.toBlob = function toBlob(callback: BlobCallback, type?: string) {
const mime =
type === 'image/jpeg' || type === 'image/webp' ? type : 'image/png';
const length = (this.width || 1) * (this.height || 1) * 4;
const blob = new window.Blob([new Uint8Array(length)], { type: mime });
setTimeout(() => callback(blob), 0);
};
}
installCanvasMock();
declare global {
interface CanvasRenderingContext2D {
__getEvents(): CanvasRenderingContext2DEvent[];
__clearEvents(): void;
__getDrawCalls(): CanvasRenderingContext2DEvent[];
__clearDrawCalls(): void;
__getPath(): CanvasRenderingContext2DEvent[];
__clearPath(): void;
__getClippingRegion(): CanvasRenderingContext2DEvent[];
}
}
@@ -20,7 +20,6 @@ import { EventEmitter } from 'events';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import 'jest-canvas-mock';
import { act } from 'react';
import { render } from 'design/utils/testing';
@@ -18,7 +18,6 @@
import '@testing-library/jest-dom';
import { screen } from '@testing-library/react';
import 'jest-canvas-mock';
import { MemoryRouter } from 'react-router';
import { act, CurrentPath, render } from 'design/utils/testing';
@@ -17,7 +17,6 @@ import '@testing-library/jest-dom';
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { screen } from '@testing-library/react';
import 'jest-canvas-mock';
import { act, render } from 'design/utils/testing';
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import { MemoryRouter } from 'react-router';
import { render, screen } from 'design/utils/testing';
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import { darkTheme } from 'design/theme';
import {
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import { darkTheme } from 'design/theme';
import { SessionRecordingThumbnail } from 'teleport/services/recordings';
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import { darkTheme } from 'design/theme';
import { SessionRecordingMetadata } from 'teleport/services/recordings';
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import { darkTheme } from 'design/theme';
import { SessionRecordingMetadata } from 'teleport/services/recordings';
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import { darkTheme } from 'design/theme';
import {
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import TtyTerminal from './terminal';
import Tty from './tty';
@@ -18,7 +18,6 @@
import { act, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import 'jest-canvas-mock';
import { mockIntersectionObserver } from 'jsdom-testing-mocks';
import { render } from 'design/utils/testing';
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import { render, screen } from 'design/utils/testing';
import { makeDeepLinkWithSafeInput } from 'shared/deepLinks';
@@ -15,7 +15,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import userEvent from '@testing-library/user-event';
import { render, screen } from 'design/utils/testing';
@@ -20,7 +20,6 @@ import { EventEmitter } from 'node:events';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import 'jest-canvas-mock';
import { render } from 'design/utils/testing';
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import Logger, { NullService } from 'teleterm/logger';
import { createMockConfigService } from 'teleterm/services/config/fixtures/mocks';
import { MockPtyProcess } from 'teleterm/services/pty/fixtures/mocks';
@@ -17,7 +17,6 @@
*/
import { renderHook, waitFor } from '@testing-library/react';
import 'jest-canvas-mock';
import Logger, { NullService } from 'teleterm/logger';
import {
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import { createRef } from 'react';
import { act, fireEvent, render, screen } from 'design/utils/testing';
@@ -15,7 +15,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'jest-canvas-mock';
import { within } from '@testing-library/react';
import { mockIntersectionObserver } from 'jsdom-testing-mocks';
@@ -18,7 +18,6 @@
import { within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import 'jest-canvas-mock';
import { act, ComponentType, createRef } from 'react';
import { render, screen } from 'design/utils/testing';
-54
View File
@@ -1,54 +0,0 @@
diff --git a/lib/classes/CanvasRenderingContext2D.js b/lib/classes/CanvasRenderingContext2D.js
index e1e4b8fe31826baadbdce3f48ee2a68455de8910..46dad56798ddc7fa094f911a7a7e0c97fb2a3770 100644
--- a/lib/classes/CanvasRenderingContext2D.js
+++ b/lib/classes/CanvasRenderingContext2D.js
@@ -15,7 +15,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
-const testFuncs = ['setLineDash', 'getLineDash', 'setTransform', 'getTransform', 'getImageData', 'save', 'restore', 'createPattern', 'createRadialGradient', 'addHitRegion', 'arc', 'arcTo', 'beginPath', 'clip', 'closePath', 'scale', 'stroke', 'clearHitRegions', 'clearRect', 'fillRect', 'strokeRect', 'rect', 'resetTransform', 'translate', 'moveTo', 'lineTo', 'bezierCurveTo', 'createLinearGradient', 'ellipse', 'measureText', 'rotate', 'drawImage', 'drawFocusIfNeeded', 'isPointInPath', 'isPointInStroke', 'putImageData', 'strokeText', 'fillText', 'quadraticCurveTo', 'removeHitRegion', 'fill', 'transform', 'scrollPathIntoView', 'createImageData'];
+const testFuncs = ['setLineDash', 'getLineDash', 'setTransform', 'getTransform', 'getImageData', 'save', 'restore', 'createPattern', 'createRadialGradient', 'addHitRegion', 'arc', 'arcTo', 'beginPath', 'clip', 'closePath', 'scale', 'stroke', 'clearHitRegions', 'clearRect', 'fillRect', 'strokeRect', 'rect', 'roundRect', 'resetTransform', 'translate', 'moveTo', 'lineTo', 'bezierCurveTo', 'createLinearGradient', 'ellipse', 'measureText', 'rotate', 'drawImage', 'drawFocusIfNeeded', 'isPointInPath', 'isPointInStroke', 'putImageData', 'strokeText', 'fillText', 'quadraticCurveTo', 'removeHitRegion', 'fill', 'transform', 'scrollPathIntoView', 'createImageData'];
const compositeOperations = ['source-over', 'source-in', 'source-out', 'source-atop', 'destination-over', 'destination-in', 'destination-out', 'destination-atop', 'lighter', 'copy', 'xor', 'multiply', 'screen', 'overlay', 'darken', 'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light', 'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'];
function getTransformSlice(ctx) {
return ctx._transformStack[ctx._stackIndex].slice();
@@ -909,6 +909,40 @@ class CanvasRenderingContext2D {
const event = (0, _createCanvasEvent.default)('restore', getTransformSlice(this), {});
this._events.push(event);
}
+ roundRect(x, y, width, height, radii) {
+ if (arguments.length < 4)
+ throw new TypeError(
+ "Failed to execute 'roundRect' on '" +
+ this.constructor.name +
+ "': 4 arguments required, but only " +
+ arguments.length +
+ ' present.'
+ );
+ if (radii.constructor === Array && (radii.length === 0 || radii.length > 4))
+ throw new TypeError(
+ "Failed to execute 'roundRect' on '" +
+ this.constructor.name +
+ "': " +
+ radii.length +
+ ' radii provided. Between one and four radii are necessary.'
+ );
+ if (!Number.isFinite(x + y + width + height)) return;
+
+ const xResult = Number(x);
+ const yResult = Number(y);
+ const widthResult = Number(width);
+ const heightResult = Number(height);
+ const event = (0, _createCanvasEvent.default)('roundRect', getTransformSlice(this), {
+ x: xResult,
+ y: yResult,
+ width: widthResult,
+ height: heightResult,
+ radii: radii,
+ });
+
+ this._events.push(event);
+ this._path.push(event);
+ }
rotate(angle) {
if (arguments.length < 1) throw new TypeError("Failed to execute 'rotate' on '" + this.constructor.name + "': 1 argument required, but only 0 present.");
angle = Number(angle);