Greasy Fork is available in English.

leetcode-example-output-checker

Check if "Run Code" result is correct when "Use Example Testcases" during LeetCode contests.

Version vom 28.05.2022. Aktuellste Version

  1. // ==UserScript==
  2. // @name leetcode-example-output-checker
  3. // @namespace iilj
  4. // @version 1.0.0
  5. // @description Check if "Run Code" result is correct when "Use Example Testcases" during LeetCode contests.
  6. // @author iilj
  7. // @license MIT
  8. // @supportURL https://github.com/iilj/leetcode-example-output-checker/issues
  9. // @match https://leetcode.com/contest/*/problems/*/
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=leetcode.com
  11. // @grant none
  12. // ==/UserScript==
  13. var label_ac = "<span class=\"text-success\">&nbsp;&rarr;&nbsp;<span class=\"label label-success\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Accepted\">AC</span><br></span>";
  14.  
  15. var label_wa = "<span class=\"text-danger\">&nbsp;&rarr;&nbsp;<span class=\"label label-warning\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Wrong Answer\">WA</span><br></span>";
  16.  
  17. (() => {
  18. // サンプルの出力をかき集める
  19. console.log('[LEOC] contest problem page');
  20. const labels = document.querySelectorAll('div.question-content pre strong');
  21. // サンプル出力の個数を検出する
  22. const outputs = [];
  23. labels.forEach((strong) => {
  24. if (strong.textContent === null ||
  25. (strong.textContent.trim() !== 'Output:' && strong.textContent.trim() !== 'Output') ||
  26. strong.nextSibling === null ||
  27. strong.nextSibling.textContent === null) {
  28. return;
  29. }
  30. if (strong.textContent.trim() === 'Output:') {
  31. // 通常
  32. const output = strong.nextSibling.textContent.trim();
  33. outputs.push(output);
  34. }
  35. else {
  36. // [null, [0, 0], [], true, false] とかになっている.スペースを除去する.
  37. const output = strong.nextSibling.textContent.trim().split(' ').join('');
  38. outputs.push(output);
  39. }
  40. });
  41. console.log('[LEOC] outputs', outputs);
  42. window.addEventListener('load', () => {
  43. const onload = () => {
  44. // 提出結果 div が出るまで若干待つ
  45. const result_div = document.querySelector('div.submission-result-base');
  46. if (result_div === null) {
  47. console.warn('[LEOC] result_div not found. Retrying.');
  48. window.setTimeout(onload, 2000);
  49. return;
  50. }
  51. const observer = new MutationObserver(() => {
  52. var _a, _b;
  53. // 提出結果の更新が始まったときに走る
  54. /** DOMの変化が起こった時の処理 */
  55. console.log('[LEOC] observer callback triggered.');
  56. const answer_labels = result_div.querySelectorAll('h5');
  57. const yourAnswerPre = Array.from(answer_labels).reduce((prev, h5) => {
  58. var _a;
  59. if (((_a = h5.textContent) === null || _a === void 0 ? void 0 : _a.trim()) !== 'Your answer') {
  60. return prev;
  61. }
  62. return h5.nextSibling;
  63. }, null);
  64. if (yourAnswerPre === null) {
  65. console.warn('[LEOC] Answer <pre> not found.');
  66. return;
  67. }
  68. const yourInputPre = Array.from(answer_labels).reduce((prev, h5) => {
  69. var _a;
  70. if (((_a = h5.textContent) === null || _a === void 0 ? void 0 : _a.trim()) !== 'Your input') {
  71. return prev;
  72. }
  73. return h5.nextSibling;
  74. }, null);
  75. if (yourInputPre === null) {
  76. console.warn('[LEOC] Input <pre> not found.');
  77. return;
  78. }
  79. console.log(`[LEOC] answer status: ${(_b = (_a = yourAnswerPre.textContent) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : ''}`); // Pending
  80. const observer2 = new MutationObserver(() => {
  81. var _a, _b, _c, _d;
  82. // Pending から別の内容に切り替わったときに走る
  83. console.log(`[LEOC] observer2 callback triggered. status: ${(_b = (_a = yourAnswerPre.textContent) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : ''}`);
  84. // pre の中に span があり,各 span が行になっている.
  85. // br は textContent では消えるので,span を走査して答えを集める
  86. const answerSpans = Array.from(yourAnswerPre.querySelectorAll('span'));
  87. console.log('[LEOC] answerSpans', answerSpans);
  88. // 出力の個数が一致しないなら,やめる
  89. if (outputs.length !== answerSpans.length)
  90. return;
  91. // 入力がサンプルと同一かどうか
  92. if (((_c = yourInputPre.textContent) === null || _c === void 0 ? void 0 : _c.trim()) !== pageData.questionExampleTestcases.trim()) {
  93. return;
  94. }
  95. console.log('[LEOC] Example testcase execution detected');
  96. // 監視を停止する
  97. observer2.disconnect();
  98. // AC かどうか判定していく
  99. for (let i = 0; i < answerSpans.length; ++i) {
  100. const yourAnswer = (_d = answerSpans[i].textContent) === null || _d === void 0 ? void 0 : _d.trim();
  101. const expectedAnswer = outputs[i];
  102. console.log(`[LEOC] yourAnswer: ${yourAnswer !== null && yourAnswer !== void 0 ? yourAnswer : ''}, expected: ${expectedAnswer}`);
  103. if (yourAnswer === expectedAnswer) {
  104. answerSpans[i].insertAdjacentHTML('beforeend', label_ac);
  105. }
  106. else {
  107. answerSpans[i].insertAdjacentHTML('beforeend', label_wa);
  108. }
  109. }
  110. });
  111. const config2 = {
  112. attributes: true,
  113. childList: true,
  114. characterData: true,
  115. };
  116. observer2.observe(yourAnswerPre, config2);
  117. });
  118. const config = {
  119. attributes: true,
  120. childList: true,
  121. characterData: true,
  122. };
  123. console.log('[LEOC] result_div', result_div);
  124. observer.observe(result_div, config);
  125. };
  126. window.setTimeout(onload, 2000);
  127. });
  128. })();