- Added e2e test

- Cleanup of new export client logic
This commit is contained in:
Ferdi Koomen 2022-01-25 15:32:20 +01:00
parent 5f58982210
commit b2d8096031
16 changed files with 1007 additions and 301 deletions

View File

@ -25,6 +25,11 @@ const config: Config.InitialOptions = {
'<rootDir>/test/e2e/v3.node.spec.ts',
'<rootDir>/test/e2e/v3.axios.spec.ts',
'<rootDir>/test/e2e/v3.babel.spec.ts',
'<rootDir>/test/e2e/client.fetch.spec.ts',
'<rootDir>/test/e2e/client.xhr.spec.ts',
'<rootDir>/test/e2e/client.node.spec.ts',
'<rootDir>/test/e2e/client.axios.spec.ts',
'<rootDir>/test/e2e/client.babel.spec.ts',
],
},
],

View File

@ -26,6 +26,6 @@ describe('getServices', () => {
});
expect(services).toHaveLength(1);
expect(services[0].name).toEqual('');
expect(services[0].name).toEqual('Default');
});
});

View File

@ -26,6 +26,6 @@ describe('getServices', () => {
});
expect(services).toHaveLength(1);
expect(services[0].name).toEqual('');
expect(services[0].name).toEqual('Default');
});
});

View File

@ -20,7 +20,7 @@ export class {{{clientName}}} {
private readonly request: BaseHttpRequest;
constructor(config?: OpenAPIConfig, HttpRequest: HttpRequestConstructor = {{{httpRequest}}}) {
constructor(config?: Partial<OpenAPIConfig>, HttpRequest: HttpRequestConstructor = {{{httpRequest}}}) {
this.request = new HttpRequest({
BASE: config?.BASE ?? '{{{server}}}',
VERSION: config?.VERSION ?? '{{{version}}}',

View File

@ -1,7 +1,13 @@
{{>header}}
{{#if @root.exportCore}}
{{#if @root.exportClient}}
export { AppClient } from './client';
{{/if}}
{{#if @root.exportCore}}
export { ApiError } from './core/ApiError';
{{#if @root.exportClient}}
export { BaseHttpRequest } from './core/BaseHttpRequest';
{{/if}}
export { CancelablePromise, CancelError } from './core/CancelablePromise';
export { OpenAPI } from './core/OpenAPI';
export type { OpenAPIConfig } from './core/OpenAPI';

View File

@ -108,7 +108,8 @@ export const writeClient = async (
exportServices,
exportModels,
exportSchemas,
postfix
postfix,
clientName
);
}
};

View File

@ -1,7 +1,7 @@
import type { Client } from '../client/interfaces/Client';
import { HttpClient } from '../HttpClient';
import { Indent } from '../Indent';
import { mkdir, rmdir, writeFile } from './fileSystem';
import { writeFile } from './fileSystem';
import type { Templates } from './registerHandlebarTemplates';
import { writeClientClass } from './writeClientClass';
@ -38,8 +38,6 @@ describe('writeClientClass', () => {
await writeClientClass(client, templates, './dist', HttpClient.FETCH, 'AppClient', Indent.SPACE_4, '');
expect(rmdir).toBeCalled();
expect(mkdir).toBeCalled();
expect(writeFile).toBeCalled();
});
});

View File

@ -2,6 +2,7 @@ import { resolve } from 'path';
import type { Client } from '../client/interfaces/Client';
import { writeFile } from './fileSystem';
import { isDefined } from './isDefined';
import { Templates } from './registerHandlebarTemplates';
import { sortModelsByName } from './sortModelsByName';
import { sortServicesByName } from './sortServicesByName';
@ -19,6 +20,7 @@ import { sortServicesByName } from './sortServicesByName';
* @param exportModels Generate models
* @param exportSchemas Generate schemas
* @param postfix Service name postfix
* @param clientName Custom client class name
*/
export const writeClientIndex = async (
client: Client,
@ -29,7 +31,8 @@ export const writeClientIndex = async (
exportServices: boolean,
exportModels: boolean,
exportSchemas: boolean,
postfix: string
postfix: string,
clientName?: string
): Promise<void> => {
const templateResult = templates.index({
exportCore,
@ -42,6 +45,7 @@ export const writeClientIndex = async (
version: client.version,
models: sortModelsByName(client.models),
services: sortServicesByName(client.services),
exportClient: isDefined(clientName),
});
await writeFile(resolve(outputPath, 'index.ts'), templateResult);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,84 @@
import { compileWithTypescript } from './scripts/compileWithTypescript';
import { generate } from './scripts/generate';
import server from './scripts/server';
describe('v3.node', () => {
beforeAll(async () => {
await generate('client/axios', 'v3', 'axios', false, false, 'AppClient');
compileWithTypescript('client/axios');
await server.start('client/axios');
}, 30000);
afterAll(async () => {
await server.stop();
});
it('requests token', async () => {
const { AppClient } = require('./generated/client/axios/index.js');
const tokenRequest = jest.fn().mockResolvedValue('MY_TOKEN');
const client = new AppClient({
TOKEN: tokenRequest,
USERNAME: undefined,
PASSWORD: undefined,
});
const result = await client.simple.getCallWithoutParametersAndResponse();
expect(tokenRequest.mock.calls.length).toBe(1);
expect(result.headers.authorization).toBe('Bearer MY_TOKEN');
});
it('uses credentials', async () => {
const { AppClient } = require('./generated/client/axios/index.js');
const client = new AppClient({
TOKEN: undefined,
USERNAME: 'username',
PASSWORD: 'password',
});
const result = await client.simple.getCallWithoutParametersAndResponse();
expect(result.headers.authorization).toBe('Basic dXNlcm5hbWU6cGFzc3dvcmQ=');
});
it('supports complex params', async () => {
const { AppClient } = require('./generated/client/axios/index.js');
const client = new AppClient();
const result = await client.complex.complexTypes({
first: {
second: {
third: 'Hello World!',
},
},
});
expect(result).toBeDefined();
});
it('supports form data', async () => {
const { AppClient } = require('./generated/client/axios/index.js');
const client = new AppClient();
const result = await client.parameters.callWithParameters(
'valueHeader',
'valueQuery',
'valueForm',
'valueCookie',
'valuePath',
{
prop: 'valueBody',
}
);
expect(result).toBeDefined();
});
it('can abort the request', async () => {
let error;
try {
const { AppClient } = require('./generated/client/axios/index.js');
const client = new AppClient();
const promise = client.simple.getCallWithoutParametersAndResponse();
setTimeout(() => {
promise.cancel();
}, 10);
await promise;
} catch (e) {
error = (e as Error).message;
}
expect(error).toContain('Request aborted');
});
});

View File

@ -0,0 +1,62 @@
import browser from './scripts/browser';
import { compileWithBabel } from './scripts/compileWithBabel';
import { copy } from './scripts/copy';
import { generate } from './scripts/generate';
import server from './scripts/server';
describe('v3.babel', () => {
beforeAll(async () => {
await generate('client/babel', 'v3', 'fetch', true, true, 'AppClient');
await copy('client/babel');
compileWithBabel('client/babel');
await server.start('client/babel');
await browser.start();
}, 30000);
afterAll(async () => {
await browser.stop();
await server.stop();
});
it('requests token', async () => {
await browser.exposeFunction('tokenRequest', jest.fn().mockResolvedValue('MY_TOKEN'));
const result = await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient({
TOKEN: (window as any).tokenRequest,
USERNAME: undefined,
PASSWORD: undefined,
});
return await client.simple.getCallWithoutParametersAndResponse();
});
expect(result.headers.authorization).toBe('Bearer MY_TOKEN');
});
it('uses credentials', async () => {
const result = await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient({
TOKEN: undefined,
USERNAME: 'username',
PASSWORD: 'password',
});
return await client.simple.getCallWithoutParametersAndResponse();
});
expect(result.headers.authorization).toBe('Basic dXNlcm5hbWU6cGFzc3dvcmQ=');
});
it('supports complex params', async () => {
const result = await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient();
return await client.complex.complexTypes({
first: {
second: {
third: 'Hello World!',
},
},
});
});
expect(result).toBeDefined();
});
});

View File

@ -0,0 +1,161 @@
import browser from './scripts/browser';
import { compileWithTypescript } from './scripts/compileWithTypescript';
import { copy } from './scripts/copy';
import { generate } from './scripts/generate';
import server from './scripts/server';
describe('v3.fetch', () => {
beforeAll(async () => {
await generate('client/fetch', 'v3', 'fetch', false, false, 'AppClient');
await copy('client/fetch');
compileWithTypescript('client/fetch');
await server.start('client/fetch');
await browser.start();
}, 30000);
afterAll(async () => {
await browser.stop();
await server.stop();
});
it('requests token', async () => {
await browser.exposeFunction('tokenRequest', jest.fn().mockResolvedValue('MY_TOKEN'));
const result = await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient({
TOKEN: (window as any).tokenRequest,
USERNAME: undefined,
PASSWORD: undefined,
});
return await client.simple.getCallWithoutParametersAndResponse();
});
expect(result.headers.authorization).toBe('Bearer MY_TOKEN');
});
it('uses credentials', async () => {
const result = await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient({
TOKEN: undefined,
USERNAME: 'username',
PASSWORD: 'password',
});
return await client.simple.getCallWithoutParametersAndResponse();
});
expect(result.headers.authorization).toBe('Basic dXNlcm5hbWU6cGFzc3dvcmQ=');
});
it('supports complex params', async () => {
const result = await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient();
return await client.complex.complexTypes({
first: {
second: {
third: 'Hello World!',
},
},
});
});
expect(result).toBeDefined();
});
it('support form data', async () => {
const result = await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient();
return await client.parameters.callWithParameters(
'valueHeader',
'valueQuery',
'valueForm',
'valueCookie',
'valuePath',
{
prop: 'valueBody',
}
);
});
expect(result).toBeDefined();
});
it('can abort the request', async () => {
let error;
try {
await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient();
const promise = client.simple.getCallWithoutParametersAndResponse();
setTimeout(() => {
promise.cancel();
}, 10);
await promise;
});
} catch (e) {
error = (e as Error).message;
}
expect(error).toContain('CancelError: Request aborted');
});
it('should throw known error (500)', async () => {
const error = await browser.evaluate(async () => {
try {
const { AppClient } = (window as any).api;
const client = new AppClient();
await client.error.testErrorCode(500);
} catch (e) {
const error = e as any;
return JSON.stringify({
name: error.name,
message: error.message,
url: error.url,
status: error.status,
statusText: error.statusText,
body: error.body,
});
}
return;
});
expect(error).toBe(
JSON.stringify({
name: 'ApiError',
message: 'Custom message: Internal Server Error',
url: 'http://localhost:3000/base/api/v1.0/error?status=500',
status: 500,
statusText: 'Internal Server Error',
body: 'Internal Server Error',
})
);
});
it('should throw unknown error (409)', async () => {
const error = await browser.evaluate(async () => {
try {
const { AppClient } = (window as any).api;
const client = new AppClient();
await client.error.testErrorCode(409);
} catch (e) {
const error = e as any;
return JSON.stringify({
name: error.name,
message: error.message,
url: error.url,
status: error.status,
statusText: error.statusText,
body: error.body,
});
}
return;
});
expect(error).toBe(
JSON.stringify({
name: 'ApiError',
message: 'Generic Error',
url: 'http://localhost:3000/base/api/v1.0/error?status=409',
status: 409,
statusText: 'Conflict',
body: 'Conflict',
})
);
});
});

View File

@ -0,0 +1,142 @@
import { compileWithTypescript } from './scripts/compileWithTypescript';
import { generate } from './scripts/generate';
import server from './scripts/server';
describe('v3.node', () => {
beforeAll(async () => {
await generate('client/node', 'v3', 'node', false, false, 'AppClient');
compileWithTypescript('client/node');
await server.start('client/node');
}, 30000);
afterAll(async () => {
await server.stop();
});
it('requests token', async () => {
const { AppClient } = require('./generated/client/node/index.js');
const tokenRequest = jest.fn().mockResolvedValue('MY_TOKEN');
const client = new AppClient({
TOKEN: tokenRequest,
USERNAME: undefined,
PASSWORD: undefined,
});
const result = await client.simple.getCallWithoutParametersAndResponse();
expect(tokenRequest.mock.calls.length).toBe(1);
expect(result.headers.authorization).toBe('Bearer MY_TOKEN');
});
it('uses credentials', async () => {
const { AppClient } = require('./generated/client/node/index.js');
const client = new AppClient({
TOKEN: undefined,
USERNAME: 'username',
PASSWORD: 'password',
});
const result = await client.simple.getCallWithoutParametersAndResponse();
expect(result.headers.authorization).toBe('Basic dXNlcm5hbWU6cGFzc3dvcmQ=');
});
it('supports complex params', async () => {
const { AppClient } = require('./generated/client/node/index.js');
const client = new AppClient();
const result = await client.complex.complexTypes({
first: {
second: {
third: 'Hello World!',
},
},
});
expect(result).toBeDefined();
});
it('support form data', async () => {
const { AppClient } = require('./generated/client/node/index.js');
const client = new AppClient();
const result = await client.parameters.callWithParameters(
'valueHeader',
'valueQuery',
'valueForm',
'valueCookie',
'valuePath',
{
prop: 'valueBody',
}
);
expect(result).toBeDefined();
});
it('can abort the request', async () => {
let error;
try {
const { AppClient } = require('./generated/client/node/index.js');
const client = new AppClient();
const promise = client.simple.getCallWithoutParametersAndResponse();
setTimeout(() => {
promise.cancel();
}, 10);
await promise;
} catch (e) {
error = (e as Error).message;
}
expect(error).toContain('Request aborted');
});
it('should throw known error (500)', async () => {
let error;
try {
const { AppClient } = require('./generated/client/node/index.js');
const client = new AppClient();
await client.error.testErrorCode(500);
} catch (e) {
const err = e as any;
error = JSON.stringify({
name: err.name,
message: err.message,
url: err.url,
status: err.status,
statusText: err.statusText,
body: err.body,
});
}
expect(error).toBe(
JSON.stringify({
name: 'ApiError',
message: 'Custom message: Internal Server Error',
url: 'http://localhost:3000/base/api/v1.0/error?status=500',
status: 500,
statusText: 'Internal Server Error',
body: 'Internal Server Error',
})
);
});
it('should throw unknown error (409)', async () => {
let error;
try {
const { AppClient } = require('./generated/client/node/index.js');
const client = new AppClient();
await client.error.testErrorCode(409);
} catch (e) {
const err = e as any;
error = JSON.stringify({
name: err.name,
message: err.message,
url: err.url,
status: err.status,
statusText: err.statusText,
body: err.body,
});
}
expect(error).toBe(
JSON.stringify({
name: 'ApiError',
message: 'Generic Error',
url: 'http://localhost:3000/base/api/v1.0/error?status=409',
status: 409,
statusText: 'Conflict',
body: 'Conflict',
})
);
});
});

