| 
									
										
										
										
											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'; | 
					
						
							| 
									
										
										
										
											2021-06-06 17:09:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-07 08:02:01 -07:00
										 |  |  | test('should be able to call expect.extend in config', async ({ runInlineTest }) => { | 
					
						
							| 
									
										
										
										
											2021-06-06 17:09:53 -07:00
										 |  |  |   const result = await runInlineTest({ | 
					
						
							|  |  |  |     'helper.ts': `
 | 
					
						
							| 
									
										
										
										
											2023-02-14 19:20:56 -08:00
										 |  |  |       import { test as base, expect } from '@playwright/test'; | 
					
						
							|  |  |  |       expect.extend({ | 
					
						
							| 
									
										
										
										
											2021-06-06 17:09:53 -07:00
										 |  |  |         toBeWithinRange(received, floor, ceiling) { | 
					
						
							|  |  |  |           const pass = received >= floor && received <= ceiling; | 
					
						
							|  |  |  |           if (pass) { | 
					
						
							|  |  |  |             return { | 
					
						
							|  |  |  |               message: () => | 
					
						
							|  |  |  |                 'passed', | 
					
						
							|  |  |  |               pass: true, | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             return { | 
					
						
							|  |  |  |               message: () => 'failed', | 
					
						
							|  |  |  |               pass: false, | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2023-02-14 19:20:56 -08:00
										 |  |  |       export const test = base; | 
					
						
							| 
									
										
										
										
											2021-06-06 17:09:53 -07:00
										 |  |  |     `,
 | 
					
						
							|  |  |  |     'expect-test.spec.ts': `
 | 
					
						
							|  |  |  |       import { test } from './helper'; | 
					
						
							|  |  |  |       test('numeric ranges', () => { | 
					
						
							|  |  |  |         test.expect(100).toBeWithinRange(90, 110); | 
					
						
							|  |  |  |         test.expect(101).not.toBeWithinRange(0, 100); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   expect(result.exitCode).toBe(0); | 
					
						
							|  |  |  |   expect(result.passed).toBe(1); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-31 18:14:59 -07:00
										 |  |  | test('should include custom error message', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |   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', () => { | 
					
						
							|  |  |  |         test.expect(1+1, 'one plus one is two!').toEqual(3); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   expect(result.exitCode).toBe(1); | 
					
						
							|  |  |  |   expect(result.passed).toBe(0); | 
					
						
							| 
									
										
										
										
											2023-02-07 15:11:44 -08:00
										 |  |  |   expect(result.output).toContain([ | 
					
						
							| 
									
										
										
										
											2023-09-16 14:24:10 -07:00
										 |  |  |     `    Error: one plus one is two!\n`, | 
					
						
							|  |  |  |     `    expect(received).toEqual(expected) // deep equality\n`, | 
					
						
							| 
									
										
										
										
											2022-01-31 18:14:59 -07:00
										 |  |  |     `    Expected: 3`, | 
					
						
							|  |  |  |     `    Received: 2`, | 
					
						
							|  |  |  |   ].join('\n')); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | test('should include custom error message with web-first assertions', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |   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(42n).toBeGreaterThan(1n); | 
					
						
							|  |  |  |       expect(42).toBeGreaterThanOrEqual(1); | 
					
						
							|  |  |  |       expect(42n).toBeGreaterThanOrEqual(1n); | 
					
						
							|  |  |  |       expect({}).toBeInstanceOf(Object); | 
					
						
							|  |  |  |       expect(42).toBeLessThan(1); | 
					
						
							|  |  |  |       expect(42n).toBeLessThan(1n); | 
					
						
							|  |  |  |       expect(42).toBeLessThanOrEqual(1); | 
					
						
							|  |  |  |       expect(42n).toBeLessThanOrEqual(1n); | 
					
						
							|  |  |  |       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); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							| 
									
										
										
										
											2022-08-10 15:10:25 -07:00
										 |  |  |   expect(output).toContain('Pending operations:'); | 
					
						
							| 
									
										
										
										
											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"'); | 
					
						
							|  |  |  |   expect(output).toContain('Received string: ""'); | 
					
						
							| 
									
										
										
										
											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'; | 
					
						
							|  |  |  |     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 | 
					
						
							|  |  |  |           ? () => 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 | 
					
						
							|  |  |  |           ? () => 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, | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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); | 
					
						
							|  |  |  | }); |