From 7e1f1b5050a2112b0cedae70317e1f26edb18629 Mon Sep 17 00:00:00 2001 From: Phred Date: Thu, 6 Aug 2026 16:01:41 -0500 Subject: [PATCH] cleaned up tests using native methods --- src/send.js | 2 + test/client.spec.js | 436 +++++++++++++++++++------------------------ test/config.spec.js | 198 +++++++------------- test/content.spec.js | 178 +++++++----------- test/logger.spec.js | 25 +-- test/send.spec.js | 4 +- test/webhook.spec.js | 174 ++++++----------- 7 files changed, 395 insertions(+), 622 deletions(-) diff --git a/src/send.js b/src/send.js index f16e3c57..826e8a0e 100644 --- a/src/send.js +++ b/src/send.js @@ -33,6 +33,8 @@ async function post(config) { case !!config.inputs.webhook: return await new Webhook().post(config); default: + // NOTE: This code is unreachable. The Config constructor will have + // already thrown before this code can be reached. throw new SlackError(config.core, "No technique given to post content"); } } diff --git a/test/client.spec.js b/test/client.spec.js index 26e613b9..3200956c 100644 --- a/test/client.spec.js +++ b/test/client.spec.js @@ -5,7 +5,6 @@ import errors from "@slack/web-api/dist/errors.js"; import sinon from "sinon"; import Client from "../src/client.js"; import Config from "../src/config.js"; -import SlackError from "../src/errors.js"; import Logger from "../src/logger.js"; import send from "../src/send.js"; import { mocks } from "./index.spec.js"; @@ -26,16 +25,10 @@ describe("client", () => { token: "xoxb-example", }, }; - try { - await new Client().post(config); - assert.fail("Failed to throw for missing input"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok(err.message.includes("No API method was provided for use")); - } else { - assert.fail(err); - } - } + await assert.rejects(() => new Client().post(config), { + message: "No API method was provided for use", + name: "SlackError", + }); }); it("requires a token is provided in inputs", async () => { @@ -49,20 +42,14 @@ describe("client", () => { }, }; mocks.core.getInput.withArgs("token").returns("xoxb-example-001"); - try { - await new Client().post(config); - assert.fail("Failed to throw for missing input"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok(err.message.includes("No token was provided to post with")); - } else { - assert.fail(err); - } - } + await assert.rejects(() => new Client().post(config), { + message: "No token was provided to post with", + name: "SlackError", + }); }); }); - describe("api", async () => { + describe("api", () => { it("uses arguments to send to a slack api method", async () => { const apis = sinon.stub().resolves({ ok: true }); const constructors = sinon @@ -164,123 +151,108 @@ describe("client", () => { describe("success", () => { it("calls 'chat.postMessage' with the given token and content", async () => { - try { - const args = { - channel: "C0123456789", - text: "hello", + const args = { + channel: "C0123456789", + text: "hello", + thread_ts: "1234567890.000001", + }; + const response = { + ok: true, + channel: "C0123456789", + ts: "1234567890.000002", + message: { thread_ts: "1234567890.000001", - }; - const response = { - ok: true, - channel: "C0123456789", - ts: "1234567890.000002", - message: { - thread_ts: "1234567890.000001", - }, - }; - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); - mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args)); - mocks.calls.resolves(response); - await send(mocks.core); - assert.deepEqual(mocks.calls.getCall(0).firstArg, "chat.postMessage"); - assert.deepEqual(mocks.calls.getCall(0).lastArg, args); - assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); - assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); - assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); - assert.equal( - mocks.core.setOutput.getCall(1).lastArg, - JSON.stringify(response), - ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "channel_id"); - assert.equal(mocks.core.setOutput.getCall(2).lastArg, "C0123456789"); - assert.equal(mocks.core.setOutput.getCall(3).firstArg, "thread_ts"); - assert.equal( - mocks.core.setOutput.getCall(3).lastArg, - "1234567890.000001", - ); - assert.equal(mocks.core.setOutput.getCall(4).firstArg, "ts"); - assert.equal( - mocks.core.setOutput.getCall(4).lastArg, - "1234567890.000002", - ); - assert.equal(mocks.core.setOutput.getCall(5).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 6); - } catch (err) { - console.error(err); - assert.fail("Unexpected error when calling the method"); - } + }, + }; + mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args)); + mocks.calls.resolves(response); + await send(mocks.core); + assert.deepEqual(mocks.calls.getCall(0).firstArg, "chat.postMessage"); + assert.deepEqual(mocks.calls.getCall(0).lastArg, args); + assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); + assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); + assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); + assert.equal( + mocks.core.setOutput.getCall(1).lastArg, + JSON.stringify(response), + ); + assert.equal(mocks.core.setOutput.getCall(2).firstArg, "channel_id"); + assert.equal(mocks.core.setOutput.getCall(2).lastArg, "C0123456789"); + assert.equal(mocks.core.setOutput.getCall(3).firstArg, "thread_ts"); + assert.equal( + mocks.core.setOutput.getCall(3).lastArg, + "1234567890.000001", + ); + assert.equal(mocks.core.setOutput.getCall(4).firstArg, "ts"); + assert.equal( + mocks.core.setOutput.getCall(4).lastArg, + "1234567890.000002", + ); + assert.equal(mocks.core.setOutput.getCall(5).firstArg, "time"); + assert.equal(mocks.core.setOutput.getCalls().length, 6); }); it("calls 'conversations.create' with the given token and content", async () => { - try { - const args = { + const args = { + name: "pull-request-review-010101", + }; + const response = { + ok: true, + channel: { + id: "C0101010101", name: "pull-request-review-010101", - }; - const response = { - ok: true, - channel: { - id: "C0101010101", - name: "pull-request-review-010101", - is_channel: true, - created: 1730425428, - }, - }; - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); - mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args)); - mocks.calls.resolves(response); - await send(mocks.core); - assert.deepEqual(mocks.calls.getCall(0).firstArg, "chat.postMessage"); - assert.deepEqual(mocks.calls.getCall(0).lastArg, args); - assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); - assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); - assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); - assert.equal( - mocks.core.setOutput.getCall(1).lastArg, - JSON.stringify(response), - ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "channel_id"); - assert.equal(mocks.core.setOutput.getCall(2).lastArg, "C0101010101"); - assert.equal(mocks.core.setOutput.getCall(3).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 4); - } catch (err) { - console.error(err); - assert.fail("Unexpected error when calling the method"); - } + is_channel: true, + created: 1730425428, + }, + }; + mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args)); + mocks.calls.resolves(response); + await send(mocks.core); + assert.deepEqual(mocks.calls.getCall(0).firstArg, "chat.postMessage"); + assert.deepEqual(mocks.calls.getCall(0).lastArg, args); + assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); + assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); + assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); + assert.equal( + mocks.core.setOutput.getCall(1).lastArg, + JSON.stringify(response), + ); + assert.equal(mocks.core.setOutput.getCall(2).firstArg, "channel_id"); + assert.equal(mocks.core.setOutput.getCall(2).lastArg, "C0101010101"); + assert.equal(mocks.core.setOutput.getCall(3).firstArg, "time"); + assert.equal(mocks.core.setOutput.getCalls().length, 4); }); it("calls 'files.uploadV2' with the provided token and content", async () => { - try { - const args = { - channel: "C0000000001", - initial_comment: "the results are in!", - file: "results.out", - filename: "results-888888.out", - }; - const response = { - ok: true, - files: [{ id: "F0000000001", created: 1234567890 }], - }; - mocks.core.getInput.withArgs("method").returns("files.uploadV2"); - mocks.core.getInput.withArgs("token").returns("xoxp-example"); - mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args)); - mocks.calls.resolves(response); - await send(mocks.core); - assert.deepEqual(mocks.calls.getCall(0).lastArg, args); - assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); - assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); - assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); - assert.equal( - mocks.core.setOutput.getCall(1).lastArg, - JSON.stringify(response), - ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); - } catch (err) { - console.error(err); - assert.fail("Unexpected error when calling the method"); - } + const args = { + channel: "C0000000001", + initial_comment: "the results are in!", + file: "results.out", + filename: "results-888888.out", + }; + const response = { + ok: true, + files: [{ id: "F0000000001", created: 1234567890 }], + }; + mocks.core.getInput.withArgs("method").returns("files.uploadV2"); + mocks.core.getInput.withArgs("token").returns("xoxp-example"); + mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args)); + mocks.calls.resolves(response); + await send(mocks.core); + assert.deepEqual(mocks.calls.getCall(0).lastArg, args); + assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); + assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); + assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); + assert.equal( + mocks.core.setOutput.getCall(1).lastArg, + JSON.stringify(response), + ); + assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); + assert.equal(mocks.core.setOutput.getCalls().length, 3); }); }); @@ -296,27 +268,23 @@ describe("client", () => { message: "Something bad happened!", }, }; - try { - mocks.core.getInput.reset(); - mocks.core.getBooleanInput.withArgs("errors").returns(true); - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); - mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`); - mocks.calls.rejects(errors.requestErrorWithOriginal(response, true)); - await send(mocks.core); - assert.fail("Expected an error but none was found"); - } catch (_err) { - assert.ok(mocks.core.setFailed.called); - assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); - assert.equal(mocks.core.setOutput.getCall(0).lastArg, false); - assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); - assert.deepEqual( - mocks.core.setOutput.getCall(1).lastArg, - JSON.stringify(response), - ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); - } + mocks.core.getInput.reset(); + mocks.core.getBooleanInput.withArgs("errors").returns(true); + mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`); + mocks.calls.rejects(errors.requestErrorWithOriginal(response, true)); + await assert.rejects(() => send(mocks.core)); + assert.ok(mocks.core.setFailed.called); + assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); + assert.equal(mocks.core.setOutput.getCall(0).lastArg, false); + assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); + assert.deepEqual( + mocks.core.setOutput.getCall(1).lastArg, + JSON.stringify(response), + ); + assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); + assert.equal(mocks.core.setOutput.getCalls().length, 3); }); it("errors when the http portion of the request fails to send", async () => { @@ -333,27 +301,23 @@ describe("client", () => { error: "unknown_http_method", }, }; - try { - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); - mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`); - mocks.calls.rejects(errors.httpErrorFromResponse(response)); - await send(mocks.core); - assert.fail("Expected an error but none was found"); - } catch (_err) { - assert.strictEqual(mocks.core.setFailed.called, false); - assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); - assert.equal(mocks.core.setOutput.getCall(0).lastArg, false); - assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); - response.body = response.data; - response.data = undefined; - assert.deepEqual( - mocks.core.setOutput.getCall(1).lastArg, - JSON.stringify(response), - ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); - } + mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`); + mocks.calls.rejects(errors.httpErrorFromResponse(response)); + await send(mocks.core); + assert.strictEqual(mocks.core.setFailed.called, false); + assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); + assert.equal(mocks.core.setOutput.getCall(0).lastArg, false); + assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); + response.body = response.data; + response.data = undefined; + assert.deepEqual( + mocks.core.setOutput.getCall(1).lastArg, + JSON.stringify(response), + ); + assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); + assert.equal(mocks.core.setOutput.getCalls().length, 3); }); it("errors when the payload arguments are invalid for the api", async () => { @@ -367,27 +331,23 @@ describe("client", () => { error: "missing_channel", }, }; - try { - mocks.core.getInput.reset(); - mocks.core.getBooleanInput.withArgs("errors").returns(true); - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); - mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`); - mocks.calls.rejects(errors.platformErrorFromResult(response)); - await send(mocks.core); - assert.fail("Expected an error but none was found"); - } catch (_err) { - assert.ok(mocks.core.setFailed.called); - assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); - assert.equal(mocks.core.setOutput.getCall(0).lastArg, false); - assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); - assert.deepEqual( - mocks.core.setOutput.getCall(1).lastArg, - JSON.stringify(response), - ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); - } + mocks.core.getInput.reset(); + mocks.core.getBooleanInput.withArgs("errors").returns(true); + mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`); + mocks.calls.rejects(errors.platformErrorFromResult(response)); + await assert.rejects(() => send(mocks.core)); + assert.ok(mocks.core.setFailed.called); + assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); + assert.equal(mocks.core.setOutput.getCall(0).lastArg, false); + assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); + assert.deepEqual( + mocks.core.setOutput.getCall(1).lastArg, + JSON.stringify(response), + ); + assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); + assert.equal(mocks.core.setOutput.getCalls().length, 3); }); it("returns the api error and details without a exit failing", async () => { @@ -398,25 +358,21 @@ describe("client", () => { error: "missing_channel", }, }; - try { - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); - mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`); - mocks.calls.rejects(errors.platformErrorFromResult(response)); - await send(mocks.core); - assert.fail("Expected an error but none was found"); - } catch (_err) { - assert.strictEqual(mocks.core.setFailed.called, false); - assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); - assert.equal(mocks.core.setOutput.getCall(0).lastArg, false); - assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); - assert.deepEqual( - mocks.core.setOutput.getCall(1).lastArg, - JSON.stringify(response), - ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); - } + mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`); + mocks.calls.rejects(errors.platformErrorFromResult(response)); + await send(mocks.core); + assert.strictEqual(mocks.core.setFailed.called, false); + assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); + assert.equal(mocks.core.setOutput.getCall(0).lastArg, false); + assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); + assert.deepEqual( + mocks.core.setOutput.getCall(1).lastArg, + JSON.stringify(response), + ); + assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); + assert.equal(mocks.core.setOutput.getCalls().length, 3); }); it("errors if rate limit responses are returned after retries", async () => { @@ -424,25 +380,21 @@ describe("client", () => { code: "slack_webapi_rate_limited_error", retryAfter: 12, }; - try { - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); - mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`); - mocks.calls.rejects(errors.rateLimitedErrorWithDelay(12)); - await send(mocks.core); - assert.fail("Expected an error but none was found"); - } catch (_err) { - assert.strictEqual(mocks.core.setFailed.called, false); - assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); - assert.equal(mocks.core.setOutput.getCall(0).lastArg, false); - assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); - assert.deepEqual( - mocks.core.setOutput.getCall(1).lastArg, - JSON.stringify(response), - ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); - } + mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`); + mocks.calls.rejects(errors.rateLimitedErrorWithDelay(12)); + await send(mocks.core); + assert.strictEqual(mocks.core.setFailed.called, false); + assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); + assert.equal(mocks.core.setOutput.getCall(0).lastArg, false); + assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); + assert.deepEqual( + mocks.core.setOutput.getCall(1).lastArg, + JSON.stringify(response), + ); + assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); + assert.equal(mocks.core.setOutput.getCalls().length, 3); }); }); @@ -464,20 +416,12 @@ describe("client", () => { mocks.core.getInput.withArgs("method").returns("chat.postMessage"); mocks.core.getInput.withArgs("proxy").returns(proxy); mocks.core.getInput.withArgs("token").returns("xoxb-example"); - try { - const config = new Config(mocks.core); - const client = new Client(); - client.proxies(config); - assert.fail("An invalid proxy URL was not thrown as error!"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes("Failed to configure the HTTPS proxy"), - ); - } else { - assert.fail(err); - } - } + const config = new Config(mocks.core); + const client = new Client(); + assert.throws(() => client.proxies(config), { + message: "Failed to configure the HTTPS proxy", + name: "SlackError", + }); }); }); diff --git a/test/config.spec.js b/test/config.spec.js index 17c292f3..4960e972 100644 --- a/test/config.spec.js +++ b/test/config.spec.js @@ -1,5 +1,5 @@ import assert from "node:assert"; -import { beforeEach, describe, it } from "node:test"; +import { afterEach, beforeEach, describe, it } from "node:test"; import webapi from "@slack/web-api"; import sinon from "sinon"; import Config from "../src/config.js"; @@ -64,127 +64,86 @@ describe("config", () => { assert.ok(mocks.core.setSecret.withArgs("https://example.com").called); }); - it("errors when both the token and webhook is provided", async () => { + it("errors when both the token and webhook is provided", () => { mocks.core.getInput.withArgs("token").returns("xoxb-example"); mocks.core.getInput.withArgs("webhook").returns("https://example.com"); - try { - new Config(mocks.core); - assert.fail("Failed to error when invalid inputs are provided"); - } catch (err) { - if (err instanceof SlackError) { + assert.throws( + () => new Config(mocks.core), + (err) => { + assert.ok(err instanceof SlackError); assert.ok( - err.message.includes( - "Invalid input! Either the token or webhook is required - not both.", - ), + err.message, + "Invalid input! Either the token or webhook is required - not both.", ); assert.ok(mocks.core.setSecret.withArgs("xoxb-example").called); assert.ok( mocks.core.setSecret.withArgs("https://example.com").called, ); - } else { - assert.fail(err); - } - } + return true; + }, + ); }); - it("errors if the method is provided without a token", async () => { + it("errors if the method is provided without a token", () => { mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - try { - new Config(mocks.core); - assert.fail("Failed to error when invalid inputs are provided"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes( - "Missing input! A token must be provided to use the method decided.", - ), - ); - } else { - assert.fail(err); - } - } + assert.throws(() => new Config(mocks.core), { + message: + "Missing input! A token must be provided to use the method decided.", + name: "SlackError", + }); }); - it("errors if neither the token or webhook is provided", async () => { - try { - new Config(mocks.core); - assert.fail("Failed to error when invalid inputs are provided"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes( - "Missing input! Either a method or webhook is required to take action.", - ), - ); - } else { - assert.fail(err); - } - } + it("errors if neither the token or webhook is provided", () => { + assert.throws(() => new Config(mocks.core), { + message: + "Missing input! Either a method or webhook is required to take action.", + name: "SlackError", + }); }); - it("errors if a webhook is provided without the type", async () => { + it("errors if a webhook is provided without the type", () => { mocks.core.getInput.withArgs("webhook").returns("https://example.com"); - try { - new Config(mocks.core); - assert.fail("Failed to error when invalid inputs are provided"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes( - "Missing input! The webhook type must be 'incoming-webhook' or 'webhook-trigger'.", - ), - ); - } else { - assert.fail(err); - } - } + assert.throws(() => new Config(mocks.core), { + message: + "Missing input! The webhook type must be 'incoming-webhook' or 'webhook-trigger'.", + name: "SlackError", + }); }); it("errors if the webhook type does not match techniques", async () => { mocks.core.getInput.withArgs("webhook").returns("https://example.com"); mocks.core.getInput.withArgs("webhook-type").returns("post"); - try { - new Config(mocks.core); - assert.fail("Failed to error when invalid inputs are provided"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes( - "Invalid input! The webhook type must be 'incoming-webhook' or 'webhook-trigger'.", - ), - ); - } else { - assert.fail(err); - } - } + assert.throws(() => new Config(mocks.core), { + message: + "Invalid input! The webhook type must be 'incoming-webhook' or 'webhook-trigger'.", + name: "SlackError", + }); }); }); describe("instrument", () => { - it("adds metadata to webapi with package name and version", async () => { + const original = Object.getOwnPropertyDescriptor(webapi, "addAppMetadata"); + + afterEach(() => { + Object.defineProperty(webapi, "addAppMetadata", original); + }); + + it("adds metadata to webapi with package name and version", () => { const stub = sinon.stub(); - const original = Object.getOwnPropertyDescriptor( - webapi, - "addAppMetadata", - ); Object.defineProperty(webapi, "addAppMetadata", { value: stub, configurable: true, }); - try { - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); - new Config(mocks.core); - assert.ok(stub.calledOnce); - const { name, version } = stub.firstCall.args[0]; - assert.equal(name, "@slack/slack-github-action"); - assert.ok(version); - } finally { - Object.defineProperty(webapi, "addAppMetadata", original); - } + mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.core.getInput.withArgs("token").returns("xoxb-example"); + new Config(mocks.core); + assert.ok(stub.calledOnce); + const { name, version } = stub.firstCall.args[0]; + assert.equal(name, "@slack/slack-github-action"); + assert.ok(version); }); - it("adds metadata to webhook with package name and version", async () => { + it("adds metadata to webhook with package name and version", () => { mocks.core.getInput.withArgs("method").returns("chat.postMessage"); mocks.core.getInput.withArgs("token").returns("xoxb-example"); const config = new Config(mocks.core); @@ -200,72 +159,49 @@ describe("config", () => { }); }); - describe("mask", async () => { + describe("mask", () => { it("treats the provided token as a secret", async () => { mocks.core.getInput.withArgs("token").returns("xoxb-example"); - try { - await send(mocks.core); - assert.fail("Failed to error for incomplete inputs while testing"); - } catch { - assert.ok(mocks.core.setSecret.withArgs("xoxb-example").called); - } + await assert.rejects( + () => send(mocks.core), + (_) => { + assert.ok(mocks.core.setSecret.withArgs("xoxb-example").called); + return true; + }, + ); }); it("treats the provided webhook as a secret", async () => { mocks.core.getInput.withArgs("webhook").returns("https://slack.com"); mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); - try { - await send(mocks.core); - assert.fail("Failed to error for incomplete inputs while testing"); - } catch { - assert.ok(mocks.core.setSecret.withArgs("https://slack.com").called); - } + await send(mocks.core); + assert.ok(mocks.core.setSecret.withArgs("https://slack.com").called); }); }); describe("validate", () => { it('allow the "retries" option with lowercased space', async () => { - mocks.axios.post.returns(Promise.resolve("LGTM")); + mocks.axios.post.resolves("LGTM"); mocks.core.getInput.withArgs("retries").returns(" rapid "); mocks.core.getInput .withArgs("webhook") .returns("https://hooks.slack.com"); mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); - try { - await send(mocks.core); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes( - 'Invalid input! An unknown "retries" value was used: FOREVER', - ), - ); - } else { - assert.fail(err); - } - } + + await send(mocks.core); }); it("errors if an invalid retries option is provided", async () => { - mocks.axios.post.returns(Promise.resolve("LGTM")); + mocks.axios.post.resolves("LGTM"); mocks.core.getInput.withArgs("retries").returns("FOREVER"); mocks.core.getInput .withArgs("webhook") .returns("https://hooks.slack.com"); mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); - try { - await send(mocks.core); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes( - 'Invalid input! An unknown "retries" value was used: FOREVER', - ), - ); - } else { - assert.fail(err); - } - } + await assert.rejects(() => send(mocks.core), { + message: 'Invalid input! An unknown "retries" value was used: FOREVER', + name: "SlackError", + }); }); }); }); diff --git a/test/content.spec.js b/test/content.spec.js index 5288d61b..92c1b957 100644 --- a/test/content.spec.js +++ b/test/content.spec.js @@ -40,24 +40,16 @@ describe("content", () => { 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.ok( - err.message.includes( - "Invalid input! Just the payload or payload file path is required.", - ), - ); - } else { - assert.fail(err); - } - } + + await assert.rejects(() => send(mocks.core), { + message: + "Invalid input! Just the payload or payload file path is required.", + name: "SlackError", + }); }); }); - describe("payload", async () => { + describe("payload", () => { it("parses complete YAML from the input payload", async () => { mocks.core.getInput.withArgs("payload").returns(` message: "this is wrapped" @@ -274,47 +266,35 @@ describe("content", () => { payloadFilePath: "unknown.json", }, }; - try { - new Content().getContentPayload(config); - assert.fail("Failed to throw for missing payload content"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes( - "Invalid input! No payload content was provided", - ), - ); - } else { - assert.fail(err); - } - } + assert.throws(() => new Content().getContentPayload(config), { + message: "Invalid input! No payload content was provided", + name: "SlackError", + }); }); 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.ok( - err.message.includes( - "Invalid input! Failed to parse contents of the provided payload", - ), + await assert.rejects( + () => send(mocks.core), + (err) => { + assert.ok(err instanceof SlackError); + assert.equal( + err.message, + "Invalid input! Failed to parse contents of the provided payload", ); assert.notStrictEqual(err.cause?.values, undefined); assert.equal(err.cause.values.length, 2); const [jsonError, yamlError] = err.cause.values; assert.ok(jsonError instanceof SyntaxError); assert.ok(yamlError instanceof YAMLException); - } else { - assert.fail(err); - } - } + + return true; + }, + ); }); }); - describe("payload file", async () => { + describe("payload file", () => { it("parses complete YAML from the input payload file", async () => { mocks.core.getInput.withArgs("payload-file-path").returns("example.yaml"); mocks.fs.readFileSync @@ -538,61 +518,41 @@ describe("content", () => { payload: "LGTM", }, }; - try { - new Content().getContentPayloadFilePath(config); - assert.fail("Failed to throw for the wrong payload type"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes("Invalid input! No payload found for content"), - ); - } else { - assert.fail(err); - } - } + assert.throws(() => new Content().getContentPayloadFilePath(config), { + message: "Invalid input! No payload found for content", + name: "SlackError", + }); }); 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.ok( - err.message.includes( - "Invalid input! Failed to parse contents of the provided payload file", - ), - ); - } else { - assert.fail(err); - } - } + await assert.rejects(() => send(mocks.core), { + message: + "Invalid input! Failed to parse contents of the provided payload file", + name: "SlackError", + }); }); 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.ok( - err.message.includes( - "Invalid input! Failed to parse contents of the provided payload file", - ), + + await assert.rejects( + () => send(mocks.core), + (err) => { + assert.ok(err instanceof SlackError); + assert.equal( + err.message, + "Invalid input! Failed to parse contents of the provided payload file", ); assert.notStrictEqual(err.cause?.values, undefined); assert.equal(err.cause.values.length, 1); - assert.ok( - err.cause.values[0].message.includes( - "Invalid input! Failed to parse file extension unknown.md", - ), + assert.equal( + err.cause.values[0].message, + "Invalid input! Failed to parse file extension unknown.md", ); - } else { - assert.fail(err); - } - } + return true; + }, + ); }); it("fails if invalid JSON exists in the input payload", async () => { @@ -601,23 +561,21 @@ describe("content", () => { .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.ok( - err.message.includes( - "Invalid input! Failed to parse contents of the provided payload file", - ), + + await assert.rejects( + () => send(mocks.core), + (err) => { + assert.ok(err instanceof SlackError); + assert.equal( + err.message, + "Invalid input! Failed to parse contents of the provided payload file", ); assert.notStrictEqual(err.cause?.values, undefined); assert.equal(err.cause.values.length, 1); assert.ok(err.cause.values[0] instanceof SyntaxError); - } else { - assert.fail(err); - } - } + return true; + }, + ); }); it("fails if invalid YAML exists in the input payload", async () => { @@ -625,23 +583,21 @@ describe("content", () => { 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) { + + await assert.rejects( + () => send(mocks.core), + (err) => { + assert.ok(err instanceof SlackError); assert.ok( - err.message.includes( - "Invalid input! Failed to parse contents of the provided payload file", - ), + err.message, + "Invalid input! Failed to parse contents of the provided payload file", ); assert.notStrictEqual(err.cause?.values, undefined); assert.equal(err.cause.values.length, 1); assert.ok(err.cause.values[0] instanceof YAMLException); - } else { - assert.fail(err); - } - } + return true; + }, + ); }); }); }); diff --git a/test/logger.spec.js b/test/logger.spec.js index a246f4fb..37af7e96 100644 --- a/test/logger.spec.js +++ b/test/logger.spec.js @@ -1,4 +1,4 @@ -import assert from "node:assert"; +import assert from "node:assert/strict"; import { beforeEach, describe, it } from "node:test"; import { LogLevel } from "@slack/logger"; import Logger from "../src/logger.js"; @@ -10,20 +10,15 @@ describe("logger", () => { }); describe("level", () => { - it("debug", () => { - mocks.core.isDebug.returns(true); - const { logger } = new Logger(mocks.core); - const actual = logger.getLevel(); - const expected = LogLevel.DEBUG; - assert.strictEqual(actual, expected); - }); - - it("info", () => { - mocks.core.isDebug.returns(false); - const { logger } = new Logger(mocks.core); - const actual = logger.getLevel(); - const expected = LogLevel.INFO; - assert.strictEqual(actual, expected); + [ + ["debug", true], + ["info", false], + ].forEach(([label, isDebug]) => { + it(label, () => { + mocks.core.isDebug.returns(isDebug); + const { logger } = new Logger(mocks.core); + assert.strictEqual(logger.getLevel(), LogLevel[label.toUpperCase()]); + }); }); }); }); diff --git a/test/send.spec.js b/test/send.spec.js index 9ab906ee..f71ae992 100644 --- a/test/send.spec.js +++ b/test/send.spec.js @@ -17,7 +17,7 @@ describe("send", () => { mocks.reset(); }); - describe("techniques", async () => { + describe("techniques", () => { it("webhook trigger", async () => { mocks.core.getInput .withArgs("webhook") @@ -64,7 +64,7 @@ describe("send", () => { .returns("https://hooks.slack.com"); mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); mocks.core.getInput.withArgs("payload").returns('"text": "hello"'); - mocks.axios.post.returns(Promise.resolve({ status: 200, data: "ok" })); + mocks.axios.post.resolves({ status: 200, data: "ok" }); await send(mocks.core); assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); diff --git a/test/webhook.spec.js b/test/webhook.spec.js index 09867e68..f5aab16c 100644 --- a/test/webhook.spec.js +++ b/test/webhook.spec.js @@ -19,27 +19,20 @@ describe("webhook", () => { .returns("https://hooks.slack.com"); mocks.core.getInput.withArgs("webhook-type").returns("webhook-trigger"); mocks.core.getInput.withArgs("payload").returns("drinks: coffee"); - mocks.axios.post.returns( - Promise.resolve({ status: 200, data: { ok: true } }), + mocks.axios.post.resolves({ status: 200, data: { ok: true } }); + await send(mocks.core); + assert.equal(mocks.axios.post.getCalls().length, 1); + const [url, payload, options] = mocks.axios.post.getCall(0).args; + assert.equal(url, "https://hooks.slack.com"); + assert.deepEqual(payload, { drinks: "coffee" }); + assert.deepEqual(options, {}); + assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); + assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); + assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); + assert.equal( + mocks.core.setOutput.getCall(1).lastArg, + JSON.stringify({ ok: true }), ); - try { - await send(mocks.core); - assert.equal(mocks.axios.post.getCalls().length, 1); - const [url, payload, options] = mocks.axios.post.getCall(0).args; - assert.equal(url, "https://hooks.slack.com"); - assert.deepEqual(payload, { drinks: "coffee" }); - assert.deepEqual(options, {}); - assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); - assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); - assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); - assert.equal( - mocks.core.setOutput.getCall(1).lastArg, - JSON.stringify({ ok: true }), - ); - } catch (err) { - console.error(err); - assert.fail("Failed to send the webhook"); - } }); it("sends the parsed payload to the provided incoming webhook", async () => { @@ -48,25 +41,20 @@ describe("webhook", () => { .returns("https://hooks.slack.com"); mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); mocks.core.getInput.withArgs("payload").returns("text: greetings"); - mocks.axios.post.returns(Promise.resolve({ status: 200, data: "ok" })); - try { - await send(mocks.core); - assert.equal(mocks.axios.post.getCalls().length, 1); - const [url, payload, options] = mocks.axios.post.getCall(0).args; - assert.equal(url, "https://hooks.slack.com"); - assert.deepEqual(payload, { text: "greetings" }); - assert.deepEqual(options, {}); - assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); - assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); - assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); - assert.equal( - mocks.core.setOutput.getCall(1).lastArg, - JSON.stringify("ok"), - ); - } catch (err) { - console.error(err); - assert.fail("Failed to send the webhook"); - } + mocks.axios.post.resolves({ status: 200, data: "ok" }); + await send(mocks.core); + assert.equal(mocks.axios.post.getCalls().length, 1); + const [url, payload, options] = mocks.axios.post.getCall(0).args; + assert.equal(url, "https://hooks.slack.com"); + assert.deepEqual(payload, { text: "greetings" }); + assert.deepEqual(options, {}); + assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok"); + assert.equal(mocks.core.setOutput.getCall(0).lastArg, true); + assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response"); + assert.equal( + mocks.core.setOutput.getCall(1).lastArg, + JSON.stringify("ok"), + ); }); }); @@ -79,16 +67,10 @@ describe("webhook", () => { core: mocks.core, inputs: {}, }; - try { - await new Webhook().post(config); - assert.fail("Failed to throw for missing input"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok(err.message.includes("No webhook was provided to post to")); - } else { - assert.fail(err); - } - } + await assert.rejects(() => new Webhook().post(config), { + message: /No webhook was provided to post to/, + name: "SlackError", + }); }); it("returns the failures from a webhook trigger", async () => { @@ -104,18 +86,8 @@ describe("webhook", () => { {}, { status: 400 }, ); - mocks.axios.post.resolves(Promise.reject(response)); - try { - await send(mocks.core); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes("Request failed with status code 400"), - ); - } else { - assert.fail(err); - } - } + mocks.axios.post.rejects(response); + await send(mocks.core); assert.equal(mocks.axios.post.getCalls().length, 1); const [url, payload, options] = mocks.axios.post.getCall(0).args; assert.equal(url, "https://hooks.slack.com"); @@ -139,18 +111,8 @@ describe("webhook", () => { {}, { status: 400 }, ); - mocks.axios.post.resolves(Promise.reject(response)); - try { - await send(mocks.core); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes("Request failed with status code 400"), - ); - } else { - assert.fail(err); - } - } + mocks.axios.post.rejects(response); + await send(mocks.core); assert.equal(mocks.axios.post.getCalls().length, 1); const [url, payload, options] = mocks.axios.post.getCall(0).args; assert.equal(url, "https://hooks.slack.com"); @@ -163,7 +125,7 @@ describe("webhook", () => { }); describe("proxies", () => { - it("requires a webhook is included in the inputs", async () => { + it("requires a webhook is included in the inputs", () => { /** * @type {Config} */ @@ -171,18 +133,10 @@ describe("webhook", () => { core: mocks.core, inputs: {}, }; - try { - new Webhook().proxies(config); - assert.fail("Failed to throw for missing input"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes("No webhook was provided to proxy to"), - ); - } else { - assert.fail(err); - } - } + assert.throws(() => new Webhook().proxies(config), { + message: "No webhook was provided to proxy to", + name: "SlackError", + }); }); it("skips proxying an http webhook url altogether", async () => { @@ -223,51 +177,37 @@ describe("webhook", () => { assert.strictEqual(proxying, false); }); - it("fails to configure proxies with an invalid proxied url", async () => { + it("fails to configure proxies with an invalid proxied url", () => { const proxy = "https://"; mocks.core.getInput .withArgs("webhook") .returns("https://hooks.slack.com"); mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); mocks.core.getInput.withArgs("proxy").returns(proxy); - try { - const config = new Config(mocks.core); - const webhook = new Webhook(); - webhook.proxies(config); - assert.fail("An invalid proxy URL was not thrown as error!"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes("Failed to configure the HTTPS proxy"), - ); - } else { - assert.fail(err); - } - } + const config = new Config(mocks.core); + assert.throws(() => new Webhook().proxies(config), { + message: "Failed to configure the HTTPS proxy", + name: "SlackError", + }); }); - it("fails to configure proxies with an unknown url protocol", async () => { + it("fails to configure proxies with an unknown url protocol", () => { const proxy = "ssh://"; mocks.core.getInput .withArgs("webhook") .returns("https://hooks.slack.com"); mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); mocks.core.getInput.withArgs("proxy").returns(proxy); - try { - const config = new Config(mocks.core); - const webhook = new Webhook(); - webhook.proxies(config); - assert.fail("An unknown URL protocol was not thrown as error!"); - } catch (err) { - if (err instanceof SlackError) { - assert.ok( - err.message.includes("Failed to configure the HTTPS proxy"), - ); - assert.ok(err.cause.message.includes("Unsupported URL protocol")); - } else { - assert.fail(err); - } - } + const config = new Config(mocks.core); + assert.throws( + () => new Webhook().proxies(config), + (err) => { + assert.ok(err instanceof SlackError); + assert.equal(err.message, "Failed to configure the HTTPS proxy"); + assert.ok(err.cause.message.startsWith("Unsupported URL protocol")); + return true; + }, + ); }); });