import { HttpClient } from './http-client';

const TEST_URL = 'http://example.com';
const FAKE_RESPONSE = {
  data: 'data',
};

describe('HttpClient', () => {
  let httpClient;

  beforeEach(() => {
    httpClient = new HttpClient();

    // setup and spy on fake fetch API
    spyOn(window, 'fetch').and.returnValue(Promise.resolve(FAKE_RESPONSE));
  });

  it('should create', () => {
    expect(httpClient).toBeTruthy();
  });

  describe('get()', () => {
    let params;

    beforeEach(() => {
      params = {
        url: TEST_URL,
      };
    });

    it('should GET the given url', () => {
      httpClient.get(params);
      expect(window.fetch).toHaveBeenCalledWith(params.url, jasmine.objectContaining({ method: 'GET' }));
    });

    it('should return a promise', (done) => {
      const result = httpClient.get(params);
      result.then((response) => {
        expect(response).toEqual(FAKE_RESPONSE);
        done();
      });
    });
  });

  describe('post()', () => {
    let params;

    beforeEach(() => {
      params = {
        url: TEST_URL,
      };
    });

    it('should POST the given url', () => {
      httpClient.post(params);
      expect(window.fetch).toHaveBeenCalledWith(params.url, jasmine.objectContaining({ method: 'POST' }));
    });

    it('should return a promise', (done) => {
      const result = httpClient.post(params);
      result.then((response) => {
        expect(response).toEqual(FAKE_RESPONSE);
        done();
      });
    });
  });

  describe('Response Interceptors', () => {
    it('can be added', () => {
      const interceptor = () => {};
      expect(httpClient._responseInterceptors.length).toBe(0);
      httpClient.addResponseInterceptor(interceptor);
      expect(httpClient._responseInterceptors.length).toBe(1);
      httpClient.addResponseInterceptor(interceptor);
      expect(httpClient._responseInterceptors.length).toBe(2);
    });

    describe('get called and removed', () => {
      let intercepted1;
      let intercepted2;
      const interceptors = {
        interceptor1: () => intercepted1 = true,
        interceptor2: () => intercepted2 = true,
      };

      beforeEach(() => {
        intercepted1 = false;
        intercepted2 = false;
        spyOn(interceptors, 'interceptor1').and.callThrough();
        spyOn(interceptors, 'interceptor2').and.callThrough();
        httpClient.addResponseInterceptor(interceptors.interceptor1);
        httpClient.addResponseInterceptor(interceptors.interceptor2);
      });

      it('for GET requests', (done) => {
        httpClient.get({ url: TEST_URL }).then(() => {
          expect(intercepted1).toBeTruthy();
          expect(intercepted2).toBeTruthy();
          expect(interceptors.interceptor1).toHaveBeenCalledWith(FAKE_RESPONSE, jasmine.any(Object));
          expect(interceptors.interceptor2).toHaveBeenCalledWith(FAKE_RESPONSE, jasmine.any(Object));
          done();
        });
      });

      it('for POST requests', (done) => {
        httpClient.post({ url: TEST_URL }).then(() => {
          expect(intercepted1).toBeTruthy();
          expect(intercepted2).toBeTruthy();
          expect(interceptors.interceptor1).toHaveBeenCalledWith(FAKE_RESPONSE, jasmine.any(Object));
          expect(interceptors.interceptor2).toHaveBeenCalledWith(FAKE_RESPONSE, jasmine.any(Object));
          done();
        });
      });

      it('can be removed', () => {
        expect(httpClient._responseInterceptors.length).toBe(2);
        httpClient.removeResponseInterceptor(interceptors.interceptor1);
        expect(httpClient._responseInterceptors.length).toBe(1);
        expect(() => {httpClient.removeResponseInterceptor(interceptors.interceptor1);}).toThrow();
        expect(httpClient._responseInterceptors.length).toBe(1);
      });
    });
  });
});
