// ==UserScript==
// @name See CodeChum hidden test cases
// @namespace http://tampermonkey.net/
// @version 0.4
// @description See hidden test cases as if they are not hidden in codechum after you press submit
// @author Lebron Samson
// @match https://citu.codechum.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=codechum.com
// @grant none
// @license none
// ==/UserScript==
// url regex
const testCaseEndpoint = new RegExp('answers-v4\/[0-9]+\/executev2', 'g');
const changeProblemEndPoints = [
"v3/page-visits/",
"v3/answers-v4/",
"v3/answer-comments/count/"
];0
// Stylings
const style_testCaseContent = `display: grid; grid-row-gap: 16px; grid-template-column: minmax(0, 1fr); margin: 16px 0 8px;`;
const style_testCaseContent_title = `margin: 8px 0 12px;`;
const style_Text_n = `color: #b0b9bf;`;
const style_Text_heading = `font-family: Monsterrat,sans-serif; font-size: 1rem; line-height: 1.25; font-weight: 700; font-style: normal;`
const style_pre = `color: rgb(204, 204, 204); border-radius: 8px; background: rgb(45, 45, 45); font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; tab-size: 4; hyphens: none; padding: 1em; margin: 0.5em 0px; overflow: auto;`;
const style_Code_sm = `background-color: #2d3845 !important;`
const style_code_el = `color: rgb(204, 204, 204); font-size: 14px; background: none; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; tab-size: 4; hyphens: none;`;
// NEW STYLINGS
const n_style_pre = `font-size: .875rem !important; font-style: normal; border-radius: 8px; margin: 0 !important; background-color: #2d3845 !important`;
const n_style_code_h6 = `margin: 8px 0 12px !important`;
const n_style_Text_heading = `
color: #b0b9bf;
font-family: Montserrat,sans-serif;
font-size: 1rem;
line-height: 1.25;
font-weight: 700;
font-style: normal;
margin: 0;
`;
// Test Case template
const testCaseDiv = document.createElement("div");
testCaseDiv.classList.add("toBeHiddenIfClicked");
testCaseDiv.innerHTML = `
<div style="${style_testCaseContent} overflow-x: scroll;">
<div>
<h6 style="${style_testCaseContent_title}${style_Text_n}${style_Text_heading}">
Expected Output
</h6>
<div>
<pre style='${style_pre}${style_Code_sm}'><code style='${style_code_el}'>
put test cases in here
</code></pre>
</div>
</div>
</div>
`;
function getCodeDisplay(title, code){
const str =
`
<h6 style='${n_style_code_h6}${n_style_Text_heading}'>${title}</h6>
<div data-test="codeDiv">
<pre
style='color: rgb(204, 204, 204); background: rgb(45, 45, 45); font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; tab-size: 4; hyphens: none; padding: 1em; margin: 0.5em 0px; overflow: auto; ${n_style_pre}'
><code style='color: rgb(204, 204, 204); background: none; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; tab-size: 4; hyphens: none;'>${code}</code></pre>
</div>
`;
const anotherSTR = `
<div style="${style_testCaseContent} overflow-x: auto;">
<div>
<h6 style="${style_testCaseContent_title}${style_Text_n}${style_Text_heading}">
${title}
</h6>
<div>
<pre style='${style_pre}${style_Code_sm}'><code style='${style_code_el}'>${code}</code></pre>
</div>
</div>
</div>
`;
const div = document.createElement("div");
div.classList.add("toBeHiddenIfClicked");
div.innerHTML = anotherSTR;
return div;
}
function getDOM(targetCL, tag = "div"){
// fuck off obfuscation
return Array.from(document.querySelectorAll(tag)).filter(e => e.classList.length > 0).filter(e => Array.from(e.classList).some(cl => cl.includes(targetCL)));
}
function getProblemName(){
return getDOM("Text", "h4")[0].innerText;
}
function event_displayHiddenCaseOnClick(e){
// if next sibling (test case container) is hidden, then show, else hide
const tobeHidden = this.parentElement.getElementsByClassName("toBeHiddenIfClicked");
Array.from(tobeHidden).forEach(el => {
el.style.display = (el.style.display != "block") ? "block" : "none";
})
}
function event_displayCasesOnClick(e){
setTimeout(() => {
tryToLoadCases("NOTHING");
}, 100);
}
function tryToLoadCases(arg){
const currProb = getProblemName();
console.log(currProb);
// try to find the test case saved in the problem
let saved = sessionStorage.getItem("saved_test_cases");
if(!saved) return;
saved = JSON.parse(saved);
const matches = saved.filter(e => e.problem == currProb);
console.log(matches);
if(matches.length){
displayCase(matches[0].cases, matches[0].actuals);
}
}
function displayCase(cases, actuals = []){
let testCaseCont = getDOM("testCases")[0];
console.log("Testcases:", testCaseCont);
if(testCaseCont.length && testCaseCont.length == 0 || cases.length == 0) return;
try{
testCaseCont = testCaseCont.children[1];
}catch{
return;
}
// if there is a constraint, then the test case content is the second child
// testCaseCont = ((testCaseCont.length && testCaseCont.length > 1) ? testCaseCont[1] : testCaseCont[0]).children[1];
Array.from(testCaseCont.children).forEach( (div, i) => {
div = div.children[0];
console.log(div);
if(!div.children[0].disabled) return; // only modify hidden test cases
const expected = getCodeDisplay("ExpectedOutput", cases[i].trim());
const actual = getCodeDisplay("Your Output", actuals[i]?.trim());
expected.style.display = "none";
actual.style.display = "none";
// if div is not yet marked, add an event listener to the button
if(div.dataset.marked != "true"){
div.children[0].disabled = false;
div.children[0].addEventListener("click", event_displayHiddenCaseOnClick);
div.children[0].style.cursor = "pointer";
}
// set the div as marked
div.dataset.marked = "true";
div.append(actual, expected);
// div.children[1].style.display = "none";
console.log("TEST");
});
}
function saveCase(arg){
const prob = getProblemName();
let saved = sessionStorage.getItem("saved_test_cases") || [
{
problem: prob,
cases: arg.cases
}
];
if(typeof saved == "string"){
saved = JSON.parse(saved);
// check if problem is already saved, if not then save
if(!saved.some(e => e.problem == prob)) {
saved.push({
problem: prob,
cases: arg.cases,
actuals: arg.actuals
});
}
}
sessionStorage.setItem("saved_test_cases", JSON.stringify(saved));
}
var shouldLoadCases = false, flag_changeProblem = [0, 0, 0];
function receiveCases(arg){
let res;
console.log("YOU SUBMITTED");
try {
res = JSON.parse(arg.response);
}catch (err){
return;
}
if(!res.test_case_statuses) return;
const testCase = {
id: res.id,
answer_id: res.answer_id,
actuals: res.test_case_statuses.map(e => e.actual_output),
cases: res.test_case_statuses.map(e => e.test_case.output)
}
if(!this.firstRun){
this.firstRun = 1;
// the first ever submit, add event listener to test cases button and change problem button
const test_case_button = document.querySelector("#testsTab");
const change_problem_button = getDOM("navigation_nav", "div")[0].querySelectorAll(`button`);
console.log(change_problem_button);
test_case_button.addEventListener("click", event_displayCasesOnClick);
change_problem_button.forEach(e => e.addEventListener("click", function(){
shouldLoadCases = true;
}));
}
displayCase(testCase.cases, testCase.actuals);
saveCase(testCase);
}
(function() {
'use strict';
// Modify open and send requests
var open = window.XMLHttpRequest.prototype.open,
send = window.XMLHttpRequest.prototype.send;
function openReplacement(method, url, async, user, password) {
this._url = url;
return open.apply(this, arguments);
}
function sendReplacement(data) {
if(this.onreadystatechange) {
this._onreadystatechange = this.onreadystatechange;
}
// if you want to modify send requests
this.onreadystatechange = onReadyStateChangeReplacement;
return send.apply(this, arguments);
}
function onReadyStateChangeReplacement() {
// modify here received requests
// for submitting
if(testCaseEndpoint.test(this._url)) receiveCases(this);
// for changing problems
else if(changeProblemEndPoints.some(e => this._url.includes(e)) && shouldLoadCases){
flag_changeProblem[changeProblemEndPoints.findIndex(e => this._url.includes(e))]++;
if(flag_changeProblem.every(e => e == 3)){
console.log("YOU CHANGED TABS!!!!!!!!!!!");
tryToLoadCases(this);
shouldLoadCases = false;
flag_changeProblem = [0, 0, 0];
}
}
if(this._onreadystatechange) {
return this._onreadystatechange.apply(this, arguments);
}
}
window.XMLHttpRequest.prototype.open = openReplacement;
window.XMLHttpRequest.prototype.send = sendReplacement;
var request = new XMLHttpRequest();
request.open('GET', '.', true);
request.send();
})();