react-native-skia icon indicating copy to clipboard operation
react-native-skia copied to clipboard

[Mock] missing some hooks from official mock

Open flo-sch opened this issue 3 years ago • 6 comments

Description

When following the official guide for setting up the test environment, the mock is injected, but it does not cover the whole API of the library, more especially the parts defined under src/renderer and src/views.

I have a component using among other things useCanvasRef, that I use to invoke makeImageSnapshot() as illustrated here.

The tests break when using the official mock, as useCanvasRef is not defined.

TypeError: (0 , _reactNativeSkia.useCanvasRef) is not a function

This is the first hook I am facing an error with, but I noticed some other methods also failing:

  • useTouchHandler

TypeError: (0 , _reactNativeSkia.useTouchHandler) is not a function

  • useValue

TypeError: Cannot read properties of undefined (reading 'createValue')

Version

v0.1.137

Steps to reproduce

  1. Build a component using useCanvasRef()
  2. Build a test case for it
  3. Setup the environment as instructed
  4. Run the tests (I am using jest)

Result: test showing the following error

TypeError: (0 , _reactNativeSkia.useCanvasRef) is not a function

Snack, code example, screenshot, or link to a repository

An example component:

// TestComponent.tsx
import { Button, View } from 'react-native';
import { Canvas, SkImage, useCanvasRef } from '@shopify/react-native-skia';

const TestComponent: React.FC<{
  onSnapshot?(snapshot: SkImage): void
}> = ({ onSnapshot }) => {
  const canvas = useCanvasRef();
  const onPress = () => {
    const snapshot = canvas.current?.makeImageSnapshot();
    onSnapshot?.(snapshot);
  }
  
  return (
    <View>
      <Canvas ref={canvas} style={{ width: 40, height: 40 }}>
        <Rect x={10} y={10} width={20} height={20} color="red" />
      </Canvas>
      <Button title="snapshot" onPress={onPress} />
    </View>
  );
}

An example test case:

// TestComponent.test.tsx
import {fireEvent, render} from '@testing-library/react-native'
import TestComponent from '[...wherever...]';

describe('TestComponent', () => {
  it('should take a snapshot', async () => {
    const onSnapshot = jest.fn();
    const {getByText} = render(<TestComponent onSnapshot={onSnapshot} />);
    await fireEvent.press(getByText('snapshot'));
    expect(onSnapshot).toHaveBeenCalled();
  });
})

flo-sch avatar Aug 10 '22 11:08 flo-sch

I am willing to contribute on the mock, but I am struggling a bit to understand:

  1. wether the src/renderer/* subfolder is intentionally not exported from the mock? (... and should rather be mocked separately? 🤔)
  2. same question about src/views (e.g. useTouchHandler())
  3. why is useValue() throwing, while the Skia method createValue() is supposed to be mocked?

flo-sch avatar Aug 10 '22 12:08 flo-sch

One thing that would help with the mock would be to load CanvasKit. Then you can test everything as you would have a full implementation of the module (this is what we have for our tests: https://github.com/Shopify/react-native-skia/blob/main/package/src/renderer/tests/documentation/getting-started/HelloWorld.spec.tsx#L51).

Do you know if it's possible to load a Mock asynchronously? (allowing us to use CanvasKit in the mock)

wcandillon avatar Aug 10 '22 12:08 wcandillon

@flo-sch We also run tests for the example app at: https://github.com/Shopify/react-native-skia/blob/main/example/src/App.spec.tsx It would be fantastic if you could add failing tests there so we know that we got you covered.

wcandillon avatar Aug 10 '22 13:08 wcandillon

Awesome, I missed those, will add tests there :)

Do you know if it's possible to load a Mock asynchronously? (allowing us to use CanvasKit in the mock)

I have no idea, never faced that scenario so far, but I guess I could try

flo-sch avatar Aug 10 '22 13:08 flo-sch

I added some (intentionally failing) tests, took the already existing ExportableCanvas as component, but I could create a custom one as documented here if you prefer?

flo-sch avatar Aug 10 '22 15:08 flo-sch

Do you know if it's possible to load a Mock asynchronously? (allowing us to use CanvasKit in the mock)

So, I tried writing a custom test environment extending the default "node" one, I think it might be a suitable option? It provides a setup and a teardown function (which I am not sure we need here), both can be async, and I managed I think to load the Skia web API within setup().

However, one caveat is that jest itself cannot transpile such an environment, so I updated the example test script to use ts-node/register instead... This is a bit cumbersome but works.

Let me know what you think about that approach? If that works, I was thinking we could potentially export the jest environment itself to simplify the setup in external projects?

PS: it does not fix the missing hooks of course.

flo-sch avatar Aug 10 '22 17:08 flo-sch

Any update on this? I'm getting the same error in my jest tests.

kylegillen avatar Nov 08 '22 16:11 kylegillen