import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import MockAxiosAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
import SharedRunnersForm from '~/group_settings/components/shared_runners_form.vue';
import axios from '~/lib/utils/axios_utils';

const provide = {
  updatePath: '/test/update',
  sharedRunnersAvailability: 'enabled',
  parentSharedRunnersAvailability: null,
  runnerDisabled: 'disabled',
  runnerEnabled: 'enabled',
  runnerAllowOverride: 'allow_override',
};

jest.mock('~/flash');

describe('group_settings/components/shared_runners_form', () => {
  let wrapper;
  let mock;

  const createComponent = (provides = {}) => {
    wrapper = shallowMount(SharedRunnersForm, {
      provide: {
        ...provide,
        ...provides,
      },
    });
  };

  const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
  const findErrorAlert = () => wrapper.find(GlAlert);
  const findEnabledToggle = () => wrapper.find('[data-testid="enable-runners-toggle"]');
  const findOverrideToggle = () => wrapper.find('[data-testid="override-runners-toggle"]');
  const changeToggle = (toggle) => toggle.vm.$emit('change', !toggle.props('value'));
  const getSharedRunnersSetting = () => JSON.parse(mock.history.put[0].data).shared_runners_setting;
  const isLoadingIconVisible = () => findLoadingIcon().exists();

  beforeEach(() => {
    mock = new MockAxiosAdapter(axios);

    mock.onPut(provide.updatePath).reply(200);
  });

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

    mock.restore();
  });

  describe('with default', () => {
    beforeEach(() => {
      createComponent();
    });

    it('loading icon does not exist', () => {
      expect(isLoadingIconVisible()).toBe(false);
    });

    it('enabled toggle exists', () => {
      expect(findEnabledToggle().exists()).toBe(true);
    });

    it('override toggle does not exist', () => {
      expect(findOverrideToggle().exists()).toBe(false);
    });
  });

  describe('loading icon', () => {
    it('shows and hides the loading icon on request', async () => {
      createComponent();

      expect(isLoadingIconVisible()).toBe(false);

      findEnabledToggle().vm.$emit('change', true);

      await wrapper.vm.$nextTick();

      expect(isLoadingIconVisible()).toBe(true);

      await waitForPromises();

      expect(isLoadingIconVisible()).toBe(false);
    });
  });

  describe('enable toggle', () => {
    beforeEach(() => {
      createComponent();
    });

    it('enabling the toggle sends correct payload', async () => {
      findEnabledToggle().vm.$emit('change', true);

      await waitForPromises();

      expect(getSharedRunnersSetting()).toEqual(provide.runnerEnabled);
      expect(findOverrideToggle().exists()).toBe(false);
    });

    it('disabling the toggle sends correct payload', async () => {
      findEnabledToggle().vm.$emit('change', false);

      await waitForPromises();

      expect(getSharedRunnersSetting()).toEqual(provide.runnerDisabled);
      expect(findOverrideToggle().exists()).toBe(true);
    });
  });

  describe('override toggle', () => {
    beforeEach(() => {
      createComponent({ sharedRunnersAvailability: provide.runnerAllowOverride });
    });

    it('enabling the override toggle sends correct payload', async () => {
      findOverrideToggle().vm.$emit('change', true);

      await waitForPromises();

      expect(getSharedRunnersSetting()).toEqual(provide.runnerAllowOverride);
    });

    it('disabling the override toggle sends correct payload', async () => {
      findOverrideToggle().vm.$emit('change', false);

      await waitForPromises();

      expect(getSharedRunnersSetting()).toEqual(provide.runnerDisabled);
    });
  });

  describe('toggle disabled state', () => {
    it(`toggles are not disabled with setting ${provide.runnerDisabled}`, () => {
      createComponent({ sharedRunnersAvailability: provide.runnerDisabled });
      expect(findEnabledToggle().props('disabled')).toBe(false);
      expect(findOverrideToggle().props('disabled')).toBe(false);
    });

    it('toggles are disabled', () => {
      createComponent({
        sharedRunnersAvailability: provide.runnerDisabled,
        parentSharedRunnersAvailability: provide.runnerDisabled,
      });
      expect(findEnabledToggle().props('disabled')).toBe(true);
      expect(findOverrideToggle().props('disabled')).toBe(true);
    });
  });

  describe.each`
    errorObj                        | message
    ${{}}                           | ${'An error occurred while updating configuration. Refresh the page and try again.'}
    ${{ error: 'Undefined error' }} | ${'Undefined error Refresh the page and try again.'}
  `(`with error $errorObj`, ({ errorObj, message }) => {
    beforeEach(async () => {
      mock.onPut(provide.updatePath).reply(500, errorObj);

      createComponent();
      changeToggle(findEnabledToggle());

      await waitForPromises();
    });

    it('error should be shown', () => {
      expect(findErrorAlert().text()).toBe(message);
    });
  });
});
