From 48aa317c7ce196e674dbb35e64220b414b7e74a5 Mon Sep 17 00:00:00 2001 From: Thomas Nordquist Date: Wed, 2 Jan 2019 15:58:44 +0100 Subject: [PATCH] Improve data model & fix tests --- app/package-lock.json | 41 ++++++++ app/package.json | 4 +- app/src/App.tsx | 46 ++++++--- app/src/components/Sidebar.tsx | 8 -- app/src/components/TitleBar.tsx | 93 +++++++++++++++++++ app/src/components/Tree.tsx | 22 ++--- app/src/components/TreeNode.tsx | 8 +- app/src/components/ValueRenderer.tsx | 10 +- app/src/index.tsx | 15 ++- backend/src/Model/Edge.ts | 4 +- backend/src/Model/Message.ts | 3 + backend/src/Model/TreeNode.ts | 28 ++++-- backend/src/Model/TreeNodeFactory.ts | 35 ++----- backend/src/Model/index.ts | 17 ++-- backend/src/Model/spec/Edge.spec.ts | 38 ++++++-- backend/src/Model/spec/Tree.spec.ts | 8 +- .../src/Model/spec/TreeNode.findNode.spec.ts | 12 +-- backend/src/Model/spec/TreeNode.findNode.ts | 9 +- backend/src/Model/spec/TreeNode.spec.ts | 34 ++++--- .../src/Model/spec/TreeNodeFactory.spec.ts | 40 +++++--- backend/src/index.ts | 2 +- backend/tsconfig.json | 6 +- package.json | 4 +- 23 files changed, 335 insertions(+), 152 deletions(-) create mode 100644 app/src/components/TitleBar.tsx create mode 100644 backend/src/Model/Message.ts diff --git a/app/package-lock.json b/app/package-lock.json index 72e9142..ca11af8 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -46,6 +46,47 @@ "warning": "^4.0.1" } }, + "@material-ui/icons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-3.0.1.tgz", + "integrity": "sha512-1kNcxYiIT1x8iDPEAlgmKrfRTIV8UyK6fLVcZ9kMHIKGWft9I451V5mvSrbCjbf7MX1TbLWzZjph0aVCRf9MqQ==", + "dev": true, + "requires": { + "@babel/runtime": "7.0.0", + "recompose": "^0.29.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", + "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.12.0" + } + }, + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==", + "dev": true + }, + "recompose": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.29.0.tgz", + "integrity": "sha512-J/qLXNU4W+AeHCDR70ajW8eMd1uroqZaECTj6qqDLPMILz3y0EzpYlvrnxKB9DnqcngWrtGwjXY9JeXaW9kS1A==", + "dev": true, + "requires": { + "@babel/runtime": "^7.0.0", + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^2.3.1", + "react-lifecycles-compat": "^3.0.2", + "symbol-observable": "^1.0.4" + } + } + } + }, "@material-ui/utils": { "version": "3.0.0-alpha.2", "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-3.0.0-alpha.2.tgz", diff --git a/app/package.json b/app/package.json index 6a81f20..e0bd397 100644 --- a/app/package.json +++ b/app/package.json @@ -9,7 +9,9 @@ }, "author": "", "license": "ISC", - "devDependencies": {}, + "devDependencies": { + "@material-ui/icons": "^3.0.1" + }, "dependencies": { "@types/node": "^10.12.18", "awesome-typescript-loader": "^5.2.1", diff --git a/app/src/App.tsx b/app/src/App.tsx index d627f45..483d861 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -1,37 +1,55 @@ import * as React from "react"; import * as q from '../../backend/src/Model' -import { AppBar, Toolbar, Typography, InputBase } from '@material-ui/core'; - -import { Tree } from "./components/Tree"; +import { Tree } from "./components/Tree" +import TitleBar from "./components/TitleBar"; import { Sidebar } from "./components/Sidebar"; -import { withStyles } from '@material-ui/core/styles'; + +import { withTheme, Theme } from '@material-ui/core/styles'; class State { public selectedNode?: q.TreeNode | undefined } -export class App extends React.Component<{}, State> { +interface Props { + theme: Theme +} + +class App extends React.Component { constructor(props: any) { super(props); this.state = { selectedNode: undefined } + + console.log("asd", this.props) + this.theme = this.props.theme + this.styles = { + primaryText: { + backgroundColor: this.theme.palette.background.default, + padding: `${this.theme.spacing.unit}px ${this.theme.spacing.unit * 2}px`, + color: this.theme.palette.text.primary, + }, + primaryColor: { + backgroundColor: this.theme.palette.background.default, + //padding: `${this.theme.spacing.unit}px ${this.theme.spacing.unit * 2}px`, + color: this.theme.palette.common.white, + }, + } } + private theme: Theme + private styles: any + public render() { - return
- - - MQTT-Xplorer - - - + return
+ { this.setState({selectedNode: node}) console.log('did select', node) - }}/> - // + }} />
} } + +export default withTheme()(App) diff --git a/app/src/components/Sidebar.tsx b/app/src/components/Sidebar.tsx index 985f5b0..37d43d7 100644 --- a/app/src/components/Sidebar.tsx +++ b/app/src/components/Sidebar.tsx @@ -27,12 +27,6 @@ export class Sidebar extends React.Component { } } - private getStyle(): {[s: string]: any} { - return { - marginTop: '64px' - } - } - public componentWillReceiveProps(nextProps: Props) { this.props.node && this.props.node.removeListener('update', this.updateNode) nextProps.node && nextProps.node.on('update', this.updateNode) @@ -69,6 +63,4 @@ export class Sidebar extends React.Component {
} - - } diff --git a/app/src/components/TitleBar.tsx b/app/src/components/TitleBar.tsx new file mode 100644 index 0000000..1d5a2eb --- /dev/null +++ b/app/src/components/TitleBar.tsx @@ -0,0 +1,93 @@ +import * as React from "react"; +import * as q from '../../../backend/src/Model' + +import SearchIcon from '@material-ui/icons/Search'; + +import { AppBar, Toolbar, Typography, InputBase } from '@material-ui/core'; +import { withStyles, StyleRulesCallback } from '@material-ui/core/styles'; +import { fade } from '@material-ui/core/styles/colorManipulator'; + +const styles: StyleRulesCallback = (theme) => ({ + title: { + display: 'none', + [theme.breakpoints.up('sm')]: { + display: 'block', + }, + }, + search: { + position: 'relative', + borderRadius: theme.shape.borderRadius, + backgroundColor: fade(theme.palette.common.white, 0.15), + '&:hover': { + backgroundColor: fade(theme.palette.common.white, 0.25), + }, + marginRight: theme.spacing.unit * 2, + marginLeft: 0, + width: '100%', + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing.unit * 3, + width: 'auto', + }, + }, + searchIcon: { + width: theme.spacing.unit * 9, + height: '100%', + position: 'absolute', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + inputRoot: { + color: 'inherit', + width: '100%', + }, + inputInput: { + paddingTop: theme.spacing.unit, + paddingRight: theme.spacing.unit, + paddingBottom: theme.spacing.unit, + paddingLeft: theme.spacing.unit * 10, + transition: theme.transitions.create('width'), + width: '100%', + [theme.breakpoints.up('md')]: { + width: 200, + }, + } +}); + +interface Props { + classes: any +} + +class TitleBar extends React.Component { + constructor(props: any) { + super(props); + this.state = { + selectedNode: undefined + } + } + + public render() { + const { classes } = this.props; + + return + + MQTT-Xplorer +
+
+ +
+ +
+
+
+ } +} + +export default withStyles(styles)(TitleBar) diff --git a/app/src/components/Tree.tsx b/app/src/components/Tree.tsx index 972196e..824ebed 100644 --- a/app/src/components/Tree.tsx +++ b/app/src/components/Tree.tsx @@ -52,21 +52,15 @@ export class Tree extends React.Component { this.socket.removeAllListeners() } - private getStyle(): {[s: string]: any} { - return { - marginTop: '64px' - } - } - public render() { - return
- - this.renderDuration = ms} - /> + return
+ + this.renderDuration = ms} + />
; } diff --git a/app/src/components/TreeNode.tsx b/app/src/components/TreeNode.tsx index cd7eef4..6e15281 100644 --- a/app/src/components/TreeNode.tsx +++ b/app/src/components/TreeNode.tsx @@ -1,5 +1,4 @@ import * as React from "react"; -import * as io from 'socket.io-client'; import * as q from '../../../backend/src/Model' import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; @@ -82,7 +81,7 @@ export class TreeNode extends React.Component { if (edges.length > 0) { const listItems = edges - .map(edge => edge.node) + .map(edge => edge.target) .map(node => ) @@ -125,11 +124,11 @@ export class TreeNode extends React.Component { paddingLeft: '5px', display: 'inline-block', } - return this.props.treeNode.value + return this.props.treeNode.message ? this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)} - > = {this.props.treeNode.value.toString()} + > = {this.props.treeNode.message.toString()} : null } @@ -146,7 +145,6 @@ export class TreeNode extends React.Component { public render() { const nodeStyle: React.CSSProperties = { - //marginLeft: '10px', display: 'block', } diff --git a/app/src/components/ValueRenderer.tsx b/app/src/components/ValueRenderer.tsx index f898959..d5f68e3 100644 --- a/app/src/components/ValueRenderer.tsx +++ b/app/src/components/ValueRenderer.tsx @@ -32,21 +32,21 @@ export class ValueRenderer extends React.Component { public render() { let node = this.props.node - if (!node) { + if (!node || !node.message) { return null } let json try { - json = JSON.parse(node.value) + json = JSON.parse(node.message.value) } catch(error) { - return this.renderRawValue(node.value) + return this.renderRawValue(node.message.value) } if (typeof json === 'string') { - return this.renderRawValue(node.value) + return this.renderRawValue(node.message.value) } else if (typeof json === 'number') { - return this.renderRawValue(node.value) + return this.renderRawValue(node.message.value) } else { return } diff --git a/app/src/index.tsx b/app/src/index.tsx index 538c953..e8d103f 100644 --- a/app/src/index.tsx +++ b/app/src/index.tsx @@ -1,10 +1,21 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; -import { App } from './App' + +import App from './App' +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; + +const theme = createMuiTheme({ + palette: { + type: 'dark', // Switching the dark mode on is a single property value change. + }, + typography: { useNextVariants: true }, +}); declare var document: any ReactDOM.render( - , + + + , document.getElementById("example") ); diff --git a/backend/src/Model/Edge.ts b/backend/src/Model/Edge.ts index 9b21d29..42d85e7 100644 --- a/backend/src/Model/Edge.ts +++ b/backend/src/Model/Edge.ts @@ -4,7 +4,7 @@ const sha1 = require('sha1') export class Edge implements Hashable { public name: string - public node!: TreeNode + public target!: TreeNode public source?: TreeNode | undefined private cachedHash?: string @@ -13,7 +13,7 @@ export class Edge implements Hashable { } public edges() { - return this.node ? Object.values(this.node.edges) : [] + return this.target ? Object.values(this.target.edges) : [] } public hash(): string { diff --git a/backend/src/Model/Message.ts b/backend/src/Model/Message.ts new file mode 100644 index 0000000..a97a67d --- /dev/null +++ b/backend/src/Model/Message.ts @@ -0,0 +1,3 @@ +export interface Message { + value?: any | undefined +} diff --git a/backend/src/Model/TreeNode.ts b/backend/src/Model/TreeNode.ts index 48a3cb0..e571bdf 100644 --- a/backend/src/Model/TreeNode.ts +++ b/backend/src/Model/TreeNode.ts @@ -1,20 +1,27 @@ -import { Edge } from './' +import { Edge, Message } from './' import { EventEmitter } from 'events' export class TreeNode extends EventEmitter { public sourceEdge?: Edge - public value?: any | null + public message?: Message public edges: {[s: string]: Edge} = {} public collapsed = false + public messages: number = 0 - constructor(sourceEdge?: Edge, value?: any) { + constructor(sourceEdge?: Edge, message?: Message) { super() if (sourceEdge) { this.sourceEdge = sourceEdge - sourceEdge.node = this + sourceEdge.target = this } - this.value = value + + this.setMessage(message) + } + + public setMessage(value: any) { + this.message = value + this.messages += 1 } public hash(): string { @@ -22,7 +29,7 @@ export class TreeNode extends EventEmitter { } public firstNode(): TreeNode { - return this.sourceEdge ? this.sourceEdge.firstEdge().node : this + return this.sourceEdge && this.sourceEdge.source ? this.sourceEdge.source.firstNode() : this } public path(): string { @@ -52,9 +59,10 @@ export class TreeNode extends EventEmitter { } public updateWithNode(node: TreeNode) { - if (node.value !== undefined) { - this.value = node.value + if (node.message) { + this.setMessage(node.message) } + this.mergeEdges(node) this.emit('update') } @@ -65,7 +73,7 @@ export class TreeNode extends EventEmitter { } return Object.values(this.edges) - .map(e => e.node.leafes()) + .map(e => e.target.leafes()) .reduce((a, b) => a.concat(b), []) } @@ -74,7 +82,7 @@ export class TreeNode extends EventEmitter { for (let edgeKey of edgeKeys) { let matchingEdge = this.edges[edgeKey] if (matchingEdge) { - matchingEdge.node.updateWithNode(node.edges[edgeKey].node) + matchingEdge.target.updateWithNode(node.edges[edgeKey].target) } else { this.addEdge(node.edges[edgeKey]) } diff --git a/backend/src/Model/TreeNodeFactory.ts b/backend/src/Model/TreeNodeFactory.ts index 3255f90..7fbb75d 100644 --- a/backend/src/Model/TreeNodeFactory.ts +++ b/backend/src/Model/TreeNodeFactory.ts @@ -1,32 +1,17 @@ -import { Edge, Tree, TreeNode } from './' +import { Edge, Message, Tree, TreeNode } from './' export abstract class TreeNodeFactory { public static fromEdgesAndValue(edgeNames: Array, value: any): TreeNode { - const lastEdgeIndex = edgeNames.length - 1 - var edges = edgeNames - .map((name, idx) => { - const edge = new Edge(name) - - const nodeValue = lastEdgeIndex == idx ? value : undefined - const node = new TreeNode(edge, nodeValue) - edge.node = node - return edge - }) - - let reversed: Array = edges.reverse() - let previous: Edge | undefined = undefined; - for (let edge of reversed) { - if (previous) { - edge.node.addEdge(previous) - } - previous = edge; + let currentNode: TreeNode = new Tree() + for (const edgeName of edgeNames) { + const edge = new Edge(edgeName) + let newNode = new TreeNode(edge) + edge.target = newNode + currentNode.addEdge(edge) + currentNode = newNode } - let leaf = reversed[0].node - - let sourceTree = new Tree() - sourceTree.updateWithNode(leaf.firstNode()) - - return leaf + currentNode.setMessage({ value: value }) + return currentNode } } diff --git a/backend/src/Model/index.ts b/backend/src/Model/index.ts index 40b5756..106de21 100644 --- a/backend/src/Model/index.ts +++ b/backend/src/Model/index.ts @@ -1,10 +1,7 @@ -import { Edge } from './Edge' -import { TreeNode } from './TreeNode' -import { TreeNodeFactory } from './TreeNodeFactory' -import { Tree } from './Tree' -import { TopicProperties } from './TopicProperties' -import { Hashable } from './Hashable' - -export { - Edge, TreeNode, TreeNodeFactory, Tree, TopicProperties, Hashable -} +export { Edge } from './Edge' +export { TreeNode } from './TreeNode' +export { Message } from './Message' +export { TreeNodeFactory } from './TreeNodeFactory' +export { Tree } from './Tree' +export { TopicProperties } from './TopicProperties' +export { Hashable } from './Hashable' diff --git a/backend/src/Model/spec/Edge.spec.ts b/backend/src/Model/spec/Edge.spec.ts index bb399e9..da7a754 100644 --- a/backend/src/Model/spec/Edge.spec.ts +++ b/backend/src/Model/spec/Edge.spec.ts @@ -1,14 +1,27 @@ -import { Edge, TreeNode } from '../' -import { expect } from 'chai'; -import 'mocha'; +import { Edge, TreeNode, TreeNodeFactory } from '../' +import { expect } from 'chai' +import 'mocha' describe('Edge', () => { - it('should contain a name', () => { let e = new Edge('foo') expect(e.name).to.equal('foo') }); + it('firstEdge should retireve the first edge', () => { + const topics = 'foo/bar/baz'.split('/') + const leaf = TreeNodeFactory.fromEdgesAndValue(topics, 5) + let bazEdge = leaf.sourceEdge + + if (!bazEdge) { + expect.fail('should not be undefined') + return; + } + + expect(bazEdge.name).to.eq('baz') + expect(bazEdge.firstEdge().name).to.eq('foo') + }); + it('hash should not be empty', () => { let e = new Edge('bar') expect(e.hash().length).to.be.gt(0) @@ -20,12 +33,17 @@ describe('Edge', () => { expect(e.hash()).to.eq(previousHash) }); - it('hash should change when parent is present', () => { - let foo = new Edge('foo') - let bar = new Edge('bar') + it('hash should include change if parents are different', () => { + const topics1 = 'foo/bar/baz'.split('/') + const bazEdge1 = TreeNodeFactory.fromEdgesAndValue(topics1, 5).sourceEdge - var previousHash = bar.hash() - bar.source = new TreeNode(foo, undefined) - expect(bar.hash()).to.not.eq(previousHash) + const topics2 = 'foo/foo/baz'.split('/') + const bazEdge2 = TreeNodeFactory.fromEdgesAndValue(topics2, 5).sourceEdge + + if (!bazEdge1 || !bazEdge2) { + throw Error('should not happen') + } + + expect(bazEdge1.hash()).to.not.eq(bazEdge2.hash()) }); }); diff --git a/backend/src/Model/spec/Tree.spec.ts b/backend/src/Model/spec/Tree.spec.ts index 1a50736..cee4ac9 100644 --- a/backend/src/Model/spec/Tree.spec.ts +++ b/backend/src/Model/spec/Tree.spec.ts @@ -1,6 +1,6 @@ -import { Edge, Tree, TreeNode, TreeNodeFactory } from '../' -import { expect } from 'chai'; -import 'mocha'; +import { Tree, TreeNodeFactory } from '../' +import { expect } from 'chai' +import 'mocha' import './TreeNode.findNode' @@ -10,7 +10,7 @@ describe('Tree', () => { const topics = 'foo/bar'.split('/') const leaf = TreeNodeFactory.fromEdgesAndValue(topics, 3) - debugger + tree.updateWithNode(leaf.firstNode()) let expectedNode = tree.findNode('foo/bar') expect(expectedNode).to.eq(leaf) diff --git a/backend/src/Model/spec/TreeNode.findNode.spec.ts b/backend/src/Model/spec/TreeNode.findNode.spec.ts index 59d7684..e5ab32f 100644 --- a/backend/src/Model/spec/TreeNode.findNode.spec.ts +++ b/backend/src/Model/spec/TreeNode.findNode.spec.ts @@ -1,6 +1,6 @@ -import { TreeNode, TreeNodeFactory } from '../' -import { expect } from 'chai'; -import 'mocha'; +import { TreeNodeFactory } from '../' +import { expect } from 'chai' +import 'mocha' import './TreeNode.findNode' @@ -10,20 +10,20 @@ describe('TreeNode.findNode', () => { const leaf = TreeNodeFactory.fromEdgesAndValue(topics, 5) let root = leaf.firstNode() - expect(root.sourceEdge.name).to.eq('') + expect(root.sourceEdge).to.eq(undefined) let barNode = root.findNode('foo/bar') if (!barNode) { expect.fail('did not find node') return } - expect(barNode.sourceEdge.name).to.eq('bar') + expect(barNode.sourceEdge && barNode.sourceEdge.name).to.eq('bar') let bazNode = root.findNode('foo/bar/baz') if (!bazNode) { expect.fail('did not find node') return } - expect(bazNode.sourceEdge.name).to.eq('baz') + expect(bazNode.sourceEdge && bazNode.sourceEdge.name).to.eq('baz') }) }) diff --git a/backend/src/Model/spec/TreeNode.findNode.ts b/backend/src/Model/spec/TreeNode.findNode.ts index 180303c..9166e06 100644 --- a/backend/src/Model/spec/TreeNode.findNode.ts +++ b/backend/src/Model/spec/TreeNode.findNode.ts @@ -1,6 +1,5 @@ -import { TreeNode } from '../' - -declare module "../" { +import { TreeNode } from '../' +declare module '../' { interface TreeNode { findNode(path: String): TreeNode | undefined } @@ -11,9 +10,9 @@ TreeNode.prototype.findNode = function(path: String): TreeNode | undefined { let edge = this.edges[topics[0]] let remainingTopics = topics.slice(1, topics.length) if (edge && remainingTopics.length === 0) { - return edge.node + return edge.target } else if (edge) { - return edge.node.findNode(remainingTopics.join('/')) + return edge.target.findNode(remainingTopics.join('/')) } return undefined diff --git a/backend/src/Model/spec/TreeNode.spec.ts b/backend/src/Model/spec/TreeNode.spec.ts index 9cd2993..0e14111 100644 --- a/backend/src/Model/spec/TreeNode.spec.ts +++ b/backend/src/Model/spec/TreeNode.spec.ts @@ -1,36 +1,44 @@ -import { Edge, Tree, TreeNode, TreeNodeFactory } from '../' -import { expect } from 'chai'; -import 'mocha'; - +import { TreeNodeFactory } from '../' import './TreeNode.findNode' +import { expect } from 'chai' +import 'mocha' describe('TreeNode', () => { + it('firstNode should retrieve first node', () => { + const topics = 'foo/bar'.split('/') + const leaf = TreeNodeFactory.fromEdgesAndValue(topics, 3) + + expect(leaf.firstNode().edges['foo']).to.not.eq(undefined) + }) + it('updateWithNode should update value', () => { const topics = 'foo/bar'.split('/') const leaf = TreeNodeFactory.fromEdgesAndValue(topics, 3) - expect(leaf.value).to.eq(3) + expect(leaf.message && leaf.message.value).to.eq(3) const updateLeave = TreeNodeFactory.fromEdgesAndValue(topics, 5) - leaf.firstNode().updateWithNode(updateLeave.firstNode()) - expect(leaf.firstNode().sourceEdge.name).to.eq(updateLeave.firstNode().sourceEdge.name) - expect(leaf.value).to.eq(5) + let root = leaf.firstNode() + root.updateWithNode(updateLeave.firstNode()) + + expect(root.sourceEdge).to.eq(undefined) + expect(leaf.message && leaf.message.value).to.eq(5) }) it('updateWithNode should update intermediate nodes', () => { const topics1 = 'foo/bar/baz'.split('/') const leaf = TreeNodeFactory.fromEdgesAndValue(topics1, 3) - expect(leaf.value).to.eq(3) + expect(leaf.message && leaf.message.value).to.eq(3) const topics2 = 'foo/bar'.split('/') const updateLeave = TreeNodeFactory.fromEdgesAndValue(topics2, 5) leaf.firstNode().updateWithNode(updateLeave.firstNode()) let barNode = leaf.firstNode().findNode('foo/bar') - expect(barNode && barNode.sourceEdge.name).to.eq('bar') - expect(barNode && barNode.value).to.eq(5) + expect(barNode && barNode.sourceEdge && barNode.sourceEdge.name).to.eq('bar') + expect(barNode && barNode.message && barNode.message.value).to.eq(5) - expect(leaf.sourceEdge.name).to.eq('baz') - expect(leaf.value).to.eq(3) + expect(leaf.sourceEdge && leaf.sourceEdge.name).to.eq('baz') + expect(leaf.message && leaf.message.value).to.eq(3) }) it('updateWithNode should add nodes to the tree', () => { diff --git a/backend/src/Model/spec/TreeNodeFactory.spec.ts b/backend/src/Model/spec/TreeNodeFactory.spec.ts index 056ea1c..b6d53a4 100644 --- a/backend/src/Model/spec/TreeNodeFactory.spec.ts +++ b/backend/src/Model/spec/TreeNodeFactory.spec.ts @@ -1,24 +1,33 @@ -import { Edge, TreeNode, TreeNodeFactory } from '../' -import { expect } from 'chai'; -import 'mocha'; +import { TreeNodeFactory } from '../' +import { expect } from 'chai' +import 'mocha' +import './TreeNode.findNode' describe('TreeNodeFactory', () => { + it('root node must not have a sourceEdge', () => { + let topic = 'foo/bar' + let edges = topic.split('/') + let leaf = TreeNodeFactory.fromEdgesAndValue(edges, 5) + + expect(leaf.firstNode().sourceEdge).to.eq(undefined) + }); + it('should create node', () => { let topic = 'foo/bar' let edges = topic.split('/') let node = TreeNodeFactory.fromEdgesAndValue(edges, 5) - expect(node).to.not.eq(undefined) - expect(node.sourceEdge.name).to.eq('bar') - expect(node.value).to.eq(5) - - if (!node.sourceEdge.source) { + if (!node.sourceEdge || !node.sourceEdge.source || !node.message) { expect.fail('should not happen') return } - let foo = node.sourceEdge.source.sourceEdge - expect(foo.name).to.eq('foo') + expect(node).to.not.eq(undefined) + expect(node.sourceEdge.name).to.eq('bar') + expect(node.message.value).to.eq(5) + + let foo = node.firstNode().findNode('foo') + expect(foo && foo.sourceEdge && foo.sourceEdge.name).to.eq('foo') }); it('node should contain edges in order', () => { @@ -26,18 +35,23 @@ describe('TreeNodeFactory', () => { let edges = topic.split('/') let node = TreeNodeFactory.fromEdgesAndValue(edges, 5) - expect(node.value).to.eq(5) + if (!node.sourceEdge || !node.sourceEdge.source || !node.message) { + expect.fail('should not happen') + return + } + + expect(node.message.value).to.eq(5) expect(node.sourceEdge.name).to.eq('baz') const barNode = node.sourceEdge.source - if (!barNode) { + if (!barNode || !barNode.sourceEdge) { expect.fail('should not fail') return } expect(barNode.sourceEdge.name).to.eq('bar') const fooNode = barNode.sourceEdge.source - if (!fooNode) { + if (!fooNode || !fooNode.sourceEdge) { expect.fail('should not fail') return } diff --git a/backend/src/index.ts b/backend/src/index.ts index 5296f04..0f25878 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -16,13 +16,13 @@ io.on('connection', client => { a.forEach(b => { io.emit('message', b) }) - client.on('event', data => { /* … */ }); client.on('disconnect', () => { /* … */ }); }); server.listen(3000); let state = dataSource.connect(options) dataSource.onMessage((topic: string, payload: Buffer) => { + a.push({ topic, payload: payload.toString('base64') }) if (payload.length > 10000) { payload = payload.slice(0, 10000) } diff --git a/backend/tsconfig.json b/backend/tsconfig.json index d0a28ac..695a9d5 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -12,8 +12,8 @@ "src/**/*.ts" ], "exclude": [ - "app", - "node_modules", - "**/*.spec.ts" + "app", + "node_modules", + "src/**/*.spec.ts" ] } diff --git a/package.json b/package.json index 7a60a61..df1b1d3 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "main": "electron.js", "scripts": { "start": "electron .", - "release": "./release.sh" + "test": "npm run test-backend", + "test-backend": "cd backend && npm run test && cd ..", + "release": "npm run test && ./release.sh" }, "build": { "appId": "mq-explorer",