Skip to content

Commit de1895d

Browse files
committed
feat: add image snap test
1 parent 9aba8f2 commit de1895d

16 files changed

+335
-39
lines changed

.github/actions/setup/action.yml

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@ runs:
55
using: composite
66
steps:
77
- name: Setup Node.js
8-
uses: actions/setup-node@v3
8+
uses: actions/setup-node@v4
99
with:
10-
node-version: 16.x
10+
node-version: lts/*
1111

1212
- name: Cache dependencies
1313
id: yarn-cache
1414
uses: actions/cache@v3
1515
with:
1616
path: |
1717
**/node_modules
18-
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
18+
key: ${{ runner.os }}-yarn-v1-${{ hashFiles('**/yarn.lock') }}
1919
restore-keys: |
20-
${{ runner.os }}-yarn-
20+
${{ runner.os }}-yarn-v1-
2121
2222
- name: Install dependencies
2323
if: steps.yarn-cache.outputs.cache-hit != 'true'
2424
run: |
2525
yarn install --cwd example --frozen-lockfile
26-
yarn install --frozen-lockfile
26+
yarn install --frozen-lockfile --ignore-engines
2727
shell: bash

.github/workflows/ci.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44

55
jobs:
66
lint:
7-
runs-on: ubuntu-latest
7+
runs-on: macos-latest
88
steps:
99
- name: Checkout
1010
uses: actions/checkout@v3
@@ -19,7 +19,7 @@ jobs:
1919
run: yarn typescript
2020

2121
test:
22-
runs-on: ubuntu-latest
22+
runs-on: macos-latest
2323
steps:
2424
- name: Checkout
2525
uses: actions/checkout@v3
@@ -31,7 +31,7 @@ jobs:
3131
run: yarn test --maxWorkers=2 --coverage
3232

3333
build:
34-
runs-on: ubuntu-latest
34+
runs-on: macos-latest
3535
steps:
3636
- name: Checkout
3737
uses: actions/checkout@v3

.github/workflows/publish.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ env:
1313
jobs:
1414
publish:
1515
name: publish
16-
runs-on: ubuntu-latest
16+
runs-on: macos-latest
1717
strategy:
1818
fail-fast: false
1919
matrix:
2020
node-version: [16.x]
2121

2222
steps:
2323
- uses: actions/checkout@v2
24+
- uses: actions/setup-node@v4
25+
with:
26+
node-version: latest
2427
- name : GITHUB CONTEXT
2528
env:
2629
GITHUB_CONTEXT: ${{ toJson(github) }}

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,6 @@ lib/
7777

7878
# Docusaurus (when switching from docs branches to code branches)
7979
.docusaurus/
80+
81+
# jest
82+
coverage/

example/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"dependencies": {
1212
"@expo/webpack-config": "~19.0.1",
1313
"@shopify/react-native-skia": "0.1.221",
14-
"echarts": "^5.5.0",
14+
"echarts": "5.5.0-rc.1",
1515
"expo": "^50.0.0",
1616
"react": "18.2.0",
1717
"react-dom": "18.2.0",

example/yarn.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -4042,7 +4042,7 @@ dotenv@~16.0.3:
40424042
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07"
40434043
integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==
40444044

4045-
echarts@^5.5.0:
4045+
echarts@5.5.0-rc.1:
40464046
version "5.5.0-rc.1"
40474047
resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.5.0-rc.1.tgz#31ee2c4e09da24fe8149bb011c1fbd271a2bf10f"
40484048
integrity sha512-Fnfls+zDAg9HLuWK27JakdjLjpkEZsKTk+eeSA6eOqphBNA9iXCI1CvQJZ5xD8WJY40bqdh4zCaE4AxSDjJslw==

jest.config.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
module.exports = {
2-
preset: "react-native",
2+
preset: 'react-native',
33
modulePathIgnorePatterns: [
4-
"<rootDir>/example/node_modules",
5-
"<rootDir>/lib/"
4+
'<rootDir>/example/node_modules',
5+
'<rootDir>/lib/',
66
],
77
transformIgnorePatterns: [
88
'node_modules/(?!(react-native|react-native.*|@react-native.*|@?react-navigation.*|@shopify/react-native-skia|zrender|echarts)/)',
99
],
1010
setupFiles: [
1111
'@shopify/react-native-skia/jestSetup.js',
1212
'react-native-gesture-handler/jestSetup.js',
13-
'./jestSetup.js'
13+
'./jestSetup.js',
1414
],
1515
testTimeout: 30000,
16-
};
16+
};

