playwright icon indicating copy to clipboard operation
playwright copied to clipboard

[Docs]: `expect.extend` documented as if it was immutable but it's in fact mutable

Open IgnusG opened this issue 1 year ago • 2 comments

Version

1.46.1

Steps to reproduce

The following code ends up in an infinite loop as jest's expect is mutable - the documentation of Playwright however does not mention this and since it uses the pattern export const expect = baseExpect.extend() one would assume it's by default immutable (the original is not changed).

import {
  expect as baseExpect,
  MatcherReturnType,
  Page,
} from '@playwright/test';

export const expect = baseExpect.extend({
  toHaveURL: async (
    page: Page,
    ...args: Parameters<PageAssertions['toHaveURL']>
  ) => {
    try {
      // one would assume this calls the original method but instead it actually calls the extended method in a loop
      await baseExpect.toHaveURL(...args);
      // do some stuff

      return {
        pass: true,
        message: () => '',
      };
    } catch (error) {
      const matcherError = error as { matcherResult: MatcherReturnType };

      if (matcherError.matcherResult) {
        return matcherError.matcherResult;
      }

      throw error;
    }
  },
});

PS: I know the recommendation would be to just not overwrite the base matchers but people will always try nonetheless 😅

Expected behavior

Either the expect should be adjusted to be immutable or this should be mentioned in the docs.

Actual behavior

The expect is mutable and calling expect.extend modifies the original expect.

Additional context

No response

Environment

System:
    OS: macOS 14.5
    Memory: 78.63 MB / 32.00 GB
  Binaries:
    Node: 18.14.2 - ~/.asdf/installs/nodejs/18.14.2/bin/node
    Yarn: 1.22.19 - ~/.asdf/shims/yarn
    npm: 9.5.0 - ~/.asdf/plugins/nodejs/shims/npm
  Languages:
    Bash: 3.2.57 - /bin/bash

IgnusG avatar Aug 22 '24 14:08 IgnusG

Sounds good! Feel free to open a pull request with a small docs addition for this.

Skn0tt avatar Aug 23 '24 10:08 Skn0tt

Anther instance of folks being surprised by the behaviour here: https://github.com/microsoft/playwright/issues/32340

Skn0tt avatar Aug 28 '24 08:08 Skn0tt

Hey @Skn0tt, would I be able to be assigned to this as my first contribution? Thanks!

ryangchung avatar Dec 25 '24 06:12 ryangchung

It seems that it is not the case anymore (1.56.1). i.e. built-in expect can be overridden and the original remains accessible and intact.

You can use the base expect as follows, even from within the expect.extend

await baseExpect(page).toHaveURL(url);

Should we add a note in the documentation? I'm not sure if it's good to encourage overriding assertion.

jfgreffier avatar Oct 30 '25 17:10 jfgreffier

Ah yes, we fixed this through https://github.com/microsoft/playwright/pull/32366. I don't think we need to mention it in the documentation - the API is designed so it looks immutable, and it now is indeed immutable.

Skn0tt avatar Nov 11 '25 11:11 Skn0tt