Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Scroll pages and elements
.claude/skills/testdriverai-testdriver-scroll/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-09 | ✗→✓ | ▲ Improved | 130% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 44% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 32% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 27% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 42% | 0% |
<!-- Generated from scroll.mdx. DO NOT EDIT. -->
Scroll the page or active element in any direction using mouse wheel or keyboard.
<Warning> Focus Requirements
Scrolling requires the page or a frame to be focused. If an input field or other interactive element has focus, scroll commands may not work as expected. Before scrolling, ensure focus is on the page by:
If scroll is still not working, try using Page Down/Page Up keys directly: javascript await testdriver.pressKeys(['pagedown']); // Scroll down await testdriver.pressKeys(['pageup']); // Scroll up </Warning>
javascriptawait testdriver.scroll(direction, options)
<ParamField path="direction" type="string" default="down"> Direction to scroll: 'up', 'down' </ParamField>
<ParamField path="options" type="object"> <Expandable title="properties"> <ParamField path="amount" type="number" default="300"> Amount to scroll in pixels </ParamField> </Expandable> </ParamField>
Promise<void>
javascript// Scroll down (default) await testdriver.scroll(); // Scroll down 5 clicks await testdriver.scroll('down', { amount: 5 }); // Scroll up await testdriver.scroll('up'); // Scroll up 2 clicks await testdriver.scroll('up', { amount: 2 });
javascript// Scroll right await testdriver.scroll('right', { amount: 3 }); // Scroll left await testdriver.scroll('left', { amount: 3 });
javascript// Mouse wheel scroll (default) await testdriver.scroll('down', { amount: 3 }); // For keyboard-based scrolling, use pressKeys instead await testdriver.pressKeys(['pagedown']);
<Check> Ensure page has focus before scrolling
javascript // After typing in an input, unfocus it first await testdriver.find('email input').click(); await testdriver.type('user@example.com');
// Click elsewhere or press Escape before scrolling await testdriver.pressKeys('escape']); // Or click a non-interactive area // await testdriver.find('page background').click();
// Now scroll will work properly await testdriver.scroll('down');
// If scroll still doesn't work, use Page Down directly // await testdriver.pressKeys('pagedown']); </Check>
<Check> Control scroll distance with the options object
javascript // For web pages, mouse scroll works well await testdriver.scroll('down', { amount: 3 });
// For desktop apps or when mouse doesn't work, use keyboard await testdriver.pressKeys('pagedown']); </Check>
<Warning> Keyboard scroll uses Page Down/Up
Keyboard scrolling typically moves by one "page" at a time, which may be more than the specified click amount. It's more compatible but less precise than mouse scrolling. </Warning>
<AccordionGroup> <Accordion title="Infinite Scroll"> javascript // Scroll multiple times for infinite scroll for (let i = 0; i < 5; i++) { await testdriver.scroll('down', { amount: 5 }); await new Promise(r => setTimeout(r, 1000)); // Wait for load } </Accordion>
<Accordion title="Horizontal Gallery"> javascript // Navigate horizontal carousel await testdriver.scroll('right', { amount: 3 }); await new Promise(r => setTimeout(r, 500));
const nextImage = await testdriver.find('next image in carousel'); await nextImage.click(); </Accordion> </AccordionGroup>
javascriptimport { beforeAll, afterAll, describe, it } from 'vitest'; import TestDriver from 'testdriverai'; describe('Scrolling', () => { let testdriver; beforeAll(async () => { client = new TestDriver(process.env.TD_API_KEY); await testdriver.auth(); await testdriver.connect(); }); afterAll(async () => { await testdriver.disconnect(); }); it('should scroll to find elements', async () => { await testdriver.focusApplication('Google Chrome'); // Scroll down the page await testdriver.scroll('down', { amount: 5 }); // Click footer link const privacyLink = await testdriver.find('Privacy Policy link'); await privacyLink.click(); await testdriver.assert('privacy policy page is displayed'); }); it('should handle infinite scroll', async () => { await testdriver.focusApplication('Google Chrome'); // Scroll multiple times to load content for (let i = 0; i < 3; i++) { await testdriver.scroll('down', { amount: 5 }); await new Promise(r => setTimeout(r, 1500)); // Wait for load } // Verify content loaded await testdriver.assert('more than 10 items are visible'); }); });
find() - Locate elements after scrollingpressKeys() - Use Page Down/Up keyswait() - Wait after scrolling| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-09 | fail→pass | 5,082 | 1,508 | -70% | 1 | 1 | 0% | 713 | 1,638 | +130% | 0 | 0 | — |
case-01 | fail→pass | 7,578 | 12,074 | +59% | 1 | 1 | 0% | 1,339 | 1,928 | +44% | 0 | 0 | — |
case-02 | fail→pass | 9,884 | 4,586 | -54% | 1 | 1 | 0% | 1,699 | 2,238 | +32% | 0 | 0 | — |
case-03 | fail→pass | 10,924 | 4,739 | -57% | 1 | 1 | 0% | 1,813 | 2,296 | +27% | 0 | 0 | — |
case-04 | fail→pass | 7,886 | 3,510 | -55% | 1 | 1 | 0% | 1,391 | 1,979 | +42% | 0 | 0 | — |
case-05 | pass→pass | 6,840 | 3,863 | -44% | 1 | 1 | 0% | 1,086 | 2,080 | +92% | 0 | 0 | — |
case-06 | fail→pass | 6,288 | 2,947 | -53% | 1 | 1 | 0% | 1,097 | 1,837 | +67% | 0 | 0 | — |
case-07 | pass→pass | 5,240 | 1,810 | -65% | 1 | 1 | 0% | 849 | 1,697 | +100% | 0 | 0 | — |
case-08 | pass→pass | 7,578 | 1,746 | -77% | 1 | 1 | 0% | 1,162 | 1,683 | +45% | 0 | 0 | — |
case-10 | fail→pass | 8,459 | 1,496 | -82% | 1 | 1 | 0% | 1,355 | 1,668 | +23% | 0 | 0 | — |
case-11 | fail→pass | 6,929 | 2,027 | -71% | 1 | 1 | 0% | 1,118 | 1,706 | +53% | 0 | 0 | — |
case-12 | fail→pass | 6,088 | 2,368 | -61% | 1 | 1 | 0% | 1,011 | 1,803 | +78% | 0 | 0 | — |
case-13 | fail→pass | 11,895 | 3,207 | -73% | 1 | 1 | 0% | 1,989 | 1,956 | -2% | 0 | 0 | — |
case-14 | pass→pass | 4,385 | 1,901 | -57% | 1 | 1 | 0% | 702 | 1,674 | +138% | 0 | 0 | — |
case-15 | pass→pass | 4,560 | 3,008 | -34% | 1 | 1 | 0% | 806 | 1,974 | +145% | 0 | 0 | — |
case-16 | pass→pass | 7,318 | 3,225 | -56% | 1 | 1 | 0% | 1,290 | 1,978 | +53% | 0 | 0 | — |
case-17 | fail→pass | 6,210 | 2,641 | -57% | 1 | 1 | 0% | 1,066 | 1,845 | +73% | 0 | 0 | — |
case-18 | pass→pass | 4,632 | 2,415 | -48% | 1 | 1 | 0% | 691 | 1,788 | +159% | 0 | 0 | — |
case-19 | pass→pass | 13,424 | 4,293 | -68% | 1 | 1 | 0% | 2,090 | 2,183 | +4% | 0 | 0 | — |
case-20 | pass→pass | 7,373 | 4,571 | -38% | 1 | 1 | 0% | 1,573 | 2,450 | +56% | 0 | 0 | — |
case-21 | fail→pass | 9,405 | 1,584 | -83% | 1 | 1 | 0% | 1,627 | 1,604 | -1% | 0 | 0 | — |
case-22 | fail→pass | 9,095 | 2,207 | -76% | 1 | 1 | 0% | 1,579 | 1,777 | +13% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +59 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.