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

Auto resolve imports to lwc/componentFoo/__mocks__/componentFoo.js #379

Conversation

nwcm
Copy link

@nwcm nwcm commented Jul 26, 2024

Update LWC resolver to attempt to resolve to **/lwc/componentName/__mocks__/componentName.js first, then fallback to the actual implementation.

This avoids having to set many moduleNameMapper items or defining mocks in a separate directory from the components which complicates upkeep.

If there was a way to honor jest.requireActual() that would be great, but I'm not familiar with how to implement that

Copy link
Contributor

@nolanlawson nolanlawson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for the PR!

This looks good to me at a first glance, although there are lint errors in CI.

Sadly it may not be possible to test this given the current test structure in this project.

Question: does this supersede salesforce/lwc-test#260 or are you looking to get both merged?

@nwcm
Copy link
Author

nwcm commented Jul 29, 2024

@nolanlawson There's is one thing before we progress.

Currently it will resolve the mock first and then fallback to the implementation. Which is great for all jests generally to avoid tests spanning over components. However, as it is currently when you want to test the actual component. c/componentName resolves to the mock, so you would need to change it to ../componentName for the import in the jest file.

This would break everyone's tests if they upgraded!

It doesn't look like we have enough context in the resolve to make a distinction on import from test file vs component file. I'll need to look at the transformer

@nwcm
Copy link
Author

nwcm commented Jul 29, 2024

As for salesforce/lwc-test#260, yes looking to get both merged

@nolanlawson
Copy link
Contributor

However, as it is currently when you want to test the actual component. c/componentName resolves to the mock, so you would need to change it to ../componentName for the import in the jest file.

This isn't even possible for parent-child implicit imports – e.g. <c-foo> in an LWC HTML file will always resolve to c/foo, not ../c/foo. You could maybe fix that with moduleNameMapper, but it would get hairy.

Rather than doing this at the transformer level, I wonder if it would be cleaner to expose a config or something that can be set in the test:

import { setComponentsToMock } from 'somewhere'

beforeEach(() => {
  setComponentsToMock(['c/foo', 'c/bar'])
})

afterEach(() => {
  setComponentsToMock([])
})

@nwcm
Copy link
Author

nwcm commented Jul 30, 2024

I would expect that any component referenced would use the mock if it is there. Such that tests are shallow and only test the referenced component. So <c-foo> or import Foo from "c/foo"; in the actual component would resolve to the mock, if it is mocked.

The import c/ vs ../ is only for the import in the test file for createElement()

It seems clean to me without having to have additional config or per test file setups. If there was a way to follow jests requireActual() then you could override the test to use the true implementation. If you have any ideas for this, happy to implement

@nwcm
Copy link
Author

nwcm commented Jul 31, 2024

Some more exploring with Jest and LWC. This may be unnecessary. It's convenient to automatically use any manual mocks created, however, with jest you can use jest.mock('c/foo'); in the test file to apply the manual mock from __mocks__. However, you would need to call this for each component. Whereas I was seeking a way to automatically use manual mocks.

automock=true seems to have a different result and not use the manual mocks as I think it should be. That may be something with how LWC and Jest interact

https://jestjs.io/docs/manual-mocks

@nwcm
Copy link
Author

nwcm commented Jul 31, 2024

Options I see.

Manual mocks in */lwc/foo/__mocks__/ and manual pointers

  • jest.mock('c/foo') in each test file
  • jest.mock('c/foo) in a setupFile configured to run via jest.config setupFiles
  • "c/foo": "<rootDir>/force-app/main/default/lwc/foo/__mocks__/foo.js" in moduleNameMapper in jest.config
    All of these require implicit defining per component, which from a management perspective isn't great. Could create a script to generate the setup js but feels hacky.

Update resolver to resolve to manual mocks first and define mocks in */lwc/foo/__mocks__/.

  • Any importing a component with a manual mock will use the manual mock
  • Cannot use components actual implementation outside of the .test.js (import Foo from ../foo)
    • So if someone wanted to have a jest test which actually tested a component and it's dependencies as implemented this would be impossible.
    • Is deviation from standard jest behaviors

There are quite a few issues with this change, so will abandon it.

I think a setupLwcMocks.js setup file which is dynamic from a yarn script

from pathlib import Path

JEST_MOCK_SETUP_FILE = "setupLwcMocks.js"

with open(JEST_MOCK_SETUP_FILE, 'w',encoding="utf-8") as file:
    for path in Path('force-app/main').rglob('lwc/*/__mocks__/*.js'):
        print(path.stem)

        file.write(f'jest.mock(\'c/{path.stem}\');\n')
jest.mock('c/foo');
jest.mock('c/bar');
jest.mock('c/cookie');
jest.mock('c/woo');

@nwcm nwcm closed this Jul 31, 2024
@nolanlawson
Copy link
Contributor

Thanks for providing so many details! This is helpful for anyone else trying to solve this problem. 💪

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants