import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import setupAxiosStartupCalls from '~/lib/utils/axios_startup_calls';

describe('setupAxiosStartupCalls', () => {
  const AXIOS_RESPONSE = { text: 'AXIOS_RESPONSE' };
  const STARTUP_JS_RESPONSE = { text: 'STARTUP_JS_RESPONSE' };
  let mock;

  function mockFetchCall(status) {
    const p = {
      ok: status >= 200 && status < 300,
      status,
      headers: new Headers({ 'Content-Type': 'application/json' }),
      statusText: `MOCK-FETCH ${status}`,
      clone: () => p,
      json: () => Promise.resolve(STARTUP_JS_RESPONSE),
    };
    return Promise.resolve(p);
  }

  function mockConsoleWarn() {
    jest.spyOn(console, 'warn').mockImplementation();
  }

  function expectConsoleWarn(path) {
    // eslint-disable-next-line no-console
    expect(console.warn).toHaveBeenCalledWith(expect.stringMatching(path), expect.any(Error));
  }

  beforeEach(() => {
    window.gl = {};
    mock = new MockAdapter(axios);
    mock.onGet('/non-startup').reply(200, AXIOS_RESPONSE);
    mock.onGet('/startup').reply(200, AXIOS_RESPONSE);
    mock.onGet('/startup-failing').reply(200, AXIOS_RESPONSE);
  });

  afterEach(() => {
    delete window.gl;
    axios.interceptors.request.handlers = [];
    mock.restore();
  });

  it('if no startupCalls are registered: does not register a request interceptor', () => {
    setupAxiosStartupCalls(axios);

    expect(axios.interceptors.request.handlers.length).toBe(0);
  });

  describe('if startupCalls are registered', () => {
    beforeEach(() => {
      window.gl.startup_calls = {
        '/startup': {
          fetchCall: mockFetchCall(200),
        },
        '/startup-failing': {
          fetchCall: mockFetchCall(400),
        },
      };
      setupAxiosStartupCalls(axios);
    });

    it('registers a request interceptor', () => {
      expect(axios.interceptors.request.handlers.length).toBe(1);
    });

    it('detaches the request interceptor if every startup call has been made', async () => {
      expect(axios.interceptors.request.handlers[0]).not.toBeNull();

      await axios.get('/startup');
      mockConsoleWarn();
      await axios.get('/startup-failing');

      // Axios sets the interceptor to null
      expect(axios.interceptors.request.handlers[0]).toBeNull();
    });

    it('delegates to startup calls if URL is registered and call is successful', async () => {
      const { headers, data, status, statusText } = await axios.get('/startup');

      expect(headers).toEqual({ 'content-type': 'application/json' });
      expect(status).toBe(200);
      expect(statusText).toBe('MOCK-FETCH 200');
      expect(data).toEqual(STARTUP_JS_RESPONSE);
      expect(data).not.toEqual(AXIOS_RESPONSE);
    });

    it('delegates to startup calls exactly once', async () => {
      await axios.get('/startup');
      const { data } = await axios.get('/startup');

      expect(data).not.toEqual(STARTUP_JS_RESPONSE);
      expect(data).toEqual(AXIOS_RESPONSE);
    });

    it('does not delegate to startup calls if the call is failing', async () => {
      mockConsoleWarn();
      const { data } = await axios.get('/startup-failing');

      expect(data).not.toEqual(STARTUP_JS_RESPONSE);
      expect(data).toEqual(AXIOS_RESPONSE);
      expectConsoleWarn('/startup-failing');
    });

    it('does not delegate to startup call if URL is not registered', async () => {
      const { data } = await axios.get('/non-startup');

      expect(data).toEqual(AXIOS_RESPONSE);
      expect(data).not.toEqual(STARTUP_JS_RESPONSE);
    });
  });

  describe('startup call', () => {
    let oldGon;

    beforeEach(() => {
      oldGon = window.gon;
      window.gon = { gitlab_url: 'https://example.org/gitlab' };
    });

    afterEach(() => {
      window.gon = oldGon;
    });

    it('removes GitLab Base URL from startup call', async () => {
      window.gl.startup_calls = {
        '/startup': {
          fetchCall: mockFetchCall(200),
        },
      };
      setupAxiosStartupCalls(axios);

      const { data } = await axios.get('https://example.org/gitlab/startup');

      expect(data).toEqual(STARTUP_JS_RESPONSE);
    });

    it('sorts the params in the requested API url', async () => {
      window.gl.startup_calls = {
        '/startup?alpha=true&bravo=true': {
          fetchCall: mockFetchCall(200),
        },
      };
      setupAxiosStartupCalls(axios);

      // Use a full url instead of passing options = { params: { ... } } to axios.get
      // to ensure the params are listed in the specified order.
      const { data } = await axios.get('https://example.org/gitlab/startup?bravo=true&alpha=true');

      expect(data).toEqual(STARTUP_JS_RESPONSE);
    });
  });
});
