openSlackChannelFromTask.test.ts4.1 KBView on GitHub
/**
 * The one definition of "open this Slack task's channel".
 *
 * Two things about it are easy to get wrong and impossible to see afterwards:
 *
 *   1. WHICH composer the draft lands in. There are two Slack composers, fed by two different
 *      mechanisms — the per-deal timeline one takes a ProseMirror doc, the unibox's
 *      `ChannelThreadView` takes raw mrkdwn through `pending-channel-draft`. This path opens the
 *      unibox one, so seeding the deal's put the message somewhere the user was never sent and
 *      left the channel that DID open with an empty box. Nothing errors; the draft is just gone.
 *   2. Whether a SENT message is seeded again. Sending completes the task but leaves the text on
 *      `taskActionData`, so a done task re-seeded an already-posted message — one Enter from a
 *      duplicate post.
 */
import { openSlackChannelFromTask } from '@/modules/userTasks/utils/open-slack-channel-from-task';
import { seedChannelDraft } from '@/modules/inbox/store/pending-channel-draft';
import { seedComposerDoc } from '@/modules/conversations/components/timeline/composer/useComposerDraft';
import { useCedarStore } from '@/modules/store';

jest.mock('@/modules/inbox/store/pending-channel-draft', () => ({
  seedChannelDraft: jest.fn(),
}));

// The OTHER Slack composer — the per-deal timeline one. Mocked purely so the test below can
// assert it is never reached; the two are chosen between by which surface actually opens.
jest.mock('@/modules/conversations/components/timeline/composer/useComposerDraft', () => ({
  seedComposerDoc: jest.fn(),
  requestComposerOpen: jest.fn(),
}));

const TARGET = {
  conversationId: 'conv-1',
  channelId: 'C0BNUT9RD8X',
  channelName: 'cedar-payrollintegrations',
  workspaceId: 'T08AHSLJBTL',
  message: 'Recap of our call…',
};

beforeEach(() => {
  jest.clearAllMocks();
  useCedarStore.getState().setSlackChannelHint(null);
  useCedarStore.getState().setSelectedArtifact(null);
});

describe('openSlackChannelFromTask', () => {
  it('seeds the UNIBOX channel composer, keyed by the container the view opens', () => {
    openSlackChannelFromTask(TARGET);

    expect(seedChannelDraft).toHaveBeenCalledWith('C0BNUT9RD8X', 'Recap of our call…');
  });

  it('opens the channel through the display artifact, as an email task opens its thread', () => {
    openSlackChannelFromTask(TARGET);

    expect(useCedarStore.getState().getDisplayArtifact()).toMatchObject({
      kind: 'slack_thread',
      id: 'C0BNUT9RD8X',
    });
    expect(useCedarStore.getState().isChannelChatOpen).toBe(true);
  });

  it('leaves the workspace behind — the channel cannot be READ without it', () => {
    // `inbox.slackChannelMessages` is keyed by workspace AND channel, so a hint carrying only the
    // channel id renders a header over an empty thread.
    openSlackChannelFromTask(TARGET);

    expect(useCedarStore.getState().slackChannelHint).toEqual({
      channelId: 'C0BNUT9RD8X',
      workspaceId: 'T08AHSLJBTL',
      channelName: 'cedar-payrollintegrations',
      conversationId: 'conv-1',
    });
  });

  it('does not re-seed a message that is already on Slack', () => {
    openSlackChannelFromTask({ ...TARGET, alreadySent: true });

    expect(seedChannelDraft).not.toHaveBeenCalled();
    // It still OPENS — a sent Slack task's channel is exactly where you want to land.
    expect(useCedarStore.getState().getDisplayArtifact()).toMatchObject({ kind: 'slack_thread' });
  });

  it('opens a channel the task drafted nothing for, without seeding an empty draft', () => {
    openSlackChannelFromTask({ ...TARGET, message: undefined });

    expect(seedChannelDraft).not.toHaveBeenCalled();
    expect(useCedarStore.getState().isChannelChatOpen).toBe(true);
  });

  it('never seeds the per-deal timeline composer instead', () => {
    // The regression this file exists for. Both composers accept a Slack draft and neither
    // errors on the wrong one, so seeding the deal's silently produced a channel with an empty
    // box and a draft sitting in a surface the click never navigated to.
    openSlackChannelFromTask(TARGET);

    expect(seedComposerDoc).not.toHaveBeenCalled();
  });
});