Skip to content

Instantly share code, notes, and snippets.

@alexcmgit
Last active February 14, 2022 21:48
Show Gist options
  • Save alexcmgit/6f2adc8a73e27fef73f99f42258d7912 to your computer and use it in GitHub Desktop.
Save alexcmgit/6f2adc8a73e27fef73f99f42258d7912 to your computer and use it in GitHub Desktop.
Too lazy do find JS selectors and implement testing code, so lets make the browser my friend
/**
* Generate unique CSS selector from document [HTMLElement]
*/
function generateCssSelector(element) {
let path = [],
parent;
while ((parent = element.parentNode)) {
path.unshift(
`${element.tagName}:nth-child(${
[].indexOf.call(parent.children, element) + 1
})`
);
element = parent;
}
return `${path.join(" > ")}`.toLowerCase();
}
/*
* Start and end record by typing these commands on the console
*
* Also I can see myself putting these commands in a separated extension
* to don't even need to type it, just click on buttons
*/
let recording = false;
window.stoprecording = () => (recording = false);
window.startrecording = () => (recording = true);
let pressed = [];
/**
* Add global click and input event listener to find the test "path"
* I mean, the E2E is just a bot trying to use the app in
* a deterministic and linear path, but you need to provide
* the selectors to cypress in order to do that
* so here I'll "record" every click that I'll do in the browser
* to generate a linear path about what the bot needs to click and type
*/
let path = [];
function combine(e, code, f, o) {
if (e.code === code) f(e);
else o();
}
function addGlobalEventListeners() {
document.onkeydown = (e) => {
if (e.code === "Backspace") {
const last = path[path.length - 1];
if (last && last.event === "input") {
const entry = {
...last,
value: last.value.substring(0, last.value.length - 1),
};
console.log(
`Deleting a current INPUT entry: ${obj(entry)} at index: ${
path.length - 1
}`
);
path[path.length - 1] = entry;
}
return;
}
const control = pressed.some((p) => p.code === "ControlLeft");
if (e.code === "ShiftLeft" && control) {
console.log(
`Now, ${
(recording = !recording)
? "we are recording"
: "we aren NOT recording"
}`
);
return;
}
if (e.code === "AltLeft" && control && !recording) {
genCode();
clearpath();
return;
}
pressed.push(e);
};
document.onkeyup = (e) =>
(pressed = pressed.filter((p) => p.code !== e.code));
document.onclick = function (e) {
if (!recording) return;
const entry = {
event: "click",
element: e.target,
selector: generateCssSelector(e.target),
};
console.log(`Pushing a new CLICK entry: ${obj(entry)}`);
path.push(entry);
};
document.oninput = function (e) {
if (!recording || !e.data) return;
let last = path[path.length - 1];
if (last && last.event === "input" && !!e.data) {
const entry = { ...last, value: e.target.value };
console.log(
`Editing a current INPUT entry: ${obj(entry)} at index: ${
path.length - 1
}`
);
path[path.length - 1] = entry;
} else {
const entry = {
event: "input",
element: e.target,
selector: generateCssSelector(e.target),
value: e.target.value,
};
console.log(`Pushing a new INPUT entry: ${obj(entry)}`);
path.push(entry);
}
};
}
function obj(e) {
return JSON.stringify({ event: e.event, value: e.value }, null, 2);
}
function printPath() {
console.log(JSON.stringify(path, null, 2));
}
function clearpath() {
path = [];
}
function genCode() {
const code = [];
for (let i = 0; !!path[i]; i += 2) {
const [e, n] = [path[i], path[i + 1]];
if (!!e && !!n) {
if (e.event === "click" && n.event === "input") {
code.push(`cy.get("${e.selector}").type("${n.value}");`);
}
} else {
const t = e ?? n;
if (t?.event === "click") {
code.push(`cy.get("${e.selector}").click();`);
} else if (t.event === "input") {
/// ...
}
}
}
console.log(code.join("\n\n"));
}
addGlobalEventListeners();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment