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

Fix DeepMockProxy type error for objects which are functions and have fields at the same time #95

Merged
merged 3 commits into from
Jul 16, 2022

Conversation

lallenfrancisl
Copy link

@lallenfrancisl lallenfrancisl commented Jul 8, 2022

For some types like AxiosInstance the object (or rather better called as a function that has fields) doubles as function. This means that in the current type implementation for DeepMockProxy the if condition gets triggered for the function part. This causes the type to incorrectly say that only the first nested field is possible. To show this in action I am putting a simplified version similar to how AxiosInstance behaves when used inside a class. You can find the original implementation of AxiosInstance here

import { CalledWithMock, DeepMockProxy } from './Mock';

interface DemoInter {
    (config: Record<string, any>): Promise<any>;
    (url: string, config?: Record<string, any>): Promise<any>;

    request(inp: string): Promise<any>;

    dataField: {
        hello: 'world';
    };
}

class DemoClass {
    client: DemoInter;

    constructor(a: DemoInter) {
        this.client = a;
    }
}

type OldDeepMockProxy<T> = {
    // This supports deep mocks in the else branch
    [K in keyof T]: T[K] extends (...args: infer A) => infer B ? CalledWithMock<B, A> : OldDeepMockProxy<T[K]>;
} & T;

let oldMock: OldDeepMockProxy<DemoClass>;

// This would fail compilation with existing DeepMockProxy
oldMock.client.request.mockImplementationOnce();

let newMock: DeepMockProxy<DemoClass>;

// This is properly typed
newMock.client.request.mockImplementationOnce();

// Properly works for other data fields
newMock.client.dataField.hello;

This PR fixes #94

@lallenfrancisl lallenfrancisl changed the title Fix DeepMockProxy type error for objects which are functions and have fields at the same time Fix DeepMockProxy type error for objects which are functions and have fields at the same time, fixes #94 Jul 8, 2022
@lallenfrancisl lallenfrancisl changed the title Fix DeepMockProxy type error for objects which are functions and have fields at the same time, fixes #94 Fix DeepMockProxy type error for objects which are functions and have fields at the same time Jul 8, 2022
@lallenfrancisl
Copy link
Author

@marchaos I know its kind of annoying to tag, but doing just in case this does not get lost in your notifications

@marchaos
Copy link
Owner

Thanks. This did actually get lost in my notifications! Any chance you can add a test? That will help me understand the issue a bit more too.

@lallenfrancisl
Copy link
Author

Yeah working on some tests, at first I thought since this is type change tests wouldn't be needed. But this is something that needs to be tested to know if it works too

expect(mockObj.funcValueProp.deepProp.getNumber).toHaveBeenCalledTimes(1);
});

