Skip to content
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

Add option to mountHook to use custom mount method #14672

Closed
grmlin opened this issue Jan 21, 2021 · 14 comments
Closed

Add option to mountHook to use custom mount method #14672

grmlin opened this issue Jan 21, 2021 · 14 comments
Labels
CT Issue related to component testing npm: @cypress/react @cypress/react package issues type: feature New feature that does not currently exist

Comments

@grmlin
Copy link

grmlin commented Jan 21, 2021

What would you like?

It would be great to have the option to use a custom mount method when using mountHook.

Why is this needed?

Currently I have to mount a component to test hooks that depend on a React.Context or a Provider. With a custom version of mount as described in https://github.com/cypress-io/cypress/blob/develop/npm/react/docs/providers-and-composition.md, this would be possible without creating components.

@jennifer-shehane jennifer-shehane added npm: @cypress/react @cypress/react package issues type: feature New feature that does not currently exist labels Jan 21, 2021
@JessicaSachs
Copy link
Contributor

JessicaSachs commented Jan 21, 2021

Can you show a code example for the API you'd like to use for mountHook? We're on the fence about API design around this function.

@grmlin
Copy link
Author

grmlin commented Jan 21, 2021

Hi,
thanks for the quick reply! Maybe it would be a good idea to support some sort of options object, to be ready for future updates? I like how @testing-library/react-hooks does it, and that's what I currently use. Adding a wrapper is really simple property to tell the renderHook method how to render the hook.

const result = mountHook(
  () => useApiGet('/some/url/'),
  {
    wrapper: ({ children }) => (
      <Provider store={store}>{children}</Provider>
    ),
  },
);

But if you prefer to do something similar to mount one could also replace wrapper with a createMount option and use that instead.

const result = mountHook(() => useApiGet('/some/url/'), {
  createMount: (element: React.ReactElement) => {
    mount(<Provider store={store}>{element}</Provider>);
  },
});

I personally like the first one, as it's so simple.

@edimitchel
Copy link
Contributor

edimitchel commented Jun 23, 2021

Is it possible to have some progress about the mountHook function for providing a wrapper as second parameter ?
In case we need a redux context, we are constrained to wrap a component which provide the context we need..
cc @elevatebart

@sethomas
Copy link

sethomas commented Jun 23, 2021

bump, also here for same thing.

I have context, that needs a provider.

Sure I could just use mount and add the provider there, and the hook, but then I need to render items from the hook and check the rendered items. I'd rather just test the return of the hook directly and the above example with @testing-library/react-hooks is exactly what I want with that wrapper option.

EDIT: I am actually using import {renderHook} from '@testing-library/react-hooks/dom'; in my cypress component testing setup, I can use renderHook with the wrapper option, get the return values, then set my assertions.

@JessicaSachs
Copy link
Contributor

JessicaSachs commented Aug 18, 2021

We're currently discussing improvements to the cy.mount API and we're looking closely at Testing Library's wrapper API as well as Storybook's. Our current reccomendation is to use a custom Cypress mount command inside of the support file.

Any changes we decide make to the cy.mount API will take a while to land.

support/commands.jsx

import { mount as cypressMount } from '@cypress/react'

const theme = {/* ... */}
const i18n = {/* ... */}
Cypress.Commands.add('mount', (children) => {
  return cypressMount(<ThemeProvider theme={theme}>
    <I18nProvider i18n={i18n}>
      { children }
    </I18nProvider>
  </ThemeProvider>)
})

component.spec.jsx

it('renders', () => {
  cy.mount(<YourComponent/>) // Will get wrapped in the proper providers
})

I'll keep this issue open and we'll close it when we make a decision on the final API, but that won't be for a while.

@Rorymercer
Copy link

We're currently discussing improvements to the cy.mount API and we're looking closely at Testing Library's wrapper API as well as Storybook's. Our current reccomendation is to use a custom Cypress mount command inside of the support file.

Any changes we decide make to the cy.mount API will take a while to land.

support/commands.jsx

import { mount as cypressMount } from '@cypress/react'

