From 3e14bed41fb556c7cbce2ee258f11dfdbeb3d3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Or=C5=82=C3=B3w?= <adrian.orlow@fishbrain.com> Date: Fri, 17 Nov 2023 18:00:23 +0100 Subject: [PATCH] test: add tests for the pin rendering module --- package-lock.json | 26 +++++ package.json | 1 + setupTests.ts | 1 + .../utils/config/getCanvasIcon.test.ts | 100 ++++++++++++++++++ .../MapViewer/utils/config/getCanvasIcon.ts | 14 ++- .../utils/config/useOlMapLayers.test.ts | 21 +++- .../MapViewer/utils/config/useOlMapLayers.ts | 2 +- .../utils/config/useOlMapTileLayer.test.ts | 77 ++++++++++++++ .../utils/config/useOlMapView.test.ts | 2 +- src/utils/canvas/getCanvas.test.ts | 15 +++ src/utils/canvas/getFontSizeToFit.test.ts | 25 +++++ src/utils/canvas/getFontSizeToFit.ts | 4 +- yarn.lock | 68 +++++------- 13 files changed, 302 insertions(+), 54 deletions(-) create mode 100644 src/components/Map/MapViewer/utils/config/getCanvasIcon.test.ts create mode 100644 src/components/Map/MapViewer/utils/config/useOlMapTileLayer.test.ts create mode 100644 src/utils/canvas/getCanvas.test.ts create mode 100644 src/utils/canvas/getFontSizeToFit.test.ts diff --git a/package-lock.json b/package-lock.json index 6b0f180a..fb3ac0a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,7 @@ "eslint-plugin-testing-library": "^6.0.1", "husky": "^8.0.0", "jest": "^29.7.0", + "jest-canvas-mock": "^2.5.2", "jest-environment-jsdom": "^29.7.0", "jest-junit": "^16.0.0", "jest-watch-typeahead": "^2.2.2", @@ -4024,6 +4025,12 @@ "node": ">=4" } }, + "node_modules/cssfontparser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", + "integrity": "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==", + "dev": true + }, "node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", @@ -7648,6 +7655,16 @@ } } }, + "node_modules/jest-canvas-mock": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.5.2.tgz", + "integrity": "sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==", + "dev": true, + "dependencies": { + "cssfontparser": "^1.2.1", + "moo-color": "^1.0.2" + } + }, "node_modules/jest-changed-files": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", @@ -9745,6 +9762,15 @@ "node": ">=10" } }, + "node_modules/moo-color": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz", + "integrity": "sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==", + "dev": true, + "dependencies": { + "color-name": "^1.1.4" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/package.json b/package.json index c5b84e0b..8b060676 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "eslint-plugin-testing-library": "^6.0.1", "husky": "^8.0.0", "jest": "^29.7.0", + "jest-canvas-mock": "^2.5.2", "jest-environment-jsdom": "^29.7.0", "jest-junit": "^16.0.0", "jest-watch-typeahead": "^2.2.2", diff --git a/setupTests.ts b/setupTests.ts index 11a7da9a..db87ae92 100644 --- a/setupTests.ts +++ b/setupTests.ts @@ -1,4 +1,5 @@ import '@testing-library/jest-dom'; +import 'jest-canvas-mock'; // used by openlayers module global.ResizeObserver = jest.fn().mockImplementation(() => ({ diff --git a/src/components/Map/MapViewer/utils/config/getCanvasIcon.test.ts b/src/components/Map/MapViewer/utils/config/getCanvasIcon.test.ts new file mode 100644 index 00000000..2915ff79 --- /dev/null +++ b/src/components/Map/MapViewer/utils/config/getCanvasIcon.test.ts @@ -0,0 +1,100 @@ +/* eslint-disable no-magic-numbers */ +import { DEFAULT_FONT_FAMILY } from '@/constants/font'; +import { getCanvas } from '@/utils/canvas/getCanvas'; +import { + drawNumberOnCanvas, + drawPinOnCanvas, + getTextPosition, + getTextWidth, +} from './getCanvasIcon'; + +const getContext = (): CanvasRenderingContext2D => { + const canvas = getCanvas({ width: 100, height: 100 }); + return canvas.getContext('2d') as CanvasRenderingContext2D; +}; + +const ONCE = 1; + +describe('getCanvasIcon - util', () => { + beforeEach(() => { + jest.restoreAllMocks(); + }); + + describe('getTextWidth - subUtil', () => { + const cases: [number, number][] = [ + [1, 6.25], + [7, 8.333], + [43, 12.5], + [105, 16.666], + ]; + + it.each(cases)('on value=%s should return %s', (input, output) => { + expect(getTextWidth(input)).toBeCloseTo(output); + }); + }); + + describe('getTextPosition - subUtil', () => { + const cases: [number, number, number, number][] = [ + [100, 100, -37.5, -27.2], + [532, 443, -253.5, -164.4], + [10, 0, 7.5, 12.8], + [0, 10, 12.5, 8.8], + [0, 0, 12.5, 12.8], + ]; + + it.each(cases)( + 'on textWidth=%s textHeight=%s should return x=%s y=%s', + (textWidth, textHeight, x, y) => { + expect(getTextPosition(textWidth, textHeight)).toMatchObject({ + x, + y, + }); + }, + ); + }); + + describe('drawPinOnCanvas - subUtil', () => { + const color = '#000000'; + + it('should run set fillStyle with color', () => { + const ctx = getContext(); + drawPinOnCanvas({ color }, ctx); + expect(ctx.fillStyle).toBe(color); + }); + + it('should run fill method with valid arguments', () => { + const ctx = getContext(); + const fillSpy = jest.spyOn(ctx, 'fill'); + drawPinOnCanvas({ color }, ctx); + + const call = fillSpy.mock.calls[0][0]; + expect(call).toBeInstanceOf(Path2D); + expect(fillSpy).toBeCalledTimes(ONCE); + }); + }); + + describe('drawNumberOnCanvas - subUtil', () => { + const ctx = getContext(); + const fillTextSpy = jest.spyOn(ctx, 'fillText'); + const value = 69; + + beforeAll(() => { + drawNumberOnCanvas( + { + value, + }, + ctx, + ); + }); + it('should set valid ctx fields', () => { + expect(ctx.fillStyle).toBe('#ffffff'); + expect(ctx.textBaseline).toBe('top'); + expect(ctx.font).toBe(`6.25px ${DEFAULT_FONT_FAMILY}`); + }); + + it('should run fillText once with valid args', () => { + expect(fillTextSpy).toBeCalledWith(`${value}`, 6.25, 12.8); + expect(fillTextSpy).toBeCalledTimes(ONCE); + }); + }); +}); diff --git a/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts b/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts index 0229e86b..7df1b0b0 100644 --- a/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts +++ b/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts @@ -14,13 +14,16 @@ interface Args { value: number; } -const drawPinOnCanvas = ({ color }: Args, ctx: CanvasRenderingContext2D): void => { +export const drawPinOnCanvas = ( + { color }: Pick<Args, 'color'>, + ctx: CanvasRenderingContext2D, +): void => { const path = new Path2D(PIN_PATH2D); ctx.fillStyle = color; ctx.fill(path); }; -const getTextWidth = (value: number): number => { +export const getTextWidth = (value: number): number => { switch (true) { case value === SMALL_TEXT_VALUE: return PIN_SIZE.width / QUARTER; @@ -33,12 +36,15 @@ const getTextWidth = (value: number): number => { } }; -const getTextPosition = (textWidth: number, textHeight: number): Point => ({ +export const getTextPosition = (textWidth: number, textHeight: number): Point => ({ x: (PIN_SIZE.width - textWidth) / HALF, y: (PIN_SIZE.height - textHeight) / TWO_AND_HALF, }); -const drawNumberOnCanvas = ({ value }: Args, ctx: CanvasRenderingContext2D): void => { +export const drawNumberOnCanvas = ( + { value }: Pick<Args, 'value'>, + ctx: CanvasRenderingContext2D, +): void => { const text = `${value}`; const textMetrics = ctx.measureText(text); diff --git a/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts index 1ed0fcc6..00a22c0b 100644 --- a/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts +++ b/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts @@ -5,7 +5,9 @@ import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrappe import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; import { renderHook, waitFor } from '@testing-library/react'; import { Map } from 'ol'; +import BaseLayer from 'ol/layer/Base'; import TileLayer from 'ol/layer/Tile'; +import VectorLayer from 'ol/layer/Vector'; import React from 'react'; import { useOlMap } from '../useOlMap'; import { useOlMapLayers } from './useOlMapLayers'; @@ -46,7 +48,7 @@ describe('useOlMapLayers - util', () => { await waitFor(() => expect(setLayersSpy).toBeCalledTimes(CALLED_ONCE)); }); - it('should return valid View instance', async () => { + const getRenderedHookResults = (): BaseLayer[] => { const { Wrapper } = getReduxWrapperWithStore({ map: { data: { @@ -89,7 +91,20 @@ describe('useOlMapLayers - util', () => { }, ); - expect(result.current[0]).toBeInstanceOf(TileLayer); - expect(result.current[0].getSourceState()).toBe('ready'); + return result.current; + }; + + it('should return valid TileLayer instance', () => { + const result = getRenderedHookResults(); + + expect(result[0]).toBeInstanceOf(TileLayer); + expect(result[0].getSourceState()).toBe('ready'); + }); + + it('should return valid VectorLayer instance', () => { + const result = getRenderedHookResults(); + + expect(result[1]).toBeInstanceOf(VectorLayer); + expect(result[1].getSourceState()).toBe('ready'); }); }); diff --git a/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts b/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts index 350a1aec..c092a146 100644 --- a/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts +++ b/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts @@ -20,5 +20,5 @@ export const useOlMapLayers = ({ mapInstance }: UseOlMapLayersInput): MapConfig[ mapInstance.setLayers([tileLayer, pinsLayer]); }, [tileLayer, pinsLayer, mapInstance]); - return [tileLayer]; + return [tileLayer, pinsLayer]; }; diff --git a/src/components/Map/MapViewer/utils/config/useOlMapTileLayer.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapTileLayer.test.ts new file mode 100644 index 00000000..8c5321cb --- /dev/null +++ b/src/components/Map/MapViewer/utils/config/useOlMapTileLayer.test.ts @@ -0,0 +1,77 @@ +/* eslint-disable no-magic-numbers */ +import { MAP_DATA_INITIAL_STATE, OPENED_MAPS_INITIAL_STATE } from '@/redux/map/map.constants'; +import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; +import { renderHook } from '@testing-library/react'; +import BaseLayer from 'ol/layer/Base'; +import TileLayer from 'ol/layer/Tile'; +import React from 'react'; +import { useOlMapTileLayer } from './useOlMapTileLayer'; + +const useRefValue = { + current: null, +}; + +Object.defineProperty(useRefValue, 'current', { + get: jest.fn(() => ({ + innerHTML: '', + appendChild: jest.fn(), + addEventListener: jest.fn(), + getRootNode: jest.fn(), + })), + set: jest.fn(() => ({ + innerHTML: '', + appendChild: jest.fn(), + addEventListener: jest.fn(), + getRootNode: jest.fn(), + })), +}); + +jest.spyOn(React, 'useRef').mockReturnValue(useRefValue); + +describe('useOlMapTileLayer - util', () => { + const getRenderedHookResults = (): BaseLayer => { + const { Wrapper } = getReduxWrapperWithStore({ + map: { + data: { + ...MAP_DATA_INITIAL_STATE, + size: { + width: 256, + height: 256, + tileSize: 256, + minZoom: 1, + maxZoom: 1, + }, + position: { + initial: { + x: 256, + y: 256, + }, + last: { + x: 256, + y: 256, + }, + }, + }, + loading: 'idle', + error: { + name: '', + message: '', + }, + openedMaps: OPENED_MAPS_INITIAL_STATE, + }, + }); + + const { result } = renderHook(() => useOlMapTileLayer(), { + wrapper: Wrapper, + }); + + return result.current; + }; + + it('should return valid TileLayer instance', () => { + const result = getRenderedHookResults(); + + expect(result).toBeInstanceOf(TileLayer); + expect(result.getSourceState()).toBe('ready'); + }); +}); diff --git a/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts index aa6137cb..c50c543a 100644 --- a/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts +++ b/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts @@ -1,9 +1,9 @@ /* eslint-disable no-magic-numbers */ +import { MAP_DATA_INITIAL_STATE, OPENED_MAPS_INITIAL_STATE } from '@/redux/map/map.constants'; import mapSlice, { setMapPosition } from '@/redux/map/map.slice'; import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer'; import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; import { renderHook, waitFor } from '@testing-library/react'; -import { MAP_DATA_INITIAL_STATE, OPENED_MAPS_INITIAL_STATE } from '@/redux/map/map.constants'; import { View } from 'ol'; import Map from 'ol/Map'; import React from 'react'; diff --git a/src/utils/canvas/getCanvas.test.ts b/src/utils/canvas/getCanvas.test.ts new file mode 100644 index 00000000..fec95f0b --- /dev/null +++ b/src/utils/canvas/getCanvas.test.ts @@ -0,0 +1,15 @@ +/* eslint-disable no-magic-numbers */ +import { getCanvas } from './getCanvas'; + +describe('getCanvas', () => { + it('should return HTMLCanvasElement with valid size on positive params', () => { + const result = getCanvas({ + width: 800, + height: 600, + }); + + expect(result).toBeInstanceOf(HTMLCanvasElement); + expect(result.width).toEqual(800); + expect(result.height).toEqual(600); + }); +}); diff --git a/src/utils/canvas/getFontSizeToFit.test.ts b/src/utils/canvas/getFontSizeToFit.test.ts new file mode 100644 index 00000000..3f5dea7b --- /dev/null +++ b/src/utils/canvas/getFontSizeToFit.test.ts @@ -0,0 +1,25 @@ +/* eslint-disable no-magic-numbers */ +import { getCanvas } from './getCanvas'; +import { getFontSizeToFit } from './getFontSizeToFit'; + +const getContext = (): CanvasRenderingContext2D => { + const canvas = getCanvas({ width: 100, height: 100 }); + return canvas.getContext('2d') as CanvasRenderingContext2D; +}; + +describe('getFontSizeToFit', () => { + const cases: [string, string, number, number][] = [ + ['Hello', 'Helvetica', 50, 10], + ['123', 'Arial', 48, 16], + ['1', '', 48, 48], + ['Text', '', 0, 0], + ['', '', 0, 0], + ]; + it.each(cases)( + 'on text=%s, fontFace=%s, maxWidth=%s it should return value %s', + (text, fontFace, maxWidth, result) => { + const ctx = getContext(); + expect(getFontSizeToFit(ctx, text, fontFace, maxWidth)).toBeCloseTo(result); + }, + ); +}); diff --git a/src/utils/canvas/getFontSizeToFit.ts b/src/utils/canvas/getFontSizeToFit.ts index c74b4b05..dfad99cf 100644 --- a/src/utils/canvas/getFontSizeToFit.ts +++ b/src/utils/canvas/getFontSizeToFit.ts @@ -1,3 +1,5 @@ +const DEFAULT_VALID_SIZE = 0; + export const getFontSizeToFit = ( ctx: CanvasRenderingContext2D, text: string, @@ -5,5 +7,5 @@ export const getFontSizeToFit = ( maxWidth: number, ): number => { ctx.font = `1px ${fontFace}`; - return maxWidth / ctx.measureText(text).width; + return maxWidth / ctx.measureText(text).width || DEFAULT_VALID_SIZE; }; diff --git a/yarn.lock b/yarn.lock index e5bd596f..2c6c5417 100644 --- a/yarn.lock +++ b/yarn.lock @@ -847,49 +847,9 @@ "resolved" "https://registry.npmjs.org/@next/font/-/font-13.5.4.tgz" "version" "13.5.4" -"@next/swc-darwin-x64@13.4.19": - "integrity" "sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw==" - "resolved" "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz" - "version" "13.4.19" - -"@next/swc-darwin-x64@13.4.19": - "integrity" "sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw==" - "resolved" "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz" - "version" "13.4.19" - -"@next/swc-linux-arm64-gnu@13.4.19": - "integrity" "sha512-vdlnIlaAEh6H+G6HrKZB9c2zJKnpPVKnA6LBwjwT2BTjxI7e0Hx30+FoWCgi50e+YO49p6oPOtesP9mXDRiiUg==" - "resolved" "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.19.tgz" - "version" "13.4.19" - -"@next/swc-linux-arm64-musl@13.4.19": - "integrity" "sha512-aU0HkH2XPgxqrbNRBFb3si9Ahu/CpaR5RPmN2s9GiM9qJCiBBlZtRTiEca+DC+xRPyCThTtWYgxjWHgU7ZkyvA==" - "resolved" "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.19.tgz" - "version" "13.4.19" - -"@next/swc-linux-x64-gnu@13.4.19": - "integrity" "sha512-htwOEagMa/CXNykFFeAHHvMJeqZfNQEoQvHfsA4wgg5QqGNqD5soeCer4oGlCol6NGUxknrQO6VEustcv+Md+g==" - "resolved" "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.19.tgz" - "version" "13.4.19" - -"@next/swc-linux-x64-musl@13.4.19": - "integrity" "sha512-4Gj4vvtbK1JH8ApWTT214b3GwUh9EKKQjY41hH/t+u55Knxi/0wesMzwQRhppK6Ddalhu0TEttbiJ+wRcoEj5Q==" - "resolved" "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.19.tgz" - "version" "13.4.19" - -"@next/swc-win32-arm64-msvc@13.4.19": - "integrity" "sha512-bUfDevQK4NsIAHXs3/JNgnvEY+LRyneDN788W2NYiRIIzmILjba7LaQTfihuFawZDhRtkYCv3JDC3B4TwnmRJw==" - "resolved" "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.19.tgz" - "version" "13.4.19" - -"@next/swc-win32-ia32-msvc@13.4.19": - "integrity" "sha512-Y5kikILFAr81LYIFaw6j/NrOtmiM4Sf3GtOc0pn50ez2GCkr+oejYuKGcwAwq3jiTKuzF6OF4iT2INPoxRycEA==" - "resolved" "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.19.tgz" - "version" "13.4.19" - -"@next/swc-win32-x64-msvc@13.4.19": - "integrity" "sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw==" - "resolved" "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.19.tgz" +"@next/swc-darwin-arm64@13.4.19": + "integrity" "sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ==" + "resolved" "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz" "version" "13.4.19" "@nodelib/fs.scandir@2.1.5": @@ -2189,7 +2149,7 @@ dependencies: "color-name" "~1.1.4" -"color-name@~1.1.4": +"color-name@^1.1.4", "color-name@~1.1.4": "integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" "version" "1.1.4" @@ -2360,6 +2320,11 @@ "resolved" "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" "version" "3.0.0" +"cssfontparser@^1.2.1": + "integrity" "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==" + "resolved" "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz" + "version" "1.2.1" + "cssom@^0.5.0": "integrity" "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" "resolved" "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz" @@ -4451,6 +4416,14 @@ "reflect.getprototypeof" "^1.0.4" "set-function-name" "^2.0.1" +"jest-canvas-mock@^2.5.2": + "integrity" "sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==" + "resolved" "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.5.2.tgz" + "version" "2.5.2" + dependencies: + "cssfontparser" "^1.2.1" + "moo-color" "^1.0.2" + "jest-changed-files@^29.7.0": "integrity" "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==" "resolved" "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz" @@ -5377,6 +5350,13 @@ "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" "version" "1.0.4" +"moo-color@^1.0.2": + "integrity" "sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==" + "resolved" "https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz" + "version" "1.0.3" + dependencies: + "color-name" "^1.1.4" + "ms@^2.1.1", "ms@2.1.2": "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" -- GitLab