import $ from 'jquery';
import MockAdaptor from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import IntegrationSettingsForm from '~/integrations/integration_settings_form';

describe('IntegrationSettingsForm', () => {
  const FIXTURE = 'services/edit_service.html';
  preloadFixtures(FIXTURE);

  beforeEach(() => {
    loadFixtures(FIXTURE);
  });

  describe('contructor', () => {
    let integrationSettingsForm;

    beforeEach(() => {
      integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
      jest.spyOn(integrationSettingsForm, 'init').mockImplementation(() => {});
    });

    it('should initialize form element refs on class object', () => {
      // Form Reference
      expect(integrationSettingsForm.$form).toBeDefined();
      expect(integrationSettingsForm.$form.prop('nodeName')).toEqual('FORM');
      expect(integrationSettingsForm.formActive).toBeDefined();

      // Form Child Elements
      expect(integrationSettingsForm.$submitBtn).toBeDefined();
      expect(integrationSettingsForm.$submitBtnLoader).toBeDefined();
      expect(integrationSettingsForm.$submitBtnLabel).toBeDefined();
    });

    it('should initialize form metadata on class object', () => {
      expect(integrationSettingsForm.testEndPoint).toBeDefined();
      expect(integrationSettingsForm.canTestService).toBeDefined();
    });
  });

  describe('toggleServiceState', () => {
    let integrationSettingsForm;

    beforeEach(() => {
      integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
    });

    it('should remove `novalidate` attribute to form when called with `true`', () => {
      integrationSettingsForm.formActive = true;
      integrationSettingsForm.toggleServiceState();

      expect(integrationSettingsForm.$form.attr('novalidate')).not.toBeDefined();
    });

    it('should set `novalidate` attribute to form when called with `false`', () => {
      integrationSettingsForm.formActive = false;
      integrationSettingsForm.toggleServiceState();

      expect(integrationSettingsForm.$form.attr('novalidate')).toBeDefined();
    });
  });

  describe('toggleSubmitBtnLabel', () => {
    let integrationSettingsForm;

    beforeEach(() => {
      integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
    });

    it('should set Save button label to "Test settings and save changes" when serviceActive & canTestService are `true`', () => {
      integrationSettingsForm.canTestService = true;
      integrationSettingsForm.formActive = true;

      integrationSettingsForm.toggleSubmitBtnLabel();

      expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual(
        'Test settings and save changes',
      );
    });

    it('should set Save button label to "Save changes" when either serviceActive or canTestService (or both) is `false`', () => {
      integrationSettingsForm.canTestService = false;
      integrationSettingsForm.formActive = false;

      integrationSettingsForm.toggleSubmitBtnLabel();

      expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes');

      integrationSettingsForm.formActive = true;

      integrationSettingsForm.toggleSubmitBtnLabel();

      expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes');

      integrationSettingsForm.canTestService = true;
      integrationSettingsForm.formActive = false;

      integrationSettingsForm.toggleSubmitBtnLabel();

      expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes');
    });
  });

  describe('toggleSubmitBtnState', () => {
    let integrationSettingsForm;

    beforeEach(() => {
      integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
    });

    it('should disable Save button and show loader animation when called with `true`', () => {
      integrationSettingsForm.toggleSubmitBtnState(true);

      expect(integrationSettingsForm.$submitBtn.is(':disabled')).toBeTruthy();
      expect(integrationSettingsForm.$submitBtnLoader.hasClass('hidden')).toBeFalsy();
    });

    it('should enable Save button and hide loader animation when called with `false`', () => {
      integrationSettingsForm.toggleSubmitBtnState(false);

      expect(integrationSettingsForm.$submitBtn.is(':disabled')).toBeFalsy();
      expect(integrationSettingsForm.$submitBtnLoader.hasClass('hidden')).toBeTruthy();
    });
  });

  describe('testSettings', () => {
    let integrationSettingsForm;
    let formData;
    let mock;

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

      jest.spyOn(axios, 'put');

      integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
      // eslint-disable-next-line no-jquery/no-serialize
      formData = integrationSettingsForm.$form.serialize();
    });

    afterEach(() => {
      mock.restore();
    });

    it('should make an ajax request with provided `formData`', () => {
      return integrationSettingsForm.testSettings(formData).then(() => {
        expect(axios.put).toHaveBeenCalledWith(integrationSettingsForm.testEndPoint, formData);
      });
    });

    it('should show error Flash with `Save anyway` action if ajax request responds with error in test', () => {
      const errorMessage = 'Test failed.';
      mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
        error: true,
        message: errorMessage,
        service_response: 'some error',
        test_failed: true,
      });

      return integrationSettingsForm.testSettings(formData).then(() => {
        const $flashContainer = $('.flash-container');

        expect(
          $flashContainer
            .find('.flash-text')
            .text()
            .trim(),
        ).toEqual('Test failed. some error');

        expect($flashContainer.find('.flash-action')).toBeDefined();
        expect(
          $flashContainer
            .find('.flash-action')
            .text()
            .trim(),
        ).toEqual('Save anyway');
      });
    });

    it('should not show error Flash with `Save anyway` action if ajax request responds with error in validation', () => {
      const errorMessage = 'Validations failed.';
      mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
        error: true,
        message: errorMessage,
        service_response: 'some error',
        test_failed: false,
      });

      return integrationSettingsForm.testSettings(formData).then(() => {
        const $flashContainer = $('.flash-container');

        expect(
          $flashContainer
            .find('.flash-text')
            .text()
            .trim(),
        ).toEqual('Validations failed. some error');

        expect($flashContainer.find('.flash-action')).toBeDefined();
        expect(
          $flashContainer
            .find('.flash-action')
            .text()
            .trim(),
        ).toEqual('');
      });
    });

    it('should submit form if ajax request responds without any error in test', () => {
      jest.spyOn(integrationSettingsForm.$form, 'submit').mockImplementation(() => {});

      mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
        error: false,
      });

      return integrationSettingsForm.testSettings(formData).then(() => {
        expect(integrationSettingsForm.$form.submit).toHaveBeenCalled();
      });
    });

    it('should submit form when clicked on `Save anyway` action of error Flash', () => {
      jest.spyOn(integrationSettingsForm.$form, 'submit').mockImplementation(() => {});

      const errorMessage = 'Test failed.';
      mock.onPut(integrationSettingsForm.testEndPoint).reply(200, {
        error: true,
        message: errorMessage,
        test_failed: true,
      });

      return integrationSettingsForm
        .testSettings(formData)
        .then(() => {
          const $flashAction = $('.flash-container .flash-action');

          expect($flashAction).toBeDefined();

          $flashAction.get(0).click();
        })
        .then(() => {
          expect(integrationSettingsForm.$form.submit).toHaveBeenCalled();
        });
    });

    it('should show error Flash if ajax request failed', () => {
      const errorMessage = 'Something went wrong on our end.';

      mock.onPut(integrationSettingsForm.testEndPoint).networkError();

      return integrationSettingsForm.testSettings(formData).then(() => {
        expect(
          $('.flash-container .flash-text')
            .text()
            .trim(),
        ).toEqual(errorMessage);
      });
    });

    it('should always call `toggleSubmitBtnState` with `false` once request is completed', () => {
      mock.onPut(integrationSettingsForm.testEndPoint).networkError();

      jest.spyOn(integrationSettingsForm, 'toggleSubmitBtnState').mockImplementation(() => {});

      return integrationSettingsForm.testSettings(formData).then(() => {
        expect(integrationSettingsForm.toggleSubmitBtnState).toHaveBeenCalledWith(false);
      });
    });
  });
});