import { GlLink, GlTable, GlSkeletonLoader } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerList from '~/runner/components/runner_list.vue';
import { runnersData } from '../mock_data';

const mockRunners = runnersData.data.runners.nodes;
const mockActiveRunnersCount = mockRunners.length;

describe('RunnerList', () => {
  let wrapper;

  const findActiveRunnersMessage = () => wrapper.findByTestId('active-runners-message');
  const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
  const findTable = () => wrapper.findComponent(GlTable);
  const findHeaders = () => wrapper.findAll('th');
  const findRows = () => wrapper.findAll('[data-testid^="runner-row-"]');
  const findCell = ({ row = 0, fieldKey }) =>
    findRows().at(row).find(`[data-testid="td-${fieldKey}"]`);

  const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => {
    wrapper = extendedWrapper(
      mountFn(RunnerList, {
        propsData: {
          runners: mockRunners,
          activeRunnersCount: mockActiveRunnersCount,
          ...props,
        },
      }),
    );
  };

  beforeEach(() => {
    createComponent({}, mount);
  });

  afterEach(() => {
    wrapper.destroy();
  });

  it('Displays active runner count', () => {
    expect(findActiveRunnersMessage().text()).toBe(
      `Runners currently online: ${mockActiveRunnersCount}`,
    );
  });

  it('Displays a large active runner count', () => {
    createComponent({ props: { activeRunnersCount: 2000 } });

    expect(findActiveRunnersMessage().text()).toBe('Runners currently online: 2,000');
  });

  it('Displays headers', () => {
    const headerLabels = findHeaders().wrappers.map((w) => w.text());

    expect(headerLabels).toEqual([
      'Type/State',
      'Runner',
      'Version',
      'IP Address',
      'Projects',
      'Jobs',
      'Tags',
      'Last contact',
      '', // actions has no label
    ]);
  });

  it('Displays a list of runners', () => {
    expect(findRows()).toHaveLength(2);

    expect(findSkeletonLoader().exists()).toBe(false);
  });

  it('Displays details of a runner', () => {
    const { id, description, version, ipAddress, shortSha } = mockRunners[0];

    // Badges
    expect(findCell({ fieldKey: 'type' }).text()).toMatchInterpolatedText('shared locked');

    // Runner identifier
    expect(findCell({ fieldKey: 'name' }).text()).toContain(
      `#${getIdFromGraphQLId(id)} (${shortSha})`,
    );
    expect(findCell({ fieldKey: 'name' }).text()).toContain(description);

    // Other fields: some cells are empty in the first iteration
    // See https://gitlab.com/gitlab-org/gitlab/-/issues/329658#pending-features
    expect(findCell({ fieldKey: 'version' }).text()).toBe(version);
    expect(findCell({ fieldKey: 'ipAddress' }).text()).toBe(ipAddress);
    expect(findCell({ fieldKey: 'projectCount' }).text()).toBe('');
    expect(findCell({ fieldKey: 'jobCount' }).text()).toBe('');
    expect(findCell({ fieldKey: 'tagList' }).text()).toBe('');
    expect(findCell({ fieldKey: 'contactedAt' }).text()).toEqual(expect.any(String));
    expect(findCell({ fieldKey: 'actions' }).text()).toBe('');
  });

  it('Links to the runner page', () => {
    const { id } = mockRunners[0];

    expect(findCell({ fieldKey: 'name' }).find(GlLink).attributes('href')).toBe(
      `/admin/runners/${getIdFromGraphQLId(id)}`,
    );
  });

  describe('When data is loading', () => {
    it('shows a busy state', () => {
      createComponent({ props: { runners: [], loading: true } });
      expect(findTable().attributes('busy')).toBeTruthy();
    });

    it('when there are no runners, shows an skeleton loader', () => {
      createComponent({ props: { runners: [], loading: true } }, mount);

      expect(findSkeletonLoader().exists()).toBe(true);
    });

    it('when there are runners, shows a busy indicator skeleton loader', () => {
      createComponent({ props: { loading: true } }, mount);

      expect(findSkeletonLoader().exists()).toBe(false);
    });
  });
});