Webapp: unit test

This commit is contained in:
Jeronimo Vallelunga 2017-07-14 00:02:28 -03:00
parent a557640228
commit 30c6a83399
9 changed files with 282 additions and 50 deletions

12
src/index.test.js Normal file
View File

@ -0,0 +1,12 @@
/* global jest, test, expect */
import bluetooth from '.';
jest.mock('./webapp', () => null);
jest.mock('./peripheral', () => null);
test('actions', () => {
expect(Object.keys(bluetooth)).toEqual([
'peripheral',
'webapp',
]);
});

View File

@ -0,0 +1,11 @@
/* global jest, test, expect */
import peripheral from '.';
jest.mock('./bleno', () => ({
start: jest.fn().mockReturnValue('mockStart'),
}));
test('peripheral', () => {
const result = peripheral(null, null);
expect(result).toEqual('mockStart');
});

View File

@ -0,0 +1,52 @@
export default function Central(
bluetooth,
{ encode, decode },
{ SERVICE_UUID, CHARACTERISTIC_UUID }) {
const state = {
server: null,
characteristic: null,
};
const connect = name => bluetooth
.requestDevice({
filters: [{ services: [SERVICE_UUID], name }],
})
.then(device => device.gatt.connect())
.then((server) => {
state.server = server;
return server.getPrimaryService(SERVICE_UUID);
})
.then(service => service.getCharacteristic(CHARACTERISTIC_UUID))
.then((characteristic) => {
state.characteristic = characteristic;
});
const handler = callback => state.characteristic.startNotifications().then(() => {
const listerner = event => callback(decode(event.target.value));
state.characteristic.addEventListener('characteristicvaluechanged', listerner);
return listerner;
});
const read = () => {
if (state.server && state.server.connected && state.characteristic) {
return state.characteristic.readValue().then(data => decode(data));
}
return Promise.reject(new Error('Bluetooth: Not Connected'));
};
const write = (action) => {
if (!state.server || !state.server.connected || !state.characteristic) return null;
const stringify = JSON.stringify(action);
const serialized = encode(stringify);
return state.characteristic.writeValue(serialized);
};
return {
connected: state.server && state.server.connected,
connect,
handler,
read,
write,
};
}

View File

@ -0,0 +1,100 @@
/* global jest, beforeEach, afterEach, test, expect */
import { CENTRAL_CONFIG } from '../../common/config';
import Central from './central';
const encoder = {
encode: jest.fn().mockReturnValue('mockEncode'),
decode: jest.fn().mockReturnValue('mockDecode'),
};
const characteristic = {
startNotifications: jest.fn().mockReturnValue(Promise.resolve()),
addEventListener: jest.fn(),
writeValue: jest.fn().mockReturnValue(Promise.resolve()),
readValue: jest.fn().mockReturnValue(Promise.resolve('mockData')),
};
const service = {
getCharacteristic: jest.fn().mockReturnValue(Promise.resolve(characteristic)),
};
const server = {
connected: true,
getPrimaryService: jest.fn().mockReturnValue(Promise.resolve(service)),
};
const device = {
gatt: {
connect: jest.fn().mockReturnValue(Promise.resolve(server)),
},
};
const bluetooth = {
requestDevice: jest.fn().mockReturnValue(Promise.resolve(device)),
};
let central = null;
beforeEach(() => {
central = new Central(bluetooth, encoder, CENTRAL_CONFIG);
});
afterEach(() => {
central = null;
});
test('Central: connect', () => {
expect.assertions(5);
const promise = central.connect('mockName').then(() => {
expect(bluetooth.requestDevice).toBeCalled();
expect(device.gatt.connect).toBeCalled();
expect(server.getPrimaryService).toBeCalled();
expect(service.getCharacteristic).toBeCalled();
return true;
});
return expect(promise).resolves.toBe(true);
});
test('Central: handler', () => {
const callback = jest.fn();
expect.assertions(3);
const promise = central.connect('mockName').then(() => central.handler(callback))
.then((listerner) => {
expect(characteristic.startNotifications).toBeCalled();
listerner({ target: { value: 'mockEvent' } });
expect(callback).toBeCalledWith('mockDecode');
return true;
});
return expect(promise).resolves.toBe(true);
});
test('Central: read', () => {
expect.assertions(2);
const promise = central.connect('mockName').then(() => central.read())
.then((data) => {
expect(characteristic.readValue).toBeCalled();
return data;
});
return expect(promise).resolves.toBe('mockDecode');
});
test('Central: write', () => {
expect.assertions(2);
const promise = central.connect('mockName')
.then(() => central.write({ type: 'ACTION' }))
.then(() => {
expect(characteristic.writeValue).toBeCalledWith('mockEncode');
return true;
});
return expect(promise).resolves.toBe(true);
});

View File

