playwright-skill
npx skills add https://github.com/mgdaaslab/wharttest --skill playwright-skill
Agent 安装分布
Skill 文档
Playwright æµè§å¨èªå¨å
æ§è¡æµè§å¨èªå¨åä»»å¡ï¼æ¯æé¡µé¢æµè¯ã表åæä½ãç»å½éªè¯çã
â ï¸ å¼ºå¶è§åï¼å è·å页é¢ç»æï¼åæä½å ç´
ç¦æ¢çæµéæ©å¨ï¼ç¦æ¢éè¿æªå¾è¯å«å ç´ ï¼
æå¼ä»»ä½é¡µé¢åï¼å¿
é¡»ç«å³è°ç¨ helpers.describePageForAI(page) è·å页é¢å
ç´ å表ï¼ç¶åæ ¹æ®è¿åçéæ©å¨è¿è¡æä½ã
// æå¼é¡µé¢å第ä¸ä»¶äºï¼è·å页é¢ç»æ
await page.goto('http://example.com');
const desc = await helpers.describePageForAI(page);
console.log(desc); // è¾åºææå¯äº¤äºå
ç´ åå
¶éæ©å¨
è¾åºç¤ºä¾ï¼
## Page: WHartTest
URL: http://192.168.150.114:8913/login
### Input Fields (2)
- text: selector="#username" placeholder="请è¾å
¥ç¨æ·å"
- password: selector="#password" placeholder="请è¾å
¥å¯ç "
### Buttons (1)
- "ç»å½": selector="button[type="submit"]"
ç¶åæ ¹æ®è¾åºçéæ©å¨è¿è¡æä½ï¼
await page.fill('#username', 'admin');
await page.fill('#password', '123456');
await page.click('button[type="submit"]');
ä½¿ç¨æ¹æ³
éè¿ execute_skill_script å·¥å
·è°ç¨ï¼ä¼ å
¥ inline 代ç ï¼
node run.js "your playwright code here"
éè¦ï¼ä»£ç å¿ é¡»åå¨ä¸è¡ï¼è¯å¥ç¨åå·åéãrun.js ä¼èªå¨å è£ async IIFE å require è¯å¥ãç¦æ¢æ·»å –sessionã–inlineã–eval çåæ°ï¼run.js 䏿¯æè¿äºåæ°ã
æªå¾è·¯å¾çº¦å®
å¿
须使ç¨ç¯å¢åé process.env.SCREENSHOT_DIR ä½ä¸ºæªå¾ä¿åç®å½ãç³»ç»ä¼èªå¨è®¾ç½®è¯¥åéæå playwright-skill ç®å½ä¸ç media/screenshots/ åç®å½ã
æªå¾å½å建议ï¼case_{case_id}_step{step_number}.png
示ä¾ï¼
const screenshotDir = process.env.SCREENSHOT_DIR || './media/screenshots';
await page.screenshot({ path: `${screenshotDir}/case_11_step1.png` });
åºç¡ç¤ºä¾
æå¼é¡µé¢å¹¶æªå¾
node run.js "const dir = process.env.SCREENSHOT_DIR; const browser = await chromium.launch({ headless: false }); const page = await browser.newPage(); await page.goto('http://example.com'); await page.screenshot({ path: dir + '/example.png' }); console.log('æªå¾å·²ä¿å:', dir + '/example.png'); await browser.close();"
ç»å½æµè¯ï¼å¸¦æªå¾ï¼
node run.js "const dir = process.env.SCREENSHOT_DIR; const browser = await chromium.launch({ headless: false, slowMo: 100 }); const page = await browser.newPage(); await page.goto('http://192.168.150.114:8913/'); await page.screenshot({ path: dir + '/step1_open.png' }); await page.fill('input[type=\"text\"]', 'admin'); await page.fill('input[type=\"password\"]', 'admin123456'); await page.screenshot({ path: dir + '/step2_filled.png' }); await page.click('button[type=\"submit\"]'); await page.waitForTimeout(2000); await page.screenshot({ path: dir + '/step3_result.png' }); console.log('æªå¾å·²ä¿åå°', dir); await browser.close();"
表åå¡«å
node run.js "const browser = await chromium.launch({ headless: false }); const page = await browser.newPage(); await page.goto('http://example.com/form'); await page.fill('input[name=\"username\"]', 'testuser'); await page.fill('input[name=\"email\"]', 'test@example.com'); await page.click('button[type=\"submit\"]'); console.log('表åæäº¤å®æ'); await browser.close();"
å ¶ä» helpers 彿°
helpers.getPageStructure(page) – è·åç»æå JSON
è¿å JSON 对象ï¼éåç¨åºåå¤çã
const structure = await helpers.getPageStructure(page);
console.log(JSON.stringify(structure, null, 2));
helpers.getPageText(page) – è·åçº¯ææ¬å 容
è¿å页颿æå¯è§ææ¬ã
const text = await helpers.getPageText(page);
console.log(text);
å¸¸ç¨ API
æµè§å¨å¯å¨
// å¯è§æ¨¡å¼ï¼æ¨èè°è¯ï¼
const browser = await chromium.launch({ headless: false, slowMo: 100 });
// æ 头模å¼ï¼åå°æ§è¡ï¼
const browser = await chromium.launch({ headless: true });
页é¢å¯¼èª
await page.goto('http://example.com');
await page.goto('http://example.com', { waitUntil: 'networkidle' });
å ç´ å®ä½ä¸æä½
// è¾å
¥ææ¬
await page.fill('input[name="username"]', 'admin');
await page.fill('input[type="password"]', '123456');
// ç¹å»
await page.click('button[type="submit"]');
await page.click('text=ç»å½');
await page.click('.login-btn');
// çå¾
å
ç´
await page.waitForSelector('.success-message');
await page.waitForURL('**/dashboard');
常ç¨éæ©å¨
| éæ©å¨ç±»å | ç¤ºä¾ |
|---|---|
| CSS | input[name="username"], .login-btn, #submit |
| ææ¬ | text=ç»å½, button:has-text("æäº¤") |
| ç±»å | input[type="text"], input[type="password"] |
| å ä½ç¬¦ | input[placeholder*="è´¦å·"], input[placeholder*="å¯ç "] |
æªå¾ï¼ä½¿ç¨ç¯å¢åéï¼
const dir = process.env.SCREENSHOT_DIR;
await page.screenshot({ path: `${dir}/screenshot.png` });
await page.screenshot({ path: `${dir}/full.png`, fullPage: true });
çå¾
await page.waitForTimeout(2000); // çå¾
2ç§
await page.waitForLoadState('networkidle'); // çå¾
ç½ç»ç©ºé²
宿´æµè¯æµç¨ç¤ºä¾
æ§è¡ä¸ä¸ªå®æ´çç»å½æµè¯ï¼
node run.js "const dir = process.env.SCREENSHOT_DIR; const browser = await chromium.launch({ headless: false, slowMo: 100 }); const page = await browser.newPage(); console.log('æ¥éª¤1: æå¼ç»å½é¡µ'); await page.goto('http://192.168.150.114:8913/'); await page.screenshot({ path: dir + '/step1_open.png' }); console.log('æ¥éª¤2: è¾å
¥è´¦å·'); await page.fill('input[type=\"text\"]', 'admin'); console.log('æ¥éª¤3: è¾å
¥å¯ç '); await page.fill('input[type=\"password\"]', 'admin123456'); await page.screenshot({ path: dir + '/step2_input.png' }); console.log('æ¥éª¤4: ç¹å»ç»å½'); await page.click('button[type=\"submit\"]'); await page.waitForTimeout(2000); await page.screenshot({ path: dir + '/step3_result.png' }); console.log('ç»å½ç»æ - å½åURL:', page.url()); await browser.close(); console.log('æµè¯å®æï¼æªå¾ä¿åå¨:', dir);"
注æäºé¡¹
- æªå¾è·¯å¾ï¼å¿
须使ç¨
process.env.SCREENSHOT_DIRç¯å¢åé - ä»£ç æ ¼å¼ï¼inline 代ç ç¨åå·åéè¯å¥ï¼åå¨ä¸è¡å
- å¼å·è½¬ä¹ï¼å符串å
çåå¼å·éè¦è½¬ä¹
\" - browser.close()ï¼éæä¹ 忍¡å¼ä¸æ§è¡å®æ¯åå¡å¿ å ³éæµè§å¨
- console.log()ï¼ç¨äºè¾åºæ§è¡è¿åº¦åç»æ
- headless: falseï¼è°è¯æ¶ä½¿ç¨å¯è§æ¨¡å¼ï¼æ¹ä¾¿è§å¯
æä¹ åä¼è¯æ¨¡å¼
对äºéè¦è·¨å¤ä¸ªæ¥éª¤ä¿ææµè§å¨æå¼çåºæ¯ï¼å¦èªå¨åæµè¯ç¨ä¾ï¼ï¼ä½¿ç¨ session_id åæ°ï¼
â ï¸ æ ¸å¿è§åï¼å¿ é¡»ä¸¥æ ¼éµå®ï¼
- session_id å¿
é¡»å®å
¨ä¸è´ï¼æ´ä¸ªæµè¯æµç¨ä¸æææ¥éª¤å¿
须使ç¨å®å
¨ç¸åç session_id å符串ï¼å¦åä¼åå»ºæ°æµè§å¨å¯¼è´ç¶æä¸¢å¤±ï¼
- â
æ£ç¡®ï¼æææ¥éª¤é½ç¨
session_id="case_11" - â é误ï¼ç¬¬ä¸æ¥ç¨
case_11ï¼ç¬¬äºæ¥ç¨case-11ï¼ç¬¬ä¸æ¥ç¨test-case-11ï¼è¿ä¼å建3个ä¸åçæµè§å¨ï¼ï¼
- â
æ£ç¡®ï¼æææ¥éª¤é½ç¨
- ç´æ¥ä½¿ç¨
pageåéï¼æ échromium.launch()ï¼ç³»ç»èªå¨ç®¡ç - ä¸è¦è°ç¨
browser.close()ï¼æµè§å¨ç±ç³»ç»ç®¡çï¼ç©ºé² 15 åéèªå¨å ³é
æä¹ 忍¡å¼ç¤ºä¾
æ¥éª¤ 1ï¼æå¼é¡µé¢
execute_skill_script(
skill_name="playwright-skill",
command='node run.js "await page.goto(\'http://example.com\'); console.log(page.url());"',
session_id="test-case-001"
)
æ¥éª¤ 2ï¼å¡«å表åï¼å¤ç¨å䏿µè§å¨ï¼
execute_skill_script(
skill_name="playwright-skill",
command='node run.js "await page.fill(\'input[name=username]\', \'admin\'); await page.fill(\'input[name=password]\', \'123456\');"',
session_id="test-case-001"
)
æ¥éª¤ 3ï¼ç¹å»ç»å½ï¼ç»§ç»å¤ç¨ï¼
execute_skill_script(
skill_name="playwright-skill",
command='node run.js "await page.click(\'button[type=submit]\'); await page.waitForTimeout(2000); console.log(\'ç»å½åURL:\', page.url());"',
session_id="test-case-001"
)
æä¹ å vs éæä¹ å对æ¯
| ç¹æ§ | éæä¹ åï¼æ session_idï¼ | æä¹ åï¼æ session_idï¼ |
|---|---|---|
| æµè§å¨çå½å¨æ | ä»£ç æå¨ç®¡ç | ç³»ç»èªå¨ç®¡ç |
| å¯å¨æ¹å¼ | chromium.launch() |
ç´æ¥ä½¿ç¨ page |
| å ³éæ¹å¼ | browser.close() |
èªå¨å ³éï¼15åé空é²ï¼ |
| è·¨æ¥éª¤ç¶æ | ä¸ä¿æ | ä¿æï¼ç»å½æãcookieçï¼ |
| éç¨åºæ¯ | 忥æä½ | 夿¥éª¤æµè¯ç¨ä¾ |