Manual Reference Source Test

test/unit/services.location.js

import test from 'ava';
import nock from 'nock';
import G_TIMEZONES from './data/timezones';
import {
  client,
  clinical6,
  locationService,
  Location,
  Timezone,
} from '../../src';

test.before('start server', (t) => {
  t.context.storage = client.storageUtility;
  client.apiBaseUrl = 'https://somesite.Clinical6.com';
  client.authToken = 'valid_token';

  t.context.getResponseAll = {
    data: [
      {
        id: '1',
        type: 'locations',
        attributes: {
          country: 'USA',
          state: 'CA',
          city: 'San Diego',
          latitude: '32.8563846',
          longitude: '-117.2029363',
          title: 'Location Title',
          zip_code: null,
          address_line_1: 'Main',
          address_line_2: null,
          address_line_3: null
        }
      },
      {
        id: '2',
        type: 'locations',
        attributes: {
          country: 'USA',
          state: 'CA',
          city: 'San Diego',
          latitude: '32.8563846',
          longitude: '-117.2029363',
          title: 'Location Title',
          zip_code: null,
          address_line_1: 'Main',
          address_line_2: null,
          address_line_3: null
        }
      },
      {
        id: '3',
        type: 'locations',
        attributes: {
          country: 'USA',
          state: 'CA',
          city: 'San Diego',
          latitude: '32.8563846',
          longitude: '-117.2029363',
          title: 'Location Title',
          zip_code: null,
          address_line_1: 'Main',
          address_line_2: null,
          address_line_3: null
        }
      }
    ]
  };

  t.context.getResponseId = {
    data: {
      id: '4',
      type: 'locations',
      attributes: {
        country: 'USA',
        state: 'CA',
        city: 'San Diego',
        latitude: '32.8563846',
        longitude: '-117.2029363',
        title: 'Location Title',
        zip_code: null,
        address_line_1: 'Main',
        address_line_2: null,
        address_line_3: null
      }
    }
  };

  t.context.getResponseAllTimezones = G_TIMEZONES;

  t.context.postResponse = {
    data: {
      id: '106',
      type: 'locations',
      attributes: {
        country: 'USA',
        state: 'CA',
        city: 'Poway',
        latitude: '12.1223',
        longitude: '14.1221',
        title: '12345 Cherry Lane, Poway, CA 92064',
        zip_code: '92064',
        address_line_1: '12345 Cherry Lane',
        address_line_2: 'Unit #59',
        address_line_3: null
      }
    }
  };

  t.context.patchResponse = {
    data: {
      id: '106',
      type: 'locations',
      attributes: {
        country: 'USA',
        state: 'CA',
        city: 'Poway',
        latitude: '12.1223',
        longitude: '14.1221',
        title: '12345 Cherry Lane, Poway, CA 92064',
        zip_code: '92064',
        address_line_1: '12345 Cherry Lane',
        address_line_2: 'Unit #59',
        address_line_3: null
      }
    }
  };
});

test.beforeEach((t) => {
  client.cache = 'never';
  client.authToken = 'valid_token';

  // client.location = new Location({});

  // client.timezone = new Timezone({});
  t.context.locationJsonApi = {
    id: 106,
    type: 'locations',
    attributes: {
      country: 'USA',
      state: 'CA',
      city: 'Poway',
      latitude: '12.1223',
      longitude: '14.1221',
      title: '12345 Cherry Lane, Poway, CA 92064',
      zip_code: '92064',
      address_line_1: '12345 Cherry Lane',
      address_line_2: 'Unit #59',
      address_line_3: null
    }
  };

  t.context.locationJson = {
    id: 106,
    type: 'locations',
    country: 'USA',
    state: 'CA',
    city: 'Poway',
    latitude: '12.1223',
    longitude: '14.1221',
    title: '12345 Cherry Lane, Poway, CA 92064',
    zip_code: '92064',
    address_line_1: '12345 Cherry Lane',
    address_line_2: 'Unit #59',
    address_line_3: null
  };

  t.context.location = new Location(t.context.locationJsonApi);
});

/**
 * @test {Location}
 */
test('[unit] Location should handle location data with a normal json format', (t) => {
  const location = new Location(t.context.locationJsonApi);
  t.is(location.id, 106);
  t.is(location.type, 'locations');
  t.is(location.country, 'USA');
  t.is(location.state, 'CA');
  t.is(location.city, 'Poway');
  t.is(location.latitude, '12.1223');
  t.is(location.longitude, '14.1221');
  t.is(location.title, '12345 Cherry Lane, Poway, CA 92064');
  t.is(location.zipCode, '92064');
  t.is(location.addressLine1, '12345 Cherry Lane');
  t.is(location.addressLine2, 'Unit #59');
  t.is(location.addressLine3, null);
});

/**
 * @test {Location}
 */
test('[unit] Location should handle location data with json api format', (t) => {
  const { locationJsonApi } = t.context;
  const location = new Location({ data: locationJsonApi });
  t.is(location.id, 106);
  t.is(location.type, 'locations');
  t.is(location.country, 'USA');
  t.is(location.state, 'CA');
  t.is(location.city, 'Poway');
  t.is(location.latitude, '12.1223');
  t.is(location.longitude, '14.1221');
  t.is(location.title, '12345 Cherry Lane, Poway, CA 92064');
  t.is(location.zipCode, '92064');
  t.is(location.addressLine1, '12345 Cherry Lane');
  t.is(location.addressLine2, 'Unit #59');
  t.is(location.addressLine3, null);
});


// LocationService.delete method
/**
 * @test {LocationService.delete}
 */
test('[unit] LocationService.delete should throw errors for invalid parameters', async (t) => {
  const undefinedError = 'LocationService.delete error: location does not have id';
  await t.throwsAsync(clinical6.delete(new Location()), undefinedError);
});

/**
 * @test {LocationService.delete}
 */
test('[unit] LocationService.delete should make a properly formatted delete request and response', async (t) => {
  const { location } = t.context;
  location.id = 5;
  let request = {};
  nock(client.apiBaseUrl).delete(/\/v3\/locations\/([0-9]*)$/).reply(function () {
    request = this.req;
    return [200, ''];
  });
  const response = await clinical6.delete(location);
  t.is(request.path, `/v3/locations/${location.id}`);
  t.is(request.headers.accept, 'application/json');
  t.is(request.headers['content-type'], 'application/json');
  t.is(request.headers.authorization, 'Token token=valid_token');
  t.falsy(response);
});

/**
 * @test {LocationService.delete}
 */
test('[unit] LocationService.delete should remove the element from local storage', async (t) => {
  const { location, storage } = t.context;
  location.id = 5;
  nock(client.apiBaseUrl).delete(/\/v3\/locations\/([0-9]*)$/).reply(200, '');
  await storage.set('locations', location.toJSON(), { id: location.id });
  await clinical6.delete(location);
  t.is(storage.has('locations', { id: location.id }), false);
});


// LocationService.get method
/**
 * @test {LocationService.get}
 */
test.serial('[unit] LocationService.get should throw an error when there is no authToken', async (t) => {
  client.authToken = undefined;
  const expectedError = 'LocationService.get error: requires authToken';
  await t.throwsAsync(locationService.get(Location), expectedError);
});

/**
 * @test {LocationService.get}
 */
test('[unit] LocationService.get should make a properly formatted get request and response without an id', async (t) => {
  const { getResponseAll } = t.context;
  let request = {};
  nock(client.apiBaseUrl).get('/v3/locations').reply(function () {
    request = this.req;
    return [200, getResponseAll];
  });
  const response = await locationService.get(Location);
  t.is(request.path, `/v3/locations`);
  t.is(request.headers.accept, 'application/json');
  t.is(request.headers['content-type'], 'application/json');
  t.is(request.headers.authorization, 'Token token=valid_token');
  t.truthy(response);
  t.is(Object.keys(response).length, 3);
});

/**
 * @test {LocationService.get}
 */
test('[unit] LocationService.get should receive a valid response for a get request with an id', async (t) => {
  const { getResponseId } = t.context;
  nock(client.apiBaseUrl).get('/v3/locations/4').reply(200, getResponseId);
  const response = await locationService.get(new Location({ id: 4 }));
  t.truthy(response);
  t.is(response.id, 4);
  t.is(response.type, 'locations');
  t.is(response.country, 'USA');
  t.is(response.state, 'CA');
  t.is(response.city, 'San Diego');
  t.is(response.latitude, '32.8563846');
  t.is(response.longitude, '-117.2029363');
  t.is(response.title, 'Location Title');
  t.is(response.zipCode, null);
  t.is(response.addressLine1, 'Main');
  t.is(response.addressLine2, null);
  t.is(response.addressLine3, null);
});


// LocationService.insert method
/**
 * @test {Location.insert}
 */
test('[unit] Location.insert should successfully insert a location with a location object', async (t) => {
  const { postResponse } = t.context;
  const json = JSON.parse(JSON.stringify(t.context.locationJsonApi));
  const location = new Location({ data: json });

  let request = {};
  nock(client.apiBaseUrl).post(`/v3/locations`).reply(function (uri, requestBody) {
    request = this.req;
    request.requestBody = requestBody;
    return [201, postResponse];
  });

  const response = await clinical6.insert(location);
  t.is(request.path, `/v3/locations`);
  t.is(request.headers.accept, 'application/json');
  t.deepEqual(request.requestBody, { data: json });
  t.is(request.headers['content-type'], 'application/json');
  t.is(request.headers.authorization, 'Token token=valid_token');
  t.is(response.id, 106);
  t.is(response.type, 'locations');
  t.is(response.country, 'USA');
  t.is(response.state, 'CA');
  t.is(response.city, 'Poway');
  t.is(response.latitude, '12.1223');
  t.is(response.longitude, '14.1221');
  t.is(response.title, '12345 Cherry Lane, Poway, CA 92064');
  t.is(response.zipCode, '92064');
  t.is(response.addressLine1, '12345 Cherry Lane');
  t.is(response.addressLine2, 'Unit #59');
  t.is(response.addressLine3, null);
});

