import { mount } from '@vue/test-utils';
import { GlFormGroup, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormTextarea } from '@gitlab/ui';
import DynamicField from '~/integrations/edit/components/dynamic_field.vue';

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

  const defaultProps = {
    help: 'The URL of the project',
    name: 'project_url',
    placeholder: 'https://jira.example.com',
    title: 'Project URL',
    type: 'text',
    value: '1',
  };

  const createComponent = (props, isInheriting = false) => {
    wrapper = mount(DynamicField, {
      propsData: { ...defaultProps, ...props },
      computed: {
        isInheriting: () => isInheriting,
      },
    });
  };

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

  const findGlFormGroup = () => wrapper.find(GlFormGroup);
  const findGlFormCheckbox = () => wrapper.find(GlFormCheckbox);
  const findGlFormInput = () => wrapper.find(GlFormInput);
  const findGlFormSelect = () => wrapper.find(GlFormSelect);
  const findGlFormTextarea = () => wrapper.find(GlFormTextarea);

  describe('template', () => {
    describe.each([[true, 'disabled', 'readonly'], [false, undefined, undefined]])(
      'dynamic field, when isInheriting = `%p`',
      (isInheriting, disabled, readonly) => {
        describe('type is checkbox', () => {
          beforeEach(() => {
            createComponent(
              {
                type: 'checkbox',
              },
              isInheriting,
            );
          });

          it(`renders GlFormCheckbox, which ${isInheriting ? 'is' : 'is not'} disabled`, () => {
            expect(findGlFormCheckbox().exists()).toBe(true);
            expect(
              findGlFormCheckbox()
                .find('[type=checkbox]')
                .attributes('disabled'),
            ).toBe(disabled);
          });

          it('does not render other types of input', () => {
            expect(findGlFormSelect().exists()).toBe(false);
            expect(findGlFormTextarea().exists()).toBe(false);
            expect(findGlFormInput().exists()).toBe(false);
          });
        });

        describe('type is select', () => {
          beforeEach(() => {
            createComponent(
              {
                type: 'select',
                choices: [['all', 'All details'], ['standard', 'Standard']],
              },
              isInheriting,
            );
          });

          it(`renders GlFormSelect, which ${isInheriting ? 'is' : 'is not'} disabled`, () => {
            expect(findGlFormSelect().exists()).toBe(true);
            expect(findGlFormSelect().findAll('option')).toHaveLength(2);
            expect(
              findGlFormSelect()
                .find('select')
                .attributes('disabled'),
            ).toBe(disabled);
          });

          it('does not render other types of input', () => {
            expect(findGlFormCheckbox().exists()).toBe(false);
            expect(findGlFormTextarea().exists()).toBe(false);
            expect(findGlFormInput().exists()).toBe(false);
          });
        });

        describe('type is textarea', () => {
          beforeEach(() => {
            createComponent(
              {
                type: 'textarea',
              },
              isInheriting,
            );
          });

          it(`renders GlFormTextarea, which ${isInheriting ? 'is' : 'is not'} readonly`, () => {
            expect(findGlFormTextarea().exists()).toBe(true);
            expect(
              findGlFormTextarea()
                .find('textarea')
                .attributes('readonly'),
            ).toBe(readonly);
          });

          it('does not render other types of input', () => {
            expect(findGlFormCheckbox().exists()).toBe(false);
            expect(findGlFormSelect().exists()).toBe(false);
            expect(findGlFormInput().exists()).toBe(false);
          });
        });

        describe('type is password', () => {
          beforeEach(() => {
            createComponent(
              {
                type: 'password',
              },
              isInheriting,
            );
          });

          it(`renders GlFormInput, which ${isInheriting ? 'is' : 'is not'} readonly`, () => {
            expect(findGlFormInput().exists()).toBe(true);
            expect(findGlFormInput().attributes('type')).toBe('password');
            expect(findGlFormInput().attributes('readonly')).toBe(readonly);
          });

          it('does not render other types of input', () => {
            expect(findGlFormCheckbox().exists()).toBe(false);
            expect(findGlFormSelect().exists()).toBe(false);
            expect(findGlFormTextarea().exists()).toBe(false);
          });
        });

        describe('type is text', () => {
          beforeEach(() => {
            createComponent(
              {
                type: 'text',
                required: true,
              },
              isInheriting,
            );
          });

          it(`renders GlFormInput, which ${isInheriting ? 'is' : 'is not'} readonly`, () => {
            expect(findGlFormInput().exists()).toBe(true);
            expect(findGlFormInput().attributes()).toMatchObject({
              type: 'text',
              id: 'service_project_url',
              name: 'service[project_url]',
              placeholder: defaultProps.placeholder,
              required: 'required',
            });
            expect(findGlFormInput().attributes('readonly')).toBe(readonly);
          });

          it('does not render other types of input', () => {
            expect(findGlFormCheckbox().exists()).toBe(false);
            expect(findGlFormSelect().exists()).toBe(false);
            expect(findGlFormTextarea().exists()).toBe(false);
          });
        });
      },
    );

    describe('help text', () => {
      it('renders description with help text', () => {
        createComponent();

        expect(
          findGlFormGroup()
            .find('small')
            .text(),
        ).toBe(defaultProps.help);
      });

      it('renders description with help text as HTML', () => {
        const helpHTML = 'The <strong>URL</strong> of the project';

        createComponent({
          help: helpHTML,
        });

        expect(
          findGlFormGroup()
            .find('small')
            .html(),
        ).toContain(helpHTML);
      });
    });

    describe('label text', () => {
      it('renders label with title', () => {
        createComponent();

        expect(
          findGlFormGroup()
            .find('label')
            .text(),
        ).toBe(defaultProps.title);
      });

      describe('for password field with some value (hidden by backend)', () => {
        it('renders label with new password title', () => {
          createComponent({
            type: 'password',
            value: 'true',
          });

          expect(
            findGlFormGroup()
              .find('label')
              .text(),
          ).toBe(`Enter new ${defaultProps.title}`);
        });
      });
    });

    describe('validations', () => {
      describe('password field', () => {
        beforeEach(() => {
          createComponent({
            type: 'password',
            required: true,
            value: null,
          });

          wrapper.vm.validated = true;
        });

        describe('without value', () => {
          it('requires validation', () => {
            expect(wrapper.vm.valid).toBe(false);
            expect(findGlFormGroup().classes('is-invalid')).toBe(true);
            expect(findGlFormInput().classes('is-invalid')).toBe(true);
          });
        });

        describe('with value', () => {
          beforeEach(() => {
            wrapper.setProps({ value: 'true' });
          });

          it('does not require validation', () => {
            expect(wrapper.vm.valid).toBe(true);
            expect(findGlFormGroup().classes('is-valid')).toBe(true);
            expect(findGlFormInput().classes('is-valid')).toBe(true);
          });
        });
      });
    });
  });
});
