Travis ui tests (#57)
* Prepare travis is tests * Fix ffmpeg travis source * Trying xenial * Move shell scripts * Upload video assets * Upload video assets * Change text input method * Add ui test docker support * Fix travis docker build * Fix asset uploader * Fix dockerfile * Update dockerfile * Change writeText behavior * Fix type error * Fix exit codes * Fix types * fix upload * Fix writeText * Fix argument name * Add test scenarios * Enable vnc and change mqtt host
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- xvfb
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
@@ -17,12 +17,19 @@ os:
|
|||||||
- linux
|
- linux
|
||||||
- osx
|
- osx
|
||||||
|
|
||||||
|
dist: xenial
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- yarn install
|
- yarn install
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker build docker --tag uitest; fi;
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- yarn run build
|
- yarn run build
|
||||||
- yarn test
|
- 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_TAG" != "" ]]; then yarn run prepare-release; fi
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$TRAVIS_TAG" != "" ]]; then yarn run package -- linux; 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 -- mac; fi
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ class Publish extends React.Component<Props, State> {
|
|||||||
size="small"
|
size="small"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={this.publish}
|
onClick={this.publish}
|
||||||
|
id="publish-button"
|
||||||
>
|
>
|
||||||
<Navigation style={{ marginRight: '8px' }} /> Publish
|
<Navigation style={{ marginRight: '8px' }} /> Publish
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
17
docker/Dockerfile
Normal file
17
docker/Dockerfile
Normal 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
docker/cloneBuildAndTest.sh
Executable file
12
docker/cloneBuildAndTest.sh
Executable 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
docker/testMounted.sh
Executable file
5
docker/testMounted.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
yarn ui-test
|
||||||
|
yarn upload-video-artifacts
|
||||||
@@ -9,9 +9,11 @@
|
|||||||
"install": "cd app; yarn; cd ..",
|
"install": "cd app; yarn; cd ..",
|
||||||
"build": "tsc && cd app && yarn run build && cd ..",
|
"build": "tsc && cd app && yarn run build && cd ..",
|
||||||
"test-backend": "cd backend && yarn run test && cd ..",
|
"test-backend": "cd backend && yarn run test && cd ..",
|
||||||
"prepare-release": "./prepare-release.sh",
|
"prepare-release": "./scripts/prepare-release.sh",
|
||||||
"package": "ts-node package.ts",
|
"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": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -42,6 +44,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.1.7",
|
"@types/chai": "^4.1.7",
|
||||||
|
"@types/mime": "^2.0.0",
|
||||||
"@types/mocha": "^5.2.5",
|
"@types/mocha": "^5.2.5",
|
||||||
"@types/mustache": "^0.8.32",
|
"@types/mustache": "^0.8.32",
|
||||||
"@types/node": "^10.12.18",
|
"@types/node": "^10.12.18",
|
||||||
@@ -51,6 +54,7 @@
|
|||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"electron": "^4.0.2",
|
"electron": "^4.0.2",
|
||||||
"electron-builder": "^20.38.4",
|
"electron-builder": "^20.38.4",
|
||||||
|
"mime": "^2.4.0",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"mustache": "^3.0.1",
|
"mustache": "^3.0.1",
|
||||||
"nyc": "^13.1.0",
|
"nyc": "^13.1.0",
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
cp app.mp4 original.mp4
|
||||||
# The video starts with a few blank frames, we want to know when the stop
|
# 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
|
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`
|
END_OF_BLACK=`cat ffmpeg_info | grep end | head -n1 | cut -d'=' -f2`
|
||||||
|
|
||||||
# Find crop values for black bars
|
# Trim black frames at start
|
||||||
CROP=`ffmpeg -i app.mp4 -t 1 -vf cropdetect -f null - 2>&1 | awk '/crop/ { print $NF }' | head -n1`
|
ffmpeg -ss $END_OF_BLACK -i app.mp4 app2.mp4
|
||||||
|
|
||||||
# Crop and trim black frames at start
|
|
||||||
ffmpeg -ss $END_ONF_BLACK -i app.mp4 -vf "$CROP" app2.mp4
|
|
||||||
mv app2.mp4 app.mp4
|
mv app2.mp4 app.mp4
|
||||||
|
|
||||||
# Generate gif palette
|
# Generate gif palette
|
||||||
@@ -19,3 +16,6 @@ ffmpeg -i app.mp4 -i palette.png -filter_complex "fps=10,scale=720:-1:flags=lanc
|
|||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
rm ffmpeg_info palette.png
|
rm ffmpeg_info palette.png
|
||||||
|
|
||||||
|
mv app.mp4 ui-test.mp4
|
||||||
|
mv app.gif ui-test.gif
|
||||||
@@ -1,22 +1,26 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
function finish {
|
function finish {
|
||||||
echo "UUUUPS I crashed"
|
echo "Exiting, cleaning up"
|
||||||
tmux send-keys -t record q || echo "No tmux was running"
|
tmux send-keys -t record q || echo "No tmux was running"
|
||||||
echo kill $PID_XVFB $PID_CHROMEDRIVER $PID_MOSQUITTO
|
#echo kill $PID_XVFB $PID_CHROMEDRIVER $PID_MOSQUITTO
|
||||||
kill $PID_XVFB $PID_CHROMEDRIVER $PID_MOSQUITTO
|
#kill $PID_XVFB $PID_CHROMEDRIVER $PID_MOSQUITTO
|
||||||
}
|
}
|
||||||
|
|
||||||
trap finish EXIT
|
trap finish EXIT
|
||||||
|
|
||||||
set -e
|
DIMENSIONS="1024x700"
|
||||||
|
|
||||||
SCR=99
|
SCR=99
|
||||||
# Start new window manager
|
# Start new window manager
|
||||||
Xvfb :$SCR -screen 0 1024x800x24 -ac &
|
Xvfb :$SCR -screen 0 "$DIMENSIONS"x24 -ac &
|
||||||
export PID_XVFB=$!
|
export PID_XVFB=$!
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
|
# Debug with VNC
|
||||||
|
while [ "$TEST_EXIT_CODE" = "" ]; do x11vnc -passwd "bierbier" -display :$SCR; done &
|
||||||
|
export PID_VNC=$!
|
||||||
|
|
||||||
|
# Start mqtt broker
|
||||||
mosquitto &
|
mosquitto &
|
||||||
export PID_MOSQUITTO=$!
|
export PID_MOSQUITTO=$!
|
||||||
|
|
||||||
@@ -28,10 +32,12 @@ sleep 2
|
|||||||
rm ./app.mp4 || echo no need to delete ./app.mp4
|
rm ./app.mp4 || echo no need to delete ./app.mp4
|
||||||
|
|
||||||
# Start recoring in tmux
|
# Start recoring in tmux
|
||||||
tmux new-session -d -s record ffmpeg -f x11grab -draw_mouse 0 -video_size 1024x800 -i :$SCR -codec:v libx264 -r 20 ./app.mp4
|
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
|
# Start tests
|
||||||
node dist/src/spec/webdriverio.js
|
node dist/src/spec/webdriverio.js
|
||||||
|
TEST_EXIT_CODE=$?
|
||||||
|
echo "Webriver exitet with $TEST_EXIT_CODE"
|
||||||
|
|
||||||
# Stop recording
|
# Stop recording
|
||||||
tmux send-keys -t record q
|
tmux send-keys -t record q
|
||||||
@@ -40,4 +46,6 @@ tmux send-keys -t record q
|
|||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
# Process the video
|
# Process the video
|
||||||
./prepareVideo.sh
|
./scripts/prepareVideo.sh
|
||||||
|
|
||||||
|
exit $TEST_EXIT_CODE
|
||||||
86
scripts/uploadVideoAsset.ts
Executable file
86
scripts/uploadVideoAsset.ts
Executable 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()
|
||||||
@@ -14,6 +14,8 @@ if (!isDev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isDebugEnabled = Boolean(process.argv.find(arg => arg === 'debug'))
|
const isDebugEnabled = Boolean(process.argv.find(arg => arg === 'debug'))
|
||||||
|
const isFullscreen = Boolean(process.argv.find(arg => arg === '--fullscreen'))
|
||||||
|
|
||||||
require('electron-debug')({ enabled: isDebugEnabled })
|
require('electron-debug')({ enabled: isDebugEnabled })
|
||||||
|
|
||||||
autoUpdater.logger = log
|
autoUpdater.logger = log
|
||||||
@@ -41,7 +43,10 @@ function createWindow() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
mainWindow.once('ready-to-show', () => {
|
mainWindow.once('ready-to-show', () => {
|
||||||
mainWindow && mainWindow.show()
|
if (mainWindow) {
|
||||||
|
isFullscreen && mainWindow.setFullScreen(true)
|
||||||
|
mainWindow.show()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('icon path', iconPath)
|
console.log('icon path', iconPath)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function startServer(): Promise<mqtt.MqttClient> {
|
|||||||
|
|
||||||
function connectMqtt(): Promise<mqtt.MqttClient> {
|
function connectMqtt(): Promise<mqtt.MqttClient> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const client = mqtt.connect('mqtt://localhost:1883', { username: 'thomas', password: 'bierbier' })
|
const client = mqtt.connect('mqtt://127.0.0.1:1883', { username: 'thomas', password: 'bierbier' })
|
||||||
client.once('connect', () => {
|
client.once('connect', () => {
|
||||||
resolve(client)
|
resolve(client)
|
||||||
})
|
})
|
||||||
@@ -52,6 +52,15 @@ function generateData(client: mqtt.MqttClient) {
|
|||||||
client.publish('livingroom/lamp-1/brightness', '48', { 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/state', 'off', { retain: true, qos: 0 })
|
||||||
client.publish('livingroom/lamp-2/brightness', '48', { 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/temperature', temperature()), 1500))
|
||||||
intervals.push(setInterval(() => client.publish('kitchen/humidity', temperature(60, -5, 0)), 1800))
|
intervals.push(setInterval(() => client.publish('kitchen/humidity', temperature(60, -5, 0)), 1800))
|
||||||
|
|
||||||
@@ -66,12 +75,32 @@ function generateData(client: mqtt.MqttClient) {
|
|||||||
// Used for demonstrating "clean up"
|
// Used for demonstrating "clean up"
|
||||||
client.publish('test 123', 'Hello world', { retain: true, qos: 0 })
|
client.publish('test 123', 'Hello world', { retain: true, qos: 0 })
|
||||||
client.publish('hello', 'sunshine', { 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 })
|
client.publish('01-80-C2-00-00-0F/LWT', 'offline', { retain: true, qos: 0 })
|
||||||
|
|
||||||
intervals.push(setInterval(() => {
|
intervals.push(setInterval(() => {
|
||||||
client.publish('3d-printer/OctoPrint/temperature/bed', '{"_timestamp":1548589083,"actual":25.9,"target":0}')
|
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}')
|
client.publish('3d-printer/OctoPrint/temperature/tool0', '{"_timestamp":1548589093,"actual":26.4,"target":0}')
|
||||||
}, 3333))
|
}, 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
|
export default startServer
|
||||||
|
|||||||
14
src/spec/scenarios/compareJsonSideBySide.ts
Normal file
14
src/spec/scenarios/compareJsonSideBySide.ts
Normal file
@@ -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)
|
||||||
|
}
|
||||||
7
src/spec/scenarios/disconnect.ts
Normal file
7
src/spec/scenarios/disconnect.ts
Normal 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
src/spec/scenarios/publishTopic.ts
Normal file
23
src/spec/scenarios/publishTopic.ts
Normal 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)
|
||||||
|
}
|
||||||
@@ -1,8 +1,15 @@
|
|||||||
import { clickOn, sleep, writeText } from '../util'
|
import { clickOn, sleep, writeText, delteTextWithBackspaces, showText } from '../util'
|
||||||
import { Browser } from 'webdriverio'
|
import { Browser } from 'webdriverio'
|
||||||
|
|
||||||
export async function searchTree(browser: Browser<void>) {
|
export async function searchTree(text: string, browser: Browser<void>) {
|
||||||
const searchField = await browser.$('//input[contains(@placeholder, "Search")]')
|
const searchField = await browser.$('//input[contains(@placeholder, "Search")]')
|
||||||
await clickOn(searchField, browser, 1)
|
await clickOn(searchField, browser, 1)
|
||||||
writeText('temp', browser)
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { clickOn, sleep, writeText, expandTopic } from '../util'
|
|||||||
import { Browser } from 'webdriverio'
|
import { Browser } from 'webdriverio'
|
||||||
|
|
||||||
export async function showJsonPreview(browser: Browser<void>) {
|
export async function showJsonPreview(browser: Browser<void>) {
|
||||||
await expandTopic('3d-printer/OctoPrint/temperature/bed', browser)
|
await expandTopic('actuality/showcase', browser)
|
||||||
|
|
||||||
await sleep(1000)
|
await sleep(1000)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { clickOn, sleep, writeText, expandTopic } from '../util'
|
import { clickOn, sleep, writeText, expandTopic, clickOnHistory } from '../util'
|
||||||
import { Browser } from 'webdriverio'
|
import { Browser } from 'webdriverio'
|
||||||
|
|
||||||
export async function showNumericPlot(browser: Browser<void>) {
|
export async function showNumericPlot(browser: Browser<void>) {
|
||||||
await expandTopic('livingroom/temperature', browser)
|
await expandTopic('livingroom/temperature', browser)
|
||||||
|
|
||||||
const messageHistory = await browser.$('//span/*[contains(text(), "History")]')
|
await clickOnHistory(browser)
|
||||||
await clickOn(messageHistory, browser, 1)
|
|
||||||
|
|
||||||
await sleep(1000)
|
await sleep(1000)
|
||||||
await expandTopic('livingroom/humidity', browser)
|
await expandTopic('livingroom/humidity', browser)
|
||||||
|
|||||||
@@ -1,17 +1,29 @@
|
|||||||
import { Element, Browser } from 'webdriverio'
|
import { Element, Browser } from 'webdriverio'
|
||||||
export { expandTopic } from './expandTopic'
|
export { expandTopic } from './expandTopic'
|
||||||
|
|
||||||
export function sleep(ms: number) {
|
export function sleep(ms: number, required = false) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(resolve, ms)
|
if (required) {
|
||||||
|
setTimeout(resolve, ms)
|
||||||
|
} else {
|
||||||
|
setTimeout(resolve, ms)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function writeText(text: string, to: Browser<void>) {
|
export async function writeText(text: string, browser: Browser<void>, delay = 0) {
|
||||||
text.split('').forEach(async (c) => {
|
for (const c of text.split('')) {
|
||||||
await to.keys([c])
|
await browser.keys([c])
|
||||||
await sleep(50)
|
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>) {
|
export async function moveToCenterOfElement(element: Element<void>, browser: Browser<void>) {
|
||||||
@@ -37,22 +49,32 @@ export async function moveToCenterOfElement(element: Element<void>, browser: Bro
|
|||||||
const stepY = deltaY / steps
|
const stepY = deltaY / steps
|
||||||
let currentStep = 0
|
let currentStep = 0
|
||||||
function getCloser() {
|
function getCloser() {
|
||||||
e.style.left = String(left + (stepX * currentStep)) + 5 + 'px'
|
e.style.left = String(left + (stepX * currentStep)) + 'px'
|
||||||
e.style.top = String(top + (stepY * currentStep)) + 5 + 'px'
|
e.style.top = String(top + (stepY * currentStep)) + 'px'
|
||||||
if (currentStep < steps) {
|
if (currentStep < steps) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
currentStep += 1
|
currentStep += 1
|
||||||
getCloser()
|
if (currentStep === steps) {
|
||||||
|
e.style.left = String(targetX + 5) + 'px'
|
||||||
|
e.style.top = String(targetY + 1) + 'px'
|
||||||
|
} else {
|
||||||
|
getCloser()
|
||||||
|
}
|
||||||
}, duration/steps)
|
}, duration/steps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getCloser()
|
getCloser()
|
||||||
}`
|
}`
|
||||||
await browser.execute(js)
|
await browser.execute(js)
|
||||||
await sleep(550)
|
await sleep(550, true)
|
||||||
await element.moveTo()
|
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) {
|
export async function clickOn(element: Element<void>, browser: Browser<void>, clicks = 1) {
|
||||||
await moveToCenterOfElement(element, browser)
|
await moveToCenterOfElement(element, browser)
|
||||||
for (let i = 0; i < clicks; i += 1) {
|
for (let i = 0; i < clicks; i += 1) {
|
||||||
@@ -71,25 +93,24 @@ export async function createFakeMousePointer(browser: Browser<void>) {
|
|||||||
+ 'document.body.appendChild(i)'
|
+ 'document.body.appendChild(i)'
|
||||||
|
|
||||||
await browser.execute(addCursorImage)
|
await browser.execute(addCursorImage)
|
||||||
|
|
||||||
const onMouseMove = `document.onmousemove = (event) => {
|
|
||||||
const e = document.getElementById('bier')
|
|
||||||
e.style.left = (event.pageX+1) + 'px'
|
|
||||||
e.style.top = event.pageY + 'px'
|
|
||||||
}`
|
|
||||||
await browser.execute(onMouseMove)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function showText(text: string, duration: number = 0, browser: Browser<void>) {
|
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 js = `
|
||||||
|
const postition = "${positions[location]}vh"
|
||||||
let previousDiv = document.getElementById('tests-text-overlay')
|
let previousDiv = document.getElementById('tests-text-overlay')
|
||||||
previousDiv && previousDiv.remove()
|
previousDiv && previousDiv.remove()
|
||||||
let div = document.createElement('div')
|
let div = document.createElement('div')
|
||||||
div.id = "tests-text-overlay"
|
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: -65vh;"
|
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')
|
let div2 = document.createElement('div')
|
||||||
div2.style = "text-align: center;font-size: 4em;color: white;"
|
div2.style = "text-align: center;font-size: 4em;color: white;"
|
||||||
div2.innerHTML = "${text}"
|
div2.innerHTML = \`${text}\`
|
||||||
div.appendChild(div2)
|
div.appendChild(div2)
|
||||||
document.body.appendChild(div)
|
document.body.appendChild(div)
|
||||||
if (${duration} > 0) {
|
if (${duration} > 0) {
|
||||||
|
|||||||
@@ -1,22 +1,28 @@
|
|||||||
process.on('unhandledRejection', (error: Error) => {
|
process.on('unhandledRejection', (error: Error) => {
|
||||||
console.error('unhandledRejection', error.message, error.stack);
|
console.error('unhandledRejection', error.message, error.stack)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
});
|
})
|
||||||
|
|
||||||
import * as webdriverio from 'webdriverio'
|
import * as webdriverio from 'webdriverio'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import mockMqtt, { stop } from './mock-mqtt'
|
import mockMqtt, { stop } from './mock-mqtt'
|
||||||
import { connectTo } from './scenarios/connect'
|
import { connectTo } from './scenarios/connect'
|
||||||
|
import { disconnect } from './scenarios/disconnect'
|
||||||
import { showNumericPlot } from './scenarios/showNumericPlot'
|
import { showNumericPlot } from './scenarios/showNumericPlot'
|
||||||
import { showJsonPreview } from './scenarios/showJsonPreview'
|
import { showJsonPreview } from './scenarios/showJsonPreview'
|
||||||
|
import { compareJsonSideBySide } from './scenarios/compareJsonSideBySide'
|
||||||
|
import { searchTree, clearSearch } from './scenarios/searchTree'
|
||||||
import { copyTopicToClipboard } from './scenarios/copyTopicToClipboard'
|
import { copyTopicToClipboard } from './scenarios/copyTopicToClipboard'
|
||||||
import { copyValueToClipboard } from './scenarios/copyValueToClipboard'
|
import { copyValueToClipboard } from './scenarios/copyValueToClipboard'
|
||||||
|
import { publishTopic } from './scenarios/publishTopic'
|
||||||
import { clearOldTopics } from './scenarios/clearOldTopics'
|
import { clearOldTopics } from './scenarios/clearOldTopics'
|
||||||
import { showMenu } from './scenarios/showMenu'
|
import { showMenu } from './scenarios/showMenu'
|
||||||
|
|
||||||
import { createFakeMousePointer, sleep, showText, hideText } from './util'
|
import { createFakeMousePointer, sleep, showText, hideText, clickOnHistory } from './util'
|
||||||
|
|
||||||
const binary = os.platform() === 'darwin' ? 'Electron.app/Contents/MacOS/Electron' : 'electron'
|
const binary = os.platform() === 'darwin' ? 'Electron.app/Contents/MacOS/Electron' : 'electron'
|
||||||
|
const fullscreen = os.platform() === 'darwin' ? [] : ['--fullscreen']
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
host: 'localhost', // Use localhost as chrome driver server
|
host: 'localhost', // Use localhost as chrome driver server
|
||||||
port: 9515, // "9515" is the port opened by chrome driver.
|
port: 9515, // "9515" is the port opened by chrome driver.
|
||||||
@@ -24,7 +30,7 @@ const options = {
|
|||||||
browserName: 'electron',
|
browserName: 'electron',
|
||||||
chromeOptions: {
|
chromeOptions: {
|
||||||
binary: `${__dirname}/../../../node_modules/electron/dist/${binary}`,
|
binary: `${__dirname}/../../../node_modules/electron/dist/${binary}`,
|
||||||
args: [`--app=${__dirname}/../../..`, '--force-device-scale-factor=1', '--no-sandbox', '--disable-dev-shm-usage', '--disable-extensions'],
|
args: [`--app=${__dirname}/../../..`, '--force-device-scale-factor=1', '--no-sandbox', '--disable-dev-shm-usage', '--disable-extensions'].concat(fullscreen),
|
||||||
},
|
},
|
||||||
windowTypes: ['app', 'webview'],
|
windowTypes: ['app', 'webview'],
|
||||||
},
|
},
|
||||||
@@ -35,28 +41,58 @@ async function doStuff() {
|
|||||||
const browser = await webdriverio.remote(options)
|
const browser = await webdriverio.remote(options)
|
||||||
await createFakeMousePointer(browser)
|
await createFakeMousePointer(browser)
|
||||||
|
|
||||||
await connectTo('localhost', browser)
|
await connectTo('127.0.0.1', browser)
|
||||||
await sleep(2000) // Allow some topics to pour in
|
await sleep(1000)
|
||||||
await showText('Plotting topics', 0, browser)
|
|
||||||
|
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 showNumericPlot(browser)
|
||||||
await sleep(2000)
|
await sleep(2000)
|
||||||
await hideText(browser)
|
|
||||||
await showText('JSON preview', 0, browser)
|
await showText('Formatted messages', 2000, browser, 'top')
|
||||||
await showJsonPreview(browser)
|
await showJsonPreview(browser)
|
||||||
await sleep(2000)
|
await sleep(2000)
|
||||||
|
|
||||||
|
await showText('Compare messages', 2000, browser, 'top')
|
||||||
|
await compareJsonSideBySide(browser)
|
||||||
await hideText(browser)
|
await hideText(browser)
|
||||||
await showText('Copy&Paste data', 2000, browser)
|
|
||||||
await copyTopicToClipboard(browser)
|
await showText('Publish topics', 2000, browser, 'top')
|
||||||
|
await clickOnHistory(browser)
|
||||||
|
await publishTopic(browser)
|
||||||
await sleep(1000)
|
await sleep(1000)
|
||||||
|
|
||||||
|
await showText('Copy to Clipboard', 2000, browser)
|
||||||
|
await copyTopicToClipboard(browser)
|
||||||
await hideText(browser)
|
await hideText(browser)
|
||||||
await copyValueToClipboard(browser)
|
await copyValueToClipboard(browser)
|
||||||
await sleep(1000)
|
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 hideText(browser)
|
||||||
await showText('Delete retained topics', 0, browser)
|
await showText('Delete retained topics', 0, browser)
|
||||||
await clearOldTopics(browser)
|
await clearOldTopics(browser)
|
||||||
await hideText(browser)
|
await hideText(browser)
|
||||||
await showText('Settings', 3000, browser)
|
|
||||||
|
await showText('Display Options', 2000, browser)
|
||||||
await showMenu(browser)
|
await showMenu(browser)
|
||||||
|
|
||||||
|
await sleep(2000)
|
||||||
|
await disconnect(browser)
|
||||||
|
await sleep(3000)
|
||||||
|
|
||||||
browser.closeWindow()
|
browser.closeWindow()
|
||||||
|
|
||||||
stop()
|
stop()
|
||||||
|
|||||||
Reference in New Issue
Block a user