See CodeChum hidden test cases

See hidden test cases as if they are not hidden in codechum after you press submit

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==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();

})();