const theme = {/* ... */}
const i18n = {/* ... */}
Cypress.Commands.add('mount', (children) => {
  return cypressMount(<ThemeProvider theme={theme}>
    <I18nProvider i18n={i18n}>
      { children }
    </I18nProvider>
  </ThemeProvider>)
})

component.spec.jsx

it('renders', () => {
  cy.mount(<YourComponent/>) // Will get wrapped in the proper providers
})

I'll keep this issue open and we'll close it when we make a decision on the final API, but that won't be for a while.

What would the equivalent of this be for Vue? Trying to wrap components for testing in a from Vuetify. If I try and use the above Webpack complains that it doesn't know the required loader (Vue Loader already included as per the docs)

@genepaul
Copy link

genepaul commented Apr 1, 2022

The current documented method of using a custom mount also breaks rerender. If I create a custom mount that wraps my component in a ThemeProvider, then try to rerender, it mounts the child component instead of the ThemeProvider. Example code:

const mount = (jsx, options) => cypressReact.mount(
    <ThemeProvider>{jsx}</ThemeProvider>,
    options
);

In a test:

    mount(<MyComponent foo='bar'/>).then(({rerender}) => {
             rerender(<MyComponent foo='baz'/>);
             // error occurs because MyComponent relies on something
             // from the theme passed down by ThemeProvider, and it gets rerendered without the theme context
    });

I believe RTL had the same problem as this, and that's where the wrapper approach came from - so the mount function could know to wrap rerendered components. I haven't dug into the code, though.

@brennarvo
Copy link

We're currently discussing improvements to the cy.mount API and we're looking closely at Testing Library's wrapper API as well as Storybook's. Our current reccomendation is to use a custom Cypress mount command inside of the support file.

Any changes we decide make to the cy.mount API will take a while to land.

support/commands.jsx

import { mount as cypressMount } from '@cypress/react'

const theme = {/* ... */}
const i18n = {/* ... */}
Cypress.Commands.add('mount', (children) => {
  return cypressMount(<ThemeProvider theme={theme}>
    <I18nProvider i18n={i18n}>
      { children }
    </I18nProvider>
  </ThemeProvider>)
})

component.spec.jsx

it('renders', () => {
  cy.mount(<YourComponent/>) // Will get wrapped in the proper providers
})

I'll keep this issue open and we'll close it when we make a decision on the final API, but that won't be for a while.

I like this solution, and it works great for Cypress Component testing, but I'm having issues as it relates to using ES modules. When I regular Cypress E2E testing, I'll get a SyntaxError related to the import and export statements. My first guess is that it is related to Issue 7001.

@brennarvo
Copy link

This has been resolved with #19633 in Cypress v10.0.0

@lmiller1990 lmiller1990 added CT Issue related to component testing and removed component testing labels Aug 15, 2022
@chrisl-peopleplus
Copy link

I'm trying to do something similar where I am trying to fake a hook response from a 3rd party library (auth0-react). I'm not entirely sure how #19633 might provide this, could we get an example of how to intercept a 3rd party hook in the docs?

@boylec
Copy link

boylec commented Oct 14, 2022

This has been resolved with #19633 in Cypress v10.0.0

From what I can tell #19633 does not appear to resolve this.

This issue is about adding functionality to cy.mountHook (from cypress/react) to allow for easily wrapping the mounted hook with providers and such.

#19633 appears to merely allow one to override cy.mount or cy.hover no?

@chrisl-peopleplus
Copy link

From what I can tell #19633 does not appear to resolve this.

I'm glad it's not just me! I was beginning to think I totally missed the point :)

@rockindahizzy
Copy link
Contributor

We removed mountHook in v11. We recommend mounting your hook in a test component instead. Here are docs describing the way forward without mountHook

@LordA98
Copy link

LordA98 commented Jan 9, 2024

We removed mountHook in v11. We recommend mounting your hook in a test component instead. Here are docs describing the way forward without mountHook

The correct link for the relevant docs is now - https://docs.cypress.io/guides/references/migration-guide#React---mountHook-Removed.

There are additional hyphens in the fragment at the end of the URL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CT Issue related to component testing npm: @cypress/react @cypress/react package issues type: feature New feature that does not currently exist
Projects
None yet
Development

No branches or pull requests