2024-12-19 10:44:08 +08:00
import path from 'node:path' ;
2024-08-20 07:41:08 +08:00
import { PuppeteerAgent } from '@/puppeteer' ;
2025-01-24 15:02:50 +08:00
import { sleep } from '@midscene/core/utils' ;
2025-04-24 22:54:52 +08:00
import { vlLocateMode } from '@midscene/shared/env' ;
2025-01-26 16:49:32 +08:00
import { afterEach , describe , expect , it , vi } from 'vitest' ;
2024-08-20 07:41:08 +08:00
import { launchPage } from './utils' ;
describe (
'puppeteer integration' ,
( ) = > {
2025-01-26 16:49:32 +08:00
let resetFn : ( ) = > Promise < void > ;
afterEach ( async ( ) = > {
if ( resetFn ) {
2025-03-12 13:49:50 +08:00
try {
await resetFn ( ) ;
} catch ( e ) {
console . warn ( 'resetFn error' ) ;
console . warn ( e ) ;
}
2025-01-26 16:49:32 +08:00
}
} ) ;
2024-08-20 07:41:08 +08:00
it ( 'Sauce Demo by Swag Lab' , async ( ) = > {
2024-09-29 17:16:07 +08:00
const { originPage , reset } = await launchPage (
'https://www.saucedemo.com/' ,
) ;
2025-01-26 16:49:32 +08:00
resetFn = reset ;
2025-01-10 10:55:41 +08:00
const onTaskStartTip = vi . fn ( ) ;
2025-02-24 14:29:17 +08:00
const agent = new PuppeteerAgent ( originPage , {
2024-11-07 21:06:01 +08:00
cacheId : 'puppeteer(Sauce Demo by Swag Lab)' ,
2025-01-10 10:55:41 +08:00
onTaskStartTip ,
2024-11-07 21:06:01 +08:00
} ) ;
2025-02-21 09:56:09 +08:00
await sleep ( 10 * 1000 ) ;
2025-04-29 13:36:49 +08:00
const flag = await agent . aiBoolean ( 'this is a login page' ) ;
expect ( flag ) . toBe ( true ) ;
2025-02-24 14:29:17 +08:00
await agent . aiAction (
2025-04-02 19:26:56 +08:00
'type "standard_user" in user name input, type "secret_sauce" in password' ,
2024-08-20 07:41:08 +08:00
) ;
2025-04-02 19:26:56 +08:00
await agent . aiTap ( 'Login' ) ;
2025-01-10 10:55:41 +08:00
expect ( onTaskStartTip . mock . calls . length ) . toBeGreaterThan ( 1 ) ;
2024-12-16 15:04:21 +08:00
2024-08-21 14:43:35 +08:00
await expect ( async ( ) = > {
2025-02-24 14:29:17 +08:00
await agent . aiWaitFor ( 'there is a cookie prompt in the UI' , {
2024-08-21 14:43:35 +08:00
timeoutMs : 10 * 1000 ,
} ) ;
} ) . rejects . toThrowError ( ) ;
2024-08-20 07:41:08 +08:00
// find the items
2025-02-24 14:29:17 +08:00
const items = await agent . aiQuery (
2024-08-20 07:41:08 +08:00
'"{name: string, price: number, actionBtnName: string}[], return item name, price and the action button name on the lower right corner of each item (like "Remove")' ,
) ;
console . log ( 'item list' , items ) ;
expect ( items . length ) . toBeGreaterThanOrEqual ( 2 ) ;
2025-02-26 17:06:14 +08:00
await agent . aiAssert ( 'The price of "Sauce Labs Backpack" is 29.99' ) ;
2024-08-20 07:41:08 +08:00
} ) ;
it ( 'extract the Github service status' , async ( ) = > {
2024-09-29 17:16:07 +08:00
const { originPage , reset } = await launchPage (
'https://www.githubstatus.com/' ,
) ;
2025-01-26 16:49:32 +08:00
resetFn = reset ;
2025-02-24 14:29:17 +08:00
const agent = new PuppeteerAgent ( originPage ) ;
2024-08-20 07:41:08 +08:00
2025-02-24 14:29:17 +08:00
const result = await agent . aiQuery (
2024-08-20 07:41:08 +08:00
'this is a service status page. Extract all status data with this scheme: {[serviceName]: [statusText]}' ,
) ;
console . log ( 'Github service status' , result ) ;
expect ( async ( ) = > {
// there is no food delivery service on Github
2025-02-24 14:29:17 +08:00
await agent . aiAssert (
2024-08-20 07:41:08 +08:00
'there is a "food delivery" service on page and is in normal state' ,
) ;
} ) ;
} ) ;
2024-09-09 15:57:36 +08:00
2025-01-26 16:49:32 +08:00
it . skipIf ( process . env . CI ) ( 'find widgets in antd' , async ( ) = > {
2024-09-29 17:16:07 +08:00
const { originPage , reset } = await launchPage (
2025-01-26 16:49:32 +08:00
'https://ant.design/components/form/' , // will be banned by the website on CI
2024-09-09 15:57:36 +08:00
) ;
2025-01-26 16:49:32 +08:00
resetFn = reset ;
2025-02-24 14:29:17 +08:00
const agent = new PuppeteerAgent ( originPage ) ;
2024-09-09 15:57:36 +08:00
2025-02-24 14:29:17 +08:00
// await agent.aiAction('If pop-ups are displayed click seven days out alert');
2025-01-24 15:02:50 +08:00
await sleep ( 8000 ) ;
2025-02-24 14:29:17 +08:00
await agent . aiAction (
2024-12-08 20:12:17 +08:00
'Click the password input in the demo section on page, type "abc"' ,
) ;
2024-10-17 10:44:30 +08:00
2025-02-24 14:29:17 +08:00
await agent . aiAction (
2024-12-08 20:12:17 +08:00
'click the "icon" on the categories on the left, sleep 5s, in the newly loaded page, type "pause" in the icon search box(it shows "search icon here")' ,
) ;
2024-09-09 15:57:36 +08:00
2025-02-24 14:29:17 +08:00
const names = await agent . aiQuery (
2024-12-08 20:12:17 +08:00
'find all component names in the page, return in string[]' ,
2024-09-09 15:57:36 +08:00
) ;
2024-12-08 20:12:17 +08:00
expect ( names . length ) . toBeGreaterThan ( 5 ) ;
2024-09-09 15:57:36 +08:00
} ) ;
2024-10-12 12:09:25 +08:00
2025-04-01 10:41:31 +08:00
it . skipIf ( ! vlLocateMode ( ) ) (
'search engine with specific actions' ,
async ( ) = > {
const { originPage , reset } = await launchPage (
'https://www.baidu.com/' ,
) ;
resetFn = reset ;
const agent = new PuppeteerAgent ( originPage ) ;
2025-03-24 09:50:27 +08:00
2025-04-01 10:41:31 +08:00
await agent . aiInput ( 'AI 101' , 'the search bar input' ) ;
await agent . aiTap ( 'the search button' ) ;
2025-03-24 09:50:27 +08:00
2025-04-01 10:41:31 +08:00
await sleep ( 3000 ) ;
2025-03-24 09:50:27 +08:00
2025-04-01 10:41:31 +08:00
await agent . aiScroll ( {
direction : 'down' ,
scrollType : 'untilBottom' ,
} ) ;
2025-03-24 09:50:27 +08:00
2025-04-01 10:41:31 +08:00
await sleep ( 3000 ) ;
2025-03-24 09:50:27 +08:00
2025-04-01 10:41:31 +08:00
await agent . aiTap ( 'the settings button' , {
deepThink : true ,
} ) ;
2025-03-24 09:50:27 +08:00
2025-04-01 10:41:31 +08:00
await agent . aiTap ( '搜索设置' , {
deepThink : true ,
} ) ;
await agent . aiTap ( 'the close button of the popup' , {
deepThink : true ,
} ) ;
await agent . aiAssert ( 'there is NOT a popup shown in the page' ) ;
} ,
) ;
2025-03-17 19:19:54 +08:00
2025-01-30 14:14:14 +08:00
it (
'search engine' ,
async ( ) = > {
2025-02-21 16:15:51 +08:00
const { originPage , reset } = await launchPage ( 'https://www.bing.com/' ) ;
2025-01-30 14:14:14 +08:00
resetFn = reset ;
2025-02-24 14:29:17 +08:00
const agent = new PuppeteerAgent ( originPage ) ;
await agent . aiAction ( 'type "AI 101" in search box' ) ;
await agent . aiAction (
2025-02-21 16:15:51 +08:00
'type "Hello world" in search box, hit Enter, wait 2s' ,
2025-01-30 14:14:14 +08:00
) ;
2024-10-12 12:09:25 +08:00
2025-02-24 14:29:17 +08:00
await agent . aiWaitFor (
2025-01-30 14:14:14 +08:00
'there are some search results about "Hello world"' ,
) ;
} ,
2025-02-21 16:15:51 +08:00
3 * 60 * 1000 ,
2025-01-30 14:14:14 +08:00
) ;
2024-12-19 10:44:08 +08:00
2025-05-21 21:05:47 +08:00
it ( 'element describer' , async ( ) = > {
const { originPage , reset } = await launchPage ( 'https://www.taobao.com/' ) ;
resetFn = reset ;
const agent = new PuppeteerAgent ( originPage ) ;
const { center } = await agent . aiLocate ( 'the search bar' ) ;
const describeResult = await agent . describeElementAtPoint ( center ) ;
expect ( describeResult . verifyResult ? . pass ) . toBe ( true ) ;
expect ( describeResult . verifyResult ? . rect ) . toBeTruthy ( ) ;
expect ( describeResult . verifyResult ? . center ) . toBeTruthy ( ) ;
} ) ;
it ( 'element describer - deep think' , async ( ) = > {
const { originPage , reset } = await launchPage ( 'https://www.taobao.com/' ) ;
resetFn = reset ;
const agent = new PuppeteerAgent ( originPage ) ;
const { center } = await agent . aiLocate ( 'the "search" button' ) ;
const describeResult = await agent . describeElementAtPoint ( center , {
deepThink : true ,
} ) ;
expect ( describeResult . verifyResult ? . pass ) . toBe ( true ) ;
expect ( describeResult . verifyResult ? . rect ) . toBeTruthy ( ) ;
expect ( describeResult . verifyResult ? . center ) . toBeTruthy ( ) ;
} ) ;
2024-12-19 10:44:08 +08:00
it ( 'scroll' , async ( ) = > {
const htmlPath = path . join ( __dirname , 'scroll.html' ) ;
const { originPage , reset } = await launchPage ( ` file:// ${ htmlPath } ` ) ;
2025-01-26 16:49:32 +08:00
resetFn = reset ;
2025-02-24 14:29:17 +08:00
const agent = new PuppeteerAgent ( originPage ) ;
await agent . aiAction (
2024-12-19 10:44:08 +08:00
'find the "Vertical 2" element, scroll down 200px, find the "Horizontal 2" element, scroll right 100px' ,
) ;
2025-02-24 14:29:17 +08:00
await agent . aiAssert (
2024-12-19 10:44:08 +08:00
'the "Horizontal 2", "Horizontal 4" and "Vertical 5" elements are visible' ,
) ;
2025-01-26 16:49:32 +08:00
} ) ;
it ( 'not tracking active tab' , async ( ) = > {
const { originPage , reset } = await launchPage ( 'https://www.baidu.com/' ) ;
resetFn = reset ;
2025-02-24 14:29:17 +08:00
const agent = new PuppeteerAgent ( originPage , {
2025-02-14 21:54:47 +08:00
forceSameTabNavigation : false ,
2025-01-26 16:49:32 +08:00
} ) ;
2025-02-24 14:29:17 +08:00
await agent . aiAction ( 'Tap hao123 in the navigation bar' ) ;
2025-02-07 14:55:52 +08:00
await sleep ( 6000 ) ;
2025-01-26 16:49:32 +08:00
expect ( async ( ) = > {
2025-02-24 14:29:17 +08:00
await agent . aiAssert ( 'There is a weather forecast in the page' ) ;
2025-01-26 16:49:32 +08:00
} ) . rejects . toThrowError ( ) ;
} ) ;
it ( 'tracking active tab' , async ( ) = > {
const { originPage , reset } = await launchPage ( 'https://www.baidu.com/' ) ;
resetFn = reset ;
2025-02-24 14:29:17 +08:00
const agent = new PuppeteerAgent ( originPage , {
2025-02-14 21:54:47 +08:00
forceSameTabNavigation : true ,
2025-01-26 16:49:32 +08:00
} ) ;
2025-02-24 14:29:17 +08:00
await agent . aiAction ( 'Tap hao123 in the navigation bar' ) ;
2025-02-07 14:55:52 +08:00
2025-02-24 14:29:17 +08:00
await agent . aiWaitFor ( 'There is a weather forecast in the page' ) ;
2024-12-19 10:44:08 +08:00
} ) ;
2025-05-22 07:20:32 +08:00
it ( 'input xss content' , async ( ) = > {
const { originPage , reset } = await launchPage ( 'https://www.baidu.com/' ) ;
const agent = new PuppeteerAgent ( originPage ) ;
await agent . aiInput (
'</script><script>alert("xss")</script>' ,
'the search box' ,
) ;
await reset ( ) ;
const reportFile = agent . reportFile ;
const reportPage = await launchPage ( ` file:// ${ reportFile } ` ) ;
const reportAgent = new PuppeteerAgent ( reportPage . originPage ) ;
await reportAgent . aiAssert ( 'there is a sidebar in the page' ) ;
resetFn = reportPage . reset ;
} ) ;
2025-05-21 20:36:22 +08:00
it ( 'Sauce Demo by Swag Lab - aiQuery' , async ( ) = > {
const { originPage , reset } = await launchPage (
'https://www.saucedemo.com/' ,
) ;
resetFn = reset ;
const agent = new PuppeteerAgent ( originPage , {
cacheId : 'puppeteer(Sauce Demo by Swag Lab)' ,
} ) ;
await sleep ( 10 * 1000 ) ;
const title = await agent . aiQuery ( 'the page title, string' ) ;
const list = await agent . aiQuery ( 'the name of input fields, string[]' ) ;
const button = await agent . aiQuery ( {
first_input_name : 'the name of the first input field, string' ,
login_button_name : 'the name of the login button, string' ,
} ) ;
expect ( title ) . toBe ( 'Swag Labs' ) ;
expect ( list . length ) . toBeGreaterThan ( 0 ) ;
expect ( button . first_input_name ) . toBeDefined ( ) ;
} ) ;
2024-12-19 10:44:08 +08:00
it . skip ( 'Playground' , async ( ) = > {
const { originPage , reset } = await launchPage ( 'https://www.baidu.com/' ) ;
2025-01-26 16:49:32 +08:00
resetFn = reset ;
2025-02-24 14:29:17 +08:00
const agent = new PuppeteerAgent ( originPage ) ;
// await agent.aiAction('Close the cookie prompt');
await agent . aiAction (
2024-12-19 10:44:08 +08:00
'Type "AI 101" in search box, hit Enter, wait 2s. If there is a cookie prompt, close it' ,
) ;
} ) ;
2024-08-20 07:41:08 +08:00
} ,
2025-02-21 16:15:51 +08:00
4 * 60 * 1000 ,
2024-08-20 07:41:08 +08:00
) ;