2020-12-28 14:50:12 -08:00
/ * *
* Copyright ( c ) Microsoft Corporation .
*
* 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 .
* /
2021-05-08 17:45:04 -07:00
import { test , expect } from './inspectorTest' ;
2024-10-11 16:33:17 +02:00
import type { ConsoleMessage } from 'playwright' ;
2020-12-28 14:50:12 -08:00
2021-04-02 11:19:26 -07:00
test . describe ( 'cli codegen' , ( ) = > {
2021-04-30 13:26:13 -07:00
test . skip ( ( { mode } ) = > mode !== 'default' ) ;
2024-09-24 12:35:05 +02:00
test . skip ( ( { trace , codegenMode } ) = > trace === 'on' && codegenMode === 'trace-events' ) ;
2021-04-02 11:19:26 -07:00
2024-09-19 10:31:44 -07:00
test ( 'should click' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2020-12-28 14:50:12 -08:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <button onclick="console.log('click')">Submit</button> ` ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . hoverOverElement ( 'button' ) ;
expect ( locator ) . toBe ( ` getByRole('button', { name: 'Submit' }) ` ) ;
2020-12-28 17:39:30 -08:00
2021-02-16 18:13:26 -08:00
const [ message , sources ] = await Promise . all ( [
2021-05-12 22:19:27 +00:00
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'click' ) ,
2022-11-14 15:16:25 -08:00
recorder . trustedClick ( ) ,
2020-12-28 17:39:30 -08:00
] ) ;
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
await page . getByRole ( 'button' , { name : 'Submit' } ) . click ( ) ; ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
page . get_by_role ( "button" , name = "Submit" ) . click ( ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
await page . get_by_role ( "button" , name = "Submit" ) . click ( ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-10-10 11:25:56 -08:00
page . getByRole ( AriaRole . BUTTON , new Page . GetByRoleOptions ( ) . setName ( "Submit" ) ) . click ( ) ` );
2021-03-03 14:32:09 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . GetByRole ( AriaRole . Button , new ( ) { Name = "Submit" } ) . ClickAsync ( ) ; ` );
2021-02-16 18:13:26 -08:00
2020-12-28 17:39:30 -08:00
expect ( message . text ( ) ) . toBe ( 'click' ) ;
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should double click' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2024-09-12 11:40:44 -07:00
await recorder . setContentAndWait ( ` <button onclick="console.log('click ' + event.detail)" ondblclick="console.log('dblclick ' + event.detail)">Submit</button> ` ) ;
const locator = await recorder . hoverOverElement ( 'button' ) ;
expect ( locator ) . toBe ( ` getByRole('button', { name: 'Submit' }) ` ) ;
const messages : string [ ] = [ ] ;
page . on ( 'console' , message = > {
if ( message . text ( ) . includes ( 'click' ) )
messages . push ( message . text ( ) ) ;
} ) ;
const [ , sources ] = await Promise . all ( [
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' && msg . text ( ) === 'dblclick 2' ) ,
recorder . waitForOutput ( 'JavaScript' , 'dblclick' ) ,
recorder . trustedDblclick ( ) ,
] ) ;
expect . soft ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
await page . getByRole ( 'button' , { name : 'Submit' } ) . dblclick ( ) ; ` );
expect . soft ( sources . get ( 'Python' ) ! . text ) . toContain ( `
page . get_by_role ( "button" , name = "Submit" ) . dblclick ( ) ` );
expect . soft ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
await page . get_by_role ( "button" , name = "Submit" ) . dblclick ( ) ` );
expect . soft ( sources . get ( 'Java' ) ! . text ) . toContain ( `
page . getByRole ( AriaRole . BUTTON , new Page . GetByRoleOptions ( ) . setName ( "Submit" ) ) . dblclick ( ) ` );
expect . soft ( sources . get ( 'C#' ) ! . text ) . toContain ( `
await page . GetByRole ( AriaRole . Button , new ( ) { Name = "Submit" } ) . DblClickAsync ( ) ; ` );
expect ( messages ) . toEqual ( [
'click 1' ,
'click 2' ,
'dblclick 2' ,
] ) ;
} ) ;
2022-11-14 15:16:25 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should ignore programmatic events' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2022-11-14 15:16:25 -08:00
await recorder . setContentAndWait ( ` <button onclick="console.log('click')">Submit</button> ` ) ;
const locator = await recorder . hoverOverElement ( 'button' ) ;
expect ( locator ) . toBe ( ` getByRole('button', { name: 'Submit' }) ` ) ;
await page . dispatchEvent ( 'button' , 'click' , { detail : 1 } ) ;
await Promise . all ( [
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
recorder . waitForOutput ( 'JavaScript' , 'click' ) ,
recorder . trustedClick ( )
] ) ;
2023-10-23 09:31:30 -07:00
const clicks = recorder . sources ( ) . get ( 'Playwright Test' ) ! . actions ! . filter ( l = > l . includes ( 'Submit' ) ) ;
2022-11-14 15:16:25 -08:00
expect ( clicks . length ) . toBe ( 1 ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should click after same-document navigation' , async ( { openRecorder , server } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2021-05-08 17:45:04 -07:00
server . setRoute ( '/foo.html' , ( req , res ) = > {
2021-01-13 14:25:42 -08:00
res . setHeader ( 'Content-Type' , 'text/html; charset=utf-8' ) ;
res . end ( '' ) ;
} ) ;
2021-05-08 17:45:04 -07:00
await recorder . setContentAndWait ( ` <button onclick="console.log('click')">Submit</button> ` , server . PREFIX + '/foo.html' ) ;
2021-01-13 14:25:42 -08:00
await Promise . all ( [
page . waitForNavigation ( ) ,
page . evaluate ( ( ) = > history . pushState ( { } , '' , '/url.html' ) ) ,
] ) ;
// This is the only way to give recorder a chance to install
// the second unnecessary copy of the recorder script.
await page . waitForTimeout ( 1000 ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . hoverOverElement ( 'button' ) ;
expect ( locator ) . toBe ( ` getByRole('button', { name: 'Submit' }) ` ) ;
2021-01-13 14:25:42 -08:00
2021-02-16 18:13:26 -08:00
const [ message , sources ] = await Promise . all ( [
2021-05-12 22:19:27 +00:00
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'click' ) ,
2022-11-14 15:16:25 -08:00
recorder . trustedClick ( ) ,
2021-01-13 14:25:42 -08:00
] ) ;
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
await page . getByRole ( 'button' , { name : 'Submit' } ) . click ( ) ; ` );
2021-01-13 14:25:42 -08:00
expect ( message . text ( ) ) . toBe ( 'click' ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should make a positioned click on a canvas' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-10-14 17:37:29 +02:00
await recorder . setContentAndWait ( `
< canvas width = "500" height = "500" style = "margin: 42px" / >
< script >
document . querySelector ( "canvas" ) . addEventListener ( "click" , event = > {
const rect = event . target . getBoundingClientRect ( ) ;
console . log ( "click" , event . clientX - rect . left , event . clientY - rect . top ) ;
} )
< / script >
` );
2022-11-14 15:16:25 -08:00
const locator = await recorder . hoverOverElement ( 'canvas' , {
2021-10-14 17:37:29 +02:00
position : { x : 250 , y : 250 } ,
2022-11-14 15:16:25 -08:00
} ) ;
2022-10-24 18:01:48 -04:00
expect ( locator ) . toBe ( ` locator('canvas') ` ) ;
2021-10-14 17:37:29 +02:00
const [ message , sources ] = await Promise . all ( [
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
recorder . waitForOutput ( 'JavaScript' , 'click' ) ,
2022-11-14 15:16:25 -08:00
recorder . trustedClick ( ) ,
2021-10-14 17:37:29 +02:00
] ) ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-02-04 19:27:45 -08:00
await page . locator ( 'canvas' ) . click ( {
2021-10-14 17:37:29 +02:00
position : {
x : 250 ,
y : 250
}
} ) ; ` );
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-02-04 19:27:45 -08:00
page . locator ( "canvas" ) . click ( position = { "x" : 250 , "y" : 250 } ) ` );
2021-10-14 17:37:29 +02:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-02-04 19:27:45 -08:00
await page . locator ( "canvas" ) . click ( position = { "x" : 250 , "y" : 250 } ) ` );
2021-10-14 17:37:29 +02:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-02-04 19:27:45 -08:00
page . locator ( "canvas" ) . click ( new Locator . ClickOptions ( )
2021-10-14 17:37:29 +02:00
. setPosition ( 250 , 250 ) ) ; ` );
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . Locator ( "canvas" ) . ClickAsync ( new LocatorClickOptions
{
Position = new Position
{
X = 250 ,
Y = 250 ,
} ,
} ) ; ` );
2021-10-14 17:37:29 +02:00
expect ( message . text ( ) ) . toBe ( 'click 250 250' ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should work with TrustedTypes' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2021-02-22 11:38:49 -08:00
await recorder . setContentAndWait ( `
< head >
< meta http-equiv = "Content-Security-Policy" content = "trusted-types unsafe escape; require-trusted-types-for 'script'" >
< / head >
< body >
< button onclick = "console.log('click')" > Submit < / button >
< / body > ` );
2022-10-24 18:01:48 -04:00
const locator = await recorder . hoverOverElement ( 'button' ) ;
expect ( locator ) . toBe ( ` getByRole('button', { name: 'Submit' }) ` ) ;
2021-02-22 11:38:49 -08:00
const [ message , sources ] = await Promise . all ( [
2021-05-12 22:19:27 +00:00
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'click' ) ,
2022-11-14 15:16:25 -08:00
recorder . trustedClick ( ) ,
2021-02-22 11:38:49 -08:00
] ) ;
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
await page . getByRole ( 'button' , { name : 'Submit' } ) . click ( ) ; ` );
2021-02-22 11:38:49 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
page . get_by_role ( "button" , name = "Submit" ) . click ( ) ` );
2021-02-22 11:38:49 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
await page . get_by_role ( "button" , name = "Submit" ) . click ( ) ` );
2021-02-22 11:38:49 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-10-10 11:25:56 -08:00
page . getByRole ( AriaRole . BUTTON , new Page . GetByRoleOptions ( ) . setName ( "Submit" ) ) . click ( ) ` );
2021-03-03 14:32:09 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . GetByRole ( AriaRole . Button , new ( ) { Name = "Submit" } ) . ClickAsync ( ) ; ` );
2021-02-22 11:38:49 -08:00
expect ( message . text ( ) ) . toBe ( 'click' ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should not target selector preview by text regexp' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <span>dummy</span> ` ) ;
2020-12-28 14:50:12 -08:00
2020-12-28 17:39:30 -08:00
// Force highlight.
await recorder . hoverOverElement ( 'span' ) ;
// Append text after highlight.
await page . evaluate ( ( ) = > {
const div = document . createElement ( 'div' ) ;
div . setAttribute ( 'onclick' , "console.log('click')" ) ;
div . textContent = ' Some long text here ' ;
2022-11-14 15:16:25 -08:00
document . body . appendChild ( div ) ;
2020-12-28 17:39:30 -08:00
} ) ;
2020-12-28 14:50:12 -08:00
2022-10-24 18:01:48 -04:00
const locator = await recorder . hoverOverElement ( 'div' ) ;
expect ( locator ) . toBe ( ` getByText('Some long text here') ` ) ;
2020-12-28 14:50:12 -08:00
2022-10-24 18:01:48 -04:00
const divContents = await page . $eval ( 'div' , div = > div . outerHTML ) ;
2023-03-29 23:17:17 -07:00
expect ( divContents ) . toBe ( ` <div onclick="console.log('click')"> Some long text here </div> ` ) ;
2020-12-28 14:50:12 -08:00
2021-02-16 18:13:26 -08:00
const [ message , sources ] = await Promise . all ( [
2021-05-12 22:19:27 +00:00
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'click' ) ,
2022-11-14 15:16:25 -08:00
recorder . trustedMove ( 'div' ) . then ( ( ) = > recorder . trustedClick ( ) ) ,
2020-12-28 17:39:30 -08:00
] ) ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
await page . getByText ( 'Some long text here' ) . click ( ) ; ` );
2020-12-28 17:39:30 -08:00
expect ( message . text ( ) ) . toBe ( 'click' ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should fill' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <input id="input" name="name" oninput="console.log(input.value)"></input> ` ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . focusElement ( 'input' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` locator('#input') ` ) ;
2020-12-28 14:50:12 -08:00
2021-02-16 18:13:26 -08:00
const [ message , sources ] = await Promise . all ( [
2021-05-12 22:19:27 +00:00
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'fill' ) ,
2020-12-28 17:39:30 -08:00
page . fill ( 'input' , 'John' )
] ) ;
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( '#input' ) . fill ( 'John' ) ; ` );
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . locator ( "#input" ) . fill ( "John" ) ; ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . locator ( "#input" ) . fill ( \ "John\" ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( "#input" ) . fill ( \ "John\" ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . Locator ( "#input" ) . FillAsync ( \ "John\" ) ; ` );
2021-02-16 18:13:26 -08:00
2020-12-28 17:39:30 -08:00
expect ( message . text ( ) ) . toBe ( 'John' ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should fill japanese text' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2022-08-10 09:46:30 +02:00
// In Japanese, "てすと" or "テスト" means "test".
await recorder . setContentAndWait ( ` <input id="input" name="name" oninput="input.value === 'てすと' && console.log(input.value)"></input> ` ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . focusElement ( 'input' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` locator('#input') ` ) ;
2022-08-10 09:46:30 +02:00
const [ message , sources ] = await Promise . all ( [
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
recorder . waitForOutput ( 'JavaScript' , 'fill' ) ,
( async ( ) = > {
2022-10-24 18:01:48 -04:00
await recorder . page . dispatchEvent ( 'input' , 'keydown' , { key : 'Process' } ) ;
2022-08-11 08:48:55 +02:00
await recorder . page . keyboard . insertText ( 'てすと' ) ;
2022-10-24 18:01:48 -04:00
await recorder . page . dispatchEvent ( 'input' , 'keyup' , { key : 'Process' } ) ;
2022-08-10 09:46:30 +02:00
} ) ( )
] ) ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( '#input' ) . fill ( 'てすと' ) ; ` );
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . locator ( "#input" ) . fill ( "てすと" ) ; ` );
2022-08-10 09:46:30 +02:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . locator ( "#input" ) . fill ( \ "てすと\" ) ` );
2022-08-10 09:46:30 +02:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( "#input" ) . fill ( \ "てすと\" ) ` );
2022-08-10 09:46:30 +02:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . Locator ( "#input" ) . FillAsync ( \ "てすと\" ) ; ` );
2022-08-10 09:46:30 +02:00
expect ( message . text ( ) ) . toBe ( 'てすと' ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should fill textarea' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <textarea id="textarea" name="name" oninput="console.log(textarea.value)"></textarea> ` ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . focusElement ( 'textarea' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` locator('#textarea') ` ) ;
2020-12-28 14:50:12 -08:00
2021-02-16 18:13:26 -08:00
const [ message , sources ] = await Promise . all ( [
2021-05-12 22:19:27 +00:00
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'fill' ) ,
2020-12-28 17:39:30 -08:00
page . fill ( 'textarea' , 'John' )
] ) ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( '#textarea' ) . fill ( 'John' ) ; ` );
2020-12-28 17:39:30 -08:00
expect ( message . text ( ) ) . toBe ( 'John' ) ;
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should fill textarea with new lines at the end' , async ( { openRecorder } ) = > {
2023-06-19 17:07:37 +02:00
test . info ( ) . annotations . push ( { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/23774' } ) ;
2024-09-19 10:31:44 -07:00
const { page , recorder } = await openRecorder ( ) ;
2023-06-19 17:07:37 +02:00
await recorder . setContentAndWait ( ` <textarea id="textarea"></textarea> ` ) ;
const textarea = page . locator ( 'textarea' ) ;
await textarea . evaluate < void , HTMLTextAreaElement > ( e = > e . addEventListener ( 'input' , ( ) = > ( window as any ) . lastInputValue = e . value ) ) ;
const waitForOutputPromise = recorder . waitForOutput ( 'JavaScript' , 'Hello\\n' ) ;
await textarea . type ( 'Hello\n' ) ;
// Issue was that the input event was not fired for the last newline, so we check for that.
await page . waitForFunction ( ( ) = > ( window as any ) . lastInputValue === 'Hello\n' ) ;
const sources = await waitForOutputPromise ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( ` await page.locator('#textarea').fill('Hello \\ n'); ` ) ;
expect ( sources . get ( 'JavaScript' ) ! . text ) . not . toContain ( ` Enter ` ) ;
2023-06-19 17:07:37 +02:00
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should fill [contentEditable]' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2022-12-01 12:41:03 -08:00
await recorder . setContentAndWait ( ` <div id="content" contenteditable="" oninput="console.log(content.innerText)"/> ` ) ;
const locator = await recorder . focusElement ( 'div' ) ;
expect ( locator ) . toBe ( ` locator('#content') ` ) ;
const [ message , sources ] = await Promise . all ( [
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
recorder . waitForOutput ( 'JavaScript' , 'fill' ) ,
page . fill ( 'div' , 'John Doe' )
] ) ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-12-01 12:41:03 -08:00
await page . locator ( '#content' ) . fill ( 'John Doe' ) ; ` );
expect ( message . text ( ) ) . toBe ( 'John Doe' ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should press' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <input name="name" onkeypress="console.log('press')"></input> ` ) ;
2020-12-28 14:50:12 -08:00
2022-10-24 18:01:48 -04:00
const locator = await recorder . focusElement ( 'input' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` getByRole('textbox') ` ) ;
2020-12-28 14:50:12 -08:00
2020-12-28 17:39:30 -08:00
const messages : any [ ] = [ ] ;
2020-12-29 09:59:35 -08:00
page . on ( 'console' , message = > messages . push ( message ) ) ;
2021-02-16 18:13:26 -08:00
const [ , sources ] = await Promise . all ( [
2020-12-28 17:39:30 -08:00
recorder . waitForActionPerformed ( ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'press' ) ,
2020-12-28 17:39:30 -08:00
page . press ( 'input' , 'Shift+Enter' )
] ) ;
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . getByRole ( 'textbox' ) . press ( 'Shift+Enter' ) ; ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . getByRole ( AriaRole . TEXTBOX ) . press ( "Shift+Enter" ) ; ` );
2021-03-03 14:32:09 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . get_by_role ( "textbox" ) . press ( "Shift+Enter" ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . get_by_role ( "textbox" ) . press ( "Shift+Enter" ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . GetByRole ( AriaRole . Textbox ) . PressAsync ( "Shift+Enter" ) ; ` );
2021-02-16 18:13:26 -08:00
2020-12-28 17:39:30 -08:00
expect ( messages [ 0 ] . text ( ) ) . toBe ( 'press' ) ;
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-24 19:56:31 -07:00
test ( 'should update selected element after pressing Tab' , async ( { openRecorder , browserName , codegenMode } ) = > {
2024-09-19 10:31:44 -07:00
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( `
< input name = "one" > < / input >
< input name = "two" > < / input >
` );
2020-12-28 14:50:12 -08:00
2024-09-25 18:18:36 -07:00
const input1 = page . locator ( 'input[name="one"]' ) ;
const input2 = page . locator ( 'input[name="two"]' ) ;
{
await input1 . click ( ) ;
await recorder . waitForOutput ( 'JavaScript' , 'click' ) ;
await expect ( input1 ) . toBeFocused ( ) ;
}
{
await page . keyboard . type ( 'foobar123' ) ;
await recorder . waitForOutput ( 'JavaScript' , 'foobar123' ) ;
await expect ( input1 ) . toHaveValue ( 'foobar123' ) ;
}
{
await page . keyboard . press ( 'Tab' ) ;
await recorder . waitForOutput ( 'JavaScript' , 'Tab' ) ;
await expect ( input2 ) . toBeFocused ( ) ;
}
{
await page . keyboard . type ( 'barfoo321' ) ;
await recorder . waitForOutput ( 'JavaScript' , 'barfoo321' ) ;
await expect ( input2 ) . toHaveValue ( 'barfoo321' ) ;
}
2020-12-28 14:50:12 -08:00
2023-10-23 09:31:30 -07:00
const text = recorder . sources ( ) . get ( 'JavaScript' ) ! . text ;
2021-02-16 18:13:26 -08:00
expect ( text ) . toContain ( `
2022-02-04 19:27:45 -08:00
await page . locator ( 'input[name="one"]' ) . fill ( 'foobar123' ) ; ` );
2020-12-28 14:50:12 -08:00
2021-02-16 18:13:26 -08:00
expect ( text ) . toContain ( `
2022-02-04 19:27:45 -08:00
await page . locator ( 'input[name="one"]' ) . press ( 'Tab' ) ; ` );
2020-12-28 14:50:12 -08:00
2021-02-16 18:13:26 -08:00
expect ( text ) . toContain ( `
2022-02-04 19:27:45 -08:00
await page . locator ( 'input[name="two"]' ) . fill ( 'barfoo321' ) ; ` );
2020-12-28 17:39:30 -08:00
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should record ArrowDown' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <input name="name" onkeydown="console.log('press:' + event.key)"></input> ` ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . focusElement ( 'input' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` getByRole('textbox') ` ) ;
2020-12-28 17:39:30 -08:00
const messages : any [ ] = [ ] ;
page . on ( 'console' , message = > {
messages . push ( message ) ;
2020-12-29 09:59:35 -08:00
} ) ;
2021-02-16 18:13:26 -08:00
const [ , sources ] = await Promise . all ( [
2020-12-28 17:39:30 -08:00
recorder . waitForActionPerformed ( ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'press' ) ,
2020-12-28 17:39:30 -08:00
page . press ( 'input' , 'ArrowDown' )
] ) ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . getByRole ( 'textbox' ) . press ( 'ArrowDown' ) ; ` );
2020-12-28 17:39:30 -08:00
expect ( messages [ 0 ] . text ( ) ) . toBe ( 'press:ArrowDown' ) ;
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should emit single keyup on ArrowDown' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <input name="name" onkeydown="console.log('down:' + event.key)" onkeyup="console.log('up:' + event.key)"></input> ` ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . focusElement ( 'input' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` getByRole('textbox') ` ) ;
2020-12-28 17:39:30 -08:00
const messages : any [ ] = [ ] ;
page . on ( 'console' , message = > {
2021-05-12 22:19:27 +00:00
if ( message . type ( ) !== 'error' )
messages . push ( message ) ;
2020-12-29 09:59:35 -08:00
} ) ;
2021-02-16 18:13:26 -08:00
const [ , sources ] = await Promise . all ( [
2020-12-28 17:39:30 -08:00
recorder . waitForActionPerformed ( ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'press' ) ,
2020-12-28 17:39:30 -08:00
page . press ( 'input' , 'ArrowDown' )
] ) ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . getByRole ( 'textbox' ) . press ( 'ArrowDown' ) ; ` );
2020-12-28 17:39:30 -08:00
expect ( messages . length ) . toBe ( 2 ) ;
expect ( messages [ 0 ] . text ( ) ) . toBe ( 'down:ArrowDown' ) ;
expect ( messages [ 1 ] . text ( ) ) . toBe ( 'up:ArrowDown' ) ;
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should check' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <input id="checkbox" type="checkbox" name="accept" onchange="console.log(checkbox.checked)"></input> ` ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . focusElement ( 'input' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` locator('#checkbox') ` ) ;
2020-12-28 17:39:30 -08:00
2021-02-16 18:13:26 -08:00
const [ message , sources ] = await Promise . all ( [
2021-05-12 22:19:27 +00:00
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'check' ) ,
2020-12-28 17:39:30 -08:00
page . click ( 'input' )
] ) ;
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( '#checkbox' ) . check ( ) ; ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . locator ( "#checkbox" ) . check ( ) ; ` );
2021-03-03 14:32:09 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . locator ( "#checkbox" ) . check ( ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( "#checkbox" ) . check ( ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . Locator ( "#checkbox" ) . CheckAsync ( ) ; ` );
2021-02-16 18:13:26 -08:00
2020-12-28 17:39:30 -08:00
expect ( message . text ( ) ) . toBe ( 'true' ) ;
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should check a radio button' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2022-02-16 19:10:00 +01:00
await recorder . setContentAndWait ( ` <input id="checkbox" type="radio" name="accept" onchange="console.log(checkbox.checked)"></input> ` ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . focusElement ( 'input' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` locator('#checkbox') ` ) ;
2022-02-16 19:10:00 +01:00
const [ message , sources ] = await Promise . all ( [
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
recorder . waitForOutput ( 'JavaScript' , 'check' ) ,
page . click ( 'input' )
] ) ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( '#checkbox' ) . check ( ) ; ` );
2022-02-16 19:10:00 +01:00
expect ( message . text ( ) ) . toBe ( 'true' ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should check with keyboard' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <input id="checkbox" type="checkbox" name="accept" onchange="console.log(checkbox.checked)"></input> ` ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . focusElement ( 'input' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` locator('#checkbox') ` ) ;
2020-12-28 17:39:30 -08:00
2021-02-16 18:13:26 -08:00
const [ message , sources ] = await Promise . all ( [
2021-05-12 22:19:27 +00:00
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'check' ) ,
2020-12-28 17:39:30 -08:00
page . keyboard . press ( 'Space' )
] ) ;
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( '#checkbox' ) . check ( ) ; ` );
2020-12-28 17:39:30 -08:00
expect ( message . text ( ) ) . toBe ( 'true' ) ;
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should uncheck' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <input id="checkbox" type="checkbox" checked name="accept" onchange="console.log(checkbox.checked)"></input> ` ) ;
2022-10-24 18:01:48 -04:00
const locator = await recorder . focusElement ( 'input' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` locator('#checkbox') ` ) ;
2020-12-28 17:39:30 -08:00
2021-02-16 18:13:26 -08:00
const [ message , sources ] = await Promise . all ( [
2021-05-12 22:19:27 +00:00
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'uncheck' ) ,
2020-12-28 17:39:30 -08:00
page . click ( 'input' )
] ) ;
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( '#checkbox' ) . uncheck ( ) ; ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . locator ( "#checkbox" ) . uncheck ( ) ; ` );
2021-03-03 14:32:09 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . locator ( "#checkbox" ) . uncheck ( ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( "#checkbox" ) . uncheck ( ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . Locator ( "#checkbox" ) . UncheckAsync ( ) ; ` );
2021-02-16 18:13:26 -08:00
2020-12-28 17:39:30 -08:00
expect ( message . text ( ) ) . toBe ( 'false' ) ;
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should select' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( '<select id="age" onchange="console.log(age.selectedOptions[0].value)"><option value="1"><option value="2"></select>' ) ;
2020-12-28 14:50:12 -08:00
2022-10-24 18:01:48 -04:00
const locator = await recorder . hoverOverElement ( 'select' ) ;
2022-11-09 17:22:13 -08:00
expect ( locator ) . toBe ( ` locator('#age') ` ) ;
2024-06-17 17:28:07 +09:00
await page . locator ( 'select' ) . click ( ) ;
2020-12-28 14:50:12 -08:00
2021-02-16 18:13:26 -08:00
const [ message , sources ] = await Promise . all ( [
2021-05-12 22:19:27 +00:00
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'select' ) ,
2020-12-28 17:39:30 -08:00
page . selectOption ( 'select' , '2' )
] ) ;
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( '#age' ) . selectOption ( '2' ) ; ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . locator ( "#age" ) . selectOption ( "2" ) ; ` );
2021-03-03 14:32:09 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
page . locator ( "#age" ) . select_option ( "2" ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-11-09 17:22:13 -08:00
await page . locator ( "#age" ) . select_option ( "2" ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . Locator ( "#age" ) . SelectOptionAsync ( new [ ] { "2" } ) ; ` );
2021-02-16 18:13:26 -08:00
2020-12-28 17:39:30 -08:00
expect ( message . text ( ) ) . toBe ( '2' ) ;
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should select with size attribute' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2022-11-10 15:06:53 -08:00
await recorder . setContentAndWait ( `
< style >
body {
margin : 0 ;
}
< / style >
< select id = "age" size = "2" onchange = "console.log(age.selectedOptions[0].value)" >
< option value = "1" > v1 < / option >
< option value = "2" > v2 < / option >
< / select >
` );
const locator = await recorder . hoverOverElement ( 'select' ) ;
expect ( locator ) . toBe ( ` locator('#age') ` ) ;
const [ message , sources ] = await Promise . all ( [
page . waitForEvent ( 'console' , msg = > msg . type ( ) !== 'error' ) ,
recorder . waitForOutput ( 'JavaScript' , 'select' ) ,
page . mouse . click ( 10 , 25 )
] ) ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-11-10 15:06:53 -08:00
await page . locator ( '#age' ) . selectOption ( '2' ) ; ` );
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-11-10 15:06:53 -08:00
page . locator ( "#age" ) . selectOption ( "2" ) ; ` );
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-11-10 15:06:53 -08:00
page . locator ( \ "#age\").select_option(\"2\" ) ` );
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-11-10 15:06:53 -08:00
await page . locator ( \ "#age\").select_option(\"2\" ) ` );
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . Locator ( \ "#age\").SelectOptionAsync(new[] { \"2\" } ) ; ` );
2022-11-10 15:06:53 -08:00
expect ( message . text ( ) ) . toBe ( '2' ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should await popup' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( '<a target=_blank rel=noopener href="about:blank">link</a>' ) ;
2020-12-28 14:50:12 -08:00
2022-10-24 18:01:48 -04:00
const locator = await recorder . hoverOverElement ( 'a' ) ;
expect ( locator ) . toBe ( ` getByRole('link', { name: 'link' }) ` ) ;
2020-12-28 14:50:12 -08:00
2021-02-16 18:13:26 -08:00
const [ popup , sources ] = await Promise . all ( [
2020-12-28 17:39:30 -08:00
page . context ( ) . waitForEvent ( 'page' ) ,
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'waitForEvent' ) ,
2022-11-14 15:16:25 -08:00
recorder . trustedClick ( ) ,
2020-12-28 17:39:30 -08:00
] ) ;
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-12-02 17:33:01 -08:00
const page1Promise = page . waitForEvent ( 'popup' ) ;
await page . getByRole ( 'link' , { name : 'link' } ) . click ( ) ;
const page1 = await page1Promise ; ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2021-03-03 14:32:09 -08:00
Page page1 = page . waitForPopup ( ( ) - > {
2022-10-10 11:25:56 -08:00
page . getByRole ( AriaRole . LINK , new Page . GetByRoleOptions ( ) . setName ( "link" ) ) . click ( ) ;
2021-03-03 14:32:09 -08:00
} ) ; ` );
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-12-02 17:33:01 -08:00
with page . expect_popup ( ) as page1_info :
2022-10-03 16:14:02 -08:00
page . get_by_role ( "link" , name = "link" ) . click ( )
2022-12-02 17:33:01 -08:00
page1 = page1_info . value ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-12-02 17:33:01 -08:00
async with page . expect_popup ( ) as page1_info :
2022-10-03 16:14:02 -08:00
await page . get_by_role ( "link" , name = "link" ) . click ( )
2022-12-02 17:33:01 -08:00
page1 = await page1_info . value ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
var page1 = await page . RunAndWaitForPopupAsync ( async ( ) = >
{
await page . GetByRole ( AriaRole . Link , new ( ) { Name = "link" } ) . ClickAsync ( ) ;
} ) ; ` );
2021-02-16 18:13:26 -08:00
2020-12-28 17:39:30 -08:00
expect ( popup . url ( ) ) . toBe ( 'about:blank' ) ;
} ) ;
2020-12-28 14:50:12 -08:00
2024-09-19 10:31:44 -07:00
test ( 'should attribute navigation to click' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-04-02 11:19:26 -07:00
2020-12-28 17:39:30 -08:00
await recorder . setContentAndWait ( ` <a onclick="window.location.href='about:blank#foo'">link</a> ` ) ;
2020-12-28 14:50:12 -08:00
2022-10-24 18:01:48 -04:00
const locator = await recorder . hoverOverElement ( 'a' ) ;
expect ( locator ) . toBe ( ` getByText('link') ` ) ;
2021-02-16 18:13:26 -08:00
const [ , sources ] = await Promise . all ( [
2020-12-28 17:39:30 -08:00
page . waitForNavigation ( ) ,
2022-10-19 11:26:19 -07:00
recorder . waitForOutput ( 'JavaScript' , '.click()' ) ,
2022-11-14 15:16:25 -08:00
recorder . trustedClick ( ) ,
2020-12-28 17:39:30 -08:00
] ) ;
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2024-09-16 14:39:36 -07:00
await page . goto ( 'about:blank' ) ;
await page . getByText ( 'link' ) . click ( ) ;
// ---------------------
await context . close ( ) ; ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Playwright Test' ) ! . text ) . toContain ( `
2024-09-16 14:39:36 -07:00
await page . goto ( 'about:blank' ) ;
await page . getByText ( 'link' ) . click ( ) ;
} ) ; ` );
2021-06-10 16:52:59 -07:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2024-09-16 14:39:36 -07:00
page . navigate ( \ "about:blank\" ) ;
page . getByText ( \ "link\" ) . click ( ) ;
} ` );
2021-03-03 14:32:09 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2024-09-16 14:39:36 -07:00
page . goto ( "about:blank" )
page . get_by_text ( "link" ) . click ( )
# -- -- -- -- -- -- -- -- -- -- -
context . close ( ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2024-09-16 14:39:36 -07:00
await page . goto ( "about:blank" )
await page . get_by_text ( "link" ) . click ( )
# -- -- -- -- -- -- -- -- -- -- -
await context . close ( ) ` );
2021-02-16 18:13:26 -08:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'Pytest' ) ! . text ) . toContain ( `
2024-09-16 14:39:36 -07:00
page . goto ( "about:blank" )
2022-10-19 11:26:19 -07:00
page . get_by_text ( "link" ) . click ( ) ` );
2022-05-18 10:02:09 -07:00
2023-10-23 09:31:30 -07:00
expect . soft ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2024-09-16 14:39:36 -07:00
await page . GotoAsync ( "about:blank" ) ;
2023-12-15 10:24:26 -08:00
await page . GetByText ( "link" ) . ClickAsync ( ) ; ` );
2021-02-16 18:13:26 -08:00
2020-12-28 17:39:30 -08:00
expect ( page . url ( ) ) . toContain ( 'about:blank#foo' ) ;
} ) ;
2021-04-12 09:00:29 -07:00
2021-05-13 10:22:23 -07:00
test ( 'should ignore AltGraph' , async ( { openRecorder , browserName } ) = > {
test . skip ( browserName === 'firefox' , 'The TextInputProcessor in Firefox does not work with AltGraph.' ) ;
2024-09-19 10:31:44 -07:00
const { recorder } = await openRecorder ( ) ;
2021-04-12 09:00:29 -07:00
await recorder . setContentAndWait ( ` <input></input> ` ) ;
await recorder . page . type ( 'input' , 'playwright' ) ;
await recorder . page . keyboard . press ( 'AltGraph' ) ;
await recorder . page . keyboard . insertText ( '@' ) ;
await recorder . page . keyboard . type ( 'example.com' ) ;
2021-06-10 16:52:59 -07:00
await recorder . waitForOutput ( 'JavaScript' , 'example.com' ) ;
2023-10-23 09:31:30 -07:00
expect ( recorder . sources ( ) . get ( 'JavaScript' ) ! . text ) . not . toContain ( ` await page.getByRole('textbox').press('AltGraph'); ` ) ;
expect ( recorder . sources ( ) . get ( 'JavaScript' ) ! . text ) . toContain ( ` await page.getByRole('textbox').fill('playwright@example.com'); ` ) ;
2021-04-12 09:00:29 -07:00
} ) ;
2021-05-20 15:47:14 -07:00
2024-09-19 10:31:44 -07:00
test ( 'should middle click' , async ( { openRecorder , server } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2021-05-20 15:47:14 -07:00
await recorder . setContentAndWait ( ` <a href ${ JSON . stringify ( server . EMPTY_PAGE ) } >Click me</a> ` ) ;
const [ sources ] = await Promise . all ( [
2021-06-10 16:52:59 -07:00
recorder . waitForOutput ( 'JavaScript' , 'click' ) ,
2021-05-20 15:47:14 -07:00
page . click ( 'a' , { button : 'middle' } ) ,
] ) ;
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
await page . getByText ( 'Click me' ) . click ( {
2021-05-20 15:47:14 -07:00
button : 'middle'
} ) ; ` );
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
page . get_by_text ( "Click me" ) . click ( button = "middle" ) ` );
2021-05-20 15:47:14 -07:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
await page . get_by_text ( "Click me" ) . click ( button = "middle" ) ` );
2021-05-20 15:47:14 -07:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'Java' ) ! . text ) . toContain ( `
2022-10-03 16:14:02 -08:00
page . getByText ( "Click me" ) . click ( new Locator . ClickOptions ( )
2021-05-21 16:17:25 -07:00
. setButton ( MouseButton . MIDDLE ) ) ; ` );
2021-05-20 15:47:14 -07:00
2023-10-23 09:31:30 -07:00
expect ( sources . get ( 'C#' ) ! . text ) . toContain ( `
2023-12-15 10:24:26 -08:00
await page . GetByText ( "Click me" ) . ClickAsync ( new LocatorClickOptions
{
Button = MouseButton . Middle ,
} ) ; ` );
2021-05-20 15:47:14 -07:00
} ) ;
2024-02-01 21:45:19 +00:00
2024-09-19 10:31:44 -07:00
test ( 'should record slider' , async ( { openRecorder } ) = > {
const { page , recorder } = await openRecorder ( ) ;
2024-02-01 21:45:19 +00:00
await recorder . setContentAndWait ( ` <input type="range" min="0" max="10" value="5"> ` ) ;
const dragSlider = async ( ) = > {
const { x , y , width , height } = await page . locator ( 'input' ) . boundingBox ( ) ;
await page . mouse . move ( x + width / 2 , y + height / 2 ) ;
await page . mouse . down ( ) ;
await page . mouse . move ( x + width , y + height / 2 ) ;
await page . mouse . up ( ) ;
} ;
const [ sources ] = await Promise . all ( [
recorder . waitForOutput ( 'JavaScript' , 'fill' ) ,
dragSlider ( ) ,
] ) ;
await expect ( page . locator ( 'input' ) ) . toHaveValue ( '10' ) ;
expect ( sources . get ( 'JavaScript' ) ! . text ) . not . toContain ( `
await page . getByRole ( 'slider' ) . click ( ) ; ` );
expect ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
await page . getByRole ( 'slider' ) . fill ( '10' ) ; ` );
expect . soft ( sources . get ( 'Python' ) ! . text ) . toContain ( `
page . get_by_role ( "slider" ) . fill ( "10" ) ` );
expect . soft ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
await page . get_by_role ( "slider" ) . fill ( "10" ) ` );
expect . soft ( sources . get ( 'Java' ) ! . text ) . toContain ( `
page . getByRole ( AriaRole . SLIDER ) . fill ( "10" ) ` );
expect . soft ( sources . get ( 'C#' ) ! . text ) . toContain ( `
await page . GetByRole ( AriaRole . Slider ) . FillAsync ( "10" ) ; ` );
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should click button with nested div' , async ( { openRecorder } ) = > {
2024-02-01 21:45:19 +00:00
test . info ( ) . annotations . push ( { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/29067' } ) ;
2024-09-19 10:31:44 -07:00
const { recorder } = await openRecorder ( ) ;
2024-02-01 21:45:19 +00:00
await recorder . setContentAndWait ( ` <button><div role="none">Submit</div></button> ` ) ;
// we hover the nested div, but it must record the button
const locator = await recorder . hoverOverElement ( 'div' ) ;
expect ( locator ) . toBe ( ` getByRole('button', { name: 'Submit' }) ` ) ;
const [ sources ] = await Promise . all ( [
recorder . waitForOutput ( 'JavaScript' , 'Submit' ) ,
recorder . trustedClick ( ) ,
] ) ;
expect . soft ( sources . get ( 'JavaScript' ) ! . text ) . toContain ( `
await page . getByRole ( 'button' , { name : 'Submit' } ) . click ( ) ; ` );
expect . soft ( sources . get ( 'Python' ) ! . text ) . toContain ( `
page . get_by_role ( "button" , name = "Submit" ) . click ( ) ` );
expect . soft ( sources . get ( 'Python Async' ) ! . text ) . toContain ( `
await page . get_by_role ( "button" , name = "Submit" ) . click ( ) ` );
expect . soft ( sources . get ( 'Java' ) ! . text ) . toContain ( `
page . getByRole ( AriaRole . BUTTON , new Page . GetByRoleOptions ( ) . setName ( "Submit" ) ) . click ( ) ` );
expect . soft ( sources . get ( 'C#' ) ! . text ) . toContain ( `
await page . GetByRole ( AriaRole . Button , new ( ) { Name = "Submit" } ) . ClickAsync ( ) ; ` );
} ) ;
fix(codegen): fill action prevents omnibox navigation recording (#29790)
This PR is a fix proposal for a bug when trying to record a omnibox
navigation after a recorded action (e.g., `fill`).
The following test, included in this PR, reproduces the problem:
```ts
test('should record omnibox navigations after recordAction', async ({ page, openRecorder, server }) => {
const recorder = await openRecorder();
await recorder.setContentAndWait(`<textarea></textarea>`);
await Promise.all([
recorder.waitForOutput('JavaScript', 'fill'),
page.locator('textarea').fill('Hello world'),
]);
// for performed actions, 5 seconds is the time needed to ensure they are committed
await page.waitForTimeout(5000);
await page.goto(server.PREFIX + `/empty.html`);
await recorder.waitForOutput('JavaScript', `await page.goto('${server.PREFIX}/empty.html');`);
});
```
After performed actions (e.g., `click`), it successfully records the
navigation as long as there's at least a 5 sec. gap between both
actions. That happens because after that 5 sec. interval the performed
action is automatically commited and therefore the navigation is not
stored as a signal of that action.
The proposed fix for recorded actions also forces that action to be
automatically commited after 5 sec (for testing, I'm using 500ms to
speed up the test execution).
2024-03-04 20:31:03 +00:00
2024-09-19 10:31:44 -07:00
test ( 'should record omnibox navigations after performAction' , async ( { openRecorder , server } ) = > {
const { page , recorder } = await openRecorder ( ) ;
fix(codegen): fill action prevents omnibox navigation recording (#29790)
This PR is a fix proposal for a bug when trying to record a omnibox
navigation after a recorded action (e.g., `fill`).
The following test, included in this PR, reproduces the problem:
```ts
test('should record omnibox navigations after recordAction', async ({ page, openRecorder, server }) => {
const recorder = await openRecorder();
await recorder.setContentAndWait(`<textarea></textarea>`);
await Promise.all([
recorder.waitForOutput('JavaScript', 'fill'),
page.locator('textarea').fill('Hello world'),
]);
// for performed actions, 5 seconds is the time needed to ensure they are committed
await page.waitForTimeout(5000);
await page.goto(server.PREFIX + `/empty.html`);
await recorder.waitForOutput('JavaScript', `await page.goto('${server.PREFIX}/empty.html');`);
});
```
After performed actions (e.g., `click`), it successfully records the
navigation as long as there's at least a 5 sec. gap between both
actions. That happens because after that 5 sec. interval the performed
action is automatically commited and therefore the navigation is not
stored as a signal of that action.
The proposed fix for recorded actions also forces that action to be
automatically commited after 5 sec (for testing, I'm using 500ms to
speed up the test execution).
2024-03-04 20:31:03 +00:00
await recorder . setContentAndWait ( ` <button>Submit</button> ` ) ;
await Promise . all ( [
recorder . waitForOutput ( 'JavaScript' , 'click' ) ,
page . locator ( 'button' ) . click ( ) ,
] ) ;
await page . waitForTimeout ( 500 ) ;
await page . goto ( server . PREFIX + ` /empty.html ` ) ;
await recorder . waitForOutput ( 'JavaScript' , ` await page.goto(' ${ server . PREFIX } /empty.html'); ` ) ;
} ) ;
2024-09-19 10:31:44 -07:00
test ( 'should record omnibox navigations after recordAction' , async ( { openRecorder , server } ) = > {
const { page , recorder } = await openRecorder ( ) ;
fix(codegen): fill action prevents omnibox navigation recording (#29790)
This PR is a fix proposal for a bug when trying to record a omnibox
navigation after a recorded action (e.g., `fill`).
The following test, included in this PR, reproduces the problem:
```ts
test('should record omnibox navigations after recordAction', async ({ page, openRecorder, server }) => {
const recorder = await openRecorder();
await recorder.setContentAndWait(`<textarea></textarea>`);
await Promise.all([
recorder.waitForOutput('JavaScript', 'fill'),
page.locator('textarea').fill('Hello world'),
]);
// for performed actions, 5 seconds is the time needed to ensure they are committed
await page.waitForTimeout(5000);
await page.goto(server.PREFIX + `/empty.html`);
await recorder.waitForOutput('JavaScript', `await page.goto('${server.PREFIX}/empty.html');`);
});
```
After performed actions (e.g., `click`), it successfully records the
navigation as long as there's at least a 5 sec. gap between both
actions. That happens because after that 5 sec. interval the performed
action is automatically commited and therefore the navigation is not
stored as a signal of that action.
The proposed fix for recorded actions also forces that action to be
automatically commited after 5 sec (for testing, I'm using 500ms to
speed up the test execution).
2024-03-04 20:31:03 +00:00
await recorder . setContentAndWait ( ` <textarea></textarea> ` ) ;
await Promise . all ( [
recorder . waitForOutput ( 'JavaScript' , 'fill' ) ,
page . locator ( 'textarea' ) . fill ( 'Hello world' ) ,
] ) ;
await page . waitForTimeout ( 500 ) ;
await page . goto ( server . PREFIX + ` /empty.html ` ) ;
await recorder . waitForOutput ( 'JavaScript' , ` await page.goto(' ${ server . PREFIX } /empty.html'); ` ) ;
} ) ;
2024-04-23 15:33:12 +01:00
2024-09-19 10:31:44 -07:00
test ( 'should not throw csp directive violation errors' , async ( { openRecorder , server } ) = > {
const { page } = await openRecorder ( ) ;
2024-04-23 15:33:12 +01:00
await page . goto ( server . PREFIX + '/csp.html' ) ;
const predicate = ( msg : ConsoleMessage ) = > msg . type ( ) === 'error' && /Content[\- ]Security[\- ]Policy/i . test ( msg . text ( ) ) ;
await expect ( page . waitForEvent ( 'console' , { predicate , timeout : 1000 } ) ) . rejects . toThrow ( ) ;
} ) ;
2024-12-06 10:17:06 -08:00
test ( 'should clear when recording is disabled' , { annotation : { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/33802' } } , async ( { openRecorder } ) = > {
const { recorder } = await openRecorder ( ) ;
await recorder . setContentAndWait ( `
< button id = "foo" onclick = "console.log('click')" > Foo < / button >
< button id = "bar" onclick = "console.log('click')" > Bar < / button >
` );
await recorder . hoverOverElement ( '#foo' ) ;
let [ sources ] = await Promise . all ( [
recorder . waitForOutput ( 'JavaScript' , 'click' ) ,
recorder . trustedClick ( ) ,
] ) ;
expect ( sources . get ( 'JavaScript' ) . text ) . toContain ( ` getByRole('button', { name: 'Foo' }).click() ` ) ;
await recorder . recorderPage . getByRole ( 'button' , { name : 'Record' } ) . click ( ) ;
await recorder . recorderPage . getByRole ( 'button' , { name : 'Clear' } ) . click ( ) ;
await recorder . recorderPage . getByRole ( 'button' , { name : 'Record' } ) . click ( ) ;
await recorder . hoverOverElement ( '#bar' ) ;
[ sources ] = await Promise . all ( [
recorder . waitForOutput ( 'JavaScript' , 'click' ) ,
recorder . trustedClick ( ) ,
] ) ;
expect ( sources . get ( 'JavaScript' ) . text ) . toContain ( ` getByRole('button', { name: 'Bar' }).click() ` ) ;
expect ( sources . get ( 'JavaScript' ) . text ) . not . toContain ( ` getByRole('button', { name: 'Foo' }) ` ) ;
} ) ;
2020-12-28 14:50:12 -08:00
} ) ;