airbnb/caravel

View on GitHub
superset-frontend/src/components/DropdownContainer/DropdownContainer.test.tsx

Summary

Maintainability
A
40 mins
Test Coverage
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
import userEvent from '@testing-library/user-event';
import { screen, render } from 'spec/helpers/testing-library';
import Button from '../Button';
import Icons from '../Icons';
import DropdownContainer from '.';

const generateItems = (n: number) =>
  Array.from({ length: n }).map((_, i) => ({
    id: `el-${i + 1}`,
    element: <Button>{`Element ${i + 1}`}</Button>,
  }));

const ITEMS = generateItems(10);

const mockOverflowingIndex = async (
  overflowingIndex: number,
  func: Function,
) => {
  const spy = jest.spyOn(global.React, 'useState');
  spy.mockImplementation(() => [overflowingIndex, jest.fn()]);
  await func();
  spy.mockRestore();
};

test('renders children', () => {
  render(<DropdownContainer items={generateItems(3)} />);
  expect(screen.getByText('Element 1')).toBeInTheDocument();
  expect(screen.getByText('Element 2')).toBeInTheDocument();
  expect(screen.getByText('Element 3')).toBeInTheDocument();
});

test('renders children with custom horizontal spacing', () => {
  render(<DropdownContainer items={ITEMS} style={{ gap: 20 }} />);
  expect(screen.getByTestId('container')).toHaveStyle('gap: 20px');
});

test('renders a dropdown trigger when overflowing', async () => {
  await mockOverflowingIndex(3, () => {
    render(<DropdownContainer items={ITEMS} />);
    expect(screen.getByText('More')).toBeInTheDocument();
  });
});

test('renders a dropdown trigger with custom icon', async () => {
  await mockOverflowingIndex(3, async () => {
    render(
      <DropdownContainer items={ITEMS} dropdownTriggerIcon={<Icons.Link />} />,
    );
    expect(
      await screen.findByRole('img', { name: 'link' }),
    ).toBeInTheDocument();
  });
});

test('renders a dropdown trigger with custom text', async () => {
  await mockOverflowingIndex(3, () => {
    const customText = 'Custom text';
    render(
      <DropdownContainer items={ITEMS} dropdownTriggerText={customText} />,
    );
    expect(screen.getByText(customText)).toBeInTheDocument();
  });
});

test('renders a dropdown trigger with custom count', async () => {
  await mockOverflowingIndex(3, () => {
    const customCount = 99;
    render(
      <DropdownContainer items={ITEMS} dropdownTriggerCount={customCount} />,
    );
    expect(screen.getByTitle(customCount)).toBeInTheDocument();
  });
});

test('does not render a dropdown button when not overflowing', () => {
  render(<DropdownContainer items={generateItems(3)} />);
  expect(screen.queryByText('More')).not.toBeInTheDocument();
});

test('renders a dropdown when overflowing', async () => {
  await mockOverflowingIndex(3, () => {
    render(<DropdownContainer items={ITEMS} />);
    userEvent.click(screen.getByText('More'));
    expect(screen.getByTestId('dropdown-content')).toBeInTheDocument();
  });
});

test('renders children with custom vertical spacing', async () => {
  await mockOverflowingIndex(3, () => {
    render(<DropdownContainer items={ITEMS} dropdownStyle={{ gap: 20 }} />);
    userEvent.click(screen.getByText('More'));
    expect(screen.getByTestId('dropdown-content')).toHaveStyle('gap: 20px');
  });
});

test('fires event when overflowing state changes', async () => {
  await mockOverflowingIndex(3, () => {
    const onOverflowingStateChange = jest.fn();
    render(
      <DropdownContainer
        items={generateItems(5)}
        onOverflowingStateChange={onOverflowingStateChange}
      />,
    );
    expect(onOverflowingStateChange).toHaveBeenCalledWith({
      notOverflowed: ['el-1', 'el-2', 'el-3'],
      overflowed: ['el-4', 'el-5'],
    });
  });
});

test('renders a dropdown with custom content', async () => {
  await mockOverflowingIndex(3, () => {
    const customDropdownContent = <div>Custom content</div>;
    render(
      <DropdownContainer
        items={ITEMS}
        dropdownContent={() => customDropdownContent}
      />,
    );
    userEvent.click(screen.getByText('More'));
    expect(screen.getByText('Custom content')).toBeInTheDocument();
  });
});

test('Shows tooltip on dropdown trigger hover', async () => {
  await mockOverflowingIndex(3, async () => {
    render(
      <DropdownContainer
        items={generateItems(5)}
        dropdownTriggerTooltip="Test tooltip"
      />,
    );
    userEvent.hover(screen.getByText('More'));
    expect(await screen.findByText('Test tooltip')).toBeInTheDocument();
  });
});