From df84d6ba728f03917a373918b19f9afa0f7e3cca Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 3 Nov 2026 12:00:34 -0800 Subject: [PATCH] test: replace sinon with node:test mocks Co-Authored-By: Claude --- package-lock.json | 90 ----------- package.json | 2 - test/client.spec.js | 346 +++++++++++++++++++++++++------------------ test/config.spec.js | 143 +++++++++++------- test/content.spec.js | 173 +++++++++++++--------- test/index.spec.js | 97 ++++++------ test/logger.spec.js | 4 +- test/proxies.spec.js | 60 ++++---- test/send.spec.js | 132 +++++++++-------- test/webhook.spec.js | 108 +++++++------- 10 files changed, 594 insertions(+), 561 deletions(-) diff --git a/package-lock.json b/package-lock.json index f022317d..07362b6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,9 +26,7 @@ "@types/js-yaml": "^4.0.9", "@types/markup-js": "^1.5.0", "@types/node": "^24.19.2", - "@types/sinon": "^22.0.2", "@vercel/ncc": "^0.45.0", - "sinon": "^22.1.0", "typescript": "^7.0.2" }, "engines": { @@ -780,47 +778,6 @@ "url": "https://opencollective.com/pnpm" } }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "15.4.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", - "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-10.0.2.tgz", - "integrity": "sha512-8lVwD1Df1BmzoaOLhMcGGcz/Jyr5QY2KSB75/YK1QgKzoabTeLdIVyhXNZK9ojfSKSdirbXqdbsXXqP9/Ve8+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1", - "type-detect": "^4.1.0" - } - }, - "node_modules/@sinonjs/samsam/node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/@slack/logger": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-5.0.0.tgz", @@ -929,16 +886,6 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "license": "MIT" }, - "node_modules/@types/sinon": { - "version": "22.0.2", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-22.0.2.tgz", - "integrity": "sha512-2qLRFlluKkn6mci189eKhlOsHcdFpsn/9+29S4VmwKCpfb+vseKrZSSOSy5259yzIpP/y4gLfqQfIIR/lZn4Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinonjs/fake-timers": "^15.4.0" - } - }, "node_modules/@typescript/typescript-aix-ppc64": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", @@ -1321,16 +1268,6 @@ "node": ">=20.19.0" } }, - "node_modules/diff": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", - "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -1604,23 +1541,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sinon": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-22.1.0.tgz", - "integrity": "sha512-n1ajF2rBWMTtEwbKcw4UdFg4nCnDdq/U6RDoxtOd7oapOlRoJ5ynwFx60owROyhDpA9QhMZi0pCO/xtmwFjG7w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^15.4.0", - "@sinonjs/samsam": "^10.0.2", - "diff": "^9.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -1664,16 +1584,6 @@ "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/typescript": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", diff --git a/package.json b/package.json index 60fa613a..454da376 100644 --- a/package.json +++ b/package.json @@ -62,9 +62,7 @@ "@types/js-yaml": "^4.0.9", "@types/markup-js": "^1.5.0", "@types/node": "^24.19.2", - "@types/sinon": "^22.0.2", "@vercel/ncc": "^0.45.0", - "sinon": "^22.1.0", "typescript": "^7.0.2" } } diff --git a/test/client.spec.js b/test/client.spec.js index b9e69f8d..a0af38d8 100644 --- a/test/client.spec.js +++ b/test/client.spec.js @@ -1,7 +1,6 @@ import assert from "node:assert"; -import { beforeEach, describe, it } from "node:test"; +import { beforeEach, describe, it, mock } from "node:test"; import webapi from "@slack/web-api"; -import sinon from "sinon"; import Client from "../src/client.js"; import Config from "../src/config.js"; import SlackError from "../src/errors.js"; @@ -47,7 +46,7 @@ describe("client", () => { method: "chat.postMessage", }, }; - mocks.core.getInput.withArgs("token").returns("xoxb-example-001"); + mocks.inputs.set("token", "xoxb-example-001"); try { await new Client().post(config); assert.fail("Failed to throw for missing input"); @@ -63,10 +62,10 @@ describe("client", () => { describe("api", async () => { it("uses arguments to send to a slack api method", async () => { - const apis = sinon.stub().resolves({ ok: true }); - const constructors = sinon - .stub(mocks.webapi, "WebClient") - .returns({ apiCall: apis }); + const apis = mock.fn(async () => ({ ok: true })); + const constructors = mock.method(mocks.webapi, "WebClient", function () { + this.apiCall = apis; + }); /** * @type {Config} */ @@ -86,31 +85,36 @@ describe("client", () => { webapi: mocks.webapi, }; await new Client().post(config); - assert.ok(constructors.calledWithNew()); - assert.ok( - constructors.calledWith("xoxb-example-002", { + assert.ok(constructors.mock.calls[0].target); + assert.deepEqual(constructors.mock.calls[0].arguments, [ + "xoxb-example-002", + { fetch: undefined, allowAbsoluteUrls: false, logger: config.logger, retryConfig: webapi.retryPolicies.fiveRetriesInFiveMinutes, slackApiUrl: undefined, - }), - ); - assert.ok(apis.calledOnce); - assert.ok( - apis.calledWith("pins.add", { + }, + ]); + assert.equal(apis.mock.callCount(), 1); + assert.deepEqual(apis.mock.calls[0].arguments, [ + "pins.add", + { channel: "CHANNELHERE", timestamp: "1234567890.000000", - }), - ); - assert.ok(config.core.setOutput.calledWith("ok", true)); + }, + ]); + assert.deepEqual(config.core.setOutput.mock.calls[0].arguments, [ + "ok", + true, + ]); }); it("uses arguments to send to a custom api method", async () => { - const apis = sinon.stub().resolves({ done: true, response: "Infinite" }); - const constructors = sinon - .stub(mocks.webapi, "WebClient") - .returns({ apiCall: apis }); + const apis = mock.fn(async () => ({ done: true, response: "Infinite" })); + const constructors = mock.method(mocks.webapi, "WebClient", function () { + this.apiCall = apis; + }); /** * @type {Config} */ @@ -133,31 +137,34 @@ describe("client", () => { webapi: mocks.webapi, }; await new Client().post(config); - assert.ok(constructors.calledWithNew()); - assert.ok( - constructors.calledWith("ollamapassword", { + assert.ok(constructors.mock.calls[0].target); + assert.deepEqual(constructors.mock.calls[0].arguments, [ + "ollamapassword", + { fetch: undefined, allowAbsoluteUrls: false, logger: config.logger, retryConfig: webapi.retryPolicies.tenRetriesInAboutThirtyMinutes, slackApiUrl: "http://localhost:11434/api/", - }), - ); - assert.ok(apis.calledOnce); - assert.ok( - apis.calledWith("generate", { + }, + ]); + assert.equal(apis.mock.callCount(), 1); + assert.deepEqual(apis.mock.calls[0].arguments, [ + "generate", + { model: "llama3.2", prompt: "How many sides does a circle have?", stream: false, - }), - ); - assert.ok(config.core.setOutput.calledWith("ok", undefined)); - assert.ok( - config.core.setOutput.calledWith( - "response", - JSON.stringify({ done: true, response: "Infinite" }), - ), - ); + }, + ]); + assert.deepEqual(config.core.setOutput.mock.calls[0].arguments, [ + "ok", + undefined, + ]); + assert.deepEqual(config.core.setOutput.mock.calls[1].arguments, [ + "response", + JSON.stringify({ done: true, response: "Infinite" }), + ]); }); }); @@ -177,34 +184,49 @@ describe("client", () => { 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); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("payload", JSON.stringify(args)); + mocks.calls.mock.mockImplementation(async () => 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.deepEqual( + mocks.calls.mock.calls[0].arguments[0], + "chat.postMessage", + ); + assert.deepEqual(mocks.calls.mock.calls[0].arguments[1], args); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], true); assert.equal( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[0], + "response", + ); + assert.equal( + mocks.core.setOutput.mock.calls[1].arguments[1], 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, + mocks.core.setOutput.mock.calls[2].arguments[0], + "channel_id", + ); + assert.equal( + mocks.core.setOutput.mock.calls[2].arguments[1], + "C0123456789", + ); + assert.equal( + mocks.core.setOutput.mock.calls[3].arguments[0], + "thread_ts", + ); + assert.equal( + mocks.core.setOutput.mock.calls[3].arguments[1], "1234567890.000001", ); - assert.equal(mocks.core.setOutput.getCall(4).firstArg, "ts"); + assert.equal(mocks.core.setOutput.mock.calls[4].arguments[0], "ts"); assert.equal( - mocks.core.setOutput.getCall(4).lastArg, + mocks.core.setOutput.mock.calls[4].arguments[1], "1234567890.000002", ); - assert.equal(mocks.core.setOutput.getCall(5).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 6); + assert.equal(mocks.core.setOutput.mock.calls[5].arguments[0], "time"); + assert.equal(mocks.core.setOutput.mock.callCount(), 6); } catch (err) { console.error(err); assert.fail("Unexpected error when calling the method"); @@ -225,24 +247,36 @@ describe("client", () => { 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); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("payload", JSON.stringify(args)); + mocks.calls.mock.mockImplementation(async () => 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.deepEqual( + mocks.calls.mock.calls[0].arguments[0], + "chat.postMessage", + ); + assert.deepEqual(mocks.calls.mock.calls[0].arguments[1], args); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], true); + assert.equal( + mocks.core.setOutput.mock.calls[1].arguments[0], + "response", + ); assert.equal( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[1], 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); + assert.equal( + mocks.core.setOutput.mock.calls[2].arguments[0], + "channel_id", + ); + assert.equal( + mocks.core.setOutput.mock.calls[2].arguments[1], + "C0101010101", + ); + assert.equal(mocks.core.setOutput.mock.calls[3].arguments[0], "time"); + assert.equal(mocks.core.setOutput.mock.callCount(), 4); } catch (err) { console.error(err); assert.fail("Unexpected error when calling the method"); @@ -261,21 +295,24 @@ describe("client", () => { 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); + mocks.inputs.set("method", "files.uploadV2"); + mocks.inputs.set("token", "xoxp-example"); + mocks.inputs.set("payload", JSON.stringify(args)); + mocks.calls.mock.mockImplementation(async () => 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.deepEqual(mocks.calls.mock.calls[0].arguments[1], args); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], true); + assert.equal( + mocks.core.setOutput.mock.calls[1].arguments[0], + "response", + ); assert.equal( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[1], JSON.stringify(response), ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); + assert.equal(mocks.core.setOutput.mock.calls[2].arguments[0], "time"); + assert.equal(mocks.core.setOutput.mock.callCount(), 3); } catch (err) { console.error(err); assert.fail("Unexpected error when calling the method"); @@ -287,25 +324,30 @@ describe("client", () => { it("errors when the request to the api cannot be sent correct", async () => { const response = new Error("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(new webapi.WebAPIRequestError(response)); + mocks.inputs.clear(); + mocks.booleanInputs.set("errors", true); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("payload", `"text": "hello"`); + mocks.calls.mock.mockImplementation(async () => { + throw new webapi.WebAPIRequestError(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.ok(mocks.core.setFailed.mock.callCount() > 0); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], false); + assert.equal( + mocks.core.setOutput.mock.calls[1].arguments[0], + "response", + ); assert.deepEqual( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[1], JSON.stringify(response), ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); + assert.equal(mocks.core.setOutput.mock.calls[2].arguments[0], "time"); + assert.equal(mocks.core.setOutput.mock.callCount(), 3); } }); @@ -319,93 +361,115 @@ describe("client", () => { { ok: false, 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(response); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("payload", `"text": "hello"`); + mocks.calls.mock.mockImplementation(async () => { + throw 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.strictEqual(mocks.core.setFailed.mock.callCount(), 0); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], false); + assert.equal( + mocks.core.setOutput.mock.calls[1].arguments[0], + "response", + ); assert.equal( - JSON.parse(mocks.core.setOutput.getCall(1).lastArg).statusCode, + JSON.parse(mocks.core.setOutput.mock.calls[1].arguments[1]) + .statusCode, 500, ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); + assert.equal(mocks.core.setOutput.mock.calls[2].arguments[0], "time"); + assert.equal(mocks.core.setOutput.mock.callCount(), 3); } }); it("errors when the payload arguments are invalid for the api", async () => { const response = { ok: false, 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(new webapi.WebAPIPlatformError(response)); + mocks.inputs.clear(); + mocks.booleanInputs.set("errors", true); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("payload", `"text": "hello"`); + mocks.calls.mock.mockImplementation(async () => { + throw new webapi.WebAPIPlatformError(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.ok(mocks.core.setFailed.mock.callCount() > 0); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], false); + assert.equal( + mocks.core.setOutput.mock.calls[1].arguments[0], + "response", + ); assert.deepEqual( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[1], JSON.stringify(response), ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); + assert.equal(mocks.core.setOutput.mock.calls[2].arguments[0], "time"); + assert.equal(mocks.core.setOutput.mock.callCount(), 3); } }); it("returns the api error and details without a exit failing", async () => { const response = { ok: false, 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(new webapi.WebAPIPlatformError(response)); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("payload", `"text": "hello"`); + mocks.calls.mock.mockImplementation(async () => { + throw new webapi.WebAPIPlatformError(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.strictEqual(mocks.core.setFailed.mock.callCount(), 0); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], false); + assert.equal( + mocks.core.setOutput.mock.calls[1].arguments[0], + "response", + ); assert.deepEqual( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[1], JSON.stringify(response), ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); + assert.equal(mocks.core.setOutput.mock.calls[2].arguments[0], "time"); + assert.equal(mocks.core.setOutput.mock.callCount(), 3); } }); it("errors if rate limit responses are returned after retries", async () => { 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(new webapi.WebAPIRateLimitedError(12)); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("payload", `"text": "hello"`); + mocks.calls.mock.mockImplementation(async () => { + throw new webapi.WebAPIRateLimitedError(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.strictEqual(mocks.core.setFailed.mock.callCount(), 0); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], false); + assert.equal( + mocks.core.setOutput.mock.calls[1].arguments[0], + "response", + ); assert.equal( - JSON.parse(mocks.core.setOutput.getCall(1).lastArg).retryAfter, + JSON.parse(mocks.core.setOutput.mock.calls[1].arguments[1]) + .retryAfter, 12, ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.equal(mocks.core.setOutput.getCalls().length, 3); + assert.equal(mocks.core.setOutput.mock.calls[2].arguments[0], "time"); + assert.equal(mocks.core.setOutput.mock.callCount(), 3); } }); }); diff --git a/test/config.spec.js b/test/config.spec.js index 029f7eb7..c0c3b76b 100644 --- a/test/config.spec.js +++ b/test/config.spec.js @@ -1,8 +1,7 @@ import assert from "node:assert"; -import { beforeEach, describe, it } from "node:test"; +import { beforeEach, describe, it, mock } from "node:test"; import webapi from "@slack/web-api"; import webhook from "@slack/webhook"; -import sinon from "sinon"; import Config from "../src/config.js"; import SlackError from "../src/errors.js"; import send from "../src/send.js"; @@ -23,13 +22,13 @@ describe("config", () => { describe("inputs", () => { it("valid values are collected from the action inputs", async () => { - mocks.core.getInput.withArgs("api").returns("http://localhost:8080"); - mocks.core.getBooleanInput.withArgs("errors").returns(true); - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("payload").returns('"hello": "world"'); - mocks.core.getInput.withArgs("proxy").returns("https://example.com"); - mocks.core.getInput.withArgs("retries").returns("0"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.inputs.set("api", "http://localhost:8080"); + mocks.booleanInputs.set("errors", true); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("payload", '"hello": "world"'); + mocks.inputs.set("proxy", "https://example.com"); + mocks.inputs.set("retries", "0"); + mocks.inputs.set("token", "xoxb-example"); const config = new Config(mocks.core); assert.equal(config.inputs.api, "http://localhost:8080"); assert.equal(config.inputs.errors, true); @@ -38,36 +37,56 @@ describe("config", () => { assert.equal(config.inputs.proxy, "https://example.com"); assert.equal(config.inputs.retries, config.Retries.ZERO); assert.equal(config.inputs.token, "xoxb-example"); - assert.ok(mocks.core.setSecret.withArgs("xoxb-example").called); + assert.ok( + mocks.core.setSecret.mock.calls.some( + (call) => call.arguments[0] === "xoxb-example", + ), + ); }); it("allows token environment variables with a webhook", async () => { process.env.SLACK_TOKEN = "xoxb-example"; - mocks.core.getInput.withArgs("webhook").returns("https://example.com"); - mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); + mocks.inputs.set("webhook", "https://example.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); const config = new Config(mocks.core); assert.equal(config.inputs.token, "xoxb-example"); assert.equal(config.inputs.webhook, "https://example.com"); assert.equal(config.inputs.webhookType, "incoming-webhook"); - assert.ok(mocks.core.setSecret.withArgs("xoxb-example").called); - assert.ok(mocks.core.setSecret.withArgs("https://example.com").called); + assert.ok( + mocks.core.setSecret.mock.calls.some( + (call) => call.arguments[0] === "xoxb-example", + ), + ); + assert.ok( + mocks.core.setSecret.mock.calls.some( + (call) => call.arguments[0] === "https://example.com", + ), + ); }); it("allows webhook environment variables with a token", async () => { process.env.SLACK_WEBHOOK_URL = "https://example.com"; - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); const config = new Config(mocks.core); assert.equal(config.inputs.method, "chat.postMessage"); assert.equal(config.inputs.token, "xoxb-example"); assert.equal(config.inputs.webhook, "https://example.com"); - assert.ok(mocks.core.setSecret.withArgs("xoxb-example").called); - assert.ok(mocks.core.setSecret.withArgs("https://example.com").called); + assert.ok( + mocks.core.setSecret.mock.calls.some( + (call) => call.arguments[0] === "xoxb-example", + ), + ); + assert.ok( + mocks.core.setSecret.mock.calls.some( + (call) => call.arguments[0] === "https://example.com", + ), + ); }); it("errors when both the token and webhook is provided", async () => { - mocks.core.getInput.withArgs("token").returns("xoxb-example"); - mocks.core.getInput.withArgs("webhook").returns("https://example.com"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("webhook", "https://example.com"); try { new Config(mocks.core); assert.fail("Failed to error when invalid inputs are provided"); @@ -78,9 +97,15 @@ describe("config", () => { "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, + mocks.core.setSecret.mock.calls.some( + (call) => call.arguments[0] === "xoxb-example", + ), + ); + assert.ok( + mocks.core.setSecret.mock.calls.some( + (call) => call.arguments[0] === "https://example.com", + ), ); } else { assert.fail(err); @@ -89,7 +114,7 @@ describe("config", () => { }); it("errors if the method is provided without a token", async () => { - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.inputs.set("method", "chat.postMessage"); try { new Config(mocks.core); assert.fail("Failed to error when invalid inputs are provided"); @@ -124,7 +149,7 @@ describe("config", () => { }); it("errors if a webhook is provided without the type", async () => { - mocks.core.getInput.withArgs("webhook").returns("https://example.com"); + mocks.inputs.set("webhook", "https://example.com"); try { new Config(mocks.core); assert.fail("Failed to error when invalid inputs are provided"); @@ -142,8 +167,8 @@ describe("config", () => { }); 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"); + mocks.inputs.set("webhook", "https://example.com"); + mocks.inputs.set("webhook-type", "post"); try { new Config(mocks.core); assert.fail("Failed to error when invalid inputs are provided"); @@ -163,7 +188,7 @@ describe("config", () => { describe("instrument", () => { it("adds metadata to webapi with package name and version", async () => { - const stub = sinon.stub(); + const stub = mock.fn(); const original = Object.getOwnPropertyDescriptor( webapi, "addAppMetadata", @@ -173,11 +198,11 @@ describe("config", () => { configurable: true, }); try { - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); new Config(mocks.core); - assert.ok(stub.calledOnce); - const { name, version } = stub.firstCall.args[0]; + assert.equal(stub.mock.callCount(), 1); + const { name, version } = stub.mock.calls[0].arguments[0]; assert.equal(name, "@slack/slack-github-action"); assert.ok(version); } finally { @@ -186,7 +211,7 @@ describe("config", () => { }); it("adds metadata to webhook with package name and version", async () => { - const stub = sinon.stub(); + const stub = mock.fn(); const original = Object.getOwnPropertyDescriptor( webhook, "addAppMetadata", @@ -196,15 +221,11 @@ describe("config", () => { configurable: true, }); try { - mocks.core.getInput - .withArgs("webhook") - .returns("https://hooks.slack.com"); - mocks.core.getInput - .withArgs("webhook-type") - .returns("incoming-webhook"); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); new Config(mocks.core); - assert.ok(stub.calledOnce); - const { name, version } = stub.firstCall.args[0]; + assert.equal(stub.mock.callCount(), 1); + const { name, version } = stub.mock.calls[0].arguments[0]; assert.equal(name, "@slack/slack-github-action"); assert.ok(version); } finally { @@ -215,35 +236,43 @@ describe("config", () => { describe("mask", async () => { it("treats the provided token as a secret", async () => { - mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.inputs.set("token", "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); + assert.ok( + mocks.core.setSecret.mock.calls.some( + (call) => call.arguments[0] === "xoxb-example", + ), + ); } }); 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"); + mocks.inputs.set("webhook", "https://slack.com"); + mocks.inputs.set("webhook-type", "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); + assert.ok( + mocks.core.setSecret.mock.calls.some( + (call) => call.arguments[0] === "https://slack.com", + ), + ); } }); }); describe("validate", () => { it('allow the "retries" option with lowercased space', async () => { - mocks.webhook.incoming.resolves({ text: "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"); + mocks.webhook.incoming.mock.mockImplementation(async () => ({ + text: "LGTM", + })); + mocks.inputs.set("retries", " rapid "); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); try { await send(mocks.core); } catch (err) { @@ -260,12 +289,12 @@ describe("config", () => { }); it("errors if an invalid retries option is provided", async () => { - mocks.webhook.incoming.resolves({ text: "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"); + mocks.webhook.incoming.mock.mockImplementation(async () => ({ + text: "LGTM", + })); + mocks.inputs.set("retries", "FOREVER"); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); try { await send(mocks.core); } catch (err) { diff --git a/test/content.spec.js b/test/content.spec.js index 072f6a52..836f816b 100644 --- a/test/content.spec.js +++ b/test/content.spec.js @@ -14,19 +14,22 @@ import { mocks } from "./index.spec.js"; describe("content", () => { beforeEach(() => { mocks.reset(); - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); }); describe("flatten", () => { it("flattens nested payloads provided with delimiter", async () => { - mocks.core.getInput.withArgs("payload").returns(` + mocks.inputs.set( + "payload", + ` "apples": "tree", "bananas": { "truthiness": true } - `); - mocks.core.getInput.withArgs("payload-delimiter").returns("_"); + `, + ); + mocks.inputs.set("payload-delimiter", "_"); const config = new Config(mocks.core); const expected = { apples: "tree", @@ -38,8 +41,8 @@ describe("content", () => { 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"); + mocks.inputs.set("payload", `"message"="hello"`); + mocks.inputs.set("payload-file-path", "example.json"); try { await send(mocks.core); assert.fail("Failed to throw for invalid input"); @@ -59,10 +62,13 @@ describe("content", () => { describe("payload", async () => { it("parses complete YAML from the input payload", async () => { - mocks.core.getInput.withArgs("payload").returns(` + mocks.inputs.set( + "payload", + ` message: "this is wrapped" channel: "C0123456789" - `); + `, + ); const config = new Config(mocks.core); const expected = { message: "this is wrapped", @@ -75,9 +81,10 @@ describe("content", () => { * @see {@link https://github.com/slackapi/slack-github-action/issues/637} */ it("parses multiline YAML from the input payload", async () => { - mocks.core.getInput - .withArgs("payload") - .returns('channel: C0123456789\ntext: "first line\n\n second line"'); + mocks.inputs.set( + "payload", + 'channel: C0123456789\ntext: "first line\n\n second line"', + ); const config = new Config(mocks.core); const expected = { channel: "C0123456789", @@ -87,11 +94,14 @@ describe("content", () => { }); it("parses complete JSON from the input payload", async () => { - mocks.core.getInput.withArgs("payload").returns(`{ + mocks.inputs.set( + "payload", + `{ "message": "this is wrapped", "channel": "C0123456789" } - `); + `, + ); const config = new Config(mocks.core); const expected = { message: "this is wrapped", @@ -101,11 +111,14 @@ describe("content", () => { }); it("templatizes variables requires configuration", async () => { - mocks.core.getInput.withArgs("payload").returns(`{ + mocks.inputs.set( + "payload", + `{ "message": "this matches an existing variable: \${{ github.apiUrl }}", "channel": "C0123456789" } - `); + `, + ); const config = new Config(mocks.core); // biome-ignore-start lint/suspicious/noTemplateCurlyInString: GitHub Action YAML variable syntax const expected = { @@ -117,7 +130,9 @@ describe("content", () => { }); it("templatizes variables with matching variables", async () => { - mocks.core.getInput.withArgs("payload").returns(` + mocks.inputs.set( + "payload", + ` channel: C0123456789 reply_broadcast: false message: Served \${{ env.NUMBER }} items @@ -151,8 +166,9 @@ describe("content", () => { type: plain_text text: "\${{ github.graphqlUrl }}" value: graphql - `); - mocks.core.getBooleanInput.withArgs("payload-templated").returns(true); + `, + ); + mocks.booleanInputs.set("payload-templated", true); process.env.DETAILS = ` -fri -sat @@ -225,11 +241,12 @@ describe("content", () => { */ it("templatizes variables with missing variables", async () => { // biome-ignore-start lint/suspicious/noTemplateCurlyInString: GitHub Action YAML variable syntax - mocks.core.getInput - .withArgs("payload") - .returns("message: What makes ${{ env.TREASURE }} a secret"); + mocks.inputs.set( + "payload", + "message: What makes ${{ env.TREASURE }} a secret", + ); // biome-ignore-end lint/suspicious/noTemplateCurlyInString: https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#using-contexts-to-access-variable-values - mocks.core.getBooleanInput.withArgs("payload-templated").returns(true); + mocks.booleanInputs.set("payload-templated", true); const config = new Config(mocks.core); const expected = { message: "What makes ??? a secret", @@ -238,10 +255,13 @@ describe("content", () => { }); it("trims last comma JSON with the input payload", async () => { - mocks.core.getInput.withArgs("payload").returns(` + mocks.inputs.set( + "payload", + ` "message": "LGTM!", "channel": "C0123456789", - `); + `, + ); const config = new Config(mocks.core); const expected = { message: "LGTM!", @@ -251,7 +271,9 @@ describe("content", () => { }); it("wraps incomplete JSON from the input payload", async () => { - mocks.core.getInput.withArgs("payload").returns(` + mocks.inputs.set( + "payload", + ` "message": "LGTM!", "channel": "C0123456789", "blocks": [ @@ -262,7 +284,8 @@ describe("content", () => { } } ] - `); + `, + ); const config = new Config(mocks.core); const expected = { message: "LGTM!", @@ -306,7 +329,7 @@ describe("content", () => { }); it("fails if invalid JSON exists in the input payload", async () => { - mocks.core.getInput.withArgs("payload").returns("{"); + mocks.inputs.set("payload", "{"); try { await send(mocks.core); assert.fail("Failed to throw for invalid JSON"); @@ -331,13 +354,14 @@ describe("content", () => { 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(` + mocks.inputs.set("payload-file-path", "example.yaml"); + mocks.files.set( + path.resolve("example.yaml"), + ` message: "drink water" channel: "C6H12O6H2O2" - `); + `, + ); const config = new Config(mocks.core); const expected = { message: "drink water", @@ -347,13 +371,14 @@ describe("content", () => { }); 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(` + mocks.inputs.set("payload-file-path", "example.yml"); + mocks.files.set( + path.resolve("example.yml"), + ` message: "drink coffee" channel: "C0FFEEEEEEEE" - `); + `, + ); const config = new Config(mocks.core); const expected = { message: "drink coffee", @@ -363,13 +388,14 @@ describe("content", () => { }); 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(`{ + mocks.inputs.set("payload-file-path", "example.json"); + mocks.files.set( + path.resolve("example.json"), + `{ "message": "drink water", "channel": "C6H12O6H2O2" - }`); + }`, + ); const config = new Config(mocks.core); const expected = { message: "drink water", @@ -379,14 +405,15 @@ describe("content", () => { }); it("templatizes variables requires configuration", async () => { - mocks.core.getInput.withArgs("payload-file-path").returns("example.json"); - mocks.fs.readFileSync - .withArgs(path.resolve("example.json"), "utf-8") - .returns(`{ + mocks.inputs.set("payload-file-path", "example.json"); + mocks.files.set( + path.resolve("example.json"), + `{ "message": "this matches an existing variable: \${{ github.apiUrl }}", "channel": "C0123456789" } - `); + `, + ); const config = new Config(mocks.core); // biome-ignore-start lint/suspicious/noTemplateCurlyInString: GitHub Action YAML variable syntax const expected = { @@ -398,10 +425,10 @@ describe("content", () => { }); 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(`{ + mocks.inputs.set("payload-file-path", "example.json"); + mocks.files.set( + path.resolve("example.json"), + `{ "channel": "C0123456789", "reply_broadcast": false, "message": "Served \${{ env.NUMBER }} items", @@ -456,8 +483,9 @@ describe("content", () => { } } ] - }`); - mocks.core.getBooleanInput.withArgs("payload-templated").returns(true); + }`, + ); + mocks.booleanInputs.set("payload-templated", true); process.env.DETAILS = ` -fri -sat @@ -529,13 +557,14 @@ describe("content", () => { * @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(`{ + mocks.inputs.set("payload-file-path", "example.json"); + mocks.files.set( + path.resolve("example.json"), + `{ "message": "What makes $\{{ env.TREASURE }} a secret" - }`); - mocks.core.getBooleanInput.withArgs("payload-templated").returns(true); + }`, + ); + mocks.booleanInputs.set("payload-templated", true); const config = new Config(mocks.core); const expected = { message: "What makes ??? a secret", @@ -568,7 +597,7 @@ describe("content", () => { }); it("fails to parse a file path that does not exist", async () => { - mocks.core.getInput.withArgs("payload-file-path").returns("unknown.json"); + mocks.inputs.set("payload-file-path", "unknown.json"); try { await send(mocks.core); assert.fail("Failed to throw for nonexistent files"); @@ -586,7 +615,7 @@ describe("content", () => { }); it("fails to parse a file with an unknown extension", async () => { - mocks.core.getInput.withArgs("payload-file-path").returns("unknown.md"); + mocks.inputs.set("payload-file-path", "unknown.md"); try { await send(mocks.core); assert.fail("Failed to throw for an unknown extension"); @@ -611,11 +640,12 @@ 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`); + mocks.inputs.set("payload-file-path", "example.json"); + mocks.files.set( + path.resolve("example.json"), + `{ + "message": "a truncated file without an end`, + ); try { await send(mocks.core); assert.fail("Failed to throw for invalid JSON"); @@ -636,10 +666,11 @@ describe("content", () => { }); 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"`); + mocks.inputs.set("payload-file-path", "example.yaml"); + mocks.files.set( + path.resolve("example.yaml"), + `- "message": "assigned": "values"`, + ); try { await send(mocks.core); assert.fail("Failed to throw for invalid YAML"); diff --git a/test/index.spec.js b/test/index.spec.js index 55ae357e..0cd6a29f 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -1,7 +1,7 @@ import fs from "node:fs"; +import { mock } from "node:test"; import webapi from "@slack/web-api"; import webhook from "@slack/webhook"; -import sinon from "sinon"; /** * Hello experimenter! These tests are here to confirm that the happy paths keep @@ -26,54 +26,47 @@ export class Mock { * @see {@link ../action.yml} */ constructor() { - this.sandbox = sinon.createSandbox(); - this.calls = this.sandbox.stub(webapi.WebClient.prototype, "apiCall"); - this.core = { - debug: this.sandbox.stub(), - error: this.sandbox.stub(), - getInput: this.sandbox.stub(), - getBooleanInput: this.sandbox.stub(), - info: this.sandbox.stub(), - isDebug: this.sandbox.stub(), - setFailed: this.sandbox.stub(), - setOutput: this.sandbox.stub(), - setSecret: this.sandbox.stub(), - warning: this.sandbox.stub(), - }; - this.fetch = this.sandbox.stub(globalThis, "fetch"); - this.fs = this.sandbox.stub(fs); - this.webapi = { - WebClient: function () { - this.apiCall = () => ({ - ok: true, - }); - }, - }; - this.webhook = { - incoming: this.sandbox.stub(webhook.IncomingWebhook.prototype, "send"), - trigger: this.sandbox.stub(webhook.WebhookTrigger.prototype, "send"), - }; - this.core.getInput.withArgs("errors").returns("false"); - this.core.getInput.withArgs("retries").returns("5"); + this.reset(); } /** - * Testing interface that removes internal state from existing stubs. + * Testing interface that restores existing mocks and replaces these with new + * mocks without internal state. + * + * Values returned from inputs and files are matched to the arguments of the + * call using the "inputs", "booleanInputs", and "files" maps. */ reset() { - this.sandbox.reset(); - this.calls.resetHistory(); - this.core.debug.reset(); - this.core.error.reset(); - this.core.getInput.reset(); - this.core.getBooleanInput.reset(); - this.core.info.reset(); - this.core.isDebug.reset(); - this.core.setFailed.reset(); - this.core.setOutput.reset(); - this.core.setSecret.reset(); - this.core.warning.reset(); - this.fetch.reset(); + mock.reset(); + this.booleanInputs = new Map(); + this.files = new Map(); + this.inputs = new Map([ + ["errors", "false"], + ["retries", "5"], + ]); + this.calls = mock.method( + webapi.WebClient.prototype, + "apiCall", + () => undefined, + ); + this.core = { + debug: mock.fn(), + error: mock.fn(), + getInput: mock.fn((name) => this.inputs.get(name)), + getBooleanInput: mock.fn((name) => this.booleanInputs.get(name)), + info: mock.fn(), + isDebug: mock.fn(), + setFailed: mock.fn(), + setOutput: mock.fn(), + setSecret: mock.fn(), + warning: mock.fn(), + }; + this.fetch = mock.method(globalThis, "fetch", () => undefined); + this.fs = { + readFileSync: mock.method(fs, "readFileSync", (file, encoding) => + encoding === "utf-8" ? this.files.get(file) : undefined, + ), + }; this.webapi = { WebClient: function () { this.apiCall = () => ({ @@ -81,10 +74,18 @@ export class Mock { }); }, }; - this.webhook.incoming.resetHistory(); - this.webhook.trigger.resetHistory(); - this.core.getInput.withArgs("errors").returns("false"); - this.core.getInput.withArgs("retries").returns("5"); + this.webhook = { + incoming: mock.method( + webhook.IncomingWebhook.prototype, + "send", + () => undefined, + ), + trigger: mock.method( + webhook.WebhookTrigger.prototype, + "send", + () => undefined, + ), + }; process.env.SLACK_TOKEN = ""; process.env.SLACK_WEBHOOK_URL = ""; } diff --git a/test/logger.spec.js b/test/logger.spec.js index a246f4fb..74f1d155 100644 --- a/test/logger.spec.js +++ b/test/logger.spec.js @@ -11,7 +11,7 @@ describe("logger", () => { describe("level", () => { it("debug", () => { - mocks.core.isDebug.returns(true); + mocks.core.isDebug.mock.mockImplementation(() => true); const { logger } = new Logger(mocks.core); const actual = logger.getLevel(); const expected = LogLevel.DEBUG; @@ -19,7 +19,7 @@ describe("logger", () => { }); it("info", () => { - mocks.core.isDebug.returns(false); + mocks.core.isDebug.mock.mockImplementation(() => false); const { logger } = new Logger(mocks.core); const actual = logger.getLevel(); const expected = LogLevel.INFO; diff --git a/test/proxies.spec.js b/test/proxies.spec.js index f4ae9227..e2b92812 100644 --- a/test/proxies.spec.js +++ b/test/proxies.spec.js @@ -12,26 +12,26 @@ describe("proxies", () => { describe("fetch", () => { it("returns a custom fetch function when a proxy is configured", async () => { - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("proxy").returns("https://example.com"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("proxy", "https://example.com"); + mocks.inputs.set("token", "xoxb-example"); const config = new Config(mocks.core); const fetchFn = fetch(config); assert.strictEqual(typeof fetchFn, "function"); }); it("returns undefined when no proxy is configured", async () => { - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); const config = new Config(mocks.core); const fetchFn = fetch(config); assert.strictEqual(fetchFn, undefined); }); it("fails to configure a fetch with an invalid proxied url", async () => { - mocks.core.getInput.withArgs("method").returns("chat.postMessage"); - mocks.core.getInput.withArgs("proxy").returns("not-a-url"); - mocks.core.getInput.withArgs("token").returns("xoxb-example"); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("proxy", "not-a-url"); + mocks.inputs.set("token", "xoxb-example"); try { const config = new Config(mocks.core); fetch(config); @@ -50,52 +50,44 @@ describe("proxies", () => { describe("dispatcher", () => { it("returns no dispatcher when a proxy is not configured", async () => { - mocks.core.getInput - .withArgs("webhook") - .returns("https://hooks.slack.com"); - mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); const config = new Config(mocks.core); const dispatcher = proxies(config, config.inputs.webhook); assert.strictEqual(dispatcher, undefined); }); it("skips proxying a non-https destination altogether", async () => { - mocks.core.getInput.withArgs("webhook").returns("http://hooks.slack.com"); - mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); - mocks.core.getInput.withArgs("proxy").returns("https://example.com"); + mocks.inputs.set("webhook", "http://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); + mocks.inputs.set("proxy", "https://example.com"); const config = new Config(mocks.core); const dispatcher = proxies(config, config.inputs.webhook); assert.strictEqual(dispatcher, undefined); }); it("returns a proxy dispatcher for the provided https proxy", async () => { - 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("https://example.com"); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); + mocks.inputs.set("proxy", "https://example.com"); const config = new Config(mocks.core); const dispatcher = proxies(config, config.inputs.webhook); assert.ok(dispatcher); }); it("returns a proxy dispatcher for http proxies", async () => { - 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("http://example.com"); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); + mocks.inputs.set("proxy", "http://example.com"); const config = new Config(mocks.core); const dispatcher = proxies(config, config.inputs.webhook); assert.ok(dispatcher); }); it("fails to configure a dispatcher with an invalid proxied url", async () => { - 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("https://"); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); + mocks.inputs.set("proxy", "https://"); try { const config = new Config(mocks.core); proxies(config, config.inputs.webhook); @@ -112,11 +104,9 @@ describe("proxies", () => { }); it("fails to configure a dispatcher with an unknown url protocol", async () => { - 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("ssh://example.com"); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); + mocks.inputs.set("proxy", "ssh://example.com"); try { const config = new Config(mocks.core); proxies(config, config.inputs.webhook); diff --git a/test/send.spec.js b/test/send.spec.js index c8d1b33f..a17a914c 100644 --- a/test/send.spec.js +++ b/test/send.spec.js @@ -20,101 +20,111 @@ describe("send", () => { describe("techniques", async () => { it("webhook trigger", async () => { - mocks.core.getInput - .withArgs("webhook") - .returns("https://hooks.slack.com"); - mocks.core.getInput.withArgs("webhook-type").returns("webhook-trigger"); - mocks.core.getInput.withArgs("payload").returns('"greetings": "hello"'); - mocks.webhook.trigger.resolves({ ok: true }); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "webhook-trigger"); + mocks.inputs.set("payload", '"greetings": "hello"'); + mocks.webhook.trigger.mock.mockImplementation(async () => ({ ok: true })); await send(mocks.core); - 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.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], true); + assert.equal(mocks.core.setOutput.mock.calls[1].arguments[0], "response"); assert.equal( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[1], JSON.stringify({ ok: true }), ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.ok(mocks.core.setOutput.getCall(2).lastArg >= 0); + assert.equal(mocks.core.setOutput.mock.calls[2].arguments[0], "time"); + assert.ok(mocks.core.setOutput.mock.calls[2].arguments[1] >= 0); }); it("token", async () => { process.env.SLACK_WEBHOOK_URL = "https://example.com"; // https://github.com/slackapi/slack-github-action/issues/373 - mocks.calls.resolves({ ok: 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.mock.mockImplementation(async () => ({ ok: true })); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("payload", '"text": "hello"'); await send(mocks.core); - 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.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], true); + assert.equal(mocks.core.setOutput.mock.calls[1].arguments[0], "response"); assert.equal( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[1], JSON.stringify({ ok: true }), ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.ok(mocks.core.setOutput.getCall(2).lastArg >= 0); + assert.equal(mocks.core.setOutput.mock.calls[2].arguments[0], "time"); + assert.ok(mocks.core.setOutput.mock.calls[2].arguments[1] >= 0); }); it("incoming webhook", async () => { process.env.SLACK_TOKEN = "xoxb-example"; // https://github.com/slackapi/slack-github-action/issues/373 - mocks.core.getInput - .withArgs("webhook") - .returns("https://hooks.slack.com"); - mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); - mocks.core.getInput.withArgs("payload").returns('"text": "hello"'); - mocks.webhook.incoming.resolves({ text: "ok" }); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); + mocks.inputs.set("payload", '"text": "hello"'); + mocks.webhook.incoming.mock.mockImplementation(async () => ({ + text: "ok", + })); await send(mocks.core); - 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.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], true); + assert.equal(mocks.core.setOutput.mock.calls[1].arguments[0], "response"); assert.equal( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[1], JSON.stringify("ok"), ); - assert.equal(mocks.core.setOutput.getCall(2).firstArg, "time"); - assert.ok(mocks.core.setOutput.getCall(2).lastArg >= 0); + assert.equal(mocks.core.setOutput.mock.calls[2].arguments[0], "time"); + assert.ok(mocks.core.setOutput.mock.calls[2].arguments[1] >= 0); }); }); describe("logging", async () => { it("warns of the failed request when errors are not fatal", async () => { - 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( - new webapi.WebAPIPlatformError({ ok: false, error: "invalid_auth" }), - ); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("payload", '"text": "hello"'); + mocks.calls.mock.mockImplementation(async () => { + throw new webapi.WebAPIPlatformError({ + ok: false, + error: "invalid_auth", + }); + }); await send(mocks.core); - assert.strictEqual(mocks.core.setFailed.called, false); - assert.equal(mocks.core.warning.getCalls().length, 1); - assert.match(mocks.core.warning.getCall(0).firstArg, /invalid_auth/); + assert.strictEqual(mocks.core.setFailed.mock.callCount(), 0); + assert.equal(mocks.core.warning.mock.callCount(), 1); + assert.match( + mocks.core.warning.mock.calls[0].arguments[0], + /invalid_auth/, + ); }); it("warns of the failed webhook when errors are not fatal", async () => { - mocks.core.getInput - .withArgs("webhook") - .returns("https://hooks.slack.com"); - mocks.core.getInput.withArgs("webhook-type").returns("webhook-trigger"); - mocks.core.getInput.withArgs("payload").returns('"greetings": "hello"'); - mocks.webhook.trigger.rejects(new Error("invalid_payload")); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "webhook-trigger"); + mocks.inputs.set("payload", '"greetings": "hello"'); + mocks.webhook.trigger.mock.mockImplementation(async () => { + throw new Error("invalid_payload"); + }); await send(mocks.core); - assert.strictEqual(mocks.core.setFailed.called, false); - assert.equal(mocks.core.warning.getCalls().length, 1); - assert.match(mocks.core.warning.getCall(0).firstArg, /invalid_payload/); + assert.strictEqual(mocks.core.setFailed.mock.callCount(), 0); + assert.equal(mocks.core.warning.mock.callCount(), 1); + assert.match( + mocks.core.warning.mock.calls[0].arguments[0], + /invalid_payload/, + ); }); it("fails the step without warning when errors are fatal", async () => { - 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( - new webapi.WebAPIPlatformError({ ok: false, error: "invalid_auth" }), - ); + mocks.booleanInputs.set("errors", true); + mocks.inputs.set("method", "chat.postMessage"); + mocks.inputs.set("token", "xoxb-example"); + mocks.inputs.set("payload", '"text": "hello"'); + mocks.calls.mock.mockImplementation(async () => { + throw new webapi.WebAPIPlatformError({ + ok: false, + error: "invalid_auth", + }); + }); await assert.rejects(send(mocks.core)); - assert.ok(mocks.core.setFailed.called); - assert.strictEqual(mocks.core.warning.called, false); + assert.ok(mocks.core.setFailed.mock.callCount() > 0); + assert.strictEqual(mocks.core.warning.mock.callCount(), 0); }); }); }); diff --git a/test/webhook.spec.js b/test/webhook.spec.js index 91dcc88b..91dc9f77 100644 --- a/test/webhook.spec.js +++ b/test/webhook.spec.js @@ -14,23 +14,24 @@ describe("webhook", () => { describe("success", () => { it("sends the parsed payload to the provided webhook trigger", async () => { - mocks.core.getInput - .withArgs("webhook") - .returns("https://hooks.slack.com"); - mocks.core.getInput.withArgs("webhook-type").returns("webhook-trigger"); - mocks.core.getInput.withArgs("payload").returns("drinks: coffee"); - mocks.webhook.trigger.resolves({ ok: true }); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "webhook-trigger"); + mocks.inputs.set("payload", "drinks: coffee"); + mocks.webhook.trigger.mock.mockImplementation(async () => ({ ok: true })); try { await send(mocks.core); - assert.equal(mocks.webhook.trigger.getCalls().length, 1); - assert.deepEqual(mocks.webhook.trigger.getCall(0).firstArg, { + assert.equal(mocks.webhook.trigger.mock.callCount(), 1); + assert.deepEqual(mocks.webhook.trigger.mock.calls[0].arguments[0], { drinks: "coffee", }); - 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.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], true); assert.equal( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[0], + "response", + ); + assert.equal( + mocks.core.setOutput.mock.calls[1].arguments[1], JSON.stringify({ ok: true }), ); } catch (err) { @@ -40,23 +41,26 @@ describe("webhook", () => { }); it("sends the parsed payload to the provided incoming webhook", async () => { - mocks.core.getInput - .withArgs("webhook") - .returns("https://hooks.slack.com"); - mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); - mocks.core.getInput.withArgs("payload").returns("text: greetings"); - mocks.webhook.incoming.resolves({ text: "ok" }); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); + mocks.inputs.set("payload", "text: greetings"); + mocks.webhook.incoming.mock.mockImplementation(async () => ({ + text: "ok", + })); try { await send(mocks.core); - assert.equal(mocks.webhook.incoming.getCalls().length, 1); - assert.deepEqual(mocks.webhook.incoming.getCall(0).firstArg, { + assert.equal(mocks.webhook.incoming.mock.callCount(), 1); + assert.deepEqual(mocks.webhook.incoming.mock.calls[0].arguments[0], { text: "greetings", }); - 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.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], true); + assert.equal( + mocks.core.setOutput.mock.calls[1].arguments[0], + "response", + ); assert.equal( - mocks.core.setOutput.getCall(1).lastArg, + mocks.core.setOutput.mock.calls[1].arguments[1], JSON.stringify("ok"), ); } catch (err) { @@ -114,15 +118,13 @@ describe("webhook", () => { }); it("returns the failures from a webhook trigger", async () => { - mocks.core.getBooleanInput.withArgs("errors").returns(true); - mocks.core.getInput - .withArgs("webhook") - .returns("https://hooks.slack.com"); - mocks.core.getInput.withArgs("webhook-type").returns("webhook-trigger"); - mocks.core.getInput.withArgs("payload").returns("drinks: coffee"); - mocks.webhook.trigger.rejects( - new Error("An HTTP protocol error occurred"), - ); + mocks.booleanInputs.set("errors", true); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "webhook-trigger"); + mocks.inputs.set("payload", "drinks: coffee"); + mocks.webhook.trigger.mock.mockImplementation(async () => { + throw new Error("An HTTP protocol error occurred"); + }); try { await send(mocks.core); assert.fail(); @@ -133,26 +135,24 @@ describe("webhook", () => { assert.fail(err); } } - assert.equal(mocks.webhook.trigger.getCalls().length, 1); - assert.deepEqual(mocks.webhook.trigger.getCall(0).firstArg, { + assert.equal(mocks.webhook.trigger.mock.callCount(), 1); + assert.deepEqual(mocks.webhook.trigger.mock.calls[0].arguments[0], { drinks: "coffee", }); - 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.ok(mocks.core.setFailed.mock.callCount() > 0); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], false); + assert.equal(mocks.core.setOutput.mock.calls[1].arguments[0], "response"); }); it("returns the failures from an incoming webhook", async () => { - mocks.core.getBooleanInput.withArgs("errors").returns(true); - mocks.core.getInput - .withArgs("webhook") - .returns("https://hooks.slack.com"); - mocks.core.getInput.withArgs("webhook-type").returns("incoming-webhook"); - mocks.core.getInput.withArgs("payload").returns("textt: oops"); - mocks.webhook.incoming.rejects( - new Error("An HTTP protocol error occurred"), - ); + mocks.booleanInputs.set("errors", true); + mocks.inputs.set("webhook", "https://hooks.slack.com"); + mocks.inputs.set("webhook-type", "incoming-webhook"); + mocks.inputs.set("payload", "textt: oops"); + mocks.webhook.incoming.mock.mockImplementation(async () => { + throw new Error("An HTTP protocol error occurred"); + }); try { await send(mocks.core); assert.fail(); @@ -163,14 +163,14 @@ describe("webhook", () => { assert.fail(err); } } - assert.equal(mocks.webhook.incoming.getCalls().length, 1); - assert.deepEqual(mocks.webhook.incoming.getCall(0).firstArg, { + assert.equal(mocks.webhook.incoming.mock.callCount(), 1); + assert.deepEqual(mocks.webhook.incoming.mock.calls[0].arguments[0], { textt: "oops", }); - 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.ok(mocks.core.setFailed.mock.callCount() > 0); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[0], "ok"); + assert.equal(mocks.core.setOutput.mock.calls[0].arguments[1], false); + assert.equal(mocks.core.setOutput.mock.calls[1].arguments[0], "response"); }); });