text: import { GlDropdownItem } from '@gitlab/ui';
  import { mount, shallowMount } from '@vue/test-utils';
  import Vue from 'vue';
  import { __ } from '~/locale';
  import TagFieldNew from '~/releases/components/tag_field_new.vue';
  import createStore from '~/releases/stores';
  import createEditNewModule from '~/releases/stores/modules/edit_new';
  
  const TEST_TAG_NAME = 'test-tag-name';
  const TEST_PROJECT_ID = '1234';
  const TEST_CREATE_FROM = 'test-create-from';
  const NONEXISTENT_TAG_NAME = 'nonexistent-tag';
  
  describe('releases/components/tag_field_new', () => {
    let store;
    let wrapper;
    let RefSelectorStub;
  
    const createComponent = (
      mountFn = shallowMount,
      { searchQuery } = { searchQuery: NONEXISTENT_TAG_NAME },
    ) => {
      // A mock version of the RefSelector component that just renders the
      // #footer slot, so that the content inside this slot can be tested.
      RefSelectorStub = Vue.component('RefSelectorStub', {
        data() {
          return {
            footerSlotProps: {
              isLoading: false,
              matches: {
                tags: {
                  totalCount: 1,
                  list: [{ name: TEST_TAG_NAME }],
                },
              },
              query: searchQuery,
            },
          };
        },
        template: '<div><slot name="footer" v-bind="footerSlotProps"></slot></div>',
      });
  
      wrapper = mountFn(TagFieldNew, {
        store,
        stubs: {
          RefSelector: RefSelectorStub,
        },
      });
    };
  
    beforeEach(() => {
      store = createStore({
        modules: {
          editNew: createEditNewModule({
            projectId: TEST_PROJECT_ID,
          }),
        },
      });
  
      store.state.editNew.createFrom = TEST_CREATE_FROM;
  
      store.state.editNew.release = {
        tagName: TEST_TAG_NAME,
        assets: {
          links: [],
        },
      };
    });
  
    afterEach(() => {
      wrapper.destroy();
      wrapper = null;
    });
  
    const findTagNameFormGroup = () => wrapper.find('[data-testid="tag-name-field"]');
    const findTagNameDropdown = () => findTagNameFormGroup().find(RefSelectorStub);
  
    const findCreateFromFormGroup = () => wrapper.find('[data-testid="create-from-field"]');
    const findCreateFromDropdown = () => findCreateFromFormGroup().find(RefSelectorStub);
  
    const findCreateNewTagOption = () => wrapper.find(GlDropdownItem);
  
    describe('"Tag name" field', () => {
      describe('rendering and behavior', () => {
        beforeEach(() => createComponent());
  
        it('renders a label', () => {
          expect(findTagNameFormGroup().attributes().label).toBe(__('Tag name'));
          expect(findTagNameFormGroup().props().labelDescription).toBe(__('*Required'));
        });
  
        describe('when the user selects a new tag name', () => {
          beforeEach(async () => {
            findCreateNewTagOption().vm.$emit('click');
          });
  
          it("updates the store's release.tagName property", () => {
            expect(store.state.editNew.release.tagName).toBe(NONEXISTENT_TAG_NAME);
          });
  
          it('hides the "Create from" field', () => {
            expect(findCreateFromFormGroup().exists()).toBe(true);
          });
        });
  
        describe('when the user selects an existing tag name', () => {
          const updatedTagName = 'updated-tag-name';
  
          beforeEach(async () => {
            findTagNameDropdown().vm.$emit('input', updatedTagName);
          });
  
          it("updates the store's release.tagName property", () => {
            expect(store.state.editNew.release.tagName).toBe(updatedTagName);
          });
  
          it('shows the "Create from" field', () => {
            expect(findCreateFromFormGroup().exists()).toBe(false);
          });
        });
      });
  
      describe('"Create tag" option', () => {
        describe('when the search query exactly matches one of the search results', () => {
          beforeEach(async () => {
            createComponent(mount, { searchQuery: TEST_TAG_NAME });
          });
  
          it('does not show the "Create tag" option', () => {
            expect(findCreateNewTagOption().exists()).toBe(false);
          });
        });
  
        describe('when the search query does not exactly match one of the search results', () => {
          beforeEach(async () => {
            createComponent(mount, { searchQuery: NONEXISTENT_TAG_NAME });
          });
  
          it('shows the "Create tag" option', () => {
            expect(findCreateNewTagOption().exists()).toBe(true);
          });
        });
      });
  
      describe('validation', () => {
        beforeEach(() => {
          createComponent(mount);
        });
  
        /**
         * Utility function to test the visibility of the validation message
         * @param {'shown' | 'hidden'} state The expected state of the validation message.
         * Should be passed either 'shown' or 'hidden'
         */
        const expectValidationMessageToBe = async (state) => {
          await wrapper.vm.$nextTick();
  
          expect(findTagNameFormGroup().element).toHaveClass(
            state === 'shown' ? 'is-invalid' : 'is-valid',
          );
          expect(findTagNameFormGroup().element).not.toHaveClass(
            state === 'shown' ? 'is-valid' : 'is-invalid',
          );
        };
  
        describe('when the user has not yet interacted with the component', () => {
          it('does not display a validation error', async () => {
            findTagNameDropdown().vm.$emit('input', '');
  
            await expectValidationMessageToBe('hidden');
          });
        });
  
        describe('when the user has interacted with the component and the value is not empty', () => {
          it('does not display validation error', async () => {
            findTagNameDropdown().vm.$emit('hide');
  
            await expectValidationMessageToBe('hidden');
          });
        });
  
        describe('when the user has interacted with the component and the value is empty', () => {
          it('displays a validation error', async () => {
            findTagNameDropdown().vm.$emit('input', '');
            findTagNameDropdown().vm.$emit('hide');
  
            await expectValidationMessageToBe('shown');
          });
        });
      });
    });
  
    describe('"Create from" field', () => {
      beforeEach(() => createComponent());
  
      it('renders a label', () => {
        expect(findCreateFromFormGroup().attributes().label).toBe('Create from');
      });
  
      describe('when the user selects a git ref', () => {
        it("updates the store's createFrom property", async () => {
          const updatedCreateFrom = 'update-create-from';
          findCreateFromDropdown().vm.$emit('input', updatedCreateFrom);
  
          expect(store.state.editNew.createFrom).toBe(updatedCreateFrom);
        });
      });
    });
  });
  