Comparing version 0.0.3 to 0.0.4
@@ -7,3 +7,2 @@ const os = require('os'); | ||
const hFile = path.join(os.homedir(), '.taiko_repl_history'); | ||
const commands = []; | ||
@@ -38,4 +37,4 @@ const leakLocals = (nodes) => { | ||
const aEval = (oEval) => (cmd, context, filename, callback) => { | ||
commands.push(cmd); | ||
const aEval = (oEval, cmdCallback) => (cmd, context, filename, callback) => { | ||
const oCmd = cmd.trim(); | ||
try { | ||
@@ -53,2 +52,3 @@ cmd = rewrite(cmd); | ||
callback.call(this, err, value); | ||
cmdCallback(oCmd, value); | ||
} catch (error) { | ||
@@ -62,8 +62,7 @@ callback.call(this, error, null); | ||
module.exports = { | ||
aEval: (repl) => { | ||
aEval: (repl, callback) => { | ||
const oEval = repl.eval; | ||
history(repl, hFile); | ||
repl.eval = aEval(oEval); | ||
repl.eval = aEval(oEval, callback); | ||
}, | ||
commands: () => commands, | ||
}; |
{ | ||
"name": "taiko", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"description": "An easy to use wrapper over Google Chrome's Puppeteer library.", | ||
@@ -20,4 +20,6 @@ "main": "taiko.js", | ||
"headless", | ||
"puppeteer", | ||
"browser" | ||
"headless-chrome", | ||
"headless-testing", | ||
"headless-browser", | ||
"puppeteer" | ||
], | ||
@@ -27,4 +29,5 @@ "author": "getgauge", | ||
"dependencies": { | ||
"babylon": "^6.18.0", | ||
"figlet": "^1.2.0", | ||
"puppeteer": "^0.12.0", | ||
"babylon": "^6.18.0", | ||
"recast": "^0.12.8", | ||
@@ -31,0 +34,0 @@ "repl.history": "^0.1.4" |
@@ -1,4 +0,13 @@ | ||
# Taiko | ||
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) | ||
``` | ||
___________ .__ __ | ||
\__ ___/____ |__| | ______ | ||
| | \__ \ | | |/ / _ \ | ||
| | / __ \| | < <_> ) | ||
|____| (____ /__|__|_ \____/ | ||
\/ \/ | ||
``` | ||
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![npm version](https://badge.fury.io/js/taiko.svg)](https://badge.fury.io/js/taiko) | ||
An easy to use wrapper over Google Chrome's Puppeteer library. | ||
An easy to use wrapper over Google Chrome's Puppeteer library. | ||
[![NPM](https://nodei.co/npm/taiko.png)](https://npmjs.org/package/taiko) |
107
repl.js
#! /usr/bin/env node | ||
const { aEval, commands } = require('./awaitEval'); | ||
const fs = require('fs'); | ||
const util = require('util'); | ||
const { aEval } = require('./awaitEval'); | ||
const taiko = require('./taiko'); | ||
const repl = require('repl').start({ prompt: '>> ' }); | ||
const funcs = []; | ||
try { | ||
const figlet = require('figlet'); | ||
const fonts = ['Graffiti', '3D Diagonal', 'Acrobatic', 'Avatar', 'Big Money-ne', 'Big Money-nw', 'Big Money-se', 'Graffiti', 'Big Money-sw', 'Big', 'Blocks', 'Bulbhead', 'Cards', 'Chiseled', 'Crawford2', 'Crazy', 'Dancing Font', 'Doh', 'Doom', 'Epic', 'Graffiti', 'Fire Font-k', 'Fire Font-s', 'Flower Power', 'Ghost', 'Graceful', 'Graffiti', 'Impossible', 'Isometric1', 'Isometric2', 'Graffiti', 'Isometric3', 'Isometric4', 'JS Bracket Letters', 'Lil Devil', 'Merlin1', 'Modular', 'Ogre', 'Patorjk\'s Cheese', 'Patorjk-HeX', 'Rectangles', 'Slant', 'Slant Relief', 'Small', 'Small Slant', 'Small Isometric1', 'Soft', 'Standard', 'Star Wars', 'Sub-Zero', 'Swamp Land', 'Sweet', 'Train', 'Twisted', 'Wet Letter', 'Varsity', '3D-ASCII', 'ANSI Shadow', 'Bloody', 'Calvin S', 'Delta Corps Priest 1', 'Electronic', 'Elite', 'Stronger Than All', 'THIS', 'The Edge', '4Max', '5 Line Oblique', 'AMC 3 Line', 'AMC AAA01', 'AMC Neko', 'AMC Razor', 'AMC Razor2', 'AMC Slash', 'AMC Slider', 'AMC Thin', 'AMC Tubes', 'AMC Untitled', 'ASCII New Roman', 'Alligator', 'Alligator2', 'Alphabet', 'Arrows', 'Banner', 'Banner3-D', 'Banner3', 'Banner4', 'Basic', 'Bear', 'Bell', 'Bigfig', 'Block', 'Bolger', 'Braced', 'Bright', 'Broadway KB', 'Broadway', 'Bubble', 'Caligraphy', 'Caligraphy2', 'Chunky', 'Coinstak', 'Cola', 'Colossal', 'Computer', 'Contessa', 'Contrast', 'Cosmike', 'Crawford', 'Cricket', 'Cursive', 'Cyberlarge', 'Cybermedium', 'Cybersmall', 'Cygnet', 'Def Leppard', 'Diamond', 'Diet Cola', 'Digital', 'Dot Matrix', 'Double Shorts', 'Double', 'Dr Pepper', 'Efti Font', 'Efti Italic', 'Efti Robot', 'Efti Water', 'Fender', 'Flipped', 'Four Tops', 'Fraktur', 'Fuzzy', 'Georgi16', 'Georgia11', 'Ghoulish', 'Glenyn', 'Goofy', 'Gothic', 'Greek', 'Heart Left', 'Heart Right', 'Henry 3D', 'Hollywood', 'Horizontal Left', 'Horizontal Right', 'Invita', 'Italic', 'Ivrit', 'JS Block Letters', 'JS Capital Curves', 'JS Cursive', 'JS Stick Letters', 'Jacky', 'Jazmine', 'Kban', 'Keyboard', 'Knob', 'LCD', 'Larry 3D', 'Lean', 'Letters', 'Line Blocks', 'Linux', 'Lockergnome', 'Madrid', 'Marquee', 'Maxfour', 'Mini', 'Muzzle', 'NScript', 'NT Greek', 'NV Script', 'Nancyj-Fancy', 'Nancyj-Underlined', 'Nancyj', 'Nipples', 'O8', 'OS2', 'Old Banner', 'Pawp', 'Peaks', 'Pebbles', 'Poison', 'Puffy', 'Puzzle', 'Pyramid', 'Rammstein', 'Roman', 'Rounded', 'Rowan Cap', 'Rozzo', 'S Blood', 'Santa Clara', 'Script', 'Serifcap', 'Shadow', 'Shimrod', 'Short', 'Small Caps', 'Small Keyboard', 'Small Poison', 'Small Script', 'Graffiti', 'Small Shadow', 'Speed', 'Spliff', 'Stacey', 'Stampate', 'Graffiti', 'Stampatello', 'Star Strips', 'Stellar', 'Graffiti', 'Stforek', 'Stick Letters', 'Stop', 'Graffiti', 'Straight', 'Swan', 'Tanja', 'Thick', 'Thin', 'Graffiti', 'Thorned', 'Three Point', 'Tiles', 'Tinker-Toy', 'Tombstone', 'Tubular', 'Two Point', 'Univers', 'Graffiti', 'Weird', 'Whimsy']; | ||
console.log(figlet.textSync('Taiko', { | ||
font: fonts[Math.floor(Math.random() * fonts.length)], | ||
horizontalLayout: 'default', | ||
verticalLayout: 'default' | ||
}).trimRight() + '\n'); | ||
} catch (e) {} | ||
const repl = require('repl').start({ prompt: '> ', ignoreUndefined: true }); | ||
const dWrite = repl.writer; | ||
const funcs = {}; | ||
const commands = []; | ||
const stringColor = util.inspect.styles.string; | ||
const openBrowser = taiko.openBrowser; | ||
taiko.openBrowser = async (options = {}) => { | ||
if (!options.headless) options.headless = false; | ||
return await openBrowser(options); | ||
}; | ||
let lastStack = ''; | ||
for (let func in taiko) { | ||
repl.context[func] = taiko[func]; | ||
funcs.push(func); | ||
if (taiko[func].constructor.name === 'AsyncFunction') { | ||
repl.context[func] = async function(...args) { | ||
lastStack = ''; | ||
try { | ||
return await taiko[func].call(this, ...args); | ||
} catch (e) { | ||
return handleError(e); | ||
} finally { | ||
util.inspect.styles.string = stringColor; | ||
} | ||
}; | ||
} else { | ||
repl.context[func] = function(...args) { | ||
lastStack = ''; | ||
try { | ||
return taiko[func].call(this, ...args); | ||
} catch (e) { | ||
return handleError(e); | ||
} finally { | ||
util.inspect.styles.string = stringColor; | ||
} | ||
}; | ||
} | ||
funcs[func] = true; | ||
} | ||
aEval(repl); | ||
aEval(repl, (cmd, res) => { | ||
if (!util.isError(res)) commands.push(cmd.trim()); | ||
}); | ||
const handleError = (e) => { | ||
util.inspect.styles.string = 'red'; | ||
lastStack = removeQuotes(util.inspect(e.stack, { colors: true }).replace(/\\n/g, '\n'), e.stack); | ||
e.message = ' ✘ Error: ' + e.message + ', run `.trace` for more info.'; | ||
return new Error(removeQuotes(util.inspect(e.message, { colors: true }), e.message)); | ||
}; | ||
const isTaikoFunc = (keyword) => keyword.split('(')[0] in funcs; | ||
repl.defineCommand('trace', { | ||
help: 'Show last error stack trace', | ||
action() { | ||
console.log(lastStack ? lastStack : util.inspect(undefined, { colors: true })); | ||
this.displayPrompt(); | ||
} | ||
}); | ||
repl.on('reset', () => { | ||
commands.length = 0; | ||
lastStack = ''; | ||
}); | ||
repl.defineCommand('code', { | ||
help: 'Prints code for all evaluated commands in this REPL session', | ||
/*eslint-disable no-unused-vars*/ | ||
action(name) { | ||
const text = commands().map(e => '\t' + e.trim()).map(e => e.endsWith(';') ? e : e + ';').join('\n'); | ||
console.log(`const { ${funcs.join(', ')} } = require('taiko');\n\n(async => {\n${text}\n})();`); | ||
help: 'Prints or saves the code for all evaluated commands in this REPL session', | ||
action(file) { | ||
const text = commands.map(e => { | ||
if (!e.endsWith(';')) e += ';'; | ||
return isTaikoFunc(e) ? '\tawait ' + e : '\t' + e; | ||
}).join('\n'); | ||
const content = `const { ${Object.keys(funcs).join(', ')} } = require('taiko');\n\n(async () => {\n${text}\n})();`; | ||
if (!file) console.log(content); | ||
else fs.writeFileSync(file, content); | ||
this.displayPrompt(); | ||
} | ||
}); | ||
}); | ||
repl.writer = output => { | ||
if (util.isError(output)) return output.message; | ||
else if (typeof(output) === 'object' && 'description' in output) | ||
return removeQuotes(util.inspect(' ✔ ' + output.description, { colors: true }), ' ✔ ' + output.description); | ||
else return dWrite(output); | ||
}; | ||
const removeQuotes = (textWithQuotes, textWithoutQuotes) => textWithQuotes.replace(`'${textWithoutQuotes}'`, textWithoutQuotes); |
174
taiko.js
@@ -15,5 +15,6 @@ const puppeteer = require('puppeteer'); | ||
const openBrowser = async (options) => { | ||
const openBrowser = async options => { | ||
b = await puppeteer.launch(options); | ||
p = await b.newPage(); | ||
return { description: 'Browser and page initialized' }; | ||
}; | ||
@@ -25,2 +26,3 @@ | ||
b, p = null; | ||
return { description: 'Browser and page closed' }; | ||
}; | ||
@@ -31,7 +33,9 @@ | ||
await p.goto(url, options); | ||
return { description: `Navigated to url "${p.url()}"`, url: p.url() }; | ||
}; | ||
const reload = async (options) => { | ||
const reload = async options => { | ||
validate(); | ||
await p.reload(options); | ||
return { description: `"${p.url()}" reloaded`, url: p.url() }; | ||
}; | ||
@@ -45,2 +49,3 @@ | ||
if (waitForNavigation) await p.waitForNavigation(); | ||
return { description: 'Clicked ' + description(selector, true) }; | ||
}; | ||
@@ -51,2 +56,3 @@ | ||
await click(selector, waitForNavigation, Object.assign({ clickCount: 2, }, options)); | ||
return { description: 'Double clicked ' + description(selector, true) }; | ||
}; | ||
@@ -57,5 +63,6 @@ | ||
await click(selector, waitForNavigation, Object.assign({ button: 'right', }, options)); | ||
return { description: 'Right clicked ' + description(selector, true) }; | ||
}; | ||
const hover = async (selector) => { | ||
const hover = async selector => { | ||
validate(); | ||
@@ -65,7 +72,9 @@ const e = await element(selector); | ||
await e.dispose(); | ||
return { description: 'Hovered over the ' + description(selector, true) }; | ||
}; | ||
const focus = async (selector) => { | ||
const focus = async selector => { | ||
validate(); | ||
await (await _focus(selector)).dispose(); | ||
return { description: 'Focussed on the ' + description(selector, true) }; | ||
}; | ||
@@ -75,5 +84,7 @@ | ||
validate(); | ||
const e = await _focus(isString(into) ? textField(into) : into); | ||
const selector = isString(into) ? textField(into) : into; | ||
const e = await _focus(selector); | ||
await e.type(text); | ||
await e.dispose(); | ||
return { description: `Wrote ${text} in the ` + description(selector, true) }; | ||
}; | ||
@@ -83,8 +94,11 @@ | ||
validate(); | ||
let e; | ||
if (isString(to)) e = await $xpath(`//input[@type='file'][@id=(//label[contains(text(),'${to}')]/@for)]`); | ||
else if (isSelector(to)) e = await to.get(); | ||
else throw Error('Invalid element passed as paramenter'); | ||
if (isString(to)) to = { | ||
get: async () => $xpath(`//input[@type='file'][@id=(//label[contains(text(),'${to}')]/@for)]`), | ||
description: `File input field with label containing "${to}"`, | ||
}; | ||
else if (!isSelector(to)) throw Error('Invalid element passed as paramenter'); | ||
const e = await to.get(); | ||
await e.uploadFile(filepath); | ||
await e.dispose(); | ||
return { description: `Uploaded ${filepath} to the ` + description(to, true) }; | ||
}; | ||
@@ -95,12 +109,15 @@ | ||
await p.keyboard.press(key, options); | ||
return { description: `Pressed the ${key} key` }; | ||
}; | ||
const highlight = async (selector) => { | ||
const highlight = async selector => { | ||
validate(); | ||
await evaluate(selector, e => e.style.border = '0.5em solid red'); | ||
return { description: 'Highlighted the ' + description(selector, true) }; | ||
}; | ||
const scrollTo = async (selector) => { | ||
const scrollTo = async selector => { | ||
validate(); | ||
await evaluate(selector, e => e.scrollIntoViewIfNeeded()); | ||
return { description: 'Scrolled to the ' + description(selector, true) }; | ||
}; | ||
@@ -110,3 +127,3 @@ | ||
validate(); | ||
await scroll(e, px, px => window.scrollBy(px, 0), (e, px) => e.scrollLeft += px); | ||
return await scroll(e, px, px => window.scrollBy(px, 0), (e, px) => e.scrollLeft += px, 'right'); | ||
}; | ||
@@ -116,3 +133,3 @@ | ||
validate(); | ||
await scroll(e, px, px => window.scrollBy(px * -1, 0), (e, px) => e.scrollLeft -= px); | ||
return await scroll(e, px, px => window.scrollBy(px * -1, 0), (e, px) => e.scrollLeft -= px, 'left'); | ||
}; | ||
@@ -122,3 +139,3 @@ | ||
validate(); | ||
await scroll(e, px, px => window.scrollBy(0, px * -1), (e, px) => e.scrollTop -= px); | ||
return await scroll(e, px, px => window.scrollBy(0, px * -1), (e, px) => e.scrollTop -= px), 'top'; | ||
}; | ||
@@ -128,40 +145,40 @@ | ||
validate(); | ||
await scroll(e, px, px => window.scrollBy(0, px), (e, px) => e.scrollTop += px); | ||
return await scroll(e, px, px => window.scrollBy(0, px), (e, px) => e.scrollTop += px, 'down'); | ||
}; | ||
const $ = (selector) => { | ||
const $ = selector => { | ||
validate(); | ||
const get = async () => selector.startsWith('//') ? $xpath(selector) : p.$(selector); | ||
return { get: get, exists: exists(get), }; | ||
return { get: get, exists: exists(get), description: `Custom selector "$(${selector})"` }; | ||
}; | ||
const $$ = (selector) => { | ||
const $$ = selector => { | ||
validate(); | ||
const get = async () => selector.startsWith('//') ? $$xpath(selector) : p.$$(selector); | ||
return { get: get, exists: async () => (await get()).length > 0, }; | ||
return { get: get, exists: async () => (await get()).length > 0, description: `Custom selector $$(${selector})` }; | ||
}; | ||
const image = (selector) => { | ||
const image = selector => { | ||
validate(); | ||
assertType(selector); | ||
const get = async () => p.$(`img[alt='${selector}']`); | ||
return { get: get, exists: exists(get), }; | ||
return { get: get, exists: exists(get), description: `Image with "alt=${selector}"` }; | ||
}; | ||
const link = (selector) => { | ||
const link = selector => { | ||
validate(); | ||
const get = async () => getElementByTag(selector, 'a'); | ||
return { get: get, exists: exists(get), }; | ||
const get = async () => element(selector, 'a'); | ||
return { get: get, exists: exists(get), description: description(selector).replace('Element', 'Link') }; | ||
}; | ||
const listItem = (selector) => { | ||
const listItem = selector => { | ||
validate(); | ||
const get = async () => getElementByTag(selector, 'li'); | ||
return { get: get, exists: exists(get), }; | ||
const get = async () => element(selector, 'li'); | ||
return { get: get, exists: exists(get), description: description(selector).replace('Element', 'List item') }; | ||
}; | ||
const button = (selector) => { | ||
const button = selector => { | ||
validate(); | ||
const get = async () => getElementByTag(selector, 'button'); | ||
return { get: get, exists: exists(get), }; | ||
const get = async () => element(selector, 'button'); | ||
return { get: get, exists: exists(get), description: description(selector).replace('Element', 'Button') }; | ||
}; | ||
@@ -173,13 +190,23 @@ | ||
const get = async () => p.$(`input[${attribute}='${selector}']`); | ||
return { get: get, exists: exists(get), value: async () => p.evaluate(e => e.value, await get()), }; | ||
return { | ||
get: get, | ||
exists: exists(get), | ||
description: `Input field with "${attribute} = ${selector}"`, | ||
value: async () => p.evaluate(e => e.value, await get()), | ||
}; | ||
}; | ||
const textField = (selector) => { | ||
const textField = selector => { | ||
validate(); | ||
assertType(selector); | ||
const get = async () => $xpath(`//input[@type='text'][@id=(//label[contains(text(),'${selector}')]/@for)]`); | ||
return { get: get, exists: exists(get), value: async () => p.evaluate(e => e.value, await get()), }; | ||
return { | ||
get: get, | ||
exists: exists(get), | ||
description: `Text field with label containing "${selector}"`, | ||
value: async () => p.evaluate(e => e.value, await get()), | ||
}; | ||
}; | ||
const comboBox = (selector) => { | ||
const comboBox = selector => { | ||
validate(); | ||
@@ -191,2 +218,3 @@ assertType(selector); | ||
exists: exists(get), | ||
description: `Combo box with label containing "${selector}"`, | ||
select: async (value) => { | ||
@@ -199,7 +227,7 @@ const box = await get(); | ||
}, | ||
value: async () => p.evaluate(e => e.value, await get()) | ||
value: async () => p.evaluate(e => e.value, await get()), | ||
}; | ||
}; | ||
const checkBox = (selector) => { | ||
const checkBox = selector => { | ||
validate(); | ||
@@ -211,7 +239,8 @@ assertType(selector); | ||
exists: exists(get), | ||
isChecked: async () => p.evaluate(e => e.checked, await get()) | ||
description: `Checkbox with label containing "${selector}"`, | ||
isChecked: async () => p.evaluate(e => e.checked, await get()), | ||
}; | ||
}; | ||
const radioButton = (selector) => { | ||
const radioButton = selector => { | ||
validate(); | ||
@@ -223,2 +252,3 @@ assertType(selector); | ||
exists: exists(get), | ||
description: `Radio button with label containing "${selector}"`, | ||
isSelected: async () => p.evaluate(e => e.checked, await get()) | ||
@@ -228,28 +258,28 @@ }; | ||
const alert = (message, callback) => dialog('alert', message, callback); | ||
const prompt = (message, callback) => dialog('prompt', message, callback); | ||
const confirm = (message, callback) => dialog('confirm', message, callback); | ||
const beforeunload = (message, callback) => dialog('beforeunload', message, callback); | ||
const text = (text) => { | ||
const text = text => { | ||
validate(); | ||
assertType(text); | ||
const get = async (e = '*') => $xpath('//' + e + `[text()='${text}']`); | ||
return { get: get, exists: exists(get), }; | ||
return { get: get, exists: exists(get), description: `Element with text "${text}"` }; | ||
}; | ||
const contains = (text) => { | ||
const contains = text => { | ||
validate(); | ||
assertType(text); | ||
const get = async (e = '*') => $xpath('//' + e + `[contains(text(),'${text}')]`); | ||
return { get: get, exists: exists(get), }; | ||
return { get: get, exists: exists(get), description: `Element containing text "${text}"` }; | ||
}; | ||
const element = async (selector) => { | ||
const alert = (message, callback) => dialog('alert', message, callback); | ||
const prompt = (message, callback) => dialog('prompt', message, callback); | ||
const confirm = (message, callback) => dialog('confirm', message, callback); | ||
const beforeunload = (message, callback) => dialog('beforeunload', message, callback); | ||
const element = async (selector, tag) => { | ||
const e = await (() => { | ||
if (isString(selector)) return contains(selector).get(); | ||
else if (isSelector(selector)) return selector.get(); | ||
if (isString(selector)) return contains(selector).get(tag); | ||
else if (isSelector(selector)) return selector.get(tag); | ||
return null; | ||
@@ -261,9 +291,12 @@ })(); | ||
const getElementByTag = async (selector, tag) => { | ||
if (isString(selector)) return contains(selector).get(tag); | ||
else if (isSelector(selector)) return selector.get(tag); | ||
return null; | ||
const description = (selector, lowerCase = false) => { | ||
const d = (() => { | ||
if (isString(selector)) return contains(selector).description; | ||
else if (isSelector(selector)) return selector.description; | ||
return ''; | ||
})(); | ||
return lowerCase ? d.charAt(0).toLowerCase() + d.slice(1) : d; | ||
}; | ||
const _focus = async (selector) => { | ||
const _focus = async selector => { | ||
const e = await element(selector); | ||
@@ -274,5 +307,10 @@ await p.evaluate(e => e.focus(), e); | ||
const scroll = async (e, px, scrollPage, scrollElement) => { | ||
const scroll = async (e, px, scrollPage, scrollElement, direction) => { | ||
e = e || 100; | ||
await (Number.isInteger(e) ? p.evaluate(scrollPage, e) : evaluate(e, scrollElement, px)); | ||
if (Number.isInteger(e)) { | ||
await p.evaluate(scrollPage, e); | ||
return { description: `Scrolled ${direction} the page by ${px} pixels` }; | ||
} | ||
await evaluate(e, scrollElement, px); | ||
return { description: `Scrolled ${direction} ` + description(e, true) + ` by ${px} pixels` }; | ||
}; | ||
@@ -288,9 +326,9 @@ | ||
const screenshot = async (options) => p.screenshot(options); | ||
const screenshot = async options => p.screenshot(options); | ||
const isString = (obj) => typeof obj === 'string' || obj instanceof String; | ||
const isString = obj => typeof obj === 'string' || obj instanceof String; | ||
const isSelector = (obj) => obj['get'] && obj['exists']; | ||
const isSelector = obj => obj['get'] && obj['exists']; | ||
const $xpath = async (selector) => { | ||
const $xpath = async selector => { | ||
const result = await $$xpath(selector); | ||
@@ -300,3 +338,3 @@ return result.length > 0 ? result[0] : null; | ||
const $$xpath = async (selector) => { | ||
const $$xpath = async selector => { | ||
const arrayHandle = await p.mainFrame()._context.evaluateHandle(selector => { | ||
@@ -323,3 +361,3 @@ let result = document.evaluate(selector, document, null, XPathResult.ANY_TYPE, null), | ||
const validate = () => { | ||
if (!b || !p) throw new Error('Browser or Page not initialized. Call openBrowser() before using this API.'); | ||
if (!b || !p) throw new Error('Browser or page not initialized. Call `openBrowser()` before using this API'); | ||
}; | ||
@@ -331,3 +369,3 @@ | ||
const sleep = (milliseconds) => { | ||
const sleep = milliseconds => { | ||
var start = new Date().getTime(); | ||
@@ -338,3 +376,3 @@ for (var i = 0; i < 1e7; i++) | ||
const exists = (get) => { | ||
const exists = get => { | ||
return async (intervalTime = 1000, timeout = 10000) => { | ||
@@ -341,0 +379,0 @@ try { |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
25475
568
14
0
5
3
+ Addedfiglet@^1.2.0
+ Addedfiglet@1.8.0(transitive)