Repository: microsoft/playwright
File: packages/playwright/src/matchers/matchers.ts (TypeScript)

Code before:
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { asLocatorDescription, constructURLBasedOnBaseURL, isRegExp, isString, isTextualMimeType, pollAgainstDeadline, serializeExpectedTextValues } from 'playwright-core/lib/utils';
import { colors } from 'playwright-core/lib/utils';

import { expectTypes } from '../util';
import { toBeTruthy } from './toBeTruthy';
import { toEqual } from './toEqual';
import { toHaveURLWithPredicate } from './toHaveURL';
import { toMatchText } from './toMatchText';
import { toHaveScreenshotStepTitle } from './toMatchSnapshot';
import { takeFirst } from '../common/config';
import { currentTestInfo } from '../common/globals';
import { TestInfoImpl } from '../worker/testInfo';
import { formatMatcherMessage } from './matcherHint';

import type { ExpectMatcherState } from '../../types/test';
import type { TestStepInfoImpl } from '../worker/testInfo';
import type { APIResponse, Locator, Frame, Page } from 'playwright-core';
import type { FrameExpectParams } from 'playwright-core/lib/client/types';
import type { CSSProperties } from 'react';

export type ExpectMatcherStateInternal = ExpectMatcherState & { _stepInfo?: TestStepInfoImpl };

export interface LocatorEx extends Locator {
  _selector: string;
  _expect(expression: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean, errorMessage?: string }>;
}

export interface FrameEx extends Frame {
  _expect(expression: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean, errorMessage?: string }>;
}

interface APIResponseEx extends APIResponse {
  _fetchLog(): Promise<string[]>;
}

export function toBeAttached(
  this: ExpectMatcherState,
  locator: LocatorEx,
  options?: { attached?: boolean, timeout?: number },
) {
  const attached = !options || options.attached === undefined || options.attached;
  const expected = attached ? 'attached' : 'detached';
  const arg = attached ? '' : '{ attached: false }';
  return toBeTruthy.call(this, 'toBeAttached', locator, 'Locator', expected, arg, async (isNot, timeout) => {

Review comment (suggestion):
We can't afford a dependency on 'react'. Can you make it work in a best effort fashion similar to what we do for [Electron types](https://github.com/microsoft/playwright/blob/0ed249b8b9ca880cf10c5d97728ddf97192a177f/packages/playwright-core/types/types.d.ts#L15999-L16000) today? Basically, if the user has 'react' in their project, strong typecheck would apply. Otherwise accepted type should be `string` only.

Code after:
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { asLocatorDescription, constructURLBasedOnBaseURL, isRegExp, isString, isTextualMimeType, pollAgainstDeadline, serializeExpectedTextValues, toKebabCase } from 'playwright-core/lib/utils';
import { colors } from 'playwright-core/lib/utils';

import { expectTypes } from '../util';
import { toBeTruthy } from './toBeTruthy';
import { toEqual } from './toEqual';
import { toHaveURLWithPredicate } from './toHaveURL';
import { toMatchText } from './toMatchText';
import { toHaveScreenshotStepTitle } from './toMatchSnapshot';
import { takeFirst } from '../common/config';
import { currentTestInfo } from '../common/globals';
import { TestInfoImpl } from '../worker/testInfo';
import { formatMatcherMessage, MatcherResult } from './matcherHint';

import type { ExpectMatcherState } from '../../types/test';
import type { TestStepInfoImpl } from '../worker/testInfo';
import type { APIResponse, Locator, Frame, Page } from 'playwright-core';
import type { FrameExpectParams } from 'playwright-core/lib/client/types';
import type { CSSProperties } from '../../types/test';

export type ExpectMatcherStateInternal = ExpectMatcherState & { _stepInfo?: TestStepInfoImpl };

export interface LocatorEx extends Locator {
  _selector: string;
  _expect(expression: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean, errorMessage?: string }>;
}

export interface FrameEx extends Frame {
  _expect(expression: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean, errorMessage?: string }>;
}

interface APIResponseEx extends APIResponse {
  _fetchLog(): Promise<string[]>;
}

export function toBeAttached(
  this: ExpectMatcherState,
  locator: LocatorEx,
  options?: { attached?: boolean, timeout?: number },
) {
  const attached = !options || options.attached === undefined || options.attached;
  const expected = attached ? 'attached' : 'detached';
  const arg = attached ? '' : '{ attached: false }';
  return toBeTruthy.call(this, 'toBeAttached', locator, 'Locator', expected, arg, async (isNot, timeout) => {