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

[Feature]: Add ability for presets interfere with jest auto mock #15429

Open
MillerSvt opened this issue Dec 27, 2024 · 0 comments
Open

[Feature]: Add ability for presets interfere with jest auto mock #15429

MillerSvt opened this issue Dec 27, 2024 · 0 comments

Comments

@MillerSvt
Copy link

MillerSvt commented Dec 27, 2024

🚀 Feature Proposal

I suggest adding the ability to interfere with the jest auto mock logic in order to be able to modify its behavior at the presets level.

The original problem is that when you try to mock the angular library, then when creating the service, undefined is returned. This is because Angular creates services using ɵfac and ɵprov, which are replaced with jest.fn(), which by default return undefined.

Related to: thymikee/jest-preset-angular#2908

Motivation

Currently, I'm trying to test a component that depends on an external Angular library. This library provides a SomeService that is provided in root.

Here is my component:

class Component {
  someService = inject(SomeService);

  method() {
    this.someService.someMethod();
  }
}

Here is my test:

import { SomeService } from 'some-library';

jest.mock('some-library');

beforeEach(() => {
  TestBed.configureTestingModule({
    providers: [SomeService],
  });

  fixture = TestBed.createComponent(Component);
  component = fixture.componentInstance;
  fixture.detectChanges();
});

it('test', () => {
  component.method();
});

Currently I receive error: Cannot read property 'someMethod' of undefined.

Example

I've done some research. Here is my working patch:

jest.mock(`some-library`, () => {
    const moduleMock = jest.createMockFromModule(`some-library`);

    function* walk(obj: unknown, walkedNodes: any[] = []): Generator<[key: string, target: any]> {
        if ((typeof obj !== `function` && typeof obj !== `object`) || walkedNodes.includes(obj)) {
            return;
        }

        for (const key in obj) {
            if (typeof key === `string` && key.startsWith(`ɵ`)) {
                yield [key, obj];
            }

            yield* walk(obj[key], [...walkedNodes, obj]);
        }
    }

    for (const [key, target] of walk(moduleMock)) {
        switch (key) {
            case `ɵfac`: {
                target[key] = () => new target();
                break;
            }
            case `ɵprov`: {
                if (target[key] === undefined) {
                    break;
                }

                if (`factory` in target[key]) {
                    target[key].factory = () => new target();
                }

                break;
            }
        }
    }

    return moduleMock;
});

However, it would not be beneficial to repeat it in each test and mock.

Pitch

I believe that this can make it easier to work with jest auto mock in projects that often use special tools.

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

No branches or pull requests

1 participant