Skip to content

Instantly share code, notes, and snippets.

@elijahr
Last active July 11, 2024 20:16
Show Gist options
  • Save elijahr/61ed5402375118a5f15f2ee96a0adfa6 to your computer and use it in GitHub Desktop.
Save elijahr/61ed5402375118a5f15f2ee96a0adfa6 to your computer and use it in GitHub Desktop.
Bookmarklet to parse `py.test` failures from a CircleCI build page and generate a command to re-run those tests locally.

parseTestFailures.js

A bookmarklet to parse py.test failures from a CircleCI build page and generate a command to re-run those tests locally.

Installation

Create a bookmark with the following URL:

javascript:(function()%7Bfunction%20requireJQuery(fn)%20%7B%0A%20%20if%20(window.jQuery)%20%7B%0A%20%20%20%20fn()%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20console.log(%22No%20jQuery%20available%20yet%2C%20retrying%20in%201s%22)%3B%0A%20%20%20%20if%20(!document.getElementById(%22jquery-script%22))%20%7B%0A%20%20%20%20%20%20var%20script%20%3D%20document.createElement(%22script%22)%3B%0A%20%20%20%20%20%20script.setAttribute(%22id%22%2C%20%22jquery-script%22)%3B%0A%20%20%20%20%20%20script.setAttribute(%22src%22%2C%20%22%2F%2Fcode.jquery.com%2Fjquery.js%22)%3B%0A%20%20%20%20%20%20document.getElementsByTagName(%22body%22)%5B0%5D.appendChild(script)%3B%0A%20%20%20%20%7D%0A%20%20%20%20setTimeout(requireJQuery.bind(this%2C%20fn)%2C%201000)%3B%0A%20%20%7D%0A%7D%0A%0Afunction%20parseTestFailures()%20%7B%0A%20%20var%20%24%20%3D%20window.jQuery%3B%0A%20%20var%20tests%20%3D%20new%20Set()%3B%0A%0A%20%20function%20printCommand(chunkSize)%7B%0A%20%20%20%20var%20t%20%3D%20Array.from(tests)%3B%0A%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20t.length%3B%20i%20%2B%3D%20chunkSize)%20%7B%0A%20%20%20%20%20%20%20%20let%20chunk%20%3D%20t.slice(i%2C%20i%20%2B%20chunkSize)%3B%0A%20%20%20%20%20%20%20%20chunk%20%3D%20chunk.map((value)%20%3D%3E%20%22'%22%20%2B%20value%20%2B%20%22'%22)%3B%0A%20%20%20%20%20%20%20%20var%20command%20%3D%20%22tt%20%22%20%2B%20Array.from(chunk).join(%22%20%22)%20%2B%20%22%20--update-queries%20--lf%20-x%22%3B%0A%20%20%20%20%20%20%20%20console.log(command)%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%24(%22%5Bdata-testid%3Dtest-result-title%5D%22).each(function()%20%7B%0A%20%20%20%20var%20title%20%3D%20%24(this).text()%3B%0A%20%20%20%20var%20mod%20%3D%20%24(this).parent().parent().children().eq(-1).text()%3B%0A%20%20%20%20if%20(mod.toLowerCase()%20%3D%3D%20mod)%20%7B%0A%20%20%20%20%20%20%2F%2F%20A%20standalone%20function%20based%20test%0A%20%20%20%20%20%20var%20modPath%20%3D%20mod.split(%22.%22).join(%22%2F%22)%20%2B%20%22.py%22%3B%0A%20%20%20%20%20%20tests.add(modPath%20%2B%20%22%3A%3A%22%20%2B%20title)%3B%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%2F%2F%20A%20class%20based%20TestCase%0A%20%20%20%20%20%20var%20modPath%20%3D%20mod.split(%22.%22).slice(0%2C%20-1).join(%22%2F%22)%20%2B%20%22.py%22%3B%0A%20%20%20%20%20%20var%20className%20%3D%20mod.split(%22.%22).slice(-1)%5B0%5D%3B%0A%20%20%20%20%20%20tests.add(modPath%20%2B%20%22%3A%3A%22%20%2B%20className%20%2B%20%22%3A%3A%22%20%2B%20title)%3B%0A%20%20%20%20%7D%0A%20%20%7D)%3B%0A%20%20printCommand(100)%3B%0A%7D%0A%0ArequireJQuery(parseTestFailures)%3B%7D)()%3B

Usage

Visit a CircleCI build page representing a py.test run. Click the bookmark. An alert box will display with content such as the following:

tt 'styleseat/autocharge/tests.py::FindAutochargeAppointmentsTestCase::test_not_in_appointments_to_checkout_if_probe_expired'"

If the text is clipped by an ellipsis due to being too large, you can find the full command in the JS console of the page.

tt is a shell alias you can add to your shell startup script:

alias tt="./test.sh --no-wait --debug -- -s"
function requireJQuery(fn) {
if (window.jQuery) {
fn();
} else {
console.log("No jQuery available yet, retrying in 1s");
if (!document.getElementById("jquery-script")) {
var script = document.createElement("script");
script.setAttribute("id", "jquery-script");
script.setAttribute("src", "//code.jquery.com/jquery.js");
document.getElementsByTagName("body")[0].appendChild(script);
}
setTimeout(requireJQuery.bind(this, fn), 1000);
}
}
function parseTestFailures() {
var $ = window.jQuery;
var tests = new Set();
function printCommand(chunkSize){
var t = Array.from(tests);
for (let i = 0; i < t.length; i += chunkSize) {
let chunk = t.slice(i, i + chunkSize);
chunk = chunk.map((value) => "'" + value + "'");
var command = "tt " + Array.from(chunk).join(" ") + " --update-queries --lf -x";
console.log(command);
}
}
$("[data-testid=test-result-title]").each(function() {
var title = $(this).text();
var mod = $(this).parent().parent().children().eq(-1).text();
if (mod.toLowerCase() == mod) {
// A standalone function based test
var modPath = mod.split(".").join("/") + ".py";
tests.add(modPath + "::" + title);
} else {
// A class based TestCase
var modPath = mod.split(".").slice(0, -1).join("/") + ".py";
var className = mod.split(".").slice(-1)[0];
tests.add(modPath + "::" + className + "::" + title);
}
});
printCommand(100);
}
requireJQuery(parseTestFailures);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment