Skip to content

Commit 9d339fd

Browse files
author
lucifer
committed
feat: 一键复制所有测试用例
1 parent da9a998 commit 9d339fd

8 files changed

+225
-15
lines changed

‎config-overrides.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module.exports = function override(config, env) {
2+
//do stuff with the webpack config...
3+
config.entry = {
4+
main: "./src/index.js",
5+
content: "./src/contentScript.js",
6+
};
7+
config.optimization.runtimeChunk = false;
8+
9+
config.optimization.splitChunks = void 0;
10+
config.output.filename = "static/js/[name].js";
11+
config.output.chunkFilename = "static/js/[name].chunk.js";
12+
const cssPlugin = config.plugins[5];
13+
cssPlugin.options.filename = "static/css/[name].css";
14+
cssPlugin.options.chunkFilename = "static/css/[name].chunk.css";
15+
return config;
16+
};

‎package.json

+6-5
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
"scripts": {
2020
"release": "zip -r leetcode-cheat.zip build/ && mv leetcode-cheat.zip ../leetcode-cheat-release/ && cd ../leetcode-cheat-release && git commit -am 'feat: release' && git push",
2121
"lint": "eslint src",
22-
"start": "react-scripts start",
23-
"build": "react-scripts build",
24-
"test": "react-scripts test",
25-
"eject": "react-scripts eject",
22+
"start": "react-app-rewired start",
23+
"build": "react-app-rewired build",
24+
"test": "react-app-rewired test",
25+
"eject": "react-app-rewired eject",
2626
"crawl": "node scripts/curlLeetcode.js && node scripts/generateLeetcode.js",
2727
"clean": "rm -f src/db/root.db.js && rm -rf spider/"
2828
},
@@ -43,6 +43,7 @@
4343
"cheerio": "^1.0.0-rc.3",
4444
"iconv-lite": "^0.5.1",
4545
"log4js": "^6.3.0",
46-
"mkdirp": "^1.0.4"
46+
"mkdirp": "^1.0.4",
47+
"react-app-rewired": "^2.1.7"
4748
}
4849
}

‎public/background.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//background script is always running unless extension
2+
//is disabled
3+
4+
//Wait for some one connect to it
5+
let contentPort;
6+
chrome.runtime.onConnect.addListener(function (portFrom) {
7+
if (portFrom.name === "background-content") {
8+
//This is how you add listener to a port.
9+
portFrom.onMessage.addListener(function (message) {
10+
alert(message);
11+
//Do something to this message(offsetheight and width)
12+
});
13+
}
14+
});
15+
16+
//Send a message to a tab which has your content script injected.
17+
//You should able to use postMessage here as well.
18+
19+
setInterval(() => {
20+
chrome.tabs.query({ active: true }, function (tabs) {
21+
chrome.tabs.sendMessage(tabs[0].id, { action: "GET_DIMENSION" });
22+
});
23+
}, 2000);

‎public/manifest.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
"content_scripts": [
1414
{
1515
"matches": ["*://leetcode-cn.com/*", "*://leetcode.com/*"],
16-
"js": ["main.js"]
16+
"js": ["/static/js/content.js"]
1717
}
1818
],
19-
"permissions": ["tabs"],
19+
"permissions": ["tabs", "activeTab"],
2020
"content_security_policy": "script-src 'self' 'sha256-9HcBuUP35aPkU0991A4mASdsuifTkUlifJ7elThz6Ow=' 'sha256-0Jo/EYaXS11i7poc/P9fGcq/o6P0djny2JW6WivTVVw='; object-src 'self'"
2121
}

‎src/content.css

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.my-extension {
2+
padding: 20px;
3+
}
4+
.my-extension h1 {
5+
color: #000;
6+
}

