Testing Library
Purpose
You want to write maintainable tests that give you high confidence that your components are working for your users. There are many open-source testing libraries designed for React and React-like frameworks. The most popular one of them is the testing library that provides friendly and universal APIs to write scalable test cases.
We provides a binding for the testing library called @goji/testing-library, which should work well with Jest and others test runners.
Installation
For npm users,
npm install @goji/testing-library --save-dev
For yarn users,
yarn add @goji/testing-library --dev
Read the Jest documentation to setup a Jest config file. You can copy these
three files, jest.config.js, jestFileMock.js and jestBabelTransform.js from
here to your project.
Example
import React, { useState } from 'react';
import { render, fireEvent } from '@goji/testing-library';
import { Input } from '@goji/core';
const ExampleInput = ({ onSave }: { onSave: (value: string) => void }) => {
  const [value, setValue] = useState('');
  return (
    <Input
      testID="example-input"
      value={value}
      onInput={e => setValue(e.detail.value)}
      onConfirm={() => {
        onSave(value);
        setValue('');
      }}
    />
  );
};
describe('example input', () => {
  it('works', () => {
    const onSave = jest.fn();
    const wrapper = render(<ExampleInput onSave={onSave} />);
    const input = wrapper.getByTestId('example-input');
    expect(input).toBeTruthy();
    // input
    fireEvent.input(input, 'hi');
    expect(input.props.value).toBe('hi');
    // confirm and then cleanup
    fireEvent.confirm(input);
    expect(onSave).toBeCalledTimes(1);
    expect(onSave).toBeCalledWith('hi');
    expect(input.props.value).toBe('');
  });
});
API
render
render will run the components in NodeJS by
React Test Renderer. It mount the components and run
their lifecycles as the same way as real Mini Programs.
It returns a wrapper so you can find and enter the specific children element by different queries.
In this case getByTestId returns the first element which matches the testID property.
render Options
Queries
GojiJS provides several types of queries, all of them are combined from variants and By- queries.
They follow the best practices from the testing library. You can find more information
here.
Variants:
- getBy
- getAllBy
- queryBy
- queryAllBy
- findBy
- findAllBy
By- queries:
- ByTestId(testId: string): matches the- testIDproperty
- ByText(text: string | RegExp | ((text: string, node: ReactTestInstance) => boolean)): matches element's inner text, for more details see TextMatch
- ByProp(propKey: string, propValue: string): matches specific property
We recommend to use testID because it doesn't affect any runtime cost.
Feel free to create an issue if you'd like to append new queries.
debug
This methods print the elements in console for debug purpose.
const wrapper = render(<Comp />);
// print all elements
wrapper.debug();
// print specific elements
wrapper.debug(wrapper.getByTextId('test'));

within
If you'd like to restrict your query in specific container, you can use within.
const Comp = () => (
  <View testID="view">
    <Button>Click me</Button>
  </View>
);
const wrapper = render(<Comp />);
const view = wrapper.getByTestId('view');
const buttonInsideView = within(view).getByText('Click me');
fireEvent
In your test cases, if you'd like to simulates the interactive events on the elements the
fireEvent.[event] could help.
For example, to input text into the <Input testID="my-test-id"> element, you can use,
const input = wrapper.getByTestId('my-test-id');
fireEvent.input(input, 'hello, world!');
fireEvent supports these events,
- tap
- input
- confirm
Feel free to create an issue if you'd like to use new events.
act
The act function has same behavior in
React Test Renderer. Usually you
should wrap the async component updating in act.
let increase: Function;
const Comp = () => {
  const [count, setCount] = useState(0);
  increase = () => setCount(count + 1);
  return <View>Count: {count}</View>;
};
const wrapper = render(<Comp />);
act(() => {
  // update component
  increase();
});
expect(wrapper.getByText('Count: 1')).toBeTruthy;
Please note that all fireEvent methods are already wrapped by act so there is no need to wrap
again.
waitFor and waitForElement
waitFor catches the assertion error util it passed, otherwise throw after timeout. It's used for
async component tests.
Here is an example,
await waitFor(() => expect(wrapper.getByText('hello')).toBeTruthy());
It equals to,
await waitForElement(() => wrapper.getByText('hello'));