mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			247 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			247 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 | 
						|
 *
 | 
						|
 * This source code is licensed under the MIT license found in the
 | 
						|
 * LICENSE file in the root directory of this source tree.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
import { test, expect } from './fixtures';
 | 
						|
import { expect as expectUnderTest, mock } from '../../packages/playwright/bundles/expect/src/expectBundleImpl';
 | 
						|
 | 
						|
// Test test file demonstrates and tests the capability of recursive custom
 | 
						|
// testers that call `equals` within their tester logic. These testers should
 | 
						|
// receive the array of custom testers and be able to pass it into equals
 | 
						|
 | 
						|
const CONNECTION_PROP = '__connection';
 | 
						|
type DbConnection = number;
 | 
						|
let DbConnectionId = 0;
 | 
						|
 | 
						|
class Author {
 | 
						|
  public name: string;
 | 
						|
  public [CONNECTION_PROP]: DbConnection;
 | 
						|
 | 
						|
  constructor(name: string) {
 | 
						|
    this.name = name;
 | 
						|
    this[CONNECTION_PROP] = DbConnectionId++;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class Book {
 | 
						|
  public name: string;
 | 
						|
  public authors: Array<Author>;
 | 
						|
  public [CONNECTION_PROP]: DbConnection;
 | 
						|
 | 
						|
  constructor(name: string, authors: Array<Author>) {
 | 
						|
    this.name = name;
 | 
						|
    this.authors = authors;
 | 
						|
    this[CONNECTION_PROP] = DbConnectionId++;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
const areAuthorsEqual = (a: unknown, b: unknown) => {
 | 
						|
  const isAAuthor = a instanceof Author;
 | 
						|
  const isBAuthor = b instanceof Author;
 | 
						|
 | 
						|
  if (isAAuthor && isBAuthor)
 | 
						|
    return a.name === b.name;
 | 
						|
  else if (isAAuthor !== isBAuthor)
 | 
						|
    return false;
 | 
						|
  else
 | 
						|
    return undefined;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
const areBooksEqual = function(
 | 
						|
  a: unknown,
 | 
						|
  b: unknown,
 | 
						|
  customTesters: [],
 | 
						|
) {
 | 
						|
  const isABook = a instanceof Book;
 | 
						|
  const isBBook = b instanceof Book;
 | 
						|
 | 
						|
  if (isABook && isBBook) {
 | 
						|
    return (
 | 
						|
      a.name === b.name && this.equals(a.authors, b.authors, customTesters)
 | 
						|
    );
 | 
						|
  } else if (isABook !== isBBook) {
 | 
						|
    return false;
 | 
						|
  } else {
 | 
						|
    return undefined;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
function* toIterator<T>(array: Array<T>): Iterator<T> {
 | 
						|
  for (const obj of array)
 | 
						|
    yield obj;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
expectUnderTest.extend({
 | 
						|
  toEqualBook(expected: Book, actual: Book) {
 | 
						|
    const result = this.equals(expected, actual, this.customTesters);
 | 
						|
 | 
						|
    return {
 | 
						|
      message: () =>
 | 
						|
        `Expected Book object: ${expected.name}. Actual Book object: ${actual.name}`,
 | 
						|
      pass: result,
 | 
						|
    };
 | 
						|
  },
 | 
						|
});
 | 
						|
 | 
						|
// Create books with the same name and authors for use in tests. Without the
 | 
						|
// custom tester, these books would not be equal because their DbConnections
 | 
						|
// would have different values. However, with our custom tester they are equal.
 | 
						|
const book1 = new Book('Book 1', [
 | 
						|
  new Author('Author 1'),
 | 
						|
  new Author('Author 2'),
 | 
						|
]);
 | 
						|
const book1b = new Book('Book 1', [
 | 
						|
  new Author('Author 1'),
 | 
						|
  new Author('Author 2'),
 | 
						|
]);
 | 
						|
 | 
						|
const bookArg1a = new Book('Book Arg 1', [
 | 
						|
  new Author('Author Arg 1'),
 | 
						|
  new Author('Author Arg 2'),
 | 
						|
]);
 | 
						|
const bookArg1b = new Book('Book Arg 1', [
 | 
						|
  new Author('Author Arg 1'),
 | 
						|
  new Author('Author Arg 2'),
 | 
						|
]);
 | 
						|
const bookArg2a = new Book('Book Arg 2', [
 | 
						|
  new Author('Author Arg 3'),
 | 
						|
  new Author('Author Arg 4'),
 | 
						|
]);
 | 
						|
const bookArg2b = new Book('Book Arg 2', [
 | 
						|
  new Author('Author Arg 3'),
 | 
						|
  new Author('Author Arg 4'),
 | 
						|
]);
 | 
						|
 | 
						|
const bookReturn1a = new Book('Book Return 1', [
 | 
						|
  new Author('Author Return 1'),
 | 
						|
  new Author('Author Return 2'),
 | 
						|
]);
 | 
						|
const bookReturn1b = new Book('Book Return 1', [
 | 
						|
  new Author('Author Return 1'),
 | 
						|
  new Author('Author Return 2'),
 | 
						|
]);
 | 
						|
 | 
						|
const testArgs = [bookArg1a, bookArg1b, [bookArg2a, bookArg2b]];
 | 
						|
// Swap the order of args to assert custom tester works correctly and ignores
 | 
						|
// DbConnection differences
 | 
						|
const expectedArgs = [bookArg1b, bookArg1a, [bookArg2b, bookArg2a]];
 | 
						|
 | 
						|
expectUnderTest.addEqualityTesters([areAuthorsEqual, areBooksEqual]);
 | 
						|
 | 
						|
test.describe('with custom equality testers', () => {
 | 
						|
  test('exposes an equality function to custom testers', () => {
 | 
						|
    const runTestSymbol = Symbol('run this test');
 | 
						|
 | 
						|
    expectUnderTest.addEqualityTesters([
 | 
						|
      function dummyTester() {
 | 
						|
        return undefined;
 | 
						|
      },
 | 
						|
    ]);
 | 
						|
 | 
						|
    expectUnderTest(() =>
 | 
						|
      expectUnderTest(runTestSymbol).toEqual(runTestSymbol),
 | 
						|
    ).not.toThrow();
 | 
						|
  });
 | 
						|
 | 
						|
  test('basic matchers customTesters do not apply to still do not pass different Book objects', () => {
 | 
						|
    expectUnderTest(book1).not.toBe(book1b);
 | 
						|
    expectUnderTest([book1]).not.toContain(book1b);
 | 
						|
  });
 | 
						|
 | 
						|
  test('basic matchers pass different Book objects', () => {
 | 
						|
    expectUnderTest(book1).toEqual(book1);
 | 
						|
    expectUnderTest(book1).toEqual(book1b);
 | 
						|
    expectUnderTest([book1, book1b]).toEqual([book1b, book1]);
 | 
						|
    expectUnderTest(new Map([['key', book1]])).toEqual(new Map([['key', book1b]]));
 | 
						|
    expectUnderTest(new Set([book1])).toEqual(new Set([book1b]));
 | 
						|
    expectUnderTest(toIterator([book1, book1b])).toEqual(toIterator([book1b, book1]));
 | 
						|
    expectUnderTest([book1]).toContainEqual(book1b);
 | 
						|
    expectUnderTest({ a: book1 }).toHaveProperty('a', book1b);
 | 
						|
    expectUnderTest({ a: book1, b: undefined }).toStrictEqual({
 | 
						|
      a: book1b,
 | 
						|
      b: undefined,
 | 
						|
    });
 | 
						|
    expectUnderTest({ a: 1, b: { c: book1 } }).toMatchObject({
 | 
						|
      a: 1,
 | 
						|
      b: { c: book1b },
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  test('asymmetric matchers pass different Book objects', () => {
 | 
						|
    expectUnderTest([book1]).toEqual(expect.arrayContaining([book1b]));
 | 
						|
    expectUnderTest({ a: 1, b: { c: book1 } }).toEqual(
 | 
						|
        expect.objectContaining({ b: { c: book1b } }),
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  test('spy matchers pass different Book objects', () => {
 | 
						|
    const mockFn = mock.fn<(...args: Array<unknown>) => unknown>(
 | 
						|
        () => bookReturn1a,
 | 
						|
    );
 | 
						|
    mockFn(...testArgs);
 | 
						|
 | 
						|
    expectUnderTest(mockFn).toHaveBeenCalledWith(...expectedArgs);
 | 
						|
    expectUnderTest(mockFn).toHaveBeenLastCalledWith(...expectedArgs);
 | 
						|
    expectUnderTest(mockFn).toHaveBeenNthCalledWith(1, ...expectedArgs);
 | 
						|
 | 
						|
    expectUnderTest(mockFn).toHaveReturnedWith(bookReturn1b);
 | 
						|
    expectUnderTest(mockFn).toHaveLastReturnedWith(bookReturn1b);
 | 
						|
    expectUnderTest(mockFn).toHaveNthReturnedWith(1, bookReturn1b);
 | 
						|
  });
 | 
						|
 | 
						|
  test('custom matchers pass different Book objects', () => {
 | 
						|
    (expectUnderTest as any)(book1).toEqualBook(book1b);
 | 
						|
  });
 | 
						|
 | 
						|
  test('toBe recommends toStrictEqual even with different Book objects', () => {
 | 
						|
    expectUnderTest(() => expectUnderTest(book1).toBe(book1b)).toThrow('toStrictEqual');
 | 
						|
  });
 | 
						|
 | 
						|
  test('toBe recommends toEqual even with different Book objects', () => {
 | 
						|
    expectUnderTest(() => expectUnderTest({ a: undefined, b: book1 }).toBe({ b: book1b })).toThrow(
 | 
						|
        'toEqual',
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  test('toContains recommends toContainEquals even with different Book objects', () => {
 | 
						|
    expectUnderTest(() => expectUnderTest([book1]).toContain(book1b)).toThrow('toContainEqual');
 | 
						|
  });
 | 
						|
 | 
						|
  test('toMatchObject error shows Book objects as equal', () => {
 | 
						|
    expect(() =>
 | 
						|
      expectUnderTest({ a: 1, b: book1 }).toMatchObject({ a: 2, b: book1b })
 | 
						|
    ).toThrowErrorMatchingSnapshot(`<d>expect(</><r>received</><d>).</>toMatchObject<d>(</><g>expected</><d>)</>
 | 
						|
 | 
						|
<g>- Expected  - 1</>
 | 
						|
<r>+ Received  + 1</>
 | 
						|
 | 
						|
<y>@@ -1,7 +1,7 @@</>
 | 
						|
<d>  Object {</>
 | 
						|
<g>-   "a": 2,</>
 | 
						|
<r>+   "a": 1,</>
 | 
						|
<d>    "b": Book {</>
 | 
						|
<d>      "__connection": 5,</>
 | 
						|
<d>      "authors": Array [</>
 | 
						|
<d>        Author {</>
 | 
						|
<d>          "__connection": 3,</>`);
 | 
						|
  });
 | 
						|
 | 
						|
  test('iterableEquality still properly detects cycles', () => {
 | 
						|
    const a = new Set();
 | 
						|
    a.add(book1);
 | 
						|
    a.add(a);
 | 
						|
 | 
						|
    const b = new Set();
 | 
						|
    b.add(book1b);
 | 
						|
    b.add(b);
 | 
						|
 | 
						|
    expectUnderTest(a).toEqual(b);
 | 
						|
  });
 | 
						|
});
 |