diff --git a/src/aria.ts b/src/aria.ts index 6f8ac7b..32206a5 100644 --- a/src/aria.ts +++ b/src/aria.ts @@ -17,12 +17,22 @@ import { readFileSync } from 'fs'; import * as path from 'path'; - export function loadAndPatchAriaModule() { +export function loadAndPatchAriaModule() { // Load aria implementation from https://github.com/xi/aria-api (MIT Licensed) - let script = readFileSync(path.join(__dirname, '../lib/inject.js'), { encoding: 'utf-8' }) + let script = readFileSync(path.join(__dirname, '../node_modules/aria-api/dist/aria.js'), { encoding: 'utf-8' }) script = script.replace(`var childNodes = [];`, `var childNodes = Array.from(node.shadowRoot?.childNodes || []).filter(n => !getOwner(n) && !isHidden(n))`); // Todo(https://github.com/puppeteer/recorder/issues/15): Check if this is the right approach script = script.replace(`'input[type="text"]:not([list])',`, `'input[type="text"]:not([list])',\n'input[type="password"]:not([list])',`); return script; - } \ No newline at end of file +} + +export function loadAndPatchInjectedModule() { + // Load aria implementation from https://github.com/xi/aria-api (MIT Licensed) + let script = readFileSync(path.join(__dirname, '../lib/inject.js'), { encoding: 'utf-8' }) + script = script.replace(`var childNodes = [];`, `var childNodes = Array.from(node.shadowRoot?.childNodes || []).filter(n => !getOwner(n) && !isHidden(n))`); + // Todo(https://github.com/puppeteer/recorder/issues/15): Check if this is the right approach + script = script.replace(`'input[type="text"]:not([list])',`, `'input[type="text"]:not([list])',\n'input[type="password"]:not([list])',`); + + return script; +} \ No newline at end of file diff --git a/src/injected/index.ts b/src/injected/index.ts index 066a586..1549d2a 100644 --- a/src/injected/index.ts +++ b/src/injected/index.ts @@ -45,3 +45,16 @@ window.addEventListener('submit', (e) => { const selector = getSelectorForEvent(e); addLineToPuppeteerScript(`await submit('${selector}');`); }, true); + +let scrollTimeout = null; +window.addEventListener('scroll', (e) => { + if(scrollTimeout) clearTimeout(scrollTimeout); + const prevScrollHeight = document.body.scrollHeight; + scrollTimeout = setTimeout(() => { + const currentScrollHeight = document.body.scrollHeight; + console.log(prevScrollHeight, currentScrollHeight); + if(currentScrollHeight > prevScrollHeight) { + addLineToPuppeteerScript(`await scrollToBottom();`); + } + }, 1000); +}, true); diff --git a/src/recorder.ts b/src/recorder.ts index 9445f47..71c128f 100644 --- a/src/recorder.ts +++ b/src/recorder.ts @@ -16,7 +16,7 @@ import * as puppeteer from 'puppeteer'; import { Readable } from 'stream'; -import { loadAndPatchAriaModule } from './aria'; +import { loadAndPatchInjectedModule } from './aria'; interface RecorderOptions { wsEndpoint?: string @@ -52,10 +52,10 @@ export default async (url: string, options: RecorderOptions = {}) => { } page.exposeFunction('addLineToPuppeteerScript', addLineToPuppeteerScript); - page.evaluateOnNewDocument(loadAndPatchAriaModule()); + page.evaluateOnNewDocument(loadAndPatchInjectedModule()); // Setup puppeteer - addLineToPuppeteerScript(`const {open, click, type, submit, expect} = require('@pptr/recorder');`) + addLineToPuppeteerScript(`const {open, click, type, submit, expect, scrollToBottom} = require('@pptr/recorder');`) addLineToPuppeteerScript(`open('${url}', {}, async (page) => {`); identation += 1; diff --git a/src/runner.ts b/src/runner.ts index f951c92..bb7e893 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -20,7 +20,7 @@ import * as readline from 'readline'; import { loadAndPatchAriaModule } from './aria'; import * as expect from 'expect'; -export {expect}; +export { expect }; declare const __dirname; @@ -29,7 +29,7 @@ const aria = loadAndPatchAriaModule(); const ariaSelectorEngine = new Function('element', 'selector', ` // Inject the aria library in case it has not been loaded yet if(!globalThis.aria) {${aria}} - + // Backslashes have to be escaped here const m = /(?\\w+)\\[(?\\w+)(?=|\\*=)"(?.+)"\\]/.exec(selector); if(!m) throw new Error('Invalid aria selector: ' + selector); @@ -84,11 +84,11 @@ interface RunnerOptions { async function beforeStep(...args) { console.log(...args); - if(!debug) { + if (!debug) { await timeout(delay); return; } - + const rl = readline.createInterface({ input: process.stdin, output: process.stdout, @@ -114,13 +114,13 @@ export async function open(url, options: RunnerOptions, cb) { export async function click(selector) { await beforeStep('click', selector); - const element = await page.waitForSelector(selector); + const element = await page.waitForSelector(selector, { visible: true }); await element.click(); } export async function type(selector, value) { await beforeStep('type', selector, value); - const element = await page.waitForSelector(selector); + const element = await page.waitForSelector(selector, { visible: true }); await element.click({ clickCount: 3 }); await element.press('Backspace'); await element.type(value); @@ -130,3 +130,8 @@ export async function submit(selector) { await beforeStep('submit', selector); await page.$eval(selector, form => form.requestSubmit()); } + +export async function scrollToBottom() { + await beforeStep('scrollToBottom'); + await page.evaluate(() => window.scrollBy(0, document.body.scrollHeight)); +}