See CodeChum hidden test cases

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

  1. // ==UserScript==
  2. // @name See CodeChum hidden test cases
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.4
  5. // @description See hidden test cases as if they are not hidden in codechum after you press submit
  6. // @author Lebron Samson
  7. // @match https://citu.codechum.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=codechum.com
  9. // @grant none
  10. // @license none
  11. // ==/UserScript==
  12.  
  13. // url regex
  14. const testCaseEndpoint = new RegExp('answers-v4\/[0-9]+\/executev2', 'g');
  15. const changeProblemEndPoints = [
  16. "v3/page-visits/",
  17. "v3/answers-v4/",
  18. "v3/answer-comments/count/"
  19. ];0
  20.  
  21. // Stylings
  22. const style_testCaseContent = `display: grid; grid-row-gap: 16px; grid-template-column: minmax(0, 1fr); margin: 16px 0 8px;`;
  23. const style_testCaseContent_title = `margin: 8px 0 12px;`;
  24. const style_Text_n = `color: #b0b9bf;`;
  25. const style_Text_heading = `font-family: Monsterrat,sans-serif; font-size: 1rem; line-height: 1.25; font-weight: 700; font-style: normal;`
  26. 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;`;
  27. const style_Code_sm = `background-color: #2d3845 !important;`
  28. 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;`;
  29.  
  30. // NEW STYLINGS
  31. const n_style_pre = `font-size: .875rem !important; font-style: normal; border-radius: 8px; margin: 0 !important; background-color: #2d3845 !important`;
  32. const n_style_code_h6 = `margin: 8px 0 12px !important`;
  33. const n_style_Text_heading = `
  34. color: #b0b9bf;
  35. font-family: Montserrat,sans-serif;
  36. font-size: 1rem;
  37. line-height: 1.25;
  38. font-weight: 700;
  39. font-style: normal;
  40. margin: 0;
  41. `;
  42.  
  43. // Test Case template
  44. const testCaseDiv = document.createElement("div");
  45. testCaseDiv.classList.add("toBeHiddenIfClicked");
  46. testCaseDiv.innerHTML = `
  47. <div style="${style_testCaseContent} overflow-x: scroll;">
  48. <div>
  49. <h6 style="${style_testCaseContent_title}${style_Text_n}${style_Text_heading}">
  50. Expected Output
  51. </h6>
  52. <div>
  53. <pre style='${style_pre}${style_Code_sm}'><code style='${style_code_el}'>
  54. put test cases in here
  55. </code></pre>
  56. </div>
  57. </div>
  58. </div>
  59. `;
  60.  
  61. function getCodeDisplay(title, code){
  62. const str =
  63. `
  64. <h6 style='${n_style_code_h6}${n_style_Text_heading}'>${title}</h6>
  65. <div data-test="codeDiv">
  66. <pre
  67. 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}'
  68. ><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>
  69. </div>
  70. `;
  71.  
  72. const anotherSTR = `
  73. <div style="${style_testCaseContent} overflow-x: auto;">
  74. <div>
  75. <h6 style="${style_testCaseContent_title}${style_Text_n}${style_Text_heading}">
  76. ${title}
  77. </h6>
  78. <div>
  79. <pre style='${style_pre}${style_Code_sm}'><code style='${style_code_el}'>${code}</code></pre>
  80. </div>
  81. </div>
  82. </div>
  83. `;
  84.  
  85. const div = document.createElement("div");
  86. div.classList.add("toBeHiddenIfClicked");
  87. div.innerHTML = anotherSTR;
  88. return div;
  89. }
  90.  
  91. function getDOM(targetCL, tag = "div"){
  92. // fuck off obfuscation
  93. return Array.from(document.querySelectorAll(tag)).filter(e => e.classList.length > 0).filter(e => Array.from(e.classList).some(cl => cl.includes(targetCL)));
  94. }
  95.  
  96. function getProblemName(){
  97. return getDOM("Text", "h4")[0].innerText;
  98. }
  99.  
  100. function event_displayHiddenCaseOnClick(e){
  101. // if next sibling (test case container) is hidden, then show, else hide
  102. const tobeHidden = this.parentElement.getElementsByClassName("toBeHiddenIfClicked");
  103. Array.from(tobeHidden).forEach(el => {
  104. el.style.display = (el.style.display != "block") ? "block" : "none";
  105. })
  106. }
  107.  
  108. function event_displayCasesOnClick(e){
  109. setTimeout(() => {
  110. tryToLoadCases("NOTHING");
  111. }, 100);
  112. }
  113.  
  114. function tryToLoadCases(arg){
  115. const currProb = getProblemName();
  116.  
  117. console.log(currProb);
  118.  
  119. // try to find the test case saved in the problem
  120. let saved = sessionStorage.getItem("saved_test_cases");
  121. if(!saved) return;
  122.  
  123. saved = JSON.parse(saved);
  124.  
  125. const matches = saved.filter(e => e.problem == currProb);
  126.  
  127. console.log(matches);
  128.  
  129. if(matches.length){
  130. displayCase(matches[0].cases, matches[0].actuals);
  131. }
  132. }
  133.  
  134. function displayCase(cases, actuals = []){
  135.  
  136. let testCaseCont = getDOM("testCases")[0];
  137.  
  138. console.log("Testcases:", testCaseCont);
  139.  
  140. if(testCaseCont.length && testCaseCont.length == 0 || cases.length == 0) return;
  141.  
  142. try{
  143. testCaseCont = testCaseCont.children[1];
  144. }catch{
  145. return;
  146. }
  147. // if there is a constraint, then the test case content is the second child
  148. // testCaseCont = ((testCaseCont.length && testCaseCont.length > 1) ? testCaseCont[1] : testCaseCont[0]).children[1];
  149.  
  150. Array.from(testCaseCont.children).forEach( (div, i) => {
  151. div = div.children[0];
  152. console.log(div);
  153. if(!div.children[0].disabled) return; // only modify hidden test cases
  154.  
  155. const expected = getCodeDisplay("ExpectedOutput", cases[i].trim());
  156. const actual = getCodeDisplay("Your Output", actuals[i]?.trim());
  157.  
  158. expected.style.display = "none";
  159. actual.style.display = "none";
  160.  
  161. // if div is not yet marked, add an event listener to the button
  162. if(div.dataset.marked != "true"){
  163. div.children[0].disabled = false;
  164. div.children[0].addEventListener("click", event_displayHiddenCaseOnClick);
  165. div.children[0].style.cursor = "pointer";
  166. }
  167.  
  168. // set the div as marked
  169. div.dataset.marked = "true";
  170.  
  171. div.append(actual, expected);
  172. // div.children[1].style.display = "none";
  173.  
  174. console.log("TEST");
  175. });
  176. }
  177.  
  178. function saveCase(arg){
  179. const prob = getProblemName();
  180.  
  181. let saved = sessionStorage.getItem("saved_test_cases") || [
  182. {
  183. problem: prob,
  184. cases: arg.cases
  185. }
  186. ];
  187.  
  188. if(typeof saved == "string"){
  189. saved = JSON.parse(saved);
  190.  
  191. // check if problem is already saved, if not then save
  192. if(!saved.some(e => e.problem == prob)) {
  193. saved.push({
  194. problem: prob,
  195. cases: arg.cases,
  196. actuals: arg.actuals
  197. });
  198. }
  199. }
  200.  
  201. sessionStorage.setItem("saved_test_cases", JSON.stringify(saved));
  202. }
  203.  
  204. var shouldLoadCases = false, flag_changeProblem = [0, 0, 0];
  205.  
  206. function receiveCases(arg){
  207. let res;
  208.  
  209. console.log("YOU SUBMITTED");
  210.  
  211. try {
  212. res = JSON.parse(arg.response);
  213. }catch (err){
  214. return;
  215. }
  216.  
  217. if(!res.test_case_statuses) return;
  218.  
  219. const testCase = {
  220. id: res.id,
  221. answer_id: res.answer_id,
  222. actuals: res.test_case_statuses.map(e => e.actual_output),
  223. cases: res.test_case_statuses.map(e => e.test_case.output)
  224. }
  225.  
  226. if(!this.firstRun){
  227. this.firstRun = 1;
  228. // the first ever submit, add event listener to test cases button and change problem button
  229.  
  230. const test_case_button = document.querySelector("#testsTab");
  231. const change_problem_button = getDOM("navigation_nav", "div")[0].querySelectorAll(`button`);
  232.  
  233. console.log(change_problem_button);
  234.  
  235. test_case_button.addEventListener("click", event_displayCasesOnClick);
  236. change_problem_button.forEach(e => e.addEventListener("click", function(){
  237. shouldLoadCases = true;
  238. }));
  239. }
  240.  
  241. displayCase(testCase.cases, testCase.actuals);
  242. saveCase(testCase);
  243. }
  244.  
  245. (function() {
  246. 'use strict';
  247.  
  248. // Modify open and send requests
  249.  
  250. var open = window.XMLHttpRequest.prototype.open,
  251. send = window.XMLHttpRequest.prototype.send;
  252.  
  253. function openReplacement(method, url, async, user, password) {
  254. this._url = url;
  255. return open.apply(this, arguments);
  256. }
  257.  
  258. function sendReplacement(data) {
  259. if(this.onreadystatechange) {
  260. this._onreadystatechange = this.onreadystatechange;
  261. }
  262.  
  263. // if you want to modify send requests
  264.  
  265. this.onreadystatechange = onReadyStateChangeReplacement;
  266. return send.apply(this, arguments);
  267. }
  268.  
  269. function onReadyStateChangeReplacement() {
  270.  
  271. // modify here received requests
  272.  
  273. // for submitting
  274. if(testCaseEndpoint.test(this._url)) receiveCases(this);
  275.  
  276. // for changing problems
  277. else if(changeProblemEndPoints.some(e => this._url.includes(e)) && shouldLoadCases){
  278. flag_changeProblem[changeProblemEndPoints.findIndex(e => this._url.includes(e))]++;
  279.  
  280. if(flag_changeProblem.every(e => e == 3)){
  281. console.log("YOU CHANGED TABS!!!!!!!!!!!");
  282. tryToLoadCases(this);
  283. shouldLoadCases = false;
  284. flag_changeProblem = [0, 0, 0];
  285. }
  286. }
  287.  
  288. if(this._onreadystatechange) {
  289. return this._onreadystatechange.apply(this, arguments);
  290. }
  291. }
  292.  
  293. window.XMLHttpRequest.prototype.open = openReplacement;
  294. window.XMLHttpRequest.prototype.send = sendReplacement;
  295.  
  296. var request = new XMLHttpRequest();
  297. request.open('GET', '.', true);
  298. request.send();
  299.  
  300. })();