UNPKG

bowow

Version:

A micro browser automation tool

178 lines (153 loc) 5.04 kB
/* global jQuery, wait$ */ const util = require('util') const debug = require('debug')('bowow:QueryProxy') const jQueryProperties = ['length', 'jquery', 'selector'] module.exports = function QueryProxy(selector, session, timeout = 30000) { debug("Creating QueryProxy for selector", selector) const proxyID = 'proxy' + Math.floor(Math.random() * 1000000) if (timeout >= 0) { let waited = 0 let found = false do { found = session.execute(selector => jQuery(selector).length > 0, selector) if (found) break session.execute(() => new Promise(resolve => setTimeout(resolve, 250))) waited += 250 } while (waited < timeout) if (!found) { throw new Error('unable to find element: ' + selector) } } session.execute( ({ proxyID, selector }) => (window[proxyID] = jQuery(selector)), { proxyID, selector } ) const currentXPath = () => { const xpath = session.execute( '({proxyID}) => window[proxyID] ? getElementXPath(window[proxyID].get(0)) : null', { proxyID } ) if (!xpath) throw new Error(`Unable to compute xpath (${selector})`) return xpath } const proxy = new Proxy( {}, { get(target, prop) { if (prop === 'then') return undefined if (prop === 'inspect') return undefined if (prop === util.inspect.custom) return undefined if (jQueryProperties.includes(prop)) return session.execute(({ proxyID, prop }) => window[proxyID][prop], { proxyID, prop }) if (prop === 'type') return (...args) => { const opts = typeof args[args.length - 1] === 'object' ? args[args.length - 1] : {} const skipClear = opts.clear === false if (!skipClear) { let start = Date.now() let cleared = false do { const result = session.clear(currentXPath()) cleared = !result.status } while (!cleared && Date.now() - start < 30000) if (!cleared) { throw new Error('Timed out waiting for element to editable') } } session.sendKeys(currentXPath(), ...args) return proxy } if (prop === 'click') return (...args) => { const start = Date.now() let clicked = false do { try { session.click(currentXPath(), ...args) clicked = true } catch (err) { debug(err) session.execute( () => new Promise(resolve => setTimeout(resolve, 1000)) ) // synchronous sleep } } while (!clicked && Date.now() - start < 30000) if (!clicked) throw new Error('element not found') return proxy } if (prop === 'filter') { return (...args) => { let result if (typeof args[0] === 'string') { result = session.execute( `({ proxyID }) => { window[proxyID] = window[proxyID].filter(function() { return $(this).text().trim() === ${JSON.stringify(args[0])} }) return '$CHAIN$' }`, { proxyID } ) } else if (args[0] instanceof RegExp) { result = session.execute( `({ proxyID }) => { window[proxyID] = window[proxyID].filter(function() { return $(this).text().match(${args[0].toString()}) }) return '$CHAIN$' }`, { proxyID } ) } else { result = session.execute( `({ proxyID }) => { let result = (window[proxyID] = window[proxyID].filter(${toArgs( args )})) return (result && result.jquery) ? '$CHAIN$' : result }`, { proxyID } ) } return result === '$CHAIN$' ? proxy : result } } return (...args) => { let result = session.execute( `({ proxyID, prop }) => { let result = window[proxyID][prop](${toArgs(args)}) if (result && result.jquery) { window[proxyID] = result return '$CHAIN$' } return result }`, { proxyID, prop } ) return result === '$CHAIN$' ? proxy : result } } } ) return proxy } function toArgs(args) { return args.map(toArg).join(',') } const argHandlers = { function: a => a.toString(), string: a => JSON.stringify(a), object: a => JSON.stringify(a) } function toArg(a) { return (argHandlers[typeof a] || (a => a))(a) }