From 1723783da7289c3162373fd4e05fb3feed56ca39 Mon Sep 17 00:00:00 2001 From: darialutkova <daria.lutkova@leroymerlin.ru> Date: Fri, 15 May 2020 13:18:02 +0300 Subject: [PATCH] Close Issue #11: Implement Progress Bar --- package-lock.json | 9 +++++--- server/index.js | 19 ++++++++++++++-- src/App.js | 15 +++++++++--- src/api.js | 10 ++++++++ src/subpages/Tests.js | 53 +++++++++++++++++++++++++++++++++++-------- 5 files changed, 88 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 79afaa1..cd09df5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2709,7 +2709,8 @@ }, "kind-of": { "version": "6.0.2", - "resolved": "" + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -5587,7 +5588,8 @@ }, "kind-of": { "version": "6.0.2", - "resolved": "" + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -12295,7 +12297,8 @@ }, "kind-of": { "version": "6.0.2", - "resolved": "" + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, diff --git a/server/index.js b/server/index.js index b6989af..34499f6 100644 --- a/server/index.js +++ b/server/index.js @@ -10,6 +10,8 @@ const TastyRunner = require(path.resolve(process.cwd(), 'index.js')); const { version } = require('../package.json'); const config = require('../config'); +const PROJECT_NAME = config.get('name'); + const io = socket(server); TastyRunner.logStream.on('data', (data) => { @@ -43,16 +45,23 @@ app.get('/api/reports/:id', async (req, res) => { app.post('/api/test', (req, res) => { const filters = req.body.data; + const tests = filters.tests.length; + let done = 0; io.emit('tests:start'); TastyRunner.setFilters(filters); - TastyRunner.run(filters.type) + TastyRunner.run(filters.type, false, [], { + onTestEnd: () => { + done ++; + io.emit('tests:test:finished', Math.round((done / tests) * 100)); + }, + }) .then((stats) => { io.emit('tests:end', stats); - config.get('notification').map(notification => { + config.get('notification') && config.get('notification').map(notification => { if (notification.type) { io.emit('tests:test', notification.type); const { isNotificationEnabled, notify, getMessage } = require(`./notification/${notification.type}`); @@ -98,4 +107,10 @@ app.get('/api/version', (req, res) => { }); }); +app.get('/api/name', (req, res) => { + res.json({ + name: PROJECT_NAME + }); +}); + module.exports = server; diff --git a/src/App.js b/src/App.js index b411bc7..177d732 100644 --- a/src/App.js +++ b/src/App.js @@ -1,17 +1,26 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { BrowserRouter as Router, NavLink, Redirect, Route, Switch } from 'react-router-dom'; import { Container, Nav, Navbar } from 'react-bootstrap'; import Testing from './pages/Testing'; import Reports from './pages/Reports'; -const PROJECT_NAME = 'Lego'; +import { getProjectName } from "./api"; function App() { + const [ProjectName, setProjectName] = useState(''); + + useEffect( () => { + (async () => { + const res = await getProjectName(); + setProjectName(res.name) + })() +}, []); + return ( <Router> <Navbar variant="dark" bg="dark" expand="lg"> - <Navbar.Brand as={NavLink} to="/">{`${PROJECT_NAME} Tasty Point`}</Navbar.Brand> + <Navbar.Brand as={NavLink} to="/">{`${ProjectName} Tasty Point`}</Navbar.Brand> <Nav className="mr-auto"> <NavLink to="/tests" className="nav-link">Tests</NavLink> <NavLink to="/reports" className="nav-link">Reports</NavLink> diff --git a/src/api.js b/src/api.js index 7dacdd8..e589fd6 100644 --- a/src/api.js +++ b/src/api.js @@ -65,3 +65,13 @@ export const getLog = async () => { return null; } }; + +export const getProjectName = async () => { + try { + const res = await axios.get('/api/name'); + + return res.data; + } catch (err) { + return null; + } +}; diff --git a/src/subpages/Tests.js b/src/subpages/Tests.js index ff5de0c..a851364 100644 --- a/src/subpages/Tests.js +++ b/src/subpages/Tests.js @@ -1,5 +1,5 @@ import React from 'react'; -import { Badge, Button, Col, ListGroup, ProgressBar, Row, Spinner, Toast } from 'react-bootstrap'; +import { Badge, Button, Col, ListGroup, Row, Spinner, Toast, ProgressBar } from 'react-bootstrap'; import _ from 'lodash'; import * as api from '../api'; import { FaPlay as Run } from 'react-icons/fa'; @@ -15,6 +15,7 @@ class Tests extends React.Component { funcLog: '', loadLog: '', errors: [], + percentage: 0, }; socket = socketIOClient(); @@ -39,29 +40,53 @@ class Tests extends React.Component { }); this.socket.on('tests:start', () => { - this.setState({ loading: true }); + localStorage.setItem('func_log', ''); + localStorage.setItem('load_log', ''); + + this.setState({ + loading: true, + }); }); this.socket.on('tests:end', (stats) => { - this.setState({ loading: false, stats }); + this.setState({ + loading: false, + stats, + percentage: 0 + }); localStorage.setItem(this.type === 'func' ? 'func_stats' : 'load_stats', JSON.stringify(stats)); }); this.socket.on('tests:error', (err) => { this.setState((prevState) => ({ loading: false, + percentage: 0, errors: [...prevState.errors, err] })); }); this.socket.on('tests:func:log', (log) => { - localStorage.setItem('func_log', this.state.funcLog + log); - this.setState({ funcLog: this.state.funcLog + log }); + localStorage.setItem('func_log', localStorage.getItem('func_log') + log ); }); + this.socket.on('tests:func:log', _.throttle(this.setFuncLog, 300, { + trailing: true, + leading: false + })); + this.socket.on('tests:load:log', (log) => { - localStorage.setItem('load_log', this.state.loadLog + log); - this.setState({ loadLog: this.state.loadLog + log }); + localStorage.setItem('load_log', localStorage.getItem('load_log') + log); + }); + + this.socket.on('tests:load:log', _.throttle(this.setLoadLog, 300, { + trailing: true, + leading: false + })); + + this.socket.on('tests:test:finished', (percentage) => { + this.setState({ + percentage + }) }) } @@ -87,6 +112,14 @@ class Tests extends React.Component { } } + setFuncLog = () => { + this.setState({ funcLog: localStorage.getItem('func_log') }) + }; + + setLoadLog = () => { + this.setState({ loadLog: localStorage.getItem('load_log') }); + }; + handleToggle = (test) => { const { selected } = this.state; @@ -107,7 +140,7 @@ class Tests extends React.Component { const selectedTests = _.get(this.state, 'selected', this.state.tests); const filters = { type: this.type, - tests: selectedTests.map(test => test.id), + tests: selectedTests.length ? selectedTests.map(test => test.id) : this.state.tests.map(test => test.id), }; this.setState({ @@ -198,7 +231,7 @@ class Tests extends React.Component { }; render() { - const { tests, errors } = this.state; + const { tests, errors, percentage } = this.state; if (!tests) return <Spinner />; @@ -214,7 +247,7 @@ class Tests extends React.Component { </div> {this.state.loading ? ( <Col> - <ProgressBar now={0} className="my-auto" /> + <ProgressBar now={percentage} label={`${percentage}%`} animated striped className="my-auto" /> </Col> ) : ( this.renderStats()