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 the ability of creating matchers wrapping a value #8540

Closed
XVincentX opened this issue Jun 8, 2019 · 17 comments
Closed

Add the ability of creating matchers wrapping a value #8540

XVincentX opened this issue Jun 8, 2019 · 17 comments

Comments

@XVincentX
Copy link

🚀 Feature Proposal

Currently there's no way to create a matcher that holds a wrapped value, such as a Promise.

Motivation

This limits the possibility extending jest to work nicely with some fp libraries, forcing me to write repetitive boilerplate code:

expect(actualConfig.isRight()).toBeTruthy();
actualConfig.map(operationConfig => {
  expect(operationConfig).toEqual(expectedConfig);
});

Example

expect(value).right.toMatchObject({})
@pedrottimark
Copy link
Contributor

Can you write an example test to contrast your need with assertion which has resolves or rejects

@XVincentX
Copy link
Author

@pedrottimark I'm afraid I fail to understand your request — the general idea is to have a way to extend the expect API in a way we do not just add simple assertions — but also "container" modifiers such as resolves and rejects that's internally performing additional checks.

Does that clarify?

@pedrottimark
Copy link
Contributor

It is likely that my lack of experience with fp libraries affects my ability to understand.

Here is an example of a limitation I do know which might be analogy to what you need.

The toHaveProperty(path, value) matcher combines:

  • check does the path exist
  • if the path exists, compare value similar to toEqual matcher

But what if you need to compare like toStrictEqual or toMatchObject or application-specific?

Is the extension ability you need analogy to chain matcher for path followed by matcher for value?

@XVincentX
Copy link
Author

XVincentX commented Jun 13, 2019

Kind of — I'm really looking to wrapped values though, let me give you the minimal introduction, maybe that's going to help you

fp-ts in particular has an object, Either — which represents a success or a failure.
You can inspect the result by checking either.isRight, either.isLeft and other methods, just like a Promise can be either resolved or rejected.

What I'd like to do is

const either = operationThatMightFail();

expect(either).success.toMatchObject(expectedData);
expect(either).not.fail.toMatchObject(expectedData);

Internally, the success or fail would unwrap the value internally and then execute the expect statement. Does that clarify?

@pedrottimark
Copy link
Contributor

Yes, that does clarify. Here is a thought that came to mind about a work around:

  • test helper function which given either returns a promise which is responsible to unwrap the value and call either its resolve or reject function
  • assertions with resolves or rejects chain to matcher

@XVincentX
Copy link
Author

@pedrottimark Yeah I ended up defining my own function https://github.com/stoplightio/prism/pull/323/files#diff-e53e98c10a813d2d271cc0fa5c55ddcaR11

although I'd say your workaround seems cool as well.

In any case, do you think there's going to be a way to have a native way to handle generically wrapped values?

@pedrottimark
Copy link
Contributor

If I understand correctly that a generic description of the need is be able to extend with methods that can continue the chain, then I will keep that in mind while I explore depths of expect package, which is where my attention has been this year.

As an example of another request which has that status: asymmetric matchers do not have access to the matcher utils that are available to application-specific matchers.

@XVincentX
Copy link
Author

Fantastic, you got my need perfectly! I'll subscribe to the issue, I'm eager to see this moving forward.

The workaround you suggested, for now, will just do :)

@XVincentX
Copy link
Author

@pedrottimark 👋

Just wondering if this is being considered as a feature or if you'd be interested in having a PR for that. It's not blocking me, but it would simplify a lot of the testing code we currently have in Prism.

Cheers!

@jeysal
Copy link
Contributor

jeysal commented Jun 7, 2020

I would be wary of adding things to expect that make it a highly dynamic assertion library. Additional matchers etc are okay, but things that can fundamentally change the structure of assertion statements will also make our lives harder e.g. in eslint-plugin-jest. I would say it is good enough that anyone can use any assertion library they choose to use with Jest, and that can include one built on top of expect! So for cases like this, you could create a custom expect that is imported from somewhere, which works roughly like

const xpct = (val) => ({
  ...expect(val),
  get right() {
    return xpct(val.getWrappedRightSomehow());
  }
})

@XVincentX
Copy link
Author

@jeysal That would be a good compromise.

Now my memory might be flaking but I recall trying the approach and could not get very far, but I might give it another try.

Is there a documentation page on how to extend properly the expect library?

@jeysal
Copy link
Contributor

jeysal commented Jun 7, 2020

Only for the official expect.extend which allows for custom matchers etc.
For this kind of customization, no, but I'm not sure we need one - it's like wrapping any other JS library. As long as the calls that the facade makes to expect are the same that a user would make to it directly (all public API) it'll work and no breakages should happen in a minor/patch version.

@XVincentX
Copy link
Author

XVincentX commented Jun 7, 2020

That's be my intention — transform a custom function call into internal expect calls. My idea would be to transform this:

export function assertRight<L, A>(e: E.Either<L, A>, onRight: (a: A) => void = noop): asserts e is E.Right<A> {
  pipe(
    e,
    E.fold(l => {
      throw new Error('Right expected, got a Left: ' + l);
    }, onRight)
  );
}

Into something that'd be integrated with jest — that'd unwrap the value for me:

into expect(val).right().toBe(10)

The problem is that the proposed right function would need to return an expect object so I could chain it. I guess I can do something like this?:

const xpct = <L, A>(val: E.Either<L, A>) => ({
  ...expect(val),
  get aRight() {
    return pipe(
      e,
      E.fold(() => {
        expect.assertions(Infinity)
        return expect(val)
      }, expect)
    );
  }
})

Although I'd have the problem that I'd be handling multiple types, such as Either, Option, TaskEither

@jeysal
Copy link
Contributor

jeysal commented Jun 7, 2020

That's similar to my example so something like it should work. Note that I'd recommend returning an xpct() again in the inner function so that it still has your customizations applied. Of course if you want to restrict the order in which you can call certain modifiers a more advanced system may be needed.

@github-actions
Copy link

This issue is stale because it has been open for 1 year with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions github-actions bot added the Stale label Feb 25, 2022
@github-actions
Copy link

This issue was closed because it has been stalled for 7 days with no activity. Please open a new issue if the issue is still relevant, linking to this one.

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 30, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants