Skip to content
This repository has been archived by the owner on Nov 27, 2023. It is now read-only.

Commit

Permalink
feat: allow form encoding of body data
Browse files Browse the repository at this point in the history
  • Loading branch information
KnisterPeter committed Nov 19, 2018
1 parent cde0692 commit 8b7acb1
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 17 deletions.
16 changes: 16 additions & 0 deletions src/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,19 @@ export function FormData(name: string): ParameterDecorator {
});
};
}

export function FormEncoding(target: any, property: string | symbol, parameter: number): void {
if (!target.__pretend_parameter__) {
Object.defineProperty(target, '__pretend_parameter__', {
enumerable: false,
value: {}
});
}
if (!target.__pretend_parameter__[property]) {
target.__pretend_parameter__[property] = [];
}
target.__pretend_parameter__[property].push({
type: 'FormEncoding',
parameter
});
}
37 changes: 24 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export {Get, Post, Put, Delete, Headers, Patch, FormData} from './decorators';
export { Delete, FormData, FormEncoding, Get, Headers, Patch, Post, Put } from './decorators';

export type IPretendDecoder = (response: Response) => Promise<any>;
export type IPretendRequest = { url: string, options: RequestInit };
Expand All @@ -20,17 +20,24 @@ interface Instance {
headers?: {[name: string]: string[]};
};
parameters: {
[method: string]: FormDataParameter[]
[method: string]: FormParameter[]
}
};
}

type FormParameter = FormDataParameter | FormEncodingParameter;

interface FormDataParameter {
type: 'FormData';
name: string;
parameter: number;
}

interface FormEncodingParameter {
type: 'FormEncoding';
parameter: number;
}

function createUrl(url: string, args: any[]): [string, number] {
let i = 0;
return [url
Expand All @@ -42,12 +49,12 @@ function createUrl(url: string, args: any[]): [string, number] {
function createQuery(parameters: object): string {
return Object.keys(parameters)
.reduce((query, name) => {
return `${query}&${name}=${encodeURIComponent(parameters[name])}`;
return `${query}&${encodeURIComponent(name)}=${encodeURIComponent(parameters[name])}`;
}, '')
.replace(/^&/, '?');
}

function filterFormData(args: any[], parameters?: FormDataParameter[]): any[] {
function filterFormData(args: any[], parameters?: FormParameter[]): any[] {
return args.filter((_, index) => {
return !parameters || parameters.every(param => param.parameter !== index);
});
Expand Down Expand Up @@ -80,17 +87,21 @@ function chainFactory(interceptors: Interceptor[]): (request: IPretendRequest) =
}

function execute(instance: Instance, method: string, tmpl: string, args: any[], sendBody: boolean,
appendQuery: boolean, parameters?: FormDataParameter[]): Promise<any> {
appendQuery: boolean, parameters?: FormParameter[]): Promise<any> {
const createBody = () => {
if (parameters) {
const formData = new FormData();
parameters.forEach(parameter => {
const value = args[parameter.parameter];
if (value) {
formData.append(parameter.name, value, value.name);
}
});
return formData;
if (parameters.some(parameter => parameter.type === 'FormData')) {
const formData = new FormData();
parameters.forEach((parameter: FormDataParameter) => {
const value = args[parameter.parameter];
if (value) {
formData.append(parameter.name, value, value.name);
}
});
return formData;
}
const body = args[appendQuery ? queryOrBodyIndex + 1 : queryOrBodyIndex];
return createQuery(body).substr(1);
}
return sendBody ? JSON.stringify(args[appendQuery ? queryOrBodyIndex + 1 : queryOrBodyIndex]) : undefined;
};
Expand Down
23 changes: 19 additions & 4 deletions test/index-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'isomorphic-fetch';
import 'isomorphic-form-data';
import * as nock from 'nock';
import { Delete, FormData, Get, Headers, Patch, Post, Pretend, Put } from '../src';
import { FormEncoding } from '../src/decorators';

interface Test {
getSimple(): Promise<any>;
Expand All @@ -14,6 +15,7 @@ interface Test {
postWithFormData(_formData: any): Promise<any>;
postWithFormDataAndQuery(_query: any, _formData: any): Promise<any>;
postWithEmptyFormDataAndQuery(_query: any, _formData: any): Promise<any>;
postWithUrlEncodedBody(_query: any, _body: any): Promise<any>;
put(): Promise<any>;
putWithQuery(_parameters: any): Promise<any>;
delete(_id: string): Promise<any>;
Expand Down Expand Up @@ -42,6 +44,8 @@ class TestImpl implements Test {
public postWithFormDataAndQuery(_query: any, @FormData('name') _formData: any): any { /* */ }
@Post('/path/withFormData', true)
public postWithEmptyFormDataAndQuery(_query: any, @FormData('name') _formData: any): any { /* */ }
@Post('/path/withUrlEncodedBody', true)
public postWithUrlEncodedBody(_query: any, @FormEncoding _body: any): any { /* */ }
@Put('/path')
public put(): any { /* */ }
@Put('/path', true)
Expand Down Expand Up @@ -175,10 +179,10 @@ test('Pretend should call a post method with FormData and query', () => {
test('Pretend should call a post method with empty FormData and query', () => {
const test = setup();
nock('http://host:port/', {
reqheaders: {
'Content-Type': /^multipart\/form-data/
}
})
reqheaders: {
'Content-Type': /^multipart\/form-data/
}
})
.post('/path/withFormData?query=params', undefined)
.reply(200, mockResponse);
return test.postWithEmptyFormDataAndQuery({ query: 'params' }, undefined)
Expand All @@ -187,6 +191,17 @@ test('Pretend should call a post method with empty FormData and query', () => {
});
});

test('Pretend should call a post method and form-encode the body', () => {
const test = setup();
nock('http://host:port/')
.post('/path/withUrlEncodedBody?query=params', 'p1=d1&p2=a%20b')
.reply(200, mockResponse);
return test.postWithUrlEncodedBody({ query: 'params' }, { p1: 'd1', p2: 'a b' })
.then(response => {
expect(response).toEqual(mockResponse);
});
});

test('Pretend should call a put method', () => {
const test: Test = Pretend.builder().target(TestImpl, 'http://host:port');
nock('http://host:port/').put('/path').reply(200, mockResponse);
Expand Down

0 comments on commit 8b7acb1

Please sign in to comment.