diff --git a/src/content.js b/src/content.js index 6594a32d..e9506a09 100644 --- a/src/content.js +++ b/src/content.js @@ -55,6 +55,7 @@ export default class Content { * @returns {Content} - the parsed JSON payload to use in requests. */ getContentPayload(config) { + const errors = []; if (!config.inputs.payload) { throw new SlackError( config.core, @@ -71,8 +72,7 @@ export default class Content { return /** @type {Content} */ (content); } catch (error) { if (error instanceof Error) { - config.core.debug("Failed to parse input payload as YAML"); - config.core.debug(error.message); + errors.push(error); } } try { @@ -90,11 +90,12 @@ export default class Content { } return JSON.parse(trimmed); } catch (/** @type {any} */ error) { + errors.unshift(error); throw new SlackError( config.core, "Invalid input! Failed to parse contents of the provided payload", { - cause: error, + cause: { values: errors }, }, ); } @@ -140,7 +141,7 @@ export default class Content { config.core, "Invalid input! Failed to parse contents of the provided payload file", { - cause: error, + cause: { values: [error] }, }, ); } diff --git a/src/errors.js b/src/errors.js index b2d4ce0c..3d0d87b1 100644 --- a/src/errors.js +++ b/src/errors.js @@ -1,12 +1,17 @@ import core from "@actions/core"; +/** + * @typedef Cause + * @property {Error[]} [values] - Caught exceptions. + */ + /** * SlackError is a custom error wrapper for known errors of Slack GitHub Action. */ export default class SlackError extends Error { /** * @typedef Options - * @property {Error} [cause] - thrown exception of this error. + * @property {Cause} [cause] - Reason for an error. */ /** diff --git a/src/index.js b/src/index.js index fcdf2277..ff3b701b 100644 --- a/src/index.js +++ b/src/index.js @@ -6,13 +6,21 @@ import send from "./send.js"; * from the send.js file for testing purposes. */ try { - send(core); + await send(core); } catch (error) { if (error instanceof Error) { core.error(error.message); - core.debug(`${error.name} cause: ${error.cause}`); - core.debug(`${error.name} stack: ${error.stack}`); + /** @type {import('./errors.js').Cause} */ + const causes = /** @type {any} */ (error.cause); + if (causes?.values) { + for (const cause of causes.values) { + core.info(`${cause.stack}`); + } + } else { + core.info(`${error.stack}`); + } } else { core.error(`${error}`); } + throw error; } diff --git a/test/content.spec.js b/test/content.spec.js index be279d44..30a37286 100644 --- a/test/content.spec.js +++ b/test/content.spec.js @@ -1,6 +1,7 @@ import path from "node:path"; import core from "@actions/core"; import { assert } from "chai"; +import { YAMLException } from "js-yaml"; import Config from "../src/config.js"; import Content from "../src/content.js"; import SlackError from "../src/errors.js"; @@ -190,6 +191,11 @@ describe("content", () => { err.message, "Invalid input! Failed to parse contents of the provided payload", ); + assert.isDefined(err.cause?.values); + assert.equal(err.cause.values.length, 2); + const [jsonError, yamlError] = err.cause.values; + assert.isTrue(jsonError instanceof SyntaxError); + assert.isTrue(yamlError instanceof YAMLException); } else { assert.fail("Failed to throw a SlackError", err); } @@ -334,8 +340,10 @@ describe("content", () => { err.message, "Invalid input! Failed to parse contents of the provided payload file", ); + assert.isDefined(err.cause?.values); + assert.equal(err.cause.values.length, 1); assert.include( - err.cause.message, + err.cause.values[0].message, "Invalid input! Failed to parse file extension unknown.md", ); } else { @@ -343,5 +351,52 @@ describe("content", () => { } } }); + + it("fails if invalid JSON exists in the input payload", async () => { + mocks.core.getInput.withArgs("payload-file-path").returns("example.json"); + mocks.fs.readFileSync + .withArgs(path.resolve("example.json"), "utf-8") + .returns(`{ + "message": "a truncated file without an end`); + try { + await send(mocks.core); + assert.fail("Failed to throw for invalid JSON"); + } catch (err) { + if (err instanceof SlackError) { + assert.include( + err.message, + "Invalid input! Failed to parse contents of the provided payload file", + ); + assert.isDefined(err.cause?.values); + assert.equal(err.cause.values.length, 1); + assert.isTrue(err.cause.values[0] instanceof SyntaxError); + } else { + assert.fail("Failed to throw a SlackError", err); + } + } + }); + + it("fails if invalid YAML exists in the input payload", async () => { + mocks.core.getInput.withArgs("payload-file-path").returns("example.yaml"); + mocks.fs.readFileSync + .withArgs(path.resolve("example.yaml"), "utf-8") + .returns(`- "message": "assigned": "values"`); + try { + await send(mocks.core); + assert.fail("Failed to throw for invalid YAML"); + } catch (err) { + if (err instanceof SlackError) { + assert.include( + err.message, + "Invalid input! Failed to parse contents of the provided payload file", + ); + assert.isDefined(err.cause?.values); + assert.equal(err.cause.values.length, 1); + assert.isTrue(err.cause.values[0] instanceof YAMLException); + } else { + assert.fail("Failed to throw a SlackError", err); + } + } + }); }); });