Skip to content
This repository has been archived by the owner on Feb 3, 2022. It is now read-only.

Commit

Permalink
Add simple implementation to capture scroll events
Browse files Browse the repository at this point in the history
This change adds a simple heuristic to capture scroll events
that lead to the lazy loading of content (infinite scroll).

See #17
  • Loading branch information
jschfflr committed Aug 4, 2020
1 parent 447d496 commit bbc7c76
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 12 deletions.
16 changes: 13 additions & 3 deletions src/aria.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

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;
}
13 changes: 13 additions & 0 deletions src/injected/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
6 changes: 3 additions & 3 deletions src/recorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down
17 changes: 11 additions & 6 deletions src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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 = /(?<role>\\w+)\\[(?<attribute>\\w+)(?<operator>=|\\*=)"(?<value>.+)"\\]/.exec(selector);
if(!m) throw new Error('Invalid aria selector: ' + selector);
Expand Down Expand Up @@ -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,
Expand All @@ -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);
Expand All @@ -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));
}

0 comments on commit bbc7c76

Please sign in to comment.