2021-06-06 17:09:53 -07:00
/ * *
* Copyright Microsoft Corporation . All rights reserved .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* 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 .
* /
2022-08-10 15:10:25 -07:00
import path from 'path' ;
2023-09-22 12:12:17 -07:00
import { test , expect , parseTestRunnerOutput , stripAnsi } from './playwright-test-fixtures' ;
2023-12-19 18:55:05 +00:00
const { spawnAsync } = require ( '../../packages/playwright-core/lib/utils' ) ;
2021-06-06 17:09:53 -07:00
2021-12-03 14:55:16 -08:00
test ( 'should not expand huge arrays' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'expect-test.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-12-03 14:55:16 -08:00
test ( 'numeric ranges' , ( ) = > {
const a1 = Array ( 100000 ) . fill ( 1 ) ;
const a2 = Array ( 100000 ) . fill ( 1 ) ;
a2 [ 500 ] = 2 ;
test . expect ( a1 ) . toEqual ( a2 ) ;
} ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
expect ( result . output . length ) . toBeLessThan ( 100000 ) ;
} ) ;
2024-02-06 12:12:45 -08:00
test ( 'should include custom expect message' , async ( { runInlineTest } ) = > {
2022-01-31 18:14:59 -07:00
const result = await runInlineTest ( {
'expect-test.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-01-31 18:14:59 -07:00
test ( 'custom expect message' , ( ) = > {
2024-02-06 12:12:45 -08:00
test . expect ( 1 + 1 , 'one plus one should be two!' ) . toEqual ( 3 ) ;
2022-01-31 18:14:59 -07:00
} ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
2023-02-07 15:11:44 -08:00
expect ( result . output ) . toContain ( [
2024-02-06 12:12:45 -08:00
` Error: one plus one should be two! \ n ` ,
2023-09-16 14:24:10 -07:00
` expect(received).toEqual(expected) // deep equality \ n ` ,
2022-01-31 18:14:59 -07:00
` Expected: 3 ` ,
` Received: 2 ` ,
] . join ( '\n' ) ) ;
} ) ;
2024-02-06 12:12:45 -08:00
test ( 'should include custom expect message with web-first assertions' , async ( { runInlineTest } ) = > {
2022-01-31 18:14:59 -07:00
const result = await runInlineTest ( {
'expect-test.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-01-31 18:14:59 -07:00
test ( 'custom expect message' , async ( { page } ) = > {
2022-03-18 17:31:26 -06:00
await expect ( page . locator ( 'x-foo' ) , { message : 'x-foo must be visible' } ) . toBeVisible ( { timeout : 1 } ) ;
2022-01-31 18:14:59 -07:00
} ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
2023-09-16 14:24:10 -07:00
expect ( result . output ) . toContain ( 'Error: x-foo must be visible' ) ;
expect ( result . output ) . toContain ( ` Timed out 1ms waiting for expect(locator).toBeVisible() ` ) ;
expect ( result . output ) . toContain ( 'Call log:' ) ;
2022-01-31 18:14:59 -07:00
} ) ;
2023-02-01 16:55:52 -08:00
test ( 'should work with generic matchers' , async ( { runTSC } ) = > {
2021-06-06 17:09:53 -07:00
const result = await runTSC ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { expect } from '@playwright/test' ;
2023-02-01 16:55:52 -08:00
expect ( 42 ) . toBe ( 42 ) ;
expect ( 0.1 + 0.2 ) . toBeCloseTo ( 0.3 , 5 ) ;
expect ( null ) . toBeDefined ( ) ;
expect ( null ) . toBeFalsy ( ) ;
expect ( 42 ) . toBeGreaterThan ( 1 ) ;
expect ( 42 ) . toBeGreaterThanOrEqual ( 42 ) ;
expect ( { } ) . toBeInstanceOf ( Object ) ;
expect ( 42 ) . toBeLessThan ( 100 ) ;
expect ( 42 ) . toBeLessThanOrEqual ( 42 ) ;
expect ( null ) . toBeNull ( ) ;
expect ( 42 ) . toBeTruthy ( ) ;
expect ( undefined ) . toBeUndefined ( ) ;
expect ( NaN ) . toBeNaN ( ) ;
expect ( 'abc' ) . toContain ( 'a' ) ;
expect ( [ 'abc' ] ) . toContain ( 'abc' ) ;
expect ( [ 'abc' ] ) . toContainEqual ( 'abc' ) ;
expect ( { } ) . toEqual ( { } ) ;
expect ( [ 1 , 2 ] ) . toHaveLength ( 2 ) ;
expect ( 'abc' ) . toMatch ( /a.?c/ ) ;
2023-03-07 17:53:50 +01:00
expect ( 'abc' ) . toMatch ( 'abc' ) ;
2023-02-01 16:55:52 -08:00
expect ( { a : 1 , b : 2 } ) . toMatchObject ( { a : 1 } ) ;
expect ( { } ) . toStrictEqual ( { } ) ;
expect ( ( ) = > { throw new Error ( 'Something bad' ) ; } ) . toThrow ( 'something' ) ;
expect ( ( ) = > { throw new Error ( 'Something bad' ) ; } ) . toThrowError ( 'something' ) ;
2023-02-03 15:56:31 -08:00
expect ( [ 'Bob' , 'Eve' ] ) . not . toEqual ( expect . arrayContaining ( [ 'Alice' , 'Bob' ] ) ) ;
expect ( { } ) . toEqual ( expect . anything ( ) ) ;
expect ( { sum : 0.1 + 0.2 } ) . toEqual ( { sum : expect.closeTo ( 0.3 , 5 ) } ) ;
class Cat { }
expect ( new Cat ( ) ) . toEqual ( expect . any ( Cat ) ) ;
expect ( { x : 2 , y : 3 , foo : 'bar' } ) . toEqual ( expect . objectContaining ( {
x : expect.any ( Number ) ,
y : expect.any ( Number ) ,
} ) ) ;
expect ( 'abc' ) . toEqual ( expect . stringContaining ( 'bc' ) ) ;
2023-03-20 12:52:52 -07:00
expect ( 'hello world' ) . toEqual ( expect . not . stringContaining ( 'text' ) ) ;
2023-02-03 15:56:31 -08:00
expect ( [ 'Alicia' , 'Roberto' , 'Evelina' ] ) . toEqual (
expect . arrayContaining ( [
expect . stringMatching ( /^Alic/ ) ,
expect . stringMatching ( 'Roberto' ) ,
] ) ,
) ;
2021-06-06 17:09:53 -07:00
`
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2023-02-01 16:55:52 -08:00
test ( 'should compile generic matchers' , async ( { runTSC } ) = > {
const result = await runTSC ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { expect } from '@playwright/test' ;
2023-02-01 16:55:52 -08:00
expect ( 42 ) . toBe ( 42 ) ;
expect ( 42 ) . toBeCloseTo ( 42 ) ;
expect ( 42 ) . toBeCloseTo ( 42 , 5 ) ;
expect ( 42 ) . toBeDefined ( ) ;
expect ( 42 ) . toBeFalsy ( ) ;
expect ( 42 ) . toBeGreaterThan ( 1 ) ;
expect ( 42 n ) . toBeGreaterThan ( 1 n ) ;
expect ( 42 ) . toBeGreaterThanOrEqual ( 1 ) ;
expect ( 42 n ) . toBeGreaterThanOrEqual ( 1 n ) ;
expect ( { } ) . toBeInstanceOf ( Object ) ;
expect ( 42 ) . toBeLessThan ( 1 ) ;
expect ( 42 n ) . toBeLessThan ( 1 n ) ;
expect ( 42 ) . toBeLessThanOrEqual ( 1 ) ;
expect ( 42 n ) . toBeLessThanOrEqual ( 1 n ) ;
expect ( 42 ) . toBeNull ( ) ;
expect ( 42 ) . toBeTruthy ( ) ;
expect ( 42 ) . toBeUndefined ( ) ;
expect ( 42 ) . toBeNaN ( ) ;
expect ( 'abc' ) . toContain ( 'b' ) ;
expect ( [ 1 , 2 ] ) . toContain ( 1 ) ;
expect ( new Set ( [ 1 , 2 ] ) ) . toContain ( 1 ) ;
expect ( [ { } , { a : 1 } ] ) . toContainEqual ( { } ) ;
expect ( { } ) . toEqual ( { } ) ;
expect ( [ 1 , 2 ] ) . toHaveLength ( 2 ) ;
expect ( 'abc' ) . toMatch ( /a.?c/ ) ;
expect ( { a : 1 , b : 2 } ) . toMatchObject ( { a : 1 } ) ;
expect ( [ ] ) . toMatchObject ( [ ] ) ;
expect ( { } ) . toStrictEqual ( { } ) ;
expect ( ( ) = > { throw new Error ( 'Something bad' ) ; } ) . toThrow ( 'something' ) ;
expect ( ( ) = > { throw new Error ( 'Something bad' ) ; } ) . toThrow ( ) ;
expect ( ( ) = > { throw new Error ( 'Something bad' ) ; } ) . toThrowError ( 'something' ) ;
expect ( ( ) = > { throw new Error ( 'Something bad' ) ; } ) . toThrowError ( ) ;
2023-02-03 15:56:31 -08:00
expect ( [ 'Bob' , 'Eve' ] ) . not . toEqual ( expect . arrayContaining ( [ 'Alice' , 'Bob' ] ) ) ;
expect ( { } ) . toEqual ( expect . anything ( ) ) ;
expect ( { sum : 0.1 + 0.2 } ) . toEqual ( { sum : expect.closeTo ( 0.3 , 5 ) } ) ;
class Cat { }
expect ( new Cat ( ) ) . toEqual ( expect . any ( Cat ) ) ;
expect ( { x : 2 , y : 3 , foo : 'bar' } ) . toEqual ( expect . objectContaining ( {
x : expect.any ( Number ) ,
y : expect.any ( Number ) ,
} ) ) ;
expect ( 'abc' ) . toEqual ( expect . stringContaining ( 'bc' ) ) ;
expect ( [ 'Alicia' , 'Roberto' , 'Evelina' ] ) . toEqual (
expect . arrayContaining ( [
expect . stringMatching ( /^Alic/ ) ,
expect . stringMatching ( 'Roberto' ) ,
] ) ,
) ;
2023-02-01 16:55:52 -08:00
// @ts-expect-error
expect ( 42 ) . toBe ( 123 , 456 ) ;
// @ts-expect-error
expect ( 42 ) . toBeCloseTo ( 42 , '5' ) ;
// @ts-expect-error
expect ( 42 ) . toBeFalsy ( 123 ) ;
// @ts-expect-error
expect ( { } ) . toBeInstanceOf ( { } ) ;
` ,
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2023-12-12 16:22:48 -08:00
test ( 'should work when passing a ReadonlyArray' , async ( { runTSC } ) = > {
const result = await runTSC ( {
'a.spec.ts' : `
import { test , expect } from '@playwright/test' ;
test ( 'example' , async ( { page } ) = > {
const readonlyArray : ReadonlyArray < string > = [ '1' , '2' , '3' ] ;
expect ( page . locator ( '.foo' ) ) . toHaveText ( readonlyArray ) ;
await page . locator ( '.foo' ) . setInputFiles ( readonlyArray ) ;
} ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2022-01-31 18:14:59 -07:00
test ( 'should work with expect message' , async ( { runTSC } ) = > {
const result = await runTSC ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-01-31 18:14:59 -07:00
test . expect ( 42 , 'this is expect message' ) . toBe ( 42 ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2021-09-27 18:58:08 +02:00
test ( 'should work with default expect matchers and esModuleInterop=false' , async ( { runTSC } ) = > {
2021-06-14 12:58:10 -07:00
const result = await runTSC ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-06-14 12:58:10 -07:00
test . expect ( 42 ) . toBe ( 42 ) ;
` ,
'tsconfig.json' : JSON . stringify ( {
'compilerOptions' : {
'target' : 'ESNext' ,
'moduleResolution' : 'node' ,
'module' : 'commonjs' ,
'strict' : true ,
'rootDir' : '.' ,
'esModuleInterop' : false ,
'allowSyntheticDefaultImports' : false ,
'lib' : [ 'esnext' , 'dom' , 'DOM.Iterable' ]
} ,
'exclude' : [
'node_modules'
]
} ) ,
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2021-09-27 18:58:08 +02:00
test ( 'should work with custom PlaywrightTest namespace' , async ( { runTSC } ) = > {
2021-06-06 17:09:53 -07:00
const result = await runTSC ( {
'global.d.ts' : `
2021-06-07 08:02:01 -07:00
declare namespace PlaywrightTest {
2021-06-06 17:09:53 -07:00
interface Matchers < R > {
toBeEmpty ( ) : R ;
}
2022-04-05 16:11:11 -07:00
interface Matchers < R , T > {
toBeNonEmpty ( ) : R ;
}
2021-06-06 17:09:53 -07:00
}
` ,
'a.spec.ts' : `
2023-09-15 18:05:44 +02:00
import { test , expect , type Page , type APIResponse } from '@playwright/test' ;
2021-06-06 17:09:53 -07:00
test . expect . extend ( {
2023-09-22 12:12:17 -07:00
toBeWithinRange() {
return {
pass : true ,
message : ( ) = > '' ,
} ;
} ,
2021-06-06 17:09:53 -07:00
} ) ;
2023-09-15 18:05:44 +02:00
const page = { } as Page ;
const locator = page . locator ( '' ) ;
const apiResponse = { } as APIResponse ;
test . expect ( page ) . toBeEmpty ( ) ;
test . expect ( page ) . not . toBeEmpty ( ) ;
test . expect ( locator ) . toBeEmpty ( ) ;
test . expect ( locator ) . not . toBeEmpty ( ) ;
test . expect ( apiResponse ) . toBeEmpty ( ) ;
test . expect ( apiResponse ) . not . toBeEmpty ( ) ;
2021-06-06 17:09:53 -07:00
test . expect ( '' ) . toBeEmpty ( ) ;
test . expect ( 'hello' ) . not . toBeEmpty ( ) ;
test . expect ( [ ] ) . toBeEmpty ( ) ;
test . expect ( [ 'hello' ] ) . not . toBeEmpty ( ) ;
test . expect ( { } ) . toBeEmpty ( ) ;
test . expect ( { hello : 'world' } ) . not . toBeEmpty ( ) ;
2022-04-05 16:11:11 -07:00
test . expect ( '' ) . toBeNonEmpty ( ) ;
2021-06-06 17:09:53 -07:00
`
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2021-07-19 11:59:53 -05:00
2021-12-03 14:55:16 -08:00
test ( 'should propose only the relevant matchers when custom expect matcher classes were passed' , async ( { runTSC } ) = > {
const result = await runTSC ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2021-12-03 14:55:16 -08:00
test ( 'custom matchers' , async ( { page } ) = > {
2023-08-23 13:14:39 -07:00
// Page-specific assertions apply to Page.
2021-12-03 14:55:16 -08:00
await test . expect ( page ) . toHaveURL ( 'https://example.com' ) ;
2022-04-11 10:42:19 -07:00
await test . expect ( page ) . not . toHaveURL ( 'https://example.com' ) ;
2023-08-23 13:14:39 -07:00
// Some generic assertions also apply to Page.
test . expect ( page ) . toBe ( true ) ;
test . expect ( page ) . toBeDefined ( ) ;
test . expect ( page ) . toBeFalsy ( ) ;
test . expect ( page ) . toBeNull ( ) ;
test . expect ( page ) . toBeTruthy ( ) ;
test . expect ( page ) . toBeUndefined ( ) ;
// Locator-specific and most generic assertions do not apply to Page.
2021-12-03 14:55:16 -08:00
// @ts-expect-error
await test . expect ( page ) . toBeEnabled ( ) ;
2022-04-11 10:42:19 -07:00
// @ts-expect-error
await test . expect ( page ) . not . toBeEnabled ( ) ;
2023-08-23 13:14:39 -07:00
// @ts-expect-error
test . expect ( page ) . toEqual ( ) ;
2021-12-03 14:55:16 -08:00
2023-08-23 13:14:39 -07:00
// Locator-specific assertions apply to Locator.
2021-12-03 14:55:16 -08:00
await test . expect ( page . locator ( 'foo' ) ) . toBeEnabled ( ) ;
2022-09-06 11:40:34 -07:00
await test . expect ( page . locator ( 'foo' ) ) . toBeEnabled ( { enabled : false } ) ;
await test . expect ( page . locator ( 'foo' ) ) . not . toBeEnabled ( { enabled : true } ) ;
2023-08-23 13:14:39 -07:00
await test . expect ( page . locator ( 'foo' ) ) . toBeChecked ( ) ;
await test . expect ( page . locator ( 'foo' ) ) . not . toBeChecked ( { checked : true } ) ;
await test . expect ( page . locator ( 'foo' ) ) . not . toBeEditable ( ) ;
await test . expect ( page . locator ( 'foo' ) ) . toBeEditable ( { editable : false } ) ;
await test . expect ( page . locator ( 'foo' ) ) . toBeVisible ( ) ;
await test . expect ( page . locator ( 'foo' ) ) . not . toBeVisible ( { visible : false } ) ;
// Some generic assertions also apply to Locator.
test . expect ( page . locator ( 'foo' ) ) . toBe ( true ) ;
// Page-specific and most generic assertions do not apply to Locator.
// @ts-expect-error
await test . expect ( page . locator ( 'foo' ) ) . toHaveURL ( 'https://example.com' ) ;
// @ts-expect-error
await test . expect ( page . locator ( 'foo' ) ) . toHaveLength ( 1 ) ;
// Wrong arguments for assertions do not compile.
2022-09-06 11:40:34 -07:00
// @ts-expect-error
await test . expect ( page . locator ( 'foo' ) ) . toBeEnabled ( { unknown : false } ) ;
// @ts-expect-error
await test . expect ( page . locator ( 'foo' ) ) . toBeEnabled ( { enabled : 'foo' } ) ;
2023-08-23 13:14:39 -07:00
// Generic assertions work.
test . expect ( [ 123 ] ) . toHaveLength ( 1 ) ;
test . expect ( '123' ) . toMatchSnapshot ( 'name' ) ;
test . expect ( await page . screenshot ( ) ) . toMatchSnapshot ( 'screenshot.png' ) ;
// All possible assertions apply to "any" type.
const x : any = 123 ;
test . expect ( x ) . toHaveLength ( 1 ) ;
await test . expect ( x ) . toHaveURL ( 'url' ) ;
await test . expect ( x ) . toBeEnabled ( ) ;
test . expect ( x ) . toMatchSnapshot ( 'snapshot name' ) ;
2021-12-03 14:55:16 -08:00
2023-08-23 13:14:39 -07:00
// APIResponse-specific assertions apply to APIResponse.
2021-12-03 14:55:16 -08:00
const res = await page . request . get ( 'http://i-do-definitely-not-exist.com' ) ;
await test . expect ( res ) . toBeOK ( ) ;
2023-08-23 13:14:39 -07:00
// Some generic assertions also apply to APIResponse.
test . expect ( res ) . toBe ( true ) ;
// Page-specific and most generic assertions do not apply to APIResponse.
2021-12-03 14:55:16 -08:00
// @ts-expect-error
await test . expect ( res ) . toHaveURL ( 'https://example.com' ) ;
2023-08-23 13:14:39 -07:00
// @ts-expect-error
test . expect ( res ) . toEqual ( 123 ) ;
2021-12-13 13:42:36 -05:00
2023-08-23 13:14:39 -07:00
// Explicitly casting to "any" supports all assertions.
2021-12-13 13:42:36 -05:00
await test . expect ( res as any ) . toHaveURL ( 'https://example.com' ) ;
2023-08-23 13:14:39 -07:00
// Playwright-specific assertions do not apply to generic values.
2021-12-13 13:42:36 -05:00
// @ts-expect-error
await test . expect ( 123 ) . toHaveURL ( 'https://example.com' ) ;
2021-12-03 14:55:16 -08:00
} ) ;
2021-07-19 11:59:53 -05:00
`
} ) ;
2021-12-03 14:55:16 -08:00
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2022-04-01 13:38:22 -07:00
2022-04-05 16:11:11 -07:00
test ( 'should return void/Promise when appropriate' , async ( { runTSC } ) = > {
const result = await runTSC ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-04-05 16:11:11 -07:00
type AssertType < T , S > = S extends T ? AssertNotAny < S > : false ;
type AssertNotAny < S > = { notRealProperty : number } extends S ? false : true ;
2023-02-14 19:20:56 -08:00
test ( 'example' , async ( { page } ) = > {
2022-04-05 16:11:11 -07:00
{
const value = expect ( 1 ) . toBe ( 2 ) ;
const assertion : AssertType < void , typeof value > = true ;
}
{
const value = expect ( 1 ) . not . toBe ( 2 ) ;
const assertion : AssertType < void , typeof value > = true ;
}
{
const value = expect ( page ) . toHaveURL ( '' ) ;
const assertion : AssertType < Promise < void > , typeof value > = true ;
}
{
const value = expect ( Promise . resolve ( 1 ) ) . resolves . toBe ( 1 ) ;
const assertion : AssertType < Promise < void > , typeof value > = true ;
}
{
const value = expect . soft ( 1 ) . toBe ( 2 ) ;
const assertion : AssertType < void , typeof value > = true ;
}
{
const value = expect . poll ( ( ) = > true ) . toBe ( 2 ) ;
const assertion : AssertType < Promise < void > , typeof value > = true ;
}
} ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2022-04-01 13:38:22 -07:00
test . describe ( 'helpful expect errors' , ( ) = > {
test ( 'top-level' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-04-01 13:38:22 -07:00
test ( 'explodes' , ( ) = > {
expect ( 1 ) . nope ( ) ;
} ) ;
`
} ) ;
expect ( result . output ) . toContain ( ` expect: Property 'nope' not found. ` ) ;
} ) ;
test ( 'soft' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-04-01 13:38:22 -07:00
test ( 'explodes' , ( ) = > {
expect . soft ( 1 ) . nope ( ) ;
} ) ;
`
} ) ;
expect ( result . output ) . toContain ( ` expect: Property 'nope' not found. ` ) ;
} ) ;
test ( 'poll' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-04-01 13:38:22 -07:00
test ( 'explodes' , ( ) = > {
expect . poll ( ( ) = > { } ) . nope ( ) ;
} ) ;
`
} ) ;
expect ( result . output ) . toContain ( ` expect: Property 'nope' not found. ` ) ;
} ) ;
test ( 'not' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-04-01 13:38:22 -07:00
test ( 'explodes' , ( ) = > {
expect ( 1 ) . not . nope ( ) ;
} ) ;
`
} ) ;
expect ( result . output ) . toContain ( ` expect: Property 'nope' not found. ` ) ;
} ) ;
test ( 'bare' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-04-01 13:38:22 -07:00
test ( 'explodes' , ( ) = > {
expect ( '' ) ;
} ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
expect ( result . passed ) . toBe ( 1 ) ;
} ) ;
} ) ;
2022-05-03 21:53:15 +01:00
test ( 'should reasonably work in global setup' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'playwright.config.ts' : `
export default { globalSetup : './global-setup' } ;
` ,
'global-setup.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-05-03 21:53:15 +01:00
export default async ( ) = > {
expect ( 1 ) . toBe ( 1 ) ;
await expect . poll ( async ( ) = > {
await new Promise ( f = > setTimeout ( f , 50 ) ) ;
return 42 ;
} ) . toBe ( 42 ) ;
expect ( 1 ) . toBe ( 2 ) ;
} ;
` ,
'a.spec.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-05-03 21:53:15 +01:00
test ( 'skipped' , ( ) = > { } ) ;
` ,
} ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
2023-02-14 19:20:56 -08:00
expect ( result . output ) . toContain ( '> 9 | expect(1).toBe(2);' ) ;
2022-05-03 21:53:15 +01:00
} ) ;
2022-08-10 15:10:25 -07:00
test ( 'should support toHaveURL with baseURL from webServer' , async ( { runInlineTest } , testInfo ) = > {
const port = testInfo . workerIndex + 10500 ;
const result = await runInlineTest ( {
'a.test.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-08-10 15:10:25 -07:00
test ( 'pass' , async ( { page } ) = > {
await page . goto ( '/foobar' ) ;
await expect ( page ) . toHaveURL ( '/foobar' ) ;
await expect ( page ) . toHaveURL ( 'http://localhost:${port}/foobar' ) ;
} ) ;
test ( 'fail' , async ( { page } ) = > {
await page . goto ( '/foobar' ) ;
await expect ( page ) . toHaveURL ( '/kek' , { timeout : 1000 } ) ;
} ) ;
` ,
'playwright.config.ts' : `
module . exports = {
webServer : {
command : 'node ${JSON.stringify(path.join(__dirname, ' assets ', ' simple - server . js '))} ${port}' ,
port : $ { port } ,
} ,
} ;
` ,
} , { workers : 1 } ) ;
2023-02-07 15:11:44 -08:00
const output = result . output ;
2022-08-10 15:10:25 -07:00
expect ( output ) . toContain ( 'expect(page).toHaveURL' ) ;
expect ( output ) . toContain ( ` Expected string: \ "http://localhost: ${ port } /kek \ " ` ) ;
expect ( result . passed ) . toBe ( 1 ) ;
expect ( result . failed ) . toBe ( 1 ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
} ) ;
test ( 'should respect expect.timeout' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'playwright.config.js' : ` module.exports = { expect: { timeout: 1000 } } ` ,
'a.test.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-08-10 15:10:25 -07:00
test ( 'timeout' , async ( { page } ) = > {
await page . goto ( 'data:text/html,<div>A</div>' ) ;
2022-11-29 12:54:53 -08:00
const error = await expect ( page ) . toHaveURL ( 'data:text/html,<div>B</div>' ) . catch ( e = > e ) ;
expect ( error . message ) . toContain ( 'expect.toHaveURL with timeout 1000ms' ) ;
expect ( error . message ) . toContain ( 'data:text/html,<div>' ) ;
2022-08-10 15:10:25 -07:00
} ) ;
` ,
} , { workers : 1 } ) ;
2022-11-29 12:54:53 -08:00
expect ( result . exitCode ) . toBe ( 0 ) ;
expect ( result . passed ) . toBe ( 1 ) ;
2022-08-10 15:10:25 -07:00
} ) ;
test ( 'should log scale the time' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.test.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-08-10 15:10:25 -07:00
test ( 'pass' , async ( { page } ) = > {
await page . setContent ( '<div id=div>Wrong</div>' ) ;
await expect ( page . locator ( 'div' ) ) . toHaveText ( 'Text' , { timeout : 2000 } ) ;
} ) ;
` ,
} , { workers : 1 } ) ;
2023-02-07 15:11:44 -08:00
const output = result . output ;
2022-08-10 15:10:25 -07:00
const tokens = output . split ( 'unexpected value' ) ;
// Log scale: 0, 100, 250, 500, 1000, 1000, should be less than 8.
expect ( tokens . length ) . toBeGreaterThan ( 1 ) ;
expect ( tokens . length ) . toBeLessThan ( 8 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
} ) ;
test ( 'should print expected/received before timeout' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.test.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-08-10 15:10:25 -07:00
test ( 'times out waiting for text' , async ( { page } ) = > {
await page . setContent ( '<div id=node>Text content</div>' ) ;
await expect ( page . locator ( '#node' ) ) . toHaveText ( 'Text 2' ) ;
} ) ;
` ,
} , { workers : 1 , timeout : 2000 } ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
expect ( result . passed ) . toBe ( 0 ) ;
expect ( result . failed ) . toBe ( 1 ) ;
expect ( result . output ) . toContain ( 'Test timeout of 2000ms exceeded.' ) ;
2023-02-07 15:11:44 -08:00
expect ( result . output ) . toContain ( 'Expected string: "Text 2"' ) ;
expect ( result . output ) . toContain ( 'Received string: "Text content"' ) ;
2022-08-10 15:10:25 -07:00
} ) ;
test ( 'should print pending operations for toHaveText' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.test.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-08-10 15:10:25 -07:00
test ( 'fail' , async ( { page } ) = > {
await page . setContent ( '<div id=node>Text content</div>' ) ;
await expect ( page . locator ( 'no-such-thing' ) ) . toHaveText ( 'Text' ) ;
} ) ;
` ,
} , { workers : 1 , timeout : 2000 } ) ;
expect ( result . failed ) . toBe ( 1 ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
2023-02-07 15:11:44 -08:00
const output = result . output ;
2023-09-16 14:24:10 -07:00
expect ( output ) . toContain ( ` expect(locator).toHaveText(expected) ` ) ;
2022-08-10 15:10:25 -07:00
expect ( output ) . toContain ( 'Expected string: "Text"' ) ;
2024-04-10 10:01:19 +02:00
expect ( output ) . toContain ( 'Received: <element(s) not found>' ) ;
2022-11-04 15:19:16 -07:00
expect ( output ) . toContain ( 'waiting for locator(\'no-such-thing\')' ) ;
2022-08-10 15:10:25 -07:00
} ) ;
2023-07-25 15:46:39 -07:00
test ( 'should print expected/received on Ctrl+C' , async ( { interactWithTestRunner } ) = > {
2022-08-10 15:10:25 -07:00
test . skip ( process . platform === 'win32' , 'No sending SIGINT on Windows' ) ;
2023-07-25 15:46:39 -07:00
const testProcess = await interactWithTestRunner ( {
2022-08-10 15:10:25 -07:00
'a.test.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-08-10 15:10:25 -07:00
test ( 'times out waiting for text' , async ( { page } ) = > {
await page . setContent ( '<div id=node>Text content</div>' ) ;
const promise = expect ( page . locator ( '#node' ) ) . toHaveText ( 'Text 2' ) ;
2022-12-21 14:27:35 -08:00
await new Promise ( f = > setTimeout ( f , 1000 ) ) ;
2022-08-10 15:10:25 -07:00
console . log ( '\\n%%SEND-SIGINT%%' ) ;
await promise ;
} ) ;
` ,
2023-07-25 15:46:39 -07:00
} , { workers : 1 } ) ;
await testProcess . waitForOutput ( '%%SEND-SIGINT%%' ) ;
2023-09-22 10:57:35 -07:00
process . kill ( - testProcess . process . pid ! , 'SIGINT' ) ;
2023-07-25 15:46:39 -07:00
const { exitCode } = await testProcess . exited ;
expect ( exitCode ) . toBe ( 130 ) ;
const result = parseTestRunnerOutput ( testProcess . output ) ;
2022-08-10 15:10:25 -07:00
expect ( result . passed ) . toBe ( 0 ) ;
expect ( result . interrupted ) . toBe ( 1 ) ;
2023-02-07 15:11:44 -08:00
expect ( result . output ) . toContain ( 'Expected string: "Text 2"' ) ;
expect ( result . output ) . toContain ( 'Received string: "Text content"' ) ;
2022-08-10 15:10:25 -07:00
} ) ;
2022-11-16 17:00:42 -08:00
2023-04-03 15:06:13 -07:00
test ( 'should not print timed out error message when test times out' , async ( { runInlineTest } ) = > {
2022-11-16 17:00:42 -08:00
const result = await runInlineTest ( {
'a.test.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2022-11-16 17:00:42 -08:00
test ( 'fail' , async ( { page } ) = > {
await page . setContent ( '<div id=node>Text content</div>' ) ;
2023-04-03 15:06:13 -07:00
await expect ( page . locator ( 'no-such-thing' ) ) . toHaveText ( 'hey' , { timeout : 5000 } ) ;
2022-11-16 17:00:42 -08:00
} ) ;
` ,
2023-04-03 15:06:13 -07:00
} , { workers : 1 , timeout : 3000 } ) ;
2022-11-16 17:00:42 -08:00
expect ( result . failed ) . toBe ( 1 ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
2023-02-07 15:11:44 -08:00
const output = result . output ;
2023-04-03 15:06:13 -07:00
expect ( output ) . toContain ( 'Test timeout of 3000ms exceeded' ) ;
expect ( output ) . not . toContain ( 'Timed out 5000ms waiting for expect' ) ;
2023-09-16 14:24:10 -07:00
expect ( output ) . toContain ( ` Error: expect(locator).toHaveText(expected) ` ) ;
2022-11-16 17:00:42 -08:00
} ) ;
2023-01-30 17:24:12 -08:00
test ( 'should not leak long expect message strings' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.test.ts' : `
2023-02-14 19:20:56 -08:00
import { test , expect } from '@playwright/test' ;
2023-01-30 17:24:12 -08:00
let logs : string = 'Ab' ;
const consoleLogWatcher = ( msg : ConsoleMessage ) = > {
if ( logs . length < ( 1 << 28 ) )
logs += logs ;
expect ( msg . text ( ) , logs ) . toMatch ( /^\\d+$/ ) ;
}
test ( 'main' , async ( { page } ) = > {
page . on ( 'console' , consoleLogWatcher ) ;
await page . evaluate ( ( ) = > {
for ( let i = 0 ; i < 20 ; i ++ )
console . log ( i ) ;
} ) ;
} ) ;
` ,
} , { workers : 1 } ) ;
// expect(result.output).toBe('');
expect ( result . failed ) . toBe ( 0 ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2023-09-22 12:12:17 -07:00
test ( 'should chain expect matchers and expose matcher utils (TSC)' , async ( { runTSC } ) = > {
const result = await runTSC ( {
'a.spec.ts' : `
import { test , expect as baseExpect } from '@playwright/test' ;
2023-11-13 18:37:50 -08:00
import type { Page , Locator , ExpectMatcherState , Expect } from '@playwright/test' ;
2023-09-22 12:12:17 -07:00
function callLogText ( log : string [ ] | undefined ) : string {
if ( ! log )
return '' ;
return log . join ( '\\n' ) ;
}
2023-11-13 18:37:50 -08:00
const dummy : Expect = baseExpect ;
const dummy2 : Expect < { } > = baseExpect ;
2023-09-22 12:12:17 -07:00
const expect = baseExpect . extend ( {
async toHaveAmount ( locator : Locator , expected : string , options ? : { timeout? : number } ) {
2023-11-13 18:37:50 -08:00
// Make sure "this" is inferred as ExpectMatcherState.
const self : ExpectMatcherState = this ;
const self2 : ReturnType < Expect [ 'getState' ] > = self ;
2023-09-22 12:12:17 -07:00
const baseAmount = locator . locator ( '.base-amount' ) ;
let pass : boolean ;
let matcherResult : any ;
try {
await baseExpect ( baseAmount ) . toHaveAttribute ( 'data-amount' , expected , options ) ;
pass = true ;
} catch ( e : any ) {
matcherResult = e . matcherResult ;
pass = false ;
}
const expectOptions = {
isNot : this.isNot ,
} ;
const log = callLogText ( matcherResult ? . log ) ;
const message = pass
? ( ) = > this . utils . matcherHint ( 'toBe' , locator , expected , expectOptions ) +
'\\n\\n' +
\ ` Expected: \ ${ this . isNot ? 'not' : '' } \ ${ this . utils . printExpected ( expected ) } \\ n \` +
( matcherResult ? \ ` Received: \ ${ this . utils . printReceived ( matcherResult . actual ) } \` : '') +
log
: ( ) = > this . utils . matcherHint ( 'toBe' , locator , expected , expectOptions ) +
'\\n\\n' +
\ ` Expected: \ ${ this . utils . printExpected ( expected ) } \ n \` +
( matcherResult ? \ ` Received: \ ${ this . utils . printReceived ( matcherResult . actual ) } \` : '') +
log ;
return {
name : 'toHaveAmount' ,
expected ,
message ,
pass ,
actual : matcherResult?.actual ,
log : matcherResult?.log ,
} ;
} ,
async toBeANicePage ( page : Page ) {
return {
name : 'toBeANicePage' ,
expected : 1 ,
message : ( ) = > '' ,
pass : true ,
} ;
}
} ) ;
test ( 'custom matchers' , async ( { page } ) = > {
await page . setContent ( \ `
< div >
< div class = 'base-amount' data-amount = '2' > < / div >
< / div >
\ ` );
await expect ( page . locator ( 'div' ) ) . toHaveAmount ( '3' , { timeout : 1000 } ) ;
await expect ( page ) . toBeANicePage ( ) ;
// @ts-expect-error
await expect ( page ) . toHaveAmount ( '3' , { timeout : 1000 } ) ;
// @ts-expect-error
await expect ( page . locator ( 'div' ) ) . toBeANicePage ( ) ;
} ) ; `
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
test ( 'should chain expect matchers and expose matcher utils' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'a.spec.ts' : `
import { test , expect as baseExpect } from '@playwright/test' ;
import type { Page , Locator } from '@playwright/test' ;
function callLogText ( log : string [ ] | undefined ) : string {
if ( ! log )
return '' ;
return log . join ( '\\n' ) ;
}
const expect = baseExpect . extend ( {
async toHaveAmount ( locator : Locator , expected : string , options ? : { timeout? : number } ) {
const baseAmount = locator . locator ( '.base-amount' ) ;
let pass : boolean ;
let matcherResult : any ;
try {
await baseExpect ( baseAmount ) . toHaveAttribute ( 'data-amount' , expected , options ) ;
pass = true ;
} catch ( e : any ) {
matcherResult = e . matcherResult ;
pass = false ;
}
const expectOptions = {
isNot : this.isNot ,
} ;
const log = callLogText ( matcherResult ? . log ) ;
const message = pass
2023-10-05 14:59:59 -07:00
? ( ) = > this . utils . matcherHint ( 'toHaveAmount' , undefined , undefined , expectOptions ) +
2023-09-22 12:12:17 -07:00
'\\n\\n' +
\ ` Expected: \ ${ this . isNot ? 'not' : '' } \ ${ this . utils . printExpected ( expected ) } \\ n \` +
( matcherResult ? \ ` Received: \ ${ this . utils . printReceived ( matcherResult . actual ) } \` : '') +
2023-10-05 14:59:59 -07:00
'\\n\\n' + log
: ( ) = > this . utils . matcherHint ( 'toHaveAmount' , undefined , undefined , expectOptions ) +
2023-09-22 12:12:17 -07:00
'\\n\\n' +
\ ` Expected: \ ${ this . utils . printExpected ( expected ) } \ n \` +
( matcherResult ? \ ` Received: \ ${ this . utils . printReceived ( matcherResult . actual ) } \` : '') +
2023-10-05 14:59:59 -07:00
'\\n\\n' + log ;
2023-09-22 12:12:17 -07:00
return {
name : 'toHaveAmount' ,
expected ,
message ,
pass ,
actual : matcherResult?.actual ,
log : matcherResult?.log ,
} ;
} ,
} ) ;
test ( 'custom matchers' , async ( { page } ) = > {
await page . setContent ( \ `
< div >
< div class = 'base-amount' data-amount = '2' > < / div >
< / div >
\ ` );
await expect ( page . locator ( 'div' ) ) . toHaveAmount ( '3' , { timeout : 1000 } ) ;
} ) ; `
} , { workers : 1 } ) ;
const output = stripAnsi ( result . output ) ;
expect ( output ) . toContain ( ` await expect(page.locator('div')).toHaveAmount('3', { timeout: 1000 }); ` ) ;
expect ( output ) . toContain ( 'a.spec.ts:60' ) ;
expect ( result . failed ) . toBe ( 1 ) ;
expect ( result . exitCode ) . toBe ( 1 ) ;
} ) ;
2023-10-04 09:27:28 -07:00
2024-05-08 20:40:03 +02:00
test ( 'should support toHaveAttribute without optional value' , async ( { runTSC } ) = > {
2023-10-04 09:27:28 -07:00
const result = await runTSC ( {
'a.spec.ts' : `
import { test , expect as baseExpect } from '@playwright/test' ;
test ( 'custom matchers' , async ( { page } ) = > {
const locator = page . locator ( '#node' ) ;
await test . expect ( locator ) . toHaveAttribute ( 'name' , 'value' ) ;
await test . expect ( locator ) . toHaveAttribute ( 'name' , 'value' , { timeout : 10 } ) ;
await test . expect ( locator ) . toHaveAttribute ( 'disabled' ) ;
await test . expect ( locator ) . toHaveAttribute ( 'disabled' , { timeout : 10 } ) ;
// @ts-expect-error
await test . expect ( locator ) . toHaveAttribute ( 'disabled' , { foo : 1 } ) ;
// @ts-expect-error
await test . expect ( locator ) . toHaveAttribute ( 'name' , 'value' , 'opt' ) ;
} ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
2023-10-04 15:01:25 -07:00
} ) ;
2023-10-11 13:56:27 -07:00
test ( 'should support mergeExpects (TSC)' , async ( { runTSC } ) = > {
2023-10-04 15:01:25 -07:00
const result = await runTSC ( {
'a.spec.ts' : `
2023-10-11 13:56:27 -07:00
import { test , mergeExpects , expect as baseExpect } from '@playwright/test' ;
2023-10-04 15:01:25 -07:00
import type { Page } from '@playwright/test' ;
const expect1 = baseExpect . extend ( {
async toBeAGoodPage ( page : Page , x : number ) {
return { pass : true , message : ( ) = > '' } ;
}
} ) ;
const expect2 = baseExpect . extend ( {
async toBeABadPage ( page : Page , y : string ) {
return { pass : true , message : ( ) = > '' } ;
}
} ) ;
2023-10-11 13:56:27 -07:00
const expect = mergeExpects ( expect1 , expect2 ) ;
2023-10-04 15:01:25 -07:00
test ( 'custom matchers' , async ( { page } ) = > {
await expect ( page ) . toBeAGoodPage ( 123 ) ;
await expect ( page ) . toBeABadPage ( '123' ) ;
// @ts-expect-error
2024-05-08 20:40:03 +02:00
await expect ( page ) . toBeAMediocrePage ( ) ;
2023-10-04 15:01:25 -07:00
// @ts-expect-error
await expect ( page ) . toBeABadPage ( 123 ) ;
// @ts-expect-error
await expect ( page ) . toBeAGoodPage ( '123' ) ;
} ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2023-10-11 13:56:27 -07:00
test ( 'should support mergeExpects' , async ( { runInlineTest } ) = > {
2023-10-04 15:01:25 -07:00
const result = await runInlineTest ( {
'a.spec.ts' : `
2023-10-11 13:56:27 -07:00
import { test , mergeExpects , expect as baseExpect } from '@playwright/test' ;
2023-10-04 15:01:25 -07:00
import type { Page } from '@playwright/test' ;
const expect1 = baseExpect . extend ( {
async toBeAGoodPage ( page : Page , x : number ) {
return { pass : true , message : ( ) = > '' } ;
}
} ) ;
const expect2 = baseExpect . extend ( {
async toBeABadPage ( page : Page , y : string ) {
return { pass : true , message : ( ) = > '' } ;
}
} ) ;
2023-10-11 13:56:27 -07:00
const expect = mergeExpects ( expect1 , expect2 ) ;
2023-10-04 15:01:25 -07:00
test ( 'custom matchers' , async ( { page } ) = > {
await expect ( page ) . toBeAGoodPage ( 123 ) ;
await expect ( page ) . toBeABadPage ( '123' ) ;
} ) ;
`
} , { workers : 1 } ) ;
expect ( result . passed ) . toBe ( 1 ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
} ) ;
2023-12-19 18:55:05 +00:00
test ( 'should respect timeout from configured expect when used outside of the test runner' , async ( { runInlineTest , writeFiles , runTSC } ) = > {
const files = {
'script.mjs' : `
import { test , expect as baseExpect , chromium } from '@playwright/test' ;
const configuredExpect = baseExpect . configure ( {
timeout : 10 ,
} ) ;
let browser ;
try {
browser = await chromium . launch ( ) ;
const context = await browser . newContext ( ) ;
const page = await context . newPage ( ) ;
await configuredExpect ( page . getByTestId ( "does-not-exist" ) ) . toBeAttached ( ) ;
} catch ( e ) {
console . error ( e ) ;
process . exit ( 1 ) ;
}
finally {
await browser ? . close ( ) ;
}
2024-02-06 12:12:45 -08:00
2023-12-19 18:55:05 +00:00
`
} ;
const baseDir = await writeFiles ( files ) ;
const { code , stdout , stderr } = await spawnAsync ( 'node' , [ 'script.mjs' ] , { stdio : 'pipe' , cwd : baseDir } ) ;
expect ( code ) . toBe ( 1 ) ;
expect ( stdout ) . toBe ( '' ) ;
expect ( stripAnsi ( stderr ) ) . toContain ( 'Timed out 10ms waiting for expect(locator).toBeAttached()' ) ;
} ) ;
2024-05-24 08:56:43 -07:00
test ( 'should expose timeout to custom matchers' , async ( { runInlineTest , runTSC } ) = > {
const files = {
'playwright.config.ts' : `
export default {
expect : { timeout : 1100 }
} ;
` ,
'a.test.ts' : `
import type { ExpectMatcherState , MatcherReturnType } from '@playwright/test' ;
import { test , expect as base } from '@playwright/test' ;
const expect = base . extend ( {
assertTimeout ( page : any , value : number ) {
const pass = this . timeout === value ;
return {
message : ( ) = > 'Unexpected timeout: ' + this . timeout ,
pass ,
name : 'assertTimeout' ,
} ;
}
} ) ;
test ( 'from config' , async ( { page } ) = > {
expect ( page ) . assertTimeout ( 1100 ) ;
} ) ;
test ( 'from expect.configure' , async ( { page } ) = > {
expect . configure ( { timeout : 2200 } ) ( page ) . assertTimeout ( 2200 ) ;
} ) ;
` ,
} ;
const { exitCode } = await runTSC ( files ) ;
expect ( exitCode ) . toBe ( 0 ) ;
const result = await runInlineTest ( files ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
expect ( result . failed ) . toBe ( 0 ) ;
expect ( result . passed ) . toBe ( 2 ) ;
} ) ;
2024-07-17 13:22:00 +02:00
test ( 'should throw error when using .equals()' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'helper.ts' : `
2024-09-12 19:56:38 +02:00
import { test as base , expect as baseExpect } from '@playwright/test' ;
export const expect = baseExpect . extend ( {
2024-07-17 13:22:00 +02:00
toBeWithinRange ( received , floor , ceiling ) {
this . equals ( 1 , 2 ) ;
} ,
} ) ;
export const test = base ;
` ,
'expect-test.spec.ts' : `
2024-09-12 19:56:38 +02:00
import { test , expect } from './helper' ;
2024-07-17 13:22:00 +02:00
test ( 'numeric ranges' , ( ) = > {
2024-09-12 19:56:38 +02:00
expect ( ( ) = > {
expect ( 100 ) . toBeWithinRange ( 90 , 110 ) ;
2024-07-17 13:22:00 +02:00
} ) . toThrowError ( 'It looks like you are using custom expect matchers that are not compatible with Playwright. See https://aka.ms/playwright/expect-compatibility' ) ;
} ) ;
`
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
expect ( result . passed ) . toBe ( 1 ) ;
} ) ;
2024-09-12 19:56:38 +02:00
test ( 'expect.extend should be immutable' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'expect-test.spec.ts' : `
import { test , expect } from '@playwright/test' ;
const expectFoo = expect . extend ( {
toFoo() {
console . log ( '%%foo' ) ;
return { pass : true } ;
}
} ) ;
const expectFoo2 = expect . extend ( {
toFoo() {
console . log ( '%%foo2' ) ;
return { pass : true } ;
}
} ) ;
const expectBar = expectFoo . extend ( {
toBar() {
console . log ( '%%bar' ) ;
return { pass : true } ;
}
} ) ;
test ( 'logs' , ( ) = > {
expect ( expectFoo ) . not . toBe ( expectFoo2 ) ;
expect ( expectFoo ) . not . toBe ( expectBar ) ;
expectFoo ( ) . toFoo ( ) ;
expectFoo2 ( ) . toFoo ( ) ;
expectBar ( ) . toFoo ( ) ;
expectBar ( ) . toBar ( ) ;
} ) ;
`
} ) ;
expect ( result . outputLines ) . toEqual ( [
'foo' ,
'foo2' ,
'foo' ,
'bar' ,
] ) ;
2024-09-24 14:00:13 -07:00
} ) ;
test ( 'expect.extend should fall back to legacy behavior' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'expect-test.spec.ts' : `
import { test , expect } from '@playwright/test' ;
expect . extend ( {
toFoo() {
console . log ( '%%foo' ) ;
return { pass : true } ;
}
} ) ;
expect . extend ( {
toFoo() {
console . log ( '%%foo2' ) ;
return { pass : true } ;
}
} ) ;
expect . extend ( {
toBar() {
console . log ( '%%bar' ) ;
return { pass : true } ;
}
} ) ;
test ( 'logs' , ( ) = > {
expect ( ) . toFoo ( ) ;
expect ( ) . toBar ( ) ;
} ) ;
`
} ) ;
expect ( result . outputLines ) . toEqual ( [
'foo2' ,
'bar' ,
] ) ;
} ) ;
2024-09-26 06:27:37 -07:00
test ( 'custom asymmetric matchers should work with expect.extend' , async ( { runInlineTest } ) = > {
const result = await runInlineTest ( {
'expect-test.spec.ts' : `
import { test , expect as baseExpect , mergeExpects } from '@playwright/test' ;
const expect1 = baseExpect . extend ( {
isFoo ( received : unknown , expected : string ) {
return { pass : received === 'foo' , message : ( ) = > '' } ;
} ,
} ) ;
const expect2 = baseExpect . extend ( {
isSomething ( received : unknown , expected : string ) {
return { pass : received === expected , message : ( ) = > '' } ;
} ,
} ) ;
const expect = mergeExpects ( expect1 , expect2 ) ;
test ( 'example' , ( ) = > {
expect ( 'foo' ) . toEqual ( expect . isFoo ( ) ) ;
expect ( 'bar' ) . toEqual ( expect . isSomething ( 'bar' ) ) ;
try {
expect ( 'foo2' ) . toEqual ( expect . isFoo ( ) ) ;
console . log ( 'should not run 1' ) ;
} catch ( e ) {
}
try {
expect ( 'bar2' ) . toEqual ( expect . isSomething ( 'bar' ) ) ;
console . log ( 'should not run 2' ) ;
} catch ( e ) {
}
} ) ;
` ,
} ) ;
expect ( result . exitCode ) . toBe ( 0 ) ;
expect ( result . passed ) . toBe ( 1 ) ;
expect ( result . output ) . not . toContain ( 'should not run' ) ;
} ) ;