Skip to content

Commit

Permalink
Merge pull request #6 from coveo/ADUI-4773/unit-tests-hackaton-files
Browse files Browse the repository at this point in the history
Improve test coverage
  • Loading branch information
gdostie authored Sep 23, 2019
2 parents 3ea34d1 + 9b9f7d3 commit 086379d
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 94 deletions.
15 changes: 8 additions & 7 deletions src/APICore.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {IRestResponse} from './handlers/HandlerConstants';
import {Handlers} from './handlers/Handlers';

const removeEmptyEntries = (obj) =>
Object.keys(obj).reduce((memo, key) => {
function removeEmptyEntries(obj) {
return Object.keys(obj).reduce((memo, key) => {
const val = obj[key];
if (val && typeof val === 'object') {
memo[key] = removeEmptyEntries(obj);
Expand All @@ -11,18 +11,19 @@ const removeEmptyEntries = (obj) =>
}
return memo;
}, {});
}

function convertToQueryString(parameters: any) {
return parameters ? `?${new URLSearchParams(Object.entries(removeEmptyEntries(parameters))).toString()}` : '';
}

export default class API {
static orgPlaceholder = '{organizationName}';

constructor(private host: string, private orgId: string, private accessTokenRetriever: () => string) {}

static toQueryString(parameters: any) {
return parameters ? `?${new URLSearchParams(Object.entries(removeEmptyEntries(parameters))).toString()}` : '';
}

async get<T>(url: string, queryParams?: any, args: RequestInit = {method: 'get'}): Promise<IRestResponse<T>> {
return await this.request<T>(url + API.toQueryString(queryParams), args);
return await this.request<T>(url + convertToQueryString(queryParams), args);
}

async post<T>(
Expand Down
33 changes: 15 additions & 18 deletions src/CoveoPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,25 @@ export interface CoveoPlatformOptions {
host?: string;
}

const Environments = {
local: 'local',
dev: 'development',
staging: 'staging',
prod: 'production',
hipaa: 'hipaa',
};

const Hosts = {
[Environments.dev]: 'https://platformdev.cloud.coveo.com',
[Environments.staging]: 'https://platformqa.cloud.coveo.com',
[Environments.prod]: 'https://platform.cloud.coveo.com',
[Environments.hipaa]: 'https://platformhipaa.cloud.coveo.com',
};

export default class CoveoPlatform extends CoveoPlatformResources {
static Environments = {
dev: 'development',
staging: 'staging',
prod: 'production',
hipaa: 'hipaa',
};
static Hosts = {
[CoveoPlatform.Environments.dev]: 'https://platformdev.cloud.coveo.com',
[CoveoPlatform.Environments.staging]: 'https://platformqa.cloud.coveo.com',
[CoveoPlatform.Environments.prod]: 'https://platform.cloud.coveo.com',
[CoveoPlatform.Environments.hipaa]: 'https://platformhipaa.cloud.coveo.com',
};
static defaultOptions: Partial<CoveoPlatformOptions> = {
environment: Environments.prod,
environment: CoveoPlatform.Environments.prod,
};

private options: CoveoPlatformOptions;
private tokenInfo: any; // define a better type
private tokenInfo: Record<string, any>; // define a better type
private readonly API: API;

constructor(options: CoveoPlatformOptions) {
Expand All @@ -40,7 +37,7 @@ export default class CoveoPlatform extends CoveoPlatformResources {
...options,
};

const host = this.options.host || Hosts[this.options.environment];
const host = this.options.host || CoveoPlatform.Hosts[this.options.environment];
if (!host) {
throw new Error(`CoveoPlatform's host is undefined.`);
}
Expand Down
5 changes: 5 additions & 0 deletions src/resources/BaseInterfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface PageModel<T> {
items: T[];
totalEntries: number;
totalPages: number;
}
45 changes: 5 additions & 40 deletions src/resources/Catalogs/Catalog.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,6 @@
import API from '../../APICore';

export interface CatalogsListOptions {
page?: number;
pageSize?: number;
}

interface PageModel<T> {
items: T[];
totalEntries: number;
totalPages: number;
}

interface CatalogModel {
availability?: AvailabilityHierarchyModel;
description?: string;
id?: string;
name?: string;
product?: ProductHierarchyModel;
variant?: VariantHierarchyModel;
}

interface VariantHierarchyModel {
fields: string[];
idField: string;
objectType: string;
}

interface ProductHierarchyModel {
idField?: string;
objectType?: string;
}

interface AvailabilityHierarchyModel {
availableSkusField: string;
fields: string[];
objectType: string;
}
import {PageModel} from '../BaseInterfaces';
import {CatalogModel, CatalogsListOptions, NewCatalogModel} from './Interfaces';

export default class Catalog {
static baseUrl = `/rest/organizations/${API.orgPlaceholder}/catalogs`;
Expand All @@ -46,7 +11,7 @@ export default class Catalog {
return this.api.get<PageModel<CatalogModel>>(Catalog.baseUrl, options);
}

create(catalog: CatalogModel) {
create(catalog: NewCatalogModel) {
return this.api.post<CatalogModel>(Catalog.baseUrl, catalog);
}

Expand All @@ -58,7 +23,7 @@ export default class Catalog {
return this.api.get<CatalogModel>(`${Catalog.baseUrl}/${catalogId}`);
}

update(catalogId: string, catalog: CatalogModel) {
return this.api.put<CatalogModel>(`${Catalog.baseUrl}/${catalogId}`, catalog);
update(catalog: CatalogModel) {
return this.api.put<CatalogModel>(`${Catalog.baseUrl}/${catalog.id}`, catalog);
}
}
32 changes: 32 additions & 0 deletions src/resources/Catalogs/Interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export interface CatalogsListOptions {
page?: number;
pageSize?: number;
}

export interface CatalogModel {
id: string;
name: string;
product: ProductHierarchyModel;
availability?: AvailabilityHierarchyModel;
description?: string;
variant?: VariantHierarchyModel;
}

export type NewCatalogModel = Omit<CatalogModel, 'id'>;

export interface VariantHierarchyModel {
fields: string[];
idField: string;
objectType: string;
}

export interface ProductHierarchyModel {
idField: string;
objectType: string;
}

export interface AvailabilityHierarchyModel {
availableSkusField: string;
fields: string[];
objectType: string;
}
80 changes: 80 additions & 0 deletions src/resources/Catalogs/tests/Catalog.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import API from '../../../APICore';
import Catalog from '../Catalog';
import {CatalogModel, CatalogsListOptions, NewCatalogModel} from '../Interfaces';

jest.mock('../../../APICore');

const APIMock: jest.Mock<API> = API as any;

describe('Catalog', () => {
let catalog: Catalog;
const api = new APIMock() as jest.Mocked<API>;

beforeEach(() => {
jest.clearAllMocks();
catalog = new Catalog(api);
});

describe('list', () => {
it('should make a GET call to the catalogs base url', () => {
const options: CatalogsListOptions = {
page: 2,
pageSize: 10,
};
catalog.list(options);
expect(api.get).toHaveBeenCalledTimes(1);
expect(api.get).toHaveBeenCalledWith(Catalog.baseUrl, options);
});
});

describe('create', () => {
it('should make a POST call to the catalogs base url', () => {
const catalogModel: NewCatalogModel = {
name: 'New catalog',
product: {
idField: '@uri',
objectType: 'product',
},
};

catalog.create(catalogModel);
expect(api.post).toHaveBeenCalledTimes(1);
expect(api.post).toHaveBeenCalledWith(Catalog.baseUrl, catalogModel);
});
});

describe('delete', () => {
it('should make a DELETE call to the specific catalog url', () => {
const catalogToDeleteId = 'catalog-to-be-deleted';
catalog.delete(catalogToDeleteId);
expect(api.delete).toHaveBeenCalledTimes(1);
expect(api.delete).toHaveBeenCalledWith(`${Catalog.baseUrl}/${catalogToDeleteId}`);
});
});

describe('get', () => {
it('should make a GET call to the specific catalog url', () => {
const catalogToGetId = 'catalog-to-be-fetched';
catalog.get(catalogToGetId);
expect(api.get).toHaveBeenCalledTimes(1);
expect(api.get).toHaveBeenCalledWith(`${Catalog.baseUrl}/${catalogToGetId}`);
});
});

describe('update', () => {
it('should make a PUT call to the specific catalog url', () => {
const catalogModel: CatalogModel = {
id: 'catalog-to-update-id',
name: 'Catalog to be updated',
product: {
idField: '@uri',
objectType: 'product',
},
};

catalog.update(catalogModel);
expect(api.put).toHaveBeenCalledTimes(1);
expect(api.put).toHaveBeenCalledWith(`${Catalog.baseUrl}/${catalogModel.id}`, catalogModel);
});
});
});
2 changes: 1 addition & 1 deletion src/resources/Resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class CoveoPlatformResources {

const registerAll = (platform: CoveoPlatform, api: API) => {
resourcesMap.forEach(({key, resource}) => {
CoveoPlatform.prototype[key] = new resource(api);
platform[key] = new resource(api);
});
};

Expand Down
23 changes: 23 additions & 0 deletions src/resources/tests/Resources.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import API from '../../APICore';
import CoveoPlatform from '../../CoveoPlatform';
import Catalog from '../Catalogs/Catalog';
import {Resources} from '../Resources';

describe('Resources', () => {
describe('registerAll', () => {
let platform: CoveoPlatform;
let api: API;

beforeEach(() => {
platform = {} as CoveoPlatform;
api = {} as API;
});

it('should register the catalog resource on the platform instance', () => {
Resources.registerAll(platform, api);

expect(platform.catalog).toBeDefined();
expect(platform.catalog).toBeInstanceOf(Catalog);
});
});
});
Loading

0 comments on commit 086379d

Please sign in to comment.