import { shallowMount } from '@vue/test-utils';
import { GlModal, GlLoadingIcon } from '@gitlab/ui';
import {
  mockCancelledPipelineHeader,
  mockFailedPipelineHeader,
  mockRunningPipelineHeader,
  mockSuccessfulPipelineHeader,
} from './mock_data';
import HeaderComponent from '~/pipelines/components/header_component.vue';
import deletePipelineMutation from '~/pipelines/graphql/mutations/delete_pipeline.mutation.graphql';
import retryPipelineMutation from '~/pipelines/graphql/mutations/retry_pipeline.mutation.graphql';
import cancelPipelineMutation from '~/pipelines/graphql/mutations/cancel_pipeline.mutation.graphql';

describe('Pipeline details header', () => {
  let wrapper;
  let glModalDirective;

  const findDeleteModal = () => wrapper.find(GlModal);
  const findRetryButton = () => wrapper.find('[data-testid="retryPipeline"]');
  const findCancelButton = () => wrapper.find('[data-testid="cancelPipeline"]');
  const findDeleteButton = () => wrapper.find('[data-testid="deletePipeline"]');
  const findLoadingIcon = () => wrapper.find(GlLoadingIcon);

  const defaultProvideOptions = {
    pipelineId: 14,
    pipelineIid: 1,
    paths: {
      pipelinesPath: '/namespace/my-project/-/pipelines',
      fullProject: '/namespace/my-project',
    },
  };

  const createComponent = (pipelineMock = mockRunningPipelineHeader, { isLoading } = false) => {
    glModalDirective = jest.fn();

    const $apollo = {
      queries: {
        pipeline: {
          loading: isLoading,
          stopPolling: jest.fn(),
          startPolling: jest.fn(),
        },
      },
      mutate: jest.fn(),
    };

    return shallowMount(HeaderComponent, {
      data() {
        return {
          pipeline: pipelineMock,
        };
      },
      provide: {
        ...defaultProvideOptions,
      },
      directives: {
        glModal: {
          bind(_, { value }) {
            glModalDirective(value);
          },
        },
      },
      mocks: { $apollo },
    });
  };

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

  describe('initial loading', () => {
    beforeEach(() => {
      wrapper = createComponent(null, { isLoading: true });
    });

    it('shows a loading state while graphQL is fetching initial data', () => {
      expect(findLoadingIcon().exists()).toBe(true);
    });
  });

  describe('visible state', () => {
    it.each`
      state           | pipelineData                    | retryValue | cancelValue
      ${'cancelled'}  | ${mockCancelledPipelineHeader}  | ${true}    | ${false}
      ${'failed'}     | ${mockFailedPipelineHeader}     | ${true}    | ${false}
      ${'running'}    | ${mockRunningPipelineHeader}    | ${false}   | ${true}
      ${'successful'} | ${mockSuccessfulPipelineHeader} | ${false}   | ${false}
    `(
      'with a $state pipeline, it will show actions: retry $retryValue and cancel $cancelValue',
      ({ pipelineData, retryValue, cancelValue }) => {
        wrapper = createComponent(pipelineData);

        expect(findRetryButton().exists()).toBe(retryValue);
        expect(findCancelButton().exists()).toBe(cancelValue);
      },
    );
  });

  describe('polling', () => {
    it('is stopped when pipeline is finished', async () => {
      wrapper = createComponent({ ...mockRunningPipelineHeader });

      await wrapper.setData({
        pipeline: { ...mockCancelledPipelineHeader },
      });

      expect(wrapper.vm.$apollo.queries.pipeline.stopPolling).toHaveBeenCalled();
    });

    it('is not stopped when pipeline is not finished', () => {
      wrapper = createComponent();

      expect(wrapper.vm.$apollo.queries.pipeline.stopPolling).not.toHaveBeenCalled();
    });
  });

  describe('actions', () => {
    describe('Retry action', () => {
      beforeEach(() => {
        wrapper = createComponent(mockCancelledPipelineHeader);
      });

      it('should call retryPipeline Mutation with pipeline id', () => {
        findRetryButton().vm.$emit('click');

        expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
          mutation: retryPipelineMutation,
          variables: { id: mockCancelledPipelineHeader.id },
        });
      });
    });

    describe('Cancel action', () => {
      beforeEach(() => {
        wrapper = createComponent(mockRunningPipelineHeader);
      });

      it('should call cancelPipeline Mutation with pipeline id', () => {
        findCancelButton().vm.$emit('click');

        expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
          mutation: cancelPipelineMutation,
          variables: { id: mockRunningPipelineHeader.id },
        });
      });
    });

    describe('Delete action', () => {
      beforeEach(() => {
        wrapper = createComponent(mockFailedPipelineHeader);
      });

      it('displays delete modal when clicking on delete and does not call the delete action', () => {
        findDeleteButton().vm.$emit('click');

        expect(findDeleteModal().props('modalId')).toBe(wrapper.vm.$options.DELETE_MODAL_ID);
        expect(glModalDirective).toHaveBeenCalledWith(wrapper.vm.$options.DELETE_MODAL_ID);
        expect(wrapper.vm.$apollo.mutate).not.toHaveBeenCalled();
      });

      it('should call deletePipeline Mutation with pipeline id when modal is submitted', () => {
        findDeleteModal().vm.$emit('ok');

        expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
          mutation: deletePipelineMutation,
          variables: { id: mockFailedPipelineHeader.id },
        });
      });
    });
  });
});