142
test/e2e/client.xhr.spec.ts Normal file
View File

@ -0,0 +1,142 @@
import browser from './scripts/browser';
import { compileWithTypescript } from './scripts/compileWithTypescript';
import { copy } from './scripts/copy';
import { generate } from './scripts/generate';
import server from './scripts/server';
describe('v3.xhr', () => {
beforeAll(async () => {
await generate('client/xhr', 'v3', 'xhr', false, false, 'AppClient');
await copy('client/xhr');
compileWithTypescript('client/xhr');
await server.start('client/xhr');
await browser.start();
}, 30000);
afterAll(async () => {
await browser.stop();
await server.stop();
});
it('requests token', async () => {
await browser.exposeFunction('tokenRequest', jest.fn().mockResolvedValue('MY_TOKEN'));
const result = await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient({
TOKEN: (window as any).tokenRequest,
USERNAME: undefined,
PASSWORD: undefined,
});
return await client.simple.getCallWithoutParametersAndResponse();
});
expect(result.headers.authorization).toBe('Bearer MY_TOKEN');
});
it('uses credentials', async () => {
const result = await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient({
TOKEN: undefined,
USERNAME: 'username',
PASSWORD: 'password',
});
return await client.simple.getCallWithoutParametersAndResponse();
});
expect(result.headers.authorization).toBe('Basic dXNlcm5hbWU6cGFzc3dvcmQ=');
});
it('supports complex params', async () => {
const result = await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient();
return await client.complex.complexTypes({
first: {
second: {
third: 'Hello World!',
},
},
});
});
expect(result).toBeDefined();
});
it('can abort the request', async () => {
let error;
try {
await browser.evaluate(async () => {
const { AppClient } = (window as any).api;
const client = new AppClient();
const promise = client.simple.getCallWithoutParametersAndResponse();
setTimeout(() => {
promise.cancel();
}, 10);
await promise;
});
} catch (e) {
error = (e as Error).message;
}
expect(error).toContain('CancelError: Request aborted');
});
it('should throw known error (500)', async () => {
const error = await browser.evaluate(async () => {
try {
const { AppClient } = (window as any).api;
const client = new AppClient();
await client.error.testErrorCode(500);
} catch (e) {
const error = e as any;
return JSON.stringify({
name: error.name,
message: error.message,
url: error.url,
status: error.status,
statusText: error.statusText,
body: error.body,
});
}
return;
});
expect(error).toBe(
JSON.stringify({
name: 'ApiError',
message: 'Custom message: Internal Server Error',
url: 'http://localhost:3000/base/api/v1.0/error?status=500',
status: 500,
statusText: 'Internal Server Error',
body: 'Internal Server Error',
})
);
});
it('should throw unknown error (409)', async () => {
const error = await browser.evaluate(async () => {
try {
const { AppClient } = (window as any).api;
const client = new AppClient();
await client.error.testErrorCode(409);
} catch (e) {
const error = e as any;
return JSON.stringify({
name: error.name,
message: error.message,
url: error.url,
status: error.status,
statusText: error.statusText,
body: error.body,
});
}
return;
});
expect(error).toBe(
JSON.stringify({
name: 'ApiError',
message: 'Generic Error',
url: 'http://localhost:3000/base/api/v1.0/error?status=409',
status: 409,
statusText: 'Conflict',
body: 'Conflict',
})
);
});
});

View File

@ -5,7 +5,8 @@ export const generate = async (
version: string,
client: 'fetch' | 'xhr' | 'node' | 'axios',
useOptions: boolean = false,
useUnionTypes: boolean = false
useUnionTypes: boolean = false,
clientName?: string
) => {
await __generate({
input: `./test/spec/${version}.json`,
@ -13,5 +14,6 @@ export const generate = async (
httpClient: client,
useOptions,
useUnionTypes,
clientName,
});
};

View File

@ -14,7 +14,7 @@ const generate = async (input, output) => {
exportSchemas: true,
exportModels: true,
exportServices: true,
clientName: 'AppClient',
// clientName: 'AppClient',
// indent: OpenAPI.Indent.SPACE_2,
// postfix: 'Api',
// request: './test/custom/request.ts',