From 05701841a1b92aa78bb9421a817b6a2eb14921be Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 26 Apr 2024 18:50:31 +0200 Subject: [PATCH 1/4] feat: improve body parsing logic Current body parsing logic with trim() + split("###") is too fragile and pose problems with some body that contains case with ### in the middle of the line or case with codeblock ``` section. These case will cause the script to parse these as separate section and produce wrong outputs and in some case even prints error assuming things are checkbox and errors out on the concat function. To make the parsing logic more solid, implement a dedicated function and parse with this logic: - We split the body for "\n" - We ignore codeblock ``` section - We check "###" only at the start of the line - We check for "### " (with the space included) as that is the correct section Github issue template expects. With the following change case like: ``` root@OpenWrt:~# cat /boot/config.txt ``` Are correctly parsed as all part of a single section instead of being wrongly treated as different empty sections. Signed-off-by: Christian Marangi --- index.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 53cc884..3b7ac7e 100644 --- a/index.js +++ b/index.js @@ -111,9 +111,35 @@ async function run(env, body, fs, core) { return result } - result = body - .trim() - .split("###") + function parseBody(body) { + let result = []; + let ignore = false; + + body.split("\n").reduce((str, line, idx, arr) => { + // ``` Section must be ignored and not parsed as new section + if (line.startsWith("```")) + ignore = !ignore + + // Parse new setion only if they start with ### SPACE + if (!ignore && line.startsWith("### ")) { + result.push(str.trim()) + + return line.replace(/^### /, "")+"\n"; + } + + str += line + "\n" + + // Push the last string if we are at the end of lines + if (arr.length - 1 == idx) + result.push(str.trim()) + + return str; + }, "") + + return result; + } + + result = parseBody(body) .filter(Boolean) .map((line) => { return line From b6c8f2cacb09bd2186df2101c548683bf28f0469 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 26 Apr 2024 18:57:19 +0200 Subject: [PATCH 2/4] feat: add additional test for section with #### Add additional test for section with #### to prevent and catch in future code change that would produce wrong parsing. Signed-off-by: Christian Marangi --- .../paragraph-confusing-####/expected.json | 3 ++ fixtures/paragraph-confusing-####/form.yml | 9 +++++ .../paragraph-confusing-####/issue-body.md | 3 ++ fixtures/paragraph-confusing-####/issue.js | 6 +++ test.spec.js | 39 +++++++++++++++++++ 5 files changed, 60 insertions(+) create mode 100644 fixtures/paragraph-confusing-####/expected.json create mode 100644 fixtures/paragraph-confusing-####/form.yml create mode 100644 fixtures/paragraph-confusing-####/issue-body.md create mode 100644 fixtures/paragraph-confusing-####/issue.js diff --git a/fixtures/paragraph-confusing-####/expected.json b/fixtures/paragraph-confusing-####/expected.json new file mode 100644 index 0000000..34954d8 --- /dev/null +++ b/fixtures/paragraph-confusing-####/expected.json @@ -0,0 +1,3 @@ +{ + "textarea-one": "Textarea input text 1 ####" +} diff --git a/fixtures/paragraph-confusing-####/form.yml b/fixtures/paragraph-confusing-####/form.yml new file mode 100644 index 0000000..cb5bf9f --- /dev/null +++ b/fixtures/paragraph-confusing-####/form.yml @@ -0,0 +1,9 @@ +body: + - type: textarea + id: textarea-one + attributes: + label: My textarea input + - type: textarea + id: textarea-two + attributes: + label: Another textarea input diff --git a/fixtures/paragraph-confusing-####/issue-body.md b/fixtures/paragraph-confusing-####/issue-body.md new file mode 100644 index 0000000..38a4b14 --- /dev/null +++ b/fixtures/paragraph-confusing-####/issue-body.md @@ -0,0 +1,3 @@ +### My textarea input + +Textarea input text 1 #### diff --git a/fixtures/paragraph-confusing-####/issue.js b/fixtures/paragraph-confusing-####/issue.js new file mode 100644 index 0000000..6851c74 --- /dev/null +++ b/fixtures/paragraph-confusing-####/issue.js @@ -0,0 +1,6 @@ +const { resolve } = require("path"); +const { readFileSync } = require("fs"); + +const issueBodyPath = resolve(__dirname, "issue-body.md"); + +module.exports = readFileSync(issueBodyPath, "utf-8") diff --git a/test.spec.js b/test.spec.js index c3ce9bc..d75d5da 100644 --- a/test.spec.js +++ b/test.spec.js @@ -174,6 +174,45 @@ it("multiple paragraphs", () => { expect(core.setOutput.mock.calls.length).toBe(3) }); +it("paragraph with confusing ####", () => { + const expectedOutput = require("./fixtures/paragraph-confusing-####/expected.json"); + const expectedOutputJson = JSON.stringify(expectedOutput, null, 2); + + // mock ENV + const env = { + HOME: "", + }; + + // mock event payload + const eventPayload = require("./fixtures/paragraph-confusing-####/issue"); + + // mock fs + const fs = { + readFileSync(path, encoding) { + expect(path).toBe(""); + expect(encoding).toBe("utf8"); + return readFileSync("fixtures/paragraph-confusing-####/form.yml", "utf-8"); + }, + writeFileSync(path, content) { + expect(path).toBe("/issue-parser-result.json"); + expect(content).toBe(expectedOutputJson); + }, + }; + + // mock core + const core = { + getInput: jest.fn(() => ''), + setOutput: jest.fn(), + }; + + run(env, eventPayload, fs, core); + + expect(core.getInput).toHaveBeenCalledWith('template-path') + expect(core.setOutput).toHaveBeenCalledWith('jsonString', JSON.stringify(expectedOutput, null, 2)) + expect(core.setOutput).toHaveBeenCalledWith('issueparser_textarea-one', 'Textarea input text 1 ####') + expect(core.setOutput.mock.calls.length).toBe(2) +}); + it("blank", () => { const expectedOutput = require("./fixtures/blank/expected.json"); const expectedOutputJson = JSON.stringify(expectedOutput, null, 2); From 1816705404929deef0d2befb309d1799c383d74f Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 26 Apr 2024 18:58:26 +0200 Subject: [PATCH 3/4] feat: add additional test for codeblock ``` ignore Add test for codeblock ``` ignore to prevent and catch in future code change that would produce wrong parsing. Signed-off-by: Christian Marangi --- fixtures/paragraph-ignore-```/expected.json | 3 ++ fixtures/paragraph-ignore-```/form.yml | 9 +++++ fixtures/paragraph-ignore-```/issue-body.md | 7 ++++ fixtures/paragraph-ignore-```/issue.js | 6 ++++ test.spec.js | 39 +++++++++++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 fixtures/paragraph-ignore-```/expected.json create mode 100644 fixtures/paragraph-ignore-```/form.yml create mode 100644 fixtures/paragraph-ignore-```/issue-body.md create mode 100644 fixtures/paragraph-ignore-```/issue.js diff --git a/fixtures/paragraph-ignore-```/expected.json b/fixtures/paragraph-ignore-```/expected.json new file mode 100644 index 0000000..2f8fdda --- /dev/null +++ b/fixtures/paragraph-ignore-```/expected.json @@ -0,0 +1,3 @@ +{ + "textarea-one": "Textarea input text 1\n\n```\n### To be ignored tag\n```" +} diff --git a/fixtures/paragraph-ignore-```/form.yml b/fixtures/paragraph-ignore-```/form.yml new file mode 100644 index 0000000..cb5bf9f --- /dev/null +++ b/fixtures/paragraph-ignore-```/form.yml @@ -0,0 +1,9 @@ +body: + - type: textarea + id: textarea-one + attributes: + label: My textarea input + - type: textarea + id: textarea-two + attributes: + label: Another textarea input diff --git a/fixtures/paragraph-ignore-```/issue-body.md b/fixtures/paragraph-ignore-```/issue-body.md new file mode 100644 index 0000000..ebe88ce --- /dev/null +++ b/fixtures/paragraph-ignore-```/issue-body.md @@ -0,0 +1,7 @@ +### My textarea input + +Textarea input text 1 + +``` +### To be ignored tag +``` diff --git a/fixtures/paragraph-ignore-```/issue.js b/fixtures/paragraph-ignore-```/issue.js new file mode 100644 index 0000000..6851c74 --- /dev/null +++ b/fixtures/paragraph-ignore-```/issue.js @@ -0,0 +1,6 @@ +const { resolve } = require("path"); +const { readFileSync } = require("fs"); + +const issueBodyPath = resolve(__dirname, "issue-body.md"); + +module.exports = readFileSync(issueBodyPath, "utf-8") diff --git a/test.spec.js b/test.spec.js index d75d5da..9354d58 100644 --- a/test.spec.js +++ b/test.spec.js @@ -213,6 +213,45 @@ it("paragraph with confusing ####", () => { expect(core.setOutput.mock.calls.length).toBe(2) }); +it("paragraph with ``` section", () => { + const expectedOutput = require("./fixtures/paragraph-ignore-```/expected.json"); + const expectedOutputJson = JSON.stringify(expectedOutput, null, 2); + + // mock ENV + const env = { + HOME: "", + }; + + // mock event payload + const eventPayload = require("./fixtures/paragraph-ignore-```/issue"); + + // mock fs + const fs = { + readFileSync(path, encoding) { + expect(path).toBe(""); + expect(encoding).toBe("utf8"); + return readFileSync("fixtures/paragraph-ignore-```/form.yml", "utf-8"); + }, + writeFileSync(path, content) { + expect(path).toBe("/issue-parser-result.json"); + expect(content).toBe(expectedOutputJson); + }, + }; + + // mock core + const core = { + getInput: jest.fn(() => ''), + setOutput: jest.fn(), + }; + + run(env, eventPayload, fs, core); + + expect(core.getInput).toHaveBeenCalledWith('template-path') + expect(core.setOutput).toHaveBeenCalledWith('jsonString', JSON.stringify(expectedOutput, null, 2)) + expect(core.setOutput).toHaveBeenCalledWith('issueparser_textarea-one', 'Textarea input text 1\n\n```\n### To be ignored tag\n```') + expect(core.setOutput.mock.calls.length).toBe(2) +}); + it("blank", () => { const expectedOutput = require("./fixtures/blank/expected.json"); const expectedOutputJson = JSON.stringify(expectedOutput, null, 2); From 06dd03d86c0db88358048c125f76444c101a7beb Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 31 Jan 2025 13:49:27 +0100 Subject: [PATCH 4/4] feat: add additional test for codeblock ```sh ignore Add test for codeblock ```sh ignore to prevent and catch in future code change that would produce wrong parsing. This is a variant of the ``` test that makes the code block section target specific code. Signed-off-by: Christian Marangi --- fixtures/paragraph-ignore-```sh/expected.json | 3 ++ fixtures/paragraph-ignore-```sh/form.yml | 9 +++++ fixtures/paragraph-ignore-```sh/issue-body.md | 7 ++++ fixtures/paragraph-ignore-```sh/issue.js | 6 +++ test.spec.js | 39 +++++++++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 fixtures/paragraph-ignore-```sh/expected.json create mode 100644 fixtures/paragraph-ignore-```sh/form.yml create mode 100644 fixtures/paragraph-ignore-```sh/issue-body.md create mode 100644 fixtures/paragraph-ignore-```sh/issue.js diff --git a/fixtures/paragraph-ignore-```sh/expected.json b/fixtures/paragraph-ignore-```sh/expected.json new file mode 100644 index 0000000..1026cc4 --- /dev/null +++ b/fixtures/paragraph-ignore-```sh/expected.json @@ -0,0 +1,3 @@ +{ + "textarea-one": "Textarea input text 1\n\n```sh\n### To be ignored tag\n```" +} diff --git a/fixtures/paragraph-ignore-```sh/form.yml b/fixtures/paragraph-ignore-```sh/form.yml new file mode 100644 index 0000000..cb5bf9f --- /dev/null +++ b/fixtures/paragraph-ignore-```sh/form.yml @@ -0,0 +1,9 @@ +body: + - type: textarea + id: textarea-one + attributes: + label: My textarea input + - type: textarea + id: textarea-two + attributes: + label: Another textarea input diff --git a/fixtures/paragraph-ignore-```sh/issue-body.md b/fixtures/paragraph-ignore-```sh/issue-body.md new file mode 100644 index 0000000..cf4981e --- /dev/null +++ b/fixtures/paragraph-ignore-```sh/issue-body.md @@ -0,0 +1,7 @@ +### My textarea input + +Textarea input text 1 + +```sh +### To be ignored tag +``` diff --git a/fixtures/paragraph-ignore-```sh/issue.js b/fixtures/paragraph-ignore-```sh/issue.js new file mode 100644 index 0000000..6851c74 --- /dev/null +++ b/fixtures/paragraph-ignore-```sh/issue.js @@ -0,0 +1,6 @@ +const { resolve } = require("path"); +const { readFileSync } = require("fs"); + +const issueBodyPath = resolve(__dirname, "issue-body.md"); + +module.exports = readFileSync(issueBodyPath, "utf-8") diff --git a/test.spec.js b/test.spec.js index 9354d58..d8935e5 100644 --- a/test.spec.js +++ b/test.spec.js @@ -252,6 +252,45 @@ it("paragraph with ``` section", () => { expect(core.setOutput.mock.calls.length).toBe(2) }); +it("paragraph with ```sh section", () => { + const expectedOutput = require("./fixtures/paragraph-ignore-```sh/expected.json"); + const expectedOutputJson = JSON.stringify(expectedOutput, null, 2); + + // mock ENV + const env = { + HOME: "", + }; + + // mock event payload + const eventPayload = require("./fixtures/paragraph-ignore-```sh/issue"); + + // mock fs + const fs = { + readFileSync(path, encoding) { + expect(path).toBe(""); + expect(encoding).toBe("utf8"); + return readFileSync("fixtures/paragraph-ignore-```sh/form.yml", "utf-8"); + }, + writeFileSync(path, content) { + expect(path).toBe("/issue-parser-result.json"); + expect(content).toBe(expectedOutputJson); + }, + }; + + // mock core + const core = { + getInput: jest.fn(() => ''), + setOutput: jest.fn(), + }; + + run(env, eventPayload, fs, core); + + expect(core.getInput).toHaveBeenCalledWith('template-path') + expect(core.setOutput).toHaveBeenCalledWith('jsonString', JSON.stringify(expectedOutput, null, 2)) + expect(core.setOutput).toHaveBeenCalledWith('issueparser_textarea-one', 'Textarea input text 1\n\n```sh\n### To be ignored tag\n```') + expect(core.setOutput.mock.calls.length).toBe(2) +}); + it("blank", () => { const expectedOutput = require("./fixtures/blank/expected.json"); const expectedOutputJson = JSON.stringify(expectedOutput, null, 2);