‎src/contentScript.js

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { message } from "antd";
2+
// import "./content.css";
3+
import { copyToClipboard } from "./utils";
4+
5+
// testcase eg: `bottom = "BCD", allowed = ["BCG", "CDE", "GEA", "FFF"], c = [1,2,3], d = 2`
6+
function normalize(testCase) {
7+
testCase = testCase.replace(/\n/g, "").replace(" ", "");
8+
console.log(testCase);
9+
// 单一参数
10+
if (!testCase.includes("=")) {
11+
// 数组直接返回
12+
if (testCase.includes("[")) {
13+
return testCase;
14+
} else {
15+
// 输入: 3, 2, 0, 0
16+
// 输入: 0.0625
17+
18+
const parts = testCase.split(",");
19+
if (parts.length == 0) return parts.join("");
20+
return parts.join("\n");
21+
}
22+
}
23+
let stack = [];
24+
let i = 0;
25+
while (i < testCase.length) {
26+
while (i < testCase.length && testCase[i] !== "=") {
27+
i += 1;
28+
}
29+
// skip =
30+
i += 1;
31+
32+
while (i < testCase.length && testCase[i] !== "[" && testCase[i] != ",") {
33+
stack.push(testCase[i]);
34+
i += 1;
35+
}
36+
if (testCase[i] == ",") {
37+
// skip ,
38+
i += 1;
39+
stack.push("\n");
40+
} else {
41+
// cnt 左括号[ 与 右括号] 个数的差值
42+
let cnt = 0;
43+
while (i < testCase.length) {
44+
stack.push(testCase[i]);
45+
cnt += testCase[i] === "[";
46+
cnt -= testCase[i] === "]";
47+
i += 1;
48+
if (cnt == 0) {
49+
if (i !== testCase.length) {
50+
stack.push("\n");
51+
}
52+
53+
break;
54+
}
55+
}
56+
}
57+
}
58+
return stack.join("");
59+
}
60+
61+
function getProviedTestCases() {
62+
const possibleTags = ["pre", "p"];
63+
const possiblePrefixs = ["输入:", "输入:"];
64+
const ans = [];
65+
for (let tag of possibleTags) {
66+
const pres = document.querySelectorAll(tag);
67+
68+
for (let prefix of possiblePrefixs) {
69+
for (var i = 0; i < pres.length; ++i) {
70+
if (pres[i].innerText.includes(prefix)) {
71+
console.log(
72+
pres[i].innerText.match(new RegExp(`${prefix}(.*)输出`, "s"))
73+
);
74+
const testcase = pres[i].innerText.match(
75+
new RegExp(`${prefix}(.*?)输出`, "mgs")
76+
)[1];
77+
ans.push(normalize(testcase));
78+
}
79+
}
80+
if (ans.length > 0) return ans;
81+
}
82+
}
83+
return ans;
84+
}
85+
86+
function insertButton() {
87+
const buttons = document.querySelectorAll("button");
88+
for (var i = 0; i < buttons.length; ++i) {
89+
if (buttons[i].innerText.includes("执行代码")) {
90+
const copyButton = buttons[i].cloneNode(true);
91+
copyButton.innerText = "复制所有内置用例";
92+
copyButton.style["margin-left"] = "10px";
93+
copyButton.onclick = () => {
94+
const cases = getProviedTestCases();
95+
if (cases.filter(Boolean).length === 0)
96+
return message.error({
97+
content:
98+
"力扣不讲武德,不套路出牌。不过没关系啊,你反馈给我,我下次一定全部防出去啊!",
99+
});
100+
copyToClipboard(cases.join("\n"));
101+
message.success({
102+
content: "复制成功~",
103+
});
104+
};
105+
buttons[i].parentElement.prepend(copyButton);
106+
break;
107+
}
108+
}
109+
}
110+
let inserted = false;
111+
const timerId = setInterval(() => {
112+
if (inserted) return clearInterval(timerId);
113+
insertButton();
114+
inserted = true;
115+
}, 1000);
116+
117+
// class Main extends React.Component {
118+
// render() {
119+
// return (
120+
// <div className={"my-extension"}>
121+
// <h1 onClick={t}>Hello world - My first Extension</h1>
122+
// </div>
123+
// );
124+
// }
125+
// }
126+
127+
// const app = document.createElement("div");
128+
// app.id = "my-extension-root";
129+
// document.body.appendChild(app);
130+
131+
// ReactDOM.render(<Main />, app);

‎src/index.js

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ import App from "./App";
66

77
import * as serviceWorker from "./serviceWorker";
88

9+
// Get the element to prepend our app to. This could be any element on a specific website or even just `document.body`.
10+
const viewport = document.querySelector("body");
11+
12+
// Create a div to render the <App /> component to.
13+
const app = document.createElement("div");
14+
15+
// Set the app element's id to `root`. This is the same as the element that create-react-app renders to by default so it will work on the local server too.
16+
app.id = "root";
17+
18+
// Prepend the <App /> component to the viewport element if it exists. You could also use `appendChild` depending on your needs.
19+
if (viewport) viewport.prepend(app);
20+
921
ReactDOM.render(
1022
<React.StrictMode>
1123
<App />

‎src/testCase.js

+29-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010

1111
const { Option } = Select;
1212

13+
/*global chrome*/
14+
1315
// TODO referrence 支持 , 比如 k 是在数组大小范围内动态的
1416

1517
function isNull(c) {
@@ -126,8 +128,23 @@ function gussType(input) {
126128
return "single";
127129
}
128130

129-
function getProviedTestCases() {
130-
const pres = document.getElementsByTagName("pre");
131+
function t() {
132+
return new Promise((r) => {
133+
chrome.tabs.query({ active: true }, function (tabs) {
134+
var tab = tabs[0];
135+
136+
chrome.tabs.executeScript(
137+
tab.id,
138+
{
139+
code: 'document.getElementsByTagName("pre")',
140+
},
141+
r
142+
);
143+
});
144+
});
145+
}
146+
147+
function getProviedTestCases(pres) {
131148
const ans = [];
132149
for (var i = 0; i < pres.length; ++i) {
133150
if (pres[i].innerText.includes("输入:")) {
@@ -234,12 +251,16 @@ export default function TestCase() {
234251
<Button
235252
type="primary"
236253
onClick={() => {
237-
const cases = getProviedTestCases();
238-
const ans = cases.map(normalize).join("\n");
239-
console.log(cases, ans);
240-
if (ans) {
241-
if (copyToClipboard(ans)) message.success("复制成功");
242-
}
254+
t().then((elements) => {
255+
alert(JSON.stringify(elements));
256+
const cases = getProviedTestCases(elements);
257+
const ans = cases.map(normalize).join("\n");
258+
console.log(cases, ans);
259+
alert(elements);
260+
if (ans) {
261+
if (copyToClipboard(ans)) message.success("复制成功");
262+
}
263+
});
243264
}}
244265
>
245266
获取所有测试用例

0 commit comments

Comments
 (0)