Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
constructor(config: Config.ProjectConfig, context: EnvironmentContext) {
super(config);
this._testPath = context.testPath!;
this._testName = path.basename(this._testPath).split(".")[0];
// set the name of the logger to the test
logger.setName(this._testName);
}
export const selectElement = async (
elementHandle: ElementHandle,
value: string | null,
options: FindElementOptions = {}
): Promise => {
const findOptions = getFindElementOptions(options);
logger.verbose(
`selectElement: waitForOption ${value} ${JSON.stringify(findOptions)}`
);
// ensure option with desired value is loaded before selecting
await elementHandle.evaluate(
(element: HTMLSelectElement, value: string | null, timeoutMs: number) => {
const qawolf: QAWolfWeb = (window as any).qawolf;
return qawolf.select.waitForOption(element, value, timeoutMs);
},
value,
findOptions.timeoutMs || 0
);
logger.verbose("selectElement: element.select");
await elementHandle.select(value || "");
};
public static async start(options: ScreenCaptureStartOptions) {
if (!CONFIG.display) {
logger.error(
"ScreenCapture: cannot start if no xvfb display is specified (process.env.DISPLAY)"
);
return null;
}
// ffmpeg requires dimensions to be divisible by 2
options.size.height = makeEven(options.size.height);
options.size.width = makeEven(options.size.width);
logger.verbose(`ScreenCapture: start ${JSON.stringify(options)}`);
const path = resolve(options.savePath);
await ensureDir(path);
const screenCapture = new ScreenCapture(options);
return screenCapture;
}
logger.verbose(
`selectElement: waitForOption ${value} ${JSON.stringify(findOptions)}`
);
// ensure option with desired value is loaded before selecting
await elementHandle.evaluate(
(element: HTMLSelectElement, value: string | null, timeoutMs: number) => {
const qawolf: QAWolfWeb = (window as any).qawolf;
return qawolf.select.waitForOption(element, value, timeoutMs);
},
value,
findOptions.timeoutMs || 0
);
logger.verbose("selectElement: element.select");
await elementHandle.select(value || "");
};
export const findText = async (
page: Page,
selector: Selector,
options: FindElementOptions = {}
): Promise> => {
logger.verbose("findText");
const jsHandle = await page.evaluateHandle(
(selector, options) => {
const qawolf: QAWolfWeb = (window as any).qawolf;
const findCmd = () => qawolf.find.findText(selector, options);
// store the last find on the window for easy debugging
(window as any).qaw_find = findCmd;
return findCmd();
},
selector as Serializable,
options as Serializable
);
const element = jsHandle.asElement();
if (!element) throw new Error("Element not found");
export const createDomReplayer = async (page: Page, path: string) => {
logger.debug(
`Page: create dom replayer for ${page.qawolf.domEvents.length} events: ${path}`
);
if (!page.qawolf.domEvents.length) return;
// cycle event loop to ensure we get all events
try {
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 0)));
} catch (e) {
// ignore errors because the page could already be disposed
}
const replayer = replayerTemplate({
eventsJson: JSON.stringify(page.qawolf.domEvents).replace(
/<\/script>/g,
"<\\/script>"
),
const strokes = valueToStrokes(value);
if (strokes[0].value === "Enter" || strokes[0].value === "Tab") {
// do not clear the element if the first character is Enter or Tab
await element.focus();
} else {
await focusClearElement(element);
}
// logging the keyboard codes below will leak secrets
// which is why we have it hidden behind the DEBUG flag
// since we default logs to VERBOSE
for (const stroke of strokes) {
if (stroke.type === "↓") {
logger.debug(`keyboard.down("${stroke.value}")`);
await page.keyboard.down(stroke.value);
} else if (stroke.type === "↑") {
logger.debug(`keyboard.up("${stroke.value}")`);
await page.keyboard.up(stroke.value);
} else if (stroke.type === "→") {
logger.debug(`keyboard.sendCharacter("${stroke.value}")`);
await page.keyboard.sendCharacter(stroke.value);
}
await sleep(CONFIG.keyDelayMs);
}
};
export const serializeKeyEvents = (events: KeyEvent[]): string => {
/**
* Serialize key events as character strokes or key strokes.
*/
const hasNotUSKey = events.some(e => !isUSKey(e.value));
if (hasNotUSKey) {
logger.debug("serializeKeyEvents: not US key found");
// Serialize as character strokes since it was not recorded
// with a US keyboard so we cannot properly reproduce strokes.
// This will sendCharacter for each keydown and skip special keys.
// Enter & Tab are separated into another step in buildTypeSteps
// so they will get serialized properly as key strokes below.
return serializeCharacterStrokes(events);
}
// Serialize key strokes if there is a
// - special key (ignoring shift)
// - held key (ignoring shift)
const keyEventsExceptShift = events.filter(e => !e.value.includes("Shift"));
const specialKey = keyEventsExceptShift.find(e => isSpecialKey(e.value));
const hasHeldKey = isKeyHeld(keyEventsExceptShift);
logger.debug(
`serializeKeyEvents: specialKey ${specialKey &&
export const type = async (page: Page, value: string): Promise => {
logger.verbose("actions.type");
// logging the keyboard codes below will leak secrets
// which is why we have it hidden behind the DEBUG flag
// since we default logs to VERBOSE
for (const stroke of valueToStrokes(value)) {
if (stroke.type === "↓") {
logger.debug(`keyboard.down("${stroke.value}")`);
await page.keyboard.down(stroke.value);
} else if (stroke.type === "↑") {
logger.debug(`keyboard.up("${stroke.value}")`);
await page.keyboard.up(stroke.value);
} else if (stroke.type === "→") {
logger.debug(`keyboard.sendCharacter("${stroke.value}")`);
await page.keyboard.sendCharacter(stroke.value);
}
await sleep(CONFIG.keyDelayMs);
}
};
): boolean => {
/**
* Check if any key is held down for > threshold.
*/
for (let i = 0; i < events.length - 1; i++) {
const keydownEvent = events[i];
if (keydownEvent.name !== "keydown") continue;
// find the matching keyup event
const keyupEvent = events.find(
(e, j) => j > i && e.name === "keyup" && e.value === keydownEvent.value
);
const heldTime = keyupEvent ? keyupEvent.time - keydownEvent.time : 0;
if (heldTime > thresholdMs) {
logger.debug(`${keydownEvent.value} was held for ${heldTime}`);
return true;
}
}
return false;
};