Compare commits

..
Author SHA1 Message Date
Thomas Nordquist 6305971eeb Enable vnc and change mqtt host 2019-01-30 12:03:36 +01:00
Thomas Nordquist bd4f2291d5 Add test scenarios 2019-01-30 11:37:41 +01:00
Thomas Nordquist 3242b8643b Fix argument name 2019-01-29 23:06:27 +01:00
Thomas Nordquist 1adfda9cf9 Fix writeText 2019-01-29 23:03:09 +01:00
Thomas Nordquist 3814e79520 fix upload 2019-01-29 22:56:40 +01:00
Thomas Nordquist 413d1e3081 Fix types 2019-01-29 22:40:10 +01:00
Thomas Nordquist 09d2deb09f Fix exit codes 2019-01-29 22:17:28 +01:00
Thomas Nordquist d547907b09 Fix type error 2019-01-29 21:30:51 +01:00
Thomas Nordquist 5d01aea903 Change writeText behavior 2019-01-29 20:14:21 +01:00
Thomas Nordquist 1f2cc9a99c Update dockerfile 2019-01-29 19:23:30 +01:00
Thomas Nordquist 4cdee7fea9 Fix dockerfile 2019-01-29 19:12:28 +01:00
Thomas Nordquist 3c412a7e2a Fix asset uploader 2019-01-29 19:10:05 +01:00
Thomas Nordquist 0e510cd9ce Fix travis docker build 2019-01-29 19:09:13 +01:00
Thomas Nordquist 6cd1ef2267 Add ui test docker support 2019-01-29 19:01:13 +01:00
Thomas Nordquist d23df81220 Change text input method 2019-01-29 18:27:57 +01:00
Thomas Nordquist 9eda1b8dbf Upload video assets 2019-01-29 17:36:44 +01:00
Thomas Nordquist e8ff825e7d Upload video assets 2019-01-29 16:17:09 +01:00
Thomas Nordquist e714997ca2 Move shell scripts 2019-01-29 14:37:39 +01:00
Thomas Nordquist 3a78b1a75d Trying xenial 2019-01-29 13:27:08 +01:00
Thomas Nordquist fcce31cf0a Fix ffmpeg travis source 2019-01-29 12:58:46 +01:00
Thomas Nordquist 2af4554d9a Prepare travis is tests 2019-01-29 12:10:44 +01:00
Thomas Nordquist 0114d938bd Fix travis build 2019-01-29 12:01:39 +01:00
Thomas Nordquist 598e0e64fa Add video processing 2019-01-29 11:48:59 +01:00
Thomas Nordquist e32e5f57f3 Prepare automatic ui tests 2019-01-29 10:25:09 +01:00
Thomas Nordquist d30aa7c645 Fix electron binary for linux ui tests 2019-01-28 23:43:24 +01:00
Thomas Nordquist 509fa2e696 Remove recursive yarn install 2019-01-28 22:56:33 +01:00
Thomas Nordquist e813ff6c9f Clean dependecies 2019-01-28 22:50:46 +01:00
Thomas Nordquist d11337fda2 Add UI tests 2019-01-28 22:40:52 +01:00
Thomas Nordquist 2822f98103 Clear package resolution 2019-01-28 14:47:10 +01:00
Thomas Nordquist f4d3be36b6 Fix update reference 2019-01-27 22:24:38 +01:00
Thomas Nordquist 5776bab4e1 Update readme 2019-01-27 20:38:00 +01:00
Thomas Nordquist 13cea217dd Use yarn over npm 2019-01-27 01:48:52 +01:00
Thomas Nordquist e0d7f45ab8 Fix crash due to conneciton error
Error report: b6ef904c-b670-41e8-b586-fab52a31006d
2019-01-27 00:13:06 +01:00
Thomas Nordquist 809dbcbd43 Minimize build size 2019-01-26 22:11:03 +01:00
Thomas Nordquist 3f50120a9f Use github releases for update notifications 2019-01-26 17:31:19 +01:00
Thomas Nordquist da37b708b4 Fix Readme 2019-01-26 17:26:03 +01:00
Thomas Nordquist 1dbe82df2d Delete CNAME 2019-01-26 17:19:35 +01:00
Thomas Nordquist aedbdd3e45 Create CNAME 2019-01-26 17:19:03 +01:00
Thomas Nordquist 26312445c8 Set theme jekyll-theme-architect 2019-01-26 17:18:09 +01:00
Thomas Nordquist cb26f08ffd Update Readme 2019-01-26 00:38:24 +01:00
Thomas Nordquist d11c7ae066 Prepare release 2019-01-26 00:06:32 +01:00
Thomas Nordquist 4e336c91b6 Don't throw on subscription error, let frontend handle the rror 2019-01-26 00:03:57 +01:00
Thomas Nordquist b706d561aa Fix json preview alignment 2019-01-26 00:00:46 +01:00
Thomas Nordquist 904d3c77cd Fix side panel shift 2019-01-25 23:55:10 +01:00
Thomas Nordquist d072de06ed Update readme 2019-01-25 21:15:07 +01:00
Thomas Nordquist 697b57a141 Debounce select topic action 2019-01-25 20:57:20 +01:00
Thomas Nordquist 98369b278a Update dependecies & release 2019-01-25 20:17:05 +01:00
Thomas Nordquist 033e4945d0 Show broker statistics only if there are any 2019-01-25 20:02:45 +01:00
Thomas Nordquist ac498a948e Fix shield 2019-01-25 20:02:09 +01:00
Thomas Nordquist d286611825 Track app uptime 2019-01-25 20:01:44 +01:00
Thomas Nordquist 763f301bfd Fix side by side view width 2019-01-25 19:24:41 +01:00
Thomas Nordquist d92dfde167 Prepare release 2019-01-25 18:07:42 +01:00
Thomas Nordquist d8844aca89 Fix bugtracking 2019-01-25 18:07:13 +01:00
Thomas Nordquist 55b5233ee4 Clean up and fix build 2019-01-25 18:06:58 +01:00
53 changed files with 11035 additions and 13803 deletions
+15 -8
View File
@@ -1,7 +1,7 @@
language: node_js
services:
- docker
- xvfb
cache:
directories:
@@ -17,13 +17,20 @@ os:
- linux
- osx
dist: xenial
services:
- docker
install:
- npm install
- yarn install
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker build docker --tag uitest; fi;
script:
- npm run build
- npm run test
- if [[ "$TRAVIS_TAG" != "" ]]; then npm run prepare-release; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$TRAVIS_TAG" != "" ]]; then npm run package -- linux; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$TRAVIS_TAG" != "" ]]; then npm run package -- mac; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$TRAVIS_TAG" != "" ]]; then npm run package -- win; fi
- yarn run build
- yarn test
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker run -e GH_TOKEN=$GH_TOKEN -e GIT_TAG=$TRAVIS_TAG --rm -v `pwd`:/app uitest sh -c "cd app && docker/testMounted.sh"; fi
- if [[ "$TRAVIS_TAG" != "" ]]; then yarn run prepare-release; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$TRAVIS_TAG" != "" ]]; then yarn run package -- linux; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$TRAVIS_TAG" != "" ]]; then yarn run package -- mac; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$TRAVIS_TAG" != "" ]]; then yarn run package -- win; fi
+35 -13
View File
@@ -1,31 +1,27 @@
# MQTT-Explorer
[![Downloads](https://github-basic-badges.herokuapp.com/release/thomasnordquist/MQTT-Explorer.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
[![Downloads](https://img.shields.io/github/release/thomasnordquist/mqtt-explorer.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
[![Downloads](https://img.shields.io/github/downloads/thomasnordquist/mqtt-explorer/total.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
[![Build Status](https://travis-ci.org/thomasnordquist/MQTT-Explorer.svg?branch=master)](https://travis-ci.org/thomasnordquist/MQTT-Explorer)
### Version 0.1.0
### Version 0.1.3
See the whole picture of your message queue.
The perfect tool to integrate new services, IoT devices in your network.
This application subscribes to all topics on your MQTT-Server and displays your message queue hierarchy, allowing you to drill-down to the topics that are of interest.
## Download
The app is prebuilt for Windows ([portable](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.0/MQTT-Explorer-0.1.0.exe), [installer](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.0/MQTT-Explorer-Setup-0.1.0.exe)), Linux ([AppImage](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.0/MQTT-Explorer-0.1.0-x86_64.AppImage)) and Mac ([dmg](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.0/MQTT-Explorer-0.1.0.dmg)).
The app is prebuilt for Windows ([portable](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.3/MQTT-Explorer-0.1.3.exe), [installer](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.3/MQTT-Explorer-Setup-0.1.3.exe)), Linux ([AppImage](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.3/MQTT-Explorer-0.1.3-x86_64.AppImage)) and Mac ([dmg](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.3/MQTT-Explorer-0.1.3.dmg)).
<center>
| | | |
| Platform | | Downloads |
|:----------|:-------------:|:------:|
| ![windows](https://user-images.githubusercontent.com/7721625/51445407-b4172080-1d04-11e9-8c70-d8413d1d6d8b.png) | Windows | [portable](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.0&#x2F;MQTT-Explorer-0.1.0.exe), [installer](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.0&#x2F;MQTT-Explorer-Setup-0.1.0.exe) |
| ![linux](https://user-images.githubusercontent.com/7721625/51445392-947ff800-1d04-11e9-8c7f-a30efb755651.png) | Linux | [AppImage](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.0&#x2F;MQTT-Explorer-0.1.0-x86_64.AppImage)<br>*Run AppImage:<br>Make it executable and double-click it.* |
| ![mac](https://user-images.githubusercontent.com/7721625/51445390-921d9e00-1d04-11e9-8339-351469ef20ae.png) | Mac | [dmg](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.0&#x2F;MQTT-Explorer-0.1.0.dmg) |
</center>
| ![windows](https://user-images.githubusercontent.com/7721625/51445407-b4172080-1d04-11e9-8c70-d8413d1d6d8b.png) | Windows | [portable](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.3&#x2F;MQTT-Explorer-0.1.3.exe), [installer](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.3&#x2F;MQTT-Explorer-Setup-0.1.3.exe) |
| ![linux](https://user-images.githubusercontent.com/7721625/51445392-947ff800-1d04-11e9-8c7f-a30efb755651.png) | Linux | [AppImage](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.3&#x2F;MQTT-Explorer-0.1.3-x86_64.AppImage)<br>*Run AppImage:<br>Make it executable and double-click it.* |
| ![mac](https://user-images.githubusercontent.com/7721625/51445390-921d9e00-1d04-11e9-8339-351469ef20ae.png) | Mac | [dmg](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.3&#x2F;MQTT-Explorer-0.1.3.dmg) |
More architectures and package types: [Downloads](https://github.com/thomasnordquist/MQTT-Explorer/releases)
![screen1](https://user-images.githubusercontent.com/7721625/51441563-41418180-1cd3-11e9-893b-c22f6a6c3695.png)
![screen1](https://user-images.githubusercontent.com/7721625/51770198-6c6a0d80-20e5-11e9-94d5-a0174634253c.png)
![screen2](https://user-images.githubusercontent.com/7721625/51441470-59fd6780-1cd2-11e9-925f-bc491027300c.png)
![screen2](https://user-images.githubusercontent.com/7721625/51770345-c7036980-20e5-11e9-94dc-5d6fa9dbf86b.png)
## Develop
@@ -37,5 +33,31 @@ Start with `npm run start`
The `app` directory contains all the rendering logic, the `backend` directory currently contains the models, tests, connection management.
## Telemetry
The App sends telemetry and error reports, this enables me to quickly react on bugs/errors I produced.
This is a difficutlt task since this App runs on three different operating systems and architectures.
It basically sends: app version, processor architecture, operating system, used memory, user interactions and error stacks.
This greatly helps to improve the software quality and reliability.
No data about you or your data is send or stored.
Even thoug the data is purely technical, an option to disable telemetry is planned. [#52](https://github.com/thomasnordquist/MQTT-Explorer/issues/52)
Example telemetry:
```javascript
{ system: { arch: 'x64', platform: 'darwin' },
appVersion: '0.0.7',
events: { HELLO_EVENT: [ 1547714886134 ] },
now: 1547714886135,
transactionId: '1767d251-f492-4f2c-aa62-88add3acc26b' }
{ errors:
[ { time: 1547714887921,
message: 'He\'s dead Jim!',
stack:
'Error: He\'s dead Jim!\n at ./src/tracking.ts.exports.default (./mqtt-explorer/app/build/bundle.js:142765:11)\n at new Promise (<anonymous>)\n at Object../src/tracking.ts (./mqtt-explorer/app/build/bundle.js:142764:1)\n at __webpack_require__ (./mqtt-explorer/app/build/bundle.js:20:30)\n at Object../src/index.tsx (./mqtt-explorer/app/build/bundle.js:142618:1)\n at __webpack_require__ (./mqtt-explorer/app/build/bundle.js:20:30)\n at ../backend/node_modules/charenc/charenc.js.charenc.utf8.stringToBytes (./mqtt-explorer/app/build/bundle.js:84:18)\n at ./mqtt-explorer/app/build/bundle.js:87:10' } ],
now: 1547714887921,
transactionId: '53bf9aac-e695-40cc-9a81-b1cf3398843d' }
```
## License
Not yet decided which license exactly, but the basic idea is: "You may do whatever you want with this tool, except sell it."
+30 -8
View File
@@ -1,5 +1,5 @@
# MQTT-Explorer
[![Downloads](https://github-basic-badges.herokuapp.com/release/thomasnordquist/MQTT-Explorer.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
[![Downloads](https://img.shields.io/github/release/thomasnordquist/mqtt-explorer.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
[![Downloads](https://img.shields.io/github/downloads/thomasnordquist/mqtt-explorer/total.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
[![Build Status](https://travis-ci.org/thomasnordquist/MQTT-Explorer.svg?branch=master)](https://travis-ci.org/thomasnordquist/MQTT-Explorer)
@@ -11,21 +11,17 @@ This application subscribes to all topics on your MQTT-Server and displays your
## Download
The app is prebuilt for Windows ({{windowsTargets}}), Linux ({{linuxTargets}}) and Mac ({{macTargets}}).
<center>
| | | |
| Platform | | Downloads |
|:----------|:-------------:|:------:|
| ![windows](https://user-images.githubusercontent.com/7721625/51445407-b4172080-1d04-11e9-8c70-d8413d1d6d8b.png) | Windows | {{windowsTargets}} |
| ![linux](https://user-images.githubusercontent.com/7721625/51445392-947ff800-1d04-11e9-8c7f-a30efb755651.png) | Linux | {{linuxTargets}}<br>*Run AppImage:<br>Make it executable and double-click it.* |
| ![mac](https://user-images.githubusercontent.com/7721625/51445390-921d9e00-1d04-11e9-8339-351469ef20ae.png) | Mac | {{macTargets}} |
</center>
More architectures and package types: [Downloads](https://github.com/thomasnordquist/MQTT-Explorer/releases)
![screen1](https://user-images.githubusercontent.com/7721625/51441563-41418180-1cd3-11e9-893b-c22f6a6c3695.png)
![screen1](https://user-images.githubusercontent.com/7721625/51770198-6c6a0d80-20e5-11e9-94d5-a0174634253c.png)
![screen2](https://user-images.githubusercontent.com/7721625/51441470-59fd6780-1cd2-11e9-925f-bc491027300c.png)
![screen2](https://user-images.githubusercontent.com/7721625/51770345-c7036980-20e5-11e9-94dc-5d6fa9dbf86b.png)
## Develop
@@ -37,5 +33,31 @@ Start with `npm run start`
The `app` directory contains all the rendering logic, the `backend` directory currently contains the models, tests, connection management.
## Telemetry
The App sends telemetry and error reports, this enables me to quickly react on bugs/errors I produced.
This is a difficutlt task since this App runs on three different operating systems and architectures.
It basically sends: app version, processor architecture, operating system, used memory, user interactions and error stacks.
This greatly helps to improve the software quality and reliability.
No data about you or your data is send or stored.
Even thoug the data is purely technical, an option to disable telemetry is planned. [#52](https://github.com/thomasnordquist/MQTT-Explorer/issues/52)
Example telemetry:
```javascript
{ system: { arch: 'x64', platform: 'darwin' },
appVersion: '0.0.7',
events: { HELLO_EVENT: [ 1547714886134 ] },
now: 1547714886135,
transactionId: '1767d251-f492-4f2c-aa62-88add3acc26b' }
{ errors:
[ { time: 1547714887921,
message: 'He\'s dead Jim!',
stack:
'Error: He\'s dead Jim!\n at ./src/tracking.ts.exports.default (./mqtt-explorer/app/build/bundle.js:142765:11)\n at new Promise (<anonymous>)\n at Object../src/tracking.ts (./mqtt-explorer/app/build/bundle.js:142764:1)\n at __webpack_require__ (./mqtt-explorer/app/build/bundle.js:20:30)\n at Object../src/index.tsx (./mqtt-explorer/app/build/bundle.js:142618:1)\n at __webpack_require__ (./mqtt-explorer/app/build/bundle.js:20:30)\n at ../backend/node_modules/charenc/charenc.js.charenc.utf8.stringToBytes (./mqtt-explorer/app/build/bundle.js:84:18)\n at ./mqtt-explorer/app/build/bundle.js:87:10' } ],
now: 1547714887921,
transactionId: '53bf9aac-e695-40cc-9a81-b1cf3398843d' }
```
## License
Not yet decided which license exactly, but the basic idea is: "You may do whatever you want with this tool, except sell it."
+1
View File
@@ -0,0 +1 @@
theme: jekyll-theme-architect
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<title>MQTT-Explorer</title>
<script src="./build/bugtracking.bundle.js"></script>
<script src="./bugtracking.bundle.js"></script>
<style>
body, html {
-8424
View File
File diff suppressed because it is too large Load Diff
+19 -19
View File
@@ -9,49 +9,49 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@material-ui/core": "^3.9.0",
"@material-ui/icons": "^3.0.1",
"@material-ui/styles": "^3.0.0-alpha.8",
"@types/node": "^10.12.18",
"@types/react": "^16.7.18",
"@types/react-dom": "^16.0.11",
"@types/react-redux": "^6.0.12",
"css-loader": "^2.1.0",
"electron-nucleus": "^1.11.0",
"electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git",
"html-webpack-plugin": "^4.0.0-beta.5",
"number-abbreviate": "^2.0.0",
"react": "^16.8.0-alpha.1",
"react-dom": "^16.7.0",
"react-redux": "^6.0.0",
"react-vis": "^1.11.6",
"redux": "^4.0.1",
"style-loader": "^0.23.1",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-livereload-plugin": "^2.2.0"
},
"dependencies": {
"@material-ui/core": "^3.9.0",
"@material-ui/styles": "^3.0.0-alpha.8",
"@types/node": "^10.12.18",
"@types/react-resize-detector": "^3.1.0",
"@types/react-split-pane": "^0.1.67",
"@types/sha1": "^1.1.1",
"@types/socket.io-client": "^1.4.32",
"@types/vis": "^4.21.9",
"awesome-typescript-loader": "^5.2.1",
"compare-versions": "^3.4.0",
"copy-text-to-clipboard": "^1.0.4",
"css-loader": "^2.1.0",
"electron-nucleus": "^1.11.0",
"electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git#dist",
"html-webpack-plugin": "^4.0.0-beta.5",
"jquery": "^3.3.1",
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.1.1",
"moving-average": "^1.0.0",
"number-abbreviate": "^2.0.0",
"react": "^16.8.0-alpha.1",
"react-ace": "^6.3.2",
"react-dom": "^16.7.0",
"react-json-view": "^1.19.1",
"react-redux": "^6.0.0",
"react-resize-detector": "^3.4.0",
"react-split-pane": "^0.1.85",
"react-vis": "^1.11.6",
"redux": "^4.0.1",
"redux-batched-actions": "^0.4.1",
"sha1": "^1.1.1",
"socket.io-client": "^2.2.0",
"source-map-loader": "^0.2.4",
"style-loader": "^0.23.1",
"typescript": "^3.2.2",
"webpack": "^4.28.2",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.14"
"webpack-dev-server": "^3.1.14",
"webpack-livereload-plugin": "^2.2.0"
}
}
+3 -2
View File
@@ -52,6 +52,7 @@ class App extends React.PureComponent<Props, {}> {
minSize={250}
defaultSize={500}
allowResize={true}
style={{ position: 'relative' }}
pane1Style={{ overflow: 'hidden' }}
>
<div className={paneDefaults}>
@@ -98,7 +99,7 @@ const styles = (theme: Theme) => {
},
content: {
width: '100vw',
overflowX: 'hidden',
overflowX: 'hidden' as 'hidden',
backgroundColor: theme.palette.background.default,
transition: theme.transitions.create('transform', {
easing: theme.transitions.easing.sharp,
@@ -107,7 +108,7 @@ const styles = (theme: Theme) => {
transform: 'translateX(0px)',
},
contentShift: {
overflowX: 'hidden',
overflowX: 'hidden' as 'hidden',
width: '100vw',
padding: 0,
backgroundColor: theme.palette.background.default,
+99 -45
View File
@@ -10,16 +10,18 @@ import {
Typography,
} from '@material-ui/core'
import { Theme, withStyles } from '@material-ui/core/styles'
import { UpdateInfo, checkForUpdates, rendererEvents, updateAvailable } from '../../events'
import { green, red } from '@material-ui/core/colors'
import { green } from '@material-ui/core/colors'
import { AppState } from './reducers'
import Close from '@material-ui/icons/Close'
import CloudDownload from '@material-ui/icons/CloudDownload'
import { UpdateFileInfo } from 'builder-util-runtime'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { updateNotifierActions } from './actions'
import axios from 'axios'
import * as compareVersions from 'compare-versions'
import * as electron from 'electron'
import * as os from 'os'
interface Props {
showUpdateNotification: boolean
@@ -28,35 +30,55 @@ interface Props {
actions: any
}
class UpdateNotifier extends React.Component<Props, {}> {
private updateInfo?: UpdateInfo
interface GithubRelease {
url: string,
assets?: GithubAsset[]
published_at: string // "2019-01-25T20:14:39Z"
body_html: string
body: string
body_text: string
tag_name: string
}
interface GithubAsset {
id: number
node_id: string
url: string
name: string
label: string
}
interface State {
newerVersions: GithubRelease[]
}
class UpdateNotifier extends React.Component<Props, State> {
constructor(props: any) {
super(props)
this.state = {
selectedNode: undefined,
}
this.state = { newerVersions: [] }
// window.compare = compareVersions
const ownVersion = electron.remote.app.getVersion()
this.fetchReleases().then((releases) => {
const newerVersions = releases
.filter(release => compareVersions(release.tag_name, ownVersion) > 0)
.sort((a, b) => compareVersions(b.tag_name, a.tag_name))
if (newerVersions.length > 0) {
this.setState({ newerVersions })
this.props.actions.showUpdateNotification(true)
}
})
}
public componentDidMount() {
rendererEvents.emit(checkForUpdates, undefined)
rendererEvents.subscribe(updateAvailable, this.handleUpdate)
}
public componentWillUnmount() {
rendererEvents.unsubscribeAll(updateAvailable)
}
private fixUrl(url: string, version: string) {
if (!/^http/.test(url)) {
return `https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v${version}/${url}`
}
return url
}
private handleUpdate = (updateInfo: UpdateInfo) => {
this.updateInfo = updateInfo
this.props.actions.showUpdateNotification(true)
private fetchReleases(): Promise<GithubRelease[]> {
return axios.get('https://api.github.com/repos/thomasnordquist/mqtt-explorer/releases', {
headers: {
accept: 'application/vnd.github.v3.full+json',
},
}).then((res) => {
return res.data
})
}
private onCloseNotification = (event: React.SyntheticEvent<any>, reason: string) => {
@@ -93,17 +115,18 @@ class UpdateNotifier extends React.Component<Props, {}> {
vertical: 'top',
horizontal: 'right',
}
console.log(this.state.newerVersions)
return (
<Snackbar
anchorOrigin={snackbarAnchor}
open={this.props.showUpdateNotification}
autoHideDuration={7000}
autoHideDuration={12000}
onClose={this.onCloseNotification}
>
<SnackbarContent
className={this.props.classes.success}
message="Update available"
message={`${this.state.newerVersions.length} Update(s) available`}
action={this.notificationActions()}
/>
</Snackbar>
@@ -113,7 +136,7 @@ class UpdateNotifier extends React.Component<Props, {}> {
private notificationActions() {
return [(
<Button key="undo" size="small" onClick={this.showDetails}>
Download
Details
</Button>
), (
<IconButton
@@ -130,10 +153,14 @@ class UpdateNotifier extends React.Component<Props, {}> {
}
private renderUpdateDetails() {
if (!this.updateInfo) {
const latestUpdate = this.state.newerVersions[0]
if (!latestUpdate) {
return null
}
const releaseNotes = (this.updateInfo.releaseNotes as string) || ''
const releaseNotes = this.state.newerVersions
.map(release => `<p><h3>${release.tag_name}</h3><p/><p>${release.body_html}</p>`)
.join('<hr />')
return (
<Modal
open={this.props.showUpdateDetails}
@@ -141,31 +168,58 @@ class UpdateNotifier extends React.Component<Props, {}> {
onClose={this.hideDetails}
>
<Paper className={this.props.classes.root}>
<Typography variant="h6" className={this.props.classes.title}>Version {this.updateInfo.version}</Typography>
<Typography variant="h6" className={this.props.classes.title}>Version {latestUpdate.tag_name}</Typography>
<Typography className={this.props.classes.title}>Changelog</Typography>
<div className={this.props.classes.releaseNotes} dangerouslySetInnerHTML={{ __html: releaseNotes }} />
{this.renderDownloads(this.updateInfo)}
{this.renderDownloads()}
<Button
className={this.props.classes.download}
onClick={this.openGithub}
>
Github Page
</Button>
<Button className={this.props.classes.closeButton} color="secondary" onClick={this.hideDetails}>Close</Button>
</Paper>
</Modal>
)
}
private urlToFilename(url: string) {
const parts = url.split('/')
return parts[parts.length - 1]
private openGithub = () => {
this.openUrl('https://github.com/thomasnordquist/MQTT-Explorer')
}
private renderDownloads(updateInfo: UpdateInfo) {
return updateInfo.files
.map((file: UpdateFileInfo, index: number) => (
<div key={index}>
private openUrl = (url: string) => {
electron.shell.openExternal(url)
}
private assetForCurrentPlatform(asset: GithubAsset) {
let regex: RegExp
if (os.platform() === 'darwin') {
regex = /\.dmg$/
} else if (os.platform() === 'darwin') {
regex = /\.exe$/
} else {
regex = /\.AppImage$/
}
return regex.test(asset.name)
}
private renderDownloads() {
const latestUpdate = this.state.newerVersions[0]
if (!latestUpdate || !latestUpdate.assets) {
return null
}
return latestUpdate.assets
.filter(this.assetForCurrentPlatform)
.map(asset => (
<div>
<Button
className={this.props.classes.download}
href={this.fixUrl(file.url, updateInfo.version)}
onClick={() => this.openUrl(asset.url)}
>
<IconButton><CloudDownload /></IconButton>{this.urlToFilename(file.url)}
<CloudDownload />&nbsp;{asset.name}
</Button>
</div>
))
+6 -1
View File
@@ -5,8 +5,13 @@ import { Dispatch, AnyAction } from 'redux'
import { setTopic } from './Publish'
import { TopicViewModel } from '../TopicViewModel'
import { batchActions } from 'redux-batched-actions'
const debounce = require('lodash.debounce')
export const selectTopic = (topic: q.TreeNode<TopicViewModel>) => (dispatch: Dispatch<any>, getState: () => AppState) => {
debouncedSelectTopic(topic, dispatch, getState)
}
const debouncedSelectTopic = debounce((topic: q.TreeNode<TopicViewModel>, dispatch: Dispatch<any>, getState: () => AppState) => {
const { selectedTopic } = getState().tree
if (selectedTopic === topic) {
return
@@ -36,7 +41,7 @@ export const selectTopic = (topic: q.TreeNode<TopicViewModel>) => (dispatch: Dis
} else {
dispatch(selectTreeTopicDispatch)
}
}
}, 70)
export const showTree = (tree?: q.Tree<TopicViewModel>) => (dispatch: Dispatch<any>, getState: () => AppState): AnyAction => {
const visibleTree = getState().tree.tree
+1 -1
View File
@@ -42,7 +42,7 @@ class BrokerStatistics extends React.Component<Props, {}> {
public render() {
const { tree, classes } = this.props
if (!tree) {
if (!tree || !tree.findNode('$SYS')) {
return null
}
+1 -1
View File
@@ -64,7 +64,7 @@ class MessageHistory extends React.Component<Props, State> {
badgeContent={this.props.items.length}
color="primary"
>
{this.state.collapsed ? '▶' : '▼'} History
{this.state.collapsed ? '▶ History' : '▼ History'}
</Badge>
<div style={{ float: 'right' }}>{this.state.collapsed ? this.props.contentTypeIndicator : null}</div>
</Typography>
@@ -144,6 +144,7 @@ class Publish extends React.Component<Props, State> {
size="small"
color="primary"
onClick={this.publish}
id="publish-button"
>
<Navigation style={{ marginRight: '8px' }} /> Publish
</Button>
+13 -5
View File
@@ -23,15 +23,16 @@ import ExpandMore from '@material-ui/icons/ExpandMore'
import Clear from '@material-ui/icons/Clear'
import MessageHistory from './MessageHistory'
import NodeStats from './NodeStats'
const Publish = React.lazy(() => import('./Publish/Publish'))
import Topic from './Topic'
const ValueRenderer = React.lazy(() => import('./ValueRenderer'))
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { TopicViewModel } from '../../TopicViewModel'
import { default as ReactResizeDetector } from 'react-resize-detector'
const throttle = require('lodash.throttle')
const Publish = React.lazy(() => import('./Publish/Publish'))
const ValueRenderer = React.lazy(() => import('./ValueRenderer'))
interface Props {
node?: q.TreeNode<TopicViewModel>,
actions: typeof sidebarActons,
@@ -42,6 +43,7 @@ interface Props {
interface State {
node: q.TreeNode<TopicViewModel>
compareMessage?: q.Message
valueRenderWidth: number
}
class Sidebar extends React.Component<Props, State> {
@@ -53,7 +55,7 @@ class Sidebar extends React.Component<Props, State> {
constructor(props: any) {
super(props)
console.error('Find and fix me #state')
this.state = { node: new q.Tree() }
this.state = { node: new q.Tree(), valueRenderWidth: 300 }
}
public componentWillReceiveProps(nextProps: Props) {
@@ -114,6 +116,7 @@ class Sidebar extends React.Component<Props, State> {
{this.messageMetaInfo()}
<div ref={this.valueRef}>
<React.Suspense fallback={<div>Loading...</div>}>
<ReactResizeDetector handleWidth={true} onResize={this.valueRenderWidthChange} />
<ValueRenderer message={this.props.node && this.props.node.message} />
</React.Suspense>
</div>
@@ -143,9 +146,14 @@ class Sidebar extends React.Component<Props, State> {
)
}
private valueRenderWidthChange = (width: number) => {
console.log(width)
this.setState({ valueRenderWidth: width })
}
private showValueComparison = (a: any) => (
<Fade {...a.TransitionProps} timeout={350}>
<Paper>
<Paper style={{ maxWidth: this.state.valueRenderWidth }}>
<ValueRenderer message={this.state.compareMessage} />
</Paper>
</Fade>
+4 -4
View File
@@ -12,7 +12,7 @@ interface Props {
class ValueRenderer extends React.Component<Props, {}> {
public render() {
return <div>{this.renderValue()}</div>
return <div style={{ padding: '8px 0px 8px 8px' }}>{this.renderValue()}</div>
}
public renderValue() {
@@ -38,7 +38,7 @@ class ValueRenderer extends React.Component<Props, {}> {
const theme = (this.props.theme.palette.type === 'dark') ? 'monokai' : 'bright:inverted'
return (
<ReactJson
style={{ width: '100%' }}
style={{ width: '100%', overflowY: 'scroll', wordBreak: 'break-all' }}
src={json}
name={null}
theme={theme}
@@ -49,13 +49,13 @@ class ValueRenderer extends React.Component<Props, {}> {
private renderRawValue(value: string) {
const style: React.CSSProperties = {
backgroundColor: 'rgba(80, 80, 80, 0.6)',
padding: '8px 12px 8px 12px',
backgroundColor: 'rgba(100, 100, 100, 0.55)',
wordBreak: 'break-all',
width: '100%',
overflow: 'auto hidden',
display: 'block',
lineHeight: '1.2em',
padding: '12px 5px 12px 5px',
color: this.props.theme.palette.text.primary,
}
+1 -1
View File
@@ -225,7 +225,7 @@ class TreeNode extends React.Component<Props, State> {
private setHover = debounce((hover: boolean) => {
this.setState({ mouseOver: hover })
}, 5)
}, 45)
private didClickNode = (event: React.MouseEvent) => {
event.stopPropagation()
-11
View File
@@ -1,11 +0,0 @@
import { createReducer } from './lib'
import { createMuiTheme, Theme } from '@material-ui/core'
const initialState: Theme = createMuiTheme({
palette: {
type: 'dark',
},
typography: { useNextVariants: true },
})
export const theme = createReducer(initialState, {})
-2
View File
@@ -5,7 +5,6 @@ import { PublishState, publishReducer } from './Publish'
import { ConnectionState, connectionReducer } from './Connection'
import { SettingsState, settingsReducer } from './Settings'
import { TreeState, treeReducer } from './Tree'
import { theme } from './theme'
export enum ActionTypes {
showUpdateNotification = 'SHOW_UPDATE_NOTIFICATION',
@@ -63,7 +62,6 @@ const tooBigReducer: Reducer<TooBigOfState | undefined, CustomAction> = (state =
}
const reducer = combineReducers({
theme,
tooBigReducer,
publish: publishReducer,
connection: connectionReducer,
+2 -1
View File
@@ -11,10 +11,11 @@ setInterval(() => {
try {
electronRendererTelementry.trackCustomEvent({ name: 'heapStatistics', payload: process.getHeapStatistics() })
electronRendererTelementry.trackCustomEvent({ name: 'cpuUsage', payload: process.getCPUUsage() })
electronRendererTelementry.trackCustomEvent({ name: 'runningSince', payload: performance.now() })
} catch (error) {
console.error(error)
}
}, 30 * 1000)
}, 60 * 1000)
export function trackEvent(name: string) {
if (name.match(/^@@redux/)) {
+1 -1
View File
@@ -66,7 +66,7 @@ module.exports = {
plugins: [
new LiveReloadPlugin({}),
new HtmlWebpackPlugin({ template: './index.html', file: './build/index.html', inject: false }),
new BundleAnalyzerPlugin(),
// new BundleAnalyzerPlugin(),
],
// When importing a module whose path matches one of the following, just
+6022
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -9,7 +9,7 @@
"test-inspect": "mocha --inspect-brk",
"coverage": "nyc mocha",
"debug": "ts-node --inspect ./src/index.ts",
"postinstall": "npm run build"
"postinstall": "yarn build"
},
"author": "",
"license": "ISC",
+2 -2
View File
@@ -63,12 +63,12 @@ export class MqttSource implements DataSource<MqttOptions> {
this.stateMachine.setConnected(true)
client.subscribe(this.rootSubscription, (err: Error) => {
if (err) {
throw new Error('mqtt subscription failed')
this.stateMachine.setError(err)
}
})
client.subscribe('$SYS/#', (err: Error) => {
if (err) {
throw new Error('mqtt subscription failed')
console.error('failed to subscribe to sys topic', err)
}
})
})
+4 -2
View File
@@ -55,8 +55,10 @@ export class ConnectionManager {
public removeConnection(hash: string) {
const connection = this.connections[hash]
connection.disconnect()
delete this.connections[hash]
if (connection) {
connection.disconnect()
delete this.connections[hash]
}
}
public closeAllConnections() {
+17
View File
@@ -0,0 +1,17 @@
FROM node:11-stretch
RUN DEBIAN_FRONTEND="noninteractive" apt-get update \
&& apt-get install -y --no-install-recommends nano ffmpeg xvfb git-core tmux locales mosquitto x11vnc
RUN apt-get install -yq --no-install-recommends libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 libnss3
# Generate locales for TMUX
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
CMD /bin/bash
COPY cloneBuildAndTest.sh ./
VOLUME /app
EXPOSE 5900
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
set -e
git clone https://github.com/thomasnordquist/MQTT-Explorer.git /app
cd /app
git checkout travis-ui-tests
yarn
yarn build
yarn ui-test
yarn upload-video-artifacts
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
set -e
yarn ui-test
yarn upload-video-artifacts
-5227
View File
File diff suppressed because it is too large Load Diff
+17 -11
View File
@@ -1,17 +1,19 @@
{
"name": "MQTT-Explorer",
"version": "0.1.1",
"version": "0.1.3",
"description": "Explore your message queues",
"main": "dist/src/electron.js",
"scripts": {
"start": "electron .",
"install": "cd app; npm install; cd ..",
"test": "npm run test-backend",
"build": "tsc && cd app && npm run build && cd ..",
"test-backend": "cd backend && npm run test && cd ..",
"prepare-release": "./prepare-release.sh",
"test": "yarn run test-backend",
"install": "cd app; yarn; cd ..",
"build": "tsc && cd app && yarn run build && cd ..",
"test-backend": "cd backend && yarn run test && cd ..",
"prepare-release": "./scripts/prepare-release.sh",
"package": "ts-node package.ts",
"package-with-docker": "./package-with-docker.sh"
"ui-test": "./scripts/uiTests.sh",
"upload-video-artifacts": "./scripts/uploadVideoAsset.ts ui-test.mp4 ui-test.gif",
"package-with-docker": "./scripts/package-with-docker.sh"
},
"repository": {
"type": "git",
@@ -42,32 +44,36 @@
"license": "ISC",
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/mime": "^2.0.0",
"@types/mocha": "^5.2.5",
"@types/mustache": "^0.8.32",
"@types/node": "^10.12.18",
"@types/semver": "^5.5.0",
"@types/sha1": "^1.1.1",
"@types/socket.io": "^2.1.2",
"axios": "^0.18.0",
"chai": "^4.2.0",
"electron": "^4.0.1",
"electron": "^4.0.2",
"electron-builder": "^20.38.4",
"mime": "^2.4.0",
"mocha": "^5.2.0",
"mustache": "^3.0.1",
"nyc": "^13.1.0",
"redux-thunk": "^2.3.0",
"source-map-support": "^0.5.9",
"spectron": "^5.0.0",
"ts-node": "^7.0.1",
"tslint": "^5.12.0",
"tslint-config-airbnb": "^5.11.1",
"tslint-react": "^3.6.0",
"tslint-strict-null-checks": "^1.0.1",
"typescript": "^3.2.2"
"typescript": "^3.2.2",
"webdriverio": "5.4"
},
"dependencies": {
"electron-debug": "^2.0.0",
"electron-is-dev": "^1.0.1",
"electron-log": "^2.2.17",
"electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git",
"electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git#dist",
"electron-updater": "^4.0.6",
"mqtt": "^2.18.8",
"sha1": "^1.1.1"
@@ -10,19 +10,17 @@ mkdir -p "$DIR"
git clone .git "$DIR"
cd $DIR
# App
cd app
npm install
cd ..
# Backend
cd backend
npm install;
#npm run test
yarn
cd ..
# Build
npm run build
yarn
yarn build
rm -rf node_modules
yarn install --production
rm -rf app/node_modules
cd "$ORIGINAL_DIR"
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
cp app.mp4 original.mp4
# The video starts with a few blank frames, we want to know when the stop
ffprobe -f lavfi -i "movie=app.mp4,blackdetect[out0]" -show_entries tags=lavfi.black_start,lavfi.black_end -of default=nw=1 -v quiet > ffmpeg_info
END_OF_BLACK=`cat ffmpeg_info | grep end | head -n1 | cut -d'=' -f2`
# Trim black frames at start
ffmpeg -ss $END_OF_BLACK -i app.mp4 app2.mp4
mv app2.mp4 app.mp4
# Generate gif palette
ffmpeg -y -i app.mp4 -vf fps=10,scale=720:-1:flags=lanczos,palettegen palette.png
# Create gif
ffmpeg -i app.mp4 -i palette.png -filter_complex "fps=10,scale=720:-1:flags=lanczos[x];[x][1:v]paletteuse" app.gif
# Clean up
rm ffmpeg_info palette.png
mv app.mp4 ui-test.mp4
mv app.gif ui-test.gif
+51
View File
@@ -0,0 +1,51 @@
#!/bin/bash
function finish {
echo "Exiting, cleaning up"
tmux send-keys -t record q || echo "No tmux was running"
#echo kill $PID_XVFB $PID_CHROMEDRIVER $PID_MOSQUITTO
#kill $PID_XVFB $PID_CHROMEDRIVER $PID_MOSQUITTO
}
trap finish EXIT
DIMENSIONS="1024x700"
SCR=99
# Start new window manager
Xvfb :$SCR -screen 0 "$DIMENSIONS"x24 -ac &
export PID_XVFB=$!
sleep 2
# Debug with VNC
while [ "$TEST_EXIT_CODE" = "" ]; do x11vnc -passwd "bierbier" -display :$SCR; done &
export PID_VNC=$!
# Start mqtt broker
mosquitto &
export PID_MOSQUITTO=$!
DISPLAY=:$SCR ./node_modules/.bin/chromedriver --url-base=wd/hub --port=9515 &
export PID_CHROMEDRIVER=$!
sleep 2
# Delete old video
rm ./app.mp4 || echo no need to delete ./app.mp4
# Start recoring in tmux
tmux new-session -d -s record ffmpeg -f x11grab -draw_mouse 0 -video_size $DIMENSIONS -i :$SCR -codec:v libx264 -r 20 ./app.mp4
# Start tests
node dist/src/spec/webdriverio.js
TEST_EXIT_CODE=$?
echo "Webriver exitet with $TEST_EXIT_CODE"
# Stop recording
tmux send-keys -t record q
# Ensure video is written
sleep 5
# Process the video
./scripts/prepareVideo.sh
exit $TEST_EXIT_CODE
+86
View File
@@ -0,0 +1,86 @@
#!node_modules/.bin/ts-node
import axios from 'axios'
import * as fs from 'fs'
import * as path from 'path'
import * as mime from 'mime'
const githubToken = process.env.GH_TOKEN
async function tagUrl(tag: string): Promise<string | undefined> {
const response = await axios.get(`https://api.github.com/repos/thomasnordquist/mqtt-explorer/releases?access_token=${githubToken}`)
const tagRelease = response.data.find((release: any) => release.tag_name === tag)
return tagRelease ? cleanUploadUrl(tagRelease.upload_url) : undefined
}
async function createDraft(tag: string) {
console.log('create draft')
const response = await axios({
method: 'post',
url: `https://api.github.com/repos/thomasnordquist/mqtt-explorer/releases?access_token=${githubToken}`,
data: {
tag_name: tag,
name: tag.slice(1),
draft: true,
},
})
return cleanUploadUrl(response.data.upload_url)
}
function cleanUploadUrl(url: string) {
return url.match(/(.*){/)![1]
}
async function uploadAsset() {
const tag: string | undefined = process.env.GIT_TAG
const files = process.argv.slice(2)
if (!tag || files.length === 0) {
console.log('Nothing to do')
return
}
let uploadUrl: string | undefined
try {
uploadUrl = await tagUrl(tag)
if (!uploadUrl) {
console.log('tag does not exist')
try {
uploadUrl = await createDraft(tag)
} catch (error) {
console.error('failed to create draft', error.stack)
process.exit(1)
}
}
} catch (error) {
console.error('failed to find tag release', error.stack)
process.exit(1)
}
if (uploadUrl) {
console.log(uploadUrl)
for (const file of files) {
console.log('uploading file', file)
await uploadFile(uploadUrl, file)
console.log('upload completed')
}
}
}
async function uploadFile(uploadUrl: string, file: string) {
const data = fs.readFileSync(file)
const mimeType = mime.getType(path.extname(file))
return await axios({
data,
method: 'post',
url: `${uploadUrl}?name=${path.basename(file)}&access_token=${githubToken}`,
headers: {
'Content-Type': mimeType,
},
})
}
uploadAsset()
+11 -2
View File
@@ -6,10 +6,16 @@ import { autoUpdater } from 'electron-updater'
import * as log from 'electron-log'
import { ConnectionManager, updateNotifier } from '../backend/src/index'
import { electronTelemetryFactory } from 'electron-telemetry'
const isDev = require('electron-is-dev')
const electronTelemetry = electronTelemetryFactory('9b0c8ca04a361eb8160d98c5')
let electronTelemetry: any
if (!isDev) {
electronTelemetry = electronTelemetryFactory('9b0c8ca04a361eb8160d98c5')
}
const isDebugEnabled = Boolean(process.argv.find(arg => arg === 'debug'))
const isFullscreen = Boolean(process.argv.find(arg => arg === '--fullscreen'))
require('electron-debug')({ enabled: isDebugEnabled })
autoUpdater.logger = log
@@ -37,7 +43,10 @@ function createWindow() {
})
mainWindow.once('ready-to-show', () => {
mainWindow && mainWindow.show()
if (mainWindow) {
isFullscreen && mainWindow.setFullScreen(true)
mainWindow.show()
}
})
console.log('icon path', iconPath)
+106
View File
@@ -0,0 +1,106 @@
import * as mqtt from 'mqtt'
const settings = {
port: 1883,
}
let client: mqtt.MqttClient
function startServer(): Promise<mqtt.MqttClient> {
return new Promise(async (resolve) => {
// const server = new mosca.Server(settings)
// await new Promise(resolve => server.once('ready', resolve))
client = await connectMqtt()
generateData(client)
resolve(client)
})
}
function connectMqtt(): Promise<mqtt.MqttClient> {
return new Promise((resolve) => {
const client = mqtt.connect('mqtt://127.0.0.1:1883', { username: 'thomas', password: 'bierbier' })
client.once('connect', () => {
resolve(client)
})
})
}
function temperature(base = 18, sineCoefficient = 2, offset = 0) {
const temp = base + Math.sin(Date.now() / 1000 / 5 + offset) * sineCoefficient + Math.random()
return String(Math.round(temp * 100) / 100)
}
export function stop() {
for (const interval of intervals) {
clearInterval(interval)
}
try {
client && client.end()
} catch {}
}
const intervals: any = []
function generateData(client: mqtt.MqttClient) {
client.publish('livingroom/lamp/state', 'on', { retain: true, qos: 0 })
client.publish('livingroom/lamp/brightness', '128', { retain: true, qos: 0 })
client.publish('livingroom/thermostat/targetTemperature', '20°C', { retain: true, qos: 0 })
intervals.push(setInterval(() => client.publish('livingroom/temperature', temperature()), 1000))
intervals.push(setInterval(() => client.publish('livingroom/humidity', temperature(60, -2, 0)), 1000))
client.publish('livingroom/lamp-1/state', 'on', { retain: true, qos: 0 })
client.publish('livingroom/lamp-1/brightness', '48', { retain: true, qos: 0 })
client.publish('livingroom/lamp-2/state', 'off', { retain: true, qos: 0 })
client.publish('livingroom/lamp-2/brightness', '48', { retain: true, qos: 0 })
client.publish('kitchen/lamp/state', 'off', { retain: true, qos: 0 })
client.subscribe('kitchen/lamp/set')
client.on('message', (topic, payload) => {
if (topic === 'kitchen/lamp/set') {
setTimeout(() => client.publish('kitchen/lamp/state', payload, { retain: true, qos: 0 }), 500)
}
})
intervals.push(setInterval(() => client.publish('kitchen/temperature', temperature()), 1500))
intervals.push(setInterval(() => client.publish('kitchen/humidity', temperature(60, -5, 0)), 1800))
client.publish('garden/pump/state', 'off', { retain: true, qos: 0 })
client.publish('garden/water/level', '70%', { retain: true, qos: 0 })
client.publish('garden/lamps/state', 'off', { retain: true, qos: 0 })
client.publish('garden/lamps/state', 'off', { retain: true, qos: 0 })
client.publish('zigbee2mqtt/bridge/state', 'online', { retain: true, qos: 0 })
client.publish('ble2mqtt/bridge/state', 'online', { retain: true, qos: 0 })
// Used for demonstrating "clean up"
client.publish('test 123', 'Hello world', { retain: true, qos: 0 })
client.publish('hello', 'sunshine', { retain: true, qos: 0 })
// Just stuff
client.publish('01-80-C2-00-00-0F/LWT', 'offline', { retain: true, qos: 0 })
intervals.push(setInterval(() => {
client.publish('3d-printer/OctoPrint/temperature/bed', '{"_timestamp":1548589083,"actual":25.9,"target":0}')
client.publish('3d-printer/OctoPrint/temperature/tool0', '{"_timestamp":1548589093,"actual":26.4,"target":0}')
}, 3333))
let state = true
intervals.push(setInterval(() => {
state = !state
const enitityId = Math.round(Math.random() * 3000)
client.publish(
'actuality/showcase', `{
"tags":{
"entityId":${enitityId},
"entityType":"person",
"host":"d44ad81e10f9",
"server":"${state ? 'http://localhost/dataActuality' : 'http://localhost/dataStorage' }",
"status":"${state ? 'live' : 'inactive'}"},
"timestamp":${Date.now()}
}`.replace(/\s/g, ''),
{ retain: true, qos: 0 },
)
}, 2102))
}
export default startServer
+15
View File
@@ -0,0 +1,15 @@
import { clickOn, sleep, writeText, expandTopic, moveToCenterOfElement } from '../util'
import { Browser } from 'webdriverio'
export async function clearOldTopics(browser: Browser<void>) {
const topics = ['hello', 'test 123']
for (const topic of topics) {
await expandTopic(topic, browser)
await sleep(1000)
const deleteButton = await browser.$('//button[contains(@title, "Delete retained topic")]')
await moveToCenterOfElement(deleteButton, browser)
await clickOn(deleteButton, browser)
await sleep(700)
}
}
@@ -0,0 +1,14 @@
import { clickOn, sleep, writeText, expandTopic, hideText } from '../util'
import { Browser } from 'webdriverio'
// Expects a topic with at least two messages to be selected
export async function compareJsonSideBySide(browser: Browser<void>) {
const firstEntry = await browser.$('//span[contains(text(), "History")]/../../div/div[1]/div')
const secondEntry = await browser.$('//span[contains(text(), "History")]/../../div/div[2]/div')
await clickOn(secondEntry, browser)
await sleep(2000)
await clickOn(firstEntry, browser)
await sleep(2000)
await clickOn(firstEntry, browser)
await sleep(1000)
}
+20
View File
@@ -0,0 +1,20 @@
import { clickOn, sleep, writeText } from '../util'
import { Browser } from 'webdriverio'
export async function connectTo(host: string, browser: Browser<void>) {
await writeTextToInput('Host', host, browser)
await writeTextToInput('Username', 'thomas', browser, false)
await writeTextToInput('Password', 'bierbier', browser, false)
const connectButton = await browser.$('//button/span[contains(text(),"Connect")]')
clickOn(connectButton, browser)
}
async function writeTextToInput(name: string, text: string, browser: Browser<void>, wait: boolean = true) {
const input = await browser.$(`//label[contains(text(), "${name}")]/..//input`)
await clickOn(input, browser, 1)
wait && await sleep(500)
input.clearValue()
wait && await sleep(300)
await writeText(text, browser)
}
@@ -0,0 +1,7 @@
import { clickOn, sleep, writeText, expandTopic } from '../util'
import { Browser } from 'webdriverio'
export async function copyTopicToClipboard(browser: Browser<void>) {
const copyButton = await browser.$('//p[contains(text(), "Topic")]/span')
await clickOn(copyButton, browser, 1)
}
@@ -0,0 +1,7 @@
import { clickOn, sleep, writeText, expandTopic } from '../util'
import { Browser } from 'webdriverio'
export async function copyValueToClipboard(browser: Browser<void>) {
const copyButton = await browser.$('//p[contains(text(), "Value")]/span')
await clickOn(copyButton, browser, 1)
}
+7
View File
@@ -0,0 +1,7 @@
import { clickOn } from '../util'
import { Browser } from 'webdriverio'
export async function disconnect(browser: Browser<void>) {
const connectButton = await browser.$('//button/span[contains(text(),"Disconnect")]')
clickOn(connectButton, browser)
}
+23
View File
@@ -0,0 +1,23 @@
import { clickOn, sleep, writeText, delteTextWithBackspaces, expandTopic, moveToCenterOfElement, showText } from '../util'
import { Browser } from 'webdriverio'
export async function publishTopic(browser: Browser<void>) {
await expandTopic('kitchen/lamp/state', browser)
const topicInput = await browser.$('//textarea[contains(text(),"kitchen/lamp/state")][2]')
await clickOn(topicInput, browser)
await delteTextWithBackspaces(topicInput, browser, 120, 5)
await writeText('set', browser, 300)
const payloadInput = await browser.$('//*[contains(@class, "ace_text-input")]')
await payloadInput.setValue('o')
await sleep(300)
await payloadInput.setValue('n')
await sleep(700)
const publishButton = await browser.$('#publish-button')
await moveToCenterOfElement(publishButton, browser)
await showText('Lamp turns on', 1000, browser, 'top')
await sleep(500)
await clickOn(publishButton, browser)
}
+15
View File
@@ -0,0 +1,15 @@
import { clickOn, sleep, writeText, delteTextWithBackspaces, showText } from '../util'
import { Browser } from 'webdriverio'
export async function searchTree(text: string, browser: Browser<void>) {
const searchField = await browser.$('//input[contains(@placeholder, "Search")]')
await clickOn(searchField, browser, 1)
await writeText(text, browser, 100)
await sleep(1500)
}
export async function clearSearch(browser: Browser<void>) {
const searchField = await browser.$('//input[contains(@placeholder, "Search")]')
await clickOn(searchField, browser, 1)
await delteTextWithBackspaces(searchField, browser, 100)
}
+8
View File
@@ -0,0 +1,8 @@
import { clickOn, sleep, writeText, expandTopic } from '../util'
import { Browser } from 'webdriverio'
export async function showJsonPreview(browser: Browser<void>) {
await expandTopic('actuality/showcase', browser)
await sleep(1000)
}
+21
View File
@@ -0,0 +1,21 @@
import { clickOn, sleep, writeText, expandTopic, moveToCenterOfElement } from '../util'
import { Browser } from 'webdriverio'
export async function showMenu(browser: Browser<void>) {
const menuButton = await browser.$('//button[contains(@aria-label, "Menu")]')
await clickOn(menuButton, browser)
// const brokerStatistics = await browser.$('//div[contains(@class, "BrokerStatistics")]/div[1]')
// moveToCenterOfElement(brokerStatistics, browser)
await sleep(2000)
const topicOrder = await browser.$('#select-node-order')
await clickOn(topicOrder, browser)
await sleep(1000)
const alphabetically = await browser.$('//li[contains(@data-value, "abc")]')
await clickOn(alphabetically, browser)
await sleep(2000)
await clickOn(menuButton, browser)
}
+11
View File
@@ -0,0 +1,11 @@
import { clickOn, sleep, writeText, expandTopic, clickOnHistory } from '../util'
import { Browser } from 'webdriverio'
export async function showNumericPlot(browser: Browser<void>) {
await expandTopic('livingroom/temperature', browser)
await clickOnHistory(browser)
await sleep(1000)
await expandTopic('livingroom/humidity', browser)
}
+29
View File
@@ -0,0 +1,29 @@
import { Browser } from 'webdriverio'
import { clickOn } from './'
export async function expandTopic(path: string, browser: Browser<void>) {
const originalTopics = path.split('/')
let topics = path.split('/')
while (topics.length > 0 && !await topicMatches(topics, browser)) {
topics = topics.slice(0, topics.length - 1)
}
if (topics.length === 0) {
throw Error('could not expand topics, no match found')
}
while (topics.length <= originalTopics.length) {
const match = await browser.$(topicSelector(topics))
await clickOn(match, browser)
topics.push(originalTopics[topics.length])
}
}
async function topicMatches(topics: string[], browser: Browser<void>) {
const result = await browser.$(topicSelector(topics))
return result.isExisting()
}
function topicSelector(topics: string[]) {
const suffix = topics.map(topic => `*[contains(text(), "${topic}")]`).join('/../../..//')
return `//${suffix}`
}
+130
View File
@@ -0,0 +1,130 @@
import { Element, Browser } from 'webdriverio'
export { expandTopic } from './expandTopic'
export function sleep(ms: number, required = false) {
return new Promise((resolve) => {
if (required) {
setTimeout(resolve, ms)
} else {
setTimeout(resolve, ms)
}
})
}
export async function writeText(text: string, browser: Browser<void>, delay = 0) {
for (const c of text.split('')) {
await browser.keys([c])
await sleep(delay)
}
}
export async function delteTextWithBackspaces(element: Element<void>, browser: Browser<void>, delay = 0, count = 0) {
const length = count > 0 ? count : (await element.getValue()).length
for (let i = 0; i < length; i += 1) {
await browser.keys(['Backspace'])
await sleep(delay)
}
}
export async function moveToCenterOfElement(element: Element<void>, browser: Browser<void>) {
const { x, y } = await element.getLocation()
const { width, height } = await element.getSize()
const js = `{
const targetX = ${x + width / 2}
const targetY = ${y + height / 2}
const duration = 500
const maxStepSize = 10
const e = document.getElementById("bier")
const top = parseFloat(e.style.top)
const left = parseFloat(e.style.left)
const deltaY = targetY - top
const deltaX = targetX - left
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
const steps = Math.ceil(distance / maxStepSize)
const stepX = deltaX / steps
const stepY = deltaY / steps
let currentStep = 0
function getCloser() {
e.style.left = String(left + (stepX * currentStep)) + 'px'
e.style.top = String(top + (stepY * currentStep)) + 'px'
if (currentStep < steps) {
setTimeout(() => {
currentStep += 1
if (currentStep === steps) {
e.style.left = String(targetX + 5) + 'px'
e.style.top = String(targetY + 1) + 'px'
} else {
getCloser()
}
}, duration/steps)
}
}
getCloser()
}`
await browser.execute(js)
await sleep(550, true)
await element.moveTo()
}
export async function clickOnHistory(browser: Browser<void>) {
const messageHistory = await browser.$('//span/*[contains(text(), "History")]')
await clickOn(messageHistory, browser)
}
export async function clickOn(element: Element<void>, browser: Browser<void>, clicks = 1) {
await moveToCenterOfElement(element, browser)
for (let i = 0; i < clicks; i += 1) {
await element.click()
await sleep(50)
}
}
export async function createFakeMousePointer(browser: Browser<void>) {
const addCursorImage = 'const i=document.createElement("img");'
+ 'i.src="../cursor.png";'
+ 'i.width="32";'
+ 'i.height="32";'
+ 'i.id="bier";'
+ 'i.style="position: fixed; z-index:10000000; filter: invert(100%);left: 0px; top: 0px;";'
+ 'document.body.appendChild(i)'
await browser.execute(addCursorImage)
}
export async function showText(text: string, duration: number = 0, browser: Browser<void>, location: 'top' | 'bottom' | 'middle' = 'bottom') {
const positions = {
top: 0,
bottom: -65,
middle: -32,
}
const js = `
const postition = "${positions[location]}vh"
let previousDiv = document.getElementById('tests-text-overlay')
previousDiv && previousDiv.remove()
let div = document.createElement('div')
div.id = "tests-text-overlay"
div.style = "background-color: rgba(0, 0, 0, 0.8);position: fixed;left: 5vw;z-index: 1000000;margin: 30vw auto 50vw;border-radius: 16px;right: 5vw;bottom: "+ postition +";"
let div2 = document.createElement('div')
div2.style = "text-align: center;font-size: 4em;color: white;"
div2.innerHTML = \`${text}\`
div.appendChild(div2)
document.body.appendChild(div)
if (${duration} > 0) {
setTimeout(() => div.remove(), ${duration})
}
`
browser.execute(js)
}
export async function hideText(browser: Browser<void>) {
const js = `
let previousDiv = document.getElementById('tests-text-overlay')
previousDiv && previousDiv.remove()
`
browser.execute(js)
await sleep(600)
}
+101
View File
@@ -0,0 +1,101 @@
process.on('unhandledRejection', (error: Error) => {
console.error('unhandledRejection', error.message, error.stack)
process.exit(1)
})
import * as webdriverio from 'webdriverio'
import * as os from 'os'
import mockMqtt, { stop } from './mock-mqtt'
import { connectTo } from './scenarios/connect'
import { disconnect } from './scenarios/disconnect'
import { showNumericPlot } from './scenarios/showNumericPlot'
import { showJsonPreview } from './scenarios/showJsonPreview'
import { compareJsonSideBySide } from './scenarios/compareJsonSideBySide'
import { searchTree, clearSearch } from './scenarios/searchTree'
import { copyTopicToClipboard } from './scenarios/copyTopicToClipboard'
import { copyValueToClipboard } from './scenarios/copyValueToClipboard'
import { publishTopic } from './scenarios/publishTopic'
import { clearOldTopics } from './scenarios/clearOldTopics'
import { showMenu } from './scenarios/showMenu'
import { createFakeMousePointer, sleep, showText, hideText, clickOnHistory } from './util'
const binary = os.platform() === 'darwin' ? 'Electron.app/Contents/MacOS/Electron' : 'electron'
const fullscreen = os.platform() === 'darwin' ? [] : ['--fullscreen']
const options = {
host: 'localhost', // Use localhost as chrome driver server
port: 9515, // "9515" is the port opened by chrome driver.
capabilities: {
browserName: 'electron',
chromeOptions: {
binary: `${__dirname}/../../../node_modules/electron/dist/${binary}`,
args: [`--app=${__dirname}/../../..`, '--force-device-scale-factor=1', '--no-sandbox', '--disable-dev-shm-usage', '--disable-extensions'].concat(fullscreen),
},
windowTypes: ['app', 'webview'],
},
}
async function doStuff() {
await mockMqtt()
const browser = await webdriverio.remote(options)
await createFakeMousePointer(browser)
await connectTo('127.0.0.1', browser)
await sleep(1000)
await sleep(1000)
await showText('Overview of topics', 2000, browser, 'top')
await sleep(2000)
await showText('Indicates which topics change', 2000, browser, 'bottom')
await sleep(3000)
await showText('Plot topics', 2000, browser)
await showNumericPlot(browser)
await sleep(2000)
await showText('Formatted messages', 2000, browser, 'top')
await showJsonPreview(browser)
await sleep(2000)
await showText('Compare messages', 2000, browser, 'top')
await compareJsonSideBySide(browser)
await hideText(browser)
await showText('Publish topics', 2000, browser, 'top')
await clickOnHistory(browser)
await publishTopic(browser)
await sleep(1000)
await showText('Copy to Clipboard', 2000, browser)
await copyTopicToClipboard(browser)
await hideText(browser)
await copyValueToClipboard(browser)
await sleep(1000)
await showText('Search topic hierarchy', 0, browser, 'middle')
await searchTree('temp', browser)
await hideText(browser)
await showText('Topics containing "temp"', 1500, browser)
await sleep(1500)
await clearSearch(browser)
await sleep(1000)
await hideText(browser)
await showText('Delete retained topics', 0, browser)
await clearOldTopics(browser)
await hideText(browser)
await showText('Display Options', 2000, browser)
await showMenu(browser)
await sleep(2000)
await disconnect(browser)
await sleep(3000)
browser.closeWindow()
stop()
}
doStuff()
+1 -1
View File
@@ -11,5 +11,5 @@
"lib": ["es2017", "dom"],
"sourceMap": true
},
"include": ["src/electron.ts"]
"include": ["src/electron.ts", "src/spec/electron.ts", "src/spec/webdriverio.ts"]
}
+4031
View File
File diff suppressed because it is too large Load Diff