// LocationService.getTimezones method
/**
 * @test {LocationService.getTimezones}
 */
test('[unit] LocationService.getTimezones should exist', (t) => {
  t.truthy(locationService.getTimezones);
});

/**
 * @test {LocationService.getTimezones}
 */
test.serial('[unit] LocationService.getTimezones should throw an error when there is no authToken', (t) => {
  client.authToken = undefined;
  const expectedError = 'LocationService.getTimezones error: requires authToken';
  t.throws(() => locationService.getTimezones(), expectedError);
});

/**
 * @test {LocationService.getTimezones}
 */
test('[unit] LocationService.getTimezones should return a promise', async (t) => {
  t.truthy(await locationService.getTimezones().then);
});

// /**
//  * @test {LocationService.getTimezones}
//  */
// test('[unit] LocationService.getTimezones should make a properly formatted get request', async (t) => {
//   await locationService.getTimezones();
//   const request = t.context.server.requests[0];
//   t.is(request.method, 'GET');
//   t.is(request.url, `${client.apiBaseUrl}/v3/timezones`);
//   t.is(request.requestHeaders.Accept, 'application/json');
//   t.is(request.requestHeaders['Content-Type'], 'application/json;charset=utf-8');
//   t.is(request.requestHeaders.Authorization, 'Token token=valid_token');
// });

/**
 * @test {LocationService.getTimezones}
 */
test('[unit] LocationService.getTimezones should receive a valid response for a get request without an id', async (t) => {
  const { getResponseAllTimezones } = t.context;
  let request = {};
  nock(client.apiBaseUrl).get('/v3/timezones').reply(function () {
    request = this.req;
    return [200, getResponseAllTimezones];
  });
  const response = await locationService.getTimezones();
  t.truthy(response);
  t.is(request.path, `/v3/timezones`);
  t.is(request.headers.accept, 'application/json');
  t.is(request.headers['content-type'], 'application/json');
  t.is(request.headers.authorization, 'Token token=valid_token');
  t.truthy(response);
  t.is(Object.keys(response).length, 424);
  t.true(response[0] instanceof Timezone);
  t.is(response[0].id, 'pacific/pago_pago__-11.0');
  t.is(response[0].type, 'timezones');
  t.is(response[0].name, 'Pacific/Pago_Pago');
  t.is(response[0].offset, -11);
  t.is(response[0].region, 'Pacific');
});


// LocationService.update method
/**
 * @test {Location.update}
 */
test('[unit] Location.update should successfully update a location with a location object', async (t) => {
  const { patchResponse } = t.context;
  const json = JSON.parse(JSON.stringify(t.context.locationJsonApi));
  const location = new Location({ data: json });
  location.id = 106;

  let request = {};
  nock(client.apiBaseUrl).patch(`/v3/locations/${location.id}`).reply(function (uri, requestBody) {
    request = this.req;
    request.requestBody = requestBody;
    return [200, patchResponse];
  });

  const response = await clinical6.update(location);
  t.is(request.path, `/v3/locations/${location.id}`);
  t.is(request.headers.accept, 'application/json');
  t.deepEqual(request.requestBody, { data: json });
  t.is(request.headers['content-type'], 'application/json');
  t.is(request.headers.authorization, 'Token token=valid_token');
  t.is(response.id, 106);
  t.is(response.type, 'locations');
  t.is(response.country, 'USA');
  t.is(response.state, 'CA');
  t.is(response.city, 'Poway');
  t.is(response.latitude, '12.1223');
  t.is(response.longitude, '14.1221');
  t.is(response.title, '12345 Cherry Lane, Poway, CA 92064');
  t.is(response.zipCode, '92064');
  t.is(response.addressLine1, '12345 Cherry Lane');
  t.is(response.addressLine2, 'Unit #59');
  t.is(response.addressLine3, null);
});

// LocationService.update method with reason for changes
/**
 * @test {Location.update}
 */
test('[unit] Location.update should successfully update a location with reason for changes', async (t) => {
  const location = new Location();
  location.id = 106;
  location.country = 'United Kingdom';
  const meta = {
    reason_for_changes: {
      country: 'Spelling Error'
    }
  };
  const requestPayload = {
    data: {
      id: 106,
      type: 'locations',
      attributes: {
        country: 'United Kingdom',
      },
    },
    meta,
  };

  let request = {};
  nock(client.apiBaseUrl).patch(/\/v3\/locations\/([0-9]*)$/, (body) => {
    request = body;
    return requestPayload;
  }).reply(200, {
    data: {
      id: 106,
      type: 'locations',
      attributes: {
        country: 'United Kingdom',
      },
    },
    meta: {
      reason_for_changes: {
        country: 'Spelling Error',
      },
    },
  });

  const response = await clinical6.update(location, { cacheMode: 'networkOnly', meta });

  t.deepEqual(request, requestPayload);
  t.is(response.id, 106);
  t.is(response.type, 'locations');
  t.is(response.country, 'United Kingdom');
});