/* eslint-disable react/prop-types */
import React from 'react';
import {
  render, screen, waitFor, within,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import { IntlProvider } from 'react-intl';
import ChannelSelectionStep from '../ChannelSelectionStep';
import { CHANNELS } from '../../../constants';

jest.mock('../../../../CreativesContainer', () => function MockCreativesContainer({
  getCreativesData,
  handleCloseCreatives,
  creativesMode,
  channel,
}) {
  return (
    <div data-testid="creatives-mock" data-creatives-mode={creativesMode} data-creatives-channel={channel}>
      <button
        type="button"
        data-testid="creatives-save"
        onClick={() => getCreativesData({ smsBody: 'Saved body' })}
      >
        Save creative
      </button>
      <button
        type="button"
        data-testid="creatives-save-null"
        onClick={() => getCreativesData(null)}
      >
        Save null
      </button>
      <button type="button" data-testid="creatives-close" onClick={handleCloseCreatives}>
        Close creative
      </button>
    </div>
  );
});

jest.mock('../../DeliverySettingsStep', () => ({
  DeliverySettingsSection: function MockDeliverySettings({ onDeliverySettingChange }) {
    return (
      <div data-testid="delivery-settings-section">
        <button
          type="button"
          data-testid="delivery-apply"
          onClick={() => onDeliverySettingChange({ channelSetting: { SMS: { domainId: 'd1' } } })}
        >
          Apply delivery
        </button>
      </div>
    );
  },
}));

function renderStep(ui) {
  return render(
    <IntlProvider locale="en" messages={{}} defaultLocale="en">
      {ui}
    </IntlProvider>,
  );
}

describe('ChannelSelectionStep', () => {
  beforeEach(() => {
    jest.spyOn(console, 'log').mockImplementation(() => {});
  });

  afterEach(() => {
    console.log.mockRestore();
  });

  it('empty state: shows content template heading and add-template entry point', () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={onChange}
        channels={CHANNELS}
      />,
    );

    expect(screen.getAllByText('Content template').length).toBeGreaterThanOrEqual(1);
    expect(screen.getByText('Add message content and incentive')).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /content template/i })).toBeInTheDocument();
    expect(screen.queryByTestId('creatives-mock')).not.toBeInTheDocument();
  });

  it('shows validation error when error prop is set', () => {
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={jest.fn()}
        channels={CHANNELS}
        error="Channel step error"
      />,
    );
    expect(screen.getByText('Channel step error')).toBeInTheDocument();
  });

  it('with content: shows preview text, sender line for SMS, and remove updates parent', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            {
              contentId: 'id-1',
              channel: 'SMS',
              templateData: {
                smsBody: 'Preview line for card',
                templateConfigs: { registeredSenderIds: 'SID99' },
              },
            },
          ],
        }}
        onChange={onChange}
        channels={CHANNELS}
      />,
    );

    expect(screen.getByText('Preview line for card')).toBeInTheDocument();
    expect(screen.getByText(/SMS sender ID SID99/)).toBeInTheDocument();

    await userEvent.click(screen.getByLabelText('Show more content options icon'));
    await waitFor(() => {
      expect(screen.getByRole('menu')).toBeInTheDocument();
    });
    await userEvent.click(within(screen.getByRole('menu')).getByText('Remove'));

    expect(onChange).toHaveBeenCalledWith({ contentItems: [] });
  });

  it('add SMS template: opens creatives, save calls onChange with new content item', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={onChange}
        channels={CHANNELS}
      />,
    );

    await userEvent.click(screen.getByRole('button', { name: /content template/i }));
    await waitFor(() => {
      expect(screen.getByRole('menu')).toBeInTheDocument();
    });
    await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));

    await waitFor(() => {
      expect(screen.getByTestId('creatives-mock')).toBeInTheDocument();
    });

    expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-creatives-mode', 'create');

    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({ channel: 'SMS', channels: [] }),
    );

    await userEvent.click(screen.getByTestId('creatives-save'));

    expect(onChange).toHaveBeenCalledWith({
      contentItems: [
        expect.objectContaining({
          channel: 'SMS',
          templateData: { smsBody: 'Saved body' },
        }),
      ],
    });
  });

  it('closes creatives and clears editing state when handleCloseCreatives runs', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={onChange}
        channels={CHANNELS}
      />,
    );

    await userEvent.click(screen.getByRole('button', { name: /content template/i }));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
    await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument());

    await userEvent.click(screen.getByTestId('creatives-close'));

    expect(screen.queryByTestId('creatives-mock')).not.toBeInTheDocument();
  });

  it('edit existing content: opens creatives in edit mode and replaces item on save', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            {
              contentId: 'edit-me',
              channel: 'SMS',
              templateData: { smsBody: 'Before' },
            },
          ],
        }}
        onChange={onChange}
        channels={CHANNELS}
      />,
    );

    await userEvent.click(screen.getByLabelText('Show more content options icon'));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('menu')).getByText('Edit'));

    await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument());
    expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-creatives-mode', 'edit');

    await userEvent.click(screen.getByTestId('creatives-save'));

    expect(onChange).toHaveBeenCalledWith({
      contentItems: [
        expect.objectContaining({
          contentId: 'edit-me',
          channel: 'SMS',
          templateData: { smsBody: 'Saved body' },
        }),
      ],
    });
  });

  it('preview menu logs content id', async () => {
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            { contentId: 'p1', channel: 'SMS', templateData: { smsBody: 'x' } },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );

    await userEvent.click(screen.getByLabelText('Show more content options icon'));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));

    expect(console.log).toHaveBeenCalledWith('Preview content', 'p1');
  });

  it('delivery settings section forwards onDeliverySettingChange to onChange', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [{ contentId: 'x', channel: 'SMS', templateData: {} }],
          deliverySetting: {},
        }}
        onChange={onChange}
        channels={CHANNELS}
        deliverySettingsData={{}}
      />,
    );

    await userEvent.click(screen.getByTestId('delivery-apply'));

    expect(onChange).toHaveBeenCalledWith({
      deliverySetting: { channelSetting: { SMS: { domainId: 'd1' } } },
    });
  });

  it('incentive-only row (no content): selecting an incentive updates selectedOfferDetails', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={onChange}
        channels={CHANNELS}
        incentivesData={{
          types: [
            { value: 'badges', label: 'Badges', isActive: true },
            { value: 'coupons', label: 'Coupons', isActive: true },
          ],
        }}
      />,
    );

    await userEvent.click(screen.getByRole('button', { name: /^add incentive$/i }));
    await waitFor(() => {
      expect(screen.getByRole('menu')).toBeInTheDocument();
    });
    await userEvent.click(within(screen.getByRole('menu')).getByText('Badges'));

    expect(onChange).toHaveBeenCalledWith({
      selectedOfferDetails: [{ type: 'badges' }],
    });
  });

  it('card with content shows add-incentive action when incentives are available', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            { contentId: 'c1', channel: 'SMS', templateData: { smsBody: 'Body' } },
          ],
        }}
        onChange={onChange}
        channels={CHANNELS}
        incentivesData={{
          types: [{ value: 'badges', label: 'Badges', isActive: true }],
        }}
      />,
    );

    const addIncentiveLinks = screen.getAllByRole('button', { name: /add incentive/i });
    expect(addIncentiveLinks.length).toBeGreaterThanOrEqual(1);
    await userEvent.click(addIncentiveLinks[addIncentiveLinks.length - 1]);
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('menu')).getByText('Badges'));

    expect(onChange).toHaveBeenCalledWith({
      selectedOfferDetails: [{ type: 'badges' }],
    });
  });

  it('renders previews for email subject, message body, push title, and email sender footer', () => {
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            { contentId: 'e1', channel: 'EMAIL', templateData: { emailSubject: 'Subject line' } },
            { contentId: 'm1', channel: 'SMS', templateData: { messageBody: 'Plain message body text' } },
            {
              contentId: 'mp1',
              channel: 'MOBILEPUSH',
              templateData: { notificationTitle: 'Push title here' },
            },
            {
              contentId: 'em2',
              channel: 'EMAIL',
              templateData: { emailFrom: 'from@example.com' },
            },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );

    expect(screen.getByText('Subject line')).toBeInTheDocument();
    expect(screen.getByText(/Plain message body text/)).toBeInTheDocument();
    expect(screen.getByText('Push title here')).toBeInTheDocument();
    expect(screen.getByText('from@example.com')).toBeInTheDocument();
  });

  it('mobile push preview falls back to notification body when title is absent', () => {
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            {
              contentId: 'mp-body',
              channel: 'MOBILEPUSH',
              templateData: { notificationBody: 'Body only preview text' },
            },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );
    expect(screen.getByText(/Body only preview text/)).toBeInTheDocument();
  });

  it('renders SMS sender from header and Viber sender line', () => {
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            { contentId: 's1', channel: 'SMS', templateData: { header: 'HDR' } },
            {
              contentId: 'v1',
              channel: 'VIBER',
              templateData: { templateConfigs: { viberSenderId: 'VIB-1' } },
            },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );

    expect(screen.getByText(/SMS sender ID HDR/)).toBeInTheDocument();
    expect(screen.getByText(/Viber sender ID VIB-1/)).toBeInTheDocument();
  });

  it('channel dropdown returns null when no active channels in list', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={onChange}
        channels={[
          {
            value: 'SMS',
            label: 'SMS',
            iconType: 'sms',
            isActive: false,
            paneKey: 'sms',
            channelProp: 'sms',
          },
        ]}
      />,
    );

    await userEvent.click(screen.getByRole('button', { name: /content template/i }));
    expect(screen.queryByRole('menu')).not.toBeInTheDocument();
  });

  it('channel dropdown returns null when all active channels are hidden', async () => {
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={jest.fn()}
        channels={CHANNELS}
        channelsToHide={CHANNELS.map((c) => c.value)}
      />,
    );

    await userEvent.click(screen.getByRole('button', { name: /content template/i }));
    expect(screen.queryByRole('menu')).not.toBeInTheDocument();
  });

  it('incentive dropdown is empty when only coupons and points types exist', async () => {
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={jest.fn()}
        channels={CHANNELS}
        incentivesData={{
          types: [
            { value: 'coupons', label: 'Coupons', isActive: true },
            { value: 'points', label: 'Points', isActive: true },
          ],
        }}
      />,
    );

    expect(screen.queryByRole('button', { name: /^add incentive$/i })).not.toBeInTheDocument();
  });

  it('treats null value as empty contentItems', () => {
    renderStep(
      <ChannelSelectionStep value={null} onChange={jest.fn()} channels={CHANNELS} />,
    );
    expect(screen.getByText('Add message content and incentive')).toBeInTheDocument();
  });

  it('uses CHANNELS fallback when channels prop is null', async () => {
    renderStep(
      <ChannelSelectionStep value={{ contentItems: [] }} onChange={jest.fn()} channels={null} />,
    );
    await userEvent.click(screen.getByRole('button', { name: /content template/i }));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    expect(within(screen.getByRole('menu')).getByText('SMS')).toBeInTheDocument();
  });

  it('notifies parent of custom channel but skips creatives when channel is missing from CHANNELS', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={onChange}
        channels={[
          {
            value: 'CUSTOM',
            label: 'Custom',
            iconType: 'sms',
            isActive: true,
            paneKey: 'custom',
            channelProp: 'custom',
          },
        ]}
      />,
    );
    await userEvent.click(screen.getByRole('button', { name: /content template/i }));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    await userEvent.click(screen.getByText('Custom'));
    expect(onChange).toHaveBeenCalledWith({ channel: 'CUSTOM', channels: [] });
    expect(screen.queryByTestId('creatives-mock')).not.toBeInTheDocument();
  });

  it('handleCreativesData returns early when save payload is null', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep value={{ contentItems: [] }} onChange={onChange} channels={CHANNELS} />,
    );
    await userEvent.click(screen.getByRole('button', { name: /content template/i }));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
    await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument());

    await userEvent.click(screen.getByTestId('creatives-save-null'));

    const callsWithContentItems = onChange.mock.calls.filter(([arg]) => arg && 'contentItems' in arg);
    expect(callsWithContentItems).toHaveLength(0);
    expect(screen.getByTestId('creatives-mock')).toBeInTheDocument();
  });

  it('edit is no-op when content channel is not in CHANNELS config', async () => {
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            { contentId: 'orphan', channel: 'NOT_A_REAL_CHANNEL', templateData: { smsBody: 'x' } },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );

    await userEvent.click(screen.getByLabelText('Show more content options icon'));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('menu')).getByText('Edit'));

    expect(screen.queryByTestId('creatives-mock')).not.toBeInTheDocument();
  });

  it('passes creativesMode preview when not editing', async () => {
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={jest.fn()}
        channels={CHANNELS}
        creativesMode="preview"
      />,
    );
    await userEvent.click(screen.getByRole('button', { name: /content template/i }));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
    await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument());
    expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-creatives-mode', 'preview');
  });

  it('appends to selectedOfferDetails when an incentive is already selected', async () => {
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={onChange}
        channels={CHANNELS}
        selectedOfferDetails={[{ type: 'existing' }]}
        incentivesData={{
          types: [{ value: 'badges', label: 'Badges', isActive: true }],
        }}
      />,
    );
    await userEvent.click(screen.getByRole('button', { name: /^add incentive$/i }));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('menu')).getByText('Badges'));
    expect(onChange).toHaveBeenCalledWith({
      selectedOfferDetails: [{ type: 'existing' }, { type: 'badges' }],
    });
  });

  it('uses template message field and non-string smsBody fallback in preview', () => {
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            { contentId: 'line1', channel: 'LINE', templateData: { message: 'Line message preview' } },
            { contentId: 'sms-num', channel: 'SMS', templateData: { smsBody: 42 } },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );
    expect(screen.getByText('Line message preview')).toBeInTheDocument();
    expect(screen.getByText('42')).toBeInTheDocument();
  });

  it('maps MPUSH channel to card type and reads push fields from android/ios payloads', () => {
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            {
              contentId: 'mpush1',
              channel: 'MPUSH',
              templateData: { notificationTitle: 'Direct MPUSH title' },
            },
            {
              contentId: 'and',
              channel: 'MOBILEPUSH',
              templateData: { androidContent: { notificationTitle: 'Android title' } },
            },
            {
              contentId: 'ios',
              channel: 'MOBILEPUSH',
              templateData: { iosContent: { body: 'Ios body text' } },
            },
            {
              contentId: 'and-body',
              channel: 'MOBILEPUSH',
              templateData: { androidContent: { notificationBody: 'Android notif body' } },
            },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );
    expect(screen.getByText('Direct MPUSH title')).toBeInTheDocument();
    expect(screen.getByText('Android title')).toBeInTheDocument();
    expect(screen.getByText(/Ios body text/)).toBeInTheDocument();
    expect(screen.getByText('Android notif body')).toBeInTheDocument();
  });

  it('Viber sender falls back to root viberSenderId', () => {
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            {
              contentId: 'v2',
              channel: 'VIBER',
              templateData: { viberSenderId: 'ROOT-V' },
            },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );
    expect(screen.getByText(/Viber sender ID ROOT-V/)).toBeInTheDocument();
  });

  it('SMS card without sender id omits sender row', () => {
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            { contentId: 'sms-plain', channel: 'SMS', templateData: { smsBody: 'Only body' } },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );
    expect(screen.getByText('Only body')).toBeInTheDocument();
    expect(screen.queryByText(/SMS sender ID/)).not.toBeInTheDocument();
  });

  it('Viber preview uses catch when JSON.parse throws for {} fallback', () => {
    const realParse = JSON.parse;
    const parseSpy = jest.spyOn(JSON, 'parse').mockImplementation((text, reviver) => {
      if (text === '{}') {
        throw new SyntaxError('forced');
      }
      return realParse.call(JSON, text, reviver);
    });
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            {
              contentId: 'vib-parse',
              channel: 'VIBER',
              templateData: { messageBody: '' },
            },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );
    expect(screen.getByText('VIBER')).toBeInTheDocument();
    parseSpy.mockRestore();
  });

  it('Viber preview falls back to channel when parsed JSON has no text (empty messageBody)', () => {
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            {
              contentId: 'vib-empty',
              channel: 'VIBER',
              templateData: { messageBody: '' },
            },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );
    expect(screen.getByText('VIBER')).toBeInTheDocument();
  });

  it('does not open creatives when clicked channel is in channelsToDisable', async () => {
    // Covers line 174-175: channelsToDisable.includes(channelValue) branch
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={onChange}
        channels={CHANNELS}
        channelsToDisable={['SMS']}
      />,
    );
    await userEvent.click(screen.getByRole('button', { name: /content template/i }));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    // SMS is in channelsToDisable — clicking it should not call onChange or show creatives
    await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
    expect(screen.queryByTestId('creatives-mock')).not.toBeInTheDocument();
    expect(onChange).not.toHaveBeenCalled();
  });

  it('incentive dropdown returns null when all active incentive types are coupons/points only (empty after filter)', async () => {
    // Covers line 277-278: availableIncentiveTypes.length === 0 (all inactive) path
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={jest.fn()}
        channels={CHANNELS}
        incentivesData={{
          types: [
            { value: 'rewards', label: 'Rewards', isActive: false },
            { value: 'other', label: 'Other', isActive: false },
          ],
        }}
      />,
    );
    // No incentive button should appear since all types are inactive
    expect(screen.queryByRole('button', { name: /^add incentive$/i })).not.toBeInTheDocument();
  });

  it('channel in channelsToDisable still shows in dropdown but selecting is a no-op', async () => {
    // Covers line 180-181: selectedChannelObj.isActive path via disabled channel click
    const onChange = jest.fn();
    renderStep(
      <ChannelSelectionStep
        value={{ contentItems: [] }}
        onChange={onChange}
        channels={[
          {
            value: 'EMAIL',
            label: 'Email',
            iconType: 'email',
            isActive: true,
            paneKey: 'email',
            channelProp: 'email',
          },
          {
            value: 'SMS',
            label: 'SMS',
            iconType: 'sms',
            isActive: true,
            paneKey: 'sms',
            channelProp: 'sms',
          },
        ]}
        channelsToDisable={['EMAIL']}
      />,
    );
    await userEvent.click(screen.getByRole('button', { name: /content template/i }));
    await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
    // Click the disabled EMAIL menu item — handleChannelSelect should return early
    await userEvent.click(within(screen.getByRole('menu')).getByText('Email'));
    expect(onChange).not.toHaveBeenCalled();
    expect(screen.queryByTestId('creatives-mock')).not.toBeInTheDocument();
  });

  it('ios notification body shows as preview when iosContent.notificationBody is present', () => {
    // Covers line 125: templateData.iosContent.notificationBody fallback branch
    renderStep(
      <ChannelSelectionStep
        value={{
          contentItems: [
            {
              contentId: 'ios-nb',
              channel: 'MOBILEPUSH',
              templateData: { iosContent: { notificationBody: 'IOS notif body text' } },
            },
          ],
        }}
        onChange={jest.fn()}
        channels={CHANNELS}
      />,
    );
    expect(screen.getByText(/IOS notif body text/)).toBeInTheDocument();
  });
});
