Files
slack-github-action/test/content.spec.js

403 lines
13 KiB
JavaScript

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";
import send from "../src/send.js";
import { mocks } from "./index.spec.js";
/**
* Confirm values from the action input or environment variables are gathered
*/
describe("content", () => {
beforeEach(() => {
mocks.reset();
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
mocks.core.getInput.withArgs("token").returns("xoxb-example");
});
describe("flatten", () => {
it("flattens nested payloads provided with delimiter", async () => {
mocks.core.getInput.withArgs("payload").returns(`
"apples": "tree",
"bananas": {
"truthiness": true
}
`);
mocks.core.getInput.withArgs("payload-delimiter").returns("_");
const config = new Config(mocks.core);
const expected = {
apples: "tree",
bananas_truthiness: "true",
};
assert.deepEqual(config.content.values, expected);
});
});
describe("get", () => {
it("errors if both a payload and file path are provided", async () => {
mocks.core.getInput.withArgs("payload").returns(`"message"="hello"`);
mocks.core.getInput.withArgs("payload-file-path").returns("example.json");
try {
await send(mocks.core);
assert.fail("Failed to throw for invalid input");
} catch (err) {
if (err instanceof SlackError) {
assert.include(
err.message,
"Invalid input! Just the payload or payload file path is required.",
);
} else {
assert.fail("Failed to throw a SlackError", err);
}
}
});
});
describe("payload", async () => {
it("parses complete YAML from the input payload", async () => {
mocks.core.getInput.withArgs("payload").returns(`
message: "this is wrapped"
channel: "C0123456789"
`);
const config = new Config(mocks.core);
const expected = {
message: "this is wrapped",
channel: "C0123456789",
};
assert.deepEqual(config.content.values, expected);
});
it("parses complete JSON from the input payload", async () => {
mocks.core.getInput.withArgs("payload").returns(`{
"message": "this is wrapped",
"channel": "C0123456789"
}
`);
const config = new Config(mocks.core);
const expected = {
message: "this is wrapped",
channel: "C0123456789",
};
assert.deepEqual(config.content.values, expected);
});
it("templatizes variables with matching variables", async () => {
mocks.core.getInput
.withArgs("payload")
.returns("message: Served ${{ env.NUMBER }} from ${{ github.apiUrl }}");
mocks.core.getBooleanInput.withArgs("payload-templated").returns(true);
process.env.NUMBER = 12;
const config = new Config(mocks.core);
process.env.NUMBER = undefined;
const expected = {
message: "Served 12 from https://api.github.com",
};
assert.deepEqual(config.content.values, expected);
});
/**
* @see {@link https://github.com/slackapi/slack-github-action/issues/203}
*/
it("templatizes variables with missing variables", async () => {
mocks.core.getInput
.withArgs("payload")
.returns("message: What makes ${{ env.TREASURE }} a secret");
mocks.core.getBooleanInput.withArgs("payload-templated").returns(true);
const config = new Config(mocks.core);
const expected = {
message: "What makes ??? a secret",
};
assert.deepEqual(config.content.values, expected);
});
it("trims last comma JSON with the input payload", async () => {
mocks.core.getInput.withArgs("payload").returns(`
"message": "LGTM!",
"channel": "C0123456789",
`);
const config = new Config(mocks.core);
const expected = {
message: "LGTM!",
channel: "C0123456789",
};
assert.deepEqual(config.content.values, expected);
});
it("wraps incomplete JSON from the input payload", async () => {
mocks.core.getInput.withArgs("payload").returns(`
"message": "LGTM!",
"channel": "C0123456789",
"blocks": [
{
"type": "section",
"text": {
"text": "LGTM! :+1:"
}
}
]
`);
const config = new Config(mocks.core);
const expected = {
message: "LGTM!",
channel: "C0123456789",
blocks: [
{
type: "section",
text: {
text: "LGTM! :+1:",
},
},
],
};
assert.deepEqual(config.content.values, expected);
});
it("fails if no payload content is provided in input", async () => {
/**
* @type {Config}
*/
const config = {
core: core,
inputs: {
payloadFilePath: "unknown.json",
},
};
try {
new Content().getContentPayload(config);
assert.fail("Failed to throw for missing payload content");
} catch (err) {
if (err instanceof SlackError) {
assert.include(
err.message,
"Invalid input! No payload content was provided",
);
} else {
assert.fail("Failed to throw a SlackError", err);
}
}
});
it("fails if invalid JSON exists in the input payload", async () => {
mocks.core.getInput.withArgs("payload").returns("{");
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",
);
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);
}
}
});
});
describe("payload file", async () => {
it("parses complete YAML from the input payload file", async () => {
mocks.core.getInput.withArgs("payload-file-path").returns("example.yaml");
mocks.fs.readFileSync
.withArgs(path.resolve("example.yaml"), "utf-8")
.returns(`
message: "drink water"
channel: "C6H12O6H2O2"
`);
const config = new Config(mocks.core);
const expected = {
message: "drink water",
channel: "C6H12O6H2O2",
};
assert.deepEqual(config.content.values, expected);
});
it("parses complete YML from the input payload file", async () => {
mocks.core.getInput.withArgs("payload-file-path").returns("example.yml");
mocks.fs.readFileSync
.withArgs(path.resolve("example.yml"), "utf-8")
.returns(`
message: "drink coffee"
channel: "C0FFEEEEEEEE"
`);
const config = new Config(mocks.core);
const expected = {
message: "drink coffee",
channel: "C0FFEEEEEEEE",
};
assert.deepEqual(config.content.values, expected);
});
it("parses complete JSON from the input payload file", async () => {
mocks.core.getInput.withArgs("payload-file-path").returns("example.json");
mocks.fs.readFileSync
.withArgs(path.resolve("example.json"), "utf-8")
.returns(`{
"message": "drink water",
"channel": "C6H12O6H2O2"
}`);
const config = new Config(mocks.core);
const expected = {
message: "drink water",
channel: "C6H12O6H2O2",
};
assert.deepEqual(config.content.values, expected);
});
it("templatizes variables with matching variables", async () => {
mocks.core.getInput.withArgs("payload-file-path").returns("example.json");
mocks.fs.readFileSync
.withArgs(path.resolve("example.json"), "utf-8")
.returns(`{
"message": "Served $\{\{ env.NUMBER }} from $\{\{ github.apiUrl }}"
}`);
mocks.core.getBooleanInput.withArgs("payload-templated").returns(true);
process.env.NUMBER = 12;
const config = new Config(mocks.core);
process.env.NUMBER = undefined;
const expected = {
message: "Served 12 from https://api.github.com",
};
assert.deepEqual(config.content.values, expected);
});
/**
* @see {@link https://github.com/slackapi/slack-github-action/issues/203}
*/
it("templatizes variables with missing variables", async () => {
mocks.core.getInput.withArgs("payload-file-path").returns("example.json");
mocks.fs.readFileSync
.withArgs(path.resolve("example.json"), "utf-8")
.returns(`{
"message": "What makes $\{\{ env.TREASURE }} a secret"
}`);
mocks.core.getBooleanInput.withArgs("payload-templated").returns(true);
const config = new Config(mocks.core);
const expected = {
message: "What makes ??? a secret",
};
assert.deepEqual(config.content.values, expected);
});
it("fails if no payload file is provided in the input", async () => {
/**
* @type {Config}
*/
const config = {
core: core,
inputs: {
payload: "LGTM",
},
};
try {
new Content().getContentPayloadFilePath(config);
assert.fail("Failed to throw for the wrong payload type");
} catch (err) {
if (err instanceof SlackError) {
assert.include(
err.message,
"Invalid input! No payload found for content",
);
} else {
assert.fail("Failed to throw a SlackError", err);
}
}
});
it("fails to parse a file path that does not exist", async () => {
mocks.core.getInput.withArgs("payload-file-path").returns("unknown.json");
try {
await send(mocks.core);
assert.fail("Failed to throw for nonexistent files");
} catch (err) {
if (err instanceof SlackError) {
assert.include(
err.message,
"Invalid input! Failed to parse contents of the provided payload file",
);
} else {
assert.fail("Failed to throw a SlackError", err);
}
}
});
it("fails to parse a file with an unknown extension", async () => {
mocks.core.getInput.withArgs("payload-file-path").returns("unknown.md");
try {
await send(mocks.core);
assert.fail("Failed to throw for an unknown extension");
} 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.include(
err.cause.values[0].message,
"Invalid input! Failed to parse file extension unknown.md",
);
} else {
assert.fail("Failed to throw a SlackError", err);
}
}
});
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);
}
}
});
});
});