[ISSUE-11] Notify Message Limit (#12)
* [ISSUE-11] Notify Message Limit * [ISSUE-11] Unit Testing
This commit is contained in:
parent
3cb1fc853c
commit
aa03b16916
|
@ -4,7 +4,7 @@ const { Provider, connect } = require('ink-redux');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
|
|
||||||
export default (store) => {
|
export default (store) => {
|
||||||
function Counter({ counter }) {
|
function Counter({ counter, quote }) {
|
||||||
const color = {
|
const color = {
|
||||||
blue: counter > 0,
|
blue: counter > 0,
|
||||||
red: counter < 0,
|
red: counter < 0,
|
||||||
|
@ -13,19 +13,24 @@ export default (store) => {
|
||||||
<div>
|
<div>
|
||||||
Counter:
|
Counter:
|
||||||
<Text {...color}> {counter}</Text>
|
<Text {...color}> {counter}</Text>
|
||||||
|
<br />
|
||||||
|
<Text>{quote}</Text>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Counter.propTypes = {
|
Counter.propTypes = {
|
||||||
counter: PropTypes.number,
|
counter: PropTypes.number,
|
||||||
|
quote: PropTypes.string,
|
||||||
};
|
};
|
||||||
Counter.defaultProps = {
|
Counter.defaultProps = {
|
||||||
counter: 0,
|
counter: 0,
|
||||||
|
quote: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
counter: state,
|
counter: state.counter,
|
||||||
|
quote: state.quote,
|
||||||
});
|
});
|
||||||
|
|
||||||
const ConnectedCounter = connect(mapStateToProps)(Counter);
|
const ConnectedCounter = connect(mapStateToProps)(Counter);
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
export default function counter(state = 0, { type }) {
|
export default function counter(state = { counter: 0 }, { type, payload }) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'INCREMENT':
|
case 'INCREMENT':
|
||||||
return state + 1;
|
return {
|
||||||
|
counter: state.counter + 1,
|
||||||
|
quote: payload,
|
||||||
|
};
|
||||||
case 'DECREMENT':
|
case 'DECREMENT':
|
||||||
return state - 1;
|
return {
|
||||||
|
counter: state.counter - 1,
|
||||||
|
quote: payload,
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
"test": "react-scripts test --env=jsdom",
|
"test": "react-scripts test --env=jsdom",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"predeploy": "npm run build",
|
"predeploy": "npm run build",
|
||||||
"deploy": "gh-pages -d build"
|
"deploy": "gh-pages -d build"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -28,6 +28,7 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://jvallelunga.github.io/redux-bluetooth",
|
"homepage": "https://jvallelunga.github.io/redux-bluetooth",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"predator-quotes": "^1.1.16",
|
||||||
"prop-types": "^15.5.10",
|
"prop-types": "^15.5.10",
|
||||||
"react": "^15.6.1",
|
"react": "^15.6.1",
|
||||||
"react-dom": "^15.6.1",
|
"react-dom": "^15.6.1",
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
|
import predatorQuotes from 'predator-quotes';
|
||||||
import * as TYPES from './types';
|
import * as TYPES from './types';
|
||||||
|
|
||||||
export function increment() {
|
export function increment() {
|
||||||
return { type: TYPES.INCREMENT };
|
return {
|
||||||
|
type: TYPES.INCREMENT,
|
||||||
|
payload: predatorQuotes.random(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decrement() {
|
export function decrement() {
|
||||||
return { type: TYPES.DECREMENT };
|
return {
|
||||||
|
type: TYPES.DECREMENT,
|
||||||
|
payload: predatorQuotes.random(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,18 +16,20 @@ export default class App extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { store, status, onIncrement, onDecrement } = this.props;
|
const { counter, quote, status, onIncrement, onDecrement } = this.props;
|
||||||
|
|
||||||
const counter = Number(store);
|
const nCounter = Number(counter);
|
||||||
let className = 'app-counter';
|
let className = 'app-counter';
|
||||||
if (counter > 0) className += ' app-counter--positive';
|
if (nCounter > 0) className += ' app-counter--positive';
|
||||||
if (counter < 0) className += ' app-counter--negative';
|
if (nCounter < 0) className += ' app-counter--negative';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
{status === 'CONNECTED' &&
|
{status === 'CONNECTED' &&
|
||||||
<div className={className}>
|
<div>
|
||||||
{store}
|
<span className={className}>{counter}</span>
|
||||||
|
<br />
|
||||||
|
<span>{quote}</span>
|
||||||
</div>}
|
</div>}
|
||||||
<div className="app-actions">
|
<div className="app-actions">
|
||||||
{status !== 'CONNECTED' &&
|
{status !== 'CONNECTED' &&
|
||||||
|
@ -49,7 +51,8 @@ export default class App extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
App.propTypes = {
|
App.propTypes = {
|
||||||
store: PropTypes.number,
|
counter: PropTypes.number,
|
||||||
|
quote: PropTypes.string,
|
||||||
status: PropTypes.string,
|
status: PropTypes.string,
|
||||||
onConnect: PropTypes.func,
|
onConnect: PropTypes.func,
|
||||||
onIncrement: PropTypes.func,
|
onIncrement: PropTypes.func,
|
||||||
|
@ -57,7 +60,8 @@ App.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
App.defaultProps = {
|
App.defaultProps = {
|
||||||
store: 0,
|
counter: 0,
|
||||||
|
quote: '',
|
||||||
status: '',
|
status: '',
|
||||||
onConnect: () => true,
|
onConnect: () => true,
|
||||||
onIncrement: () => true,
|
onIncrement: () => true,
|
||||||
|
|
|
@ -5,7 +5,10 @@ import { increment, decrement } from '../actions';
|
||||||
|
|
||||||
import Component from './component';
|
import Component from './component';
|
||||||
|
|
||||||
const mapState = state => state;
|
const mapState = ({ status, store }) => ({
|
||||||
|
status,
|
||||||
|
...store,
|
||||||
|
});
|
||||||
|
|
||||||
const mapAction = {
|
const mapAction = {
|
||||||
onConnect: actions.connectStore,
|
onConnect: actions.connectStore,
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
.app-counter {
|
.app-counter {
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
line-height: calc(100vh - 200px);
|
line-height: calc(100vh - 300px);
|
||||||
font-size: 80vh;
|
font-size: 80vh;
|
||||||
color: black;
|
color: black;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -9,3 +9,7 @@ export const BLENO_CONFIG = {
|
||||||
CHARACTERISTIC_UUID: CONFIG.CHARACTERISTIC_UUID.replace(/-/g, ''),
|
CHARACTERISTIC_UUID: CONFIG.CHARACTERISTIC_UUID.replace(/-/g, ''),
|
||||||
DESCRIPTOR_UUID: '2901',
|
DESCRIPTOR_UUID: '2901',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const COMMON_TYPES = {
|
||||||
|
BLUETOOTH_SYNC_REQUEST: '@@bluetooth/SYNC_REQUEST',
|
||||||
|
};
|
||||||
|
|
|
@ -2,16 +2,8 @@ export default function Encoder({ TextEncoder, TextDecoder }) {
|
||||||
const encoder = new TextEncoder('utf-8');
|
const encoder = new TextEncoder('utf-8');
|
||||||
const decoder = new TextDecoder('utf-8');
|
const decoder = new TextDecoder('utf-8');
|
||||||
|
|
||||||
const encode = (json) => {
|
const encode = string => encoder.encode(string);
|
||||||
const string = JSON.stringify(json);
|
const decode = data => decoder.decode(data);
|
||||||
return encoder.encode(string);
|
|
||||||
};
|
|
||||||
|
|
||||||
const decode = (data) => {
|
|
||||||
const string = decoder.decode(data);
|
|
||||||
const json = JSON.parse(string);
|
|
||||||
return typeof json === 'string' ? JSON.parse(json) : json;
|
|
||||||
};
|
|
||||||
|
|
||||||
return { encode, decode };
|
return { encode, decode };
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,10 @@ import Encoder from '.';
|
||||||
const { encode, decode } = new Encoder(TextEncoding);
|
const { encode, decode } = new Encoder(TextEncoding);
|
||||||
|
|
||||||
test('encode / decode', () => {
|
test('encode / decode', () => {
|
||||||
const data = encode({ type: 'TEST', payload: 'PAYLOAD' });
|
const message = JSON.stringify({ type: 'TEST', payload: 'PAYLOAD' });
|
||||||
|
const data = encode(message);
|
||||||
const result = decode(data);
|
const result = decode(data);
|
||||||
|
|
||||||
expect(result).toEqual({ type: 'TEST', payload: 'PAYLOAD' });
|
expect(result).toEqual(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('encode / decode: string', () => {
|
|
||||||
const data = encode('{ "type": "TEST", "payload": "PAYLOAD" }');
|
|
||||||
const result = decode(data);
|
|
||||||
|
|
||||||
expect(result).toEqual({ type: 'TEST', payload: 'PAYLOAD' });
|
|
||||||
});
|
|
||||||
|
|
|
@ -2,11 +2,9 @@ export default function Characteristic(uuid, Parent, util, descriptor, { encode,
|
||||||
function ReduxCharacteristic() {
|
function ReduxCharacteristic() {
|
||||||
Parent.call(this, {
|
Parent.call(this, {
|
||||||
uuid,
|
uuid,
|
||||||
properties: ['read', 'write', 'notify'],
|
properties: ['write', 'notify'],
|
||||||
descriptors: [descriptor],
|
descriptors: [descriptor],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.state = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(ReduxCharacteristic, Parent);
|
util.inherits(ReduxCharacteristic, Parent);
|
||||||
|
@ -18,21 +16,14 @@ export default function Characteristic(uuid, Parent, util, descriptor, { encode,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onAction(decode(data));
|
const action = decode(data);
|
||||||
|
this.onAction(JSON.parse(action));
|
||||||
|
|
||||||
callback(this.RESULT_SUCCESS);
|
callback(this.RESULT_SUCCESS);
|
||||||
};
|
};
|
||||||
|
|
||||||
ReduxCharacteristic.prototype.onReadRequest = function (offset, callback) {
|
|
||||||
if (offset) {
|
|
||||||
callback(this.RESULT_ATTR_NOT_LONG, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(this.RESULT_SUCCESS, this.state);
|
|
||||||
};
|
|
||||||
|
|
||||||
ReduxCharacteristic.prototype.onSubscribe = function (maxValueSize, updateValueCallback) {
|
ReduxCharacteristic.prototype.onSubscribe = function (maxValueSize, updateValueCallback) {
|
||||||
|
this.maxValueSize = maxValueSize;
|
||||||
this.updateValueCallback = updateValueCallback;
|
this.updateValueCallback = updateValueCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,9 +36,16 @@ export default function Characteristic(uuid, Parent, util, descriptor, { encode,
|
||||||
};
|
};
|
||||||
|
|
||||||
ReduxCharacteristic.prototype.updateState = function (state) {
|
ReduxCharacteristic.prototype.updateState = function (state) {
|
||||||
this.state = encode(state);
|
|
||||||
if (this.updateValueCallback) {
|
if (this.updateValueCallback) {
|
||||||
this.updateValueCallback(this.state);
|
const message = encode(`[[[${JSON.stringify(state)}]]]`);
|
||||||
|
let i = 0;
|
||||||
|
do {
|
||||||
|
const next = i + this.maxValueSize;
|
||||||
|
const end = Math.min(next, message.length);
|
||||||
|
const data = message.slice(i, end);
|
||||||
|
this.updateValueCallback(data);
|
||||||
|
i = next;
|
||||||
|
} while (i < message.length);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ let decode = null;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
encode = jest.fn().mockReturnValue('mockEncode');
|
encode = jest.fn().mockReturnValue('mockEncode');
|
||||||
decode = jest.fn().mockReturnValue('mockDecode');
|
decode = jest.fn().mockReturnValue('{"mockDecode":"mockDecode"}');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -28,9 +28,8 @@ test('new Characteristic', () => {
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
||||||
|
|
||||||
expect(characteristic.uuid).toBe('mockUUID');
|
expect(characteristic.uuid).toBe('mockUUID');
|
||||||
expect(characteristic.properties).toEqual(['read', 'write', 'notify']);
|
expect(characteristic.properties).toEqual(['write', 'notify']);
|
||||||
expect(characteristic.descriptors).toEqual(['mockDescriptor']);
|
expect(characteristic.descriptors).toEqual(['mockDescriptor']);
|
||||||
expect(characteristic.state).toBe(null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Characteristic.onWriteRequest: RESULT_ATTR_NOT_LONG', () => {
|
test('Characteristic.onWriteRequest: RESULT_ATTR_NOT_LONG', () => {
|
||||||
|
@ -51,35 +50,17 @@ test('Characteristic.onWriteRequest: RESULT_SUCCESS', () => {
|
||||||
const spyOnAction = jest.spyOn(characteristic, 'onAction');
|
const spyOnAction = jest.spyOn(characteristic, 'onAction');
|
||||||
characteristic.onWriteRequest(null, false, false, callback);
|
characteristic.onWriteRequest(null, false, false, callback);
|
||||||
|
|
||||||
expect(spyOnAction).toBeCalledWith('mockDecode');
|
expect(spyOnAction).toBeCalledWith({ mockDecode: 'mockDecode' });
|
||||||
expect(callback).toBeCalledWith('RESULT_SUCCESS');
|
expect(callback).toBeCalledWith('RESULT_SUCCESS');
|
||||||
|
|
||||||
callback = jest.fn();
|
callback = jest.fn();
|
||||||
characteristic.onAction = jest.fn();
|
characteristic.onAction = jest.fn();
|
||||||
characteristic.onWriteRequest(null, false, false, callback);
|
characteristic.onWriteRequest(null, false, false, callback);
|
||||||
|
|
||||||
expect(characteristic.onAction).toBeCalledWith('mockDecode');
|
expect(characteristic.onAction).toBeCalledWith({ mockDecode: 'mockDecode' });
|
||||||
expect(callback).toBeCalledWith('RESULT_SUCCESS');
|
expect(callback).toBeCalledWith('RESULT_SUCCESS');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Characteristic.onReadRequest: RESULT_ATTR_NOT_LONG', () => {
|
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
|
||||||
|
|
||||||
const callback = jest.fn();
|
|
||||||
characteristic.onReadRequest(true, callback);
|
|
||||||
|
|
||||||
expect(callback).toBeCalledWith('RESULT_ATTR_NOT_LONG', null);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Characteristic.onReadRequest: RESULT_SUCCESS', () => {
|
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
|
||||||
|
|
||||||
const callback = jest.fn();
|
|
||||||
characteristic.onReadRequest(false, callback);
|
|
||||||
|
|
||||||
expect(callback).toBeCalledWith('RESULT_SUCCESS', characteristic.state);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Characteristic.onSubscribe', () => {
|
test('Characteristic.onSubscribe', () => {
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
||||||
|
|
||||||
|
@ -100,12 +81,10 @@ test('Characteristic.onUnsubscribe', () => {
|
||||||
test('Characteristic.updateState', () => {
|
test('Characteristic.updateState', () => {
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
||||||
|
|
||||||
characteristic.updateState('mockState');
|
const maxValueSize = 10;
|
||||||
expect(characteristic.state).toBe('mockEncode');
|
|
||||||
|
|
||||||
const updateValueCallback = jest.fn();
|
const updateValueCallback = jest.fn();
|
||||||
characteristic.onSubscribe(null, updateValueCallback);
|
characteristic.onSubscribe(maxValueSize, updateValueCallback);
|
||||||
characteristic.updateState('mockState');
|
characteristic.updateState({ mockState: 'mockState' });
|
||||||
|
|
||||||
expect(updateValueCallback).toBeCalledWith('mockEncode');
|
expect(updateValueCallback).toBeCalledWith('mockEncode');
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,9 +4,9 @@ export default function Actions(central, TYPES) {
|
||||||
payload: state,
|
payload: state,
|
||||||
});
|
});
|
||||||
|
|
||||||
const syncStore = () => ({
|
const sendAction = action => ({
|
||||||
type: TYPES.BLUETOOTH_SYNC_REQUEST,
|
type: TYPES.BLUETOOTH_SEND_REQUEST,
|
||||||
request: dispatch => central.read().then(state => dispatch(syncState(state))),
|
request: () => central.write(action),
|
||||||
});
|
});
|
||||||
|
|
||||||
const connectStore = name => ({
|
const connectStore = name => ({
|
||||||
|
@ -17,18 +17,17 @@ export default function Actions(central, TYPES) {
|
||||||
.connect(name)
|
.connect(name)
|
||||||
.then(() => central.handler(state => dispatch(syncState(state))))
|
.then(() => central.handler(state => dispatch(syncState(state))))
|
||||||
.then(() => dispatch({ type: TYPES.BLUETOOTH_CONNECTED }))
|
.then(() => dispatch({ type: TYPES.BLUETOOTH_CONNECTED }))
|
||||||
.then(() => dispatch(syncStore()));
|
.then(() => dispatch(sendAction({
|
||||||
|
type: TYPES.BLUETOOTH_SEND_REQUEST,
|
||||||
|
request: () => central.write({
|
||||||
|
type: TYPES.BLUETOOTH_SYNC_REQUEST,
|
||||||
|
}),
|
||||||
|
})));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const sendAction = action => ({
|
|
||||||
type: TYPES.BLUETOOTH_SEND_REQUEST,
|
|
||||||
request: () => central.write(action),
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
connectStore,
|
connectStore,
|
||||||
syncStore,
|
|
||||||
syncState,
|
syncState,
|
||||||
sendAction,
|
sendAction,
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,7 +33,7 @@ test('syncState', () => {
|
||||||
test('connectStore', () => {
|
test('connectStore', () => {
|
||||||
const { connectStore } = Actions(central, TYPES);
|
const { connectStore } = Actions(central, TYPES);
|
||||||
const action = connectStore('mockName');
|
const action = connectStore('mockName');
|
||||||
expect.assertions(10);
|
expect.assertions(9);
|
||||||
|
|
||||||
expect(action.type).toEqual(TYPES.BLUETOOTH_CONNECT_REQUEST);
|
expect(action.type).toEqual(TYPES.BLUETOOTH_CONNECT_REQUEST);
|
||||||
|
|
||||||
|
@ -46,28 +46,10 @@ test('connectStore', () => {
|
||||||
expect(dispatch.mock.calls[1][0]).toEqual({ type: TYPES.BLUETOOTH_CONNECTED });
|
expect(dispatch.mock.calls[1][0]).toEqual({ type: TYPES.BLUETOOTH_CONNECTED });
|
||||||
|
|
||||||
const syncStore = dispatch.mock.calls[2][0];
|
const syncStore = dispatch.mock.calls[2][0];
|
||||||
expect(syncStore.type).toEqual(TYPES.BLUETOOTH_SYNC_REQUEST);
|
expect(syncStore.type).toEqual(TYPES.BLUETOOTH_SEND_REQUEST);
|
||||||
return syncStore.request(dispatch);
|
return syncStore.request(dispatch);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
expect(central.read).toBeCalled();
|
expect(central.write).toBeCalled();
|
||||||
expect(dispatch.mock.calls[3][0]).toEqual({ type: TYPES.BLUETOOTH_SYNC, payload: 'mockState' });
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return expect(promise).resolves.toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('syncStore', () => {
|
|
||||||
const { syncStore } = Actions(central, TYPES);
|
|
||||||
const action = syncStore();
|
|
||||||
expect.assertions(4);
|
|
||||||
|
|
||||||
expect(action.type).toEqual(TYPES.BLUETOOTH_SYNC_REQUEST);
|
|
||||||
|
|
||||||
const promise = action.request(dispatch).then(() => {
|
|
||||||
expect(central.read).toBeCalled();
|
|
||||||
expect(dispatch).toBeCalledWith({ type: TYPES.BLUETOOTH_SYNC, payload: 'mockState' });
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,6 @@ jest.mock('../central', () => null);
|
||||||
test('actions', () => {
|
test('actions', () => {
|
||||||
expect(Object.keys(actions)).toEqual([
|
expect(Object.keys(actions)).toEqual([
|
||||||
'connectStore',
|
'connectStore',
|
||||||
'syncStore',
|
|
||||||
'syncState',
|
'syncState',
|
||||||
'sendAction',
|
'sendAction',
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -5,6 +5,7 @@ export default function Central(
|
||||||
const state = {
|
const state = {
|
||||||
server: null,
|
server: null,
|
||||||
characteristic: null,
|
characteristic: null,
|
||||||
|
message: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const connect = name => bluetooth
|
const connect = name => bluetooth
|
||||||
|
@ -22,7 +23,18 @@ export default function Central(
|
||||||
});
|
});
|
||||||
|
|
||||||
const handler = callback => state.characteristic.startNotifications().then(() => {
|
const handler = callback => state.characteristic.startNotifications().then(() => {
|
||||||
const listerner = event => callback(decode(event.target.value));
|
const listerner = (event) => {
|
||||||
|
const chunk = decode(event.target.value);
|
||||||
|
const message = `${state.message}${chunk}`;
|
||||||
|
|
||||||
|
if (message.startsWith('[[[') && message.endsWith(']]]')) {
|
||||||
|
const json = JSON.parse(message.slice(3, message.length - 3));
|
||||||
|
callback(json);
|
||||||
|
state.message = '';
|
||||||
|
} else {
|
||||||
|
state.message = message;
|
||||||
|
}
|
||||||
|
};
|
||||||
state.characteristic.addEventListener('characteristicvaluechanged', listerner);
|
state.characteristic.addEventListener('characteristicvaluechanged', listerner);
|
||||||
return listerner;
|
return listerner;
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Central from './central';
|
||||||
|
|
||||||
const encoder = {
|
const encoder = {
|
||||||
encode: jest.fn().mockReturnValue('mockEncode'),
|
encode: jest.fn().mockReturnValue('mockEncode'),
|
||||||
decode: jest.fn().mockReturnValue('mockDecode'),
|
decode: jest.fn().mockReturnValue('[[[{"mockDecode":"mockDecode"}]]]'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const characteristic = {
|
const characteristic = {
|
||||||
|
@ -66,7 +66,7 @@ test('Central: handler', () => {
|
||||||
.then((listerner) => {
|
.then((listerner) => {
|
||||||
expect(characteristic.startNotifications).toBeCalled();
|
expect(characteristic.startNotifications).toBeCalled();
|
||||||
listerner({ target: { value: 'mockEvent' } });
|
listerner({ target: { value: 'mockEvent' } });
|
||||||
expect(callback).toBeCalledWith('mockDecode');
|
expect(callback).toBeCalledWith({ mockDecode: 'mockDecode' });
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ test('Central: read', () => {
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
|
|
||||||
return expect(promise).resolves.toBe('mockDecode');
|
return expect(promise).resolves.toBe('[[[{"mockDecode":"mockDecode"}]]]');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Central: write', () => {
|
test('Central: write', () => {
|
||||||
|
|
|
@ -5,11 +5,11 @@ import MIDDLEWARE from './middleware';
|
||||||
import REDUCERS from './reducers';
|
import REDUCERS from './reducers';
|
||||||
import STORE from './store';
|
import STORE from './store';
|
||||||
|
|
||||||
const { connectStore, syncStore } = ACTIONS;
|
const { connectStore } = ACTIONS;
|
||||||
|
|
||||||
export const types = TYPES;
|
export const types = TYPES;
|
||||||
export const status = STATUS;
|
export const status = STATUS;
|
||||||
export const actions = { connectStore, syncStore };
|
export const actions = { connectStore };
|
||||||
export const reducers = REDUCERS;
|
export const reducers = REDUCERS;
|
||||||
export const middleware = MIDDLEWARE;
|
export const middleware = MIDDLEWARE;
|
||||||
export const createSyncStore = STORE;
|
export const createSyncStore = STORE;
|
||||||
|
|
|
@ -6,13 +6,11 @@ const { sendAction } = ACTIONS;
|
||||||
const {
|
const {
|
||||||
BLUETOOTH_CONNECT_REQUEST,
|
BLUETOOTH_CONNECT_REQUEST,
|
||||||
BLUETOOTH_SEND_REQUEST,
|
BLUETOOTH_SEND_REQUEST,
|
||||||
BLUETOOTH_SYNC_REQUEST,
|
|
||||||
} = TYPES;
|
} = TYPES;
|
||||||
|
|
||||||
const REQUESTS = [
|
const REQUESTS = [
|
||||||
BLUETOOTH_CONNECT_REQUEST,
|
BLUETOOTH_CONNECT_REQUEST,
|
||||||
BLUETOOTH_SEND_REQUEST,
|
BLUETOOTH_SEND_REQUEST,
|
||||||
BLUETOOTH_SYNC_REQUEST,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default (actions = []) => store => next => (action) => {
|
export default (actions = []) => store => next => (action) => {
|
||||||
|
|
Loading…
Reference in New Issue