jestSetup.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
const { Skia } = require("@shopify/react-native-skia");
1+
const { Skia } = require('@shopify/react-native-skia');
22
Skia.SVG.MakeFromString = (svg) => svg;
3+
// eslint-disable-next-line no-undef
34
globalThis.navigator = {
4-
product: "ReactNative",
5-
userAgent: "Node.js",
5+
product: 'ReactNative',
6+
userAgent: 'Node.js',
67
};

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,12 @@
6464
"@types/react": "~18.2.14",
6565
"commitlint": "^17.0.2",
6666
"del-cli": "^5.0.0",
67-
"echarts": "^5.5.0",
67+
"echarts": "5.5.0-rc.1",
6868
"eslint": "^8.4.1",
6969
"eslint-config-prettier": "^8.5.0",
7070
"eslint-plugin-prettier": "^4.0.0",
7171
"jest": "^29.7.0",
72+
"jest-image-snapshot": "^6.4.0",
7273
"pod-install": "^0.1.0",
7374
"prettier": "^2.0.5",
7475
"react": "18.2.0",
@@ -78,6 +79,7 @@
7879
"react-native-svg": "14.1.0",
7980
"react-test-renderer": "^18.2.0",
8081
"release-it": "^15.0.0",
82+
"sharp": "^0.33.2",
8183
"typescript": "^5.0.4",
8284
"zrender": "^5.5.0"
8385
},

