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()