See CodeChum hidden test cases

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

질문, 리뷰하거나, 이 스크립트를 신고하세요.
// ==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();

})();