split-routing.test.tsx4.8 KBView on GitHub
import React from 'react';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import { SplitInboxTabs } from '@/modules/threads/components/SplitInboxTabs';

jest.mock('@tanstack/react-query', () => ({
  useMutation: () => ({ mutateAsync: jest.fn() }),
  useQueryClient: () => ({ invalidateQueries: jest.fn() }),
}));

const slugify = (name: string) =>
  name.toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
const getInboxSlug = (inbox: { id: string; name: string }) => {
  if (inbox.id === 'default') return 'inbox';
  if (inbox.id === 'important') return 'important';
  if (inbox.id === 'other') return 'other';
  return slugify(inbox.name);
};

const getRowInboxOrder = (inboxes: Array<{ id: string }>) => {
  const important = inboxes.find((i) => i.id === 'important');
  const other = inboxes.find((i) => i.id === 'other');
  const middle = inboxes.filter((i) => i.id !== 'important' && i.id !== 'other');
  if (important && other) return [important, ...middle, other];
  if (important) return [important, ...middle];
  if (other) return [...middle, other];
  return middle;
};

const mockInboxesByLayout: Record<string, Array<{ id: string; name: string; position: number; system?: boolean; rule: { kind: 'all' } }>> = {
  important_other: [
    { id: 'default', name: 'Inbox', position: 0, system: true, rule: { kind: 'all' } },
    { id: 'important', name: 'Important', position: 1, system: true, rule: { kind: 'all' } },
  ],
  stacked: [
    { id: 'important', name: 'Important', position: 0, system: true, rule: { kind: 'all' } },
    { id: 'other', name: 'Other', position: 1, system: true, rule: { kind: 'all' } },
  ],
};

let mockCurrentLayout: 'important_other' | 'stacked' = 'important_other';

jest.mock('@/modules/threads/hooks/use-inboxes', () => ({
  getInboxSlug,
  findInboxBySlug: (
    inboxes: Array<{ id: string; name: string }>,
    slug: string,
  ) => inboxes.find((i) => getInboxSlug(i) === slug),
  getRowInboxOrder,
  useInboxes: () => ({
    inboxes: mockInboxesByLayout[mockCurrentLayout],
    activeInbox: mockInboxesByLayout[mockCurrentLayout][0],
    activeInboxId: mockInboxesByLayout[mockCurrentLayout][0].id,
    inboxLayout: mockCurrentLayout,
    setActiveInboxId: jest.fn(),
    setInboxLayout: jest.fn(),
    addInbox: jest.fn(),
    updateInbox: jest.fn(),
    renameInbox: jest.fn(),
    removeInbox: jest.fn(),
    reorderInboxes: jest.fn(),
    isLoading: false,
    inboxesLoading: false,
  }),
}));

jest.mock('@/modules/labels/hooks/use-labels-search', () => ({
  __esModule: true,
  default: () => ({ setLabels: jest.fn() }),
}));

jest.mock('@/hooks/use-inbox-counts', () => ({
  useInboxCounts: () => ({
    byId: {
      default: { count: 0, isExact: true },
      important: { count: 0, isExact: true },
      other: { count: 0, isExact: true },
    },
    done: { count: 0, isExact: true },
  }),
}));

jest.mock('@/modules/aop/hooks/use-aops', () => ({
  useAOPs: () => ({ data: { aops: [] } }),
}));

jest.mock('@/modules/labels/hooks/use-labels', () => ({
  useLabels: () => ({ allUserLabels: [], systemLabels: [] }),
}));

jest.mock('@/providers/query-provider', () => ({
  useTRPC: () => ({
    labels: {
      list: {
        queryKey: () => ['labels.list'],
      },
      create: {
        mutationOptions: () => ({}),
      },
      delete: {
        mutationOptions: () => ({}),
      },
    },
    mail: {
      createAiLabel: {
        mutationOptions: () => ({}),
      },
      backfillAiLabels: {
        mutationOptions: () => ({}),
      },
      deleteAiLabel: {
        mutationOptions: () => ({}),
      },
    },
  }),
}));

describe('split routing tabs', () => {
  beforeEach(() => {
    mockCurrentLayout = 'important_other';
  });

  it('renders split tabs on inbox route', () => {
    const view = render(
      <MemoryRouter initialEntries={['/mail/inbox']}>
        <SplitInboxTabs />
      </MemoryRouter>,
    );
    expect(view.getByText('Inbox')).toBeInTheDocument();
    expect(view.getByText('Important')).toBeInTheDocument();
  });

  it('keeps split tabs and folder label visible on system-folder routes like done', () => {
    const view = render(
      <MemoryRouter initialEntries={['/mail/done']}>
        <SplitInboxTabs />
      </MemoryRouter>,
    );
    expect(view.getByText('Inbox')).toBeInTheDocument();
    expect(view.getByText('Important')).toBeInTheDocument();
    expect(view.getByText(/^Done$/)).toBeInTheDocument();
  });

  it('renders only a single Inbox tab in stacked layout', () => {
    mockCurrentLayout = 'stacked';
    const view = render(
      <MemoryRouter initialEntries={['/mail/inbox']}>
        <SplitInboxTabs />
      </MemoryRouter>,
    );
    expect(view.getByText('Inbox')).toBeInTheDocument();
    expect(view.queryByText('Important')).not.toBeInTheDocument();
    expect(view.queryByText('Other')).not.toBeInTheDocument();
  });
});