devlucky / kakapo.js Goto Github PK
View Code? Open in Web Editor NEW:bird: Next generation mocking framework in Javascript
Home Page: http://devlucky.github.io/kakapo-js
License: MIT License
:bird: Next generation mocking framework in Javascript
Home Page: http://devlucky.github.io/kakapo-js
License: MIT License
Write down all code smells / things that annoy us and let's refactor a bit ;) This is for Beta-1 milestone but it should be completed LAST.
Cause it's awesome ;)
Support response code somehow...
Also supporting give support for common codes somehow? like SUCCESS
, NOT_FOUND
, UNAUTHORIZED
This is a must for functions like record.save()
to be useful and framework more bug-proof.
There's some similar functionality in both fakeXMLHttpRequest & fakeFetch, like: reset, saving native function and saving routes. I think we can abstract those three behaviours into some kind of wrapper. What do you think @zzarcon? :)
Right our interceptors only replace window.fetch
/ window.XMLHttpRequest
. I think we need to check if framework is used inside of browser, service worker, react native, etc and then assign fake service to window
/ global
/ self
Basically the ability to easy enable/disable components in the server
import {Router, DB, Server} from 'Kakapo';
const db1 = new DB();
db1.register('user', userFactory);
db1.register('user', commentFactory);
db1.create('user', 10);
db1.create('comment', 50);
const db2 = new DB();
db2.register('user', userFactory);
db2.create('user', 10000);
const router1 = new Router();
router1.get('v1/users', (request, db) => {db.all('user')});
const router2 = new Router({host: 'www.devlucky.com'});
router1.get('v2/user/all', (request, db) => {db.all('user')});
const server = new Server();
//Important part:
server.use(db1);
server.use(router2);
This is the expected behaviour:
const router = new Router();
router.get('/users/:user_id', (request) => {
request.params.user_id === 1;
});
fetch('/users/1');
We must ensure that the response which is returned by the fetch interceptor
is an instance of Response
fetch('/users', (response) => {
console.log(response instanceof Response === true);
});
Factories like this must work:
const userFactory = faker => ({
name: 'Hector'
address: {
zipCode: faker.address.zipCode,
city: faker.address.city,
streetName: faker.address.streetName,
}
});
The Database
must use https://github.com/Marak/faker.js in order to populate the factory records
Draft...
import {DB} from '../src/kakapo';
const db = new DB();
db.register('user', userFactory);
function userFactory(faker) {
return {
firstName: faker.name.firstName,
lastName: 'Zarco',
avatar: faker.internet.avatar,
age: 24
};
}
Whenever the DB generate the records it will check the value of the properties, if the value is a function it will execute. That way will can easily define random data
Things like Object.keys()
and Object.assign()
are not polyfill'ed with babel. We should consider changing those two to _.keys()
and _.assign()
in favor of browsers without full support of ES6. What do you think @zzarcon?
The Server
class should be able to listen for request on the following implementations:
GET
, POST
, PUT
, DELETE
Maybe we should consider using a different module for it, something like Interceptor
? and just isolate there the functionality and integrate in on the Server
...
This is pre-issue for #52
Create real life working examples inside examples folder
After #51 we need to create some kind of dope template like ramda or lo-dash is using 💪
Relationship proposal
import {DB} from '../src/kakapo';
const db = new DB();
db.register('user', userFactory);
db.register('post', postFactory);
db.register('like', likeFactory);
function userFactory() {
return {
firstName: faker.name.firstName,
lastName: 'Zarco',
avatar: faker.internet.avatar,
age: 24
};
}
function postFactory(faker) {
return {
text: 'Foo',
date: faker.date
likes: db.hasMany('like'),
author: db.belongsTo('user')
};
}
function likeFactory(faker) {
return {
date: faker.date,
author: db.belongsTo('user', 1)
}
}
Different proposals for host handling
1
gtihubServer = new Router({host: 'api.github.com'});
gtihubServer.get('/emojis', handler)
gtihubServer.get('/users', handler)
gtihubServer.get('/user/:id', handler)
runtasticServer = new Router({host: 'runtastic.com'});
runtasticServer.get('/emojis', handler)
runtasticServer.get('/users', handler)
runtasticServer.get('/user/:id', handler)
2
server = new Router();
server.get('/emojis', handler, {host: 'api.github.com'})
server.get('/users', handler, {host: 'api.github.com'})
server.get('/user/:id', handler, {host: 'api.github.com'})
server.get('/users', handler, {host: 'runtastic.com'})
server.post('/users', handler, {host: 'runtastic.com'})
3
server = new Router();
server.namespace('api.github.com')
server.get('/emojis', handler)
server.get('/users', handler)
server.get('/user/:id', handler)
server.namespace('runtastic.com')
server.get('/users', handler)
server.post('/users', handler)
I really like the 3 since allow us to easily define handlers for different host without being that verbose.
PD: Please forget about the router/server concept, let's asume is the same for now
Basically subscribe to router events, proposal:
import {Router} from 'Kakapo';
const router = new Router();
router.on('handledRequest', (verb, path, request) => {
});
router.on('unhandledRequest', (verb, path, request) => {
});
router.on('erroredRequest', (verb, path, request, error) => {
});
Offer default Serializers that will be pluggable to the request handler:
import {DB, JSONApiSerializer} from 'Kakapo';
const db = new DB();
db.register('user', (faker) => {
return JSONApiSerializer({
firstName: faker.name.firstName,
lastName: 'Zarco',
avatar: faker.internet.avatar,
age: 24
});
});
This will return a payload following the JSONApi standards:
{
"data": [{
"id": 1,
"type": "user",
"attributes": {
"firstName": "Hector",
"lastName": "Zarco",
"avatar": "http://url",
"age": 24
}
}]
}
Also the user must be able to define a custom serializers:
const ApiV1Serializer = (payload) => {
return {
version: 'v1',
data: payload,
status: {
code: 200,
text: 'Success'
}
};
};
Instead use find / filter / all
Will be cool to persist the Database in:
We must provide a way to not include all Kakapo code in the production build...
We should provide working examples as a showcase of the project
Chrome extension that will all the requested urls, the handled ones, his headers, body, etc...
jsdocs
documentation.js
to read it and produce .md
filesdoc
file like aboveThe Server component is in charge of connect all the other Kakapo pieces. A real example could be something like:
import {Router, DB, Server} from 'Kakapo';
const db = new DB();
db.register('user', (faker) => {
return {
firstName: faker.name.firstName,
lastName: 'Zarco',
avatar: faker.internet.avatar,
age: 24
};
});
const router = new Router({host: 'custom'});
router.get('/users/', (request, db) => {
return db.all('user');
});
router.get('/users/:user_id', (request, db) => {
return db.find('user', request.params.id);
});
router.post('/users/', (request, db) => {
const createdUser = db.create('user', request.body.user.id);
return createdUser;
});
const server = new Server({requestDelay: 500});
server.use(db);
server.use(router);
Router should admit the following options
host
requestDelay
defaultResponseHeaders
- Not sure about this one...What about just being explicit on this and avoid possible future errors?
import {DB} from 'Kakapo';
const db = new DB();
db.register('user', userFactory);
const userFactory = (faker, constrains) => {
return {
id: constrains.autoincrement(),
firstName: 'hector',
lastName: 'zarco'
};
}
@joanromano @MP0w @oskarcieslik
I think we need to deploy the apps somewhere in order to demo them
I think that we should change names:
filter
=> find
find
=> findOne
Ava / Mocha + chai. @zzarcon ?
RecordFactory
should attach certain methods / properties to record, like: save() before pushing it into proper store. @zzarcon?
let user = db.findById('user', 0);
user.name = 'paco';
user.save(); // updates this record in db
I was thinking about making it impossible for user to use db.store
, db.factories
, etc. I came up with this solution, @zzarcon please review :)
// stores.js
const storeFactory = () => {
const store = {};
return {
get(collectionName) {
return store[collectionName];
}
set(collectionName, value) {
store[collectionName] = value;
}
}
};
export const createStores = () => ({
factories: storeFactory(),
records: storeFactory(),
uuids: storeFactory()
});
// recordFactory.js
const updateRecord = (record, collectionName, { records }) => {
const originalRecord = records.find(collectionName, {id: record.id});
return Object.assign(originalRecord, record);
};
const assignId = (collectionName, { uuids }) => {
const id = uuids[collectionName] || 0;
uuids[collectionName] = id + 1;
return id;
};
export const recordFactory = (record, collectionName, stores) => {
record.save = () => updateRecord(record, collectionName, stores);
record.id = assignId(collectionName, stores);
return record;
};
// database.js
import { createStores } from './createStores';
import { recordFactory } from './recordFactory';
const stores = createStores();
const pushToRecords = (records, collectionName, stores) => stores.set(collectionName,
records.map(record => recordFactory(record, collectionName, stores)));
export class Database {
// ...
all(collectionName) {
return stores.records.get(collectionName);
}
// ...
};
import {Server, Router} from 'kakapo';
const server = new Server();
const router = new Router();
router.get('/foo', () => {foo: 'bar'});
server.use(router);
fetch('/foo') //request intercepted
server.remove(router);
fetch('/foo'); //request skipped
Same thing with Databases
;
Right now we support Response headers, we also should provide support for request headers handling.
import {Server} from 'Kakapo';
const server = new Server();
server.post('/users', (request) => {
request.headers === {'X-Custom-Value': 'VALUE'};
});
var headers = new Headers({'X-Custom-Value': 'VALUE'});
var options = {
method: 'POST',
headers: headers
};
var request = new Request('/users');
fetch(request, options);
The Router
must use https://github.com/pillarjs/path-to-regexp to handle requests
Draft...
import {router} from 'kakapo';
router.get('/users', usersHandler);
router.post('/users/:user_id', createUser);
function usersHandler(request, db) {
return {
users: db.all('user');
};
};
function createUser(request, db) {
db.create('user', request.params.id);
};
Investigate if we can easily extend all the existing functionalities of XMLHttpRequest
and just override some methods
import {Response, Server} from 'Kakapo';
const server = new Server();
server.get('/users/:id', (request, db) => {
const body = db.find('user', request.params.id);
const statusCode = body ? 200 : 401;
const headers = {'version': 'v1', 'x-custom-header': 'foo'};
return new Response(statusCode, body, headers);
});
I would like to have those separate
const user = db.find('user', {city: {name: 'valencia'}})
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.