import { mock } from "node:test"; import webapi from "@slack/web-api"; import axios, { AxiosError } from "axios"; /** * Hello experimenter! These tests are here to confirm that the happy paths keep * working and error cases are thrown. * * Tests are grouped with related related code using a file *.spec.js extension. * These test related functionalities of units but a full suite of integration * tests confirm correctness of the actual workflows in .github/workflows/*.yml. * * Actual tests begin in send.spec.js which has integration tests. Other modules * test the edges of this action. */ /** * The Mock class sets expected behaviors and test listeners for dependencies. */ export class Mock { /** * @typedef Errors - A collection of mocked errors to use in tests. * @prop {Object.} axios - The mocked axios errors. */ /** * The mocked errors. * @type {Errors} */ errors = { axios: { network_failed: new AxiosError("network_failed"), }, }; /** * Lookup tables for input values. */ inputs = {}; booleanInputs = {}; /** * Setup mocked dependencies and configure default input arguments for all * tests. * * @see {@link ../action.yml} */ constructor() { this.axios = { post: mock.fn((..._args) => { if (this.axios.post._promise !== undefined) { return this.axios.post._promise; } throw new Error( "Test error: axios.post was called but no promise was configured. " + "Set mocks.axios.post._promise = Promise.resolve(...) or Promise.reject(...)", ); }), }; axios.post = this.axios.post; this.calls = mock.fn((..._args) => { if (this.calls._resolvesWith !== undefined) { return Promise.resolve(this.calls._resolvesWith); } if (this.calls._rejectsWith !== undefined) { return Promise.reject(this.calls._rejectsWith); } throw new Error( "Test error: apiCall was called but no promise was configured. " + "Set mocks.calls._resolvesWith = {...} or mocks.calls._rejectsWith = {...}", ); }); webapi.WebClient.prototype.apiCall = this.calls; this.core = { debug: mock.fn(), error: mock.fn(), getInput: mock.fn((key) => this.inputs[key] ?? ""), getBooleanInput: mock.fn((key) => this.booleanInputs[key] ?? false), info: mock.fn(), isDebug: mock.fn(() => this._isDebug ?? false), setFailed: mock.fn(), setOutput: mock.fn(), setSecret: mock.fn(), warning: mock.fn(), }; } /** * Testing interface that removes internal state from existing mocks. */ reset() { this._isDebug = false; this.axios.post.mock.resetCalls(); this.axios.post._promise = undefined; this.booleanInputs = {}; this.calls.mock.resetCalls(); this.calls._resolvesWith = undefined; this.calls._rejectsWith = undefined; this.core.debug.mock.resetCalls(); this.core.error.mock.resetCalls(); this.core.getBooleanInput.mock.resetCalls(); this.core.getInput.mock.resetCalls(); this.core.info.mock.resetCalls(); this.core.isDebug.mock.resetCalls(); this.core.setFailed.mock.resetCalls(); this.core.setOutput.mock.resetCalls(); this.core.setSecret.mock.resetCalls(); this.core.warning.mock.resetCalls(); this.inputs = {}; this.webapi = {}; process.env.SLACK_TOKEN = ""; process.env.SLACK_WEBHOOK_URL = ""; } } export const mocks = new Mock();