extend-chrome / jest-chrome

A complete mock of the Chrome API for Chrome extensions for use with Jest.
MIT License
125 stars 24 forks source link

Getting from MockedFunction to Mock #9

Closed yakirn closed 3 years ago

yakirn commented 3 years ago

Hey, First of all, thank you for an awesome extension! Second, please see this unanswered question. It's more of a quality of life thing than an actual issue (I hate to use any in TS). Figured since the code works just fine there must be a less risky way to tell TS it's fine. The assertion is from jest-extended

TL;DR I'm trying to make TS to accept the following line but without casting to any:
expect(chrome.tabs.update).toHaveBeenCalledAfter(chrome.cookies.set as any); Thanks!

jacksteamdev commented 3 years ago

Great question! It looks like there's an open PR in jest-extended that fixes this issue.

The root of the problem is that Mock and MockedFunction are both based on MockInstance, but are different types.

This could be solved two ways:

  1. toHaveBeenCalledAfter could take a MockInstance, not a Mock. This is what the PR for jest-extended does.
  2. MockedFunction could be based on Mock. This is probably the best solution.

Both probably need to happen.

I've made a draft PR in @types/jest, but I don't have time to finish it before my next meeting. I'll try to get back to it soon. Feel free to run with it if you want!

jacksteamdev commented 3 years ago

@yakirn Maybe you could cast to MockInstance in the meantime? At least it's better than any.

expect(chrome.tabs.update).toHaveBeenCalledAfter(chrome.cookies.set as jest.MockInstance);
yakirn commented 3 years ago

@jacksteamdev casting to jest.Mock did the trick!
expect(chrome.tabs.update).toHaveBeenCalledAfter(chrome.cookies.set as jest.Mock);

jest.MockInstance is a generic that requires two arguments, but also jest.MockInstance<any, any> failed with:

interface jest.MockInstance<T, Y extends any[]> Argument of type 'MockInstance<any, any>' is not assignable to parameter of type 'Mock<any, any>'. Type 'MockInstance<any, any>' is missing the following properties from type 'Mock<any, any>': apply, call, bind, prototype, and 5 more.ts(2345)

Anything left to do with the PR do DefinitelyTyped? If you can fill me in I can try and continue over the weekend.

jacksteamdev commented 3 years ago

@yakirn I finished the DefinitelyTyped PR. I wanted to add some tests. We'll see how it goes 🤞

We should upvote the PR in jest-extended, which needs to get merged.

https://github.com/jest-community/jest-extended/pull/292

yakirn commented 3 years ago

Thank you so much @jacksteamdev ! I upvoted and I’ll ask my coworkers to do the same :)

jacksteamdev commented 3 years ago

@yakirn It looks like these PRs probably won't go anywhere fast. :disappointed:

But, we can use module augmentation to fix it locally. Create a declaration file, (ie, jest-mocked-function.d.ts), and put this in it:

/// <reference types="jest" />

declare namespace jest {
  interface Matchers {
    /**
     * Use `.toHaveBeenCalledBefore` when checking if a `MockInstance` was called before another `MockInstance`.
     *
     * Note: Required Jest version >=23
     *
     * @param {MockInstance} mock
     */
    toHaveBeenCalledBefore(mock: jest.MockInstance): R

    /**
     * Use `.toHaveBeenCalledAfter` when checking if a `MockInstance` was called after another `MockInstance`.
     *
     * Note: Required Jest version >=23
     *
     * @param {MockInstance} mock
     */
    toHaveBeenCalledAfter(mock: jest.MockInstance): R
  }

  interface Expect {
    /**
     * Use `.toHaveBeenCalledBefore` when checking if a `MockInstance` was called before another `MockInstance`.
     *
     * Note: Required Jest version >=23
     *
     * @param {MockInstance} mock
     */
    toHaveBeenCalledBefore(mock: jest.MockInstance): R

    /**
     * Use `.toHaveBeenCalledAfter` when checking if a `MockInstance` was called after another `MockInstance`.
     *
     * Note: Required Jest version >=23
     *
     * @param {MockInstance} mock
     */
    toHaveBeenCalledAfter(mock: jest.MockInstance): R
  }
}

I've tried it out like this, and it works for me!

import { chrome } from "jest-chrome";

import "jest-extended";

test('toBeCalledAfter allows MockedFunction', () => {
  expect(chrome.alarms.clear).toHaveBeenCalledAfter(chrome.alarms.create)
})