See CodeChum hidden test cases

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴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();

})();