test('can mock base function which have properties', () => {
Copy link
Author

@lallenfrancisl lallenfrancisl Jul 16, 2022

Choose a reason for hiding this comment

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

This test and the test after this are the ones that test the new behaviour

[K in keyof T]: T[K] extends (...args: infer A) => infer B ? CalledWithMock<B, A> : DeepMockProxy<T[K]>;
} &
T;
[K in keyof T]: T[K] extends (...args: infer A) => infer B ? CalledWithMock<B, A> & DeepMockProxy<T[K]> : DeepMockProxy<T[K]>;
Copy link
Author

Choose a reason for hiding this comment

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

We have to call DeepMockProxy in the case of functions too because some libraries (like Axios) use the functions to have other nested functions since its valid in javascript to to that

@lallenfrancisl
Copy link
Author

@marchaos I have added some unit tests. Some of the tests are actually duplicated from the mockDeep's existing tests, but changed to test everything works here too. I have added comments in the file to highlight this

@marchaos
Copy link
Owner

Sweet thanks!, taking a look...

@marchaos marchaos merged commit d0fc0d1 into marchaos:master Jul 16, 2022
@marchaos
Copy link
Owner

I pushed a new version 2.0.7 with this fix. Let me know if you've got any issues with that.

Thanks for the PR

@marchaos
Copy link
Owner

Worth noting that this has introduced a performance issue, which is now resolved in the 3.0.x branch. This required an implementation change so that functions with prop support is now opt in. see issue #97 for more details. cc @lallenfrancisl

I haven't pushed a 3.0.0 final version yet, this is just a heads up.

@lallenfrancisl
Copy link
Author

Dang sorry, didn't expect that. I can also look into some alternative while avoiding performance issue. Will try to sit on this @marchaos

@marchaos
Copy link
Owner

Hey @lallenfrancisl , I've already got an alternative for this:

mockDeep({ funcPropSupport : true })

This means that you only need to pass that in if you want to support functions with props / fields. For 99% of cases / people, this won't be required for deep mocking, but didn't want to remove that functionality. This is also why there will be a major version upgrade (3.0.0). If you want to test it out before 3.0.0 is released, you can use 3.0.0-beta1.

@lallenfrancisl
Copy link
Author

I saw the fix, but this still causes the issue when using the functionality, right ? I am not sure if I will get the time to test out this week. When are you planning to release 3.0 ?

@marchaos
Copy link
Owner

marchaos commented Sep 13, 2022 via email

Vylpes pushed a commit to Vylpes/Droplet that referenced this pull request Sep 14, 2023
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [jest-mock-extended](https://github.com/marchaos/jest-mock-extended) | devDependencies | major | [`^2.0.4` -> `^3.0.0`](https://renovatebot.com/diffs/npm/jest-mock-extended/2.0.4/3.0.4) |

---

### Release Notes

<details>
<summary>marchaos/jest-mock-extended</summary>

### [`v3.0.4`](https://github.com/marchaos/jest-mock-extended/releases/tag/3.0.4)

[Compare Source](marchaos/jest-mock-extended@3.0.3...3.0.4)

-   Updated jest dependencies
-   Updated typescript peerDependencies to allow for typescript 5.\*

### [`v3.0.3`](marchaos/jest-mock-extended@3.0.2...3.0.3)

[Compare Source](marchaos/jest-mock-extended@3.0.2...3.0.3)

### [`v3.0.2`](https://github.com/marchaos/jest-mock-extended/releases/tag/3.0.2)

[Compare Source](marchaos/jest-mock-extended@3.0.1...3.0.2)

Added marchaos/jest-mock-extended#110

### [`v3.0.1`](https://github.com/marchaos/jest-mock-extended/releases/tag/3.0.1)

[Compare Source](marchaos/jest-mock-extended@3.0.0...3.0.1)

Allow overriding calledWithFn - see marchaos/jest-mock-extended#96

### [`v3.0.0`](https://github.com/marchaos/jest-mock-extended/releases/tag/3.0.0)

[Compare Source](marchaos/jest-mock-extended@3602a65...3.0.0)

-   Fixed performance issue for marchaos/jest-mock-extended#97. This required a small API change, hence the major version bump. Deep mocking functions with properties is still possible using

```typescript
mockDeep({ funcPropSupport: true });
```

however this comes with a recursive performance cost. We hope to address this in the future.

### [`v2.0.9`](marchaos/jest-mock-extended@c6fdf33...3602a65)

[Compare Source](marchaos/jest-mock-extended@c6fdf33...3602a65)

### [`v2.0.8`](marchaos/jest-mock-extended@2.0.7...c6fdf33)

[Compare Source](marchaos/jest-mock-extended@2.0.7...c6fdf33)

### [`v2.0.7`](https://github.com/marchaos/jest-mock-extended/releases/tag/2.0.7)

[Compare Source](marchaos/jest-mock-extended@2.0.6...2.0.7)

### Fixes

-   Fix DeepMockProxy type error for objects which are functions and have fields at the same time - marchaos/jest-mock-extended#95

### [`v2.0.6`](https://github.com/marchaos/jest-mock-extended/releases/tag/2.0.6)

[Compare Source](marchaos/jest-mock-extended@2.0.5...2.0.6)

Support for Jest 28

### [`v2.0.5`](https://github.com/marchaos/jest-mock-extended/releases/tag/2.0.5)

[Compare Source](marchaos/jest-mock-extended@2.0.4...2.0.5)

Fixes:

-   Explicitly Show DeepMockProxy<T> In Readme Example - marchaos/jest-mock-extended#86
-   Fix ESM support by avoiding export default as syntax - marchaos/jest-mock-extended#83
-   Fix TypeError thrown in mockReset/mockClear - marchaos/jest-mock-extended#81

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/Droplet/pulls/109
Reviewed-by: Vylpes <ethan@vylpes.com>
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

mockDeep does not work when interface is extended from another type with AxiosInstance
2 participants