UNPKG

appium-remote-debugger

Version:
101 lines (92 loc) 3.46 kB
import {fs} from '@appium/support'; import path from 'node:path'; import _ from 'lodash'; import {log} from './logger'; import {getModuleRoot} from './utils'; const ATOMS_CACHE: Record<string, Buffer> = {}; /** * Converts a value to a JSON string, handling undefined values specially. * * @param obj - The value to stringify. * @returns A JSON string representation of the value, or 'undefined' if the value is undefined. */ function atomsStringify(obj: any): string { if (typeof obj === 'undefined') { return 'undefined'; } return JSON.stringify(obj); } /** * Loads an atom script from the atoms directory and caches it. * If the atom has been loaded before, returns the cached version. * * @param atomName - The name of the atom to load (without the .js extension). * @returns A promise that resolves to the atom script as a Buffer. * @throws Error if the atom file cannot be loaded. */ export async function getAtom(atomName: string): Promise<Buffer> { // check if we have already loaded and cached this atom if (!_.has(ATOMS_CACHE, atomName)) { const atomFileName = path.resolve(getModuleRoot(), 'atoms', `${atomName}.js`); try { ATOMS_CACHE[atomName] = await fs.readFile(atomFileName); } catch { throw new Error(`Unable to load Atom '${atomName}' from file '${atomFileName}'`); } } return ATOMS_CACHE[atomName]; } /** * Wraps a script to execute it within a specific frame context. * Uses the get_element_from_cache atom to access the frame element. * * @param script - The script to wrap. * @param frame - The frame identifier to execute the script in. * @returns A promise that resolves to the wrapped script string. */ async function wrapScriptForFrame(script: string, frame: string): Promise<string> { log.debug(`Wrapping script for frame '${frame}'`); const elFromCache = await getAtom('get_element_from_cache'); return ( `(function (window) { var document = window.document; ` + `return (${script}); })((${elFromCache.toString('utf8')})(${atomsStringify(frame)}))` ); } /** * Generates a complete script string for executing a Selenium atom. * Handles frame contexts and optional async callbacks. * * @param atom - The name of the atom to execute. * @param args - Arguments to pass to the atom function. Defaults to empty array. * @param frames - Array of frame identifiers to execute the atom in nested frames. * Defaults to empty array (executes in default context). * @param asyncCallBack - Optional callback function string for async execution. * If provided, the atom will be called with this callback. * @returns A promise that resolves to the complete script string ready for execution. */ export async function getScriptForAtom( atom: string, args: any[] = [], frames: string[] = [], asyncCallBack: string | null = null, ): Promise<string> { const atomSrc = (await getAtom(atom)).toString('utf8'); let script: string; if (frames.length > 0) { script = atomSrc; for (const frame of frames) { script = await wrapScriptForFrame(script, frame); } } else { log.debug(`Executing '${atom}' atom in default context`); script = `(${atomSrc})`; } // add the arguments, as strings args = args.map(atomsStringify); if (asyncCallBack) { script += `(${args.join(',')}, ${asyncCallBack}, true)`; } else { script += `(${args.join(',')})`; } return script; }