@ -1,57 +1,9 @@
/* global navigator TextEncoder TextDecoder */
import { CENTRAL_CONFIG } from '../../common/config';
import Encoder from '../../common/encoder';
export function Central(bluetooth, { encode, decode }, { SERVICE_UUID, CHARACTERISTIC_UUID }) {
const state = {
server: null,
characteristic: null,
};
import Central from './central';
const connect = name => bluetooth
.requestDevice({
filters: [{ services: [SERVICE_UUID], name }],
})
.then(device => device.gatt.connect())
.then((server) => {
state.server = server;
return server.getPrimaryService(SERVICE_UUID);
})
.then(service => service.getCharacteristic(CHARACTERISTIC_UUID))
.then((characteristic) => {
state.characteristic = characteristic;
});
const handler = callback => state.characteristic.startNotifications().then(() => {
state.characteristic.addEventListener('characteristicvaluechanged', (event) => {
callback(decode(event.target.value));
});
});
const read = () => {
if (state.server && state.server.connected && state.characteristic) {
return state.characteristic.readValue().then(data => decode(data));
}
return Promise.reject(new Error('Bluetooth: Not Connected'));
};
const write = (action) => {
if (!state.server || !state.server.connected || !state.characteristic) return null;
const stringify = JSON.stringify(action);
const serialized = encode(stringify);
return state.characteristic.writeValue(serialized);
};
return {
connected: state.server && state.server.connected,
connect,
handler,
read,
write,
};
}
/* global navigator TextEncoder TextDecoder */
export default new Central(
navigator.bluetooth,
Encoder({ TextEncoder, TextDecoder }),

View File

@ -0,0 +1,37 @@
/* global jest, test, expect, beforeEach, afterEach */
import middleware from '.';
jest.mock('../actions', () => ({
sendAction: jest.fn().mockReturnValue('mockAction'),
}));
let store = null;
let next = null;
beforeEach(() => {
store = {
dispatch: jest.fn(),
};
next = jest.fn();
});
afterEach(() => {
store = null;
next = null;
});
test('actions empty', () => {
middleware()(store)(next)({ type: 'ACTION' });
expect(next).toBeCalledWith({ type: 'ACTION' });
});
test('actions not included', () => {
middleware(['ACTION'])(store)(next)({ type: 'NOTEXISTS' });
expect(next).toBeCalledWith({ type: 'NOTEXISTS' });
});
test('actions included', () => {
middleware(['ACTION'])(store)(next)({ type: 'ACTION' });
expect(store.dispatch).toBeCalledWith('mockAction');
expect(next).toBeCalledWith({ type: 'ACTION' });
});

View File

@ -1,6 +1,18 @@
/* global test, expect */
import * as STATUS from '../central/status';
import * as TYPES from '../actions/types';
import initial from './initial';
import Reducer from '.';
test('Default State', () => {
const reducer = Reducer();
const nextState = reducer(undefined, { type: 'UNKNOWN' });
return expect(nextState).toBe(initial);
});
test('type: UNKNOWN', () => {
const reducer = Reducer();
@ -9,3 +21,40 @@ test('type: UNKNOWN', () => {
return expect(nextState).toBe(originalState);
});
test('type: BLUETOOTH_CONNECTING', () => {
const reducer = Reducer();
const originalState = { };
const nextState = reducer(originalState, { type: TYPES.BLUETOOTH_CONNECTING });
return expect(nextState).toEqual({ status: STATUS.CONNECTING });
});
test('type: BLUETOOTH_CONNECTED', () => {
const reducer = Reducer();
const originalState = { };
const nextState = reducer(originalState, { type: TYPES.BLUETOOTH_CONNECTED });
return expect(nextState).toEqual({ status: STATUS.CONNECTED });
});
test('type: BLUETOOTH_SYNC', () => {
const reducer = Reducer();
const originalState = { };
const nextState = reducer(originalState, { type: TYPES.BLUETOOTH_SYNC, payload: 'mockPayload' });
return expect(nextState).toEqual({ store: 'mockPayload' });
});
test('type: BLUETOOTH_SYNC, autosync: false', () => {
const reducer = Reducer(false);
const originalState = { };
const nextState = reducer(originalState, { type: TYPES.BLUETOOTH_SYNC, payload: 'mockPayload' });
return expect(nextState).toEqual({ });
});

View File

@ -0,0 +1,3 @@
import * as STATUS from '../central/status';
export default { status: STATUS.INIT };

View File

@ -0,0 +1,16 @@
/* global jest, test, expect */
import createSyncStore from '.';
jest.mock('redux', () => ({
createStore: jest.fn().mockReturnValue('mockStore'),
applyMiddleware: jest.fn(),
compose: jest.fn(),
}));
jest.mock('../middleware', () => jest.fn());
jest.mock('../reducers', () => jest.fn());
test('createSyncStore', () => {
const store = createSyncStore(['ACTION']);
return expect(store).toBe('mockStore');
});