src/SVGRenderer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class CustomSVGPainter extends SVGPainter {
7070
super.refresh();
7171
}
7272
}
73-
toDataURL(base64?: boolean):string {
73+
toDataURL(base64?: boolean): string {
7474
// @ts-ignore
7575
if (isRn && this._svgDom.makeImageSnapshot) {
7676
// @ts-ignore

src/__tests__/chart.test.tsx

+40-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
/**
22
* @jest-environment @shopify/react-native-skia/jestEnv.mjs
33
*/
4+
declare global {
5+
namespace jest {
6+
interface Matchers<R> {
7+
toMatchImageSnapshot(): R;
8+
}
9+
}
10+
}
411
import React, { useEffect, useRef } from 'react';
512
import { Dimensions } from 'react-native';
613
import { render } from '@testing-library/react-native';
@@ -9,6 +16,19 @@ import SVGComponent from '../svgChart';
916
import { SVGRenderer } from '../SVGRenderer';
1017
import * as echarts from 'echarts/core';
1118
import { BarChart } from 'echarts/charts';
19+
const sharp = require('sharp');
20+
21+
const { toMatchImageSnapshot } = require('jest-image-snapshot');
22+
23+
function getSVGImage(svg: string) {
24+
const svgString = decodeURIComponent(
25+
svg.replace('data:image/svg+xml;charset=UTF-8,', '')
26+
);
27+
return sharp(Buffer.from(svgString)).png().toBuffer();
28+
}
29+
30+
expect.extend({ toMatchImageSnapshot });
31+
1232
import {
1333
TitleComponent,
1434
TooltipComponent,
@@ -21,10 +41,11 @@ echarts.use([
2141
SVGRenderer,
2242
BarChart,
2343
]);
44+
2445
const Components = [SkiaComponent, SVGComponent];
2546
const E_HEIGHT = 250;
26-
const E_WIDTH = Dimensions.get('screen').width;//750
27-
const option = {
47+
const E_WIDTH = Dimensions.get('screen').width; //750
48+
const OPTION = {
2849
xAxis: {
2950
type: 'category',
3051
data: ['Mon', 'Tue'],
@@ -39,7 +60,8 @@ const option = {
3960
},
4061
],
4162
};
42-
function Chart({ option, Component }: any) {
63+
64+
function Chart({ option, Component, getDataURL }: any) {
4365
const ref = useRef<any>(null);
4466
useEffect(() => {
4567
let chart: any;
@@ -51,17 +73,29 @@ function Chart({ option, Component }: any) {
5173
height: E_HEIGHT,
5274
});
5375
chart.setOption(option);
76+
getDataURL?.(chart.getDataURL());
5477
}
5578
return () => chart?.dispose();
56-
}, [option]);
79+
}, [option, getDataURL]);
5780

5881
return <Component ref={ref} />;
5982
}
83+
6084
Components.forEach((Component) => {
6185
describe(`${Component.displayName} Chart` || 'unknown', () => {
62-
it('renders correctly', () => {
86+
it('renders correctly', (done) => {
6387
const { toJSON } = render(
64-
<Chart option={option} Component={Component} />
88+
<Chart
89+
option={OPTION}
90+
Component={Component}
91+
getDataURL={(data: string) => {
92+
expect(data).toBeDefined();
93+
getSVGImage(data).then((d: Buffer) => {
94+
expect(d).toMatchImageSnapshot();
95+
done();
96+
});
97+
}}
98+
/>
6599
);
66100
expect(toJSON()).toMatchSnapshot();
67101
});

src/components/PanResponderHandler.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ export function PanResponderHandler({
3333
dispatchEvents,
3434
}: PanResponderHandlerProps) {
3535
const [panResponder] = usePanResponder(dispatchEvents);
36-
return <View testID="pan-responder-handler" {...panResponder.panHandlers} style={styles.GestureView} />;
36+
return (
37+
<View
38+
testID="pan-responder-handler"
39+
{...panResponder.panHandlers}
40+
style={styles.GestureView}
41+
/>
42+
);
3743
}
3844

3945
export function usePanResponder(

src/skiaChart.tsx

+19-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import React, {
88
useRef,
99
} from 'react';
1010

11-
import { Canvas, ImageSVG, Skia, SkSVG, useCanvasRef } from '@shopify/react-native-skia';
11+
import {
12+
Canvas,
13+
ImageSVG,
14+
Skia,
15+
SkSVG,
16+
useCanvasRef,
17+
} from '@shopify/react-native-skia';
1218

1319
import { View } from 'react-native';
1420

@@ -55,7 +61,7 @@ function SkiaComponent(
5561
const [width, setWidth] = useState<number>(initialWidth ?? 0);
5662
const [height, setHeight] = useState<number>(initialHeight ?? 0);
5763
const zrenderId = useRef<number>();
58-
const canvasRef = useCanvasRef();
64+
const canvasRef = useCanvasRef?.();
5965

6066
const dispatchEvents = useCallback<DispatchEvents>(
6167
(types, nativeEvent, eventArgs) => {
@@ -88,9 +94,11 @@ function SkiaComponent(
8894
zrenderId.current = id;
8995
},
9096
makeImageSnapshot: () => {
91-
const image = canvasRef.current?.makeImageSnapshot();
92-
return image ? `data:image/png;base64,${image.encodeToBase64()}` : null;
93-
}
97+
const image = canvasRef?.current?.makeImageSnapshot();
98+
return image
99+
? `data:image/png;base64,${image.encodeToBase64()}`
100+
: null;
101+
},
94102
},
95103
viewprot: {},
96104
dispatchEvents,
@@ -101,12 +109,16 @@ function SkiaComponent(
101109
};
102110
},
103111
}),
104-
[dispatchEvents, initialWidth, initialHeight]
112+
[dispatchEvents, initialWidth, initialHeight, canvasRef]
105113
);
106114

107115
return svgString ? (
108116
<View testID="component" style={{ ...style, width, height }}>
109-
<Canvas style={{ ...style, width, height }} pointerEvents="auto" ref={canvasRef}>
117+
<Canvas
118+
style={{ ...style, width, height }}
119+
pointerEvents="auto"
120+
ref={canvasRef}
121+
>
110122
<ImageSVG svg={svgString} x={0} y={0} width={width} height={height} />
111123
</Canvas>
112124
{handleGesture ? (

0 commit comments

Comments
 (0)