IBM / slack-wrench

Tools to build and test Slack apps
Apache License 2.0
48 stars 26 forks source link

QUESTION: Usage in monorepo #132

Open kpeters-cbsi opened 2 years ago

kpeters-cbsi commented 2 years ago

I have a monorepo set up using yarn workspaces and Lerna. I've got a package inside the monorepo that sends messages to Slack, and I'd like to test it using this package. The package tests are located under <repo root>/packages/my-package/tests, but @slack/web-api is located under <repo root>/node_modules. I've tried creating a manual mock as described in the README in both <repo root>/packages/my-package/__mocks__ and <repo root>/__mocks__, but the below code still gives me an error: TypeError: Cannot read property 'chat' of undefined. What's the proper way to use this in a monorepo context?

Note that I'm using Typescript 4.7.2 and Node 14.19.0

import { handler as announcer } from "../lambda/announcer"
import { SNSEvent, Context } from "aws-lambda"
import { MockedWebClient, MockWebClient } from "@slack-wrench/jest-mock-web-client"

describe("Announcer", () => {
  let client: MockWebClient
  beforeEach(() => {
    MockedWebClient.mockClear()
    client = MockedWebClient.mock.instances[0]
  })

  it("Publishes a message to Slack", async () => {
    const recs = [
      { name: 'rec-1'  },
      { name: 'rec-2' }
    ]

    const event = {
      Records: recs.map((r) => ({
        Sns: {
          Message: JSON.stringify(r),
        },
      })),
    } as SNSEvent

    await expect(announcer(event, {} as Context, () => {})).resolves.not.toThrowError()
    expect(client.chat.postMessage).toHaveBeenCalledTimes(recs.length)
  })
})
barlock commented 2 years ago

It should work, this repo itself is a monorepo and uses it successfully. Though admittedly not directly anywhere.

From the looks of your code, I don't see anywhere between you clearing the mocks and trying to access an instance of a client where you are creating one. In order to see one in instances you need to have first created an instance. Usually this would be after instantiating a server or script. I would expect if you tried assigning client just after your await and before the failing expect that your test would pass.

kpeters-cbsi commented 2 years ago

Thanks, I'll try that.

kpeters-cbsi commented 2 years ago

So I updated my code like so:

import { handler as announcer } from "../lambda/announcer"
import { SNSEvent, Context } from "aws-lambda"
import { MockedWebClient, MockWebClient } from "@slack-wrench/jest-mock-web-client"

describe("Announcer", () => {
  let client: MockWebClient
  beforeEach(() => {
    MockedWebClient.mockClear()
  })

  it("Publishes a message to Slack", async () => {
    const recs = [
      { name: 'rec-1'  },
      { name: 'rec-2' }
    ]

    const event = {
      Records: recs.map((r) => ({
        Sns: {
          Message: JSON.stringify(r),
        },
      })),
    } as SNSEvent

    await expect(announcer(event, {} as Context, () => {})).resolves.not.toThrowError()
    client = MockedClient.mock.instances[0]
    expect(client.chat.postMessage).toHaveBeenCalledTimes(recs.length)
  })
})

And I got the same error.

Here's the basic code for announcer():

import { SNSHandler } from "aws-lambda"
import { WebClient } from "@slack/web-api

export const handler: SNSHandler = async (event, context) => {
  const config = await getConfig()
  if (!config.slack_oauth_token) {
    throw new Error("No slack_oauth_token found in config")
  }
  const webclient = new WebClient(config.slack_oauth_token)
  await Promise.allSettled(
    event.Records.map(async (record) => {
     const response = {}
      // ... do stuff with record
      return response
    })
  )
}

As you can see, I'm instantiating WebClient in announcer()

nadirabbas commented 7 months ago

@kpeters-cbsi Did you find a solution to this?