Skip to content

feat(jest-helpers): add jest-helpers package #432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Oct 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ scaleway-lib is a set of NPM packages used at Scaleway.
![npm bundle size](https://img.shields.io/bundlephobia/min/@scaleway/regex)
![npm](https://img.shields.io/npm/v/@scaleway/regex)

- [`@scaleway/jest-helpers`](./packages/jest-helpers/README.md): utilities jest functions.

![npm](https://img.shields.io/npm/dm/@scaleway/jest-helpers)
![npm bundle size](https://img.shields.io/bundlephobia/min/@scaleway/jest-helpers)
![npm](https://img.shields.io/npm/v/@scaleway/jest-helpers)

## Development

### Locally
Expand Down
3 changes: 3 additions & 0 deletions packages/jest-helpers/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/__tests__/**
src
!.npmignore
91 changes: 91 additions & 0 deletions packages/jest-helpers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# `@scaleway/jest-helpers`

## A package for utilities jest functions

## Install

```bash
$ yarn add @scaleway/jest-functions
```

## How to use

### Create the helpers functions

```tsx
import makeHelpers from '@scaleway/jest-helpers'

const Wrapper = ({ children }) => (
<ThemeProvider>{children}</ThemeProvider>
)

export const {
renderWithTheme,
shouldMatchEmotionSnapshot,
shouldMatchEmotionSnapshotWithPortal,
} = makeHelpers(Wrapper)
```

#### With a theme prop

```tsx
import makeHelpers from '@scaleway/jest-helpers'
import defaultTheme from '..'

interface WrapperProps {
theme?: typeof defaultTheme
}

const Wrapper = ({ theme, children }) => (
<ThemeProvider theme={theme}>{children}</ThemeProvider>
)

export const {
renderWithTheme,
shouldMatchEmotionSnapshot,
shouldMatchEmotionSnapshotWithPortal,
} = makeHelpers(Wrapper)
```

#### With CreateSerializerOptions

```tsx
import makeHelpers from '@scaleway/jest-helpers'

const Wrapper = ({ children }) => (
<ThemeProvider>{children}</ThemeProvider>
)

export const {
renderWithTheme,
shouldMatchEmotionSnapshot,
shouldMatchEmotionSnapshotWithPortal,
} = makeHelpers(Wrapper, { classNameReplacer: className => className })
```

### renderWithTheme

Automatically uses `CacheProvider` from `@emotion/cache`. Use it with a component, optional options & optional theme.

```tsx
const renderWithTheme = (
component: ReactNode, // The component to render
options?: RenderOptions, // RenderOptions from @testing-library/react
theme?: Theme, // Optional theme to use which will be passed to the Wrapper above
) => ReturnType<typeof render>
```

### shouldMatchEmotionSnapshot / shouldMatchEmotionSnapshotWithPortal

Internally it uses the `renderWithTheme` generated from above.

```tsx
const shouldMatchEmotionSnapshot = (
component: ReactNode, // The component to render
options: { // In an object to make it backward-compatible and don't introduce any API breaking changes
options?: RenderOptions // RenderOptions from @testing-library/react
transform?: (node: ReturnType<typeof render>) => Promise<void> | void // (a)sync function execute between the render and the expect. You can use this if you need mockAllIsIntersecting
theme?: Theme // Optional theme to use which will be passed to the Wrapper above
},
) => Promise<void>
```
30 changes: 30 additions & 0 deletions packages/jest-helpers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@scaleway/jest-helpers",
"version": "1.0.0",
"description": "A package for utilities jest functions",
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/scaleway/scaleway-lib",
"directory": "packages/jest-helpers"
},
"license": "MIT",
"dependencies": {
"@emotion/cache": "^11.1.3",
"@emotion/jest": "^11.3.0",
"@emotion/react": "^11.1.4",
"@testing-library/react": "^12.1.2"
},
"devDependencies": {
"@types/react": "^17.0.27"
},
"peerDependencies": {
"react": "^17.0.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`@jest-helpers should call tranform with shouldMatchEmotionSnapshot 1`] = `
<DocumentFragment>
<div
id="wrapper"
>
<div
id="test"
/>
</div>
</DocumentFragment>
`;

exports[`@jest-helpers should call transform with shouldMatchEmotionSnapshot 1`] = `
<DocumentFragment>
<div
id="wrapper"
>
<div
id="test"
/>
</div>
</DocumentFragment>
`;

exports[`@jest-helpers should render with renderWithTheme 1`] = `
<div
data-testid="test"
/>
`;

exports[`@jest-helpers should render with shouldMatchEmotionSnapshot 1`] = `
<DocumentFragment>
<div
id="wrapper"
>
<div
id="test"
/>
</div>
</DocumentFragment>
`;

exports[`@jest-helpers should render with shouldMatchEmotionSnapshotWithPortal 1`] = `
<DocumentFragment>
<div
id="wrapper"
>
<div
id="test"
/>
</div>
</DocumentFragment>
`;
52 changes: 52 additions & 0 deletions packages/jest-helpers/src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react'
import makeHelpers from ".."
import { RenderWithThemeFn } from "../helpers/renderWithTheme"
import { ShouldMatchEmotionSnapshotFn } from '../helpers/shouldMatchEmotionSnapshot'
import { ShouldMatchEmotionSnapshotWithPortalFn } from '../helpers/shouldMatchEmotionSnapshotWithPortal'

describe('@jest-helpers', () => {
let renderWithTheme: RenderWithThemeFn<unknown>
let shouldMatchEmotionSnapshot: ShouldMatchEmotionSnapshotFn<unknown>
let shouldMatchEmotionSnapshotWithPortal: ShouldMatchEmotionSnapshotWithPortalFn<unknown>

beforeAll(() => {
const helpers = makeHelpers(({ children }) => (
<div id="wrapper">
{children}
</div>
))

renderWithTheme = helpers.renderWithTheme
shouldMatchEmotionSnapshot = helpers.shouldMatchEmotionSnapshot
shouldMatchEmotionSnapshotWithPortal = helpers.shouldMatchEmotionSnapshotWithPortal
})

test('should render with renderWithTheme', () => {
const node = renderWithTheme(<div data-testid="test" />)
const element = node.getByTestId("test")

expect(element).toMatchSnapshot()
})

test('should render with shouldMatchEmotionSnapshot', async () => {
await shouldMatchEmotionSnapshot(<div id="test" />)
})

test('should call tranform with shouldMatchEmotionSnapshot', async () => {
const transform = jest.fn()
await shouldMatchEmotionSnapshot(<div id="test" />, { transform })

expect(transform).toHaveBeenCalledTimes(1)
})

test('should render with shouldMatchEmotionSnapshotWithPortal', async () => {
await shouldMatchEmotionSnapshotWithPortal(<div id="test" />)
})

it('should call transform with shouldMatchEmotionSnapshot', async () => {
const transform = jest.fn()
await shouldMatchEmotionSnapshotWithPortal(<div id="test" />, { transform })

expect(transform).toHaveBeenCalledTimes(1)
})
})
23 changes: 23 additions & 0 deletions packages/jest-helpers/src/helpers/renderWithTheme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import createCache from '@emotion/cache'
import { CacheProvider } from '@emotion/react'
import { RenderOptions, render } from '@testing-library/react'
import React, { FC, ReactNode } from 'react'

const emotionCache = createCache({
key: 'cache',
})

emotionCache.compat = true

export type RenderWithThemeFn<Theme> = (component: ReactNode, options?: RenderOptions, theme?: Theme) => ReturnType<typeof render>

export default function makeRenderWithTheme<Theme>(Wrapper: FC<{ theme?: Theme }>): RenderWithThemeFn<Theme> {
return (component, options, theme) => render(
<CacheProvider value={emotionCache}>
<Wrapper theme={theme}>
{component}
</Wrapper>
</CacheProvider>,
options,
)
}
21 changes: 21 additions & 0 deletions packages/jest-helpers/src/helpers/shouldMatchEmotionSnapshot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { RenderOptions, render } from '@testing-library/react'
import { ReactNode } from 'react'
import { RenderWithThemeFn } from './renderWithTheme'

interface Options<Theme> {
options?: RenderOptions
transform?: (node: ReturnType<typeof render>) => Promise<void> | void
theme?: Theme
}

export type ShouldMatchEmotionSnapshotFn<Theme> = (component: ReactNode, options?: Options<Theme>) => Promise<void>

export default function makeShouldMatchEmotionSnapshot<Theme>(renderWithTheme: RenderWithThemeFn<Theme>): ShouldMatchEmotionSnapshotFn<Theme> {
return async (component, { options, transform, theme } = {}) => {
const node = renderWithTheme(component, options, theme)
if (transform) await transform(node)

expect(node.asFragment()).toMatchSnapshot()
node.unmount()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { RenderOptions, render } from '@testing-library/react'
import { ReactNode } from 'react'
import { RenderWithThemeFn } from './renderWithTheme'

interface Options<Theme> {
options?: RenderOptions
transform?: (node: ReturnType<typeof render>) => Promise<void> | void
theme?: Theme
}

export type ShouldMatchEmotionSnapshotWithPortalFn<Theme> = (component: ReactNode, options?: Options<Theme>) => Promise<void>

export default function makeShouldMatchEmotionSnapshotWithPortal<Theme>(renderWithTheme: RenderWithThemeFn<Theme>): ShouldMatchEmotionSnapshotWithPortalFn<Theme> {
return async (component, { options, transform, theme } = {}) => {
// Save the instance of console (disable warning about adding element directly to document.body which is necessary when testing portal components)
const { console } = global
global.console = { ...console, error: jest.fn() }

const node = renderWithTheme(
component,
{
container: document.body,
...options,
},
theme,
)
if (transform) await transform(node)
expect(node.asFragment()).toMatchSnapshot()

// Unmounting to don't see the warning message described above
node.unmount()
global.console = console
}
}
29 changes: 29 additions & 0 deletions packages/jest-helpers/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { CreateSerializerOptions, createSerializer } from '@emotion/jest'
import { FC } from 'react'
import makeRenderWithTheme, { RenderWithThemeFn } from './helpers/renderWithTheme'
import makeShouldMatchEmotionSnapshot, { ShouldMatchEmotionSnapshotFn } from './helpers/shouldMatchEmotionSnapshot'
import makeShouldMatchEmotionSnapshotWithPortal, { ShouldMatchEmotionSnapshotWithPortalFn } from './helpers/shouldMatchEmotionSnapshotWithPortal'

export { default as makeRenderWithTheme } from './helpers/renderWithTheme'

type Helpers<Theme> = {
renderWithTheme: RenderWithThemeFn<Theme>
shouldMatchEmotionSnapshot: ShouldMatchEmotionSnapshotFn<Theme>
shouldMatchEmotionSnapshotWithPortal: ShouldMatchEmotionSnapshotWithPortalFn<Theme>
}

export default function makeHelpers<Theme>(Wrapper: FC<{ theme?: Theme }>, createSerializerOptions?: CreateSerializerOptions): Helpers<Theme> {
expect.addSnapshotSerializer(
createSerializer(createSerializerOptions),
)

const renderWithTheme = makeRenderWithTheme(Wrapper)
const shouldMatchEmotionSnapshot = makeShouldMatchEmotionSnapshot(renderWithTheme)
const shouldMatchEmotionSnapshotWithPortal = makeShouldMatchEmotionSnapshotWithPortal(renderWithTheme)

return {
renderWithTheme,
shouldMatchEmotionSnapshot,
shouldMatchEmotionSnapshotWithPortal,
}
}
Loading