Skip to content

Jest All Asymmetric Matchers at Point #8776

Closed
@nullxone

Description

@nullxone

🚀 Feature Proposal

Nest jest asymmetric matchers / use multiple of them at same point.

Preserve all error messages in each of nested matcher to quickly resolve errors.

Something like this?

expect.extend({
  all<T>(received: T, fn: (received: T) => void) {
    try {
      fn(received);
    } catch (e) {
      process.nextTick(() => {
        throw e;
      });
      const res = (e as any).matcherResult;
      return res || { pass: false, message: e.message };
    }
    return { pass: true, message: "" };
  },
});

it("pieces to compose", async () => {
  expect([1, { a: 1 }]).toMatchObject([1, expect.objectContaining({ a: 1 })]);

  expect([1, 2]).toEqual(expect.arrayContaining([1, expect.anything()]));

  expect({ a: 1, b: 2 }).toMatchObject({
    a: 1,
    b: expect.any(Number),
  });
});

/*
## Motivation

Please outline the motivation for the proposal.
*/

it("jest all asymmetric matcher", async () => {
  expect(1).toEqual(
    expect.all(v => {
      expect(v).toBe(1);
      expect(v + 1).toBe(2);
    })
  );
});

it("FAIL expect", async () => {
  expect(1).toEqual(
    expect.all(v => {
      expect(v).toBe(2);
      expect(v + 1).toBe(2);
    })
  );
});

it("FAIL error", async () => {
  expect(1).toEqual(
    expect.all(v => {
      throw new Error("foo!");
      expect(v).toBe(2);
      expect(v + 1).toBe(2);
    })
  );
});

/*
## Example

Please provide an example for how this feature would be used.
*/

describe("complex example", () => {
  it("use like this", async () => {
    const res = {
      users: [
        {
          id: "user1",
          posts: [{ id: "post1" }, { id: "post2" }],
        },
        {
          id: "user2",
          posts: [{ id: "post3" }],
        },
      ],
    };
    expect(res).toMatchObject({
      // users: expect.arrayContaining([
      users: expect.objectContaining([
        expect.objectContaining({
          id: "user1",
          posts: expect.all(v => {
            expect(v).toHaveLength(2);
            expect(v).toMatchObject([
              expect.objectContaining({ id: "post1" }),
              expect.objectContaining({ id: expect.anything() }),
            ]);
          }),
        }),
        expect.objectContaining({
          id: "user2",
          posts: expect.all(v => {
            expect(v).toHaveLength(1);
          }),
        }),
      ]),
    });
  });

  it("FAIL shows point where failed", async () => {
    const res = {
      users: [
        {
          id: "user1",
          posts: [{ id: "post1" }, { id: "post2" }],
        },
        {
          id: "user2",
          posts: [{ id: "post3" }],
        },
      ],
    };
    expect(res).toMatchObject({
      // users: expect.arrayContaining([
      users: expect.objectContaining([
        expect.objectContaining({
          id: "user1",
          posts: expect.all(v => {
            expect(v).toHaveLength(2);
            expect(v).toMatchObject([
              expect.objectContaining({ id: "post1" }),
              expect.objectContaining({ id: expect.anything() }),
            ]);
          }),
        }),
        expect.objectContaining({
          id: "user2",
          posts: expect.all(v => {
            expect(v).toHaveLength(2); // THIS WAS CHANGED
          }),
        }),
      ]),
    });
  });
});

Pitch

Why does this feature belong in the Jest core platform?

todo / to test

  • use with resolve rejects
  • pass async functions
  • nested expectation failures / thrown errors: should not print the test name again (but it should still print both error messages)
  • typescript types

references #5788

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions