Setup tests. Add basic GenericRestClient tests

This commit is contained in:
Alexader 2018-03-20 21:33:39 +02:00
Родитель 7b11a60ff0
Коммит 8a36fb9d22
12 изменённых файлов: 536 добавлений и 19 удалений

1
karma.conf.js Normal file
Просмотреть файл

@ -0,0 +1 @@
module.exports = require('./karma/karma.config');

50
karma/karma.config.js Normal file
Просмотреть файл

@ -0,0 +1,50 @@
const webpack = require('./webpack.test');
module.exports = config => (
config.set({
basePath: '',
frameworks: ['jasmine-ajax', 'jasmine'],
reporters: ['spec', 'kjhtml'],
browsers: [
process.env.TRAVIS
? 'ChromeHeadlessNoSandbox'
: 'Chrome',
],
plugins: [
'karma-jasmine-html-reporter',
'karma-sourcemap-loader',
'karma-chrome-launcher',
'karma-spec-reporter',
'karma-jasmine-ajax',
'karma-jasmine',
'karma-webpack',
],
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
flags: ['--no-sandbox'],
}
},
preprocessors: {
'./karma/karma.entry.js': ['webpack', 'sourcemap'],
},
files: [
{ pattern: './karma/karma.entry.js', watched: false },
],
logLevel: config.LOG_INFO,
colors: true,
mime: {
'text/x-typescript': ['ts'],
},
webpack,
webpackMiddleware: {
stats: 'errors-only',
}
})
);

6
karma/karma.entry.js Normal file
Просмотреть файл

@ -0,0 +1,6 @@
const context = require
.context('../test/', true, /\.spec\.ts$/);
context
.keys()
.forEach(context);

30
karma/webpack.test.js Normal file
Просмотреть файл

@ -0,0 +1,30 @@
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const path = require('path');
const ROOT_PATH = path.resolve(__dirname, '..');
const CONFIG_PATH = path.resolve(ROOT_PATH, 'tsconfig.test.json');
module.exports = {
devtool: 'inline-source-map',
mode: 'development',
resolve: {
extensions: ['.js', '.ts']
},
module: {
rules: [{
test: /\.ts$/,
loader: 'ts-loader',
options: {
transpileOnly: true,
configFile: CONFIG_PATH,
context: ROOT_PATH,
},
exclude: /node_modules/
}]
},
plugins: [
new ForkTsCheckerWebpackPlugin({ tsconfig: CONFIG_PATH }),
]
};

Просмотреть файл

@ -4,20 +4,41 @@
"description": "A library of components for accessing RESTful services with javascript/typescript.",
"author": "David de Regt <David.de.Regt@microsoft.com>",
"scripts": {
"prepublish": "tsc",
"prepare": "tsc",
"clean": "rimraf dist",
"build": "npm run tslint && tsc",
"tslint": "tslint --project tsconfig.json -r tslint.json --fix || true"
"tslint": "tslint --project tsconfig.json -r tslint.json --fix || true",
"test": "npm run clean && karma start --singleRun",
"test:watch": "npm run clean && karma start"
},
"dependencies": {
"@types/lodash": "^4.14.62",
"assert": "^1.4.1",
"lodash": "4.x",
"synctasks": "^0.3.0"
"@types/lodash": "4.14.107",
"assert": "1.4.1",
"lodash": "4.17.5",
"synctasks": "0.3.1"
},
"devDependencies": {
"tslint": "5.4.3",
"tslint-microsoft-contrib": "5.0.0",
"typescript": "^2.6.0"
"@types/faker": "4.1.2",
"@types/jasmine": "2.8.6",
"@types/jasmine-ajax": "3.1.37",
"faker": "4.1.0",
"fork-ts-checker-webpack-plugin": "0.4.1",
"jasmine": "3.1.0",
"jasmine-core": "3.1.0",
"karma": "2.0.0",
"karma-chrome-launcher": "2.2.0",
"karma-jasmine": "1.1.1",
"karma-jasmine-ajax": "0.1.13",
"karma-jasmine-html-reporter": "1.0.0",
"karma-sourcemap-loader": "0.3.7",
"karma-spec-reporter": "0.0.32",
"karma-webpack": "4.0.0-beta.0",
"rimraf": "2.6.2",
"ts-loader": "4.1.0",
"tslint": "5.9.1",
"tslint-microsoft-contrib": "5.0.3",
"typescript": "2.8.1",
"webpack": "4.5.0"
},
"repository": {
"type": "git",

Просмотреть файл

@ -69,7 +69,7 @@ export class GenericRestClient {
}
options.augmentHeaders['If-None-Match'] = options.eTag;
}
if (!options.contentType) {
options.contentType = _.isString(options.sendData) ? 'form' : 'json';
}

Просмотреть файл

@ -0,0 +1 @@
// @TODO

Просмотреть файл

@ -0,0 +1,373 @@
import * as faker from 'faker';
import { GenericRestClient, ApiCallOptions } from '../src/GenericRestClient';
import { DETAILED_RESPONSE, REQUEST_OPTIONS } from './helpers';
class RestClient extends GenericRestClient { }
const BASE_URL = faker.internet.url();
const http = new RestClient(BASE_URL);
describe('GenericRestClient', () => {
beforeEach(() => jasmine.Ajax.install());
afterEach(() => jasmine.Ajax.uninstall());
it('performs GET request with performApiGet', () => {
const id = faker.random.uuid();
const statusCode = 200;
const onSuccess = jasmine.createSpy('onSuccess');
const method = 'GET';
const body = {
title: faker.name.title(),
text: faker.lorem.text(),
id,
};
const path = `/get/${id}`;
const url = BASE_URL + path;
http.performApiGet(path)
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(request.status).toEqual(statusCode);
expect(onSuccess).toHaveBeenCalledWith(body);
});
it('performs GET request with performApiGetDetailed', () => {
const id = faker.random.uuid();
const statusCode = 200;
const onSuccess = jasmine.createSpy('onSuccess');
const method = 'GET';
const body = {
title: faker.name.title(),
text: faker.lorem.text(),
id,
};
const path = `/get/${id}`;
const url = BASE_URL + path;
const response = {
...DETAILED_RESPONSE,
requestHeaders: { 'Accept': 'application/json' },
statusCode,
method,
body,
url
};
http.performApiGetDetailed(path, { contentType: 'json' })
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(onSuccess).toHaveBeenCalledWith(response);
});
it('performs POST request with performApiPost', () => {
const statusCode = 201;
const onSuccess = jasmine.createSpy('onSuccess');
const method = 'POST';
const sendData = {
title: faker.name.title(),
text: faker.lorem.text(),
};
const body = { ...sendData, id: faker.random.uuid() };
const path = '/post';
const url = BASE_URL + path;
http.performApiPost(path, sendData, { contentType: 'json' })
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(request.status).toEqual(statusCode);
expect(request.data() as any).toEqual(sendData);
expect(onSuccess).toHaveBeenCalledWith(body);
});
it('performs POST request with performApiPostDetailed', () => {
const statusCode = 201;
const onSuccess = jasmine.createSpy('onSuccess');
const sendData = {
title: faker.name.title(),
text: faker.lorem.text(),
};
const method = 'POST';
const body = { ...sendData, id: faker.random.uuid() };
const path = '/post';
const url = BASE_URL + path;
const response = {
...DETAILED_RESPONSE,
requestOptions: { ...REQUEST_OPTIONS, sendData },
statusCode,
method,
body,
url
};
http.performApiPostDetailed(path, sendData, { contentType: 'json' })
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(request.status).toEqual(statusCode);
expect(onSuccess).toHaveBeenCalledWith(response);
});
it('performs PUT request with performApiPut', () => {
const id = faker.random.uuid();
const statusCode = 200;
const onSuccess = jasmine.createSpy('onSuccess');
const sendData = { title: faker.name.title() };
const method = 'PUT';
const body = { ...sendData, id };
const path = '/put/' + id;
const url = BASE_URL + path;
http.performApiPut(path, sendData, { contentType: 'json' })
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(request.status).toEqual(statusCode);
expect(request.data() as any).toEqual(sendData);
expect(onSuccess).toHaveBeenCalledWith(body);
});
it('performs PUT request with performApiPutDetailed', () => {
const id = faker.random.uuid();
const statusCode = 200;
const onSuccess = jasmine.createSpy('onSuccess');
const sendData = { title: faker.name.title() };
const method = 'PUT';
const body = { ...sendData, id: faker.random.uuid() };
const path = `/patch/${id}`;
const url = BASE_URL + path;
const response = {
...DETAILED_RESPONSE,
requestOptions: { ...REQUEST_OPTIONS, sendData },
statusCode,
method,
body,
url
};
http.performApiPutDetailed(path, sendData, { contentType: 'json' })
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(request.status).toEqual(statusCode);
expect(request.data() as any).toEqual(sendData);
expect(onSuccess).toHaveBeenCalledWith(response);
});
it('performs PATCH request with performApiPatch', () => {
const id = faker.random.uuid();
const statusCode = 200;
const onSuccess = jasmine.createSpy('onSuccess');
const method = 'PATCH';
const sendData = {
title: faker.name.title(),
text: faker.lorem.text(),
};
const body = { ...sendData, text: faker.lorem.text(), id };
const path = '/patch' + id;
const url = BASE_URL + path;
http.performApiPatch(path, sendData, { contentType: 'json' })
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(request.status).toEqual(statusCode);
expect(request.data() as any).toEqual(sendData);
expect(onSuccess).toHaveBeenCalledWith(body);
});
it('performs PATCH request with performApiPatchDetailed', () => {
const id = faker.random.uuid();
const statusCode = 200;
const onSuccess = jasmine.createSpy('onSuccess');
const sendData = {
title: faker.name.title(),
text: faker.lorem.text(),
};
const method = 'PATCH';
const body = { ...sendData, id: faker.random.uuid() };
const path = `/patch/${id}`;
const url = BASE_URL + path;
const response = {
...DETAILED_RESPONSE,
requestOptions: { ...REQUEST_OPTIONS, sendData },
statusCode,
method,
body,
url
};
http.performApiPatchDetailed(path, sendData, { contentType: 'json' })
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(request.status).toEqual(statusCode);
expect(request.data() as any).toEqual(sendData);
expect(onSuccess).toHaveBeenCalledWith(response);
});
it('performs DELETE request with performApiDelete', () => {
const statusCode = 200;
const onSuccess = jasmine.createSpy('onSuccess');
const method = 'DELETE';
const body = {};
const path = `/delete/${faker.random.uuid()}`;
const url = BASE_URL + path;
http.performApiDelete(path)
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(onSuccess).toHaveBeenCalledWith(body);
});
it('performs DELETE request with performApiDeleteDetailed', () => {
const id = faker.random.uuid();
const statusCode = 200;
const onSuccess = jasmine.createSpy('onSuccess');
const sendData = { id };
const method = 'DELETE';
const body = {};
const path = `/delete/${id}`;
const url = BASE_URL + path;
const response = {
...DETAILED_RESPONSE,
requestOptions: { ...REQUEST_OPTIONS, sendData },
statusCode,
method,
body,
url,
};
http.performApiDeleteDetailed(path, sendData, { contentType: 'json' })
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(request.data() as any).toEqual(sendData);
expect(onSuccess).toHaveBeenCalledWith(response);
});
it('performs request with custum headers', () => {
const headers = {
'Authorization': `Barrier ${faker.random.uuid()}`,
};
class Http extends GenericRestClient {
protected _getHeaders(options: ApiCallOptions): { [header: string]: string } {
return headers;
}
}
const statusCode = 200;
const onSuccess = jasmine.createSpy('onSuccess');
const http = new Http(BASE_URL);
const path = '/auth';
http.performApiGet(path)
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({ status: statusCode });
expect(request.requestHeaders['Authorization']).toEqual(headers['Authorization']);
expect(onSuccess).toHaveBeenCalled();
});
it('overrides response', () => {
class Http extends GenericRestClient {
protected _processSuccessResponse<T>(resp: any): void {
resp.body = resp.body.map((str: string) => str.trim());
}
}
const statusCode = 200;
const onSuccess = jasmine.createSpy('onSuccess');
const method = 'GET';
const http = new Http(BASE_URL);
const path = '/get';
const body = [' x ', ' y ', ' z '];
const url = BASE_URL + path;
http.performApiGet<string[]>(path)
.then(onSuccess);
const request = jasmine.Ajax.requests.mostRecent();
request.respondWith({
responseText: JSON.stringify(body),
status: statusCode,
});
expect(request.url).toEqual(url);
expect(request.method).toEqual(method);
expect(request.status).toEqual(statusCode);
expect(onSuccess).toHaveBeenCalledWith(body.map((str: string) => str.trim()));
});
});

Просмотреть файл

@ -0,0 +1 @@
// @TODO

24
test/helpers.ts Normal file
Просмотреть файл

@ -0,0 +1,24 @@
export const REQUEST_OPTIONS = {
excludeEndpointUrl: false,
withCredentials: false,
contentType: 'json',
priority: 2,
retries: 0,
};
export const REQUEST_HEADERS = {
'Content-Type': 'application/json',
'Accept': 'application/json',
};
export const DETAILED_RESPONSE = {
requestOptions: REQUEST_OPTIONS,
requestHeaders: REQUEST_HEADERS,
statusCode: 200,
statusText: undefined,
headers: {
'content-type': 'application/json',
},
body: '',
url: '',
};

Просмотреть файл

@ -1,20 +1,20 @@
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"declaration": true,
"noResolve": false,
"module": "commonjs",
"target": "es5",
"outDir": "dist/",
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true
"outDir": "dist",
},
"filesGlob": [
"src/**/*{ts,tsx}"
"include": [
"src/**/*"
],
"exclude": [

10
tsconfig.test.json Normal file
Просмотреть файл

@ -0,0 +1,10 @@
{
"extends": "./tsconfig",
"compilerOptions": {
"sourceMap": true
},
"include": [
"src/**/*",
"test/**/*"
]
}