mirror of https://github.com/Seich/Beau.git
commit
dc7d6f977d
|
|
@ -4,7 +4,11 @@
|
||||||
|
|
||||||
<h1 align="center">Beau</h1>
|
<h1 align="center">Beau</h1>
|
||||||
<p align="center">Testing JSON APIs made easy.</p>
|
<p align="center">Testing JSON APIs made easy.</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://codeclimate.com/github/Seich/Beau/maintainability"><img src="https://api.codeclimate.com/v1/badges/bc2de4d71893d6a2d18b/maintainability" /></a>
|
||||||
|
<a href="https://codeclimate.com/github/Seich/Beau/test_coverage"><img src="https://api.codeclimate.com/v1/badges/bc2de4d71893d6a2d18b/test_coverage" /></a>
|
||||||
|
<a href="https://circleci.com/gh/Seich/Beau/tree/master"><img src="https://circleci.com/gh/Seich/Beau/tree/master.svg?style=svg" alt="CircleCI"></a>
|
||||||
|
</p>
|
||||||
## What is Beau?
|
## What is Beau?
|
||||||
Beau, is a CLI that executes HTTP requests based on a YAML configuration file. This makes testing easy, it allows you to share test requests with others as part of your repo.
|
Beau, is a CLI that executes HTTP requests based on a YAML configuration file. This makes testing easy, it allows you to share test requests with others as part of your repo.
|
||||||
|
|
||||||
|
|
@ -33,7 +37,7 @@ Beau, is a CLI that executes HTTP requests based on a YAML configuration file. T
|
||||||
## Example Configuration File
|
## Example Configuration File
|
||||||
|
|
||||||
version: 1
|
version: 1
|
||||||
host: https://example.com/api/
|
endpoint: https://example.com/api/
|
||||||
|
|
||||||
POST /session:
|
POST /session:
|
||||||
ALIAS: session
|
ALIAS: session
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Beau's config Loader. should set up defaults for all requests 1`] = `
|
||||||
|
Beau {
|
||||||
|
"config": Object {
|
||||||
|
"CACHE": false,
|
||||||
|
"DEFAULTS": Object {
|
||||||
|
"headers": Object {
|
||||||
|
"authentication": "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ENDPOINT": "http://jsonplaceholder.typicode.com",
|
||||||
|
"PLUGINS": Array [],
|
||||||
|
"VERSION": 1,
|
||||||
|
},
|
||||||
|
"configKeys": Array [
|
||||||
|
"VERSION",
|
||||||
|
"CACHE",
|
||||||
|
"ENDPOINT",
|
||||||
|
"PLUGINS",
|
||||||
|
"DEFAULTS",
|
||||||
|
],
|
||||||
|
"defaults": Object {
|
||||||
|
"CACHE": false,
|
||||||
|
"DEFAULTS": Object {
|
||||||
|
"headers": Object {
|
||||||
|
"authentication": "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ENDPOINT": "http://jsonplaceholder.typicode.com",
|
||||||
|
"PLUGINS": Array [],
|
||||||
|
"VERSION": 1,
|
||||||
|
},
|
||||||
|
"requests": RequestList {
|
||||||
|
"cache": RequestCache {
|
||||||
|
"$cache": Object {},
|
||||||
|
},
|
||||||
|
"config": Object {
|
||||||
|
"CACHE": false,
|
||||||
|
"DEFAULTS": Object {
|
||||||
|
"headers": Object {
|
||||||
|
"authentication": "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ENDPOINT": "http://jsonplaceholder.typicode.com",
|
||||||
|
"PLUGINS": Array [],
|
||||||
|
"VERSION": 1,
|
||||||
|
},
|
||||||
|
"list": Array [
|
||||||
|
Request {
|
||||||
|
"ALIAS": "get-post",
|
||||||
|
"DEPENDENCIES": Set {},
|
||||||
|
"DOCUMENTATION": undefined,
|
||||||
|
"ENDPOINT": "http://jsonplaceholder.typicode.com/posts/1",
|
||||||
|
"HEADERS": Object {
|
||||||
|
"authentication": "hello",
|
||||||
|
},
|
||||||
|
"PARAMS": undefined,
|
||||||
|
"PAYLOAD": undefined,
|
||||||
|
"VERB": "GET",
|
||||||
|
"originalRequest": Object {
|
||||||
|
"ALIAS": "get-post",
|
||||||
|
"ENDPOINT": "http://jsonplaceholder.typicode.com",
|
||||||
|
"headers": Object {
|
||||||
|
"authentication": "hello",
|
||||||
|
},
|
||||||
|
"request": "GET /posts/1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Request {
|
||||||
|
"ALIAS": "user",
|
||||||
|
"DEPENDENCIES": Set {},
|
||||||
|
"DOCUMENTATION": undefined,
|
||||||
|
"ENDPOINT": "http://jsonplaceholder.typicode.com/user",
|
||||||
|
"HEADERS": Object {
|
||||||
|
"authentication": "hello",
|
||||||
|
"hello": "world",
|
||||||
|
},
|
||||||
|
"PARAMS": undefined,
|
||||||
|
"PAYLOAD": undefined,
|
||||||
|
"VERB": "GET",
|
||||||
|
"originalRequest": Object {
|
||||||
|
"ENDPOINT": "http://jsonplaceholder.typicode.com",
|
||||||
|
"alias": "user",
|
||||||
|
"headers": Object {
|
||||||
|
"authentication": "hello",
|
||||||
|
"hello": "world",
|
||||||
|
},
|
||||||
|
"request": "GET /user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"modifiers": Array [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
@ -48,7 +48,7 @@ Array [
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"alias": "show",
|
"alias": "show",
|
||||||
"host": "http://martianwabbit.com",
|
"endpoint": "http://martianwabbit.com",
|
||||||
"request": "GET /user",
|
"request": "GET /user",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,44 @@
|
||||||
const Beau = require('../beau');
|
|
||||||
const yaml = require('js-yaml');
|
const yaml = require('js-yaml');
|
||||||
|
const Beau = require('../beau');
|
||||||
|
|
||||||
describe(`Beau's config Loader.`, () => {
|
describe(`Beau's config Loader.`, () => {
|
||||||
it('Should only load valid configuration keys', () => {
|
it('Should only load valid configuration keys', () => {
|
||||||
let host = 'http://martianwabbit.com';
|
const doc = yaml.safeLoad(`
|
||||||
let version = 1;
|
version: 1
|
||||||
let cache = false;
|
endpoint: http://martianwabbit.com
|
||||||
let shouldntBeAdded = true;
|
cache: false
|
||||||
|
shouldntBeAdded: true
|
||||||
|
`);
|
||||||
|
|
||||||
let beau = new Beau({
|
const beau = new Beau(doc);
|
||||||
version,
|
|
||||||
host,
|
|
||||||
cache,
|
|
||||||
shouldntBeAdded
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(beau.config.HOST).toBe(host);
|
expect(beau.config.ENDPOINT).toBe(doc.endpoint);
|
||||||
expect(beau.config.CACHE).toBe(cache);
|
expect(beau.config.CACHE).toBe(doc.cache);
|
||||||
expect(beau.config.VERSION).toBe(version);
|
expect(beau.config.VERSION).toBe(doc.version);
|
||||||
expect(beau.config.shouldntBeAdded).toBeUndefined();
|
expect(beau.config.shouldntBeAdded).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set up defaults for all requests', () => {
|
||||||
|
const doc = yaml.safeLoad(`
|
||||||
|
version: 1
|
||||||
|
endpoint: 'http://jsonplaceholder.typicode.com'
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
headers:
|
||||||
|
authentication: hello
|
||||||
|
|
||||||
|
GET /posts/1: get-post
|
||||||
|
GET /user:
|
||||||
|
alias: user
|
||||||
|
headers:
|
||||||
|
hello: world
|
||||||
|
`);
|
||||||
|
|
||||||
|
const beau = new Beau(doc);
|
||||||
|
|
||||||
|
expect(beau).toMatchSnapshot();
|
||||||
|
beau.requests.list.forEach(r => {
|
||||||
|
expect(r.HEADERS.authentication).toMatch('hello');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ describe('Request', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
req = {
|
req = {
|
||||||
request: 'POST /user',
|
request: 'POST /user',
|
||||||
host: 'http://martianwabbit.com',
|
endpoint: 'http://martianwabbit.com',
|
||||||
alias: 'update',
|
alias: 'update',
|
||||||
params: {
|
params: {
|
||||||
userId: '$profile.UserId'
|
userId: '$profile.UserId'
|
||||||
|
|
@ -31,7 +31,7 @@ describe('Request', () => {
|
||||||
|
|
||||||
request = new Request(req);
|
request = new Request(req);
|
||||||
requestWithoutDependencies = new Request({
|
requestWithoutDependencies = new Request({
|
||||||
host: 'http://martianwabbit.com',
|
endpoint: 'http://martianwabbit.com',
|
||||||
request: 'GET /user',
|
request: 'GET /user',
|
||||||
alias: 'show'
|
alias: 'show'
|
||||||
});
|
});
|
||||||
|
|
@ -41,7 +41,7 @@ describe('Request', () => {
|
||||||
|
|
||||||
test('It should load up the given request', () => {
|
test('It should load up the given request', () => {
|
||||||
expect(request.VERB).toBe('POST');
|
expect(request.VERB).toBe('POST');
|
||||||
expect(request.ENDPOINT).toBe(req.host + '/user');
|
expect(request.ENDPOINT).toBe(req.endpoint + '/user');
|
||||||
expect(request.HEADERS).toBeDefined();
|
expect(request.HEADERS).toBeDefined();
|
||||||
expect(request.PAYLOAD).toBeDefined();
|
expect(request.PAYLOAD).toBeDefined();
|
||||||
expect(request.PARAMS).toBeDefined();
|
expect(request.PARAMS).toBeDefined();
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ const RequestList = require('../requestList');
|
||||||
const requestPromiseNativeMock = require('request-promise-native');
|
const requestPromiseNativeMock = require('request-promise-native');
|
||||||
|
|
||||||
describe('RequestList', () => {
|
describe('RequestList', () => {
|
||||||
const host = 'http://martianwabbit.com';
|
const endpoint = 'http://martianwabbit.com';
|
||||||
const doc = {
|
const doc = {
|
||||||
'POST /session': null,
|
'POST /session': null,
|
||||||
'Not a Request': null,
|
'Not a Request': null,
|
||||||
'GET /post': 'get-posts',
|
'GET /post': { alias: 'get-posts' },
|
||||||
'POST /user': {
|
'POST /user': {
|
||||||
alias: 'user',
|
alias: 'user',
|
||||||
payload: {
|
payload: {
|
||||||
|
|
@ -20,7 +20,7 @@ describe('RequestList', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
requestPromiseNativeMock.fail = false;
|
requestPromiseNativeMock.fail = false;
|
||||||
requests = new RequestList(doc, {
|
requests = new RequestList(doc, {
|
||||||
HOST: host,
|
ENDPOINT: endpoint,
|
||||||
PLUGINS: [
|
PLUGINS: [
|
||||||
{
|
{
|
||||||
'beau-jwt': {
|
'beau-jwt': {
|
||||||
|
|
@ -40,7 +40,7 @@ describe('RequestList', () => {
|
||||||
|
|
||||||
expect(requests.list.length).toBe(3);
|
expect(requests.list.length).toBe(3);
|
||||||
expect(request.VERB).toBe('POST');
|
expect(request.VERB).toBe('POST');
|
||||||
expect(request.ENDPOINT).toBe(host + '/session');
|
expect(request.ENDPOINT).toBe(endpoint + '/session');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch dependencies', () => {
|
it('should fetch dependencies', () => {
|
||||||
|
|
|
||||||
32
beau.js
32
beau.js
|
|
@ -1,17 +1,43 @@
|
||||||
|
const deepMerge = require('deepmerge');
|
||||||
|
|
||||||
const RequestList = require('./requestList');
|
const RequestList = require('./requestList');
|
||||||
|
const requestRegex = require('./shared').requestRegex;
|
||||||
|
|
||||||
class Beau {
|
class Beau {
|
||||||
constructor(doc) {
|
constructor(doc) {
|
||||||
this.defaults = {
|
this.defaults = {
|
||||||
VERSION: 1,
|
VERSION: 1,
|
||||||
CACHE: false,
|
CACHE: false,
|
||||||
HOST: '',
|
ENDPOINT: '',
|
||||||
PLUGINS: []
|
PLUGINS: [],
|
||||||
|
DEFAULTS: []
|
||||||
};
|
};
|
||||||
|
|
||||||
this.configKeys = Object.keys(this.defaults);
|
this.configKeys = Object.keys(this.defaults);
|
||||||
this.config = this.loadConfig(doc);
|
this.config = this.loadConfig(doc);
|
||||||
this.requests = new RequestList(doc, this.config);
|
this.requests = this.getRequests(doc);
|
||||||
|
this.requests = new RequestList(this.requests, this.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRequests(doc) {
|
||||||
|
let requests = Object.keys(doc).filter(key => {
|
||||||
|
return requestRegex.test(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
let results = {};
|
||||||
|
requests.forEach(r => {
|
||||||
|
if (typeof doc[r] === 'string') {
|
||||||
|
results[r] = {
|
||||||
|
ALIAS: doc[r]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
results[r] = doc[r];
|
||||||
|
}
|
||||||
|
|
||||||
|
results[r] = deepMerge(this.config.DEFAULTS, results[r]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadConfig(doc) {
|
loadConfig(doc) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
machine:
|
||||||
|
node:
|
||||||
|
version: 8.9.3
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
post:
|
||||||
|
- npm install -g codeclimate-test-reporter
|
||||||
|
|
||||||
|
test:
|
||||||
|
pre:
|
||||||
|
- npm run test:coverage
|
||||||
|
- codeclimate-test-reporter < ./coverage/lcov.info
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
VERSION: 1
|
VERSION: 1
|
||||||
HOST: https://api.github.com
|
ENDPOINT: https://api.github.com
|
||||||
|
|
||||||
auth: &auth
|
auth: &auth
|
||||||
HEADERS:
|
HEADERS:
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
version: 1
|
version: 1
|
||||||
host: 'http://jsonplaceholder.typicode.com'
|
endpoint: 'http://jsonplaceholder.typicode.com'
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
headers:
|
||||||
|
hello: $posts.body.0.userId
|
||||||
|
|
||||||
GET /posts/1: get-post
|
GET /posts/1: get-post
|
||||||
|
|
||||||
GET /posts/:
|
GET /posts/:
|
||||||
alias: posts
|
alias: posts
|
||||||
documentation:
|
headers:
|
||||||
title: Fetch Posts
|
hello: false
|
||||||
description: Fetches all posts available.
|
|
||||||
|
|
||||||
POST /posts/:
|
POST /posts/:
|
||||||
alias: new-post
|
alias: new-post
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
host: http://localhost:10080
|
endpoint: http://localhost:10080
|
||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
- beau-jwt:
|
- beau-jwt:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
VERSION: '1'
|
VERSION: '1'
|
||||||
HOST: https://slack.com/api
|
ENDPOINT: https://slack.com/api
|
||||||
|
|
||||||
auth: &auth
|
auth: &auth
|
||||||
token: xoxp-139455775026-139455775090-147751461120-f224ed6ffee029869a0f138d0859e7d6
|
token: xoxp-139455775026-139455775090-147751461120-f224ed6ffee029869a0f138d0859e7d6
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -7,12 +7,14 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"watch": "jest --watch"
|
"watch": "jest --watch",
|
||||||
|
"test:coverage": "jest --coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cli-color": "^1.1.0",
|
"cli-color": "^1.1.0",
|
||||||
"clui": "^0.3.1",
|
"clui": "^0.3.1",
|
||||||
"commander": "^2.12.2",
|
"commander": "^2.12.2",
|
||||||
|
"deepmerge": "^2.0.1",
|
||||||
"js-yaml": "^3.7.0",
|
"js-yaml": "^3.7.0",
|
||||||
"jsome": "^2.3.26",
|
"jsome": "^2.3.26",
|
||||||
"request": "^2.83.0",
|
"request": "^2.83.0",
|
||||||
|
|
@ -29,5 +31,8 @@
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"beau": "./bin/beau"
|
"beau": "./bin/beau"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
request.js
10
request.js
|
|
@ -14,15 +14,15 @@ class Request {
|
||||||
REQUEST,
|
REQUEST,
|
||||||
ALIAS,
|
ALIAS,
|
||||||
PAYLOAD,
|
PAYLOAD,
|
||||||
HOST,
|
ENDPOINT,
|
||||||
PARAMS,
|
PARAMS,
|
||||||
HEADERS,
|
HEADERS,
|
||||||
DOCUMENTATION
|
DOCUMENTATION
|
||||||
} = config;
|
} = config;
|
||||||
const { verb, endpoint } = this.parseRequest(REQUEST);
|
const { verb, path } = this.parseRequest(REQUEST);
|
||||||
|
|
||||||
this.VERB = verb;
|
this.VERB = verb;
|
||||||
this.ENDPOINT = HOST + endpoint;
|
this.ENDPOINT = ENDPOINT + path;
|
||||||
|
|
||||||
this.HEADERS = HEADERS;
|
this.HEADERS = HEADERS;
|
||||||
this.PAYLOAD = PAYLOAD;
|
this.PAYLOAD = PAYLOAD;
|
||||||
|
|
@ -39,11 +39,11 @@ class Request {
|
||||||
}
|
}
|
||||||
|
|
||||||
parseRequest(request) {
|
parseRequest(request) {
|
||||||
let parts = request.match(requestRegex);
|
const parts = request.match(requestRegex);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
verb: parts[1],
|
verb: parts[1],
|
||||||
endpoint: parts[2]
|
path: parts[2]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,17 +51,8 @@ class RequestList {
|
||||||
});
|
});
|
||||||
|
|
||||||
return requests.map(request => {
|
return requests.map(request => {
|
||||||
const type = typeof doc[request];
|
|
||||||
|
|
||||||
if (type === 'string') {
|
|
||||||
doc[request] = {
|
|
||||||
ALIAS: doc[request]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
doc[request] = doc[request] || {};
|
doc[request] = doc[request] || {};
|
||||||
|
doc[request].ENDPOINT = this.config.ENDPOINT;
|
||||||
doc[request].HOST = this.config.HOST;
|
|
||||||
doc[request].request = request;
|
doc[request].request = request;
|
||||||
|
|
||||||
return new Request(doc[request]);
|
return new Request(doc[request]);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const httpVerbs = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const requestRegex = new RegExp(`(${httpVerbs.join('|')})\\s(.*)`, 'i');
|
const requestRegex = new RegExp(`(${httpVerbs.join('|')})\\s(.*)`, 'i');
|
||||||
const replacementRegex = /\$([a-zA-Z\.\d\-\_]*)/g;
|
const replacementRegex = /\$([a-zA-Z\.\d\-\_\/\\]*)/g;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
httpVerbs,
|
httpVerbs,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue