From a082d9bf056a23fe34006e5493fd7451dfdc4e92 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Wed, 23 Nov 2022 11:21:03 -0600 Subject: [PATCH 001/110] commit after reverting to working app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com>” --- __tests__/ServerRoutes.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/__tests__/ServerRoutes.test.js b/__tests__/ServerRoutes.test.js index 9a49d602..ae8dc698 100644 --- a/__tests__/ServerRoutes.test.js +++ b/__tests__/ServerRoutes.test.js @@ -1,4 +1,22 @@ const supertest = require('supertest'); +import { Done } from '@mui/icons-material'; +import { render, fireEvent, screen } from '@testing-library/react'; +import { request } from 'http'; +import app from '../app'; + +describe('GET test route', () => { + it('responds with json', () => { + request(app) + .getHeader('/test') + .set('Accept', 'application/json') + .expect(200) + .end((err, res) => { + if (err) return Done(err); + done(); + }); + }); +}); + // const server = require('../server/app'); // const request = supertest(server); From e153c9f7af1e9769875e202a62f5ea49acb5fba0 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Wed, 23 Nov 2022 12:39:59 -0600 Subject: [PATCH 002/110] quick change --- __tests__/ContainersTab.test.js | 51 ++++++++++++++------- __tests__/ServerRoutes.test.js | 79 ++++++++++++++++++++++++++++----- __tests__/endToEnd.js | 26 +++++++++++ mocks/fileMock.js | 1 + 4 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 __tests__/endToEnd.js create mode 100644 mocks/fileMock.js diff --git a/__tests__/ContainersTab.test.js b/__tests__/ContainersTab.test.js index a7b8f6f9..d79e881f 100644 --- a/__tests__/ContainersTab.test.js +++ b/__tests__/ContainersTab.test.js @@ -1,10 +1,14 @@ import React, { Component } from 'react'; import Containers from '../src/components/tabs/Containers'; - +import {describe, expect, test, jest} from '@jest/globals'; +import '@testing-library/react'; +import '@testing-library/jest-dom'; +import { Chart } from 'react-chartjs-2'; +import {stop} from '../src/components/helper/commands.js' +import ToggleDisplay from '../src/components/display/ToggleDisplay'; // Started to migrate to React-Testing-Library... import { create } from 'react-test-renderer'; import { fireEvent, render, screen } from '@testing-library/react'; -import * as actions from '@testing-library/jest-dom'; const props = { runningList: [ @@ -27,20 +31,37 @@ const props = { Created: '2 days ago', name: 'zealous_pare' } - ] + ], + stop: jest.fn() }; + + /** Docketeer 7.0 * This was the previous groups code, we left commented just incase it became useful down the road. */ // Debug test -// describe('Containers', () => { -// test('Renders the Container Component', () => { -// render(); -// // Screening the component -// screen.debug(); -// }); -// }); +describe('Containers', () => { + beforeAll(()=>{ + render(); + + }) + + describe('Running List containers', () => { + test('Stop button is called', async () => { + const stopButton = document.querySelector('.stop-btn') + await fireEvent.click(stopButton) + screen.debug() + expect(stopButton).toBeCalled + }); + test('Wanted to test toggle display',() => { + render() + screen.debug() + expect(1).toBe(1) + }) + }) + +}); // function shallowSetup() { // const props = { @@ -103,7 +124,7 @@ const props = { * These are all preliminary tests that were not veted out. Could be useful as a starting point. */ -//! NEED TO FIGURE OUT HOW TO ADD ONCLICK TEST +// ! NEED TO FIGURE OUT HOW TO ADD ONCLICK TEST // test('ClassName run-btn in stopped component have onClick function', () => { // const handleOnClick = jest.fn(); @@ -120,9 +141,9 @@ const props = { // describe('It should render the exited containers', () => { // const { reactWrapper } = shallowSetup(); -//! test('Should render
tag in Stopped', () => { -//! expect(reactWrapper.type()).toEqual('div'); -//! }); +// ! test('Should render
tag in Stopped', () => { +// ! expect(reactWrapper.type()).toEqual('div'); +// ! }); //* test('Should have className run-btn in Stopped component', () => { //* expect( @@ -154,4 +175,4 @@ describe('dummy test', () => { test('dummy test', () => { expect(2 + 2).toBe(4); }); -}); +}); \ No newline at end of file diff --git a/__tests__/ServerRoutes.test.js b/__tests__/ServerRoutes.test.js index ae8dc698..d2a5ce34 100644 --- a/__tests__/ServerRoutes.test.js +++ b/__tests__/ServerRoutes.test.js @@ -1,22 +1,77 @@ const supertest = require('supertest'); -import { Done } from '@mui/icons-material'; -import { render, fireEvent, screen } from '@testing-library/react'; -import { request } from 'http'; -import app from '../app'; +const request = require('supertest'); +const assert = require('assert'); +const express = require('express'); -describe('GET test route', () => { - it('responds with json', () => { +const app = express(); + +app.use('/test', (req, res) => { + res.status(200).json({ + success: true, + }); +}); + +describe('/test route', () => { + it('get request to test route', (done) => { + request(app).get('/test').expect('Content-Type', /json/).expect(200, done); + }); + it('post requeust to test route', (done) => { request(app) - .getHeader('/test') + .post('/test') + .send({ random: 'info' }) .set('Accept', 'application/json') - .expect(200) - .end((err, res) => { - if (err) return Done(err); - done(); - }); + .expect('Content-Type', /json/) + .expect(200, done); + }); + it('put request to test route', (done) => { + request(app) + .put('/test') + .send({ random: 'info' }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200, done); + }); + it('delete request to test route', (done) => { + request(app) + .delete('/test') + .send({ random: 'info' }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200, done); + }); +}); + +/* all route testing needed */ + +// signup route + + +describe('/signup route', () => { + it('get request to getAllUsers controller', (done) => { + request(app) + .get('/signup') + .send({ username: 'info'}) + // .expect('Content-Type', /json/) + .expect(200, done); }); }); +// setting route + +// logout route + +// login route + +// init route + +// db route + +// api route + +// admin route + +// account route + // const server = require('../server/app'); // const request = supertest(server); diff --git a/__tests__/endToEnd.js b/__tests__/endToEnd.js new file mode 100644 index 00000000..0fa19ae8 --- /dev/null +++ b/__tests__/endToEnd.js @@ -0,0 +1,26 @@ +import '@testing-library/react'; +import '@testing-library/jest-dom'; +import React, { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +// import { useSelector, useDispatch } from 'react-redux'; +import {render, fireEvent, screen} from '@testing-library/react'; +import {App} from '../src/renderer/App'; +import Login from '../src/components/login/login'; +// import AccountDisplay from '../src/components/display/AccountDisplay'; +import {BrowserRouter, Routes, Route} from 'react-router-dom'; +import { Provider } from 'react-redux'; +import store from '../src/renderer/store.js'; +// const store = require('../src/renderer/store'); + +describe('dummy test', () => { + it('checking render', () => { + render( + + + + + + ); + screen.debug(); + }); +}); \ No newline at end of file diff --git a/mocks/fileMock.js b/mocks/fileMock.js new file mode 100644 index 00000000..0f9f9a52 --- /dev/null +++ b/mocks/fileMock.js @@ -0,0 +1 @@ + module.exports = ''; \ No newline at end of file From 61f3c1be513d4e2a7fd9dfceffcb8395c7515970 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Wed, 23 Nov 2022 12:49:55 -0600 Subject: [PATCH 003/110] testing suite started MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com>” --- package.json | 3 ++- src/components/login/login.js | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 37775eb3..7d9a1fe6 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,8 @@ "^.+\\.jsx?$": "babel-jest" }, "moduleNameMapper": { + "\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/mocks/fileMock.js", "\\.(css|scss)$": "/__mocks__/styleMock.js" } } -} \ No newline at end of file +} diff --git a/src/components/login/login.js b/src/components/login/login.js index c99e9763..3d914515 100644 --- a/src/components/login/login.js +++ b/src/components/login/login.js @@ -20,18 +20,18 @@ import Docketeer from '../../../assets/docketeer-title.png'; const updateUser = (userInfo) => dispatch(actions.updateUser(userInfo)); // Need to set the app element to body for screen-readers (disability), otherwise modal will throw an error - useEffect(() => { - fetch("http://localhost:3000/db") - .then((response) => { - return response.json(); - }) - .then((data) => { - return console.log("Connected to DB successfully", data); - }) - .catch((err) => { - return console.log("Fetch request to /db failed:", err); - }); - }, []); + // useEffect(() => { + // fetch("http://localhost:3000/db") + // .then((response) => { + // return response.json(); + // }) + // .then((data) => { + // return console.log("Connected to DB successfully", data); + // }) + // .catch((err) => { + // return console.log("Fetch request to /db failed:", err); + // }); + // }, []); // callback function invoked when 'login' button is clicked const handleLogin = (e) => { From cfca1d87c9c0218f4cfae16abb63ba9f484f54fa Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Wed, 23 Nov 2022 15:55:00 -0600 Subject: [PATCH 004/110] initial updating of App.js to App.tsx Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- .gitignore | 4 +++- server/interfaces/interfaces.ts | 6 ++++++ .../{RenderViews.js => RenderViews.tsx} | 10 ++++++++-- src/components/login/login.js | 4 +++- src/renderer/App.js | 19 ------------------- src/renderer/App.tsx | 19 +++++++++++++++++++ src/renderer/index.tsx | 4 ++-- 7 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 server/interfaces/interfaces.ts rename src/components/{RenderViews.js => RenderViews.tsx} (77%) delete mode 100644 src/renderer/App.js create mode 100644 src/renderer/App.tsx diff --git a/.gitignore b/.gitignore index 1511f82d..d00dbc12 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,6 @@ src/components/display/.StoppedContainers.js.icloud src/database/docketeerdb .env yarn.lock -coverage \ No newline at end of file +coverage + +.history \ No newline at end of file diff --git a/server/interfaces/interfaces.ts b/server/interfaces/interfaces.ts new file mode 100644 index 00000000..9856625d --- /dev/null +++ b/server/interfaces/interfaces.ts @@ -0,0 +1,6 @@ + +const interfaces = { + +} + +export default interfaces; \ No newline at end of file diff --git a/src/components/RenderViews.js b/src/components/RenderViews.tsx similarity index 77% rename from src/components/RenderViews.js rename to src/components/RenderViews.tsx index c15802af..9f966898 100644 --- a/src/components/RenderViews.js +++ b/src/components/RenderViews.tsx @@ -5,9 +5,15 @@ import AdminView from './views/Admin'; import UserView from './views/UserView'; import SysAdminView from './views/SysAdmin'; -const RenderViews = (props) => { +interface RootState { + session: { + role: 'string' + } +} + +const RenderViews = () => { // grab current user's role - const role = useSelector((state) => state.session.role); + const role = useSelector((state: RootState) => state.session.role); if (role === 'system admin') { return (
diff --git a/src/components/login/login.js b/src/components/login/login.js index 3d914515..c16da41d 100644 --- a/src/components/login/login.js +++ b/src/components/login/login.js @@ -44,13 +44,15 @@ import Docketeer from '../../../assets/docketeer-title.png'; // clears input fields after login usernameInput.value = ""; passwordInput.value = ""; - + console.log("username:", username); + console.log("password:", password); authenticateUser(username, password); }; // callback function which will send request to endpoint http://localhost:3000/login and expect // either SSID in cookie. const authenticateUser = (username, password) => { + console.log("YOU ARE HERE!") fetch("http://localhost:3000/login", { method: "POST", headers: { diff --git a/src/renderer/App.js b/src/renderer/App.js deleted file mode 100644 index ba82ef6a..00000000 --- a/src/renderer/App.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { Routes, Route } from 'react-router-dom'; - -// Components -import Login from '../components/login/login.js'; -import RenderViews from '../components/RenderViews'; -import Authentication from '../components/Authentication' - -export const App = () => { - return ( - <> - - } /> - } /> - } /> - - - ); -}; diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx new file mode 100644 index 00000000..c1fd31e5 --- /dev/null +++ b/src/renderer/App.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Routes, Route } from 'react-router-dom'; + +// Components +import Login from '../components/login/login'; +import RenderViews from '../components/RenderViews'; +import Authentication from '../components/Authentication'; + +const App = () => { + return ( + + } /> + } /> + } /> + + ); +}; + +export default App; \ No newline at end of file diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index 61535e77..a4be5580 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -10,13 +10,13 @@ import '../components/css/styles.css'; import '../components/css/metric.css'; import '../components/css/running.css'; import '../components/css/static.css'; -import { App } from './App.js'; +import App from './App'; const rootNode = document.getElementById('root')!; const root = createRoot(rootNode) root.render( - + {/* */} From 36227b70b3f869fac21d95673080aafd4e8f50cb Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Wed, 23 Nov 2022 16:12:43 -0600 Subject: [PATCH 005/110] converted RenderViews to Typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/RenderViews.tsx | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/RenderViews.tsx b/src/components/RenderViews.tsx index 9f966898..0b04d6b7 100644 --- a/src/components/RenderViews.tsx +++ b/src/components/RenderViews.tsx @@ -7,7 +7,7 @@ import SysAdminView from './views/SysAdmin'; interface RootState { session: { - role: 'string' + role: string } } @@ -15,22 +15,25 @@ const RenderViews = () => { // grab current user's role const role = useSelector((state: RootState) => state.session.role); - if (role === 'system admin') { return ( -
- -
+ if (role === 'system admin') { + return ( +
+ +
) } - else if (role === 'admin') { return ( -
- -
+ else if (role === 'admin') { + return ( +
+ +
) } - else if (role === 'user') { return ( -
- -
+ else if (role === 'user') { + return ( +
+ +
) } }; From ccd28bc53b33314751c768fc503ee6828a89b4ca Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Wed, 23 Nov 2022 19:01:08 -0600 Subject: [PATCH 006/110] aunthentication file to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/{Authentication.js => Authentication.tsx} | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) rename src/components/{Authentication.js => Authentication.tsx} (71%) diff --git a/src/components/Authentication.js b/src/components/Authentication.tsx similarity index 71% rename from src/components/Authentication.js rename to src/components/Authentication.tsx index 90fa0335..ac699e74 100644 --- a/src/components/Authentication.js +++ b/src/components/Authentication.tsx @@ -2,9 +2,15 @@ import React from 'react'; import { Navigate } from 'react-router-dom'; import { useSelector } from 'react-redux'; +interface RootState { + session: { + isLoggedIn: boolean + } +} + const Authentication = () => { // grab session information from state - const session = useSelector((state) => state.session.isLoggedIn); + const session: boolean = useSelector((state: RootState) => state.session.isLoggedIn); // if session is false navigate to login, if session is true navigate to outlet return ( session ? : From efeebde71b3c9041f7f2dd537162a4148f6a4174 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Wed, 23 Nov 2022 19:41:02 -0600 Subject: [PATCH 007/110] some work done on Image Tab tests --- __tests__/ImageTab.test.js | 90 ++++++++++++++++++++++++-- __tests__/ServerRoutes.test.js | 15 ++++- __tests__/VolumeTab.test.js | 62 +++++++++++------- src/components/helper/initDatabase.js | 35 +++++----- src/redux/reducers/imageListReducer.js | 4 +- tsconfig.json | 3 +- 6 files changed, 158 insertions(+), 51 deletions(-) diff --git a/__tests__/ImageTab.test.js b/__tests__/ImageTab.test.js index f1a5f5e1..e2f7fb6b 100644 --- a/__tests__/ImageTab.test.js +++ b/__tests__/ImageTab.test.js @@ -2,10 +2,90 @@ * These tests do not work as enzyme is highly depricated and does not communicate with React 18 */ -// import React from 'react'; -// import { configure, shallow } from "enzyme"; -// import Adapter from "enzyme-adapter-react-16"; -// import Images from '../src/components/tabs/Images'; +import React from 'react'; +import { describe, expect, test, jest } from '@jest/globals'; +import Images from '../src/components/tabs/Images'; +import '@testing-library/react'; +import '@testing-library/jest-dom'; +import { + fireEvent, + getByLabelText, + getByTestId, + render, + screen, +} from '@testing-library/react'; +import { remove } from '../src/components/helper/commands'; + +const props = { + imagesList: [ + { + ID: '2718634043dc', + Size: '111 MB', + Name: 'redis', + }, + ], + // repo: ['repo'], + run: jest.fn(), +}; + +// Debug testing, checks for image box to render on screen +describe('Images', () => { + it('renders an image', () => { + render(); + }); +}); + +/* ----- button testing ------ */ + +// currently gets stuck at window.runExec method --> reads undefined +describe('run button on click', () => { + it('fires run button functionality', async () => { + const { container } = render(); + const runButton = screen.getByRole('button', { name: 'RUN' }); + await fireEvent.click(runButton); + expect(runButton).toBeCalled; + }); +}); + +// currently gets stuck at window.runExec method --> reads undefined +describe('pull button on click', () => { + it('fires pull button functionality', async () => { + const { container } = render(); + const pullButton = screen.getByRole('button', { name: 'Pull' }); + await fireEvent.click(pullButton); + expect(pullButton).toBeCalled; + }); +}); + +// currently gets stuck at window.runExec method --> reads undefined +describe('remove button on click', () => { + it('fires remove button functionality', async () => { + const { container } = render(); + const removeButton = screen.getByRole('button', { name: 'REMOVE' }); + await fireEvent.click(removeButton); + expect(removeButton).toBeCalled; + }); +}); + +// need test for text in input field? + +/* ------ actions/reducers ------ */ + +// Get Images +// Refresh Images +// Remove Images + + + +// describe('Rendered images', () => { +// test('run button works', async () => { +// const runButton = document.querySelector('.run-btn'); +// await fireEvent.click(runButton); +// screen.debug(); +// expect(runButton).toBeCalled; +// }); +// }); +// }); // configure({ adapter: new Adapter() }); // function shallowSetup() { @@ -71,4 +151,4 @@ describe('dummy test', () => { test('dummy test', () => { expect(2 + 2).toBe(4); }); -}); \ No newline at end of file +}); diff --git a/__tests__/ServerRoutes.test.js b/__tests__/ServerRoutes.test.js index d2a5ce34..f80c073c 100644 --- a/__tests__/ServerRoutes.test.js +++ b/__tests__/ServerRoutes.test.js @@ -45,19 +45,28 @@ describe('/test route', () => { // signup route - describe('/signup route', () => { it('get request to getAllUsers controller', (done) => { request(app) .get('/signup') - .send({ username: 'info'}) + .send({ username: 'info' }) + // .expect('Content-Type', /json/) + .expect(200, done); + }); + it('post request to signup route', (done) => { + request(app) + .post('/signup') + .send({ random: 'info' }) + .set('Accept', 'application/json') // .expect('Content-Type', /json/) .expect(200, done); }); }); // setting route - +// describe('/setting route', () => { +// it(''); +// }); // logout route // login route diff --git a/__tests__/VolumeTab.test.js b/__tests__/VolumeTab.test.js index 9dbb20f6..64fb7a41 100644 --- a/__tests__/VolumeTab.test.js +++ b/__tests__/VolumeTab.test.js @@ -2,31 +2,49 @@ * These tests do not work as enzyme is highly depricated and does not communicate with React 18 */ -// import React from 'react'; -// import { configure, shallow } from "enzyme"; -// import Adapter from "enzyme-adapter-react-16"; -// import VolumeTab from '../src/components/tabs/VolumeHistory'; + +import React from 'react'; +import {describe, expect, test, jest} from '@jest/globals'; +import VolumeTab from '../src/components/tabs/VolumeHistory'; +import '@testing-library/react'; +import '@testing-library/jest-dom'; +import { fireEvent, render, screen } from '@testing-library/react'; + + // configure({ adapter: new Adapter() }) -// describe('Volumes Tab', () => { -// const props = { -// volumeContainersList: [ -// { -// vol_name: 'volumetest1', -// containers: [ -// { Names: 'container1', State: 'Running', Status: '40 minutes ago' }, -// ] -// }, -// { -// vol_name: 'volumetest2', -// containers: [ -// { Names: 'container2', State: 'Running', Status: '25 minutes ago' }, -// { Names: 'container3', State: '', Status: '' } -// ] -// } -// ] -// }; +describe('Volumes Tab', () => { + const props = { + volumeContainersList: [ + { + vol_name: 'volumetest1', + containers: [ + { Names: 'container1', State: 'Running', Status: '40 minutes ago' }, + ] + }, + { + vol_name: 'volumetest2', + containers: [ + { Names: 'container2', State: 'Running', Status: '25 minutes ago' }, + { Names: 'container3', State: '', Status: '' } + ] + } + ] + }; +} + +/*------ testing rendering test -----*/ +describe('rendering VolumeTab', () => { + beforeAll(() => { + render(); + }) + + describe('volume list appears on screen', () => { + const + }) +}) + // const wrapper = shallow(); // // console.log(wrapper.debug()) diff --git a/src/components/helper/initDatabase.js b/src/components/helper/initDatabase.js index 4f3b94bd..be0975e3 100644 --- a/src/components/helper/initDatabase.js +++ b/src/components/helper/initDatabase.js @@ -1,28 +1,27 @@ export default () => { - fetch('http://localhost:3000/init', { method: 'GET', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, }) - .then((data) => data.json()) - .then((response) => { - if (response.error !== null){ - alert(`Make sure Docker Desktop is running. \n\n ${response.error}`); - return - } - if (response.stderr){ - console.log(`stderr: ${response.stderr}`); - return; - } - console.log(response.stdout); - }) - .catch((err) => { - console.log(err); - }) + .then((data) => data.json()) + .then((response) => { + if (response.error !== null) { + alert(`Make sure Docker Desktop is running. \n\n ${response.error}`); + return; + } + if (response.stderr) { + console.log(`stderr: ${response.stderr}`); + return; + } + console.log(response.stdout); + }) + .catch((err) => { + console.log(err); + }); }; -// initDatabase is invoked upon login and composes the network consisting of a containerized SQL database +// initDatabase is invoked upon login and composes the network consisting of a containerized SQL database // which is the metrics data, notifications preferences data, and etc. being persisted // (for further details look into server / databse) diff --git a/src/redux/reducers/imageListReducer.js b/src/redux/reducers/imageListReducer.js index 6f186b5a..44d59d14 100644 --- a/src/redux/reducers/imageListReducer.js +++ b/src/redux/reducers/imageListReducer.js @@ -1,7 +1,7 @@ import * as types from '../constants/actionTypes'; const initialState = { - imagesList: [] + imagesList: [], }; export default function (state = initialState, action) { @@ -13,7 +13,7 @@ export default function (state = initialState, action) { } return { ...state, - imagesList: newImagesList + imagesList: newImagesList, }; } diff --git a/tsconfig.json b/tsconfig.json index fd90625a..f5e0c34c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,8 @@ "outDir": "./dist", "strict": true, "moduleResolution": "node", - "removeComments": true + "removeComments": true, + "noEmit": false }, "include": ["src/**/*", "server/models/psqlQuery.js"] } From 25c41b043cc96c19fc3e792c11b7a1c4e3e293cb Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 24 Nov 2022 16:46:24 -0500 Subject: [PATCH 008/110] test containerstab Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- __tests__/ContainersTab.test.js | 62 +++++++++++++++++++++++++-------- src/components/login/login.js | 18 +++++----- tsconfig.json | 2 +- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/__tests__/ContainersTab.test.js b/__tests__/ContainersTab.test.js index d79e881f..91d30816 100644 --- a/__tests__/ContainersTab.test.js +++ b/__tests__/ContainersTab.test.js @@ -4,12 +4,13 @@ import {describe, expect, test, jest} from '@jest/globals'; import '@testing-library/react'; import '@testing-library/jest-dom'; import { Chart } from 'react-chartjs-2'; -import {stop} from '../src/components/helper/commands.js' +import {stop} from '../src/components/helper/commands.js'; import ToggleDisplay from '../src/components/display/ToggleDisplay'; // Started to migrate to React-Testing-Library... import { create } from 'react-test-renderer'; import { fireEvent, render, screen } from '@testing-library/react'; + const props = { runningList: [ { @@ -26,10 +27,12 @@ const props = { stoppedList: [ { Names: 'zealous', - ID: 'c902ec744095', - Img: '84c5f6e03bf0', - Created: '2 days ago', - name: 'zealous_pare' + ID: 'c902ec744095', // only this property was correctly referenced! + Image: '84c5f6e03bf0', + RunningFor: '2 days ago', + Img: '84c5f6e03bf0', // this property is not used... + Created: '2 days ago', // this property is not used + name: 'zealous_pare' // this property is also not used anywhere } ], stop: jest.fn() @@ -42,25 +45,54 @@ const props = { // Debug test describe('Containers', () => { - beforeAll(()=>{ + beforeEach(()=>{ render(); + }); - }) + xdescribe('Running List containers', () => { + + test('Should have render correct amount of containers', () => { + const runningContainers = screen.getByText('Running Containers', {exact:false}); + const text = runningContainers.innerHTML; + // console.log(text) + // screen.debug(runningContainers) + expect(text).toEqual(`Running Containers: ${props.runningList.length}`); + }); + + test('Name of container should properly display', ()=>{ + const h3 = screen.getAllByRole('heading', { level: 3 }); + const name = h3[0].innerHTML; + expect(name).toEqual('blissful_matsumoto'); + console.log(name); + }); - describe('Running List containers', () => { test('Stop button is called', async () => { - const stopButton = document.querySelector('.stop-btn') - await fireEvent.click(stopButton) - screen.debug() - expect(stopButton).toBeCalled + const stopButton = document.querySelector('.stop-btn'); + await fireEvent.click(stopButton); + screen.debug(); + expect(stopButton).toBeCalled; }); + test('Wanted to test toggle display',() => { - render() + render(); + screen.debug(); + expect(1).toBe(1); + }); + + }); + + describe('Stopped List Containers', () => { + + xtest('Should have render correct amount of containers', () => { + const exitedContainers = screen.getByText('Exited Containers', {exact:false}); + const text = exitedContainers.innerHTML; + expect(text).toEqual(`Exited Containers: ${props.stoppedList.length}`); + }); + + test('Name of container should properly display', () => { screen.debug() - expect(1).toBe(1) }) }) - }); // function shallowSetup() { diff --git a/src/components/login/login.js b/src/components/login/login.js index 3d914515..5156194c 100644 --- a/src/components/login/login.js +++ b/src/components/login/login.js @@ -13,7 +13,7 @@ import Button from '@mui/material/Button'; import Docketeer from '../../../assets/docketeer-title.png'; - const Login = () => { +const Login = () => { const navigate = useNavigate(); const dispatch = useDispatch(); const updateSession = () => dispatch(actions.updateSession()); @@ -36,14 +36,14 @@ import Docketeer from '../../../assets/docketeer-title.png'; // callback function invoked when 'login' button is clicked const handleLogin = (e) => { e.preventDefault(); // prevents form submit from reloading page - const usernameInput = document.getElementById("username"); - const passwordInput = document.getElementById("password"); + const usernameInput = document.getElementById('username'); + const passwordInput = document.getElementById('password'); const username = usernameInput.value; const password = passwordInput.value; // clears input fields after login - usernameInput.value = ""; - passwordInput.value = ""; + usernameInput.value = ''; + passwordInput.value = ''; authenticateUser(username, password); }; @@ -51,8 +51,8 @@ import Docketeer from '../../../assets/docketeer-title.png'; // callback function which will send request to endpoint http://localhost:3000/login and expect // either SSID in cookie. const authenticateUser = (username, password) => { - fetch("http://localhost:3000/login", { - method: "POST", + fetch('http://localhost:3000/login', { + method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -65,7 +65,7 @@ import Docketeer from '../../../assets/docketeer-title.png'; return response.json(); }) .then((data) => { - if (Object.prototype.hasOwnProperty.call(data, "error")) { + if (Object.prototype.hasOwnProperty.call(data, 'error')) { window.alert(data.error); } else { updateSession(); // loggedIn = true @@ -74,7 +74,7 @@ import Docketeer from '../../../assets/docketeer-title.png'; } }) .catch((err) => { - console.log("Fetch: POST error to /login", err); + console.log('Fetch: POST error to /login', err); // created a pop window for wrong username/password window.alert('Wrong Password or Username. Please try Again!'); }); diff --git a/tsconfig.json b/tsconfig.json index fd90625a..57653a7b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,5 +18,5 @@ "moduleResolution": "node", "removeComments": true }, - "include": ["src/**/*", "server/models/psqlQuery.js"] + "include": ["src/**/*", "server/**/*"] } From 89ced9332262de7b4e6cb9f871d73649a19cf34a Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Fri, 25 Nov 2022 11:04:33 -0600 Subject: [PATCH 009/110] convert sessionReducer to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- .../{sessionReducer.js => sessionReducer.ts} | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) rename src/redux/reducers/{sessionReducer.js => sessionReducer.ts} (64%) diff --git a/src/redux/reducers/sessionReducer.js b/src/redux/reducers/sessionReducer.ts similarity index 64% rename from src/redux/reducers/sessionReducer.js rename to src/redux/reducers/sessionReducer.ts index 64169590..3fb9ba12 100644 --- a/src/redux/reducers/sessionReducer.js +++ b/src/redux/reducers/sessionReducer.ts @@ -1,33 +1,32 @@ -import * as types from '../constants/actionTypes'; +import * as types from "../constants/actionTypes"; const initialState = { - _id: '', - username: '', - email: '', - phone: '', - role: '', - role_id: '', - contact_pref: '', - mem_threshold: '', - cpu_threshold: '', - container_stops: '', - token: '', + _id: "", + username: "", + email: "", + phone: "", + role: "", + role_id: "", + contact_pref: "", + mem_threshold: "", + cpu_threshold: "", + container_stops: "", + token: "", isLoggedIn: false, - userList: [] + userList: [], }; -export default function (state = initialState, action) { +export default function (state = initialState, action: any) { switch (action.type) { // Change isLoggedIn state variable depending on previous value - case types.UPDATE_SESSION: + case types.UPDATE_SESSION: return { ...state, - isLoggedIn: !state.isLoggedIn + isLoggedIn: !state.isLoggedIn, }; // Upon successful sign-up or login, update session state with all user info - case types.UPDATE_USER: - + case types.UPDATE_USER: const { _id, username, @@ -39,7 +38,7 @@ export default function (state = initialState, action) { mem_threshold, cpu_threshold, container_stops, - token + token, } = action.payload; return { @@ -58,9 +57,9 @@ export default function (state = initialState, action) { }; // after logging out, remove all user info from session state - case types.LOGOUT_USER: + case types.LOGOUT_USER: return { - ...initialState + ...initialState, }; default: From 3a391bcb84a7326f4b450dc74fa1ae6e5ea50f36 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Fri, 25 Nov 2022 11:23:26 -0600 Subject: [PATCH 010/110] convert userReducer file to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/redux/reducers/userListReducer.js | 10 ++++----- .../{userReducer.js => userReducer.ts} | 22 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) rename src/redux/reducers/{userReducer.js => userReducer.ts} (67%) diff --git a/src/redux/reducers/userListReducer.js b/src/redux/reducers/userListReducer.js index 09dc1d93..b6d70a0d 100644 --- a/src/redux/reducers/userListReducer.js +++ b/src/redux/reducers/userListReducer.js @@ -1,7 +1,7 @@ -import * as types from '../constants/actionTypes'; +import * as types from "../constants/actionTypes"; const initialState = { - userList: [] + userList: [], }; export default function (state = initialState, action) { @@ -9,7 +9,7 @@ export default function (state = initialState, action) { // Change isLoggedIn state variable depending on previous value case types.UPDATE_USER_LIST: return { - userList: action.payload + userList: action.payload, }; case types.UPDATE_USER_ROLE: { @@ -23,12 +23,12 @@ export default function (state = initialState, action) { } return { ...state, - userList: newUserList + userList: newUserList, }; } default: return { - ...state + ...state, }; } } diff --git a/src/redux/reducers/userReducer.js b/src/redux/reducers/userReducer.ts similarity index 67% rename from src/redux/reducers/userReducer.js rename to src/redux/reducers/userReducer.ts index 8c5a8f7a..2fab32f2 100644 --- a/src/redux/reducers/userReducer.js +++ b/src/redux/reducers/userReducer.ts @@ -1,19 +1,19 @@ -import * as types from '../constants/actionTypes'; +import * as types from "../constants/actionTypes"; const initialState = { - name: '', - email: '', - phone: '', - role: '', - role_id: '', - contact_pref: '', - mem_threshold: '', - cpu_threshold: '', + name: "", + email: "", + phone: "", + role: "", + role_id: "", + contact_pref: "", + mem_threshold: "", + cpu_threshold: "", container_stops: false, - isSysAdmin: false + isSysAdmin: false, }; -export default function (state = initialState, action) { +export default function (state = initialState, action: any) { switch (action.type) { case types.UPDATE_ALL: return; From 8fc91c34d4d126c3943445d918e8f17328c26c77 Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 25 Nov 2022 15:01:41 -0500 Subject: [PATCH 011/110] add container/login-test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com>” --- __tests__/ContainersTab.test.js | 44 +++++++++++++++++++++++---- __tests__/endToEnd.js | 26 ---------------- __tests__/loginPage.js | 54 +++++++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 93 insertions(+), 32 deletions(-) delete mode 100644 __tests__/endToEnd.js create mode 100644 __tests__/loginPage.js diff --git a/__tests__/ContainersTab.test.js b/__tests__/ContainersTab.test.js index 91d30816..0ab129c5 100644 --- a/__tests__/ContainersTab.test.js +++ b/__tests__/ContainersTab.test.js @@ -4,7 +4,6 @@ import {describe, expect, test, jest} from '@jest/globals'; import '@testing-library/react'; import '@testing-library/jest-dom'; import { Chart } from 'react-chartjs-2'; -import {stop} from '../src/components/helper/commands.js'; import ToggleDisplay from '../src/components/display/ToggleDisplay'; // Started to migrate to React-Testing-Library... import { create } from 'react-test-renderer'; @@ -35,7 +34,9 @@ const props = { name: 'zealous_pare' // this property is also not used anywhere } ], - stop: jest.fn() + stop: jest.fn(), + remove: jest.fn(), + runStopped: jest.fn() }; @@ -49,7 +50,7 @@ describe('Containers', () => { render(); }); - xdescribe('Running List containers', () => { + describe('Running List containers', () => { test('Should have render correct amount of containers', () => { const runningContainers = screen.getByText('Running Containers', {exact:false}); @@ -65,6 +66,22 @@ describe('Containers', () => { expect(name).toEqual('blissful_matsumoto'); console.log(name); }); + + test('Show details button works', async () => { + + // const mockButton = jest.fn(ToggleDisplay); + // screen.debug(mockButton) + // mockButton(); + // expect(mockButton).toBeCalled; + + // this test is very basic... + // i don't think we can fully check functionality without using chart.js + const buttons = screen.getAllByRole('button'); + const showDetails = buttons[0]; + await fireEvent.click(showDetails); + expect(showDetails).toBeCalled + screen.debug() + }); test('Stop button is called', async () => { const stopButton = document.querySelector('.stop-btn'); @@ -90,11 +107,26 @@ describe('Containers', () => { }); test('Name of container should properly display', () => { - screen.debug() - }) - }) + const name = screen.getAllByText('zealous'); + expect(name).toHaveLength(2); + }); + + test('Run and remove button should fire', async () => { + const buttons = screen.getAllByRole('button'); + const runButton = buttons[2]; + const removeButton = buttons[3]; + await fireEvent.click(runButton); + await fireEvent.click(removeButton); + expect(runButton).toBeCalled; + }); + + }); }); +// check if chart autorefreshes? + + + // function shallowSetup() { // const props = { // runningList: [ diff --git a/__tests__/endToEnd.js b/__tests__/endToEnd.js deleted file mode 100644 index 0fa19ae8..00000000 --- a/__tests__/endToEnd.js +++ /dev/null @@ -1,26 +0,0 @@ -import '@testing-library/react'; -import '@testing-library/jest-dom'; -import React, { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -// import { useSelector, useDispatch } from 'react-redux'; -import {render, fireEvent, screen} from '@testing-library/react'; -import {App} from '../src/renderer/App'; -import Login from '../src/components/login/login'; -// import AccountDisplay from '../src/components/display/AccountDisplay'; -import {BrowserRouter, Routes, Route} from 'react-router-dom'; -import { Provider } from 'react-redux'; -import store from '../src/renderer/store.js'; -// const store = require('../src/renderer/store'); - -describe('dummy test', () => { - it('checking render', () => { - render( - - - - - - ); - screen.debug(); - }); -}); \ No newline at end of file diff --git a/__tests__/loginPage.js b/__tests__/loginPage.js new file mode 100644 index 00000000..19a6cf7e --- /dev/null +++ b/__tests__/loginPage.js @@ -0,0 +1,54 @@ +import '@testing-library/react'; +import '@testing-library/jest-dom'; +import React, { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +// import { useSelector, useDispatch } from 'react-redux'; +import {render, fireEvent, screen, getAllByRole} from '@testing-library/react'; +// trying to import ts files is giving issues for time being, probably related to compiling +// import App from '../src/renderer/App'; +import Login from '../src/components/login/login'; +// import AccountDisplay from '../src/components/display/AccountDisplay'; +import {BrowserRouter, Routes, Route} from 'react-router-dom'; +import { Provider } from 'react-redux'; +import store from '../src/renderer/store.js'; + +import fetchMock from 'jest-fetch-mock'; + +fetchMock.enableMocks(); + +describe('Login Page Renders', () => { + + beforeEach(() => { + fetch.resetMocks(); + render( + + + + + + ); + // screen.debug(); + }); + + test('Username accepts input', async () => { + const username = document.querySelector('#username'); + await fireEvent.change(username, {target: {value:'hi'}}); + expect(username.value).toBe('hi'); + }); + + test('Password accepts input', async () => { + const password = document.querySelector('#password'); + await fireEvent.change(password, {target: {value:'hi'}}); + expect(password.value).toBe('hi'); + }); + + test('Login button', async () => { + fetch.mockResponseOnce(JSON.stringify({ username: 'string', password: 'anotherstring' })); + + const loginButton = screen.getByRole('button'); + await fireEvent.click(loginButton); + // should fail, look into this test + expect(loginButton).not.toBeCalled; + screen.debug( loginButton); + }); +}); \ No newline at end of file diff --git a/package.json b/package.json index 7d9a1fe6..5a185921 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "html-webpack-plugin": "^5.5.0", "jest": "^29.1.2", "jest-environment-jsdom": "^29.2.1", + "jest-fetch-mock": "^3.0.3", "node-fetch": "^3.2.10", "nodemailer": "^6.8.0", "nodemon": "^2.0.20", From 3e72c2687263d8a7a6c9165ada245b7a64906e07 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Fri, 25 Nov 2022 14:42:02 -0600 Subject: [PATCH 012/110] convert userListReducer file to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- .../{userListReducer.js => userListReducer.ts} | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) rename src/redux/reducers/{userListReducer.js => userListReducer.ts} (75%) diff --git a/src/redux/reducers/userListReducer.js b/src/redux/reducers/userListReducer.ts similarity index 75% rename from src/redux/reducers/userListReducer.js rename to src/redux/reducers/userListReducer.ts index b6d70a0d..498b2e67 100644 --- a/src/redux/reducers/userListReducer.js +++ b/src/redux/reducers/userListReducer.ts @@ -1,10 +1,14 @@ import * as types from "../constants/actionTypes"; -const initialState = { +type initialState = { + userList: any[]; +}; + +const obj: initialState = { userList: [], }; -export default function (state = initialState, action) { +export default function (state: initialState, action: any) { switch (action.type) { // Change isLoggedIn state variable depending on previous value case types.UPDATE_USER_LIST: @@ -13,7 +17,7 @@ export default function (state = initialState, action) { }; case types.UPDATE_USER_ROLE: { - const { _id, role } = action.payload; + const { _id, role }: { _id: string; role: string } = action.payload; const newUserList = [...state.userList]; for (let i = 0; i < newUserList.length; i++) { if (newUserList[i]._id === _id) { From 31ca9a24818468c167ea174fe257c08d6b6d2bbc Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Fri, 25 Nov 2022 15:07:29 -0600 Subject: [PATCH 013/110] added UsersTab.test to __tests__ and worked on ImageTab.test Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- __tests__/ImageTab.test.js | 102 +++++++------------------------------ __tests__/UsersTab.test.js | 48 +++++++++++++++++ 2 files changed, 66 insertions(+), 84 deletions(-) create mode 100644 __tests__/UsersTab.test.js diff --git a/__tests__/ImageTab.test.js b/__tests__/ImageTab.test.js index e2f7fb6b..63f5b478 100644 --- a/__tests__/ImageTab.test.js +++ b/__tests__/ImageTab.test.js @@ -5,6 +5,9 @@ import React from 'react'; import { describe, expect, test, jest } from '@jest/globals'; import Images from '../src/components/tabs/Images'; +// import ImageUsers from '../src/components/tabs/ImagesUser'; + +import mockAxios from 'axios'; import '@testing-library/react'; import '@testing-library/jest-dom'; import { @@ -14,7 +17,6 @@ import { render, screen, } from '@testing-library/react'; -import { remove } from '../src/components/helper/commands'; const props = { imagesList: [ @@ -24,20 +26,18 @@ const props = { Name: 'redis', }, ], - // repo: ['repo'], - run: jest.fn(), + runIm: jest.fn(), + removeIm: jest.fn(), }; -// Debug testing, checks for image box to render on screen -describe('Images', () => { - it('renders an image', () => { - render(); - }); -}); +/* ----- mock functions ----- */ + +// jest.mock('axios'); +// const mockRun = jest.fn(); +// const mockRremove = jest.fn(); /* ----- button testing ------ */ -// currently gets stuck at window.runExec method --> reads undefined describe('run button on click', () => { it('fires run button functionality', async () => { const { container } = render(); @@ -57,7 +57,6 @@ describe('pull button on click', () => { }); }); -// currently gets stuck at window.runExec method --> reads undefined describe('remove button on click', () => { it('fires remove button functionality', async () => { const { container } = render(); @@ -71,80 +70,15 @@ describe('remove button on click', () => { /* ------ actions/reducers ------ */ -// Get Images -// Refresh Images -// Remove Images - - - -// describe('Rendered images', () => { -// test('run button works', async () => { -// const runButton = document.querySelector('.run-btn'); -// await fireEvent.click(runButton); -// screen.debug(); -// expect(runButton).toBeCalled; -// }); -// }); -// }); +describe('Images', () => { + it('renders an image if one is found', () => { + render(); + }); + it('refreshes page if image removed', () => { + // need to check that refreshRunning helper function is called -// configure({ adapter: new Adapter() }); -// function shallowSetup() { -// const props = { -// imagesList: [ -// { -// resp: "node-php-something", -// tag: "latest", -// imgid: "fc266a46f885", -// created: "toady", -// size: "234mb", -// }, -// ], -// }; -// const enzymeWrapper = shallow(); -// return { -// props, -// enzymeWrapper, -// }; -// } -// describe("Shallow all of the properties of the Images", () => { -// const { enzymeWrapper, props } = shallowSetup(); -// it("Should render
tag in Images", () => { -// expect(enzymeWrapper.type()).toEqual("div"); -// expect( -// enzymeWrapper.find("div.renderContainers").find("div").length -// ).toEqual(8); -// }); -// it("Should render

tag in Images with a title Images", () => { -// expect(enzymeWrapper.containsMatchingElement(

Images

)).toBe( -// true -// ); -// expect(enzymeWrapper.find(".tabTitle").text()).toEqual("Images"); -// }); -// it("Should render a div tag called runByImage and display all of the properties", () => { -// expect(enzymeWrapper.find("div.runByButton").find("button").length).toEqual( -// 1 -// ); -// expect(enzymeWrapper.find("div.runByButton").find("label").length).toEqual( -// 1 -// ); -// expect(enzymeWrapper.find("div.runByButton").find("span").length).toEqual( -// 1 -// ); -// expect(enzymeWrapper.find("div.runByButton").find("input").length).toEqual( -// 1 -// ); -// }); -// it(`render a div with a class name "containers" and all of it properties`, () => { -// expect(enzymeWrapper.find("div.containers")); -// expect(enzymeWrapper.find("div.box").find("div").length).toEqual(4); -// expect(enzymeWrapper.find("div.box-label").find("h3").length).toEqual(1); -// expect(enzymeWrapper.find("div.box-label").find("p").length).toEqual(1); -// expect(enzymeWrapper.find("div.stopped-info").find("li").length).toEqual(2); -// expect( -// enzymeWrapper.find("div.stopped-button").find("button").length -// ).toEqual(2); -// }); -// }); + }); +}); //* Dummy Test describe('dummy test', () => { diff --git a/__tests__/UsersTab.test.js b/__tests__/UsersTab.test.js new file mode 100644 index 00000000..001e89bd --- /dev/null +++ b/__tests__/UsersTab.test.js @@ -0,0 +1,48 @@ +import React from 'react'; +import { describe, expect, test, jest } from '@jest/globals'; +import '@testing-library/react'; +import '@testing-library/jest-dom'; +import { + fireEvent, + getByLabelText, + getByTestId, + render, + screen, +} from '@testing-library/react'; +import Users from '../src/components/tabs/Users'; +import NewUserDisplay from '../src/components/display/NewUserDisplay'; +import { checkPasswordLength } from '../src/components/helper/newUserHelper'; + +/* ----- Manage Users Table ----- */ + +// checking for button functionality +// describe('Manage Users Table', () => { +// test() +// }) + +/* ----- Create a New User ----- */ + +describe('Create a New User functionality', () => { + // render NewUserDisplay + beforeAll(() => { + render(); + }); + // input fields take in text + test('input fields take in text', () => { + const input = document.getElementById('signupEmail'); + screen.debug(); + expect(input).toHaveValue(''); + }); + // password is at least 6 chars long + test('password field must have at least 6 chars', () => { + const password = '123456'; + const passInput = document.getElementById('signupPassword'); + expect(passInput).toHaveValue(password).toBeGreaterThanOrEqual(6); + }); + // password and confirm passwords match + // if either of these tests fail, check for error message alert + + // submit button on click works + + // Manage Users Table should gain a row +}); From 4243ea89b575ff0ddb8721c258ace00ce63caa59 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Fri, 25 Nov 2022 15:22:50 -0600 Subject: [PATCH 014/110] converted login component to Typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- index.d.ts | 2 + package.json | 1 + src/components/login/login.tsx | 156 +++++++++++++++++++++++++++++++++ tsconfig.json | 2 +- webpack.react.config.js | 7 ++ 5 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 index.d.ts create mode 100644 src/components/login/login.tsx diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 00000000..19992a58 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,2 @@ +declare module '*.png'; +declare module '*.jpg'; \ No newline at end of file diff --git a/package.json b/package.json index 7d9a1fe6..73ea5747 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "electron": "^21.0.1", "electron-devtools-installer": "^3.2.0", "express": "^4.18.1", + "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", "jest": "^29.1.2", "jest-environment-jsdom": "^29.2.1", diff --git a/src/components/login/login.tsx b/src/components/login/login.tsx new file mode 100644 index 00000000..6e5ed10f --- /dev/null +++ b/src/components/login/login.tsx @@ -0,0 +1,156 @@ +/** + * @module Login + * @description Login component which renders a login page, and sign-up modal. This is the first component that is appended to the dist/.renderer-index-template.html via renderer/index.js + */ +import React, { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { useSelector, useDispatch } from 'react-redux'; +import * as actions from '../../redux/actions/actions'; + +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; + +import Docketeer from '../../../assets/docketeer-title.png'; + +interface UserInfo { + _id: number, + username: string, + email: string, + phone: string, + role: string, + role_id: number, + contact_pref: string | null, + mem_threshold: number, + cpu_threshold: number, + container_stops: boolean, + token: string +} + +const Login = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const updateSession = () => dispatch(actions.updateSession()); + const updateUser = (userInfo: UserInfo) => dispatch(actions.updateUser(userInfo)); + + // Docketeer 8.0 - We don't think the below useEffect function served a purpose any more and it caused issues with testing. This should probably be deleted + // Need to set the app element to body for screen-readers (disability), otherwise modal will throw an error + // useEffect(() => { + // fetch("http://localhost:3000/db") + // .then((response) => { + // return response.json(); + // }) + // .then((data) => { + // return console.log("Connected to DB successfully", data); + // }) + // .catch((err) => { + // return console.log("Fetch request to /db failed:", err); + // }); + // }, []); + + // callback function invoked when 'login' button is clicked + const handleLogin = (e: React.ChangeEvent) => { + e.preventDefault(); // prevents form submit from reloading page + console.log("Event:", e) + const usernameInput = document.getElementById("username"); + const passwordInput = document.getElementById("password"); + if(usernameInput != null || passwordInput != null) { + const username: string = (usernameInput as HTMLInputElement).value; + const password: string = (passwordInput as HTMLInputElement).value; + + // clears input fields after login + (usernameInput as HTMLInputElement).value = ""; + (passwordInput as HTMLInputElement).value = ""; + + console.log("username:", username); + console.log("password:", password); + authenticateUser(username, password); + } + }; + + // callback function which will send request to endpoint http://localhost:3000/login and expect + // either SSID in cookie. + const authenticateUser = (username: string, password: string) => { + console.log("YOU ARE HERE!") + fetch("http://localhost:3000/login", { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + username: username, + password: password + }) + }) + .then((response) => response.json()) + .then((data) => { + if (Object.prototype.hasOwnProperty.call(data, "error")) { + window.alert(data.error); + } else { + updateSession(); // loggedIn = true + updateUser(data); // update user info in sessions reducer + navigate('/'); + } + }) + .catch((err) => { + console.log("Fetch: POST error to /login", err); + // created a pop window for wrong username/password + window.alert('Wrong Password or Username. Please try Again!'); + }); + }; + + return ( +
+
+ +
+
+
+
+
+
+

Login

+
+
+
handleLogin}> + +
+
+ +
+ {/* * Submit Button * */} + +
+
+ +
+
+
+ ); +}; + +export default Login; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index fd90625a..bde93e3d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,5 +18,5 @@ "moduleResolution": "node", "removeComments": true }, - "include": ["src/**/*", "server/models/psqlQuery.js"] + "include": ["src/**/*", "server/models/psqlQuery.js", "index.d.ts"] } diff --git a/webpack.react.config.js b/webpack.react.config.js index c677207b..ce7d9f40 100644 --- a/webpack.react.config.js +++ b/webpack.react.config.js @@ -64,6 +64,13 @@ module.exports = { */ test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset/resource' + }, + { + test: /\.(png|jpe?g|gif)$/i, + loader: 'file-loader', + options: { + name: 'assets/[name].[ext]' + } } ] }, From aac2c238708d20132a254ba0b8429d721acadca3 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Fri, 25 Nov 2022 15:32:47 -0600 Subject: [PATCH 015/110] created usersTab test --- __tests__/UsersTab.test.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/__tests__/UsersTab.test.js b/__tests__/UsersTab.test.js index 001e89bd..45265094 100644 --- a/__tests__/UsersTab.test.js +++ b/__tests__/UsersTab.test.js @@ -35,9 +35,7 @@ describe('Create a New User functionality', () => { }); // password is at least 6 chars long test('password field must have at least 6 chars', () => { - const password = '123456'; - const passInput = document.getElementById('signupPassword'); - expect(passInput).toHaveValue(password).toBeGreaterThanOrEqual(6); + }); // password and confirm passwords match // if either of these tests fail, check for error message alert From 28fbae3ac7020a339459175f47e7966cff05e0e9 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Fri, 25 Nov 2022 16:36:00 -0600 Subject: [PATCH 016/110] convert volumeHistoryReducer file to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- package.json | 1 + src/redux/reducers/userListReducer.ts | 9 ++++---- ...toryReducer.js => volumeHistoryReducer.ts} | 22 ++++++++++++------- 3 files changed, 20 insertions(+), 12 deletions(-) rename src/redux/reducers/{volumeHistoryReducer.js => volumeHistoryReducer.ts} (69%) diff --git a/package.json b/package.json index 7d9a1fe6..f565d78b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@mui/icons-material": "^5.10.6", "@mui/material": "^5.10.8", "@mui/x-data-grid": "^5.17.7", + "@reduxjs/toolkit": "^1.9.0", "@types/node": "^18.8.3", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", diff --git a/src/redux/reducers/userListReducer.ts b/src/redux/reducers/userListReducer.ts index 498b2e67..27c6cfef 100644 --- a/src/redux/reducers/userListReducer.ts +++ b/src/redux/reducers/userListReducer.ts @@ -1,14 +1,15 @@ import * as types from "../constants/actionTypes"; +import { PayloadAction } from "@reduxjs/toolkit"; -type initialState = { +interface stateType { userList: any[]; -}; +} -const obj: initialState = { +const initialState: stateType = { userList: [], }; -export default function (state: initialState, action: any) { +export default function (state = initialState, action: PayloadAction) { switch (action.type) { // Change isLoggedIn state variable depending on previous value case types.UPDATE_USER_LIST: diff --git a/src/redux/reducers/volumeHistoryReducer.js b/src/redux/reducers/volumeHistoryReducer.ts similarity index 69% rename from src/redux/reducers/volumeHistoryReducer.js rename to src/redux/reducers/volumeHistoryReducer.ts index 0f199cd6..d0499e06 100644 --- a/src/redux/reducers/volumeHistoryReducer.js +++ b/src/redux/reducers/volumeHistoryReducer.ts @@ -1,5 +1,5 @@ -import * as types from '../constants/actionTypes'; - +import * as types from "../constants/actionTypes"; +import {PayloadAction} from '@reduxjs/toolkit'; /** * @description Reducer for the list of containers running in each volume * State has been separated into two arrays for future implementation @@ -7,22 +7,28 @@ import * as types from '../constants/actionTypes'; * @param {Array} arrayOfVolumeNames List of volumes running * @param {nested Objects} volumeContainersList Containers running under each volume */ -const initialState = { + +interface stateType { + arrayOfVolumeNames: any[]; + volumeContainersList: any[]; +} + +const initialState: stateType = { arrayOfVolumeNames: [], - volumeContainersList: [] + volumeContainersList: [], }; -export default function (state = initialState, action) { +export default function (state = initialState, action: PayloadAction) { switch (action.type) { case types.GET_VOLUME_LIST: // merges arrays using spread operator const newVolumeList = [...state.arrayOfVolumeNames, ...action.payload]; return { ...state, - arrayOfVolumeNames: newVolumeList + arrayOfVolumeNames: newVolumeList, }; - case types.GET_VOLUME_CONTAINERS_LIST: + case types.GET_VOLUME_CONTAINERS_LIST: const newVolumeContainersList = [...state.volumeContainersList]; if (newVolumeContainersList.length) { // ensures no duplicate volumes @@ -35,7 +41,7 @@ export default function (state = initialState, action) { newVolumeContainersList.push(action.payload); return { ...state, - volumeContainersList: newVolumeContainersList + volumeContainersList: newVolumeContainersList, }; default: From 475d188d3c78cfaabd7e540c6f186ff2c2f8df18 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Fri, 25 Nov 2022 16:42:59 -0600 Subject: [PATCH 017/110] convert volumeHistoryReducer file to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/redux/reducers/volumeHistoryReducer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/redux/reducers/volumeHistoryReducer.ts b/src/redux/reducers/volumeHistoryReducer.ts index d0499e06..974c609d 100644 --- a/src/redux/reducers/volumeHistoryReducer.ts +++ b/src/redux/reducers/volumeHistoryReducer.ts @@ -1,5 +1,6 @@ import * as types from "../constants/actionTypes"; -import {PayloadAction} from '@reduxjs/toolkit'; +import { PayloadAction } from "@reduxjs/toolkit"; + /** * @description Reducer for the list of containers running in each volume * State has been separated into two arrays for future implementation From fb5e0dfb466a16bd48415cebb517616b7a218c80 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Fri, 25 Nov 2022 15:18:09 -0800 Subject: [PATCH 018/110] Redux store and reducers conversion to TypeScript. --- .DS_Store | Bin 8196 -> 8196 bytes server/.DS_Store | Bin 6148 -> 6148 bytes src/components/display/ProcessLogsTable.js | 2 +- ...ListReducer.js => containerListReducer.ts} | 28 +++--- src/redux/reducers/dockerComposeReducer.js | 74 --------------- src/redux/reducers/dockerComposeReducer.ts | 80 ++++++++++++++++ src/redux/reducers/graphReducer.js | 88 ----------------- src/redux/reducers/graphReducer.ts | 89 ++++++++++++++++++ src/redux/reducers/imageListReducer.js | 41 -------- src/redux/reducers/imageListReducer.ts | 46 +++++++++ src/redux/reducers/{index.js => index.ts} | 0 src/redux/reducers/notificationReducer.js | 75 --------------- src/redux/reducers/notificationReducer.ts | 76 +++++++++++++++ src/renderer/store.js | 12 --- src/renderer/store.ts | 21 +++++ 15 files changed, 330 insertions(+), 302 deletions(-) rename src/redux/reducers/{containerListReducer.js => containerListReducer.ts} (77%) delete mode 100644 src/redux/reducers/dockerComposeReducer.js create mode 100644 src/redux/reducers/dockerComposeReducer.ts delete mode 100644 src/redux/reducers/graphReducer.js create mode 100644 src/redux/reducers/graphReducer.ts delete mode 100644 src/redux/reducers/imageListReducer.js create mode 100644 src/redux/reducers/imageListReducer.ts rename src/redux/reducers/{index.js => index.ts} (100%) delete mode 100644 src/redux/reducers/notificationReducer.js create mode 100644 src/redux/reducers/notificationReducer.ts delete mode 100644 src/renderer/store.js create mode 100644 src/renderer/store.ts diff --git a/.DS_Store b/.DS_Store index 4040bafd20ae3f96f980c556f937f8886e848d21..396deb674d08647d37183e9569c6b0fa9f4ab55b 100644 GIT binary patch delta 431 zcmZp1XmOa}FDl2tz`)4BAi&_6lb@WFlb;0S3v4W$#y+uucQZQ&3x_611q*{7Ln=ca zLm9FvptvIt>-`4<28PW%0=`VX91O_}`3z-1~AVUGrtson~E@sdL>Vnv)$B-ojbX-0|9;($KZvdTFBUGx&0hCV#YAywOIFq4- zp%P8yPq4~oLjIFoMT8jHCwmA-3vh783rJR1>l&IEndm5(8%;heV!!#L@Ij`{>=NHt KxG*I4F#!N&&u>it delta 160 zcmZp1XmOa}FDlN!z`)4BAi%(o$&klT!cfGJ%%Hcia2or>2Hwr=94s7+;+stbe3>TS z6w;f#S~zR6tB4EZj>)q{3*9BEs|`&|bQFxu4Qh21sx6HSbQDaC&1!2oIYgE9t%KsT vb8_?YyMPve>;PH<2E0%jMs;s?7CpkWu_2UkGrPn$md!z8TNyVt1Tg{t!muQ` diff --git a/server/.DS_Store b/server/.DS_Store index 751cdc027d8442f4d022d9b8d3ab9ddba765f7bf..e348316d9281338f0569e12244ec47e45fc229e3 100644 GIT binary patch delta 311 zcmZoMXfc=|#>B!ku~2NHo}wr>0|Nsi1A_nqLl#4^XHI@{Qcix-=8MeBnRP)@Yz&DE z1wg2WEDaO~s%QKOr1k!T0RzKi9wy7l{7m9GK)(L46ZRUF|)9; zv2(CbUcwYDz`+?WAX#0lYiMF*qN8ALG+BUInz4Jb4zoOC56~Pbp0ePgyqx^Jbf6iG zo1K_+84bA@G8yt1N*GcZiWt&>Zb}BS;Le4(hY6${;hr4kXmw78Vz7=fFp0$v430oO m^Cs_M-m;mUgP#K!EI_Y+XP(S2;>ZCE6(*o8!{!K)HOv6;)lHuO delta 103 zcmZoMXfc=|#>B)qu~2NHo}wrl0|Nsi1A_nqLlHwhLkZ z{N!b) { switch (action.type) { case types.ADD_RUNNING_CONTAINERS: - const newRunningList = state.runningList.slice(); + const newRunningList: object[] = state.runningList.slice(); for (const container of action.payload) { newRunningList.push(container); } @@ -17,7 +23,7 @@ export default function (state = initialState, action) { case types.STOP_RUNNING_CONTAINER: const newStoppedList = state.stoppedList.slice(); - const newestRunningList = []; + const newestRunningList: object[] = []; for (const container of state.runningList) { if (container.ID !== action.payload) { newestRunningList.push(container); @@ -31,7 +37,7 @@ export default function (state = initialState, action) { case types.RUN_STOPPED_CONTAINER: const runningListCopy = state.runningList.slice(); - const newerStoppedContainer = []; + const newerStoppedContainer: object[] = []; for (const container of state.stoppedList) { // eslint-disable-next-line no-empty if (container.ID === action.payload) { @@ -46,14 +52,14 @@ export default function (state = initialState, action) { }; case types.REFRESH_RUNNING_CONTAINERS: - const newRunningList2 = []; + const newRunningList2: object[] = []; for (const container of action.payload) { newRunningList2.push(container); } return { ...state, runningList: newRunningList2 }; case types.REMOVE_CONTAINER: - const removeContainerList = []; + const removeContainerList: object[] = []; for (const container of state.stoppedList) { if (container.ID !== action.payload) { removeContainerList.push(container); @@ -62,14 +68,14 @@ export default function (state = initialState, action) { return { ...state, stoppedList: removeContainerList }; case types.ADD_STOPPED_CONTAINERS: - const newerStoppedList = state.stoppedList.slice(); + const newerStoppedList: object[] = state.stoppedList.slice(); for (const container of action.payload) { newerStoppedList.push(container); } return { ...state, stoppedList: newerStoppedList }; case types.REFRESH_STOPPED_CONTAINERS: - const newStoppedList4 = []; + const newStoppedList4: object[] = []; for (const container of action.payload) { newStoppedList4.push(container); } @@ -77,5 +83,5 @@ export default function (state = initialState, action) { default: return state; - } -} + }; +}; \ No newline at end of file diff --git a/src/redux/reducers/dockerComposeReducer.js b/src/redux/reducers/dockerComposeReducer.js deleted file mode 100644 index 14066c8a..00000000 --- a/src/redux/reducers/dockerComposeReducer.js +++ /dev/null @@ -1,74 +0,0 @@ -/* eslint-disable no-case-declarations */ -import * as types from '../constants/actionTypes'; - -const initialState = { - networkList: [], - composeStack: [] -}; - -export default function (state = initialState, action) { - switch (action.type) { - case types.GET_NETWORK_CONTAINERS: - const networkListCopy = state.networkList.slice(); - const networkListState = [...networkListCopy, ...action.payload]; - return { ...state, networkListState }; - - case types.GET_CONTAINER_STACKS: - const currentState = state.composeStack.slice(); - const newState = action.payload; - - // docketeer 5.0 used this code in lieu of lines 38-53 below: - // - // const comparer = (otherArray) => { - // return (current) => - // otherArray.filter( - // (other) => other.Name == current.Name && other.ID == current.ID - // ).length == 0; - // }; - - // const onlyInCurrentState = currentState.filter(comparer(newState)); - // const onlyInNewState = newState.filter(comparer(currentState)); - // currentState.push(...onlyInNewState); - - // return { ...state, composeStack: currentState }; - - // the previous team's code was broken. our new 6.0 code is broken as well but less so, more specifically on the process compose tab, only the most recently added network will have a compose-down button (all networks added via compose-up in Docketeer should have a compose down button) - - // Docketeer 7.0 did not attempt this bug. So the next team could look into this issue - - const composeStackUpdater = (arr1, arr2, output = []) => { - arr1.forEach((element) => { - if (JSON.stringify(arr2).includes(JSON.stringify(element))) { - output.push(element); - } - }); - arr2.forEach((element) => { - if (!JSON.stringify(arr1).includes(JSON.stringify(element))) { - output.push(element); - } - }); - return output; - }; - - const updatedState = composeStackUpdater(currentState, newState); - return { ...state, composeStack: updatedState }; - - case types.COMPOSE_YML_FILES: - const newnetworkList = state.networkList.slice(); - newnetworkList.push(action.payload[0]); - return { ...state, networkList: newnetworkList }; - - case types.COMPOSE_DOWN: - const prevState = state.composeStack.slice(); - const fileLocation = action.payload; - - const removedStack = prevState.filter( - (container) => container.FilePath !== fileLocation - ); - - return { ...state, composeStack: removedStack }; - - default: - return state; - } -} diff --git a/src/redux/reducers/dockerComposeReducer.ts b/src/redux/reducers/dockerComposeReducer.ts new file mode 100644 index 00000000..f08678eb --- /dev/null +++ b/src/redux/reducers/dockerComposeReducer.ts @@ -0,0 +1,80 @@ +/* eslint-disable no-case-declarations */ +import * as types from '../constants/actionTypes'; +import { PayloadAction } from '@reduxjs/toolkit'; + +interface stateType { + networkList: any[], + composeStack: any[] +} + +const initialState: stateType = { + networkList: [], + composeStack: [] +}; + +export default function (state = initialState, action: PayloadAction) { + switch (action.type) { + case types.GET_NETWORK_CONTAINERS: + const networkListCopy = state.networkList.slice(); + const networkListState = [...networkListCopy, ...action.payload]; + return { ...state, networkListState }; + + case types.GET_CONTAINER_STACKS: + const currentState: any = state.composeStack.slice(); + const newState = action.payload; + + // docketeer 5.0 used this code in lieu of lines 38-53 below: + // + // const comparer = (otherArray) => { + // return (current) => + // otherArray.filter( + // (other) => other.Name == current.Name && other.ID == current.ID + // ).length == 0; + // }; + + // const onlyInCurrentState = currentState.filter(comparer(newState)); + // const onlyInNewState = newState.filter(comparer(currentState)); + // currentState.push(...onlyInNewState); + + // return { ...state, composeStack: currentState }; + + // the previous team's code was broken. our new 6.0 code is broken as well but less so, more specifically on the process compose tab, only the most recently added network will have a compose-down button (all networks added via compose-up in Docketeer should have a compose down button) + + // Docketeer 7.0 did not attempt this bug. So the next team could look into this issue + + const composeStackUpdater = (arr1: [], arr2: [], output = []) => { + arr1.forEach((element) => { + if (JSON.stringify(arr2).includes(JSON.stringify(element))) { + output.push(element); + } + }); + arr2.forEach((element) => { + if (!JSON.stringify(arr1).includes(JSON.stringify(element))) { + output.push(element); + } + }); + return output; + }; + + const updatedState = composeStackUpdater(currentState, newState); + return { ...state, composeStack: updatedState }; + + case types.COMPOSE_YML_FILES: + const newnetworkList = state.networkList.slice(); + newnetworkList.push(action.payload[0]); + return { ...state, networkList: newnetworkList }; + + case types.COMPOSE_DOWN: + const prevState = state.composeStack.slice(); + const fileLocation = action.payload; + + const removedStack = prevState.filter( + (container) => container.FilePath !== fileLocation + ); + + return { ...state, composeStack: removedStack }; + + default: + return state; + } +} diff --git a/src/redux/reducers/graphReducer.js b/src/redux/reducers/graphReducer.js deleted file mode 100644 index d574b4dd..00000000 --- a/src/redux/reducers/graphReducer.js +++ /dev/null @@ -1,88 +0,0 @@ -import * as types from '../constants/actionTypes'; - -const initialState = { - graphAxis: [], - graphMemory: [ - { - label: '', - data: [], - fill: '' - } - ], - graphCpu: [ - { - label: '', - data: [], - fill: '' - } - ], - graphWrittenIO: [ - { - label: '', - data: [], - fill: '' - } - ], - graphReadIO: [ - { - label: '', - data: [], - fill: '' - } - ] -}; - -const graphReducer = (state = initialState, action) => { - switch (action.type) { - case types.BUILD_AXIS: { - if (action.payload === 'clear') return { ...state, graphAxis: [] }; - - // cuts day of week from begingin and the timezone off the end. - let formatedDate = action.payload.toString().slice(4, 24); - - // compare two string dates - if ( - formatedDate > state.graphAxis[state.graphAxis.length - 1] || - !state.graphAxis.length - ) { - const newAxis = state.graphAxis; - newAxis.push(formatedDate); - return { ...state, graphAxis: newAxis }; - } - return { ...state }; - } - - case types.BUILD_MEMORY: { - if (action.payload === 'clear') return { ...state, graphMemory: [] }; - const newMemory = state.graphMemory.slice(); - newMemory.push(action.payload[0]); - return { ...state, graphMemory: newMemory }; - } - - case types.BUILD_CPU: { - if (action.payload === 'clear') return { ...state, graphCpu: [] }; - const newCpu = state.graphCpu.slice(); - newCpu.push(action.payload[0]); - return { ...state, graphCpu: newCpu }; - } - - case types.BUILD_WRITTEN_IO: { - if (action.payload === 'clear') return { ...state, graphWrittenIO: [] }; - const newWrittenIO = state.graphWrittenIO.slice(); - newWrittenIO.push(action.payload[0]); - return { ...state, graphWrittenIO: newWrittenIO }; - } - - case types.BUILD_READ_IO: { - if (action.payload === 'clear') return { ...state, graphReadIO: [] }; - const newReadIO = state.graphReadIO.slice(); - newReadIO.push(action.payload[0]); - return { ...state, graphReadIO: newReadIO }; - } - - default: - return state; - } -}; - -export default graphReducer; diff --git a/src/redux/reducers/graphReducer.ts b/src/redux/reducers/graphReducer.ts new file mode 100644 index 00000000..b36ac1c2 --- /dev/null +++ b/src/redux/reducers/graphReducer.ts @@ -0,0 +1,89 @@ +import * as types from '../constants/actionTypes'; +import { PayloadAction } from '@reduxjs/toolkit'; + +const initialState = { + graphAxis: [], + graphMemory: [ + { + label: '', + data: [], + fill: '' + } + ], + graphCpu: [ + { + label: '', + data: [], + fill: '' + } + ], + graphWrittenIO: [ + { + label: '', + data: [], + fill: '' + } + ], + graphReadIO: [ + { + label: '', + data: [], + fill: '' + } + ] +}; + +const graphReducer = (state = initialState, action: PayloadAction) => { + switch (action.type) { + case types.BUILD_AXIS: { + if (action.payload === 'clear') return { ...state, graphAxis: [] }; + + // cuts day of week from begingin and the timezone off the end. + const formatedDate = action.payload.toString().slice(4, 24); + + // compare two string dates + if ( + formatedDate > state.graphAxis[state.graphAxis.length - 1] || + !state.graphAxis.length + ) { + const newAxis: any[] = state.graphAxis; + newAxis.push(formatedDate); + return { ...state, graphAxis: newAxis }; + } + return { ...state }; + } + + case types.BUILD_MEMORY: { + if (action.payload === 'clear') return { ...state, graphMemory: [] }; + const newMemory = state.graphMemory.slice(); + newMemory.push(action.payload[0]); + return { ...state, graphMemory: newMemory }; + } + + case types.BUILD_CPU: { + if (action.payload === 'clear') return { ...state, graphCpu: [] }; + const newCpu = state.graphCpu.slice(); + newCpu.push(action.payload[0]); + return { ...state, graphCpu: newCpu }; + } + + case types.BUILD_WRITTEN_IO: { + if (action.payload === 'clear') return { ...state, graphWrittenIO: [] }; + const newWrittenIO = state.graphWrittenIO.slice(); + newWrittenIO.push(action.payload[0]); + return { ...state, graphWrittenIO: newWrittenIO }; + } + + case types.BUILD_READ_IO: { + if (action.payload === 'clear') return { ...state, graphReadIO: [] }; + const newReadIO = state.graphReadIO.slice(); + newReadIO.push(action.payload[0]); + return { ...state, graphReadIO: newReadIO }; + } + + default: + return state; + } +}; + +export default graphReducer; diff --git a/src/redux/reducers/imageListReducer.js b/src/redux/reducers/imageListReducer.js deleted file mode 100644 index 6f186b5a..00000000 --- a/src/redux/reducers/imageListReducer.js +++ /dev/null @@ -1,41 +0,0 @@ -import * as types from '../constants/actionTypes'; - -const initialState = { - imagesList: [] -}; - -export default function (state = initialState, action) { - switch (action.type) { - case types.GET_IMAGES: { - const newImagesList = state.imagesList.slice(); - for (const image of action.payload) { - newImagesList.push(image); - } - return { - ...state, - imagesList: newImagesList - }; - } - - case types.REFRESH_IMAGES: { - const newImagesList2 = []; - for (const image of action.payload) { - newImagesList2.push(image); - } - return { ...state, imagesList: newImagesList2 }; - } - - case types.REMOVE_IMAGE: { - const newRemoveImage = []; - for (const image of state.imagesList) { - if (image.id !== action.payload) { - newRemoveImage.push(image); - } - } - return { ...state, imagesList: newRemoveImage }; - } - - default: - return state; - } -} diff --git a/src/redux/reducers/imageListReducer.ts b/src/redux/reducers/imageListReducer.ts new file mode 100644 index 00000000..f3b00a7c --- /dev/null +++ b/src/redux/reducers/imageListReducer.ts @@ -0,0 +1,46 @@ +import * as types from '../constants/actionTypes'; +import { PayloadAction } from '@reduxjs/toolkit'; + +interface imageState { + imagesList: any[] +} + +const initialState: imageState = { + imagesList: [] +}; + +export default function (state = initialState, action: PayloadAction) { + switch (action.type) { + case types.GET_IMAGES: { + const newImagesList = state.imagesList.slice(); + for (const image of action.payload) { + newImagesList.push(image); + } + return { + ...state, + imagesList: newImagesList + }; + } + + case types.REFRESH_IMAGES: { + const newImagesList2 = []; + for (const image of action.payload) { + newImagesList2.push(image); + } + return { ...state, imagesList: newImagesList2 }; + } + + case types.REMOVE_IMAGE: { + const newRemoveImage: any[] = []; + for (const image of state.imagesList) { + if (image.id !== action.payload) { + newRemoveImage.push(image); + } + } + return { ...state, imagesList: newRemoveImage }; + } + + default: + return state; + } +} diff --git a/src/redux/reducers/index.js b/src/redux/reducers/index.ts similarity index 100% rename from src/redux/reducers/index.js rename to src/redux/reducers/index.ts diff --git a/src/redux/reducers/notificationReducer.js b/src/redux/reducers/notificationReducer.js deleted file mode 100644 index 8aa4e818..00000000 --- a/src/redux/reducers/notificationReducer.js +++ /dev/null @@ -1,75 +0,0 @@ -import * as types from '../constants/actionTypes'; - -const initialState = { - phoneNumber: '', - memoryNotificationList: new Set(), - cpuNotificationList: new Set(), - stoppedNotificationList: new Set() -}; - -export default function (state = initialState, action) { - switch (action.type) { - case types.ADD_PHONE_NUMBER: - return { - ...state, - phoneNumber: action.payload - }; - - case types.ADD_MEMORY_NOTIFICATION_SETTING: - const memoryNotificationList = new Set(action.payload); - return { - ...state, - memoryNotificationList - }; - - case types.ADD_CPU_NOTIFICATION_SETTING: - const cpuNotificationList = new Set(action.payload); - return { - ...state, - cpuNotificationList - }; - - case types.ADD_STOPPED_NOTIFICATION_SETTING: - const stoppedNotificationList = new Set(action.payload); - return { - ...state, - stoppedNotificationList - }; - - case types.REMOVE_MEMORY_NOTIFICATION_SETTING: - const newMemoryNotificationList = []; - state.memoryNotificationList.forEach((containerId) => { - if (containerId !== action.payload) - newMemoryNotificationList.push(containerId); - }); - return { - ...state, - memoryNotificationList: newMemoryNotificationList - }; - - case types.REMOVE_CPU_NOTIFICATION_SETTING: - const newCpuNotificationList = []; - state.cpuNotificationList.forEach((containerId) => { - if (containerId !== action.payload) - newCpuNotificationList.push(containerId); - }); - return { - ...state, - cpuNotificationList: newCpuNotificationList - }; - - case types.REMOVE_STOPPED_NOTIFICATION_SETTING: - const newStoppedNotificationList = []; - state.stoppedNotificationList.forEach((containerId) => { - if (containerId !== action.payload) - newStoppedNotificationList.push(containerId); - }); - return { - ...state, - stoppedNotificationList: newStoppedNotificationList - }; - - default: - return state; - } -} diff --git a/src/redux/reducers/notificationReducer.ts b/src/redux/reducers/notificationReducer.ts new file mode 100644 index 00000000..fc04f705 --- /dev/null +++ b/src/redux/reducers/notificationReducer.ts @@ -0,0 +1,76 @@ +import * as types from '../constants/actionTypes'; +import { PayloadAction } from '@reduxjs/toolkit'; + +const initialState = { + phoneNumber: '', + memoryNotificationList: new Set(), + cpuNotificationList: new Set(), + stoppedNotificationList: new Set() +}; + +export default function (state = initialState, action: PayloadAction) { + switch (action.type) { + case types.ADD_PHONE_NUMBER: + return { + ...state, + phoneNumber: action.payload + }; + + case types.ADD_MEMORY_NOTIFICATION_SETTING: + const memoryNotificationList = new Set(action.payload); + return { + ...state, + memoryNotificationList + }; + + case types.ADD_CPU_NOTIFICATION_SETTING: + const cpuNotificationList = new Set(action.payload); + return { + ...state, + cpuNotificationList + }; + + case types.ADD_STOPPED_NOTIFICATION_SETTING: + const stoppedNotificationList = new Set(action.payload); + return { + ...state, + stoppedNotificationList + }; + + case types.REMOVE_MEMORY_NOTIFICATION_SETTING: + const newMemoryNotificationList: any[] = []; + state.memoryNotificationList.forEach((containerId) => { + if (containerId !== action.payload) + newMemoryNotificationList.push(containerId); + }); + return { + ...state, + memoryNotificationList: newMemoryNotificationList + }; + + case types.REMOVE_CPU_NOTIFICATION_SETTING: + const newCpuNotificationList: any[] = []; + state.cpuNotificationList.forEach((containerId) => { + if (containerId !== action.payload) + newCpuNotificationList.push(containerId); + }); + return { + ...state, + cpuNotificationList: newCpuNotificationList + }; + + case types.REMOVE_STOPPED_NOTIFICATION_SETTING: + const newStoppedNotificationList: any[] = []; + state.stoppedNotificationList.forEach((containerId) => { + if (containerId !== action.payload) + newStoppedNotificationList.push(containerId); + }); + return { + ...state, + stoppedNotificationList: newStoppedNotificationList + }; + + default: + return state; + } +} diff --git a/src/renderer/store.js b/src/renderer/store.js deleted file mode 100644 index 8a637714..00000000 --- a/src/renderer/store.js +++ /dev/null @@ -1,12 +0,0 @@ -import { createStore, compose } from 'redux'; -import reducers from '../redux/reducers/index'; - -// Enhancers function uses compose function to pass the Redux Store to the Redux extension. -const enhancers = compose( - window.__REDUX_DEVTOOLS_EXTENSION__ - && window.__REDUX_DEVTOOLS_EXTENSION__() -); - -const store = createStore(reducers, enhancers); - -export default store; diff --git a/src/renderer/store.ts b/src/renderer/store.ts new file mode 100644 index 00000000..95c20071 --- /dev/null +++ b/src/renderer/store.ts @@ -0,0 +1,21 @@ +import { configureStore } from '@reduxjs/toolkit' +import reducers from '../redux/reducers/index'; +import { composeWithDevTools } from '@redux-devtools/extension'; + +const store = configureStore({ + reducer: reducers, + // this is not best practice!! + middleware: getDefaultMiddleware => + getDefaultMiddleware({ + serializableCheck: false, + }), +}) + +export type RootState = ReturnType +export type AppDispatch = typeof store.dispatch +export default store; + + + + + From 32acd4e6787e9fd56e61118daf935ab1abb5fab0 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Fri, 25 Nov 2022 17:24:37 -0600 Subject: [PATCH 019/110] more users testing --- __tests__/UsersTab.test.js | 38 +++++++++++++++++++++++++++++++++----- package.json | 1 + 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/__tests__/UsersTab.test.js b/__tests__/UsersTab.test.js index 45265094..15a4b855 100644 --- a/__tests__/UsersTab.test.js +++ b/__tests__/UsersTab.test.js @@ -8,10 +8,22 @@ import { getByTestId, render, screen, + waitFor, } from '@testing-library/react'; import Users from '../src/components/tabs/Users'; import NewUserDisplay from '../src/components/display/NewUserDisplay'; import { checkPasswordLength } from '../src/components/helper/newUserHelper'; +import { userInfo } from 'os'; +import user from '@testing-library/user-event'; + +const props = { + onSubmit: jest.fn(), + onChange: jest.fn(), + onClick: jest.fn(), +} + + +/* ----- Mock Functions ----- */ /* ----- Manage Users Table ----- */ @@ -24,13 +36,13 @@ import { checkPasswordLength } from '../src/components/helper/newUserHelper'; describe('Create a New User functionality', () => { // render NewUserDisplay - beforeAll(() => { - render(); + beforeEach(() => { + render(); }); // input fields take in text test('input fields take in text', () => { const input = document.getElementById('signupEmail'); - screen.debug(); + // screen.debug(); expect(input).toHaveValue(''); }); // password is at least 6 chars long @@ -40,7 +52,23 @@ describe('Create a New User functionality', () => { // password and confirm passwords match // if either of these tests fail, check for error message alert - // submit button on click works - + // submit button works when password fields match + test('onSubmit is called when password fields match', async () => { + user.type(getPasswordField(), '123456'); + user.type(getConfirmationField(), '123456'); + const submitBtn = screen.getByRole('button', { name: 'submit' }); + await user.click(submitBtn); + screen.debug(); + expect(submitBtn).toBeCalled(); + }) // Manage Users Table should gain a row + }); + +const getPasswordField = () => { + return document.getElementById('signupPassword'); +}; + +const getConfirmationField = () => { + return document.getElementById('signupPasswordConfirmation'); +} \ No newline at end of file diff --git a/package.json b/package.json index 7d9a1fe6..55f33ad2 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@mui/icons-material": "^5.10.6", "@mui/material": "^5.10.8", "@mui/x-data-grid": "^5.17.7", + "@testing-library/user-event": "^14.4.3", "@types/node": "^18.8.3", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", From d467137a2ca69897a0659fa1689f05a8f2a53087 Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 25 Nov 2022 18:25:14 -0500 Subject: [PATCH 020/110] start listreducer/add QOL Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- __tests__/ListReducer.test.js | 42 +++++------ __tests__/loginPage.js | 54 +++++++++++++ package.json | 1 + server/app.js | 1 + src/components/RenderViews.tsx | 2 +- src/components/helper/commands.js | 104 +++++++++++++------------- src/components/helper/initDatabase.js | 33 ++++---- src/components/login/login.js | 27 +++---- src/components/tabs/ContainersUser.js | 2 +- src/main/index.ts | 2 + src/renderer/index.tsx | 1 + tsconfig.json | 2 +- 12 files changed, 167 insertions(+), 104 deletions(-) create mode 100644 __tests__/loginPage.js diff --git a/__tests__/ListReducer.test.js b/__tests__/ListReducer.test.js index bf0ff804..97fa8eee 100644 --- a/__tests__/ListReducer.test.js +++ b/__tests__/ListReducer.test.js @@ -17,30 +17,30 @@ describe("Dockeeter reducer", () => { }; }); - describe("unrecognized action types", () => { - it("should return the original without any duplication", () => { - expect(containerListReducer(state, { type: "qqqq" })).toBe(state); + describe("Action Types", () => { + it("Should return initial state if type is invalid", () => { + expect(containerListReducer(state, { type: "FakeActionType" })).toBe(state); }); }); - // describe('REFRESH_RUNNING_CONTAINERS', () => { - // it('should overwrite the runningList array in the state to update it', () => { - // expect(state.runningList.length).toEqual(0); - // let action = { - // type: 'REFRESH_RUNNING_CONTAINERS', - // payload: [{ cid: '123' }, { cid: '456' }] - // }; - // expect(containerListReducer(state, action).runningList.length).toEqual(2); - // action = { - // type: 'REFRESH_RUNNING_CONTAINERS', - // payload: [{ cid: '789' }] - // }; - // expect(containerListReducer(state, action).runningList.length).toEqual(1); - // expect(containerListReducer(state, action).runningList[0].ID).toEqual( - // '789' - // ); - // }); - // }); + describe('REFRESH_RUNNING_CONTAINERS', () => { + it('should overwrite the runningList array in the state to update it', () => { + expect(state.runningList.length).toEqual(0); + let action = { + type: 'REFRESH_RUNNING_CONTAINERS', + payload: [{ cid: '123' }, { cid: '456' }] + }; + expect(containerListReducer(state, action).runningList.length).toEqual(2); + action = { + type: 'REFRESH_RUNNING_CONTAINERS', + payload: [{ cid: '789' }] + }; + // expect(containerListReducer(state, action).runningList.length).toEqual(1); + // expect(containerListReducer(state, action).runningList[0].ID).toEqual( + // '789' + // ); + }); + }); // describe('REFRESH_STOPPED_CONTAINERS', () => { // it('should overwrite the stoppedList array in the state to update it', () => { diff --git a/__tests__/loginPage.js b/__tests__/loginPage.js new file mode 100644 index 00000000..19a6cf7e --- /dev/null +++ b/__tests__/loginPage.js @@ -0,0 +1,54 @@ +import '@testing-library/react'; +import '@testing-library/jest-dom'; +import React, { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +// import { useSelector, useDispatch } from 'react-redux'; +import {render, fireEvent, screen, getAllByRole} from '@testing-library/react'; +// trying to import ts files is giving issues for time being, probably related to compiling +// import App from '../src/renderer/App'; +import Login from '../src/components/login/login'; +// import AccountDisplay from '../src/components/display/AccountDisplay'; +import {BrowserRouter, Routes, Route} from 'react-router-dom'; +import { Provider } from 'react-redux'; +import store from '../src/renderer/store.js'; + +import fetchMock from 'jest-fetch-mock'; + +fetchMock.enableMocks(); + +describe('Login Page Renders', () => { + + beforeEach(() => { + fetch.resetMocks(); + render( + + + + + + ); + // screen.debug(); + }); + + test('Username accepts input', async () => { + const username = document.querySelector('#username'); + await fireEvent.change(username, {target: {value:'hi'}}); + expect(username.value).toBe('hi'); + }); + + test('Password accepts input', async () => { + const password = document.querySelector('#password'); + await fireEvent.change(password, {target: {value:'hi'}}); + expect(password.value).toBe('hi'); + }); + + test('Login button', async () => { + fetch.mockResponseOnce(JSON.stringify({ username: 'string', password: 'anotherstring' })); + + const loginButton = screen.getByRole('button'); + await fireEvent.click(loginButton); + // should fail, look into this test + expect(loginButton).not.toBeCalled; + screen.debug( loginButton); + }); +}); \ No newline at end of file diff --git a/package.json b/package.json index 7d9a1fe6..124879d3 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "dotenv": "^16.0.3", "electron": "^21.0.1", "electron-devtools-installer": "^3.2.0", + "electron-reloader": "^1.2.3", "express": "^4.18.1", "html-webpack-plugin": "^5.5.0", "jest": "^29.1.2", diff --git a/server/app.js b/server/app.js index 7fb6a31f..36e0e264 100644 --- a/server/app.js +++ b/server/app.js @@ -38,6 +38,7 @@ app.use('/logout', logoutRouter); // Unknown Endpoint Error Handler app.use('/', (req, res) => { + console.log('why does changing react lead here'); return res.status(404).json('404 Not Found'); }); diff --git a/src/components/RenderViews.tsx b/src/components/RenderViews.tsx index 0b04d6b7..3456f2a7 100644 --- a/src/components/RenderViews.tsx +++ b/src/components/RenderViews.tsx @@ -11,7 +11,7 @@ interface RootState { } } -const RenderViews = () => { +const RenderViews = ():any => { // grab current user's role const role = useSelector((state: RootState) => state.session.role); diff --git a/src/components/helper/commands.js b/src/components/helper/commands.js index c771a82d..609103df 100644 --- a/src/components/helper/commands.js +++ b/src/components/helper/commands.js @@ -66,27 +66,28 @@ const errorCheck = (key, error) => { else{ console.log(error.message); } - return -} + return; +}; export const refreshRunning = (refreshRunningContainers) => { window.nodeMethod.runExec( 'docker stats --no-stream --format "{{json .}},"', (error, stdout, stderr) => { if (error) { - errorCheck("refreshRunning", error); + errorCheck('refreshRunning', error); return; } if (stderr) { console.log(`refreshRunning stderr: ${stderr}`); return; } - + // console.log(stdout); const dockerOutput = `[${stdout .trim() .slice(0, -1) .replaceAll(' ', '')}]`; const convertedValue = JSON.parse(dockerOutput); + // console.log(convertedValue); refreshRunningContainers(convertedValue); } ); @@ -102,7 +103,7 @@ export const refreshStopped = (refreshStoppedContainers) => { 'docker ps -f "status=exited" --format "{{json .}},"', (error, stdout, stderr) => { if (error) { - errorCheck("refreshStopped", error); + errorCheck('refreshStopped', error); return; } if (stderr) { @@ -126,7 +127,7 @@ export const refreshStopped = (refreshStoppedContainers) => { export const refreshImages = (callback) => { window.nodeMethod.runExec('docker images', (error, stdout, stderr) => { if (error) { - errorCheck("refreshImages", error); + errorCheck('refreshImages', error); return; } if (stderr) { @@ -487,7 +488,7 @@ export const dockerComposeDown = (fileLocation, ymlFileName) => { * Writes metric stats into database */ - export const writeToDb = () => { +export const writeToDb = () => { const interval = 300000; setInterval(() => { const state = store.getState(); @@ -495,7 +496,7 @@ export const dockerComposeDown = (fileLocation, ymlFileName) => { const stoppedContainers = state.containersList.stoppedList; if (!runningContainers.length) return; - const containerParameters = {} + const containerParameters = {}; runningContainers.forEach((container) => { containerParameters[container.Name] = { @@ -508,7 +509,7 @@ export const dockerComposeDown = (fileLocation, ymlFileName) => { block: container.BlockIO, pid: container.PIDs, timestamp: 'current_timestamp' - } + }; }); if (stoppedContainers.length >= 1) { stoppedContainers.forEach((container) => { @@ -522,21 +523,21 @@ export const dockerComposeDown = (fileLocation, ymlFileName) => { block: '00.0MB/00.0MB', pid: '0', timestamp: 'current_timestamp' - } + }; }); } fetch('http://localhost:3000/init/addMetrics', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - containers: containerParameters + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + containers: containerParameters + }) }) - }) - .catch((err) => { - console.log(err); - }) + .catch((err) => { + console.log(err); + }); }, interval); }; @@ -553,13 +554,13 @@ export const setDbSessionTimeZone = () => { timezone: offsetTimeZoneInHours }) }) - .then((data) => data.json()) - .then((response) => { - return; - }) - .catch((err) => { - console.log(err); - }) + .then((data) => data.json()) + .then((response) => { + return; + }) + .catch((err) => { + console.log(err); + }); }; export const getContainerGitUrl = async (container) => { @@ -571,7 +572,7 @@ export const getContainerGitUrl = async (container) => { body: JSON.stringify({ githubUrl: container }) - }) + }); return await response.json(); }; @@ -628,7 +629,6 @@ export const getVolumeContainers = (volumeName, getVolumeContainersList) => { listOfVolumeProperties(volumeName, dockerOutput) ); - return getVolumeContainersList(listOfVolumeProperties(volumeName, dockerOutput)); }); }; @@ -641,30 +641,30 @@ export const getVolumeContainers = (volumeName, getVolumeContainersList) => { */ export const getLogs = async (optionsObj, getContainerLogsDispatcher) => { - let containerLogs = { stdout: [], stderr: [] }; - - // iterate through containerIds array in optionsObj - for (let i = 0; i < optionsObj.containerIds.length; i++) { - // build inputCommandString to get logs from command line - let inputCommandString = 'docker logs --timestamps '; - if (optionsObj.since) { - inputCommandString += `--since ${optionsObj.since} `; - } - optionsObj.tail - ? (inputCommandString += `--tail ${optionsObj.tail} `) - : (inputCommandString += '--tail 50 '); - inputCommandString += `${optionsObj.containerIds[i]}`; - - window.nodeMethod.runExec(inputCommandString, (error, stdout, stderr) => { - if (error) { - console.error(`exec error: ${error}`); - return; - } - containerLogs.stdout = [...containerLogs.stdout, ...makeArrayOfObjects(stdout, optionsObj.containerIds[i])]; - containerLogs.stderr = [...containerLogs.stderr, ...makeArrayOfObjects(stderr, optionsObj.containerIds[i])]; - }); + const containerLogs = { stdout: [], stderr: [] }; + + // iterate through containerIds array in optionsObj + for (let i = 0; i < optionsObj.containerIds.length; i++) { + // build inputCommandString to get logs from command line + let inputCommandString = 'docker logs --timestamps '; + if (optionsObj.since) { + inputCommandString += `--since ${optionsObj.since} `; } - return containerLogs; + optionsObj.tail + ? (inputCommandString += `--tail ${optionsObj.tail} `) + : (inputCommandString += '--tail 50 '); + inputCommandString += `${optionsObj.containerIds[i]}`; + + window.nodeMethod.runExec(inputCommandString, (error, stdout, stderr) => { + if (error) { + console.error(`exec error: ${error}`); + return; + } + containerLogs.stdout = [...containerLogs.stdout, ...makeArrayOfObjects(stdout, optionsObj.containerIds[i])]; + containerLogs.stderr = [...containerLogs.stderr, ...makeArrayOfObjects(stderr, optionsObj.containerIds[i])]; + }); + } + return containerLogs; }; diff --git a/src/components/helper/initDatabase.js b/src/components/helper/initDatabase.js index 4f3b94bd..73c90d26 100644 --- a/src/components/helper/initDatabase.js +++ b/src/components/helper/initDatabase.js @@ -6,21 +6,24 @@ export default () => { 'Content-Type': 'application/json' }, }) - .then((data) => data.json()) - .then((response) => { - if (response.error !== null){ - alert(`Make sure Docker Desktop is running. \n\n ${response.error}`); - return - } - if (response.stderr){ - console.log(`stderr: ${response.stderr}`); - return; - } - console.log(response.stdout); - }) - .catch((err) => { - console.log(err); - }) + .then((data) => data.json()) + .then((response) => { + if (response.error !== null){ + console.log('Make sure Docker Desktop is running', response.error); + // Not clear why the alert is needed + // i'll change to console.log for now + console.log(`Make sure Docker Desktop is running. \n\n ${response.error}`); + return; + } + if (response.stderr){ + console.log(`stderr: ${response.stderr}`); + return; + } + console.log(response.stdout); + }) + .catch((err) => { + console.log(err); + }); }; // initDatabase is invoked upon login and composes the network consisting of a containerized SQL database diff --git a/src/components/login/login.js b/src/components/login/login.js index c16da41d..9e4627af 100644 --- a/src/components/login/login.js +++ b/src/components/login/login.js @@ -13,7 +13,7 @@ import Button from '@mui/material/Button'; import Docketeer from '../../../assets/docketeer-title.png'; - const Login = () => { +const Login = () => { const navigate = useNavigate(); const dispatch = useDispatch(); const updateSession = () => dispatch(actions.updateSession()); @@ -36,25 +36,25 @@ import Docketeer from '../../../assets/docketeer-title.png'; // callback function invoked when 'login' button is clicked const handleLogin = (e) => { e.preventDefault(); // prevents form submit from reloading page - const usernameInput = document.getElementById("username"); - const passwordInput = document.getElementById("password"); + const usernameInput = document.getElementById('username'); + const passwordInput = document.getElementById('password'); const username = usernameInput.value; const password = passwordInput.value; // clears input fields after login - usernameInput.value = ""; - passwordInput.value = ""; - console.log("username:", username); - console.log("password:", password); + usernameInput.value = ''; + passwordInput.value = ''; + console.log('username:', username); + console.log('password:', password); authenticateUser(username, password); }; // callback function which will send request to endpoint http://localhost:3000/login and expect // either SSID in cookie. const authenticateUser = (username, password) => { - console.log("YOU ARE HERE!") - fetch("http://localhost:3000/login", { - method: "POST", + console.log('YOU ARE HERE!'); + fetch('http://localhost:3000/login', { + method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -67,7 +67,7 @@ import Docketeer from '../../../assets/docketeer-title.png'; return response.json(); }) .then((data) => { - if (Object.prototype.hasOwnProperty.call(data, "error")) { + if (Object.prototype.hasOwnProperty.call(data, 'error')) { window.alert(data.error); } else { updateSession(); // loggedIn = true @@ -76,7 +76,7 @@ import Docketeer from '../../../assets/docketeer-title.png'; } }) .catch((err) => { - console.log("Fetch: POST error to /login", err); + console.log('Fetch: POST error to /login', err); // created a pop window for wrong username/password window.alert('Wrong Password or Username. Please try Again!'); }); @@ -96,7 +96,7 @@ import Docketeer from '../../../assets/docketeer-title.png';
- +

@@ -105,6 +105,7 @@ import Docketeer from '../../../assets/docketeer-title.png'; label='Password' type='password' variant='outlined' + value='belugas' />
diff --git a/src/components/tabs/ContainersUser.js b/src/components/tabs/ContainersUser.js index 3ea1b151..4de73a11 100644 --- a/src/components/tabs/ContainersUser.js +++ b/src/components/tabs/ContainersUser.js @@ -90,7 +90,7 @@ const Containers = (props) => {
- Date: Fri, 25 Nov 2022 18:26:28 -0500 Subject: [PATCH 021/110] merge From ada574cea4d7ef8c3ee0380b8235e8ae7e059b9d Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 25 Nov 2022 21:18:02 -0500 Subject: [PATCH 022/110] login --- src/components/login/login.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/login/login.tsx b/src/components/login/login.tsx index 7d1020db..57d5e046 100644 --- a/src/components/login/login.tsx +++ b/src/components/login/login.tsx @@ -117,7 +117,8 @@ const Login = () => {

From f1437bf7748671c406526752cb39d811aaf1f912 Mon Sep 17 00:00:00 2001 From: Jack Date: Sat, 26 Nov 2022 11:21:44 -0500 Subject: [PATCH 023/110] Add ListReducer Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- __tests__/ListReducer.test.js | 67 +++++++++++++++++-------------- package.json | 5 +++ server/app.js | 4 ++ src/components/tabs/Containers.js | 4 +- 4 files changed, 48 insertions(+), 32 deletions(-) diff --git a/__tests__/ListReducer.test.js b/__tests__/ListReducer.test.js index 97fa8eee..e4ede18d 100644 --- a/__tests__/ListReducer.test.js +++ b/__tests__/ListReducer.test.js @@ -4,9 +4,12 @@ import containerListReducer from '../src/redux/reducers/containerListReducer'; // import containerList reducer import imageListReducer from '../src/redux/reducers/imageListReducer'; // import imageListReducer reducer +import {describe, expect, test, jest} from '@jest/globals'; +import '@testing-library/react'; +import '@testing-library/jest-dom'; +import { fireEvent, render, screen } from '@testing-library/react'; - -describe("Dockeeter reducer", () => { +describe('Dockeeter reducer', () => { let state; beforeEach(() => { @@ -17,28 +20,31 @@ describe("Dockeeter reducer", () => { }; }); - describe("Action Types", () => { - it("Should return initial state if type is invalid", () => { - expect(containerListReducer(state, { type: "FakeActionType" })).toBe(state); + describe('Action Types', () => { + it('Should return initial state if type is invalid', () => { + expect(containerListReducer(state, { type: 'FakeActionType' })).toBe(state); }); }); describe('REFRESH_RUNNING_CONTAINERS', () => { - it('should overwrite the runningList array in the state to update it', () => { + it('Should return a different state with each reducer invocation', () => { expect(state.runningList.length).toEqual(0); let action = { type: 'REFRESH_RUNNING_CONTAINERS', payload: [{ cid: '123' }, { cid: '456' }] }; - expect(containerListReducer(state, action).runningList.length).toEqual(2); + let newState = containerListReducer(state, action); + expect(newState.runningList.length).toEqual(2); + action = { type: 'REFRESH_RUNNING_CONTAINERS', payload: [{ cid: '789' }] }; - // expect(containerListReducer(state, action).runningList.length).toEqual(1); - // expect(containerListReducer(state, action).runningList[0].ID).toEqual( - // '789' - // ); + newState = containerListReducer(state, action); + expect(newState.runningList.length).toEqual(1); + expect(newState.runningList[0].cid).toEqual( + '789' + ); }); }); @@ -61,31 +67,32 @@ describe("Dockeeter reducer", () => { // }); // }); - describe("REFRESH_IMAGES", () => { - it("should overwrite the imagesList array in the state to update it", () => { + describe('REFRESH_IMAGES', () => { + it('should overwrite the imagesList array in the state to update it', () => { expect(state.imagesList.length).toEqual(0); let action = { type: 'REFRESH_IMAGES', payload: [{ imgid: '123' }, { imgid: '456' }] }; expect(imageListReducer(state, action).imagesList.length).toEqual(2); - action = { type: "REFRESH_IMAGES", payload: [{ imgid: "789" }] }; + action = { type: 'REFRESH_IMAGES', payload: [{ imgid: '789' }] }; expect(imageListReducer(state, action).imagesList.length).toEqual(1); - expect(imageListReducer(state, action).imagesList[0].imgid).toEqual("789"); + expect(imageListReducer(state, action).imagesList[0].imgid).toEqual('789'); }); }); - // describe('REMOVE_CONTAINER', () => { - // it('should remove the specified container from the stoppedList array in the state', () => { - // const newState = { - // stoppedList: [{ cid: '123' }, { cid: '456' }] - // }; - // const action = { type: 'REMOVE_CONTAINER', payload: '123' }; - // expect(containerListReducer(newState, action).stoppedList[0].ID).toEqual( - // '456' - // ); - // }); - // }); + describe('REMOVE_CONTAINER', () => { + it('should remove the specified container from the stoppedList array in the state', () => { + const newState = { + stoppedList: [{ ID: '123' }, { ID: '456' }] + }; + const action = { type: 'REMOVE_CONTAINER', payload: '123' }; + const storedValue = containerListReducer(newState,action); + expect(storedValue.stoppedList[0].ID).toEqual( + '456' + ); + }); + }); // describe('STOP_RUNNING_CONTAINER', () => { // it('should remove a specified container from the runningList and add it to the stoppedList', () => { @@ -112,13 +119,13 @@ describe("Dockeeter reducer", () => { // }); // }); - describe("REMOVE_IMAGE", () => { - it("should remove a specified image from the imagesList", () => { + describe('REMOVE_IMAGE', () => { + it('should remove a specified image from the imagesList', () => { const newState = { imagesList: [{ id: '123' }, { id: '456' }] }; - const action = { type: "REMOVE_IMAGE", payload: "123" }; - expect(imageListReducer(newState, action).imagesList[0].id).toEqual("456"); + const action = { type: 'REMOVE_IMAGE', payload: '123' }; + expect(imageListReducer(newState, action).imagesList[0].id).toEqual('456'); }); }); }); \ No newline at end of file diff --git a/package.json b/package.json index ff9a1af9..a1da76e0 100644 --- a/package.json +++ b/package.json @@ -45,10 +45,13 @@ "@babel/preset-env": "^7.19.3", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.18.6", + "@jest/globals": "^29.3.1", "@mui/system": "^5.10.10", "@redux-devtools/core": "^3.13.1", + "@redux-devtools/extension": "^3.2.3", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", + "@types/jest": "^29.2.3", "@types/pg": "^8.6.5", "axios": "^1.0.0", "babel-loader": "^8.2.5", @@ -73,6 +76,7 @@ "react-test-renderer": "^18.2.0", "style-loader": "^3.3.1", "supertest": "^6.3.0", + "ts-jest": "^29.0.3", "ts-loader": "^9.4.1", "ts-node": "^10.9.1", "twilio": "^3.82.1", @@ -91,6 +95,7 @@ "transform": { "^.+\\.jsx?$": "babel-jest" }, + "preset": "ts-jest", "moduleNameMapper": { "\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/mocks/fileMock.js", "\\.(css|scss)$": "/__mocks__/styleMock.js" diff --git a/server/app.js b/server/app.js index 36e0e264..e7a34396 100644 --- a/server/app.js +++ b/server/app.js @@ -39,6 +39,10 @@ app.use('/logout', logoutRouter); // Unknown Endpoint Error Handler app.use('/', (req, res) => { console.log('why does changing react lead here'); + const url = new URL(`${req.protocol}://${req.get('host')}${req.originalUrl}`); + console.log('1',url); + console.log(req.protocol, req.hostName, req.get('Host'), req.originalUrl, req.route); + return res.status(404).json('404 Not Found'); }); diff --git a/src/components/tabs/Containers.js b/src/components/tabs/Containers.js index 4fb70507..5169c412 100644 --- a/src/components/tabs/Containers.js +++ b/src/components/tabs/Containers.js @@ -135,7 +135,7 @@ const Containers = (props) => { className='stop-btn' onClick={() => props.stop(container.ID, props.stopRunningContainer)} > - STOP + STOfP
@@ -153,7 +153,7 @@ const Containers = (props) => {

- Exited Containers: {props.stoppedList.length} + Exited Conftainers: {props.stoppedList.length}

{renderStoppedList}
From 994837c5583ddd4ea30edc22ce38ac2e9696ffac Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Sat, 26 Nov 2022 10:33:03 -0600 Subject: [PATCH 024/110] convert processLogsReducer file to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- .../{processLogsReducer.js => processLogsReducer.ts} | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) rename src/redux/reducers/{processLogsReducer.js => processLogsReducer.ts} (59%) diff --git a/src/redux/reducers/processLogsReducer.js b/src/redux/reducers/processLogsReducer.ts similarity index 59% rename from src/redux/reducers/processLogsReducer.js rename to src/redux/reducers/processLogsReducer.ts index 39ce9967..50d9dd09 100644 --- a/src/redux/reducers/processLogsReducer.js +++ b/src/redux/reducers/processLogsReducer.ts @@ -1,13 +1,17 @@ -import * as types from '../constants/actionTypes'; +import * as types from "../constants/actionTypes"; +import { PayloadAction } from "@reduxjs/toolkit"; const initialState = { containerLogs: { stdoutLogs: [], - stderrLogs: [] - } + stderrLogs: [], + }, }; -const processLogsReducer = (state = initialState, action) => { +const processLogsReducer = ( + state = initialState, + action: PayloadAction +) => { switch (action.type) { case types.GET_CONTAINER_LOGS: { const newContainerLogs = action.payload; From 1c60e2531edc81787b5f4601487b9bb6bae55013 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Sat, 26 Nov 2022 11:01:19 -0600 Subject: [PATCH 025/110] convert actionTypes to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/redux/constants/{actionTypes.js => actionTypes.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/redux/constants/{actionTypes.js => actionTypes.ts} (100%) diff --git a/src/redux/constants/actionTypes.js b/src/redux/constants/actionTypes.ts similarity index 100% rename from src/redux/constants/actionTypes.js rename to src/redux/constants/actionTypes.ts From 8de65830eac788bcb7e3d69c73d5542a3f026e82 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Sat, 26 Nov 2022 11:53:36 -0600 Subject: [PATCH 026/110] tests added to VolumeTab Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 27b2b1fa..fce33f1c 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,9 @@ "@mui/icons-material": "^5.10.6", "@mui/material": "^5.10.8", "@mui/x-data-grid": "^5.17.7", - "@testing-library/user-event": "^14.4.3", + "@redux-devtools/extension": "^3.2.3", "@reduxjs/toolkit": "^1.9.0", + "@testing-library/user-event": "^14.4.3", "@types/node": "^18.8.3", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", From 2f081eaf4267cf075b554251f45a256764f1d3d3 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Sat, 26 Nov 2022 11:54:10 -0600 Subject: [PATCH 027/110] tests added to VolumeTabs Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- __tests__/UsersTab.test.js | 8 +++ __tests__/VolumeTab.test.js | 101 +++++++++++----------------------- src/components/tabs/Images.js | 2 +- src/renderer/store.ts | 1 - 4 files changed, 40 insertions(+), 72 deletions(-) diff --git a/__tests__/UsersTab.test.js b/__tests__/UsersTab.test.js index 15a4b855..9ad5ba7e 100644 --- a/__tests__/UsersTab.test.js +++ b/__tests__/UsersTab.test.js @@ -65,6 +65,14 @@ describe('Create a New User functionality', () => { }); +//* Dummy Test +describe('dummy test', () => { + test('dummy test', () => { + expect(2 + 2).toBe(4); + }); +}); + +/* ----- clean up functions ---- */ const getPasswordField = () => { return document.getElementById('signupPassword'); }; diff --git a/__tests__/VolumeTab.test.js b/__tests__/VolumeTab.test.js index 64fb7a41..e8910599 100644 --- a/__tests__/VolumeTab.test.js +++ b/__tests__/VolumeTab.test.js @@ -2,92 +2,53 @@ * These tests do not work as enzyme is highly depricated and does not communicate with React 18 */ - import React from 'react'; -import {describe, expect, test, jest} from '@jest/globals'; +import { describe, expect, test, jest } from '@jest/globals'; import VolumeTab from '../src/components/tabs/VolumeHistory'; import '@testing-library/react'; import '@testing-library/jest-dom'; import { fireEvent, render, screen } from '@testing-library/react'; - - // configure({ adapter: new Adapter() }) - -describe('Volumes Tab', () => { - const props = { - volumeContainersList: [ - { - vol_name: 'volumetest1', - containers: [ - { Names: 'container1', State: 'Running', Status: '40 minutes ago' }, - ] - }, - { - vol_name: 'volumetest2', - containers: [ - { Names: 'container2', State: 'Running', Status: '25 minutes ago' }, - { Names: 'container3', State: '', Status: '' } - ] - } - ] - }; -} - -/*------ testing rendering test -----*/ +const props = { + volumeContainersList: [ + { + vol_name: 'volumetest1', + containers: [ + { Names: 'container1', State: 'Running', Status: '40 minutes ago' }, + ], + }, + { + vol_name: 'volumetest2', + containers: [ + { Names: 'container2', State: 'Running', Status: '25 minutes ago' }, + { Names: 'container3', State: '', Status: '' }, + ], + }, + ], +}; + +/* ------ rendering test ----- */ describe('rendering VolumeTab', () => { - beforeAll(() => { + test('shows volumes', () => { render(); - }) - - describe('volume list appears on screen', () => { - const - }) -}) - - -// const wrapper = shallow(); -// // console.log(wrapper.debug()) - -// it('renders a
element for Volume Tab', () => { -// expect(wrapper.type()).toEqual('div'); -// }); + }); +}); -// it('renders each volume in state', () => { -// expect(wrapper.find('div.containers').children().length) -// .toBe(props.volumeContainersList.length); -// }); +/* ----- search bar ----- */ -// it('renders a
element for each container in volume', () => { -// wrapper.find('.volume-container-details').forEach((node) => { -// expect(node.type()).toEqual('div'); -// }); -// }); -// it('renders the correct number of containers for each volume', () => { -// expect(wrapper.find('.box').at(0).find('.volume-container-details').length) -// .toBe(props.volumeContainersList[0].containers.length); -// expect(wrapper.find('.box').at(0).find('.volume-container-details').length) -// .toEqual(1); -// expect(wrapper.find('.box').at(0).find('.volume-container-details').length) -// .not.toBe(0); -// expect(wrapper.find('.box').at(0).find('.volume-container-details').length) -// .not.toBe(undefined); -// }); -// it('each container renders correct properties from state', () => { -// expect(wrapper.find('.volume-container-details').at(0).childAt(1).text()) -// .toEqual(props.volumeContainersList[0].containers[0].Names); -// expect(wrapper.find('ul').at(0).childAt(0).text()) -// .toEqual(`Status: ${props.volumeContainersList[0].containers[0].State}`); -// expect(wrapper.find('ul').at(0).childAt(1).text()) -// .toEqual(`Running For: ${props.volumeContainersList[0].containers[0].Status}`); -// }); -// }); +//* Dummy Test +describe('dummy test', () => { + test('dummy test', () => { + expect(2 + 2).toBe(4); + }); +}); //* Dummy Test describe('dummy test', () => { test('dummy test', () => { expect(2 + 2).toBe(4); }); -}); \ No newline at end of file +}); diff --git a/src/components/tabs/Images.js b/src/components/tabs/Images.js index 03382943..2c1675a2 100644 --- a/src/components/tabs/Images.js +++ b/src/components/tabs/Images.js @@ -12,7 +12,7 @@ const Images = (props) => { const handleClick = (e) => { e.preventDefault(); - helper.pullImage(repo); + helper.pullImage('redis'); }; const renderImagesList = props.imagesList.map((ele, i) => { diff --git a/src/renderer/store.ts b/src/renderer/store.ts index 95c20071..382f22a2 100644 --- a/src/renderer/store.ts +++ b/src/renderer/store.ts @@ -18,4 +18,3 @@ export default store; - From 0e14b2dc77fb5f0b085ef75a780feb7fe903744b Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Sat, 26 Nov 2022 12:00:11 -0600 Subject: [PATCH 028/110] completed converting actions file to TypeScript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- package.json | 2 +- src/components/helper/commands.js | 1 + src/redux/actions/{actions.js => actions.ts} | 74 ++++++++++---------- 3 files changed, 39 insertions(+), 38 deletions(-) rename src/redux/actions/{actions.js => actions.ts} (52%) diff --git a/package.json b/package.json index d7875ab8..de7bc39c 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "@mui/icons-material": "^5.10.6", "@mui/material": "^5.10.8", "@mui/x-data-grid": "^5.17.7", - "@reduxjs/toolkit": "^1.9.0", "@types/node": "^18.8.3", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", @@ -47,6 +46,7 @@ "@babel/preset-typescript": "^7.18.6", "@mui/system": "^5.10.10", "@redux-devtools/core": "^3.13.1", + "@reduxjs/toolkit": "^1.9.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@types/pg": "^8.6.5", diff --git a/src/components/helper/commands.js b/src/components/helper/commands.js index c771a82d..9f9c4254 100644 --- a/src/components/helper/commands.js +++ b/src/components/helper/commands.js @@ -226,6 +226,7 @@ export const runStopped = ( * @param {*} callback_2 */ +// this function is used to run an image from the image tab export const runIm = (id, runningList, callback_1, callback_2) => { // props.runIm(ele['imgid'], props.runningList, helper.addRunning, props.addRunningContainers) window.nodeMethod.runExec(`docker run ${id}`, (error, stdout, stderr) => { diff --git a/src/redux/actions/actions.js b/src/redux/actions/actions.ts similarity index 52% rename from src/redux/actions/actions.js rename to src/redux/actions/actions.ts index d949c991..4210b453 100644 --- a/src/redux/actions/actions.js +++ b/src/redux/actions/actions.ts @@ -1,151 +1,151 @@ import * as types from '../constants/actionTypes'; -export const addRunningContainers = (data) => ({ +export const addRunningContainers = (data: object[]) => ({ type: types.ADD_RUNNING_CONTAINERS, payload: data }); -export const removeContainer = (id) => ({ +export const removeContainer = (id: number) => ({ type: types.REMOVE_CONTAINER, payload: id }); -export const stopRunningContainer = (id) => ({ +export const stopRunningContainer = (id: number) => ({ type: types.STOP_RUNNING_CONTAINER, payload: id }); -export const addStoppedContainers = (data) => ({ +export const addStoppedContainers = (data: object[]) => ({ type: types.ADD_STOPPED_CONTAINERS, payload: data }); -export const runStoppedContainer = (id) => ({ +export const runStoppedContainer = (id: number) => ({ type: types.RUN_STOPPED_CONTAINER, payload: id }); -export const getImages = (data) => ({ +export const getImages = (data: object[]) => ({ type: types.GET_IMAGES, payload: data }); -export const runImage = (id) => ({ +export const runImage = (id: number) => ({ type: types.RUN_IMAGE, payload: id }); -export const removeImage = (id) => ({ +export const removeImage = (id: number) => ({ type: types.REMOVE_IMAGE, payload: id }); -export const refreshRunningContainers = (data) => ({ +export const refreshRunningContainers = (data: object[]) => ({ type: types.REFRESH_RUNNING_CONTAINERS, payload: data }); -export const refreshStoppedContainers = (data) => ({ +export const refreshStoppedContainers = (data: object[]) => ({ type: types.REFRESH_STOPPED_CONTAINERS, payload: data }); -export const refreshImages = (data) => ({ +export const refreshImages = (data: object[]) => ({ type: types.REFRESH_IMAGES, payload: data }); -export const composeymlFiles = (data) => ({ +export const composeymlFiles = (data: object[]) => ({ type: types.COMPOSE_YML_FILES, payload: data }); -export const getNetworkContainers = (data) => ({ +export const getNetworkContainers = (data: object[]) => ({ type: types.GET_NETWORK_CONTAINERS, payload: data }); -export const getContainerStacks = (data) => ({ +export const getContainerStacks = (data: object[]) => ({ type: types.GET_CONTAINER_STACKS, payload: data }); -export const composeDown = (data) => ({ +export const composeDown = (data: object[]) => ({ type: types.COMPOSE_DOWN, payload: data }); -export const composeUp = (data) => ({ +export const composeUp = (data: object[]) => ({ type: types.COMPOSE_UP, payload: data }); -export const buildAxis = (data) => ({ +export const buildAxis = (data: object[]) => ({ type: types.BUILD_AXIS, payload: data }); -export const buildMemory = (data) => ({ +export const buildMemory = (data: object[]) => ({ type: types.BUILD_MEMORY, payload: data }); -export const buildCpu = (data) => ({ +export const buildCpu = (data: object[]) => ({ type: types.BUILD_CPU, payload: data }); -export const buildWrittenIO = (data) => ({ +export const buildWrittenIO = (data: object[]) => ({ type: types.BUILD_WRITTEN_IO, payload: data }); -export const buildReadIO = (data) => ({ +export const buildReadIO = (data: object[]) => ({ type: types.BUILD_READ_IO, payload: data }); -export const addPhoneNumber = (data) => ({ +export const addPhoneNumber = (data: object[]) => ({ type: types.ADD_PHONE_NUMBER, payload: data }); -export const addMemoryNotificationSetting = (data) => ({ +export const addMemoryNotificationSetting = (data: object[]) => ({ type: types.ADD_MEMORY_NOTIFICATION_SETTING, payload: data }); -export const addCpuNotificationSetting = (data) => ({ +export const addCpuNotificationSetting = (data: object[]) => ({ type: types.ADD_CPU_NOTIFICATION_SETTING, payload: data }); -export const addStoppedNotificationSetting = (data) => ({ +export const addStoppedNotificationSetting = (data: object[]) => ({ type: types.ADD_STOPPED_NOTIFICATION_SETTING, payload: data }); -export const removeMemoryNotificationSetting = (data) => ({ +export const removeMemoryNotificationSetting = (data: object[]) => ({ type: types.REMOVE_MEMORY_NOTIFICATION_SETTING, payload: data }); -export const removeCpuNotificationSetting = (data) => ({ +export const removeCpuNotificationSetting = (data: object[]) => ({ type: types.REMOVE_CPU_NOTIFICATION_SETTING, payload: data }); -export const removeStoppedNotificationSetting = (data) => ({ +export const removeStoppedNotificationSetting = (data: object[]) => ({ type: types.REMOVE_STOPPED_NOTIFICATION_SETTING, payload: data }); -export const addNotificationFrequency = (data) => ({ +export const addNotificationFrequency = (data: object[]) => ({ type: types.NOTIFICATION_FREQUENCY, payload: data }); -export const addMonitoringFrequency = (data) => ({ +export const addMonitoringFrequency = (data: object[]) => ({ type: types.MONITORING_FREQUENCY, payload: data }); @@ -154,40 +154,40 @@ export const updateSession = () => ({ type: types.UPDATE_SESSION }); -export const updateUser = (data) => ({ +export const updateUser = (data: object[]) => ({ type: types.UPDATE_USER, payload: data }); -export const logoutUser = (data) => ({ +export const logoutUser = (data: object[]) => ({ type: types.LOGOUT_USER, payload: data }); -export const updateUserList = (data) => ({ +export const updateUserList = (data: object[]) => ({ type: types.UPDATE_USER_LIST, payload: data }); -export const updateUserRole = (data) => ({ +export const updateUserRole = (data: object[]) => ({ type: types.UPDATE_USER_ROLE, payload: data }); // get volume -export const getVolumeList = (data) => ({ +export const getVolumeList = (data: object[]) => ({ type: types.GET_VOLUME_LIST, payload: data }); // get containers that live in volume -export const getVolumeContainersList = (data) => ({ +export const getVolumeContainersList = (data: object[]) => ({ type: types.GET_VOLUME_CONTAINERS_LIST, payload: data }); // get container logs -export const getContainerLogs = (data) => ({ +export const getContainerLogs = (data: object[]) => ({ type: types.GET_CONTAINER_LOGS, payload: data }); From 31bc14d53b5dd6028edb9dd335ca78f635702574 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Sat, 26 Nov 2022 12:09:22 -0600 Subject: [PATCH 029/110] corrected type error for updateUser in actions.ts Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/redux/actions/actions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redux/actions/actions.ts b/src/redux/actions/actions.ts index 4210b453..a61ebbdb 100644 --- a/src/redux/actions/actions.ts +++ b/src/redux/actions/actions.ts @@ -154,7 +154,7 @@ export const updateSession = () => ({ type: types.UPDATE_SESSION }); -export const updateUser = (data: object[]) => ({ +export const updateUser = (data: object) => ({ type: types.UPDATE_USER, payload: data }); From 66f3b0e774e39bde00cd72080c7dc80c72fe9ec5 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Sat, 26 Nov 2022 12:18:04 -0600 Subject: [PATCH 030/110] converted the slack folder files to TypeScript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/main/slack/{cpuNotification.js => cpuNotification.ts} | 0 src/main/slack/{memoryNotification.js => memoryNotification.ts} | 1 + 2 files changed, 1 insertion(+) rename src/main/slack/{cpuNotification.js => cpuNotification.ts} (100%) rename src/main/slack/{memoryNotification.js => memoryNotification.ts} (99%) diff --git a/src/main/slack/cpuNotification.js b/src/main/slack/cpuNotification.ts similarity index 100% rename from src/main/slack/cpuNotification.js rename to src/main/slack/cpuNotification.ts diff --git a/src/main/slack/memoryNotification.js b/src/main/slack/memoryNotification.ts similarity index 99% rename from src/main/slack/memoryNotification.js rename to src/main/slack/memoryNotification.ts index 59e31f48..c9acd773 100644 --- a/src/main/slack/memoryNotification.js +++ b/src/main/slack/memoryNotification.ts @@ -15,3 +15,4 @@ const memoryNotification = async function () { }; export default memoryNotification; + \ No newline at end of file From 991fa9f6d622d7f87d7978469d1182ee7ce39de3 Mon Sep 17 00:00:00 2001 From: Jack Date: Sat, 26 Nov 2022 16:45:10 -0500 Subject: [PATCH 031/110] update listreducers-test Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- __tests__/ListReducer.test.js | 86 ++++++++++++++++--------------- server/server.js | 3 +- src/components/helper/commands.js | 4 ++ src/main/index.ts | 4 +- 4 files changed, 53 insertions(+), 44 deletions(-) diff --git a/__tests__/ListReducer.test.js b/__tests__/ListReducer.test.js index e4ede18d..2640cabb 100644 --- a/__tests__/ListReducer.test.js +++ b/__tests__/ListReducer.test.js @@ -48,24 +48,27 @@ describe('Dockeeter reducer', () => { }); }); - // describe('REFRESH_STOPPED_CONTAINERS', () => { - // it('should overwrite the stoppedList array in the state to update it', () => { - // expect(state.stoppedList.length).toEqual(0); - // let action = { - // type: 'REFRESH_STOPPED_CONTAINERS', - // payload: [{ cid: '123' }, { cid: '456' }] - // }; - // expect(containerListReducer(state, action).stoppedList.length).toEqual(2); - // action = { - // type: 'REFRESH_STOPPED_CONTAINERS', - // payload: [{ cid: '789' }] - // }; - // expect(containerListReducer(state, action).stoppedList.length).toEqual(1); - // expect(containerListReducer(state, action).stoppedList[0].ID).toEqual( - // '789' - // ); - // }); - // }); + describe('REFRESH_STOPPED_CONTAINERS', () => { + it('should overwrite the stoppedList array in the state to update it', () => { + expect(state.stoppedList.length).toEqual(0); + let action = { + type: 'REFRESH_STOPPED_CONTAINERS', + payload: [{ cid: '123' }, { cid: '456' }] + }; + let newState = containerListReducer(state, action); + expect(newState.stoppedList.length).toEqual(2); + + action = { + type: 'REFRESH_STOPPED_CONTAINERS', + payload: [{ cid: '789' }] + }; + newState = containerListReducer(state, action); + expect(newState.stoppedList.length).toEqual(1); + expect(newState.stoppedList[0].cid).toEqual( + '789' + ); + }); + }); describe('REFRESH_IMAGES', () => { it('should overwrite the imagesList array in the state to update it', () => { @@ -94,30 +97,31 @@ describe('Dockeeter reducer', () => { }); }); - // describe('STOP_RUNNING_CONTAINER', () => { - // it('should remove a specified container from the runningList and add it to the stoppedList', () => { - // let newState = { - // runningList: [{ cid: '123' }, { cid: '456' }], - // stoppedList: [] - // }; - // const action = { type: 'STOP_RUNNING_CONTAINER', payload: '123' }; - // newState = containerListReducer(newState, action); - // expect(newState.runningList[0].ID).toEqual('456'); - // }); - // }); + describe('STOP_RUNNING_CONTAINER', () => { + it('should remove a specified container from the runningList and add it to the stoppedList', () => { + let newState = { + runningList: [{ ID: '123' }, { ID: '456' }], + stoppedList: [] + }; + const action = { type: 'STOP_RUNNING_CONTAINER', payload: '123' }; + newState = containerListReducer(newState, action); + expect(newState.runningList[0].ID).toEqual('456'); + }); + }); - // describe('RUN_STOPPED_CONTAINER', () => { - // it('should remove a specified container from the stoppedList', () => { - // const newState = { - // runningList: [], - // stoppedList: [{ cid: '123' }, { cid: '456' }] - // }; - // const action = { type: 'RUN_STOPPED_CONTAINER', payload: '123' }; - // expect(containerListReducer(newState, action).stoppedList[0].ID).toEqual( - // '456' - // ); - // }); - // }); + describe('RUN_STOPPED_CONTAINER', () => { + it('should remove a specified container from the stoppedList', () => { + const newState = { + runningList: [], + stoppedList: [{ ID: '123' }, { ID: '456' }] + }; + const action = { type: 'RUN_STOPPED_CONTAINER', payload: '123' }; + console.log(newState); + expect(containerListReducer(newState, action).stoppedList[0].ID).toEqual( + '45fsdf6' + ); + }); + }); describe('REMOVE_IMAGE', () => { it('should remove a specified image from the imagesList', () => { diff --git a/server/server.js b/server/server.js index cdebb026..371750e8 100644 --- a/server/server.js +++ b/server/server.js @@ -1,9 +1,10 @@ const app = require('./app'); const colors = require('colors'); -const PORT = 3000; +const PORT = process.env.PORT || 3000; // Open up server on PORT app.listen(PORT, () => { + console.log(process.env); console.log(`server is listening on port ${PORT}`.green.inverse); }); diff --git a/src/components/helper/commands.js b/src/components/helper/commands.js index 609103df..5e293d0c 100644 --- a/src/components/helper/commands.js +++ b/src/components/helper/commands.js @@ -306,6 +306,10 @@ export const pullImage = (repo) => { console.log(`pullImage stderr: ${stderr}`); return; } + alert(`${repo} is currently being downloaded`); + console.log(stdout); + console.log(repo, 'is currently being pulled'); + // if not error, add a loading component until page renders a new component }); }; diff --git a/src/main/index.ts b/src/main/index.ts index cdd8ea2a..0e731e73 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -33,7 +33,7 @@ function createMainWindow() { } else { mainWindow.loadURL( url.format({ - pathname: path.join(__dirname, '/index.js'), + pathname: path.join(__dirname, '/index'), protocol: 'file:', slashes: true, }) @@ -45,7 +45,7 @@ function createMainWindow() { } electron.app.on('ready', createMainWindow) - + electron.app.on('renderer-process-crashed', createMainWindow) // MacOS Specific function electron.app.on('window-all-closed', function () { // Common for application and their menu bar to stay active until use quits explicitly From b904d775fc055baf4231cb76f840f71e35799cf2 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Sat, 26 Nov 2022 13:45:24 -0800 Subject: [PATCH 032/110] converted a reducer, notificationCategories, and emails. --- src/main/email/{emailEvent.js => emailEvent.ts} | 7 ++++--- ...onCategories.js => notificationCategories.ts} | 0 ...ersReducer.js => runningContainersReducer.ts} | 16 +++++++++++----- 3 files changed, 15 insertions(+), 8 deletions(-) rename src/main/email/{emailEvent.js => emailEvent.ts} (68%) rename src/redux/constants/{notificationCategories.js => notificationCategories.ts} (100%) rename src/redux/reducers/{runningContainersReducer.js => runningContainersReducer.ts} (78%) diff --git a/src/main/email/emailEvent.js b/src/main/email/emailEvent.ts similarity index 68% rename from src/main/email/emailEvent.js rename to src/main/email/emailEvent.ts index 6f1aabc6..c765bcd5 100644 --- a/src/main/email/emailEvent.js +++ b/src/main/email/emailEvent.ts @@ -1,6 +1,8 @@ -const fetch = require('node-fetch'); +import fetch from 'node-fetch'; +// TODO: the communication files seem to be unused, could improve functionality -const emailEvent = (args) => { + +const emailEvent = (args: string[]) => { fetch('http://localhost:3000/api', { method: 'POST', headers: { @@ -19,4 +21,3 @@ const emailEvent = (args) => { }); }; -// export default emailEvent; diff --git a/src/redux/constants/notificationCategories.js b/src/redux/constants/notificationCategories.ts similarity index 100% rename from src/redux/constants/notificationCategories.js rename to src/redux/constants/notificationCategories.ts diff --git a/src/redux/reducers/runningContainersReducer.js b/src/redux/reducers/runningContainersReducer.ts similarity index 78% rename from src/redux/reducers/runningContainersReducer.js rename to src/redux/reducers/runningContainersReducer.ts index 3cd4cf37..e86e1ebc 100644 --- a/src/redux/reducers/runningContainersReducer.js +++ b/src/redux/reducers/runningContainersReducer.ts @@ -1,11 +1,17 @@ import * as types from '../constants/actionTypes'; +import { PayloadAction } from '@reduxjs/toolkit'; -const initialState = { +interface stateType { + runningList: any[], + stoppedList: any[] +} + +const initialState: stateType = { runningList: [], stoppedList: [] }; -export default function (state = initialState, action) { +export default function (state = initialState, action: PayloadAction) { switch (action.type) { case types.ADD_RUNNING_CONTAINERS: const newRunningList = state.runningList.slice(); @@ -16,7 +22,7 @@ export default function (state = initialState, action) { case types.STOP_RUNNING_CONTAINER: const newStoppedList = state.stoppedList.slice(); - const newestRunningList = []; + const newestRunningList: object[] = []; for (let container of state.runningList) { if (container.ID !== action.payload) { newestRunningList.push(container); @@ -30,7 +36,7 @@ export default function (state = initialState, action) { case types.RUN_STOPPED_CONTAINER: const runningListCopy = state.runningList.slice(); - const newerStoppedContainer = []; + const newerStoppedContainer: object[] = []; for (let container of state.stoppedList) { if (container.ID === action.payload) { } else { @@ -44,7 +50,7 @@ export default function (state = initialState, action) { }; case types.REFRESH_RUNNING_CONTAINERS: - const newRunningList2 = []; + const newRunningList2: object[] = []; for (const container of action.payload) { newRunningList2.push(container); } From c8ebee26661c7414e47fb9f0ae76fb34b2e861b2 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Sat, 26 Nov 2022 15:46:25 -0600 Subject: [PATCH 033/110] testing suites for UsersTab and VolumeTab Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- __tests__/ImageTab.test.js | 26 +++++++++++------ __tests__/UsersTab.test.js | 46 +++++++++++++------------------ package.json | 3 +- src/components/helper/commands.js | 2 ++ src/components/tabs/Images.js | 2 +- src/renderer/store.ts | 1 - 6 files changed, 42 insertions(+), 38 deletions(-) diff --git a/__tests__/ImageTab.test.js b/__tests__/ImageTab.test.js index 63f5b478..d30b7e2a 100644 --- a/__tests__/ImageTab.test.js +++ b/__tests__/ImageTab.test.js @@ -36,10 +36,18 @@ const props = { // const mockRun = jest.fn(); // const mockRremove = jest.fn(); +/* ----- search bar ----- */ + +test('Search accepts input', async () => { + const search = screen.getByRole('input'); + await fireEvent.change(search, { target: { value: 'search' } }); + expect(search.value).toBe('search'); +}); + /* ----- button testing ------ */ describe('run button on click', () => { - it('fires run button functionality', async () => { + test('fires run button functionality', async () => { const { container } = render(); const runButton = screen.getByRole('button', { name: 'RUN' }); await fireEvent.click(runButton); @@ -49,7 +57,7 @@ describe('run button on click', () => { // currently gets stuck at window.runExec method --> reads undefined describe('pull button on click', () => { - it('fires pull button functionality', async () => { + test('fires pull button functionality', async () => { const { container } = render(); const pullButton = screen.getByRole('button', { name: 'Pull' }); await fireEvent.click(pullButton); @@ -58,7 +66,7 @@ describe('pull button on click', () => { }); describe('remove button on click', () => { - it('fires remove button functionality', async () => { + test('fires remove button functionality', async () => { const { container } = render(); const removeButton = screen.getByRole('button', { name: 'REMOVE' }); await fireEvent.click(removeButton); @@ -71,15 +79,17 @@ describe('remove button on click', () => { /* ------ actions/reducers ------ */ describe('Images', () => { - it('renders an image if one is found', () => { + test('renders an image if one is found', () => { render(); }); - it('refreshes page if image removed', () => { - // need to check that refreshRunning helper function is called - - }); }); +// it('refreshes page if image removed', () => { +// // need to check that refreshRunning helper function is called + +// }); +// }); + //* Dummy Test describe('dummy test', () => { test('dummy test', () => { diff --git a/__tests__/UsersTab.test.js b/__tests__/UsersTab.test.js index 9ad5ba7e..8a2aaa43 100644 --- a/__tests__/UsersTab.test.js +++ b/__tests__/UsersTab.test.js @@ -20,8 +20,7 @@ const props = { onSubmit: jest.fn(), onChange: jest.fn(), onClick: jest.fn(), -} - +}; /* ----- Mock Functions ----- */ @@ -46,37 +45,30 @@ describe('Create a New User functionality', () => { expect(input).toHaveValue(''); }); // password is at least 6 chars long - test('password field must have at least 6 chars', () => { - + test('Password must be 6 characters long', async () => { + const password = document.getElementById('signupPassword'); + await fireEvent.change(password, { target: { value: '123456' } }); + expect(password.value).toBe('123456'); }); // password and confirm passwords match // if either of these tests fail, check for error message alert + test('Password must match Confirm Password', async () => { + const signup = document.getElementById('signupPassword'); + const confirmation = document.getElementById('signupPasswordConfirmation'); + await fireEvent.change(signup, { target: { value: '123456' } }); + await fireEvent.change(confirmation, { target: { value: '123456' } }); + expect(signup.value).toBe(confirmation.value); + }); // submit button works when password fields match - test('onSubmit is called when password fields match', async () => { - user.type(getPasswordField(), '123456'); - user.type(getConfirmationField(), '123456'); - const submitBtn = screen.getByRole('button', { name: 'submit' }); - await user.click(submitBtn); - screen.debug(); - expect(submitBtn).toBeCalled(); - }) - // Manage Users Table should gain a row + test('onSubmit is called when password fields match', async () => {}); -}); + // Manage Users Table gains a row with correct information -//* Dummy Test -describe('dummy test', () => { - test('dummy test', () => { - expect(2 + 2).toBe(4); + //* Dummy Test + describe('dummy test', () => { + test('dummy test', () => { + expect(2 + 2).toBe(4); + }); }); }); - -/* ----- clean up functions ---- */ -const getPasswordField = () => { - return document.getElementById('signupPassword'); -}; - -const getConfirmationField = () => { - return document.getElementById('signupPasswordConfirmation'); -} \ No newline at end of file diff --git a/package.json b/package.json index fce33f1c..f2122894 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "@mui/icons-material": "^5.10.6", "@mui/material": "^5.10.8", "@mui/x-data-grid": "^5.17.7", - "@redux-devtools/extension": "^3.2.3", "@reduxjs/toolkit": "^1.9.0", "@testing-library/user-event": "^14.4.3", "@types/node": "^18.8.3", @@ -74,6 +73,7 @@ "react-test-renderer": "^18.2.0", "style-loader": "^3.3.1", "supertest": "^6.3.0", + "ts-jest": "^29.0.3", "ts-loader": "^9.4.1", "ts-node": "^10.9.1", "twilio": "^3.82.1", @@ -92,6 +92,7 @@ "transform": { "^.+\\.jsx?$": "babel-jest" }, + "preset": "ts-jest", "moduleNameMapper": { "\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/mocks/fileMock.js", "\\.(css|scss)$": "/__mocks__/styleMock.js" diff --git a/src/components/helper/commands.js b/src/components/helper/commands.js index c771a82d..ec33bec3 100644 --- a/src/components/helper/commands.js +++ b/src/components/helper/commands.js @@ -305,6 +305,8 @@ export const pullImage = (repo) => { console.log(`pullImage stderr: ${stderr}`); return; } + console.log(stdout); + console.log('pull image working'); }); }; diff --git a/src/components/tabs/Images.js b/src/components/tabs/Images.js index 2c1675a2..03382943 100644 --- a/src/components/tabs/Images.js +++ b/src/components/tabs/Images.js @@ -12,7 +12,7 @@ const Images = (props) => { const handleClick = (e) => { e.preventDefault(); - helper.pullImage('redis'); + helper.pullImage(repo); }; const renderImagesList = props.imagesList.map((ele, i) => { diff --git a/src/renderer/store.ts b/src/renderer/store.ts index 382f22a2..b168044d 100644 --- a/src/renderer/store.ts +++ b/src/renderer/store.ts @@ -1,6 +1,5 @@ import { configureStore } from '@reduxjs/toolkit' import reducers from '../redux/reducers/index'; -import { composeWithDevTools } from '@redux-devtools/extension'; const store = configureStore({ reducer: reducers, From e44d012934ae58d3f0ea56f79193d0e4a3e53fe8 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Sat, 26 Nov 2022 16:36:11 -0600 Subject: [PATCH 034/110] convert preload file to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/main/preload.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/preload.ts diff --git a/src/main/preload.ts b/src/main/preload.ts new file mode 100644 index 00000000..c56e396c --- /dev/null +++ b/src/main/preload.ts @@ -0,0 +1,24 @@ +import { contextBridge, ipcRenderer } from "electron"; +import child_process, { ExecException } from "child_process"; + +interface callback { + ( + err: ExecException, + //stdout: standard output + stdout: string | Buffer, + //stderr: standard error + stderr: string | Buffer + ): object; +} + +function runExec(command: string, callback: callback): object { + return child_process.exec(command, callback as any); +} + +// Access in the renderer/react as window.childProcess.exec +contextBridge.exposeInMainWorld("nodeMethod", { + runExec: runExec, + bool: true, + rendInvoke: (input1: string, input2: (...args: any[]) => Promise) => + ipcRenderer.invoke(input1, input2), +}); From a8bce87d216d44d244ded81e6adf49347f65e6ec Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Tue, 29 Nov 2022 09:16:16 -0600 Subject: [PATCH 035/110] removed a semicolon Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- src/components/helper/initDatabase.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/helper/initDatabase.js b/src/components/helper/initDatabase.js index e101e016..69e8ad60 100644 --- a/src/components/helper/initDatabase.js +++ b/src/components/helper/initDatabase.js @@ -7,14 +7,16 @@ export default () => { }) .then((data) => data.json()) .then((response) => { - if (response.error !== null){ + if (response.error !== null) { console.log('Make sure Docker Desktop is running', response.error); // Not clear why the alert is needed // i'll change to console.log for now - console.log(`Make sure Docker Desktop is running. \n\n ${response.error}`); + console.log( + `Make sure Docker Desktop is running. \n\n ${response.error}` + ); return; } - if (response.stderr){ + if (response.stderr) { console.log(`stderr: ${response.stderr}`); return; } @@ -22,7 +24,7 @@ export default () => { }) .catch((err) => { console.log(err); - }); + }) .then((data) => data.json()) .then((response) => { if (response.error !== null) { From 67f75cec526d127bb246d390a41e959fecdbf946 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Tue, 29 Nov 2022 08:46:07 -0800 Subject: [PATCH 036/110] added message to preload --- src/main/preload.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/preload.ts b/src/main/preload.ts index c56e396c..64704d4f 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -1,3 +1,4 @@ +// unable to currently use this ts file, possible conflict with electron import { contextBridge, ipcRenderer } from "electron"; import child_process, { ExecException } from "child_process"; From d935e259939f78809a0967d6c201d439f16b231e Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Tue, 29 Nov 2022 08:46:59 -0800 Subject: [PATCH 037/110] removed reference to unused package. --- src/renderer/store.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/store.ts b/src/renderer/store.ts index 95c20071..6eb83f0f 100644 --- a/src/renderer/store.ts +++ b/src/renderer/store.ts @@ -1,6 +1,5 @@ import { configureStore } from '@reduxjs/toolkit' import reducers from '../redux/reducers/index'; -import { composeWithDevTools } from '@redux-devtools/extension'; const store = configureStore({ reducer: reducers, From 4812918d78d7f444cbbc60ee31b48f4d3473bfc3 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Tue, 29 Nov 2022 09:56:20 -0800 Subject: [PATCH 038/110] working container tab, created TabTypes file --- .../tabs/{Containers.js => Containers.tsx} | 14 +++---- src/components/tabs/TabTypes.ts | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-) rename src/components/tabs/{Containers.js => Containers.tsx} (90%) create mode 100644 src/components/tabs/TabTypes.ts diff --git a/src/components/tabs/Containers.js b/src/components/tabs/Containers.tsx similarity index 90% rename from src/components/tabs/Containers.js rename to src/components/tabs/Containers.tsx index 5169c412..5aab52e5 100644 --- a/src/components/tabs/Containers.js +++ b/src/components/tabs/Containers.tsx @@ -2,15 +2,15 @@ import React from 'react'; import { Chart } from 'react-chartjs-2'; import ToggleDisplay from '../display/ToggleDisplay'; - +import { ContainerProps, ContainerType, ChartInfoType} from './TabTypes'; /** * Display all running and stopped containers * * @param {*} props */ -const Containers = (props) => { - const renderStoppedList = props.stoppedList.map((container, i) => { +const Containers = (props: ContainerProps) => { + const renderStoppedList = props.stoppedList.map((container: ContainerType, i: number) => { return (
@@ -52,7 +52,7 @@ const Containers = (props) => { ); }); - const renderRunningList = props.runningList.map((container, i) => { + const renderRunningList = props.runningList.map((container: ContainerType, i: number) => { const cpuData = parseFloat( container.CPUPerc.substring(0, container.CPUPerc.length - 1) ).toFixed(2); @@ -60,7 +60,7 @@ const Containers = (props) => { container.MemPerc.substring(0, container.MemPerc.length - 1) ).toFixed(2); const stack = 'stack'; - const chartInfo = { + const chartInfo: ChartInfoType = { labels: ['CPU', 'Memory'], datasets: [ { @@ -75,7 +75,7 @@ const Containers = (props) => { { stack, label: Math.random(), - data: [(100 - cpuData).toFixed(2), (100 - memoryData).toFixed(2)], + data: [(100 - Number(cpuData)).toFixed(2), (100 - Number(memoryData)).toFixed(2)], backgroundColor: ['rgba(155, 198, 233, 1)', 'rgba(217, 252, 219, 1)'], borderColor: 'rgba(0,0,0,0)', borderWidth: 1, @@ -135,7 +135,7 @@ const Containers = (props) => { className='stop-btn' onClick={() => props.stop(container.ID, props.stopRunningContainer)} > - STOfP + STOP
diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts new file mode 100644 index 00000000..8f8fa444 --- /dev/null +++ b/src/components/tabs/TabTypes.ts @@ -0,0 +1,39 @@ +// gotta do all these functions +export type ContainerProps = { + stoppedList: any[]; + runStopped: any; + runStoppedContainer: any; + removeContainer: any; + stopRunningContainer: any; + remove: any; + stop: any; + runningList: any[]; + } + // gotta do all those anys + export type ContainerType = { + Name: string; + Names: string; + ID: number; + Image: any; + RunningFor: any; + CPUPerc: any; + MemPerc: any; + + } + + export type ChartInfoType = { + labels: string[] | number[]; + datasets: DataSetType[]; + + } + // just the label function + export type DataSetType = { + stack: string; + label: any; + data: string[]; + backgroundColor: string[]; + borderColor: string; + borderWidth: number; + barPercentage: number; + } + \ No newline at end of file From f9d7183b569a651cebfe23889459ca5c9383b38c Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Tue, 29 Nov 2022 15:30:32 -0600 Subject: [PATCH 039/110] convert AccountDisplay to tsx Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/display/AccountDisplay.js | 161 --------------------- src/components/display/AccountDisplay.tsx | 164 ++++++++++++++++++++++ 2 files changed, 164 insertions(+), 161 deletions(-) delete mode 100644 src/components/display/AccountDisplay.js create mode 100644 src/components/display/AccountDisplay.tsx diff --git a/src/components/display/AccountDisplay.js b/src/components/display/AccountDisplay.js deleted file mode 100644 index 2ce37327..00000000 --- a/src/components/display/AccountDisplay.js +++ /dev/null @@ -1,161 +0,0 @@ -/** - * @module AccountDisplay - * @description Account Display for Settings tab, this will host any forms to update account details such as email, passwords, etc. - */ -import React, { useEffect, useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; - -// Material UI Imports -import Button from '@mui/material/Button'; -import TextField from '@mui/material/TextField'; -import SendIcon from '@mui/icons-material/Send'; - -import { - handlePasswordChange, - confirmPassword, - checkPasswordLength, - checkCurrentPassword, - handleEmailUpdate, - handlePhoneUpdate, - checkPhone -} from '../helper/settingsHelper'; - -const AccountDisplay = () => { - const session = useSelector((state) => state.session); - - return( -
-
-

Account Information

-
-
-

View your account information.

-
- Email -

{session.email}

-
- Username -

{session.username}

-
- Phone -

{session.phone}

-
-
-
-

Update Your Account

-
-
-

1. Update your email address

-
- - - -
- - -

2. Update your phone number

-
-
- - checkPhone(document.getElementById('update-phone-input').value) - } - variant='outlined' - size='small' - /> - -
- - -

3. Use the form below to update your password:

-
-
-

Current Password

-
- checkCurrentPassword()} - size='small' - /> - -
-

New Password

-
- checkPasswordLength()} - size='small' - /> - -
-

Confirm New Password

-
- confirmPassword()} - size='small' - /> - -
-
- - -
-
- ); -}; - -export default AccountDisplay; \ No newline at end of file diff --git a/src/components/display/AccountDisplay.tsx b/src/components/display/AccountDisplay.tsx new file mode 100644 index 00000000..1a1d8742 --- /dev/null +++ b/src/components/display/AccountDisplay.tsx @@ -0,0 +1,164 @@ +/** + * @module AccountDisplay + * @description Account Display for Settings tab, this will host any forms to update account details such as email, passwords, etc. + */ +import React, { useEffect, useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; + +// Material UI Imports +import Button from "@mui/material/Button"; +import TextField from "@mui/material/TextField"; +import SendIcon from "@mui/icons-material/Send"; + +import { + handlePasswordChange, + confirmPassword, + checkPasswordLength, + checkCurrentPassword, + handleEmailUpdate, + handlePhoneUpdate, + checkPhone, +} from "../helper/settingsHelper"; +import { RootState } from "../../renderer/store"; + +const input = document.getElementById( + "update-phone-input" +) as HTMLTextAreaElement | null; + +const AccountDisplay = () => { + const session = useSelector((state: RootState) => state.session); + + return ( +
+
+

Account Information

+
+
+

View your account information.

+
+ Email +

{session.email}

+
+ Username +

{session.username}

+
+ Phone +

{session.phone}

+
+
+
+

Update Your Account

+
+
+

1. Update your email address

+
+
+ + +
+ + +

2. Update your phone number

+
+
+ checkPhone(input?.value)} + variant="outlined" + size="small" + /> + +
+ + +

3. Use the form below to update your password:

+
+
+

Current Password

+
+ checkCurrentPassword()} + size="small" + /> + +
+

New Password

+
+ checkPasswordLength()} + size="small" + /> + +
+

Confirm New Password

+
+ confirmPassword()} + size="small" + /> + +
+
+ + +
+
+ ); +}; + +export default AccountDisplay; From 29a1b4e6b81e17b43b3b35e8ef16c6401b1207dd Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Tue, 29 Nov 2022 18:05:34 -0800 Subject: [PATCH 040/110] package, Container, Settings, TabTypes, and actions --- package.json | 2 +- src/components/tabs/Containers.tsx | 6 +-- src/components/tabs/Settings.js | 9 ++-- src/components/tabs/TabTypes.ts | 78 +++++++++++++++++++++--------- src/redux/actions/actions.ts | 10 ++-- 5 files changed, 69 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 63d6455b..4eed6e6c 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", "@types/react-router-dom": "^5.3.3", - "chart.js": "^3.9.1", "colors": "^1.4.0", "cors": "^2.8.5", "pg": "^8.8.0", @@ -55,6 +54,7 @@ "axios": "^1.0.0", "babel-loader": "^8.2.5", "bcryptjs": "^2.4.3", + "chart.js": "^4.0.1", "concurrently": "^7.4.0", "cross-env": "^7.0.3", "css-loader": "^6.7.1", diff --git a/src/components/tabs/Containers.tsx b/src/components/tabs/Containers.tsx index 5aab52e5..cd9ebaa5 100644 --- a/src/components/tabs/Containers.tsx +++ b/src/components/tabs/Containers.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Chart } from 'react-chartjs-2'; import ToggleDisplay from '../display/ToggleDisplay'; -import { ContainerProps, ContainerType, ChartInfoType} from './TabTypes'; +import { ContainerProps, ContainerType, ChartInfoType } from './TabTypes'; /** * Display all running and stopped containers * @@ -65,7 +65,7 @@ const Containers = (props: ContainerProps) => { datasets: [ { stack, - label: Math.random(), + label: Math.random().toString(), data: [cpuData, memoryData], backgroundColor: ['rgba(44, 130, 201, 1)', 'rgba(19, 221, 29, 1)'], borderColor: 'rgba(0,0,0,0)', @@ -74,7 +74,7 @@ const Containers = (props: ContainerProps) => { }, { stack, - label: Math.random(), + label: Math.random().toString(), data: [(100 - Number(cpuData)).toFixed(2), (100 - Number(memoryData)).toFixed(2)], backgroundColor: ['rgba(155, 198, 233, 1)', 'rgba(217, 252, 219, 1)'], borderColor: 'rgba(0,0,0,0)', diff --git a/src/components/tabs/Settings.js b/src/components/tabs/Settings.js index 1cfc4295..dac51f62 100644 --- a/src/components/tabs/Settings.js +++ b/src/components/tabs/Settings.js @@ -61,6 +61,7 @@ const Settings = (props) => { addStoppedNotificationSetting: PropTypes.func.isRequired, addPhoneNumber: PropTypes.func.isRequired, addNotificationFrequency: PropTypes.func.isRequired, + // the 2 below runningList: PropTypes.array.isRequired, stoppedList: PropTypes.array.isRequired, memoryNotificationList: PropTypes.object.isRequired, @@ -117,7 +118,7 @@ const Settings = (props) => { * @title NOTIFICATION PREFERENCES */ - //updates state as to which boxes are checked + // updates state as to which boxes are checked const fetchNotificationSettings = async () => { fetch('http://localhost:3000/settings/', { method: 'GET', @@ -311,9 +312,9 @@ const Settings = (props) => { const [tempGithubLink, setTempGithubLink] = useState(stateObject); - //check if githubLinks are in the correct format, then save them to the database + // check if githubLinks are in the correct format, then save them to the database const githubLink = (event) => { - const example = "https://api.github.com" + const example = 'https://api.github.com' if (!tempGithubLink[event.target.id] || tempGithubLink[event.target.id].slice(0,22) != example) return alert('Please provide a link in accordance with provided example'); if (!event.target.id) return alert('Please provide a container ID'); @@ -332,7 +333,7 @@ const Settings = (props) => { }) .then((data) => data.json()) .then((response) => { - document.getElementById('gittext').value = '' + document.getElementById('gittext').value = ''; return response; }) .catch((err) => { diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 8f8fa444..415779e8 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -1,39 +1,71 @@ -// gotta do all these functions +import type { ChartData, ChartOptions, ChartDataset } from 'chart.js'; +// Refer to the Settings Tab for more information on stoppedList and runningList +export interface StoppedListType { + Names: string, + ID: string, + Image: string, + RunningFor: string, + Img: string, + Created: string, + name: string, + CPUPerc: string, + MemPerc: string, +} +export interface RunningListType { + block: string, + ID: string, + CPUPerc: string, + MemPerc: string, + MemUsage: string, + Name: string, + NetIO: string, + PIDs: string, + Image: string, + RunningFor: string, +} + +// for more info review actions.ts file and Settings.ts export type ContainerProps = { - stoppedList: any[]; - runStopped: any; - runStoppedContainer: any; - removeContainer: any; - stopRunningContainer: any; - remove: any; - stop: any; - runningList: any[]; + stoppedList: Array; + runStopped: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; + runStoppedContainer: (id: string) => void; + removeContainer: (id: string) => void; + stopRunningContainer: (id: string) => void; + remove: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; + stop: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; + runningList: Array; } - // gotta do all those anys + + // Stopped containers have a Names key and running containers have a Name key export type ContainerType = { - Name: string; - Names: string; - ID: number; - Image: any; - RunningFor: any; - CPUPerc: any; - MemPerc: any; + Name?: string; + Names?: string; + ID: string; + Image: string; + RunningFor: string; + CPUPerc: string; + MemPerc: string; } + // uneeded at this point export type ChartInfoType = { - labels: string[] | number[]; + labels: string[]; datasets: DataSetType[]; - + data?: any } - // just the label function + export type DataSetType = { stack: string; - label: any; - data: string[]; + label: string; + data: ChartDataset<"bar", string[]> | string[]; backgroundColor: string[]; borderColor: string; borderWidth: number; barPercentage: number; } - \ No newline at end of file + +// export interface BarType { +// options: any; +// data: any; +// } diff --git a/src/redux/actions/actions.ts b/src/redux/actions/actions.ts index a61ebbdb..f96bc95b 100644 --- a/src/redux/actions/actions.ts +++ b/src/redux/actions/actions.ts @@ -5,12 +5,12 @@ export const addRunningContainers = (data: object[]) => ({ payload: data }); -export const removeContainer = (id: number) => ({ +export const removeContainer = (id: string) => ({ type: types.REMOVE_CONTAINER, payload: id }); -export const stopRunningContainer = (id: number) => ({ +export const stopRunningContainer = (id: string) => ({ type: types.STOP_RUNNING_CONTAINER, payload: id }); @@ -20,7 +20,7 @@ export const addStoppedContainers = (data: object[]) => ({ payload: data }); -export const runStoppedContainer = (id: number) => ({ +export const runStoppedContainer = (id: string) => ({ type: types.RUN_STOPPED_CONTAINER, payload: id }); @@ -30,12 +30,12 @@ export const getImages = (data: object[]) => ({ payload: data }); -export const runImage = (id: number) => ({ +export const runImage = (id: string) => ({ type: types.RUN_IMAGE, payload: id }); -export const removeImage = (id: number) => ({ +export const removeImage = (id: string) => ({ type: types.REMOVE_IMAGE, payload: id }); From 154c7a3f309e7028fa5932d3612c18248813ee32 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Wed, 30 Nov 2022 10:06:57 -0600 Subject: [PATCH 041/110] completed converting Admin file to TSX Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/helper/commands.js | 2 + src/components/views/{Admin.js => Admin.tsx} | 99 +++++++++++++------- src/components/views/interface.ts | 5 + src/redux/actions/actions.ts | 13 ++- src/renderer/store.ts | 2 +- 5 files changed, 83 insertions(+), 38 deletions(-) rename src/components/views/{Admin.js => Admin.tsx} (75%) create mode 100644 src/components/views/interface.ts diff --git a/src/components/helper/commands.js b/src/components/helper/commands.js index b5c23cf1..31b8edee 100644 --- a/src/components/helper/commands.js +++ b/src/components/helper/commands.js @@ -373,6 +373,7 @@ export const inspectDockerContainer = (containerId) => { */ export const dockerComposeUp = (fileLocation, ymlFileName) => { + console.log(fileLocation, ymlFileName); return new Promise((resolve, reject) => { const nativeYmlFilenames = [ 'docker-compose.yml', @@ -382,6 +383,7 @@ export const dockerComposeUp = (fileLocation, ymlFileName) => { ]; let cmd = `cd ${fileLocation} && docker compose up -d`; // if ymlFilename is not a native yml/yaml file name, add -f flag and non-native filename + if (!nativeYmlFilenames.includes(ymlFileName)) { cmd = `cd ${fileLocation} && docker compose -f ${ymlFileName} up -d`; } diff --git a/src/components/views/Admin.js b/src/components/views/Admin.tsx similarity index 75% rename from src/components/views/Admin.js rename to src/components/views/Admin.tsx index 1da16dbc..27065778 100644 --- a/src/components/views/Admin.js +++ b/src/components/views/Admin.tsx @@ -7,6 +7,7 @@ import { Routes, Route, Link, useNavigate } from 'react-router-dom'; import * as actions from '../../redux/actions/actions'; import * as helper from '../helper/commands'; import * as history from '../helper/volumeHistoryHelper'; +// @ts-ignore import Docketeer from '../../../assets/docketeer-title.png'; // tab component imports @@ -28,58 +29,87 @@ import initDatabase from '../helper/initDatabase'; const AdminView = () => { const navigate = useNavigate(); const dispatch = useDispatch(); - const addRunningContainers = (data) => + const addRunningContainers = (data: object[]) => dispatch(actions.addRunningContainers(data)); - const refreshRunningContainers = (data) => + const refreshRunningContainers = (data: object[]) => dispatch(actions.refreshRunningContainers(data)); - const refreshStoppedContainers = (data) => + const refreshStoppedContainers = (data: object[]) => dispatch(actions.refreshStoppedContainers(data)); - const refreshImagesList = (data) => dispatch(actions.refreshImages(data)); - const composeymlFiles = (data) => dispatch(actions.composeymlFiles(data)); - const getNetworkContainers = (data) => + const refreshImagesList = (data: object[]) => dispatch(actions.refreshImages(data)); + // const composeymlFiles = (data) => dispatch(actions.composeymlFiles(data)); + const getNetworkContainers = (data: object[]) => dispatch(actions.getNetworkContainers(data)); - const removeContainer = (id) => dispatch(actions.removeContainer(id)); - const runStoppedContainer = (data) => - dispatch(actions.runStoppedContainer(data)); - const stopRunningContainer = (id) => + const removeContainer = (id: number) => dispatch(actions.removeContainer(id)); + // this parameter was changed from data: object[] because in the actions file, an id argument was being requested + const runStoppedContainer = (id: number) => + dispatch(actions.runStoppedContainer(id)); + const stopRunningContainer = (id: number) => dispatch(actions.stopRunningContainer(id)); const updateSession = () => dispatch(actions.updateSession()); + // originally, this function have any parameters, but typescript through an error saying it was needed. Check this out later const logoutUser = () => dispatch(actions.logoutUser()); - const getVolumeList = (data) => dispatch(actions.getVolumeList(data)); - const getVolumeContainersList = (data) => - dispatch(actions.getVolumeContainersList(data)); + const getVolumeList = (data: object[]) => dispatch(actions.getVolumeList(data)); + const getVolumeContainersList = (data: object[]) => dispatch(actions.getVolumeContainersList(data)); + + interface containersList { + runningList: object[], + stoppedList: object[] + } + + interface imagesList { + imagesList: any[] + } + + interface volumeList { + arrayOfVolumeNames: any[] + volumeContainersList: any[] + } + + interface notificationList { + phoneNumber: any[], + memoryNotificationList: any[], + cpuNotificationList: any[], + stoppedNotificationList: any[], + } + + interface stateType { + containersList: containersList, + images: imagesList, + volumeList: volumeList, + notificationList: notificationList + }; // map state to props - const runningList = useSelector((state) => state.containersList.runningList); - const stoppedList = useSelector((state) => state.containersList.stoppedList); - const imagesList = useSelector((state) => state.images.imagesList); - const networkList = useSelector((state) => state.networkList.networkList); + const runningList = useSelector((state: stateType) => state.containersList.runningList); + const stoppedList = useSelector((state: stateType) => state.containersList.stoppedList); + const imagesList = useSelector((state: stateType) => state.images.imagesList); + // const networkList = useSelector((state: stateType) => state.networkList.networkList); const arrayOfVolumeNames = useSelector( - (state) => state.volumeList.arrayOfVolumeNames + (state: stateType) => state.volumeList.arrayOfVolumeNames ); const volumeContainersList = useSelector( - (state) => state.volumeList.volumeContainersList + (state: stateType) => state.volumeList.volumeContainersList ); // map state to props const phoneNumber = useSelector( - (state) => state.notificationList.phoneNumber + (state: stateType) => state.notificationList.phoneNumber ); const memoryNotificationList = useSelector( - (state) => state.notificationList.memoryNotificationList + (state: stateType) => state.notificationList.memoryNotificationList ); const cpuNotificationList = useSelector( - (state) => state.notificationList.cpuNotificationList + (state: stateType) => state.notificationList.cpuNotificationList ); const stoppedNotificationList = useSelector( - (state) => state.notificationList.stoppedNotificationList + (state: stateType) => state.notificationList.stoppedNotificationList ); // declare a local state variable called selected, initialize to '/' const [selected, setSelected] = useState('/'); - const handleLogout = (e) => { + const handleLogout = () => { updateSession(); logoutUser(); navigate('/login'); @@ -135,7 +165,7 @@ const AdminView = () => { setSelected('/app/users/')} > @@ -148,7 +178,7 @@ const AdminView = () => { style={ selected === '/app/running' ? selectedStyling - : null + : undefined } onClick={() => setSelected(() => '/app/running')} > @@ -161,7 +191,7 @@ const AdminView = () => { style={ selected === '/app/images' ? selectedStyling - : null + : undefined } onClick={() => setSelected('/app/images')} > @@ -174,7 +204,7 @@ const AdminView = () => { style={ selected === '/app/metrics' ? selectedStyling - : null + : undefined } onClick={() => setSelected('/app/metrics')} > @@ -185,7 +215,7 @@ const AdminView = () => { setSelected('/app/yml')} > @@ -198,7 +228,7 @@ const AdminView = () => { style={ selected === '/app/volume' ? selectedStyling - : null + : undefined } onClick={() => setSelected('/app/volume')} > @@ -209,7 +239,7 @@ const AdminView = () => { setSelected('/app/logs')} > @@ -226,7 +256,7 @@ const AdminView = () => { System Prune -
@@ -271,8 +301,9 @@ const AdminView = () => { path='/yml' element={ } /> diff --git a/src/components/views/interface.ts b/src/components/views/interface.ts new file mode 100644 index 00000000..15604925 --- /dev/null +++ b/src/components/views/interface.ts @@ -0,0 +1,5 @@ +// this file contains the interfaces for each file. Please search a fil by name in order to find what interfaces it uses. + +interface Data { + +} \ No newline at end of file diff --git a/src/redux/actions/actions.ts b/src/redux/actions/actions.ts index a61ebbdb..72441eee 100644 --- a/src/redux/actions/actions.ts +++ b/src/redux/actions/actions.ts @@ -55,7 +55,14 @@ export const refreshImages = (data: object[]) => ({ payload: data }); -export const composeymlFiles = (data: object[]) => ({ +/* --------------- */ +type composeymlFilesStr = { + type: string, + payload: object[] +} +/* --------------- */ + +export const composeymlFiles = (data: composeymlFilesStr) => ({ type: types.COMPOSE_YML_FILES, payload: data }); @@ -159,9 +166,9 @@ export const updateUser = (data: object) => ({ payload: data }); -export const logoutUser = (data: object[]) => ({ +export const logoutUser = () => ({ type: types.LOGOUT_USER, - payload: data + // payload: data }); export const updateUserList = (data: object[]) => ({ diff --git a/src/renderer/store.ts b/src/renderer/store.ts index 95c20071..462e9f98 100644 --- a/src/renderer/store.ts +++ b/src/renderer/store.ts @@ -1,6 +1,6 @@ import { configureStore } from '@reduxjs/toolkit' import reducers from '../redux/reducers/index'; -import { composeWithDevTools } from '@redux-devtools/extension'; +// import { composeWithDevTools } from '@redux-devtools/extension'; const store = configureStore({ reducer: reducers, From be27cfd90a09204b8c71aef0e9ab623eee5b9873 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Wed, 30 Nov 2022 10:17:15 -0600 Subject: [PATCH 042/110] completed converting Admin file to TSX Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/views/Admin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/Admin.tsx b/src/components/views/Admin.tsx index 27065778..9f671181 100644 --- a/src/components/views/Admin.tsx +++ b/src/components/views/Admin.tsx @@ -360,5 +360,5 @@ const AdminView = () => {
); }; - +//adding comment to commit export default AdminView; From 0d63692af2df58b37ffb16445970ce93ba2d4977 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Wed, 30 Nov 2022 08:42:31 -0800 Subject: [PATCH 043/110] alterations to tabtypes --- src/components/tabs/TabTypes.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 415779e8..62fba817 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -1,3 +1,4 @@ +// not sure if I need this import type { ChartData, ChartOptions, ChartDataset } from 'chart.js'; // Refer to the Settings Tab for more information on stoppedList and runningList export interface StoppedListType { @@ -58,7 +59,7 @@ export type ContainerProps = { export type DataSetType = { stack: string; label: string; - data: ChartDataset<"bar", string[]> | string[]; + data: string[]; backgroundColor: string[]; borderColor: string; borderWidth: number; From a6a1ccbf6426373a26bf048efe3b98182de7b7c4 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Wed, 30 Nov 2022 10:56:12 -0600 Subject: [PATCH 044/110] touching actions and linechart file --- ...neChartDisplay.js => LineChartDisplay.tsx} | 348 ++++++++++-------- src/redux/actions/actions.ts | 88 ++--- 2 files changed, 233 insertions(+), 203 deletions(-) rename src/components/display/{LineChartDisplay.js => LineChartDisplay.tsx} (54%) diff --git a/src/components/display/LineChartDisplay.js b/src/components/display/LineChartDisplay.tsx similarity index 54% rename from src/components/display/LineChartDisplay.js rename to src/components/display/LineChartDisplay.tsx index 689efddf..b2a7b3b1 100644 --- a/src/components/display/LineChartDisplay.js +++ b/src/components/display/LineChartDisplay.tsx @@ -1,20 +1,21 @@ /* eslint-disable react/prop-types */ -import React, { useState, useEffect } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { Line, Bar } from 'react-chartjs-2'; -import * as actions from '../../redux/actions/actions'; -import * as helper from '../helper/commands'; -import { DataGrid } from '@mui/x-data-grid'; -import { FormControlLabel, Checkbox } from '@mui/material'; +import React, { useState, useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { Line, Bar } from "react-chartjs-2"; +import * as actions from "../../redux/actions/actions"; +import * as helper from "../helper/commands"; +import { DataGrid } from "@mui/x-data-grid"; +import { FormControlLabel, Checkbox } from "@mui/material"; /** * Displays linegraph and github metrics * */ + const LineChartDisplay = () => { const [activeContainers, setActiveContainers] = useState({}); const [gitUrls, setGitUrls] = useState([]); - const [timePeriod, setTimePeriod] = useState('4'); + const [timePeriod, setTimePeriod] = useState("4"); const memory = useSelector((state) => state.graphs.graphMemory); const cpu = useSelector((state) => state.graphs.graphCpu); const writtenIO = useSelector((state) => state.graphs.graphWrittenIO); @@ -24,25 +25,26 @@ const LineChartDisplay = () => { const stoppedList = useSelector((state) => state.containersList.stoppedList); const dispatch = useDispatch(); - const buildAxis = (data) => dispatch(actions.buildAxis(data)); - const buildMemory = (data) => dispatch(actions.buildMemory(data)); - const buildCpu = (data) => dispatch(actions.buildCpu(data)); - const buildWrittenIO = (data) => dispatch(actions.buildWrittenIO(data)); - const buildReadIO = (data) => dispatch(actions.buildReadIO(data)); + const buildAxis = (data: string) => dispatch(actions.buildAxis(data)); + const buildMemory = (data: string) => dispatch(actions.buildMemory(data)); + const buildCpu = (data: string) => dispatch(actions.buildCpu(data)); + const buildWrittenIO = (data: string) => + dispatch(actions.buildWrittenIO(data)); + const buildReadIO = (data: string) => dispatch(actions.buildReadIO(data)); //Grabbing the metrics data to be displayed on the charts async function getContainerMetrics() { const containerNamesArr = Object.keys(activeContainers); - const response = await fetch('http://localhost:3000/init/getMetrics', { - method: 'POST', + const response = await fetch("http://localhost:3000/init/getMetrics", { + method: "POST", headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json", }, body: JSON.stringify({ - containers: containerNamesArr - }) - }) - return await response.json(); + containers: containerNamesArr, + }), + }); + return await response.json(); } // Auxilary Object which will be passed into Line component @@ -52,15 +54,15 @@ const LineChartDisplay = () => { }; const cpuObj = { labels: axis, - datasets: cpu + datasets: cpu, }; const writtenIOObj = { labels: axis, - datasets: writtenIO + datasets: writtenIO, }; const readIOObj = { labels: axis, - datasets: readIO + datasets: readIO, }; /** @@ -68,11 +70,11 @@ const LineChartDisplay = () => { * Builds memory and cpu object for input into Line Components */ const formatData = async () => { - buildMemory('clear'); - buildCpu('clear'); - buildAxis('clear'); - buildWrittenIO('clear'); - buildReadIO('clear'); + buildMemory("clear"); + buildCpu("clear"); + buildAxis("clear"); + buildWrittenIO("clear"); + buildReadIO("clear"); // if active containers is empty render the empty graphs if (!Object.keys(activeContainers).length) { return; @@ -80,36 +82,39 @@ const LineChartDisplay = () => { const output = await getContainerMetrics(); - const generateLineColor = (containerName, activeContainers) => { + const generateLineColor = ( + containerName: string[], + activeContainers: {} + ) => { const colorOptions = [ - 'red', - 'blue', - 'green', - 'purple', - 'yellow', - 'grey', - 'orange' + "red", + "blue", + "green", + "purple", + "yellow", + "grey", + "orange", ]; const idx = activeContainers.indexOf(containerName); return colorOptions[idx]; }; // build function that will return formated object into necessary // datastructure for chart.js line graphs - const buildLineGraphObj = (containerName) => { + const buildLineGraphObj = (containerName: string) => { const obj = { label: containerName, data: [], - lineTension: .5, + lineTension: 0.5, fill: false, borderColor: generateLineColor( containerName, Object.keys(activeContainers) - ) + ), }; return obj; }; // Datastructure for Bargraph - const buildBarGraphObj = (containerName) => { + const buildBarGraphObj = (containerName: string) => { const obj = { label: containerName, data: [], @@ -117,23 +122,23 @@ const LineChartDisplay = () => { backgroundColor: generateLineColor( containerName, Object.keys(activeContainers) - ) + ), }; return obj; }; - buildMemory('clear'); - buildCpu('clear'); - buildAxis('clear'); - buildWrittenIO('clear'); - buildReadIO('clear'); + buildMemory("clear"); + buildCpu("clear"); + buildAxis("clear"); + buildWrittenIO("clear"); + buildReadIO("clear"); if (!Object.keys(activeContainers).length) { return; } const containerMetrics = await getContainerMetrics(); - + const auxObj = {}; Object.keys(activeContainers).forEach((container) => { @@ -141,41 +146,39 @@ const LineChartDisplay = () => { memory: buildLineGraphObj(container), cpu: buildLineGraphObj(container), writtenIO: buildBarGraphObj(container), - readIO: buildBarGraphObj(container) + readIO: buildBarGraphObj(container), }; }); // iterate through each row from fetch and build Memory, CPU, Written/Read Block_IO objects [{}, {}, {}, {}] containerMetrics.rows.forEach((dataPoint) => { const currentContainer = dataPoint.container_name; - const writtenReadIO = dataPoint.block_io.split('/'); + const writtenReadIO = dataPoint.block_io.split("/"); auxObj[currentContainer].cpu.data.push( - dataPoint.cpu_pct.replace('%', '') + dataPoint.cpu_pct.replace("%", "") ); auxObj[currentContainer].memory.data.push( - dataPoint.memory_pct.replace('%', '') + dataPoint.memory_pct.replace("%", "") ); auxObj[currentContainer].writtenIO.data.push( - parseFloat(writtenReadIO[0].replace(/([A-z])+/g, '')) + parseFloat(writtenReadIO[0].replace(/([A-z])+/g, "")) ); auxObj[currentContainer].readIO.data.push( - parseFloat(writtenReadIO[1].replace(/([A-z])+/g, '')) + parseFloat(writtenReadIO[1].replace(/([A-z])+/g, "")) ); let date = ""; let time = ""; - for (let i = 1; i < dataPoint.created_at.length; i++){ - if (dataPoint.created_at[i] === 'T') { - break - } - else (date += dataPoint.created_at[i]); + for (let i = 1; i < dataPoint.created_at.length; i++) { + if (dataPoint.created_at[i] === "T") { + break; + } else date += dataPoint.created_at[i]; } - for (let i = 11; i < dataPoint.created_at.length; i++){ - if (dataPoint.created_at[i] === '.') { - break - } - else (time += dataPoint.created_at[i]); + for (let i = 11; i < dataPoint.created_at.length; i++) { + if (dataPoint.created_at[i] === ".") { + break; + } else time += dataPoint.created_at[i]; } - let timeStamp = `${date} @ ${time}` + let timeStamp = `${date} @ ${time}`; buildAxis(timeStamp); }); @@ -192,10 +195,10 @@ const LineChartDisplay = () => { if (auxObj[containerName].memory.data.length < longest) { const lengthToAdd = longest - auxObj[containerName].memory.data.length; for (let i = 0; i < lengthToAdd; i += 1) { - auxObj[containerName].memory.data.unshift('0.00'); - auxObj[containerName].cpu.data.unshift('0.00'); - auxObj[containerName].writtenIO.data.unshift('0.00'); - auxObj[containerName].readIO.data.unshift('0.00'); + auxObj[containerName].memory.data.unshift("0.00"); + auxObj[containerName].cpu.data.unshift("0.00"); + auxObj[containerName].writtenIO.data.unshift("0.00"); + auxObj[containerName].readIO.data.unshift("0.00"); } } buildMemory([auxObj[containerName].memory]); @@ -206,12 +209,12 @@ const LineChartDisplay = () => { }; //Fetching the data from github API and turning it into an object with keys of objects that contain the data of each container - const fetchGitData = async (containerName) => { + const fetchGitData = async (containerName: string) => { const ob = {}; ob[containerName] = []; const time = Number(timePeriod); //pulling the current time, and then setting it back to one month ago to check for github commit logs (2629746000 = 1 month) - let date = new Date(Date.parse(new Date()) - 2629746000) + let date = new Date(Date.parse(new Date()) - 2629746000); date.setHours(date.getHours() - time); date = date.toISOString(); const urlObj = await helper.getContainerGitUrl(containerName); @@ -220,9 +223,9 @@ const LineChartDisplay = () => { const url = urlObj.rows[0].github_url + new URLSearchParams({ - since: `${date}` + since: `${date}`, }); - //need an actual url to test this, right now it can't connect + //need an actual url to test this, right now it can't connect const data = await fetch(url); const jsonData = await data.json(); @@ -231,13 +234,13 @@ const LineChartDisplay = () => { time: commitData.commit.author.date, url: commitData.html_url, author: commitData.commit.author.name, - message: commitData.commit.message + message: commitData.commit.message, }); }); } else { ob[containerName].push({ - time: '', - url: 'Connect github repo in settings' + time: "", + url: "Connect github repo in settings", }); } return ob; @@ -251,45 +254,57 @@ const LineChartDisplay = () => { ).then((data) => setGitUrls(data)); }; //populating the github commits into a MUI DataGrid - //This should allow multiple tables be stacked if multiple containers are selected + //This should allow multiple tables be stacked if multiple containers are selected let gitData; const columns = [ - {field: 'date', headerName: 'Date', width: 125 }, - {field: 'time', headerName: 'Time', width: 100 }, - {field: 'url', headerName: 'URL', width: 175, renderCell: (params) => {params.row.id} }, - {field: 'author', headerName: 'Author', width: 175 }, - {field: 'message', headerName: 'Message', width: 525, align: 'left' }, - ] + { field: "date", headerName: "Date", width: 125 }, + { field: "time", headerName: "Time", width: 100 }, + { + field: "url", + headerName: "URL", + width: 175, + renderCell: (params) => ( + + {params.row.id} + + ), + }, + { field: "author", headerName: "Author", width: 175 }, + { field: "message", headerName: "Message", width: 525, align: "left" }, + ]; gitData = gitUrls.map((el, index) => { const name = Object.keys(el); const rows = []; el[name].forEach((ob, index) => { - let author = ''; - let date = 'n/a'; - let time = 'n/a'; - let url = 'n/a'; - let message = 'n/a'; + let author = ""; + let date = "n/a"; + let time = "n/a"; + let url = "n/a"; + let message = "n/a"; if (ob.time.length) { time = ob.time; author = ob.author; url = ob.url; - message = ''; - if (ob.message){ - if (ob.message.includes('<')){ + message = ""; + if (ob.message) { + if (ob.message.includes("<")) { for (let i = 0; i < ob.message.length; i++) { - if (ob.message[i] === '<') break + if (ob.message[i] === "<") break; message += ob.message[i]; } } else { - message = ob.message + message = ob.message; } } - time = time.split('T'); + time = time.split("T"); date = time[0]; time = time[1]; - time = time.split('').slice(0, time.length - 1).join(''); + time = time + .split("") + .slice(0, time.length - 1) + .join(""); } rows.push({ date: date, @@ -297,23 +312,23 @@ const LineChartDisplay = () => { url: url, author: author, message: message, - id: `Github Commit #${index}` + id: `Github Commit #${index}`, }); }); return ( -
+

{name}

-
+
'auto'} - initialState={{ - sorting: { - sortModel: [{field: 'date', sort: 'asc'}] - } - }} + key="DataGrid" + rows={rows} + columns={columns} + getRowHeight={() => "auto"} + initialState={{ + sorting: { + sortModel: [{ field: "date", sort: "asc" }], + }, + }} />
@@ -335,8 +350,8 @@ const LineChartDisplay = () => { } label={containerNameKey} @@ -347,7 +362,7 @@ const LineChartDisplay = () => { }; const handleChange = (e) => { - if (e.target.type === 'radio') { + if (e.target.type === "radio") { setTimePeriod(e.target.value); return; } @@ -363,42 +378,62 @@ const LineChartDisplay = () => { }; const cpuOptions = { - plugins:{ - title: { display: true, text: 'CPU', font: {size: 18}, position: 'top' }, - tooltips: {enabled: true, mode: 'index'}, - legend: { display: true, position: 'bottom' } + plugins: { + title: { + display: true, + text: "CPU", + font: { size: 18 }, + position: "top", + }, + tooltips: { enabled: true, mode: "index" }, + legend: { display: true, position: "bottom" }, }, responsive: true, - maintainAspectRatio: false + maintainAspectRatio: false, }; const memoryOptions = { - plugins:{ - title: { display: true, text: 'MEMORY', font: {size: 18}, position: 'top' }, - tooltips: {enabled: true, mode: 'index'}, - legend: { display: true, position: 'bottom' } + plugins: { + title: { + display: true, + text: "MEMORY", + font: { size: 18 }, + position: "top", + }, + tooltips: { enabled: true, mode: "index" }, + legend: { display: true, position: "bottom" }, }, responsive: true, - maintainAspectRatio: false + maintainAspectRatio: false, }; const writtenIOOptions = { - plugins:{ - title: { display: true, text: 'IO BYTES WRITTEN BY IMAGE', font: {size: 18}, position: 'top' }, - tooltips: {enabled: true, mode: 'index'}, - legend: { display: true, position: 'bottom' } + plugins: { + title: { + display: true, + text: "IO BYTES WRITTEN BY IMAGE", + font: { size: 18 }, + position: "top", + }, + tooltips: { enabled: true, mode: "index" }, + legend: { display: true, position: "bottom" }, }, responsive: true, - maintainAspectRatio: false + maintainAspectRatio: false, }; const readIOOptions = { - plugins:{ - title: { display: true, text: 'IO BYTES READ BY IMAGE', font: {size: 18}, position: 'top' }, - tooltips: {enabled: true, mode: 'index'}, - legend: { display: true, position: 'bottom' } + plugins: { + title: { + display: true, + text: "IO BYTES READ BY IMAGE", + font: { size: 18 }, + position: "top", + }, + tooltips: { enabled: true, mode: "index" }, + legend: { display: true, position: "bottom" }, }, responsive: true, - maintainAspectRatio: false + maintainAspectRatio: false, }; selectList(); @@ -409,59 +444,54 @@ const LineChartDisplay = () => { return (
-
+

Over Time

-
+
{ handleChange(e); }} > - + - - - + + +
- {currentList} + {currentList}
-
- +
+
-
- +
+
-
- +
+
-
- +
+
-
+

GitHub History

- {gitData} + {gitData}
); }; diff --git a/src/redux/actions/actions.ts b/src/redux/actions/actions.ts index a61ebbdb..b17f0dc6 100644 --- a/src/redux/actions/actions.ts +++ b/src/redux/actions/actions.ts @@ -1,193 +1,193 @@ -import * as types from '../constants/actionTypes'; +import * as types from "../constants/actionTypes"; export const addRunningContainers = (data: object[]) => ({ type: types.ADD_RUNNING_CONTAINERS, - payload: data + payload: data, }); export const removeContainer = (id: number) => ({ type: types.REMOVE_CONTAINER, - payload: id + payload: id, }); export const stopRunningContainer = (id: number) => ({ type: types.STOP_RUNNING_CONTAINER, - payload: id + payload: id, }); export const addStoppedContainers = (data: object[]) => ({ type: types.ADD_STOPPED_CONTAINERS, - payload: data + payload: data, }); export const runStoppedContainer = (id: number) => ({ type: types.RUN_STOPPED_CONTAINER, - payload: id + payload: id, }); export const getImages = (data: object[]) => ({ type: types.GET_IMAGES, - payload: data + payload: data, }); export const runImage = (id: number) => ({ type: types.RUN_IMAGE, - payload: id + payload: id, }); export const removeImage = (id: number) => ({ type: types.REMOVE_IMAGE, - payload: id + payload: id, }); export const refreshRunningContainers = (data: object[]) => ({ type: types.REFRESH_RUNNING_CONTAINERS, - payload: data + payload: data, }); export const refreshStoppedContainers = (data: object[]) => ({ type: types.REFRESH_STOPPED_CONTAINERS, - payload: data + payload: data, }); export const refreshImages = (data: object[]) => ({ type: types.REFRESH_IMAGES, - payload: data + payload: data, }); export const composeymlFiles = (data: object[]) => ({ type: types.COMPOSE_YML_FILES, - payload: data + payload: data, }); export const getNetworkContainers = (data: object[]) => ({ type: types.GET_NETWORK_CONTAINERS, - payload: data + payload: data, }); export const getContainerStacks = (data: object[]) => ({ type: types.GET_CONTAINER_STACKS, - payload: data + payload: data, }); export const composeDown = (data: object[]) => ({ type: types.COMPOSE_DOWN, - payload: data + payload: data, }); export const composeUp = (data: object[]) => ({ type: types.COMPOSE_UP, - payload: data + payload: data, }); -export const buildAxis = (data: object[]) => ({ +export const buildAxis = (data: string) => ({ type: types.BUILD_AXIS, - payload: data + payload: data, }); -export const buildMemory = (data: object[]) => ({ +export const buildMemory = (data: string) => ({ type: types.BUILD_MEMORY, - payload: data + payload: data, }); -export const buildCpu = (data: object[]) => ({ +export const buildCpu = (data: string) => ({ type: types.BUILD_CPU, - payload: data + payload: data, }); -export const buildWrittenIO = (data: object[]) => ({ +export const buildWrittenIO = (data: string) => ({ type: types.BUILD_WRITTEN_IO, - payload: data + payload: data, }); -export const buildReadIO = (data: object[]) => ({ +export const buildReadIO = (data: string) => ({ type: types.BUILD_READ_IO, - payload: data + payload: data, }); export const addPhoneNumber = (data: object[]) => ({ type: types.ADD_PHONE_NUMBER, - payload: data + payload: data, }); export const addMemoryNotificationSetting = (data: object[]) => ({ type: types.ADD_MEMORY_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const addCpuNotificationSetting = (data: object[]) => ({ type: types.ADD_CPU_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const addStoppedNotificationSetting = (data: object[]) => ({ type: types.ADD_STOPPED_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const removeMemoryNotificationSetting = (data: object[]) => ({ type: types.REMOVE_MEMORY_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const removeCpuNotificationSetting = (data: object[]) => ({ type: types.REMOVE_CPU_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const removeStoppedNotificationSetting = (data: object[]) => ({ type: types.REMOVE_STOPPED_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const addNotificationFrequency = (data: object[]) => ({ type: types.NOTIFICATION_FREQUENCY, - payload: data + payload: data, }); export const addMonitoringFrequency = (data: object[]) => ({ type: types.MONITORING_FREQUENCY, - payload: data + payload: data, }); export const updateSession = () => ({ - type: types.UPDATE_SESSION + type: types.UPDATE_SESSION, }); export const updateUser = (data: object) => ({ type: types.UPDATE_USER, - payload: data + payload: data, }); export const logoutUser = (data: object[]) => ({ type: types.LOGOUT_USER, - payload: data + payload: data, }); export const updateUserList = (data: object[]) => ({ type: types.UPDATE_USER_LIST, - payload: data + payload: data, }); export const updateUserRole = (data: object[]) => ({ type: types.UPDATE_USER_ROLE, - payload: data + payload: data, }); // get volume export const getVolumeList = (data: object[]) => ({ type: types.GET_VOLUME_LIST, - payload: data + payload: data, }); // get containers that live in volume export const getVolumeContainersList = (data: object[]) => ({ type: types.GET_VOLUME_CONTAINERS_LIST, - payload: data + payload: data, }); // get container logs export const getContainerLogs = (data: object[]) => ({ type: types.GET_CONTAINER_LOGS, - payload: data + payload: data, }); From df550b82d12314caa57faaecd0fd0b9c0b5d1805 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Wed, 30 Nov 2022 09:49:48 -0800 Subject: [PATCH 045/110] Containers Tab to TSX, Add Tab Types, etc changes Co-authored-by: Tiffany Chau Co-authored-by: Sarah Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- package.json | 2 +- src/components/tabs/Containers.tsx | 6 ------ src/components/tabs/Settings.js | 4 ++-- src/components/tabs/TabTypes.ts | 3 +-- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 4eed6e6c..782ebcea 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "axios": "^1.0.0", "babel-loader": "^8.2.5", "bcryptjs": "^2.4.3", - "chart.js": "^4.0.1", + "chart.js": "^3.9.1", "concurrently": "^7.4.0", "cross-env": "^7.0.3", "css-loader": "^6.7.1", diff --git a/src/components/tabs/Containers.tsx b/src/components/tabs/Containers.tsx index cd9ebaa5..ba037786 100644 --- a/src/components/tabs/Containers.tsx +++ b/src/components/tabs/Containers.tsx @@ -115,14 +115,8 @@ const Containers = (props: ContainerProps) => { plugins: { legend: { display: false } }, scales: { y: { - ticks: { - min: 0, - max: 100, - stepSize: 50 - }, stacked: true }, - x: { categorySpacing: 0 } } }} /> diff --git a/src/components/tabs/Settings.js b/src/components/tabs/Settings.js index dac51f62..c571f871 100644 --- a/src/components/tabs/Settings.js +++ b/src/components/tabs/Settings.js @@ -134,7 +134,7 @@ const Settings = (props) => { }); }; -/** + /** * @title COMMUNICATION */ @@ -314,7 +314,7 @@ const Settings = (props) => { const [tempGithubLink, setTempGithubLink] = useState(stateObject); // check if githubLinks are in the correct format, then save them to the database const githubLink = (event) => { - const example = 'https://api.github.com' + const example = 'https://api.github.com'; if (!tempGithubLink[event.target.id] || tempGithubLink[event.target.id].slice(0,22) != example) return alert('Please provide a link in accordance with provided example'); if (!event.target.id) return alert('Please provide a container ID'); diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 62fba817..255dd995 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -1,5 +1,4 @@ -// not sure if I need this -import type { ChartData, ChartOptions, ChartDataset } from 'chart.js'; + // Refer to the Settings Tab for more information on stoppedList and runningList export interface StoppedListType { Names: string, From 6c3b703b4992f3c15b41b7786cadf85f87e96f72 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 30 Nov 2022 14:40:54 -0500 Subject: [PATCH 046/110] fix webpack/ add tests Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- __tests__/ListReducer.test.js | 2 +- __tests__/MetricsTab.test.js | 36 +++++++++++--- __tests__/ProcessLogHelper.test.js | 4 ++ __tests__/loginPage.js | 58 ++++++++++++++--------- package.json | 5 +- src/components/helper/processLogHelper.js | 2 +- src/components/tabs/Containers.js | 4 +- src/components/tabs/ProcessLogs.js | 16 +++---- src/renderer/store.ts | 1 - webpack.electron.config.js | 4 +- webpack.react.config.js | 10 ++-- 11 files changed, 90 insertions(+), 52 deletions(-) diff --git a/__tests__/ListReducer.test.js b/__tests__/ListReducer.test.js index 2640cabb..26f4e067 100644 --- a/__tests__/ListReducer.test.js +++ b/__tests__/ListReducer.test.js @@ -118,7 +118,7 @@ describe('Dockeeter reducer', () => { const action = { type: 'RUN_STOPPED_CONTAINER', payload: '123' }; console.log(newState); expect(containerListReducer(newState, action).stoppedList[0].ID).toEqual( - '45fsdf6' + '456' ); }); }); diff --git a/__tests__/MetricsTab.test.js b/__tests__/MetricsTab.test.js index 706c0538..c878f208 100644 --- a/__tests__/MetricsTab.test.js +++ b/__tests__/MetricsTab.test.js @@ -3,13 +3,34 @@ */ -// import React from 'react'; -// import { configure, shallow } from 'enzyme'; -// import Adapter from 'enzyme-adapter-react-16'; -// import Metrics from '../src/components/tabs/Metrics'; +import React, { Component } from 'react'; +import Metrics from '../src/components/tabs/Metrics'; +import {describe, expect, test, jest} from '@jest/globals'; +import '@testing-library/jest-dom'; +import { Provider } from 'react-redux'; +import store from '../src/renderer/store'; +import { create } from 'react-test-renderer'; +import { fireEvent, render, screen } from '@testing-library/react'; + +const props = { + runningList: [{ BlockIO: "1B/2B", ID: "6f49565a501c", CPUPerc: "20.00%", MemPerc: "0.00%", MemUsage: "5B/6B", Name: "checkpoint_nginx_1", NetIO: "3B/4B", PIDs: "0" }, { BlockIO: "3B/4B", ID: "6f49565a501c", CPUPerc: "30.00%", MemPerc: "20.00%", MemUsage: "5B/6B", Name: "checkpoint_nginx_2", NetIO: "5B/6B", PIDs: "0" }] +} + +describe('Metrics tab should render', () => { + beforeEach(()=>{ + render( + + + + ) + }); + + test('Metrics', ()=>{ + expect(1).toBe(1) + }) +}) -// // Newer Enzyme versions require an adapter to a particular version of React -// configure({ adapter: new Adapter() }); +/* // function shallowSetup() { @@ -17,7 +38,6 @@ // const props = { // runningList: [{ BlockIO: "1B/2B", ID: "6f49565a501c", CPUPerc: "20.00%", MemPerc: "0.00%", MemUsage: "5B/6B", Name: "checkpoint_nginx_1", NetIO: "3B/4B", PIDs: "0" }, { BlockIO: "3B/4B", ID: "6f49565a501c", CPUPerc: "30.00%", MemPerc: "20.00%", MemUsage: "5B/6B", Name: "checkpoint_nginx_2", NetIO: "5B/6B", PIDs: "0" }] // } - // const enzymeWrapper = shallow(); @@ -82,6 +102,8 @@ // }); // }); +*/ + //* Dummy Test describe('dummy test', () => { test('dummy test', () => { diff --git a/__tests__/ProcessLogHelper.test.js b/__tests__/ProcessLogHelper.test.js index ae122341..b6edd22f 100644 --- a/__tests__/ProcessLogHelper.test.js +++ b/__tests__/ProcessLogHelper.test.js @@ -16,7 +16,9 @@ describe('makeArrayOfObjects', () => { `; const result = makeArrayOfObjects(string); + console.log(result); expect(result).toBeInstanceOf(Array); + expect(result.containerName).toBe(undefined); }); it('each element in returned array is of type object', () => { @@ -31,6 +33,8 @@ describe('makeArrayOfObjects', () => { expect(output).toEqual(true); }); + + // //We edited the makeArrayOfObjects function and now this fails, not sure why as there still is a key of logMsg and timeStamp // it('each object in returned array has timeStamp and logMsg properties', () => { // const processLog = diff --git a/__tests__/loginPage.js b/__tests__/loginPage.js index 19a6cf7e..d0b7d85a 100644 --- a/__tests__/loginPage.js +++ b/__tests__/loginPage.js @@ -5,50 +5,64 @@ import { useNavigate } from 'react-router-dom'; // import { useSelector, useDispatch } from 'react-redux'; import {render, fireEvent, screen, getAllByRole} from '@testing-library/react'; // trying to import ts files is giving issues for time being, probably related to compiling -// import App from '../src/renderer/App'; +import App from '../src/renderer/App'; import Login from '../src/components/login/login'; // import AccountDisplay from '../src/components/display/AccountDisplay'; -import {BrowserRouter, Routes, Route} from 'react-router-dom'; +import {BrowserRouter, MemoryRouter, Routes, Route, Link} from 'react-router-dom'; import { Provider } from 'react-redux'; -import store from '../src/renderer/store.js'; - +import store from '../src/renderer/store'; +import {describe, expect, test, jest} from '@jest/globals'; import fetchMock from 'jest-fetch-mock'; +import { act } from 'react-test-renderer'; + + +const mockedUsedNavigate = jest.fn(); +// jest.mock('react-router-dom', () => ({ +// useNavigate: () => mockedUsedNavigate, +// })); fetchMock.enableMocks(); describe('Login Page Renders', () => { - beforeEach(() => { + beforeEach( async () => { fetch.resetMocks(); - render( - - - - - - ); - // screen.debug(); + await act(()=>{ + render( + + + + + + ); + + }); + screen.debug(); }); test('Username accepts input', async () => { const username = document.querySelector('#username'); - await fireEvent.change(username, {target: {value:'hi'}}); - expect(username.value).toBe('hi'); + await fireEvent.change(username, {target: {value:'sysadmin'}}); + expect(username.value).toBe('sysadmin'); }); test('Password accepts input', async () => { const password = document.querySelector('#password'); - await fireEvent.change(password, {target: {value:'hi'}}); - expect(password.value).toBe('hi'); + await fireEvent.change(password, {target: {value:'belugas'}}); + expect(password.value).toBe('belugas'); }); test('Login button', async () => { - fetch.mockResponseOnce(JSON.stringify({ username: 'string', password: 'anotherstring' })); - + fetch.mockResponseOnce(JSON.stringify({ username: 'sysadmin', password: 'belugas' })); + const alert = window.alert = jest.fn(); const loginButton = screen.getByRole('button'); - await fireEvent.click(loginButton); + await act(()=>{ + fireEvent.click(loginButton); + }); + // need to fix issue of localhost/4000 not rendering anything after you login + // screen.debug( ); + // it is blank, which is expected for the test, but not for the application as a whole + // should fail, look into this test - expect(loginButton).not.toBeCalled; - screen.debug( loginButton); }); }); \ No newline at end of file diff --git a/package.json b/package.json index 63d6455b..ca2bed70 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,12 @@ "license": "MIT", "description": "A Docker Visualizer", "author": "Team Docketeer", - "main": "dist/DockteerElectron.js", + "main": "dist/DocketeerElectron.js", "scripts": { "dev:electron": "cross-env NODE_ENV=development webpack --config webpack.electron.config.js --mode development && electron .", "dev:react": "cross-env NODE_ENV=development webpack-dev-server --config webpack.react.config.js --mode development", "dev": "concurrently \"cross-env NODE_ENV=development webpack-dev-server --config webpack.react.config.js --mode development\" \"nodemon server/server.js\" \"npm run dev:electron\"", - "buildWeb": "webpack", + "buildWeb": "webpack --config=./webpack.react.config.js", "test": "jest --verbose" }, "dependencies": { @@ -55,6 +55,7 @@ "axios": "^1.0.0", "babel-loader": "^8.2.5", "bcryptjs": "^2.4.3", + "chartjs-test-utils": "^0.5.0", "concurrently": "^7.4.0", "cross-env": "^7.0.3", "css-loader": "^6.7.1", diff --git a/src/components/helper/processLogHelper.js b/src/components/helper/processLogHelper.js index ab5dc7ce..16bade43 100644 --- a/src/components/helper/processLogHelper.js +++ b/src/components/helper/processLogHelper.js @@ -57,6 +57,6 @@ export const makeArrayOfObjects = (string, containerName) => { // filter out empty messages const arrayOfLogs = arrayOfObjects.filter((obj) => obj.logMsg !== ''); - + console.log('array of logs in processloghelper', arrayOfLogs); return arrayOfLogs; }; diff --git a/src/components/tabs/Containers.js b/src/components/tabs/Containers.js index 5169c412..4fb70507 100644 --- a/src/components/tabs/Containers.js +++ b/src/components/tabs/Containers.js @@ -135,7 +135,7 @@ const Containers = (props) => { className='stop-btn' onClick={() => props.stop(container.ID, props.stopRunningContainer)} > - STOfP + STOP
@@ -153,7 +153,7 @@ const Containers = (props) => {

- Exited Conftainers: {props.stoppedList.length} + Exited Containers: {props.stoppedList.length}

{renderStoppedList}
diff --git a/src/components/tabs/ProcessLogs.js b/src/components/tabs/ProcessLogs.js index 30ceabac..dec37ba0 100644 --- a/src/components/tabs/ProcessLogs.js +++ b/src/components/tabs/ProcessLogs.js @@ -12,26 +12,26 @@ const ProcessLogs = (props) => { const renderRunningList = []; props.runningList.map((container, index) => { renderRunningList.push( - + ); }); const renderStoppedList = []; props.stoppedList.map((container, index) => { renderStoppedList.push( - + ); }); return (
-

Process Logs

+

Procsess Logs

Running Containers: {props.runningList.length}

diff --git a/src/renderer/store.ts b/src/renderer/store.ts index 95c20071..6eb83f0f 100644 --- a/src/renderer/store.ts +++ b/src/renderer/store.ts @@ -1,6 +1,5 @@ import { configureStore } from '@reduxjs/toolkit' import reducers from '../redux/reducers/index'; -import { composeWithDevTools } from '@redux-devtools/extension'; const store = configureStore({ reducer: reducers, diff --git a/webpack.electron.config.js b/webpack.electron.config.js index 3ede41e4..b6849261 100644 --- a/webpack.electron.config.js +++ b/webpack.electron.config.js @@ -2,7 +2,7 @@ const path = require('path'); module.exports = { resolve: { - extensions: ['.tsx', '.ts', '.js'] + extensions: ['.tsx', '.ts', 'jsx', '.js'] }, devtool: 'inline-source-map', entry: './src/main/index.ts', @@ -20,6 +20,6 @@ module.exports = { }, output: { path: path.join(__dirname, '/dist'), - filename: 'DockteerElectron.js' + filename: 'DocketeerElectron.js' } }; diff --git a/webpack.react.config.js b/webpack.react.config.js index 5ac1c645..6b486678 100644 --- a/webpack.react.config.js +++ b/webpack.react.config.js @@ -6,7 +6,7 @@ const webpack = require('webpack'); module.exports = { mode: process.env.NODE_ENV, resolve: { - extensions: ['.tsx', '.ts', '.js', '.jsx', '.json'], + extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'], mainFields: ['main', 'module', 'browser'], fallback: { fs: false, @@ -26,9 +26,7 @@ module.exports = { dns: false } }, - entry: { - bundle: path.join(__dirname, '/src/renderer/index.tsx') - }, + entry: '/src/renderer/index.tsx', output: { path: path.join(__dirname, '/dist'), // Taking our group of files and bundle them into Docketeer.js @@ -40,7 +38,7 @@ module.exports = { module: { rules: [ { - test: /\.(js|jsx)$/, + test: /\.(jsx|js)$/, exclude: /node_modules/, use: { loader: 'babel-loader', @@ -50,7 +48,7 @@ module.exports = { } }, { - test: /\.(ts|tsx)$/, + test: /\.(tsx|ts)$/, exclude: /node_modules/, loader: 'ts-loader' }, From adff0aed44a081d2d0c8732775a8ce526a182c1e Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Wed, 30 Nov 2022 13:44:42 -0600 Subject: [PATCH 047/110] made changes to ImageTab and UsersTab Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- __tests__/ImageTab.test.js | 39 ++++++++---------- __tests__/MetricsTab.test.js | 79 +----------------------------------- __tests__/UsersTab.test.js | 32 +++++++-------- package.json | 7 ++-- 4 files changed, 35 insertions(+), 122 deletions(-) diff --git a/__tests__/ImageTab.test.js b/__tests__/ImageTab.test.js index d30b7e2a..35b66566 100644 --- a/__tests__/ImageTab.test.js +++ b/__tests__/ImageTab.test.js @@ -6,8 +6,6 @@ import React from 'react'; import { describe, expect, test, jest } from '@jest/globals'; import Images from '../src/components/tabs/Images'; // import ImageUsers from '../src/components/tabs/ImagesUser'; - -import mockAxios from 'axios'; import '@testing-library/react'; import '@testing-library/jest-dom'; import { @@ -28,20 +26,17 @@ const props = { ], runIm: jest.fn(), removeIm: jest.fn(), + onClick: jest.fn(), }; -/* ----- mock functions ----- */ - -// jest.mock('axios'); -// const mockRun = jest.fn(); -// const mockRremove = jest.fn(); - /* ----- search bar ----- */ - -test('Search accepts input', async () => { - const search = screen.getByRole('input'); - await fireEvent.change(search, { target: { value: 'search' } }); - expect(search.value).toBe('search'); +describe('Seach bar testing', () => { + test('Search accepts input', async () => { + const { container } = render(); + const search = screen.getByRole('textbox'); + await fireEvent.change(search, { target: { value: 'search' } }); + expect(search.value).toBe('search'); + }); }); /* ----- button testing ------ */ @@ -56,14 +51,14 @@ describe('run button on click', () => { }); // currently gets stuck at window.runExec method --> reads undefined -describe('pull button on click', () => { - test('fires pull button functionality', async () => { - const { container } = render(); - const pullButton = screen.getByRole('button', { name: 'Pull' }); - await fireEvent.click(pullButton); - expect(pullButton).toBeCalled; - }); -}); +// describe('pull button on click', () => { +// test('fires pull button functionality', () => { +// const { container } = render(); +// const pullButton = screen.getByRole('button', { name: 'Pull' }); +// fireEvent.click(pullButton); +// expect(pullButton).toBeCalled; +// }); +// }); describe('remove button on click', () => { test('fires remove button functionality', async () => { @@ -74,8 +69,6 @@ describe('remove button on click', () => { }); }); -// need test for text in input field? - /* ------ actions/reducers ------ */ describe('Images', () => { diff --git a/__tests__/MetricsTab.test.js b/__tests__/MetricsTab.test.js index 706c0538..ac5a85b7 100644 --- a/__tests__/MetricsTab.test.js +++ b/__tests__/MetricsTab.test.js @@ -2,85 +2,8 @@ * These tests do not work as enzyme is highly depricated and does not communicate with React 18 */ +/* ----- testing metrics ----- */ -// import React from 'react'; -// import { configure, shallow } from 'enzyme'; -// import Adapter from 'enzyme-adapter-react-16'; -// import Metrics from '../src/components/tabs/Metrics'; - -// // Newer Enzyme versions require an adapter to a particular version of React -// configure({ adapter: new Adapter() }); - -// function shallowSetup() { - - -// const props = { -// runningList: [{ BlockIO: "1B/2B", ID: "6f49565a501c", CPUPerc: "20.00%", MemPerc: "0.00%", MemUsage: "5B/6B", Name: "checkpoint_nginx_1", NetIO: "3B/4B", PIDs: "0" }, { BlockIO: "3B/4B", ID: "6f49565a501c", CPUPerc: "30.00%", MemPerc: "20.00%", MemUsage: "5B/6B", Name: "checkpoint_nginx_2", NetIO: "5B/6B", PIDs: "0" }] -// } - -// const enzymeWrapper = shallow(); - - -// return { -// props, -// enzymeWrapper -// }; -// } - -// describe('Shallow rendered Metrics for chart', () => { -// // Setup wrapper and assign props. - -// const { enzymeWrapper, props } = shallowSetup(); - -// it('Should render
tag in Metrics', () => { -// expect(enzymeWrapper.type()).toEqual('div'); -// }) - -// it('Should render Pie chart data properly', () => { - -// // enzymeWrapper.find(selector) : Find every node in the render tree that matches the provided selector. -// expect(enzymeWrapper.find('p.legend-text')).toHaveLength(4); -// enzymeWrapper.find('p.legend-text').forEach((element) => { - -// let value = element.text().split(' ') -// let percentage = parseInt(value[1].substr(0, value[1].length-4)); -// expect(percentage).toBeGreaterThanOrEqual(0); -// }); - -// }); - -// it('should have valid number of Net I/O and Block I/O of

', () => { -// expect(enzymeWrapper.find('p.chart-number')).toHaveLength(2); -// }) - -// it('should render Net I/O data properly', () => { - -// enzymeWrapper.find('p.chart-number').forEach((element, i) => { -// if(i === 0){ -// let value = element.text().split('/') -// let IOFront = parseInt(value[0].substr(0, value[0].length-2)); -// let IOBack = parseInt(value[1].substr(0, value[1].length-2)); - -// expect(IOFront).toBeGreaterThanOrEqual(0); -// expect(IOBack).toBeGreaterThanOrEqual(0); -// } -// }); -// }); - -// it('should render Block I/O data properly', () => { - -// enzymeWrapper.find('p.chart-number').forEach((element, i) => { -// if(i === 1){ -// let value = element.text().split('/') -// let IOFront = parseInt(value[0].substr(0, value[0].length-1)); -// let IOBack = parseInt(value[1].substr(0, value[1].length-1)); - -// expect(IOFront).toBeGreaterThanOrEqual(0); -// expect(IOBack).toBeGreaterThanOrEqual(0); -// } -// }); -// }); -// }); //* Dummy Test describe('dummy test', () => { diff --git a/__tests__/UsersTab.test.js b/__tests__/UsersTab.test.js index 8a2aaa43..cecfb745 100644 --- a/__tests__/UsersTab.test.js +++ b/__tests__/UsersTab.test.js @@ -10,11 +10,8 @@ import { screen, waitFor, } from '@testing-library/react'; -import Users from '../src/components/tabs/Users'; import NewUserDisplay from '../src/components/display/NewUserDisplay'; -import { checkPasswordLength } from '../src/components/helper/newUserHelper'; -import { userInfo } from 'os'; -import user from '@testing-library/user-event'; +import * as helpers from '../src/components/helper/newUserHelper'; const props = { onSubmit: jest.fn(), @@ -22,14 +19,14 @@ const props = { onClick: jest.fn(), }; -/* ----- Mock Functions ----- */ - /* ----- Manage Users Table ----- */ -// checking for button functionality -// describe('Manage Users Table', () => { -// test() -// }) +// check for component render +// describe('Render Manage Users Table', () => { +// beforeEach(() => { +// render(); +// }); +// }); /* ----- Create a New User ----- */ @@ -41,7 +38,6 @@ describe('Create a New User functionality', () => { // input fields take in text test('input fields take in text', () => { const input = document.getElementById('signupEmail'); - // screen.debug(); expect(input).toHaveValue(''); }); // password is at least 6 chars long @@ -51,19 +47,19 @@ describe('Create a New User functionality', () => { expect(password.value).toBe('123456'); }); // password and confirm passwords match - // if either of these tests fail, check for error message alert test('Password must match Confirm Password', async () => { const signup = document.getElementById('signupPassword'); const confirmation = document.getElementById('signupPasswordConfirmation'); await fireEvent.change(signup, { target: { value: '123456' } }); - await fireEvent.change(confirmation, { target: { value: '123456' } }); + await fireEvent.change(confirmation, { target: { value: '123456' } }); expect(signup.value).toBe(confirmation.value); }); - - // submit button works when password fields match - test('onSubmit is called when password fields match', async () => {}); - - // Manage Users Table gains a row with correct information + test('check that onSubmit button is working', async () => { + const onSubmit = jest.fn(); + const button = screen.getByRole('button', { name: 'Submit' }); + await fireEvent.click(button); + expect(onSubmit).toHaveBeenCalledTimes(1); + }); //* Dummy Test describe('dummy test', () => { diff --git a/package.json b/package.json index 0b5f29c1..7835b181 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "@mui/icons-material": "^5.10.6", "@mui/material": "^5.10.8", "@mui/x-data-grid": "^5.17.7", - "@reduxjs/toolkit": "^1.9.0", "@testing-library/user-event": "^14.4.3", "@types/node": "^18.8.3", "@types/react": "^18.0.21", @@ -67,7 +66,7 @@ "express": "^4.18.1", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", - "jest": "^29.1.2", + "jest": "^29.3.1", "jest-environment-jsdom": "^29.2.1", "jest-fetch-mock": "^3.0.3", "node-fetch": "^3.2.10", @@ -77,10 +76,11 @@ "react-test-renderer": "^18.2.0", "style-loader": "^3.3.1", "supertest": "^6.3.0", + "ts-jest": "^29.0.3", "ts-loader": "^9.4.1", "ts-node": "^10.9.1", "twilio": "^3.82.1", - "typescript": "^4.8.4", + "typescript": "^4.9.3", "webpack": "^5.74.0", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.11.1" @@ -95,6 +95,7 @@ "transform": { "^.+\\.jsx?$": "babel-jest" }, + "preset": "ts-jest", "moduleNameMapper": { "\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/mocks/fileMock.js", "\\.(css|scss)$": "/__mocks__/styleMock.js" From e139c3dc01cab136bada65236d53b36d016028e0 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Wed, 30 Nov 2022 13:48:58 -0600 Subject: [PATCH 048/110] made changes to ImageTab and UsersTabs again Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- __tests__/UsersTab.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/UsersTab.test.js b/__tests__/UsersTab.test.js index cecfb745..7fc21248 100644 --- a/__tests__/UsersTab.test.js +++ b/__tests__/UsersTab.test.js @@ -58,7 +58,7 @@ describe('Create a New User functionality', () => { const onSubmit = jest.fn(); const button = screen.getByRole('button', { name: 'Submit' }); await fireEvent.click(button); - expect(onSubmit).toHaveBeenCalledTimes(1); + expect(onSubmit).toBeCalled; }); //* Dummy Test From 56af9f4822b57f9479b777b0e1a8dfaf54348554 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Wed, 30 Nov 2022 11:50:06 -0800 Subject: [PATCH 049/110] Changes to TabTypes and Admin.tsx Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/tabs/TabTypes.ts | 2 ++ src/components/views/Admin.tsx | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 255dd995..8ab7d803 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -34,6 +34,8 @@ export type ContainerProps = { remove: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; stop: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; runningList: Array; + runIm: (id: string, runningList: RunningListType, callback_1: () => void, callback_2: () => void) => void; + addRunningContainers: (data: object[]) => void; } // Stopped containers have a Names key and running containers have a Name key diff --git a/src/components/views/Admin.tsx b/src/components/views/Admin.tsx index 9f671181..ad39f84a 100644 --- a/src/components/views/Admin.tsx +++ b/src/components/views/Admin.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { Routes, Route, Link, useNavigate } from 'react-router-dom'; - +import {StoppedListType, RunningListType} from '../tabs/TabTypes' // static imports import * as actions from '../../redux/actions/actions'; import * as helper from '../helper/commands'; @@ -39,11 +39,11 @@ const AdminView = () => { // const composeymlFiles = (data) => dispatch(actions.composeymlFiles(data)); const getNetworkContainers = (data: object[]) => dispatch(actions.getNetworkContainers(data)); - const removeContainer = (id: number) => dispatch(actions.removeContainer(id)); + const removeContainer = (id: string) => dispatch(actions.removeContainer(id)); // this parameter was changed from data: object[] because in the actions file, an id argument was being requested - const runStoppedContainer = (id: number) => + const runStoppedContainer = (id: string) => dispatch(actions.runStoppedContainer(id)); - const stopRunningContainer = (id: number) => + const stopRunningContainer = (id: string) => dispatch(actions.stopRunningContainer(id)); const updateSession = () => dispatch(actions.updateSession()); // originally, this function have any parameters, but typescript through an error saying it was needed. Check this out later @@ -52,8 +52,8 @@ const AdminView = () => { const getVolumeContainersList = (data: object[]) => dispatch(actions.getVolumeContainersList(data)); interface containersList { - runningList: object[], - stoppedList: object[] + runningList: RunningListType[], + stoppedList: StoppedListType[] } interface imagesList { From c22a27f6e1483adbc642fb16004336d5bc53c8b4 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Wed, 30 Nov 2022 14:01:51 -0600 Subject: [PATCH 050/110] fix state errors in LineChartDisplay file Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/display/LineChartDisplay.tsx | 26 ++++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/components/display/LineChartDisplay.tsx b/src/components/display/LineChartDisplay.tsx index b2a7b3b1..d4ac4720 100644 --- a/src/components/display/LineChartDisplay.tsx +++ b/src/components/display/LineChartDisplay.tsx @@ -6,6 +6,7 @@ import * as actions from "../../redux/actions/actions"; import * as helper from "../helper/commands"; import { DataGrid } from "@mui/x-data-grid"; import { FormControlLabel, Checkbox } from "@mui/material"; +import { RootState } from "../../renderer/store"; /** * Displays linegraph and github metrics @@ -16,13 +17,19 @@ const LineChartDisplay = () => { const [activeContainers, setActiveContainers] = useState({}); const [gitUrls, setGitUrls] = useState([]); const [timePeriod, setTimePeriod] = useState("4"); - const memory = useSelector((state) => state.graphs.graphMemory); - const cpu = useSelector((state) => state.graphs.graphCpu); - const writtenIO = useSelector((state) => state.graphs.graphWrittenIO); - const readIO = useSelector((state) => state.graphs.graphReadIO); - const axis = useSelector((state) => state.graphs.graphAxis); - const runningList = useSelector((state) => state.containersList.runningList); - const stoppedList = useSelector((state) => state.containersList.stoppedList); + const memory = useSelector((state: RootState) => state.graphs["graphMemory"]); + const cpu = useSelector((state: RootState) => state.graphs["graphCpu"]); + const writtenIO = useSelector( + (state: RootState) => state.graphs["graphWrittenIO"] + ); + const readIO = useSelector((state: RootState) => state.graphs["graphReadIO"]); + const axis = useSelector((state: RootState) => state.graphs["graphAxis"]); + const runningList = useSelector( + (state: RootState) => state.containersList.runningList + ); + const stoppedList = useSelector( + (state: RootState) => state.containersList.stoppedList + ); const dispatch = useDispatch(); const buildAxis = (data: string) => dispatch(actions.buildAxis(data)); @@ -82,10 +89,7 @@ const LineChartDisplay = () => { const output = await getContainerMetrics(); - const generateLineColor = ( - containerName: string[], - activeContainers: {} - ) => { + const generateLineColor = (containerName: string, activeContainers: {}) => { const colorOptions = [ "red", "blue", From 15edc89fe10f095c19dd791c05fb94dbe1227113 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Wed, 30 Nov 2022 12:08:54 -0800 Subject: [PATCH 051/110] converted ContainersUser to TS Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- .../{ContainersUser.js => ContainersUser.tsx} | 22 +++++++------------ src/components/tabs/TabTypes.ts | 6 +---- 2 files changed, 9 insertions(+), 19 deletions(-) rename src/components/tabs/{ContainersUser.js => ContainersUser.tsx} (86%) diff --git a/src/components/tabs/ContainersUser.js b/src/components/tabs/ContainersUser.tsx similarity index 86% rename from src/components/tabs/ContainersUser.js rename to src/components/tabs/ContainersUser.tsx index 4de73a11..0c2e6fc0 100644 --- a/src/components/tabs/ContainersUser.js +++ b/src/components/tabs/ContainersUser.tsx @@ -2,14 +2,14 @@ import React from 'react'; import { Chart } from 'react-chartjs-2'; import ToggleDisplay from '../display/ToggleDisplay'; - +import { ContainerProps, ContainerType, ChartInfoType } from './TabTypes'; /** * Display all running and stopped containers * * @param {*} props */ -const Containers = (props) => { - const renderStoppedList = props.stoppedList.map((container, i) => { +const Containers = (props: ContainerProps) => { + const renderStoppedList = props.stoppedList.map((container: ContainerType, i: number) => { return (

@@ -34,7 +34,7 @@ const Containers = (props) => { ); }); - const renderRunningList = props.runningList.map((container, i) => { + const renderRunningList = props.runningList.map((container: ContainerType, i: number) => { const cpuData = parseFloat( container.CPUPerc.substring(0, container.CPUPerc.length - 1) ).toFixed(2); @@ -42,12 +42,12 @@ const Containers = (props) => { container.MemPerc.substring(0, container.MemPerc.length - 1) ).toFixed(2); const stack = 'stack'; - const chartInfo = { + const chartInfo: ChartInfoType = { labels: ['CPU', 'Memory'], datasets: [ { stack, - label: Math.random(), + label: Math.random().toString(), backgroundColor: ['rgba(44, 130, 201, 1)', 'rgba(19, 221, 29, 1)'], borderColor: 'rgba(0,0,0,0)', borderWidth: 1, @@ -56,11 +56,11 @@ const Containers = (props) => { }, { stack, - label: Math.random(), + label: Math.random().toString(), backgroundColor: ['rgba(155, 198, 233, 1)', 'rgba(217, 252, 219, 1)'], borderColor: 'rgba(0,0,0,0)', borderWidth: 1, - data: [(100 - cpuData).toFixed(2), (100 - memoryData).toFixed(2)], + data: [(100 - Number(cpuData)).toFixed(2), (100 - Number(memoryData)).toFixed(2)], barPercentage: 0.45, }, ], @@ -98,14 +98,8 @@ const Containers = (props) => { plugins: { legend: { display: false } }, scales: { y: { - ticks: { - min: 0, - max: 100, - stepSize: 50 - }, stacked: true }, - x: { categorySpacing: 0 } } }} /> diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 8ab7d803..ebba9d6c 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -66,8 +66,4 @@ export type ContainerProps = { borderWidth: number; barPercentage: number; } - -// export interface BarType { -// options: any; -// data: any; -// } + \ No newline at end of file From 017f44d1516630e903621b5fbd75010d33571396 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 30 Nov 2022 15:10:00 -0500 Subject: [PATCH 052/110] comment metricstabtest --- __tests__/MetricsTab.test.js | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/__tests__/MetricsTab.test.js b/__tests__/MetricsTab.test.js index c878f208..97659ce9 100644 --- a/__tests__/MetricsTab.test.js +++ b/__tests__/MetricsTab.test.js @@ -13,22 +13,25 @@ import { create } from 'react-test-renderer'; import { fireEvent, render, screen } from '@testing-library/react'; const props = { - runningList: [{ BlockIO: "1B/2B", ID: "6f49565a501c", CPUPerc: "20.00%", MemPerc: "0.00%", MemUsage: "5B/6B", Name: "checkpoint_nginx_1", NetIO: "3B/4B", PIDs: "0" }, { BlockIO: "3B/4B", ID: "6f49565a501c", CPUPerc: "30.00%", MemPerc: "20.00%", MemUsage: "5B/6B", Name: "checkpoint_nginx_2", NetIO: "5B/6B", PIDs: "0" }] -} - -describe('Metrics tab should render', () => { - beforeEach(()=>{ - render( - - - - ) - }); + runningList: [{ BlockIO: '1B/2B', ID: '6f49565a501c', CPUPerc: '20.00%', MemPerc: '0.00%', MemUsage: '5B/6B', Name: 'checkpoint_nginx_1', NetIO: '3B/4B', PIDs: '0' }, { BlockIO: '3B/4B', ID: '6f49565a501c', CPUPerc: '30.00%', MemPerc: '20.00%', MemUsage: '5B/6B', Name: 'checkpoint_nginx_2', NetIO: '5B/6B', PIDs: '0' }] +}; + +// Docketeer 8.0 +// Testing chart.js might be better handled through component rather than testing suite + +// describe('Metrics tab should render', () => { +// beforeEach(()=>{ +// render( +// +// +// +// ); +// }); - test('Metrics', ()=>{ - expect(1).toBe(1) - }) -}) +// test('Metrics', ()=>{ +// expect(1).toBe(1); +// }); +// }); /* From 248d8a677a35afbe592dab8aa9490b0be11680b1 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 30 Nov 2022 20:08:27 -0500 Subject: [PATCH 053/110] edit images Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/helper/commands.js | 6 ++-- src/components/tabs/Images.js | 49 +++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/components/helper/commands.js b/src/components/helper/commands.js index 31b8edee..e7f2b961 100644 --- a/src/components/helper/commands.js +++ b/src/components/helper/commands.js @@ -300,16 +300,18 @@ export const handlePruneClick = (e) => { export const pullImage = (repo) => { window.nodeMethod.runExec(`docker pull ${repo}`, (error, stdout, stderr) => { if (error) { - alert(`${error.message}`); + console.log('error occurred in pulling image'); + alert(`Image repo '${repo}' seems to not exist, or may be a private repo.`); + // alert(`${error.message}`); return; } if (stderr) { console.log(`pullImage stderr: ${stderr}`); return; } + alert(`${repo} is currently being downloaded`); console.log(stdout); - console.log(repo, 'is currently being pulled'); // if not error, add a loading component until page renders a new component }); }; diff --git a/src/components/tabs/Images.js b/src/components/tabs/Images.js index 03382943..f93c78c3 100644 --- a/src/components/tabs/Images.js +++ b/src/components/tabs/Images.js @@ -11,8 +11,50 @@ const Images = (props) => { const [repo, setRepo] = useState(''); const handleClick = (e) => { - e.preventDefault(); - helper.pullImage(repo); + if (!repo) { + alert('Please enter an image to pull!'); + return; + } + else { + let existingRepo = false; + if(repo.includes(':')){ + const splitRepo = repo.split(':'); + // can't break out of a forEach, so opted to use map as temp solution + props.imagesList.map((el)=>{ + if(el.reps === splitRepo[0] && el.tag === splitRepo[1]){ + existingRepo = true; + return; + } + }); + if (existingRepo === true) { + alert('This image already exists!'); + return; + } + else { + alert('Looking for image'); + helper.pullImage(repo); + return; + } + } + + else { + props.imagesList.map((el)=>{ + if (el.reps === repo && el.tag === 'latest'){ + existingRepo = true; + return; + } + }); + if (existingRepo === true){ + alert('This image already exists!'); + return; + } + else { + alert('Looking for image'); + helper.pullImage(repo); + return; + } + } + } }; const renderImagesList = props.imagesList.map((ele, i) => { @@ -69,10 +111,11 @@ const Images = (props) => {

Images

- + { setRepo(e.target.value); From bdd7bc3b058f453407c93b8677f3df617ab3a344 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Wed, 30 Nov 2022 19:11:18 -0600 Subject: [PATCH 054/110] first iteration of converting SysAdmin file to Typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/views/SysAdmin.tsx | 360 +++++++++++++++++++++++++++++ src/components/views/viewsTypes.ts | 68 ++++++ src/redux/actions/actions.ts | 2 +- 3 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 src/components/views/SysAdmin.tsx create mode 100644 src/components/views/viewsTypes.ts diff --git a/src/components/views/SysAdmin.tsx b/src/components/views/SysAdmin.tsx new file mode 100644 index 00000000..cb66c362 --- /dev/null +++ b/src/components/views/SysAdmin.tsx @@ -0,0 +1,360 @@ +// module imports +import React, { useEffect, useState } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { Routes, Route, Link, useNavigate, NetworkObj } from 'react-router-dom'; + +// static imports +import * as actions from '../../redux/actions/actions'; +import * as helper from '../helper/commands'; +import * as history from '../helper/volumeHistoryHelper'; +// @ts-ignore +import Docketeer from '../../../assets/docketeer-title.png'; + +// tab component imports +import Metrics from '../tabs/Metrics'; +import Images from '../tabs/Images'; +import Yml from '../tabs/Yml'; +import Containers from '../tabs/Containers'; +import Settings from '../tabs/Settings'; +import UserList from '../tabs/Users'; //* Feature only for SysAdmin +import VolumeHistory from '../tabs/VolumeHistory'; +import ProcessLogs from '../tabs/ProcessLogs'; +import ProcessLogsTable from '../display/ProcessLogsTable'; + +// helper function imports +import startNotificationRequester from '../helper/notificationsRequester'; +import initDatabase from '../helper/initDatabase'; +import { ContainerObj, StoppedContainerObj, ImageObj, UserObj, VolumeObj } from "./viewsTypes"; + +// Container component that has all redux logic along with react router +const SysAdmin = () => { + let navigate = useNavigate(); + const dispatch = useDispatch(); + const addRunningContainers = (data: ContainerObj[]) => + dispatch(actions.addRunningContainers(data)); + const refreshRunningContainers = (data: ContainerObj[]) => + dispatch(actions.refreshRunningContainers(data)); + const refreshStoppedContainers = (data: StoppedContainerObj[]) => + dispatch(actions.refreshStoppedContainers(data)); + const refreshImagesList = (data: ImageObj[]) => + dispatch(actions.refreshImages(data)); + const composeymlFiles = (data) => { + console.log("Magic47:", data) + return dispatch(actions.composeymlFiles(data)); + } + + const getNetworkContainers = (data: NetworkObj[]) => + dispatch(actions.getNetworkContainers(data)); + const removeContainer = (id: string) => + dispatch(actions.removeContainer(id)); + const runStoppedContainer = (data) => + { + console.log("Magic64:", data) + return dispatch(actions.runStoppedContainer(data)); + } + const stopRunningContainer = (id: string) => + dispatch(actions.stopRunningContainer(id)); + const updateSession = () => + dispatch(actions.updateSession()); + const logoutUser = () => + dispatch(actions.logoutUser()); + const updateUserList = (data: UserObj[]) => + dispatch(actions.updateUserList(data)); //* Feature only for SysAdmin + const getVolumeList = (data: { Name: string }[]) => + dispatch(actions.getVolumeList(data)); + const getVolumeContainersList = (data: VolumeObj) => + dispatch(actions.getVolumeContainersList(data)); + + // map state to props + const runningList = useSelector((state) => state.containersList.runningList); + const stoppedList = useSelector((state) => state.containersList.stoppedList); + const imagesList = useSelector((state) => state.images.imagesList); + const networkList = useSelector((state) => state.networkList.networkList); + const userInfo = useSelector((state) => state.session); //* Feature only for SysAdmin + const arrayOfVolumeNames = useSelector( + (state) => state.volumeList.arrayOfVolumeNames + ); + const volumeContainersList = useSelector( + (state) => state.volumeList.volumeContainersList + ); + + // map state to props + const phoneNumber = useSelector( + (state) => state.notificationList.phoneNumber + ); + const memoryNotificationList = useSelector( + (state) => state.notificationList.memoryNotificationList + ); + const cpuNotificationList = useSelector( + (state) => state.notificationList.cpuNotificationList + ); + const stoppedNotificationList = useSelector( + (state) => state.notificationList.stoppedNotificationList + ); + + // Local state for routers + const [selected, setSelected] = useState('/'); + + const handleLogout = (e) => { + updateSession(); + logoutUser(); + fetch('http://localhost:3000/logout', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + username: userInfo.username + }) + }) + .then((data) => data.json()) + .then((response) => { + return console.log(response); + }) + .catch((err) => { + console.log(err); + }); + navigate('/login'); + }; + + useEffect(() => { + initDatabase(); + helper.refreshRunning(refreshRunningContainers); + helper.refreshStopped(refreshStoppedContainers); + helper.refreshImages(refreshImagesList); + helper.writeToDb(); + helper.networkContainers(getNetworkContainers); + helper.setDbSessionTimeZone(); + helper.getAllDockerVolumes(getVolumeList); + }, []); + + useEffect(() => { + history.volumeByName( + helper.getVolumeContainers, + arrayOfVolumeNames, + getVolumeContainersList + ); + }, [arrayOfVolumeNames]); + + // every 5 seconds invoke helper functions to refresh running, stopped and images, as well as notifications + useEffect(() => { + const interval = setInterval(() => { + helper.refreshRunning(refreshRunningContainers); + helper.refreshStopped(refreshStoppedContainers); + helper.refreshImages(refreshImagesList); + }, 5000); + startNotificationRequester(); + return () => clearInterval(interval); + }, []); + + // * SysAdmin Unique useEffect + useEffect(() => { + fetch('http://localhost:3000/admin', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + token: userInfo.token, + username: userInfo.username + }) + }) + .then((response) => { + return response.json(); + }) + .then((data) => { + updateUserList(data); + }) + .catch((err) => { + console.log(err); + }); + }, []); + + const selectedStyling = { + background: '#e1e4e6', + color: '#042331', + borderTopRightRadius: '10px', + borderBottomRightRadius: '10px' + }; + + + + return ( +
+ {/* Navbar */} + + + } + /> + } + /> + } /> + } + /> + } /> + } + /> + } + /> + } + /> + } + /> + +
+ ); +}; + +export default SysAdmin; diff --git a/src/components/views/viewsTypes.ts b/src/components/views/viewsTypes.ts new file mode 100644 index 00000000..d3b3940a --- /dev/null +++ b/src/components/views/viewsTypes.ts @@ -0,0 +1,68 @@ +// this file contains the interfaces for each file. Please search a fil by name in order to find what interfaces it uses. + +export interface ContainerObj { + BlockIO: string, + CPUPerc: string, + Container: string, + ID: string, + MemPerc: string, + MemUsage: string, + Name: string, + NetIO: string, + PIDs: string +} + +export interface StoppedContainerObj { + Command: string, + CreatedAt: string, + ID: string, + Image: string, + Labels: string, + LocalVolumes: string, + Mounts: string, + Names: string, + Networks: string, + Ports: string, + RunningFor: string, + Size: string, + State: string, + Status: string +} + +export interface ImageObj { + imgid: string, + reps: string, + size: string, + tag: string +} + +export interface UserObj { + contact_pref: null | string, + container_stops: true | false, + cpu_threshold: number, + email: string, + mem_threshold: number, + password: string, + phone: string + role: string, + role_id: number, + token: string, + username: string, + _id: number +} + +export interface NetworkObj { + CreatedAt: string, + Driver: string, + ID: string, + IPv6: string, + Internal: string, + Labels: string, + Name: string, + Scope: string +} + +export interface VolumeObj { + vol_name: string, + containers: object[] +} \ No newline at end of file diff --git a/src/redux/actions/actions.ts b/src/redux/actions/actions.ts index 6f336104..335fdfe0 100644 --- a/src/redux/actions/actions.ts +++ b/src/redux/actions/actions.ts @@ -188,7 +188,7 @@ export const getVolumeList = (data: object[]) => ({ }); // get containers that live in volume -export const getVolumeContainersList = (data: object[]) => ({ +export const getVolumeContainersList = (data: object) => ({ type: types.GET_VOLUME_CONTAINERS_LIST, payload: data }); From 96f183acc4b3e489964744487af31dea34197300 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Wed, 30 Nov 2022 19:12:31 -0600 Subject: [PATCH 055/110] added tests to various files Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- __tests__/ImageTab.test.js | 6 ----- __tests__/ProcessLogHelper.test.js | 40 +++++++++++++++--------------- __tests__/ServerRoutes.test.js | 27 ++++++++++---------- __tests__/VolumeTab.test.js | 14 +++++------ 4 files changed, 41 insertions(+), 46 deletions(-) diff --git a/__tests__/ImageTab.test.js b/__tests__/ImageTab.test.js index 35b66566..e98d1647 100644 --- a/__tests__/ImageTab.test.js +++ b/__tests__/ImageTab.test.js @@ -77,12 +77,6 @@ describe('Images', () => { }); }); -// it('refreshes page if image removed', () => { -// // need to check that refreshRunning helper function is called - -// }); -// }); - //* Dummy Test describe('dummy test', () => { test('dummy test', () => { diff --git a/__tests__/ProcessLogHelper.test.js b/__tests__/ProcessLogHelper.test.js index ae122341..5957975d 100644 --- a/__tests__/ProcessLogHelper.test.js +++ b/__tests__/ProcessLogHelper.test.js @@ -31,26 +31,26 @@ describe('makeArrayOfObjects', () => { expect(output).toEqual(true); }); - // //We edited the makeArrayOfObjects function and now this fails, not sure why as there still is a key of logMsg and timeStamp - // it('each object in returned array has timeStamp and logMsg properties', () => { - // const processLog = - // 'this_is_the_first_timestampZ this is the first log message\nthere is no second time stamp but there is a second log message'; - // const result = makeArrayOfObjects(processLog); - - // let output = false; - - // if ( - // result.every( - // (element) => - // element.timeStamp && element.logMsg && element.containerName - // ) - // ) { - // output = true; - // } - - // expect(output).toEqual(true); - // }); -}); + // We edited the makeArrayOfObjects function and now this fails, not sure why as there still is a key of logMsg and timeStamp +// it('each object in returned array has timeStamp and logMsg properties', () => { +// const processLog = +// 'this_is_the_first_timestampZ this is the first log message\nthere is no second time stamp but there is a second log message'; +// const result = makeArrayOfObjects(processLog); + +// let output = false; + +// if ( +// result.every( +// (element) => +// element.timeStamp && element.logMsg && element.containerName +// ) +// ) { +// output = true; +// } + +// expect(output).toEqual(true); +// }); +// }); describe('buildOptionsObj', () => { diff --git a/__tests__/ServerRoutes.test.js b/__tests__/ServerRoutes.test.js index f80c073c..34ab3876 100644 --- a/__tests__/ServerRoutes.test.js +++ b/__tests__/ServerRoutes.test.js @@ -46,27 +46,28 @@ describe('/test route', () => { // signup route describe('/signup route', () => { - it('get request to getAllUsers controller', (done) => { - request(app) + it('get request', async () => { + await request(app) .get('/signup') - .send({ username: 'info' }) - // .expect('Content-Type', /json/) - .expect(200, done); + .send({ username: 'test', email: 'test@test.com', password: 'password' }) + .expect('Content-Type', 'text/html; charset=utf-8'); }); - it('post request to signup route', (done) => { - request(app) + it('post request', async () => { + await request(app) .post('/signup') - .send({ random: 'info' }) + .send({ + username: 'test', + email: 'test@test.com', + password: 'password', + phone: '+15555555555', + }) .set('Accept', 'application/json') - // .expect('Content-Type', /json/) - .expect(200, done); + .expect('Content-Type', 'text/html; charset=utf-8'); }); }); // setting route -// describe('/setting route', () => { -// it(''); -// }); + // logout route // login route diff --git a/__tests__/VolumeTab.test.js b/__tests__/VolumeTab.test.js index e8910599..2e09f2b4 100644 --- a/__tests__/VolumeTab.test.js +++ b/__tests__/VolumeTab.test.js @@ -36,16 +36,16 @@ describe('rendering VolumeTab', () => { }); /* ----- search bar ----- */ - - - -//* Dummy Test -describe('dummy test', () => { - test('dummy test', () => { - expect(2 + 2).toBe(4); +describe('Seach bar testing', () => { + test('Search accepts input', async () => { + const { container } = render(); + const search = screen.getByRole('textbox'); + await fireEvent.change(search, { target: { value: 'search' } }); + expect(search.value).toBe('search'); }); }); + //* Dummy Test describe('dummy test', () => { test('dummy test', () => { From b6a909d1e98885308737fb429715ddcf1b8f3167 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Wed, 30 Nov 2022 17:12:38 -0800 Subject: [PATCH 056/110] Settings Tab --- .../tabs/{Settings.js => Settings.tsx} | 52 +++++++------------ src/components/tabs/TabTypes.ts | 23 ++++++-- 2 files changed, 36 insertions(+), 39 deletions(-) rename src/components/tabs/{Settings.js => Settings.tsx} (94%) diff --git a/src/components/tabs/Settings.js b/src/components/tabs/Settings.tsx similarity index 94% rename from src/components/tabs/Settings.js rename to src/components/tabs/Settings.tsx index c571f871..5f14544f 100644 --- a/src/components/tabs/Settings.js +++ b/src/components/tabs/Settings.tsx @@ -2,8 +2,9 @@ import React, { useEffect, useState } from 'react'; import { connect, useSelector, useDispatch } from 'react-redux'; import * as actions from '../../redux/actions/actions'; -import PropTypes from 'prop-types'; import * as categories from '../../redux/constants/notificationCategories'; +import { DispatchType, SettingsProps } from './TabTypes'; + // React Component Imports import AccountDisplay from '../display/AccountDisplay'; @@ -25,23 +26,23 @@ import Radio from '@mui/material/Radio'; import SendIcon from '@mui/icons-material/Send'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; -const mapDispatchToProps = (dispatch) => ({ - addPhoneNumber: (data) => dispatch(actions.addPhoneNumber(data)), - addNotificationFrequency: (data) => +const mapDispatchToProps = (dispatch: DispatchType) => ({ + addPhoneNumber: (data: object[]) => dispatch(actions.addPhoneNumber(data)), + addNotificationFrequency: (data: object[]) => dispatch(actions.addNotificationFrequency(data)), - addMonitoringFrequency: (data) => + addMonitoringFrequency: (data: object[]) => dispatch(actions.addMonitoringFrequency(data)), - addMemoryNotificationSetting: (data) => + addMemoryNotificationSetting: (data: object[]) => dispatch(actions.addMemoryNotificationSetting(data)), - addCpuNotificationSetting: (data) => + addCpuNotificationSetting: (data: object[]) => dispatch(actions.addCpuNotificationSetting(data)), - addStoppedNotificationSetting: (data) => + addStoppedNotificationSetting: (data: object[]) => dispatch(actions.addStoppedNotificationSetting(data)), - removeMemoryNotificationSetting: (data) => + removeMemoryNotificationSetting: (data: object[]) => dispatch(actions.removeMemoryNotificationSetting(data)), - removeCpuNotificationSetting: (data) => + removeCpuNotificationSetting: (data: object[]) => dispatch(actions.removeCpuNotificationSetting(data)), - removeStoppedNotificationSetting: (data) => + removeStoppedNotificationSetting: (data: object[]) => dispatch(actions.removeStoppedNotificationSetting(data)) }); @@ -49,29 +50,12 @@ const mapDispatchToProps = (dispatch) => ({ let showVerificationInput = false; let isVerified = false; -const Settings = (props) => { +const Settings = (props: SettingsProps) => { const [mobileNumber, setMobileNumber] = useState(''); - // Similar to TypeScript, we can use propTypes to explicit declare a type for a prop. This enables type checking and allows for catching of bugs. - // https://reactjs.org/docs/typechecking-with-proptypes.html - Settings.propTypes = { - addMonitoringFrequency: PropTypes.func.isRequired, - addMemoryNotificationSetting: PropTypes.func.isRequired, - addCpuNotificationSetting: PropTypes.func.isRequired, - addStoppedNotificationSetting: PropTypes.func.isRequired, - addPhoneNumber: PropTypes.func.isRequired, - addNotificationFrequency: PropTypes.func.isRequired, - // the 2 below - runningList: PropTypes.array.isRequired, - stoppedList: PropTypes.array.isRequired, - memoryNotificationList: PropTypes.object.isRequired, - cpuNotificationList: PropTypes.object.isRequired, - stoppedNotificationList: PropTypes.object.isRequired - }; - // handle check // insert all container information performs two database calls on the backend - const handleCheckSetting = (containerId, containerName, metricName) => { + const handleCheckSetting = (containerId: string, containerName: string, metricName: string) => { fetch('http://localhost:3000/settings/insert', { method: 'POST', headers: { @@ -94,7 +78,7 @@ const Settings = (props) => { // handle uncheck // remove container/metric from DB - const handleUnCheckSetting = (containerId, metricName) => { + const handleUnCheckSetting = (containerId: string, metricName: string) => { fetch('http://localhost:3000/settings/delete', { method: 'POST', headers: { @@ -190,7 +174,7 @@ const Settings = (props) => { const [tempNotifFreq, setTempNotifFreq] = useState(''); const notificationFrequency = () => { - let frequency = 5; + let frequency: string | number = 5; if (isNaN(Number(tempNotifFreq))) alert('Please enter notification frequency in numerical format. ex: 15'); else { @@ -221,7 +205,7 @@ const Settings = (props) => { const [tempMonitoringFrequency, setTempMonitoringFrequency] = useState(''); const monitoringFrequency = () => { - let frequency = 2; + let frequency: string | number = 2; if (isNaN(Number(tempMonitoringFrequency))) alert('Please enter monitoring frequency in numerical format. ex: 15'); else { @@ -278,7 +262,7 @@ const Settings = (props) => { */ // general function to check if a container is in a notification setting list - const isSelected = (set, containerId) => set.has(containerId); + const isSelected = (set, containerId: string) => set.has(containerId); const allContainersList = props.runningList.concat(props.stoppedList); // INSTEAD OF CREATING A NEW STATE IN THE REDUCER CONCATENATED 2 ALREADY EXISTING STATES diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 8ab7d803..5d3366e0 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -66,8 +66,21 @@ export type ContainerProps = { borderWidth: number; barPercentage: number; } - -// export interface BarType { -// options: any; -// data: any; -// } + +export type DispatchType = { + +}; + +export type SettingsProps = { + addMonitoringFrequency: (data: string | number) => void; + addMemoryNotificationSetting: (data: object[]) => void; + addCpuNotificationSetting: (data: object[]) => void; + addStoppedNotificationSetting: (data: object[]) => void; + addPhoneNumber: (data: object[]) => void; + addNotificationFrequency: (data: object[]) => void; + runningList: RunningListType; + stoppedList: StoppedListType; + memoryNotificationList: {}; + cpuNotificationList: {}; + stoppedNotificationList: {}; + }; \ No newline at end of file From 2bb5e022bf09913a931e460023cb57b77ab13224 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Wed, 30 Nov 2022 19:35:00 -0600 Subject: [PATCH 057/110] fix container interface Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/display/LineChartDisplay.tsx | 91 +++++++++++++++------ 1 file changed, 66 insertions(+), 25 deletions(-) diff --git a/src/components/display/LineChartDisplay.tsx b/src/components/display/LineChartDisplay.tsx index d4ac4720..8405d952 100644 --- a/src/components/display/LineChartDisplay.tsx +++ b/src/components/display/LineChartDisplay.tsx @@ -7,6 +7,7 @@ import * as helper from "../helper/commands"; import { DataGrid } from "@mui/x-data-grid"; import { FormControlLabel, Checkbox } from "@mui/material"; import { RootState } from "../../renderer/store"; +import { AnyArray } from "immer/dist/internal"; /** * Displays linegraph and github metrics @@ -143,10 +144,29 @@ const LineChartDisplay = () => { const containerMetrics = await getContainerMetrics(); - const auxObj = {}; + interface auxObjType { + container?: ContainerInterface; + currentContainer?: any; + containerName?: any; + } + + interface ContainerInterface { + memory?: any; + cpu?: any; + writtenIO?: any; + readIO?: any; + } + + // interface ContainerNameInterface{ + + // } + //const container: keyof auxObjType = 'container' + + const auxObj: auxObjType = {}; + //const container: keyof typeof auxObj; Object.keys(activeContainers).forEach((container) => { - auxObj[container] = { + auxObj[container as keyof typeof auxObj] = { memory: buildLineGraphObj(container), cpu: buildLineGraphObj(container), writtenIO: buildBarGraphObj(container), @@ -155,19 +175,19 @@ const LineChartDisplay = () => { }); // iterate through each row from fetch and build Memory, CPU, Written/Read Block_IO objects [{}, {}, {}, {}] - containerMetrics.rows.forEach((dataPoint) => { + containerMetrics.rows.forEach((dataPoint: any) => { const currentContainer = dataPoint.container_name; const writtenReadIO = dataPoint.block_io.split("/"); - auxObj[currentContainer].cpu.data.push( + auxObj[currentContainer as keyof typeof auxObj].cpu.data.push( dataPoint.cpu_pct.replace("%", "") ); - auxObj[currentContainer].memory.data.push( + auxObj[currentContainer as keyof typeof auxObj].memory.data.push( dataPoint.memory_pct.replace("%", "") ); - auxObj[currentContainer].writtenIO.data.push( + auxObj[currentContainer as keyof typeof auxObj].writtenIO.data.push( parseFloat(writtenReadIO[0].replace(/([A-z])+/g, "")) ); - auxObj[currentContainer].readIO.data.push( + auxObj[currentContainer as keyof typeof auxObj].readIO.data.push( parseFloat(writtenReadIO[1].replace(/([A-z])+/g, "")) ); let date = ""; @@ -188,37 +208,58 @@ const LineChartDisplay = () => { let longest = 0; // 32 - Object.keys(auxObj).forEach((containerName) => { - if (auxObj[containerName].memory.data.length > longest) { - longest = auxObj[containerName].memory.data.length; + Object.keys(auxObj).forEach((containerName: string) => { + if ( + auxObj[containerName as keyof typeof auxObj].memory.data.length > + longest + ) { + longest = + auxObj[containerName as keyof typeof auxObj].memory.data.length; } }); // REFACTOR THIS BRUTE FORCE APROACH TO ADDING 0 DATAPOINTS TO ARRAY - Object.keys(auxObj).forEach((containerName) => { - if (auxObj[containerName].memory.data.length < longest) { - const lengthToAdd = longest - auxObj[containerName].memory.data.length; + Object.keys(auxObj).forEach((containerName: string) => { + if ( + auxObj[containerName as keyof typeof auxObj].memory.data.length < + longest + ) { + const lengthToAdd = + longest - + auxObj[containerName as keyof typeof auxObj].memory.data.length; for (let i = 0; i < lengthToAdd; i += 1) { - auxObj[containerName].memory.data.unshift("0.00"); - auxObj[containerName].cpu.data.unshift("0.00"); - auxObj[containerName].writtenIO.data.unshift("0.00"); - auxObj[containerName].readIO.data.unshift("0.00"); + auxObj[containerName as keyof typeof auxObj].memory.data.unshift( + "0.00" + ); + auxObj[containerName as keyof typeof auxObj].cpu.data.unshift("0.00"); + auxObj[containerName as keyof typeof auxObj].writtenIO.data.unshift( + "0.00" + ); + auxObj[containerName as keyof typeof auxObj].readIO.data.unshift( + "0.00" + ); } } - buildMemory([auxObj[containerName].memory]); - buildCpu([auxObj[containerName].cpu]); - buildWrittenIO([auxObj[containerName].writtenIO]); - buildReadIO([auxObj[containerName].readIO]); + buildMemory(auxObj[containerName as keyof typeof auxObj].memory); + buildCpu(auxObj[containerName as keyof typeof auxObj].cpu); + buildWrittenIO(auxObj[containerName as keyof typeof auxObj].writtenIO); + buildReadIO(auxObj[containerName as keyof typeof auxObj].readIO); }); }; + interface obType { + containerName: any; + } + + const containerName; + //Fetching the data from github API and turning it into an object with keys of objects that contain the data of each container const fetchGitData = async (containerName: string) => { const ob = {}; ob[containerName] = []; const time = Number(timePeriod); //pulling the current time, and then setting it back to one month ago to check for github commit logs (2629746000 = 1 month) - let date = new Date(Date.parse(new Date()) - 2629746000); + let date: Date = new Date(Date.parse(new Date()) - 2629746000); date.setHours(date.getHours() - time); date = date.toISOString(); const urlObj = await helper.getContainerGitUrl(containerName); @@ -233,7 +274,7 @@ const LineChartDisplay = () => { const data = await fetch(url); const jsonData = await data.json(); - jsonData.forEach((commitData) => { + jsonData.forEach((commitData: any) => { ob[containerName].push({ time: commitData.commit.author.date, url: commitData.html_url, @@ -268,7 +309,7 @@ const LineChartDisplay = () => { field: "url", headerName: "URL", width: 175, - renderCell: (params) => ( + renderCell: (params: any) => ( {params.row.id} @@ -279,7 +320,7 @@ const LineChartDisplay = () => { ]; gitData = gitUrls.map((el, index) => { const name = Object.keys(el); - const rows = []; + const rows: any[]; el[name].forEach((ob, index) => { let author = ""; let date = "n/a"; From 12c226e4dce8280ffd622e3be113c1d5011d8c40 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Wed, 30 Nov 2022 19:50:47 -0600 Subject: [PATCH 058/110] second iteration of converting SysAdmin file to Typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/tabs/TabTypes.ts | 2 +- src/components/views/Admin.tsx | 53 ++--- src/components/views/SysAdmin.js | 346 ----------------------------- src/components/views/SysAdmin.tsx | 60 +++-- src/components/views/viewsTypes.ts | 29 ++- 5 files changed, 69 insertions(+), 421 deletions(-) delete mode 100644 src/components/views/SysAdmin.js diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 8ab7d803..dbc9d95c 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -35,7 +35,7 @@ export type ContainerProps = { stop: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; runningList: Array; runIm: (id: string, runningList: RunningListType, callback_1: () => void, callback_2: () => void) => void; - addRunningContainers: (data: object[]) => void; + // addRunningContainers: (data: object[]) => void; } // Stopped containers have a Names key and running containers have a Name key diff --git a/src/components/views/Admin.tsx b/src/components/views/Admin.tsx index ad39f84a..eb262149 100644 --- a/src/components/views/Admin.tsx +++ b/src/components/views/Admin.tsx @@ -24,6 +24,9 @@ import ProcessLogsTable from '../display/ProcessLogsTable'; import startNotificationRequester from '../helper/notificationsRequester'; import initDatabase from '../helper/initDatabase'; +// Types and Interface +import { ContainerObj, StoppedContainerObj, ImageObj, UserObj, VolumeObj, NetworkObj, StateType } from "./viewsTypes"; + // Container component that has all redux logic along with react router const AdminView = () => { @@ -51,59 +54,31 @@ const AdminView = () => { const getVolumeList = (data: object[]) => dispatch(actions.getVolumeList(data)); const getVolumeContainersList = (data: object[]) => dispatch(actions.getVolumeContainersList(data)); - interface containersList { - runningList: RunningListType[], - stoppedList: StoppedListType[] - } - - interface imagesList { - imagesList: any[] - } - - interface volumeList { - arrayOfVolumeNames: any[] - volumeContainersList: any[] - } - - interface notificationList { - phoneNumber: any[], - memoryNotificationList: any[], - cpuNotificationList: any[], - stoppedNotificationList: any[], - } - - interface stateType { - containersList: containersList, - images: imagesList, - volumeList: volumeList, - notificationList: notificationList - }; - // map state to props - const runningList = useSelector((state: stateType) => state.containersList.runningList); - const stoppedList = useSelector((state: stateType) => state.containersList.stoppedList); - const imagesList = useSelector((state: stateType) => state.images.imagesList); - // const networkList = useSelector((state: stateType) => state.networkList.networkList); + const runningList = useSelector((state: StateType) => state.containersList.runningList); + const stoppedList = useSelector((state: StateType) => state.containersList.stoppedList); + const imagesList = useSelector((state: StateType) => state.images.imagesList); + // const networkList = useSelector((state: StateType) => state.networkList.networkList); const arrayOfVolumeNames = useSelector( - (state: stateType) => state.volumeList.arrayOfVolumeNames + (state: StateType) => state.volumeList.arrayOfVolumeNames ); const volumeContainersList = useSelector( - (state: stateType) => state.volumeList.volumeContainersList + (state: StateType) => state.volumeList.volumeContainersList ); // map state to props const phoneNumber = useSelector( - (state: stateType) => state.notificationList.phoneNumber + (state: StateType) => state.notificationList.phoneNumber ); const memoryNotificationList = useSelector( - (state: stateType) => state.notificationList.memoryNotificationList + (state: StateType) => state.notificationList.memoryNotificationList ); const cpuNotificationList = useSelector( - (state: stateType) => state.notificationList.cpuNotificationList + (state: StateType) => state.notificationList.cpuNotificationList ); const stoppedNotificationList = useSelector( - (state: stateType) => state.notificationList.stoppedNotificationList + (state: StateType) => state.notificationList.stoppedNotificationList ); // declare a local state variable called selected, initialize to '/' @@ -328,7 +303,7 @@ const AdminView = () => { stop={helper.stop} stopRunningContainer={stopRunningContainer} runningList={runningList} - addRunningContainers={addRunningContainers} + // addRunningContainers={addRunningContainers} // Stopped Containers runStopped={helper.runStopped} remove={helper.remove} diff --git a/src/components/views/SysAdmin.js b/src/components/views/SysAdmin.js deleted file mode 100644 index 926a0d64..00000000 --- a/src/components/views/SysAdmin.js +++ /dev/null @@ -1,346 +0,0 @@ -// module imports -import React, { useEffect, useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { Routes, Route, Link, useNavigate } from 'react-router-dom'; - -// static imports -import * as actions from '../../redux/actions/actions'; -import * as helper from '../helper/commands'; -import * as history from '../helper/volumeHistoryHelper'; -import Docketeer from '../../../assets/docketeer-title.png'; - -// tab component imports -import Metrics from '../tabs/Metrics'; -import Images from '../tabs/Images'; -import Yml from '../tabs/Yml'; -import Containers from '../tabs/Containers'; -import Settings from '../tabs/Settings'; -import UserList from '../tabs/Users'; //* Feature only for SysAdmin -import VolumeHistory from '../tabs/VolumeHistory'; -import ProcessLogs from '../tabs/ProcessLogs'; -import ProcessLogsTable from '../display/ProcessLogsTable'; - -// helper function imports -import startNotificationRequester from '../helper/notificationsRequester'; -import initDatabase from '../helper/initDatabase'; - -// Container component that has all redux logic along with react router -const SysAdmin = () => { - let navigate = useNavigate(); - const dispatch = useDispatch(); - const addRunningContainers = (data) => - dispatch(actions.addRunningContainers(data)); - const refreshRunningContainers = (data) => - dispatch(actions.refreshRunningContainers(data)); - const refreshStoppedContainers = (data) => - dispatch(actions.refreshStoppedContainers(data)); - const refreshImagesList = (data) => dispatch(actions.refreshImages(data)); - const composeymlFiles = (data) => dispatch(actions.composeymlFiles(data)); - const getNetworkContainers = (data) => - dispatch(actions.getNetworkContainers(data)); - const removeContainer = (id) => dispatch(actions.removeContainer(id)); - const runStoppedContainer = (data) => - dispatch(actions.runStoppedContainer(data)); - const stopRunningContainer = (id) => - dispatch(actions.stopRunningContainer(id)); - const updateSession = () => dispatch(actions.updateSession()); - const logoutUser = () => dispatch(actions.logoutUser()); - const updateUserList = (data) => dispatch(actions.updateUserList(data)); //* Feature only for SysAdmin - const getVolumeList = (data) => dispatch(actions.getVolumeList(data)); - const getVolumeContainersList = (data) => - dispatch(actions.getVolumeContainersList(data)); - - // map state to props - const runningList = useSelector((state) => state.containersList.runningList); - const stoppedList = useSelector((state) => state.containersList.stoppedList); - const imagesList = useSelector((state) => state.images.imagesList); - const networkList = useSelector((state) => state.networkList.networkList); - const userInfo = useSelector((state) => state.session); //* Feature only for SysAdmin - const arrayOfVolumeNames = useSelector( - (state) => state.volumeList.arrayOfVolumeNames - ); - const volumeContainersList = useSelector( - (state) => state.volumeList.volumeContainersList - ); - - // map state to props - const phoneNumber = useSelector( - (state) => state.notificationList.phoneNumber - ); - const memoryNotificationList = useSelector( - (state) => state.notificationList.memoryNotificationList - ); - const cpuNotificationList = useSelector( - (state) => state.notificationList.cpuNotificationList - ); - const stoppedNotificationList = useSelector( - (state) => state.notificationList.stoppedNotificationList - ); - - // Local state for routers - const [selected, setSelected] = useState('/'); - - const handleLogout = (e) => { - updateSession(); - logoutUser(); - fetch('http://localhost:3000/logout', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - username: userInfo.username - }) - }) - .then((data) => data.json()) - .then((response) => { - return console.log(response); - }) - .catch((err) => { - console.log(err); - }); - navigate('/login'); - }; - - useEffect(() => { - - initDatabase(); - helper.refreshRunning(refreshRunningContainers); - helper.refreshStopped(refreshStoppedContainers); - helper.refreshImages(refreshImagesList); - helper.writeToDb(); - helper.networkContainers(getNetworkContainers); - helper.setDbSessionTimeZone(); - helper.getAllDockerVolumes(getVolumeList); - }, []); - - useEffect(() => { - history.volumeByName( - helper.getVolumeContainers, - arrayOfVolumeNames, - getVolumeContainersList - ); - }, [arrayOfVolumeNames]); - - // every 5 seconds invoke helper functions to refresh running, stopped and images, as well as notifications - useEffect(() => { - const interval = setInterval(() => { - helper.refreshRunning(refreshRunningContainers); - helper.refreshStopped(refreshStoppedContainers); - helper.refreshImages(refreshImagesList); - }, 5000); - startNotificationRequester(); - return () => clearInterval(interval); - }, []); - - // * SysAdmin Unique useEffect - useEffect(() => { - fetch('http://localhost:3000/admin', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - token: userInfo.token, - username: userInfo.username - }) - }) - .then((response) => { - return response.json(); - }) - .then((data) => { - updateUserList(data); - }) - .catch((err) => { - console.log(err); - }); - }, []); - - const selectedStyling = { - background: '#e1e4e6', - color: '#042331', - borderTopRightRadius: '10px', - borderBottomRightRadius: '10px' - }; - - - - return ( -
- {/* Navbar */} - - - } - /> - } - /> - } /> - } - /> - } /> - } - /> - } - /> - } - /> - } - /> - -
- ); -}; - -export default SysAdmin; diff --git a/src/components/views/SysAdmin.tsx b/src/components/views/SysAdmin.tsx index cb66c362..ca638d38 100644 --- a/src/components/views/SysAdmin.tsx +++ b/src/components/views/SysAdmin.tsx @@ -1,7 +1,7 @@ // module imports import React, { useEffect, useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; -import { Routes, Route, Link, useNavigate, NetworkObj } from 'react-router-dom'; +import { Routes, Route, Link, useNavigate } from 'react-router-dom'; // static imports import * as actions from '../../redux/actions/actions'; @@ -24,7 +24,7 @@ import ProcessLogsTable from '../display/ProcessLogsTable'; // helper function imports import startNotificationRequester from '../helper/notificationsRequester'; import initDatabase from '../helper/initDatabase'; -import { ContainerObj, StoppedContainerObj, ImageObj, UserObj, VolumeObj } from "./viewsTypes"; +import { ContainerObj, StoppedContainerObj, ImageObj, UserObj, VolumeObj, NetworkObj, StateType } from "./viewsTypes"; // Container component that has all redux logic along with react router const SysAdmin = () => { @@ -38,20 +38,13 @@ const SysAdmin = () => { dispatch(actions.refreshStoppedContainers(data)); const refreshImagesList = (data: ImageObj[]) => dispatch(actions.refreshImages(data)); - const composeymlFiles = (data) => { - console.log("Magic47:", data) - return dispatch(actions.composeymlFiles(data)); - } - + // const composeymlFiles = (data) => dispatch(actions.composeymlFiles(data)); const getNetworkContainers = (data: NetworkObj[]) => dispatch(actions.getNetworkContainers(data)); const removeContainer = (id: string) => dispatch(actions.removeContainer(id)); - const runStoppedContainer = (data) => - { - console.log("Magic64:", data) - return dispatch(actions.runStoppedContainer(data)); - } + const runStoppedContainer = (id: string) => + dispatch(actions.runStoppedContainer(id)); const stopRunningContainer = (id: string) => dispatch(actions.stopRunningContainer(id)); const updateSession = () => @@ -66,36 +59,36 @@ const SysAdmin = () => { dispatch(actions.getVolumeContainersList(data)); // map state to props - const runningList = useSelector((state) => state.containersList.runningList); - const stoppedList = useSelector((state) => state.containersList.stoppedList); - const imagesList = useSelector((state) => state.images.imagesList); - const networkList = useSelector((state) => state.networkList.networkList); - const userInfo = useSelector((state) => state.session); //* Feature only for SysAdmin + const runningList = useSelector((state: StateType) => state.containersList.runningList); + const stoppedList = useSelector((state: StateType) => state.containersList.stoppedList); + const imagesList = useSelector((state: StateType) => state.images.imagesList); + // const networkList = useSelector((state: StateType) => state.networkList.networkList); + const userInfo = useSelector((state: StateType) => state.session); //* Feature only for SysAdmin const arrayOfVolumeNames = useSelector( - (state) => state.volumeList.arrayOfVolumeNames + (state: StateType) => state.volumeList.arrayOfVolumeNames ); const volumeContainersList = useSelector( - (state) => state.volumeList.volumeContainersList + (state: StateType) => state.volumeList.volumeContainersList ); // map state to props const phoneNumber = useSelector( - (state) => state.notificationList.phoneNumber + (state: StateType) => state.notificationList.phoneNumber ); const memoryNotificationList = useSelector( - (state) => state.notificationList.memoryNotificationList + (state: StateType) => state.notificationList.memoryNotificationList ); const cpuNotificationList = useSelector( - (state) => state.notificationList.cpuNotificationList + (state: StateType) => state.notificationList.cpuNotificationList ); const stoppedNotificationList = useSelector( - (state) => state.notificationList.stoppedNotificationList + (state: StateType) => state.notificationList.stoppedNotificationList ); // Local state for routers const [selected, setSelected] = useState('/'); - const handleLogout = (e) => { + const handleLogout = () => { updateSession(); logoutUser(); fetch('http://localhost:3000/logout', { @@ -191,7 +184,7 @@ const SysAdmin = () => {
  • setSelected('/app/')} > Settings @@ -200,7 +193,7 @@ const SysAdmin = () => {
  • setSelected('/app/users')} > Users @@ -209,7 +202,7 @@ const SysAdmin = () => {
  • setSelected(() => '/app/running')} > Containers @@ -218,7 +211,7 @@ const SysAdmin = () => {
  • setSelected('/app/images')} > Images @@ -227,7 +220,7 @@ const SysAdmin = () => {
  • setSelected('/app/metrics')} > Metrics @@ -236,7 +229,7 @@ const SysAdmin = () => {
  • setSelected('/app/yml')} > Docker Compose @@ -245,7 +238,7 @@ const SysAdmin = () => {
  • setSelected('/app/volume')} > Volume History @@ -254,7 +247,7 @@ const SysAdmin = () => {
  • setSelected('/app/logs')} > Process Logs @@ -270,7 +263,7 @@ const SysAdmin = () => { System Prune -
  • @@ -328,7 +321,6 @@ const SysAdmin = () => { stopRunningContainer={stopRunningContainer} runningList={runningList} // addRunningContainers={addRunningContainers} - imagesList={imagesList} // Stopped Containers runStopped={helper.runStopped} remove={helper.remove} diff --git a/src/components/views/viewsTypes.ts b/src/components/views/viewsTypes.ts index d3b3940a..831a96f7 100644 --- a/src/components/views/viewsTypes.ts +++ b/src/components/views/viewsTypes.ts @@ -65,4 +65,31 @@ export interface NetworkObj { export interface VolumeObj { vol_name: string, containers: object[] -} \ No newline at end of file +} + +interface containersList { + runningList: RunningListType[], + stoppedList: StoppedListType[] +} + +interface imagesList { + imagesList: any[] +} + +interface volumeList { + arrayOfVolumeNames: any[] + volumeContainersList: any[] +} + +interface notificationList { + phoneNumber: any[], + memoryNotificationList: any[], + cpuNotificationList: any[],stoppedNotificationList: any[], +} + +export interface StateType { + containersList: containersList, + images: imagesList, + volumeList: volumeList, + notificationList: notificationList +}; \ No newline at end of file From d5850ccb9ac7e3faad98036a61aa173acab9dd28 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Thu, 1 Dec 2022 01:09:21 -0600 Subject: [PATCH 059/110] fix gitData obInterface in LineChartDisplay Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/display/LineChartDisplay.tsx | 557 ++++++++++++++++++++ src/redux/actions/actions.ts | 92 ++-- 2 files changed, 603 insertions(+), 46 deletions(-) create mode 100644 src/components/display/LineChartDisplay.tsx diff --git a/src/components/display/LineChartDisplay.tsx b/src/components/display/LineChartDisplay.tsx new file mode 100644 index 00000000..8ae60b78 --- /dev/null +++ b/src/components/display/LineChartDisplay.tsx @@ -0,0 +1,557 @@ +/* eslint-disable react/prop-types */ +import React, { useState, useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { Line, Bar } from "react-chartjs-2"; +import * as actions from "../../redux/actions/actions"; +import * as helper from "../helper/commands"; +import { DataGrid } from "@mui/x-data-grid"; +import { FormControlLabel, Checkbox } from "@mui/material"; +import { RootState } from "../../renderer/store"; +import { AnyAction } from "redux"; + +/** + * Displays linegraph and github metrics + * + */ + +const LineChartDisplay = () => { + const [activeContainers, setActiveContainers] = useState({}); + const [gitUrls, setGitUrls] = useState([]); + const [timePeriod, setTimePeriod] = useState("4"); + const memory = useSelector((state: RootState) => state.graphs["graphMemory"]); + const cpu = useSelector((state: RootState) => state.graphs["graphCpu"]); + const writtenIO = useSelector( + (state: RootState) => state.graphs["graphWrittenIO"] + ); + const readIO = useSelector((state: RootState) => state.graphs["graphReadIO"]); + const axis = useSelector((state: RootState) => state.graphs["graphAxis"]); + const runningList = useSelector( + (state: RootState) => state.containersList.runningList + ); + const stoppedList = useSelector( + (state: RootState) => state.containersList.stoppedList + ); + + const dispatch = useDispatch(); + const buildAxis = (data: string) => dispatch(actions.buildAxis(data)); + const buildMemory = (data: string) => dispatch(actions.buildMemory(data)); + const buildCpu = (data: string) => dispatch(actions.buildCpu(data)); + const buildWrittenIO = (data: string) => + dispatch(actions.buildWrittenIO(data)); + const buildReadIO = (data: string) => dispatch(actions.buildReadIO(data)); + + //Grabbing the metrics data to be displayed on the charts + async function getContainerMetrics() { + const containerNamesArr = Object.keys(activeContainers); + const response = await fetch("http://localhost:3000/init/getMetrics", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + containers: containerNamesArr, + }), + }); + return await response.json(); + } + + // Auxilary Object which will be passed into Line component + const memoryObj = { + labels: axis, + datasets: memory, + }; + const cpuObj = { + labels: axis, + datasets: cpu, + }; + const writtenIOObj = { + labels: axis, + datasets: writtenIO, + }; + const readIOObj = { + labels: axis, + datasets: readIO, + }; + + /** + * Resets all graph data in global store + * Builds memory and cpu object for input into Line Components + */ + const formatData = async () => { + buildMemory("clear"); + buildCpu("clear"); + buildAxis("clear"); + buildWrittenIO("clear"); + buildReadIO("clear"); + // if active containers is empty render the empty graphs + if (!Object.keys(activeContainers).length) { + return; + } + + const output = await getContainerMetrics(); + + const generateLineColor = (containerName: string, activeContainers: {}) => { + const colorOptions = [ + "red", + "blue", + "green", + "purple", + "yellow", + "grey", + "orange", + ]; + + const idx = activeContainers.findIndex(containerName); + return colorOptions[idx]; + }; + // build function that will return formated object into necessary + // datastructure for chart.js line graphs + const buildLineGraphObj = (containerName: string) => { + const obj = { + label: containerName, + data: [], + lineTension: 0.5, + fill: false, + borderColor: generateLineColor( + containerName, + Object.keys(activeContainers) + ), + }; + return obj; + }; + // Datastructure for Bargraph + const buildBarGraphObj = (containerName: string) => { + const obj = { + label: containerName, + data: [], + fill: false, + backgroundColor: generateLineColor( + containerName, + Object.keys(activeContainers) + ), + }; + return obj; + }; + + buildMemory("clear"); + buildCpu("clear"); + buildAxis("clear"); + buildWrittenIO("clear"); + buildReadIO("clear"); + + if (!Object.keys(activeContainers).length) { + return; + } + + const containerMetrics = await getContainerMetrics(); + + interface auxObjType { + container?: ContainerInterface; + currentContainer?: any; + containerName?: string; + } + + interface ContainerInterface { + memory?: any; + cpu?: any; + writtenIO?: any; + readIO?: any; + } + + // interface ContainerNameInterface{ + + // } + + //const container: keyof auxObjType = 'container' + + const auxObj: auxObjType = {}; + //const container: keyof typeof auxObj; + Object.keys(activeContainers).forEach((container) => { + auxObj[container as keyof typeof auxObj] = { + memory: buildLineGraphObj(container), + cpu: buildLineGraphObj(container), + writtenIO: buildBarGraphObj(container), + readIO: buildBarGraphObj(container), + }; + }); + + // iterate through each row from fetch and build Memory, CPU, Written/Read Block_IO objects [{}, {}, {}, {}] + containerMetrics.rows.forEach((dataPoint: any) => { + const currentContainer = dataPoint.container_name; + const writtenReadIO = dataPoint.block_io.split("/"); + auxObj[currentContainer as keyof typeof auxObj].cpu.data.push( + dataPoint.cpu_pct.replace("%", "") + ); + auxObj[currentContainer as keyof typeof auxObj].memory.data.push( + dataPoint.memory_pct.replace("%", "") + ); + auxObj[currentContainer as keyof typeof auxObj].writtenIO.data.push( + parseFloat(writtenReadIO[0].replace(/([A-z])+/g, "")) + ); + auxObj[currentContainer as keyof typeof auxObj].readIO.data.push( + parseFloat(writtenReadIO[1].replace(/([A-z])+/g, "")) + ); + let date = ""; + let time = ""; + for (let i = 1; i < dataPoint.created_at.length; i++) { + if (dataPoint.created_at[i] === "T") { + break; + } else date += dataPoint.created_at[i]; + } + for (let i = 11; i < dataPoint.created_at.length; i++) { + if (dataPoint.created_at[i] === ".") { + break; + } else time += dataPoint.created_at[i]; + } + let timeStamp = `${date} @ ${time}`; + buildAxis(timeStamp); + }); + + let longest = 0; // 32 + + Object.keys(auxObj).forEach((containerName: string) => { + if ( + auxObj[containerName as keyof typeof auxObj].memory.data.length > + longest + ) { + longest = + auxObj[containerName as keyof typeof auxObj].memory.data.length; + } + }); + + // REFACTOR THIS BRUTE FORCE APROACH TO ADDING 0 DATAPOINTS TO ARRAY + Object.keys(auxObj).forEach((containerName: string) => { + if ( + auxObj[containerName as keyof typeof auxObj].memory.data.length < + longest + ) { + const lengthToAdd = + longest - + auxObj[containerName as keyof typeof auxObj].memory.data.length; + for (let i = 0; i < lengthToAdd; i += 1) { + auxObj[containerName as keyof typeof auxObj].memory.data.unshift( + "0.00" + ); + auxObj[containerName as keyof typeof auxObj].cpu.data.unshift("0.00"); + auxObj[containerName as keyof typeof auxObj].writtenIO.data.unshift( + "0.00" + ); + auxObj[containerName as keyof typeof auxObj].readIO.data.unshift( + "0.00" + ); + } + } + buildMemory(auxObj[containerName as keyof typeof auxObj].memory); + buildCpu(auxObj[containerName as keyof typeof auxObj].cpu); + buildWrittenIO(auxObj[containerName as keyof typeof auxObj].writtenIO); + buildReadIO(auxObj[containerName as keyof typeof auxObj].readIO); + }); + }; + + interface obType { + containerName?: any; + } + + //Fetching the data from github API and turning it into an object with keys of objects that contain the data of each container + const fetchGitData = async (containerName: string) => { + const ob: obType = {}; + ob[containerName as keyof typeof ob] = []; + const time = Number(timePeriod); + //pulling the current time, and then setting it back to one month ago to check for github commit logs (2629746000 = 1 month) + let date: Date = new Date( + Date.parse(new Date().toISOString()) - 2629746000 + ).toISOString(); + date.setHours(date.getHours() - time); + //date = date.toISOString(); + const urlObj = await helper.getContainerGitUrl(containerName); + + if (urlObj.rows.length) { + const url = + urlObj.rows[0].github_url + + new URLSearchParams({ + since: `${date}`, + }); + //need an actual url to test this, right now it can't connect + const data = await fetch(url); + const jsonData = await data.json(); + + jsonData.forEach((commitData: any) => { + ob[containerName as keyof typeof ob].push({ + time: commitData.commit.author.date, + url: commitData.html_url, + author: commitData.commit.author.name, + message: commitData.commit.message, + }); + }); + } else { + ob[containerName as keyof typeof ob].push({ + time: "", + url: "Connect github repo in settings", + }); + } + return ob; + }; + + const renderGitInfo = () => { + Promise.all( + Object.keys(activeContainers).map((container) => { + return fetchGitData(container); + }) + ).then((data: any) => setGitUrls(data)); + }; + //populating the github commits into a MUI DataGrid + //This should allow multiple tables be stacked if multiple containers are selected + let gitData; + + const columns = [ + { field: "date", headerName: "Date", width: 125 }, + { field: "time", headerName: "Time", width: 100 }, + { + field: "url", + headerName: "URL", + width: 175, + renderCell: (params: any) => ( + + {params.row.id} + + ), + }, + { field: "author", headerName: "Author", width: 175 }, + { field: "message", headerName: "Message", width: 525, align: "left" }, + ]; + gitData = gitUrls.map((el: {}, index: any) => { + type nameType = any[]; + const name: nameType = Object.keys(el); + type rowsType = any[]; + const rows: rowsType = []; + type columnsType = any[]; + const columns: columnsType = []; + el[name].forEach((ob: any, index: any) => { + let author = ""; + let date = "n/a"; + let time = "n/a"; + let url = "n/a"; + let message = "n/a"; + if (ob.time.length) { + time = ob.time; + author = ob.author; + url = ob.url; + message = ""; + if (ob.message) { + if (ob.message.includes("<")) { + for (let i = 0; i < ob.message.length; i++) { + if (ob.message[i] === "<") break; + message += ob.message[i]; + } + } else { + message = ob.message; + } + } + + time = time.split("T"); + date = time[0]; + time = time[1]; + time = time + .split("") + .slice(0, time.length - 1) + .join(""); + } + rows.push({ + date: date, + time: time, + url: url, + author: author, + message: message, + id: `Github Commit #${index}`, + }); + }); + return ( +
    +

    {name}

    +
    + "auto"} + initialState={{ + sorting: { + sortModel: [{ field: "date", sort: "asc" }], + }, + }} + /> +
    +
    + ); + }); + + let currentList; + const selectList = () => { + const result: result[] = []; + const completeContainerList = [...runningList, ...stoppedList]; + completeContainerList.forEach((container, index) => { + const containerNameKey = container.Name + ? container.Name + : container.Names; + result.push( + + } + label={containerNameKey} + /> + ); + }); + currentList = result; + }; + + const handleChange = (e: any) => { + if (e.target.type === "radio") { + setTimePeriod(e.target.value); + return; + } + const containerName = e.target.name; + // deep copy the state object + const copyObj = JSON.parse(JSON.stringify(activeContainers)); + if (activeContainers[containerName as keyof typeof activeContainers]) { + delete copyObj[containerName]; + } else { + copyObj[containerName] = true; + } + setActiveContainers(copyObj); + }; + + const cpuOptions = { + plugins: { + title: { + display: true, + text: "CPU", + font: { size: 18 }, + position: "top", + }, + tooltips: { enabled: true, mode: "index" }, + legend: { display: true, position: "bottom" }, + }, + responsive: true, + maintainAspectRatio: false, + }; + + const memoryOptions = { + plugins: { + title: { + display: true, + text: "MEMORY", + font: { size: 18 }, + position: "top", + }, + tooltips: { enabled: true, mode: "index" }, + legend: { display: true, position: "bottom" }, + }, + responsive: true, + maintainAspectRatio: false, + }; + + const writtenIOOptions = { + plugins: { + title: { + display: true, + text: "IO BYTES WRITTEN BY IMAGE", + font: { size: 18 }, + position: "top", + }, + tooltips: { enabled: true, mode: "index" }, + legend: { display: true, position: "bottom" }, + }, + responsive: true, + maintainAspectRatio: false, + }; + const readIOOptions = { + plugins: { + title: { + display: true, + text: "IO BYTES READ BY IMAGE", + font: { size: 18 }, + position: "top", + }, + tooltips: { enabled: true, mode: "index" }, + legend: { display: true, position: "bottom" }, + }, + responsive: true, + maintainAspectRatio: false, + }; + + selectList(); + useEffect(() => { + formatData(); + renderGitInfo(); + }, [activeContainers, timePeriod]); + + return ( +
    +
    +

    Over Time

    +
    +
    +
    { + handleChange(e); + }} + > + + + + + + +
    + {currentList} +
    +
    + +
    + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment +@ts-ignore */} + +
    + +
    + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment +@ts-ignore */} + +
    +
    + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment +@ts-ignore */} + +
    +
    + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment +@ts-ignore */} + +
    +
    +

    GitHub History

    +
    + {gitData} +
    + ); +}; + +export default LineChartDisplay; diff --git a/src/redux/actions/actions.ts b/src/redux/actions/actions.ts index 6f336104..f84859fb 100644 --- a/src/redux/actions/actions.ts +++ b/src/redux/actions/actions.ts @@ -1,169 +1,169 @@ -import * as types from '../constants/actionTypes'; +import * as types from "../constants/actionTypes"; export const addRunningContainers = (data: object[]) => ({ type: types.ADD_RUNNING_CONTAINERS, - payload: data + payload: data, }); export const removeContainer = (id: string) => ({ type: types.REMOVE_CONTAINER, - payload: id + payload: id, }); export const stopRunningContainer = (id: string) => ({ type: types.STOP_RUNNING_CONTAINER, - payload: id + payload: id, }); export const addStoppedContainers = (data: object[]) => ({ type: types.ADD_STOPPED_CONTAINERS, - payload: data + payload: data, }); export const runStoppedContainer = (id: string) => ({ type: types.RUN_STOPPED_CONTAINER, - payload: id + payload: id, }); export const getImages = (data: object[]) => ({ type: types.GET_IMAGES, - payload: data + payload: data, }); export const runImage = (id: string) => ({ type: types.RUN_IMAGE, - payload: id + payload: id, }); export const removeImage = (id: string) => ({ type: types.REMOVE_IMAGE, - payload: id + payload: id, }); export const refreshRunningContainers = (data: object[]) => ({ type: types.REFRESH_RUNNING_CONTAINERS, - payload: data + payload: data, }); export const refreshStoppedContainers = (data: object[]) => ({ type: types.REFRESH_STOPPED_CONTAINERS, - payload: data + payload: data, }); export const refreshImages = (data: object[]) => ({ type: types.REFRESH_IMAGES, - payload: data + payload: data, }); /* --------------- */ type composeymlFilesStr = { - type: string, - payload: object[] -} + type: string; + payload: object[]; +}; /* --------------- */ export const composeymlFiles = (data: composeymlFilesStr) => ({ type: types.COMPOSE_YML_FILES, - payload: data + payload: data, }); export const getNetworkContainers = (data: object[]) => ({ type: types.GET_NETWORK_CONTAINERS, - payload: data + payload: data, }); export const getContainerStacks = (data: object[]) => ({ type: types.GET_CONTAINER_STACKS, - payload: data + payload: data, }); export const composeDown = (data: object[]) => ({ type: types.COMPOSE_DOWN, - payload: data + payload: data, }); export const composeUp = (data: object[]) => ({ type: types.COMPOSE_UP, - payload: data + payload: data, }); -export const buildAxis = (data: object[]) => ({ +export const buildAxis = (data: string) => ({ type: types.BUILD_AXIS, - payload: data + payload: data, }); -export const buildMemory = (data: object[]) => ({ +export const buildMemory = (data: string) => ({ type: types.BUILD_MEMORY, - payload: data + payload: data, }); -export const buildCpu = (data: object[]) => ({ +export const buildCpu = (data: string) => ({ type: types.BUILD_CPU, - payload: data + payload: data, }); -export const buildWrittenIO = (data: object[]) => ({ +export const buildWrittenIO = (data: string) => ({ type: types.BUILD_WRITTEN_IO, - payload: data + payload: data, }); -export const buildReadIO = (data: object[]) => ({ +export const buildReadIO = (data: string) => ({ type: types.BUILD_READ_IO, - payload: data + payload: data, }); export const addPhoneNumber = (data: object[]) => ({ type: types.ADD_PHONE_NUMBER, - payload: data + payload: data, }); export const addMemoryNotificationSetting = (data: object[]) => ({ type: types.ADD_MEMORY_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const addCpuNotificationSetting = (data: object[]) => ({ type: types.ADD_CPU_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const addStoppedNotificationSetting = (data: object[]) => ({ type: types.ADD_STOPPED_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const removeMemoryNotificationSetting = (data: object[]) => ({ type: types.REMOVE_MEMORY_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const removeCpuNotificationSetting = (data: object[]) => ({ type: types.REMOVE_CPU_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const removeStoppedNotificationSetting = (data: object[]) => ({ type: types.REMOVE_STOPPED_NOTIFICATION_SETTING, - payload: data + payload: data, }); export const addNotificationFrequency = (data: object[]) => ({ type: types.NOTIFICATION_FREQUENCY, - payload: data + payload: data, }); export const addMonitoringFrequency = (data: object[]) => ({ type: types.MONITORING_FREQUENCY, - payload: data + payload: data, }); export const updateSession = () => ({ - type: types.UPDATE_SESSION + type: types.UPDATE_SESSION, }); export const updateUser = (data: object) => ({ type: types.UPDATE_USER, - payload: data + payload: data, }); export const logoutUser = () => ({ @@ -173,28 +173,28 @@ export const logoutUser = () => ({ export const updateUserList = (data: object[]) => ({ type: types.UPDATE_USER_LIST, - payload: data + payload: data, }); export const updateUserRole = (data: object[]) => ({ type: types.UPDATE_USER_ROLE, - payload: data + payload: data, }); // get volume export const getVolumeList = (data: object[]) => ({ type: types.GET_VOLUME_LIST, - payload: data + payload: data, }); // get containers that live in volume export const getVolumeContainersList = (data: object[]) => ({ type: types.GET_VOLUME_CONTAINERS_LIST, - payload: data + payload: data, }); // get container logs export const getContainerLogs = (data: object[]) => ({ type: types.GET_CONTAINER_LOGS, - payload: data + payload: data, }); From 83a0ccf828785af6be84669e22e0cb137e65134c Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Thu, 1 Dec 2022 02:45:50 -0600 Subject: [PATCH 060/110] convert LineChartDisplay file to typescript final Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/display/LineChartDisplay.tsx | 24 ++++++++++++++------- tsconfig.json | 8 +------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/components/display/LineChartDisplay.tsx b/src/components/display/LineChartDisplay.tsx index 8ae60b78..e790866b 100644 --- a/src/components/display/LineChartDisplay.tsx +++ b/src/components/display/LineChartDisplay.tsx @@ -101,7 +101,7 @@ const LineChartDisplay = () => { "orange", ]; - const idx = activeContainers.findIndex(containerName); + const idx = activeContainers.toString().indexOf(containerName); return colorOptions[idx]; }; // build function that will return formated object into necessary @@ -258,7 +258,8 @@ const LineChartDisplay = () => { ob[containerName as keyof typeof ob] = []; const time = Number(timePeriod); //pulling the current time, and then setting it back to one month ago to check for github commit logs (2629746000 = 1 month) - let date: Date = new Date( + + let date: any = new Date( Date.parse(new Date().toISOString()) - 2629746000 ).toISOString(); date.setHours(date.getHours() - time); @@ -319,13 +320,19 @@ const LineChartDisplay = () => { { field: "author", headerName: "Author", width: 175 }, { field: "message", headerName: "Message", width: 525, align: "left" }, ]; - gitData = gitUrls.map((el: {}, index: any) => { - type nameType = any[]; - const name: nameType = Object.keys(el); + + // interface elType { + // name: {}; + // } + + gitData = gitUrls.map((el, index: any) => { + const name = Object.keys(el); type rowsType = any[]; const rows: rowsType = []; type columnsType = any[]; const columns: columnsType = []; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore el[name].forEach((ob: any, index: any) => { let author = ""; let date = "n/a"; @@ -333,7 +340,7 @@ const LineChartDisplay = () => { let url = "n/a"; let message = "n/a"; if (ob.time.length) { - time = ob.time; + time = ob.time as string; author = ob.author; url = ob.url; message = ""; @@ -347,7 +354,8 @@ const LineChartDisplay = () => { message = ob.message; } } - + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore time = time.split("T"); date = time[0]; time = time[1]; @@ -387,7 +395,7 @@ const LineChartDisplay = () => { let currentList; const selectList = () => { - const result: result[] = []; + const result: any[] = []; const completeContainerList = [...runningList, ...stoppedList]; completeContainerList.forEach((container, index) => { const containerNameKey = container.Name diff --git a/tsconfig.json b/tsconfig.json index 2d1c34ed..dc01ed8a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,13 +4,7 @@ "module": "commonjs", "jsx": "react", "esModuleInterop": true, - "lib": [ - "dom", - "es2015", - "es2016", - "es2017", - "es2021" - ], + "lib": ["dom", "es2015", "es2016", "es2017", "es2021"], "allowJs": true, "sourceMap": true, "outDir": "./dist", From 653638187a186dc28cde8c8349984d2b4add7ef9 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Thu, 1 Dec 2022 08:11:24 -0800 Subject: [PATCH 061/110] small changes in Settings --- src/components/tabs/Settings.tsx | 2 +- src/components/tabs/TabTypes.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/tabs/Settings.tsx b/src/components/tabs/Settings.tsx index 5f14544f..7aade10b 100644 --- a/src/components/tabs/Settings.tsx +++ b/src/components/tabs/Settings.tsx @@ -235,7 +235,7 @@ const Settings = (props: SettingsProps) => { // VERIFICATION OF THE CODE TYPED IN BY USER FROM SMS const [formData, updateFormData] = useState(''); - const handleChange = (value) => { + const handleChange = (value: string) => { updateFormData(value); }; diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 5d3366e0..3daf9461 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -77,7 +77,7 @@ export type SettingsProps = { addCpuNotificationSetting: (data: object[]) => void; addStoppedNotificationSetting: (data: object[]) => void; addPhoneNumber: (data: object[]) => void; - addNotificationFrequency: (data: object[]) => void; + addNotificationFrequency: (data: string | number) => void; runningList: RunningListType; stoppedList: StoppedListType; memoryNotificationList: {}; From fa779afab3bf6578ef83775fbf122d655db0e334 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Thu, 1 Dec 2022 12:08:40 -0600 Subject: [PATCH 062/110] fixing final issues with typing Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/views/interface.ts | 5 -- src/components/views/viewsTypes.ts | 133 +++++++++++++++++------------ 2 files changed, 78 insertions(+), 60 deletions(-) delete mode 100644 src/components/views/interface.ts diff --git a/src/components/views/interface.ts b/src/components/views/interface.ts deleted file mode 100644 index 15604925..00000000 --- a/src/components/views/interface.ts +++ /dev/null @@ -1,5 +0,0 @@ -// this file contains the interfaces for each file. Please search a fil by name in order to find what interfaces it uses. - -interface Data { - -} \ No newline at end of file diff --git a/src/components/views/viewsTypes.ts b/src/components/views/viewsTypes.ts index 831a96f7..2ee7a9f7 100644 --- a/src/components/views/viewsTypes.ts +++ b/src/components/views/viewsTypes.ts @@ -1,75 +1,96 @@ -// this file contains the interfaces for each file. Please search a fil by name in order to find what interfaces it uses. +// this file contains the interfaces for each file in the views folder. +// for container's being run export interface ContainerObj { - BlockIO: string, - CPUPerc: string, - Container: string, - ID: string, - MemPerc: string, - MemUsage: string, - Name: string, - NetIO: string, - PIDs: string + BlockIO: string, + CPUPerc: string, + Container: string, + ID: string, + MemPerc: string, + MemUsage: string, + Name: string, + NetIO: string, + PIDs: string, + Image?: string, + RunningFor?: string } +// for container's being stopped export interface StoppedContainerObj { - Command: string, - CreatedAt: string, - ID: string, - Image: string, - Labels: string, - LocalVolumes: string, - Mounts: string, - Names: string, - Networks: string, - Ports: string, - RunningFor: string, - Size: string, - State: string, - Status: string + Command: string, + CreatedAt: string, + ID: string, + Image: string, + Labels: string, + LocalVolumes: string, + Mounts: string, + Names: string, + Networks: string, + Ports: string, + RunningFor: string, + Size: string, + State: string, + Status: string } export interface ImageObj { - imgid: string, - reps: string, - size: string, - tag: string + imgid: string, + reps: string, + size: string, + tag: string } export interface UserObj { - contact_pref: null | string, - container_stops: true | false, - cpu_threshold: number, - email: string, - mem_threshold: number, - password: string, - phone: string - role: string, - role_id: number, - token: string, - username: string, + contact_pref: null | string, + container_stops: true | false, + cpu_threshold: number, + email: string, + mem_threshold: number, + password: string, + phone: string + role: string, + role_id: number, + token: string, + username: string, _id: number } export interface NetworkObj { - CreatedAt: string, - Driver: string, - ID: string, - IPv6: string, - Internal: string, - Labels: string, - Name: string, - Scope: string + CreatedAt: string, + Driver: string, + ID: string, + IPv6: string, + Internal: string, + Labels: string, + Name: string, + Scope: string } export interface VolumeObj { - vol_name: string, - containers: object[] + vol_name: string, + containers: object[] } +interface session { + _id: string, + username: string, + email: string, + phone: string, + role: string, + role_id: string, + contact_pref: string, + mem_threshold: string, + cpu_threshold: string, + container_stops: string, + token: string, + isLoggedIn: boolean, + userList: any[] +} + +// "any" has been used below since strict typing was used to define these props in the tabs file interface containersList { - runningList: RunningListType[], - stoppedList: StoppedListType[] + runningList: any[], + stoppedList: any[] } interface imagesList { @@ -82,14 +103,16 @@ interface volumeList { } interface notificationList { - phoneNumber: any[], + phoneNumber: string[], memoryNotificationList: any[], - cpuNotificationList: any[],stoppedNotificationList: any[], + cpuNotificationList: any[], + stoppedNotificationList: any[], } export interface StateType { containersList: containersList, images: imagesList, - volumeList: volumeList, - notificationList: notificationList + notificationList: notificationList, + session: session, + volumeList: volumeList }; \ No newline at end of file From 55e826acf4951be8a01505014b37872916ae1d01 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Thu, 1 Dec 2022 12:27:47 -0600 Subject: [PATCH 063/110] converted UserView to Typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/views/Admin.tsx | 16 ++-- src/components/views/SysAdmin.tsx | 2 + .../views/{UserView.js => UserView.tsx} | 76 +++++++++---------- 3 files changed, 48 insertions(+), 46 deletions(-) rename src/components/views/{UserView.js => UserView.tsx} (80%) diff --git a/src/components/views/Admin.tsx b/src/components/views/Admin.tsx index eb262149..57492cfb 100644 --- a/src/components/views/Admin.tsx +++ b/src/components/views/Admin.tsx @@ -25,22 +25,22 @@ import startNotificationRequester from '../helper/notificationsRequester'; import initDatabase from '../helper/initDatabase'; // Types and Interface -import { ContainerObj, StoppedContainerObj, ImageObj, UserObj, VolumeObj, NetworkObj, StateType } from "./viewsTypes"; +import { ContainerObj, StoppedContainerObj, ImageObj, VolumeObj, NetworkObj, StateType } from "./viewsTypes"; // Container component that has all redux logic along with react router const AdminView = () => { const navigate = useNavigate(); const dispatch = useDispatch(); - const addRunningContainers = (data: object[]) => + const addRunningContainers = (data: ContainerObj[]) => dispatch(actions.addRunningContainers(data)); - const refreshRunningContainers = (data: object[]) => + const refreshRunningContainers = (data: ContainerObj[]) => dispatch(actions.refreshRunningContainers(data)); - const refreshStoppedContainers = (data: object[]) => + const refreshStoppedContainers = (data: StoppedContainerObj[]) => dispatch(actions.refreshStoppedContainers(data)); - const refreshImagesList = (data: object[]) => dispatch(actions.refreshImages(data)); + const refreshImagesList = (data: ImageObj[]) => dispatch(actions.refreshImages(data)); // const composeymlFiles = (data) => dispatch(actions.composeymlFiles(data)); - const getNetworkContainers = (data: object[]) => + const getNetworkContainers = (data: NetworkObj[]) => dispatch(actions.getNetworkContainers(data)); const removeContainer = (id: string) => dispatch(actions.removeContainer(id)); // this parameter was changed from data: object[] because in the actions file, an id argument was being requested @@ -51,8 +51,8 @@ const AdminView = () => { const updateSession = () => dispatch(actions.updateSession()); // originally, this function have any parameters, but typescript through an error saying it was needed. Check this out later const logoutUser = () => dispatch(actions.logoutUser()); - const getVolumeList = (data: object[]) => dispatch(actions.getVolumeList(data)); - const getVolumeContainersList = (data: object[]) => dispatch(actions.getVolumeContainersList(data)); + const getVolumeList = (data: { Name: string }[]) => dispatch(actions.getVolumeList(data)); + const getVolumeContainersList = (data: VolumeObj[]) => dispatch(actions.getVolumeContainersList(data)); // map state to props const runningList = useSelector((state: StateType) => state.containersList.runningList); diff --git a/src/components/views/SysAdmin.tsx b/src/components/views/SysAdmin.tsx index ca638d38..6f6a9177 100644 --- a/src/components/views/SysAdmin.tsx +++ b/src/components/views/SysAdmin.tsx @@ -24,6 +24,8 @@ import ProcessLogsTable from '../display/ProcessLogsTable'; // helper function imports import startNotificationRequester from '../helper/notificationsRequester'; import initDatabase from '../helper/initDatabase'; + +// Types and Interface import { ContainerObj, StoppedContainerObj, ImageObj, UserObj, VolumeObj, NetworkObj, StateType } from "./viewsTypes"; // Container component that has all redux logic along with react router diff --git a/src/components/views/UserView.js b/src/components/views/UserView.tsx similarity index 80% rename from src/components/views/UserView.js rename to src/components/views/UserView.tsx index 52a68107..9dc48570 100644 --- a/src/components/views/UserView.js +++ b/src/components/views/UserView.tsx @@ -7,6 +7,7 @@ import { Routes, Route, Link, useNavigate } from 'react-router-dom'; import * as actions from '../../redux/actions/actions'; import * as helper from '../helper/commands'; import * as history from '../helper/volumeHistoryHelper'; +// @ts-ignore import Docketeer from '../../../assets/docketeer-title.png'; // tab component imports @@ -23,61 +24,60 @@ import ProcessLogsTable from '../display/ProcessLogsTable'; import startNotificationRequester from '../helper/notificationsRequester'; import initDatabase from '../helper/initDatabase'; +// Types and Interface +import { ContainerObj, StoppedContainerObj, ImageObj, UserObj, VolumeObj, NetworkObj, StateType } from "./viewsTypes"; + // Container component that has all redux logic along with react router -const UserView = (props) => { +const UserView = () => { const navigate = useNavigate(); const dispatch = useDispatch(); - const addRunningContainers = (data) => + const addRunningContainers = (data: ContainerObj[]) => dispatch(actions.addRunningContainers(data)); - const refreshRunningContainers = (data) => + const refreshRunningContainers = (data: ContainerObj[]) => dispatch(actions.refreshRunningContainers(data)); - const refreshStoppedContainers = (data) => + const refreshStoppedContainers = (data: StoppedContainerObj[]) => dispatch(actions.refreshStoppedContainers(data)); - const refreshImagesList = (data) => dispatch(actions.refreshImages(data)); - const composeymlFiles = (data) => dispatch(actions.composeymlFiles(data)); - const getNetworkContainers = (data) => + const refreshImagesList = (data: ImageObj[]) => dispatch(actions.refreshImages(data)); + // const composeymlFiles = (data) => dispatch(actions.composeymlFiles(data)); + const getNetworkContainers = (data: NetworkObj[]) => dispatch(actions.getNetworkContainers(data)); - const removeContainer = (id) => dispatch(actions.removeContainer(id)); - const runStoppedContainer = (data) => - dispatch(actions.runStoppedContainer(data)); - const stopRunningContainer = (id) => + const removeContainer = (id: string) => dispatch(actions.removeContainer(id)); + const runStoppedContainer = (id: string) => + dispatch(actions.runStoppedContainer(id)); + const stopRunningContainer = (id: string) => dispatch(actions.stopRunningContainer(id)); const updateSession = () => dispatch(actions.updateSession()); const logoutUser = () => dispatch(actions.logoutUser()); - const getVolumeList = (data) => dispatch(actions.getVolumeList(data)); - const getVolumeContainersList = (data) => + const getVolumeList = (data: { Name: string }[]) => dispatch(actions.getVolumeList(data)); + const getVolumeContainersList = (data: VolumeObj[]) => dispatch(actions.getVolumeContainersList(data)); // map state to props - const runningList = useSelector((state) => state.containersList.runningList); - const stoppedList = useSelector((state) => state.containersList.stoppedList); - const imagesList = useSelector((state) => state.images.imagesList); - const networkList = useSelector((state) => state.networkList.networkList); - const arrayOfVolumeNames = useSelector( - (state) => state.volumeList.arrayOfVolumeNames - ); - const volumeContainersList = useSelector( - (state) => state.volumeList.volumeContainersList - ); + const runningList = useSelector((state: StateType) => state.containersList.runningList); + const stoppedList = useSelector((state: StateType) => state.containersList.stoppedList); + const imagesList = useSelector((state: StateType) => state.images.imagesList); + // const networkList = useSelector((state: StateType) => state.networkList.networkList); + const arrayOfVolumeNames = useSelector((state: StateType) => state.volumeList.arrayOfVolumeNames); + const volumeContainersList = useSelector((state: StateType) => state.volumeList.volumeContainersList); // map state to props const phoneNumber = useSelector( - (state) => state.notificationList.phoneNumber + (state: StateType) => state.notificationList.phoneNumber ); const memoryNotificationList = useSelector( - (state) => state.notificationList.memoryNotificationList + (state: StateType) => state.notificationList.memoryNotificationList ); const cpuNotificationList = useSelector( - (state) => state.notificationList.cpuNotificationList + (state: StateType) => state.notificationList.cpuNotificationList ); const stoppedNotificationList = useSelector( - (state) => state.notificationList.stoppedNotificationList + (state: StateType) => state.notificationList.stoppedNotificationList ); // declare a local state variable called selected, initialize to "/" const [selected, setSelected] = useState('/'); - const handleLogout = (e) => { + const handleLogout = () => { updateSession(); logoutUser(); navigate('/login'); @@ -132,7 +132,7 @@ const UserView = (props) => {
  • setSelected('/app/')} > Settings @@ -144,7 +144,7 @@ const UserView = (props) => { style={ selected === '/app/running' ? selectedStyling - : null + : undefined } onClick={() => setSelected(() => '/app/running')} > @@ -155,7 +155,7 @@ const UserView = (props) => { setSelected('/app/images')} > @@ -168,7 +168,7 @@ const UserView = (props) => { style={ selected === '/app/metrics' ? selectedStyling - : null + : undefined } onClick={() => setSelected('/app/metrics')} > @@ -179,7 +179,7 @@ const UserView = (props) => { setSelected('/app/yml')} > @@ -190,7 +190,7 @@ const UserView = (props) => { setSelected('/app/volume')} > @@ -201,7 +201,7 @@ const UserView = (props) => { setSelected('/app/logs')} > @@ -218,7 +218,7 @@ const UserView = (props) => { System Prune -
  • @@ -269,8 +269,8 @@ const UserView = (props) => { path='/yml' element={ } /> From 7bed35362779afd737c9412b47baedf860d5efc1 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Thu, 1 Dec 2022 12:46:41 -0600 Subject: [PATCH 064/110] newUserHelper converted to ts Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- server/app.js | 2 +- .../{newUserHelper.js => newUserHelper.ts} | 48 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) rename src/components/helper/{newUserHelper.js => newUserHelper.ts} (63%) diff --git a/server/app.js b/server/app.js index e7a34396..821ddc66 100644 --- a/server/app.js +++ b/server/app.js @@ -42,7 +42,7 @@ app.use('/', (req, res) => { const url = new URL(`${req.protocol}://${req.get('host')}${req.originalUrl}`); console.log('1',url); console.log(req.protocol, req.hostName, req.get('Host'), req.originalUrl, req.route); - + // return res.status(404).redirect('/app'); return res.status(404).json('404 Not Found'); }); diff --git a/src/components/helper/newUserHelper.js b/src/components/helper/newUserHelper.ts similarity index 63% rename from src/components/helper/newUserHelper.js rename to src/components/helper/newUserHelper.ts index 1d8649ed..89cd0d50 100644 --- a/src/components/helper/newUserHelper.js +++ b/src/components/helper/newUserHelper.ts @@ -5,16 +5,16 @@ import store from '../../renderer/store'; import * as actions from '../../redux/actions/actions'; -export const handleNewUser = (e) => { +export const handleNewUser = (e: Event) => { e.preventDefault(); - const username = document.getElementById('signupUsername').value; - const password = document.getElementById('signupPassword').value; - const confirmationPassword = document.getElementById( + const username = (document.getElementById('signupUsername')).value; + const password = (document.getElementById('signupPassword')).value; + const confirmationPassword = (document.getElementById( 'signupPasswordConfirmation' - ).value; - const email = document.getElementById('signupEmail').value; - const phone = document.getElementById('signupPhone').value; + )).value; + const email = (document.getElementById('signupEmail')).value; + const phone = (document.getElementById('signupPhone')).value; if (!checkPasswordLength()) { window.alert('Warning: Password must be 6 characters or longer'); @@ -34,13 +34,13 @@ export const handleNewUser = (e) => { }; export const confirmPassword = () => { - const password = document.getElementById('signupPassword').value; - const confirmationPassword = document.getElementById( + const password = (document.getElementById('signupPassword')).value; + const confirmationPassword = (document.getElementById( 'signupPasswordConfirmation' - ).value; + )).value; const passwordConfirmationAlert = document.getElementById( 'password-confirmation-alert' - ); + ) as HTMLInputElement; if (password !== confirmationPassword) { passwordConfirmationAlert.innerHTML = 'Warning: Passwords do not match'; @@ -51,8 +51,8 @@ export const confirmPassword = () => { }; export const checkPasswordLength = () => { - const passwordLengthAlert = document.getElementById('password-length-alert'); - const password = document.getElementById('signupPassword').value; + const passwordLengthAlert = (document.getElementById('password-length-alert')); + const password = (document.getElementById('signupPassword')).value; const regex = /^(?=[a-z\d]{6,}$)(?=\d*[a-z])[a-z]*\d[a-z\d]*$/; if (!regex.test(password)) { @@ -64,9 +64,9 @@ export const checkPasswordLength = () => { return password.length >= 6; }; -export const checkPhone = (phone) => { +export const checkPhone = (phone: string) => { const regex = /[+][1][\d]{10}$/; - const phoneAlert = document.getElementById('phone-alert'); + const phoneAlert = document.getElementById('phone-alert') as HTMLInputElement; if (phone.match(regex) === null) { phoneAlert.innerHTML = 'Warning: Please enter valid phone number with country code (+1).\nExample: 12345678900'; @@ -76,7 +76,7 @@ export const checkPhone = (phone) => { return phone.match(regex) !== null; }; -export const createNewUser = (email, username, password, phone) => { +export const createNewUser = (email: string, username: string, password: string, phone: string) => { fetch('http://localhost:3000/signup', { method: 'POST', headers: { @@ -90,13 +90,13 @@ export const createNewUser = (email, username, password, phone) => { }) }) .then(() => { - document.getElementById('signupUsername').value = ''; - document.getElementById('signupPassword').value = ''; - document.getElementById('signupPasswordConfirmation').value = ''; - document.getElementById('signupEmail').value = ''; - document.getElementById('signupPhone').value = ''; - document.getElementById('password-length-alert').innerHTML = ''; - document.getElementById('password-confirmation-alert').innerHTML = ''; + (document.getElementById('signupUsername')).value = ''; + (document.getElementById('signupPassword')).value = ''; + (document.getElementById('signupPasswordConfirmation')).value = ''; + (document.getElementById('signupEmail')).value = ''; + (document.getElementById('signupPhone')).value = ''; + (document.getElementById('password-length-alert')).innerHTML = ''; + (document.getElementById('password-confirmation-alert')).innerHTML = ''; window.alert(`New user has been successfully created. \n\n An email with the user's credentials and login instructions has been sent to ${email}`); @@ -131,6 +131,6 @@ export const getUpdatedUserList = () => { }); }; -export const updateUserList = (data) => { +export const updateUserList = (data: object[]) => { store.dispatch(actions.updateUserList(data)); }; From f3ce8a5dc557c375532af867ce3b2290774fcb51 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Thu, 1 Dec 2022 12:48:13 -0600 Subject: [PATCH 065/110] quick minor changes Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- src/components/helper/newUserHelper.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/helper/newUserHelper.ts b/src/components/helper/newUserHelper.ts index 89cd0d50..76fdff25 100644 --- a/src/components/helper/newUserHelper.ts +++ b/src/components/helper/newUserHelper.ts @@ -38,9 +38,9 @@ export const confirmPassword = () => { const confirmationPassword = (document.getElementById( 'signupPasswordConfirmation' )).value; - const passwordConfirmationAlert = document.getElementById( + const passwordConfirmationAlert = (document.getElementById( 'password-confirmation-alert' - ) as HTMLInputElement; + )); if (password !== confirmationPassword) { passwordConfirmationAlert.innerHTML = 'Warning: Passwords do not match'; @@ -51,7 +51,7 @@ export const confirmPassword = () => { }; export const checkPasswordLength = () => { - const passwordLengthAlert = (document.getElementById('password-length-alert')); + const passwordLengthAlert = (document.getElementById('password-length-alert')); const password = (document.getElementById('signupPassword')).value; const regex = /^(?=[a-z\d]{6,}$)(?=\d*[a-z])[a-z]*\d[a-z\d]*$/; From bb6ad0e0d50eb66b3b4421416082394d74bb917a Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Thu, 1 Dec 2022 13:30:02 -0800 Subject: [PATCH 066/110] Settings Tab and TabTypes --- src/components/tabs/Settings.tsx | 29 +++++++++++------ src/components/tabs/TabTypes.ts | 54 +++++++++++++++++++++++--------- 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/components/tabs/Settings.tsx b/src/components/tabs/Settings.tsx index 7aade10b..32676232 100644 --- a/src/components/tabs/Settings.tsx +++ b/src/components/tabs/Settings.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'; import { connect, useSelector, useDispatch } from 'react-redux'; import * as actions from '../../redux/actions/actions'; import * as categories from '../../redux/constants/notificationCategories'; -import { DispatchType, SettingsProps } from './TabTypes'; +import { DispatchType, SettingsProps, WindowType, RunningContainerType, ContainerType } from './TabTypes'; // React Component Imports @@ -121,6 +121,8 @@ const Settings = (props: SettingsProps) => { /** * @title COMMUNICATION */ + // have to declare window for TypeScript compatibility + let window: WindowType; const verifyMobileNumber = async () => { await window.nodeMethod.rendInvoke('verify-number', mobileNumber); @@ -157,7 +159,8 @@ const Settings = (props: SettingsProps) => { props.addPhoneNumber(mobileNumber); showVerificationInput = true; verifyMobileNumber(); - document.getElementById('textfield').value = ''; + let field = document.getElementById('textfield'); + if (field) (field as HTMLInputElement).value = ''; }) .catch((err) => { console.log('handlePhoneNumberSubmit: ', err); @@ -262,7 +265,8 @@ const Settings = (props: SettingsProps) => { */ // general function to check if a container is in a notification setting list - const isSelected = (set, containerId: string) => set.has(containerId); + // the below set is typed as any due to continued changing parameters + const isSelected = (set: any, containerId: string) => set.has(containerId); const allContainersList = props.runningList.concat(props.stoppedList); // INSTEAD OF CREATING A NEW STATE IN THE REDUCER CONCATENATED 2 ALREADY EXISTING STATES @@ -317,7 +321,8 @@ const Settings = (props: SettingsProps) => { }) .then((data) => data.json()) .then((response) => { - document.getElementById('gittext').value = ''; + let field = document.getElementById('gittext'); + if (field) (field as HTMLInputElement).value = ''; return response; }) .catch((err) => { @@ -370,10 +375,11 @@ const Settings = (props: SettingsProps) => { }; const handleCpuChange = (event) => { - setCpuThreshold(document.getElementById('cpu-threshold-input').value); + let field = document.getElementById('cpu-threshold-input'); + if (field) setCpuThreshold((field as HTMLInputElement).value); }; - const handleCpuSubmit = (value) => { + const handleCpuSubmit = (value: string) => { fetch('http://localhost:3000/account/cpu', { method: 'POST', headers: { @@ -442,16 +448,19 @@ const Settings = (props: SettingsProps) => { }; const handleMemChange = (event) => { - setMemThreshold(document.getElementById('mem-threshold-input').value); + let field = document.getElementById('mem-threshold-input'); + if (field) setMemThreshold((field as HTMLInputElement).value); }; const handleStoppedContainersChange = (event) => { - setStoppedContainers( - document.getElementById('stopped-containers-input').checked + let ele = document.getElementById('stopped-containers-input'); + if (ele) setStoppedContainers( + //let ele = document.getElementById('stopped-containers-input'); + (ele as HTMLInputElement).checked ); }; - const renderAllContainersList = allContainersList.map((container, i) => { + const renderAllContainersList = allContainersList.map((container: RunningContainerType, i: number) => { const isMemorySelected = isSelected( props.memoryNotificationList, container.ID diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 0277c599..0c920510 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -1,7 +1,7 @@ // Refer to the Settings Tab for more information on stoppedList and runningList export interface StoppedListType { - Names: string, + Names?: string, ID: string, Image: string, RunningFor: string, @@ -11,31 +11,32 @@ export interface StoppedListType { CPUPerc: string, MemPerc: string, } + +//BlockIO, MemUsage, Name, NetIO, PIDs export interface RunningListType { - block: string, + BlockIO?: string, ID: string, CPUPerc: string, MemPerc: string, - MemUsage: string, - Name: string, - NetIO: string, - PIDs: string, + MemUsage?: string, + Name?: string, + NetIO?: string, + PIDs?: string, Image: string, RunningFor: string, } // for more info review actions.ts file and Settings.ts export type ContainerProps = { - stoppedList: Array; + stoppedList: StoppedListType[]; runStopped: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; runStoppedContainer: (id: string) => void; removeContainer: (id: string) => void; stopRunningContainer: (id: string) => void; remove: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; stop: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; - runningList: Array; + runningList: RunningListType[]; runIm: (id: string, runningList: RunningListType, callback_1: () => void, callback_2: () => void) => void; - // addRunningContainers: (data: object[]) => void; } // Stopped containers have a Names key and running containers have a Name key @@ -47,8 +48,18 @@ export type ContainerProps = { RunningFor: string; CPUPerc: string; MemPerc: string; - } + + export type RunningContainerType = { + Name: string; + Names?: string; + ID: string; + Image: string; + RunningFor: string; + CPUPerc: string; + MemPerc: string; + } + // uneeded at this point export type ChartInfoType = { @@ -67,19 +78,32 @@ export type ContainerProps = { barPercentage: number; } -export type DispatchType = { + // dont judge me, im coming back for you +export type DispatchType = (...args: any[]) => void; + +export type WindowType = { + nodeMethod: NodeMethodType; +} + +type NodeMethodType = { + rendInvoke: (arg1: string, arg2: string | RendInvokebody) => Promise; +} -}; +type RendInvokebody = { + code: string, + mobileNumber: string +} export type SettingsProps = { addMonitoringFrequency: (data: string | number) => void; addMemoryNotificationSetting: (data: object[]) => void; addCpuNotificationSetting: (data: object[]) => void; addStoppedNotificationSetting: (data: object[]) => void; - addPhoneNumber: (data: object[]) => void; + addPhoneNumber: (data: object[] | string) => void; addNotificationFrequency: (data: string | number) => void; - runningList: RunningListType; - stoppedList: StoppedListType; + runningList: RunningListType[]; + stoppedList: StoppedListType[]; + // fixing the below memoryNotificationList: {}; cpuNotificationList: {}; stoppedNotificationList: {}; From ca353f5cbf481ec92ba684423b3ea9f55d1b9f58 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Fri, 2 Dec 2022 09:53:56 -0600 Subject: [PATCH 067/110] convert NewUserDisplay file to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/display/NewUserDisplay.js | 113 --------------------- src/components/display/NewUserDisplay.tsx | 116 ++++++++++++++++++++++ 2 files changed, 116 insertions(+), 113 deletions(-) delete mode 100644 src/components/display/NewUserDisplay.js create mode 100644 src/components/display/NewUserDisplay.tsx diff --git a/src/components/display/NewUserDisplay.js b/src/components/display/NewUserDisplay.js deleted file mode 100644 index 26bf7ab9..00000000 --- a/src/components/display/NewUserDisplay.js +++ /dev/null @@ -1,113 +0,0 @@ -/** - * @module NewUserDisplay - * @description Signup component that will be rendered in SysAdmin view, in Users tab, where sysadmin can create an account for new user in organization - */ -import React, { useEffect, useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; - -// Material UI Imports -import TextField from '@mui/material/TextField'; -import Button from '@mui/material/Button'; - -import { - handleNewUser, - checkPasswordLength, - confirmPassword, - checkPhone, -} from '../helper/newUserHelper'; - - -const NewUserDisplay = () => { - return ( -
    -
    -
    -

    Create a New User

    -
    -

    - Create a new Docketeer account for an employee. Please confirm with the employee that their information is accurate before submitting. -

    -
    -

    - Note: For the password, please choose a random string of 6 characters, - numbers, and symbols. Upon account creation, the user will receive an - email with credentials and be able to update their password when - logging in. -

    -
    -
    - -
    - -
    - checkPasswordLength()} - sx={{ - m: 1 - }} - /> - -
    - confirmPassword()} - sx={{ - m: 1 - }} - /> - -
    - { - checkPhone(document.getElementById('signupPhone').value); - }} - sx={{ - m: 1 - }} - /> -
    - -
    - - -
    -
    - ); -}; - -export default NewUserDisplay; - diff --git a/src/components/display/NewUserDisplay.tsx b/src/components/display/NewUserDisplay.tsx new file mode 100644 index 00000000..b9312669 --- /dev/null +++ b/src/components/display/NewUserDisplay.tsx @@ -0,0 +1,116 @@ +/** + * @module NewUserDisplay + * @description Signup component that will be rendered in SysAdmin view, in Users tab, where sysadmin can create an account for new user in organization + */ +import React, { useEffect, useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; + +// Material UI Imports +import TextField from "@mui/material/TextField"; +import Button from "@mui/material/Button"; + +import { + handleNewUser, + checkPasswordLength, + confirmPassword, + checkPhone, +} from "../helper/newUserHelper"; + +const NewUserDisplay = () => { + return ( +
    +
    +
    +

    Create a New User

    +
    +

    + Create a new Docketeer account for an employee. Please confirm with + the employee that their information is accurate before submitting. +

    +
    +

    + Note: For the password, please choose a random string of 6 characters, + numbers, and symbols. Upon account creation, the user will receive an + email with credentials and be able to update their password when + logging in. +

    +
    +
    + +
    + +
    + checkPasswordLength()} + sx={{ + m: 1, + }} + /> + +
    + confirmPassword()} + sx={{ + m: 1, + }} + /> + +
    + { + //fixing the property 'value' does not exist on type 'HTMLElement' + const inputValue = ( + document.getElementById("signupPhone") as HTMLTextAreaElement + ).value; + checkPhone(inputValue); + }} + sx={{ + m: 1, + }} + /> +
    + +
    + + +
    +
    + ); +}; + +export default NewUserDisplay; From 2c765e61a730b46e713b5ce937f1a6580cc19198 Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 2 Dec 2022 11:42:55 -0500 Subject: [PATCH 068/110] change images/runIm Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- server/app.js | 7 +++---- src/components/helper/commands.js | 11 +++++++++-- src/components/tabs/Images.js | 4 +++- src/main/preload.ts | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/server/app.js b/server/app.js index e7a34396..de1018a6 100644 --- a/server/app.js +++ b/server/app.js @@ -38,12 +38,11 @@ app.use('/logout', logoutRouter); // Unknown Endpoint Error Handler app.use('/', (req, res) => { - console.log('why does changing react lead here'); const url = new URL(`${req.protocol}://${req.get('host')}${req.originalUrl}`); console.log('1',url); - console.log(req.protocol, req.hostName, req.get('Host'), req.originalUrl, req.route); - - return res.status(404).json('404 Not Found'); + // for development purposes, so we don't have to reopen electron everytime + return res.status(404).redirect('/'); + // return res.status(404).json('404 Not Found') }); // Global Error Handler diff --git a/src/components/helper/commands.js b/src/components/helper/commands.js index e7f2b961..20669924 100644 --- a/src/components/helper/commands.js +++ b/src/components/helper/commands.js @@ -27,11 +27,14 @@ export const addRunning = (runningList, callback) => { return; } // trim whitespace at end out stdout, slice to remove trailing comma and remove spaces + const dockerOutput = `[${stdout .trim() .slice(0, -1) .replaceAll(' ', '')}]`; + const convertedValue = JSON.parse(dockerOutput); + const newList = []; for (let i = 0; i < convertedValue.length; i++) { @@ -44,6 +47,7 @@ export const addRunning = (runningList, callback) => { } isInTheList ? '' : newList.push(convertedValue[i]); } + console.log('addrunning newlist', newList); newList.length ? callback(newList) : ''; } ); @@ -228,9 +232,11 @@ export const runStopped = ( */ // this function is used to run an image from the image tab -export const runIm = (id, runningList, callback_1, callback_2) => { +export const runIm = (container, runningList, callback_1, callback_2) => { // props.runIm(ele['imgid'], props.runningList, helper.addRunning, props.addRunningContainers) - window.nodeMethod.runExec(`docker run ${id}`, (error, stdout, stderr) => { + const {imgid, reps, tag} = container; + console.log(container); + window.nodeMethod.runExec(`docker run --name ${reps}-${tag} ${reps}:${tag}`, (error, stdout, stderr) => { if (error) { alert(`${error.message}`); return; @@ -241,6 +247,7 @@ export const runIm = (id, runningList, callback_1, callback_2) => { } }); callback_1(runningList, callback_2); + alert('Running container'); }; /** diff --git a/src/components/tabs/Images.js b/src/components/tabs/Images.js index f93c78c3..92680d3f 100644 --- a/src/components/tabs/Images.js +++ b/src/components/tabs/Images.js @@ -11,6 +11,7 @@ const Images = (props) => { const [repo, setRepo] = useState(''); const handleClick = (e) => { + console.log(props.imagesList); if (!repo) { alert('Please enter an image to pull!'); return; @@ -77,9 +78,10 @@ const Images = (props) => {
    @@ -741,7 +742,7 @@ const Settings = (props: SettingsProps) => { }} size='medium' variant='contained' - onClick={(e) => monitoringFrequency(e)} + onClick={() => monitoringFrequency()} > Confirm From 367c07928f5eb4f4223cde3016f84a63424c7085 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Fri, 2 Dec 2022 11:05:58 -0600 Subject: [PATCH 070/110] convert ProcessLogCard file to typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- ...ProcessLogsCard.js => ProcessLogsCard.tsx} | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) rename src/components/display/{ProcessLogsCard.js => ProcessLogsCard.tsx} (52%) diff --git a/src/components/display/ProcessLogsCard.js b/src/components/display/ProcessLogsCard.tsx similarity index 52% rename from src/components/display/ProcessLogsCard.js rename to src/components/display/ProcessLogsCard.tsx index f2f2bba8..55c3f362 100644 --- a/src/components/display/ProcessLogsCard.js +++ b/src/components/display/ProcessLogsCard.tsx @@ -1,24 +1,36 @@ /** * @module ProcessLogsCard - * @description Process Logs box to display information of a Docker Container. This componenet will get cloned - * multiple times for each running and not running container. - * Note: Within the box-label div, the h3 tag contains a conditional statement. This is due to inconsistent - * naming of states for both running and not running container lists. + * @description Process Logs box to display information of a Docker Container. This componenet will get cloned + * multiple times for each running and not running container. + * Note: Within the box-label div, the h3 tag contains a conditional statement. This is due to inconsistent + * naming of states for both running and not running container lists. */ +import React from "react"; +import { useNavigate } from "react-router-dom"; +import PropTypes from "prop-types"; -import React from 'react'; -import { useNavigate } from 'react-router-dom' -import PropTypes from 'prop-types'; +interface containerType { + ID: number; + Name: string; + Names?: string; +} -const ProcessLogsCard = (props) => { +interface LogsCardProps { + container: containerType; + index: number; + status: any; +} + +const ProcessLogsCard = (props: LogsCardProps) => { const navigate = useNavigate(); return ( - - ); }; ProcessLogsCard.propTypes = { - container:PropTypes.object, + container: PropTypes.object, index: PropTypes.number, - status:PropTypes.string + status: PropTypes.string, }; export default ProcessLogsCard; - From 06fcfa8825022b8af21716c90932e630015a96e8 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Sat, 3 Dec 2022 10:32:36 -0600 Subject: [PATCH 071/110] parseContainerFormat to ts Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- ...ainerFormat.js => parseContainerFormat.ts} | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) rename src/components/helper/{parseContainerFormat.js => parseContainerFormat.ts} (78%) diff --git a/src/components/helper/parseContainerFormat.js b/src/components/helper/parseContainerFormat.ts similarity index 78% rename from src/components/helper/parseContainerFormat.js rename to src/components/helper/parseContainerFormat.ts index 7f1b3434..cbfbef39 100644 --- a/src/components/helper/parseContainerFormat.js +++ b/src/components/helper/parseContainerFormat.ts @@ -3,13 +3,13 @@ * * @param {*} stdout */ -const convert = (stdout) => { - let newArray = stdout.split("\n"); - let result = []; +const convert = (stdout: string) => { + const newArray = stdout.split('\n'); + const result = []; for (let i = 1; i < newArray.length - 1; i++) { - let removedSpace = newArray[i].replace(/\s+/g, " "); // remove all spaces and replace it to 1 space - removedSpace = removedSpace.replace(/\s[/]\s/g, "/"); // remove all the space in between slash - let splittedArray = removedSpace.split(" "); + let removedSpace = newArray[i].replace(/\s+/g, ' '); // remove all spaces and replace it to 1 space + removedSpace = removedSpace.replace(/\s[/]\s/g, '/'); // remove all the space in between slash + let splittedArray = removedSpace.split(' '); result.push(splittedArray); } return result; @@ -17,7 +17,7 @@ const convert = (stdout) => { /** * Converts all the array into array of objects containing key of the value - * + * * @param {*} array * @param {*} objArray */ @@ -43,7 +43,7 @@ const convertArrToObj = (array, objArray) => { * d: d1 Block I/O total * d2 Block I/O System Total * Refactoring is welcomed since it is not optimized way. - * + * * @param {*} array */ const convertToMetricsArr = (array) => { @@ -53,23 +53,23 @@ const convertToMetricsArr = (array) => { let netArray = [0, 0]; let blockArray = [0, 0]; for (let i = 0; i < array.length; i++) { - let cpu = array[i]["CPUPerc"].replace(/([%])+/g, ""); + let cpu = array[i]['CPUPerc'].replace(/([%])+/g, ''); cpuSum += parseFloat(cpu); - let memory = array[i]["MemPerc"].replace(/([%])+/g, ""); + let memory = array[i]['MemPerc'].replace(/([%])+/g, ''); memorySum += parseFloat(memory); - let splittedNet = array[i]["NetIO"].split("/"); - let netvalue = parseFloat(splittedNet[0].replace(/([A-z])+/g, "")); + let splittedNet = array[i]['NetIO'].split('/'); + let netvalue = parseFloat(splittedNet[0].replace(/([A-z])+/g, '')); let netTotal; - if (splittedNet[1].slice(-2) === "kB") { - netTotal = parseFloat(splittedNet[1].replace(/([A-z])+/g, "")); + if (splittedNet[1].slice(-2) === 'kB') { + netTotal = parseFloat(splittedNet[1].replace(/([A-z])+/g, '')); } else { - netTotal = parseFloat(splittedNet[1].replace(/([A-z])+/g, "")) * 1000; + netTotal = parseFloat(splittedNet[1].replace(/([A-z])+/g, '')) * 1000; } netArray[0] += netvalue; netArray[1] += netTotal; - let splittedBlock = array[i]["BlockIO"].split("/"); - let blockValue = parseFloat(splittedBlock[0].replace(/([A-z])+/g, "")); - let blockTotal = parseFloat(splittedBlock[1].replace(/([A-z])+/g, "")); + let splittedBlock = array[i]['BlockIO'].split('/'); + let blockValue = parseFloat(splittedBlock[0].replace(/([A-z])+/g, '')); + let blockTotal = parseFloat(splittedBlock[1].replace(/([A-z])+/g, '')); blockArray[0] += blockValue; blockArray[1] += blockTotal; } From 9900d599b4ca61d75f66aab02d1b676694d45c10 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Sat, 3 Dec 2022 08:35:26 -0800 Subject: [PATCH 072/110] Conversion to TS Settings tab. Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/tabs/Settings.tsx | 35 ++++++++++++++++---------------- src/components/tabs/TabTypes.ts | 18 ++++++++++++++-- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/components/tabs/Settings.tsx b/src/components/tabs/Settings.tsx index 41e94566..dd75979f 100644 --- a/src/components/tabs/Settings.tsx +++ b/src/components/tabs/Settings.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'; import { connect, useSelector, useDispatch } from 'react-redux'; import * as actions from '../../redux/actions/actions'; import * as categories from '../../redux/constants/notificationCategories'; -import { DispatchType, SettingsProps, WindowType, RunningContainerType, ContainerType } from './TabTypes'; +import { DispatchType, SettingsProps, WindowType, UserInfo } from './TabTypes'; // React Component Imports @@ -26,6 +26,7 @@ import Radio from '@mui/material/Radio'; import SendIcon from '@mui/icons-material/Send'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { AnyAaaaRecord } from 'dns'; +import { RootState } from '../../renderer/store'; const mapDispatchToProps = (dispatch: DispatchType) => ({ addPhoneNumber: (data: object[]) => dispatch(actions.addPhoneNumber(data)), @@ -51,7 +52,7 @@ const mapDispatchToProps = (dispatch: DispatchType) => ({ let showVerificationInput = false; let isVerified = false; -const Settings = (props: SettingsProps) => { +const Settings: any = (props: SettingsProps) => { const [mobileNumber, setMobileNumber] = useState(''); // handle check @@ -281,7 +282,7 @@ const Settings = (props: SettingsProps) => { // 1. CREATE AN OBJECT STATE WITH LIST OF CONTAINERS AS KEYS AND EMPTY ARRAYS AS VALUES const stateObject = {}; allContainersList.forEach((el) => { - if (!stateObject[el.ID]) stateObject[el.ID] = ''; + if (!stateObject[el.ID as keyof typeof tempGithubLink]) ((stateObject as Record))[el.ID] = ''; }); // 2. MAKE A DB REQUEST TO GET EXISTING DATA ABOUT GITHUB URL LINKS AND UPDATE THE STATE WITH THIS INFORMATION @@ -304,11 +305,11 @@ const Settings = (props: SettingsProps) => { // check if githubLinks are in the correct format, then save them to the database const githubLink = (event: any ) => { const example = 'https://api.github.com'; - if (!tempGithubLink[event.target.id] || tempGithubLink[event.target.id].slice(0,22) != example) + if (!tempGithubLink[event.target.id as keyof typeof tempGithubLink] || (tempGithubLink as Record)[event.target.id].slice(0,22) != example) return alert('Please provide a link in accordance with provided example'); if (!event.target.id) return alert('Please provide a container ID'); else { - const github_url = tempGithubLink[event.target.id]; + const github_url = tempGithubLink[event.target.id as keyof typeof tempGithubLink]; fetch('http://localhost:3000/settings/gitLinks', { method: 'POST', headers: { @@ -333,12 +334,12 @@ const Settings = (props: SettingsProps) => { }; // Redux: Map state to props - const _id = useSelector((state) => state.session._id); - const mem_threshold = useSelector((state) => state.session.mem_threshold); - const cpu_threshold = useSelector((state) => state.session.cpu_threshold); - const container_stops = useSelector((state) => state.session.container_stops); - const contact_pref = useSelector((state) => state.session.contact_pref); - const phone = useSelector((state) => state.session.phone); + const _id = useSelector((state: RootState) => state.session._id); + const mem_threshold = useSelector((state: RootState) => state.session.mem_threshold); + const cpu_threshold = useSelector((state: RootState) => state.session.cpu_threshold); + const container_stops = useSelector((state: RootState) => state.session.container_stops); + const contact_pref = useSelector((state: RootState) => state.session.contact_pref); + const phone = useSelector((state: RootState) => state.session.phone); // Local state variables to hold cpuThreshold, memThreshold, stoppedContainers, however should move to Redux session state variables const [cpuThreshold, setCpuThreshold] = useState(''); @@ -347,7 +348,7 @@ const Settings = (props: SettingsProps) => { const [value, setValue] = useState(contact_pref); const dispatch = useDispatch(); - const updateUser = (userInfo) => dispatch(actions.updateUser(userInfo)); + const updateUser = (userInfo: UserInfo) => dispatch(actions.updateUser(userInfo)); const handleRadioChange = (event: any) => { setValue(event.target.value); @@ -461,7 +462,7 @@ const Settings = (props: SettingsProps) => { ); }; - const renderAllContainersList = allContainersList.map((container: RunningListType, i: number) => { + const renderAllContainersList = allContainersList.map((container: any, i: number) => { const isMemorySelected = isSelected( props.memoryNotificationList, container.ID @@ -485,7 +486,7 @@ const Settings = (props: SettingsProps) => { + onClick={(event: any) => event.target.checked ? handleCheckSetting( container.ID, @@ -501,7 +502,7 @@ const Settings = (props: SettingsProps) => { + onClick={(event: any) => event.target.checked ? handleCheckSetting( container.ID, @@ -517,7 +518,7 @@ const Settings = (props: SettingsProps) => { + onClick={(event: any) => event.target.checked ? handleCheckSetting( container.ID, @@ -543,7 +544,7 @@ const Settings = (props: SettingsProps) => { helperText='* e.g.: https://api.github.com/repos/oslabs-beta/Docketeer/commits?' variant='outlined' onChange={(e) => { - stateObject[container.ID] = e.target.value; + (stateObject as Record)[container.ID] = e.target.value; setTempGithubLink(stateObject); }} size='small' diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 0c920510..276d6fbf 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -36,7 +36,7 @@ export type ContainerProps = { remove: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; stop: (id: string, runStoppedContainerDispatcher: (id: string) => void) => void; runningList: RunningListType[]; - runIm: (id: string, runningList: RunningListType, callback_1: () => void, callback_2: () => void) => void; + runIm: (id: ContainerType, runningList: RunningListType, callback_1: () => void, callback_2: () => void) => void; } // Stopped containers have a Names key and running containers have a Name key @@ -107,4 +107,18 @@ export type SettingsProps = { memoryNotificationList: {}; cpuNotificationList: {}; stoppedNotificationList: {}; - }; \ No newline at end of file + }; + + export interface UserInfo { + _id: number, + username: string, + email: string, + phone: string, + role: string, + role_id: number, + contact_pref: string | null, + mem_threshold: number, + cpu_threshold: number, + container_stops: boolean, + token: string + } \ No newline at end of file From 0b52af4d75414e5d57717428145725903555f546 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Sat, 3 Dec 2022 10:38:12 -0600 Subject: [PATCH 073/110] converted Images file to Typescript Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/tabs/{Images.js => Images.tsx} | 43 +++++++++++++++++-- src/components/views/UserView.tsx | 1 - 2 files changed, 39 insertions(+), 5 deletions(-) rename src/components/tabs/{Images.js => Images.tsx} (72%) diff --git a/src/components/tabs/Images.js b/src/components/tabs/Images.tsx similarity index 72% rename from src/components/tabs/Images.js rename to src/components/tabs/Images.tsx index f93c78c3..e17f9e61 100644 --- a/src/components/tabs/Images.js +++ b/src/components/tabs/Images.tsx @@ -2,15 +2,46 @@ import React, { useState } from 'react'; import * as helper from '../helper/commands'; +// types to add to the types file +interface ContainerObj { + BlockIO: string, + CPUPerc: string, + Container: string, + ID: string, + MemPerc: string, + MemUsage: string, + Name: string, + NetIO: string, + PIDs: string, + Image?: string, + RunningFor?: string +} + +interface imageObj { + reps: string, + tag: string, + imgid: string, + size: string +} + +interface ImagesProps { + imagesList: imageObj[], + runningList: ContainerObj[], + addRunningContainers: (data: ContainerObj[]) => void, + refreshImagesList: (data: imageObj[]) => void, + runIm: (id: string, runningList: ContainerObj[], callback_1: () => void, callback_2: () => void) => void, + removeIm: ( id: string, imagesList: imageObj[], callback_1: () => void, callback_2: () => void) => void +} + /** * Render Images of the user has * * @param {*} props */ -const Images = (props) => { +const Images = (props: ImagesProps) => { const [repo, setRepo] = useState(''); - const handleClick = (e) => { + const handleClick = () => { if (!repo) { alert('Please enter an image to pull!'); return; @@ -26,6 +57,8 @@ const Images = (props) => { return; } }); + // ingore was used below because Typescript says the codition will never be true, but this is not an accurate error + // @ts-ignore if (existingRepo === true) { alert('This image already exists!'); return; @@ -44,6 +77,8 @@ const Images = (props) => { return; } }); + // ingore was used below because Typescript says the codition will never be true, but this is not an accurate error + // @ts-ignore if (existingRepo === true){ alert('This image already exists!'); return; @@ -57,7 +92,7 @@ const Images = (props) => { } }; - const renderImagesList = props.imagesList.map((ele, i) => { + const renderImagesList = props.imagesList.map((ele, i: number) => { return (
    @@ -122,7 +157,7 @@ const Images = (props) => { }} > -
    diff --git a/src/components/views/UserView.tsx b/src/components/views/UserView.tsx index 9dc48570..40cd460d 100644 --- a/src/components/views/UserView.tsx +++ b/src/components/views/UserView.tsx @@ -295,7 +295,6 @@ const UserView = () => { stop={helper.stop} stopRunningContainer={stopRunningContainer} runningList={runningList} - addRunningContainers={addRunningContainers} // Stopped Containers runStopped={helper.runStopped} remove={helper.remove} From 800f97cafcaff42c4daaeee0570a5c66413441fa Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Sat, 3 Dec 2022 10:47:08 -0600 Subject: [PATCH 074/110] changed to test page Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- __tests__/{loginPage.js => loginPage.test.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename __tests__/{loginPage.js => loginPage.test.js} (100%) diff --git a/__tests__/loginPage.js b/__tests__/loginPage.test.js similarity index 100% rename from __tests__/loginPage.js rename to __tests__/loginPage.test.js From 85e9368561e3f1db9ca2d6fa63925cff174291ba Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Sat, 3 Dec 2022 10:50:30 -0600 Subject: [PATCH 075/110] fix props processlogtable file Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- package.json | 1 + src/components/display/ProcessLogsTable.js | 200 ---------------- src/components/display/ProcessLogsTable.tsx | 250 ++++++++++++++++++++ 3 files changed, 251 insertions(+), 200 deletions(-) delete mode 100644 src/components/display/ProcessLogsTable.js create mode 100644 src/components/display/ProcessLogsTable.tsx diff --git a/package.json b/package.json index 4b81868b..d1ba1bd7 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@testing-library/react": "^13.4.0", "@types/jest": "^29.2.3", "@types/pg": "^8.6.5", + "@types/react-csv": "^1.1.3", "axios": "^1.0.0", "babel-loader": "^8.2.5", "bcryptjs": "^2.4.3", diff --git a/src/components/display/ProcessLogsTable.js b/src/components/display/ProcessLogsTable.js deleted file mode 100644 index aaa79ab6..00000000 --- a/src/components/display/ProcessLogsTable.js +++ /dev/null @@ -1,200 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useDispatch } from 'react-redux'; -import { buildOptionsObj } from '../helper/processLogHelper'; -import { getLogs } from '../helper/commands'; -import * as actions from '../../redux/actions/actions'; - -import store from '../../renderer/store'; -import { DataGrid } from '@mui/x-data-grid'; -import { Checkbox, FormControlLabel, FormGroup, Button } from '@mui/material'; // use for container selection -import { CSVLink } from 'react-csv'; - -/** - * Displays process logs as table - * @module ProcessLogsTable - * @description Container that displays all running and not running docker containers. Each box is wrapped by - * a Router link. - */ - - const ProcessLogsTable = () => { - - const dispatch = useDispatch(); - const getContainerLogsDispatcher = (data) => - dispatch(actions.getContainerLogs(data)); - - // grab clicked container - const urlString = window.location.href; - const containerID = urlString.split('/'); - const id = containerID[containerID.length - 1]; - - // access runningList from state - const runningList = store.getState().containersList.runningList; - - const [btnIdList, setBtnIdList] = useState([id]); - const [rows, setRows] = useState([]); - - const [csvData, setCsvData] = useState([ - [ 'container', 'type', 'time', 'message'] - ]); - - const [logs, setLogs] = useState({ stdout: [], stderr: [] }); - const { stdout, stderr } = logs; - - // This will update the logs table after all logs have been pulled - there will be a lag before they render - useEffect(() => { - tableData(); - }, [logs.stderr.length, csvData.length]); - - // Get logs button handler function. Grabs logs and updates component state - const handleGetLogs = (idList) => { - const optionsObj = buildOptionsObj(idList); - - // Using a promise as the process to pull the container logs takes a fair bit of time - const containerLogsPromise = Promise.resolve(getLogs(optionsObj, getContainerLogsDispatcher)); - containerLogsPromise.then((data) => { - const newLogs = data; - setLogs(newLogs); - return newLogs; - }); - }; - - const columns = [ - { field: 'container', headerName: 'Container', width: 150 }, - { field: 'type', headerName: 'Log Type', width: 120 }, - { field: 'time', headerName: 'Timestamp', width: 200 }, - { field: 'message', headerName: 'Message', width: 550 } - ]; - - const createContainerCheckboxes = (currId) => { - // iterate through runningList -> create label and checkbox for each one - for (let i = 0; i < runningList.length; i++) { - // by default, clicked container should be checked - if (runningList[i].ID === currId){ - containerSelectors.push( handleCheck(e)} />} label={`${runningList[i].Name}`} />); - } else { - // by default all others should be unchecked - containerSelectors.push( handleCheck(e)} />} label={`${runningList[i].Name}`} />); - } - } - }; - - // create array to hold labels & boxes to render - const containerSelectors = []; - createContainerCheckboxes(id); - - // handle checkboxes - const handleCheck = (e) => { - - const box = e.target; - // if checkbox is changed to true add to button idList - if (box.checked === true) { - btnIdList.push(box.id); - setBtnIdList(btnIdList); - } else { - // else remove from button idList - const newIdList = btnIdList.filter(container => container !== box.id); - setBtnIdList(newIdList); - } - }; - - // Populating the StdOut Table Data Using stdout.map - const tableData = () => { - const newRows = []; - const newCSV = []; - - if(stdout) { - stdout.forEach((log, index) => { - const currCont = runningList.find(el => el.ID === log.containerName); - newRows.push({ - container: currCont.Name, - type: 'stdout', - time: log.timeStamp, - message: log.logMsg, - id: Math.random() * 100 - }); - newCSV.push([currCont.Name, 'stdout', log.timeStamp, log.logMsg]); - }); - - stderr.forEach((log, index) => { - const currCont = runningList.find(el => el.ID === log.containerName); - newRows.push({ - container: currCont.Name, - type: 'stderr', - time: log.timeStamp, - message: log.logMsg, - id: `stderr ${index}` - }); - newCSV.push([currCont.Name, 'stderr', log.timeStamp, log.logMsg]); - }); - - setRows(newRows); - setCsvData([[ 'container', 'type', 'time', 'message'], ...newCSV]); - } - }; - - return ( -
    - -
    -
    -

    Container Process Logs

    - - - - -

    - - - - - - {containerSelectors} {/** Checkboxes for running containers */} - - - -
    - -
    - 'auto'} - initialState={{ - sorting: { - sortModel: [{ field: 'time', sort: 'desc' }], // default sorts table by time - }, - }} - /> -
    -
    -
    - ); -}; - -export default ProcessLogsTable; \ No newline at end of file diff --git a/src/components/display/ProcessLogsTable.tsx b/src/components/display/ProcessLogsTable.tsx new file mode 100644 index 00000000..991d1490 --- /dev/null +++ b/src/components/display/ProcessLogsTable.tsx @@ -0,0 +1,250 @@ +import React, { useEffect, useState } from "react"; +import { useDispatch } from "react-redux"; +import { buildOptionsObj } from "../helper/processLogHelper"; +import { getLogs } from "../helper/commands"; +import * as actions from "../../redux/actions/actions"; + +import store from "../../renderer/store"; +import { DataGrid } from "@mui/x-data-grid"; +import { Checkbox, FormControlLabel, FormGroup, Button } from "@mui/material"; // use for container selection +import { CSVLink } from "react-csv"; + +/** + * Displays process logs as table + * @module ProcessLogsTable + * @description Container that displays all running and not running docker containers. Each box is wrapped by + * a Router link. + */ + +const ProcessLogsTable = () => { + const dispatch = useDispatch(); + const getContainerLogsDispatcher = (data: object[]) => + dispatch(actions.getContainerLogs(data)); + + // grab clicked container + const urlString = window.location.href; + const containerID = urlString.split("/"); + const id = containerID[containerID.length - 1]; + + // access runningList from state + const runningList = store.getState().containersList.runningList; + + const [btnIdList, setBtnIdList] = useState([id]); + const [rows, setRows] = useState([]); + + const [csvData, setCsvData] = useState([ + ["container", "type", "time", "message"], + ]); + + const [logs, setLogs] = useState({ stdout: [], stderr: [] }); + const { stdout, stderr } = logs; + + // This will update the logs table after all logs have been pulled - there will be a lag before they render + useEffect(() => { + tableData(); + }, [logs.stderr.length, csvData.length]); + + // Get logs button handler function. Grabs logs and updates component state + const handleGetLogs = (idList: string[]) => { + const optionsObj = buildOptionsObj(idList); + + // Using a promise as the process to pull the container logs takes a fair bit of time + const containerLogsPromise = Promise.resolve( + getLogs(optionsObj, getContainerLogsDispatcher) + ); + containerLogsPromise.then((data) => { + const newLogs = data; + setLogs(newLogs); + return newLogs; + }); + }; + + const columns = [ + { field: "container", headerName: "Container", width: 150 }, + { field: "type", headerName: "Log Type", width: 120 }, + { field: "time", headerName: "Timestamp", width: 200 }, + { field: "message", headerName: "Message", width: 550 }, + ]; + + const createContainerCheckboxes = (currId) => { + // iterate through runningList -> create label and checkbox for each one + for (let i = 0; i < runningList.length; i++) { + // by default, clicked container should be checked + if (runningList[i].ID === currId) { + containerSelectors.push( + handleCheck(e)} + /> + } + label={`${runningList[i].Name}`} + /> + ); + } else { + // by default all others should be unchecked + containerSelectors.push( + handleCheck(e)} + /> + } + label={`${runningList[i].Name}`} + /> + ); + } + } + }; + + // create array to hold labels & boxes to render + const containerSelectors = []; + createContainerCheckboxes(id); + + // handle checkboxes + const handleCheck = (e) => { + const box = e.target; + // if checkbox is changed to true add to button idList + if (box.checked === true) { + btnIdList.push(box.id); + setBtnIdList(btnIdList); + } else { + // else remove from button idList + const newIdList = btnIdList.filter((container) => container !== box.id); + setBtnIdList(newIdList); + } + }; + + // Populating the StdOut Table Data Using stdout.map + const tableData = () => { + const newRows = []; + const newCSV = []; + + if (stdout) { + stdout.forEach((log, index) => { + const currCont = runningList.find((el) => el.ID === log.containerName); + newRows.push({ + container: currCont.Name, + type: "stdout", + time: log.timeStamp, + message: log.logMsg, + id: Math.random() * 100, + }); + newCSV.push([currCont.Name, "stdout", log.timeStamp, log.logMsg]); + }); + + stderr.forEach((log, index) => { + const currCont = runningList.find((el) => el.ID === log.containerName); + newRows.push({ + container: currCont.Name, + type: "stderr", + time: log.timeStamp, + message: log.logMsg, + id: `stderr ${index}`, + }); + newCSV.push([currCont.Name, "stderr", log.timeStamp, log.logMsg]); + }); + + setRows(newRows); + setCsvData([["container", "type", "time", "message"], ...newCSV]); + } + }; + + return ( +
    +
    +
    +

    Container Process Logs

    + + + + +

    + + + + + + {containerSelectors} {/** Checkboxes for running containers */} + + + +
    + +
    + "auto"} + initialState={{ + sorting: { + sortModel: [{ field: "time", sort: "desc" }], // default sorts table by time + }, + }} + /> +
    +
    +
    + ); +}; + +export default ProcessLogsTable; From ece8714bb8fc739ef08425b6e121bb8518f72ba4 Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Sat, 3 Dec 2022 09:42:14 -0800 Subject: [PATCH 076/110] Setttings, TabTypes, type conflict adjustments. Co-authored-by: Sarah --- src/components/tabs/Settings.tsx | 9 +++++---- src/components/tabs/TabTypes.ts | 31 ++++++++++++++++++------------- src/components/views/SysAdmin.tsx | 2 +- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/components/tabs/Settings.tsx b/src/components/tabs/Settings.tsx index dd75979f..9d8c1a17 100644 --- a/src/components/tabs/Settings.tsx +++ b/src/components/tabs/Settings.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'; import { connect, useSelector, useDispatch } from 'react-redux'; import * as actions from '../../redux/actions/actions'; import * as categories from '../../redux/constants/notificationCategories'; -import { DispatchType, SettingsProps, WindowType, UserInfo } from './TabTypes'; +import { DispatchType, SettingsProps, WindowType, UserInfo, RunningListType } from './TabTypes'; // React Component Imports @@ -25,7 +25,6 @@ import FormControl from '@mui/material/FormControl'; import Radio from '@mui/material/Radio'; import SendIcon from '@mui/icons-material/Send'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; -import { AnyAaaaRecord } from 'dns'; import { RootState } from '../../renderer/store'; const mapDispatchToProps = (dispatch: DispatchType) => ({ @@ -52,7 +51,8 @@ const mapDispatchToProps = (dispatch: DispatchType) => ({ let showVerificationInput = false; let isVerified = false; -const Settings: any = (props: SettingsProps) => { + +const Settings = (props: SettingsProps) => { const [mobileNumber, setMobileNumber] = useState(''); // handle check @@ -874,4 +874,5 @@ const Settings: any = (props: SettingsProps) => { ); }; -export default connect(null, mapDispatchToProps)(Settings); \ No newline at end of file +export default connect(null, mapDispatchToProps)(Settings); + diff --git a/src/components/tabs/TabTypes.ts b/src/components/tabs/TabTypes.ts index 276d6fbf..5bf6b8ea 100644 --- a/src/components/tabs/TabTypes.ts +++ b/src/components/tabs/TabTypes.ts @@ -78,7 +78,7 @@ export type ContainerProps = { barPercentage: number; } - // dont judge me, im coming back for you + export type DispatchType = (...args: any[]) => void; export type WindowType = { @@ -95,18 +95,23 @@ type RendInvokebody = { } export type SettingsProps = { - addMonitoringFrequency: (data: string | number) => void; - addMemoryNotificationSetting: (data: object[]) => void; - addCpuNotificationSetting: (data: object[]) => void; - addStoppedNotificationSetting: (data: object[]) => void; - addPhoneNumber: (data: object[] | string) => void; - addNotificationFrequency: (data: string | number) => void; - runningList: RunningListType[]; - stoppedList: StoppedListType[]; - // fixing the below - memoryNotificationList: {}; - cpuNotificationList: {}; - stoppedNotificationList: {}; + addMonitoringFrequency: (data: any) => void; + addMemoryNotificationSetting: (data: any) => void; + addCpuNotificationSetting: (data: any) => void; + addStoppedNotificationSetting: (data: any) => void; + addPhoneNumber: (data: any) => void; + addNotificationFrequency: (data: any) => void; + runningList: any[]; + stop?: (id: any, callback: any) => void; + stoppedList: any[]; + stopRunningContainer: (id: string) => { type: string; payload: string; }; + runStopped: (id: any, runStoppedContainerDispatcher: any) => void; + refreshRunningContainers: (data: any[]) => void; + runStoppedContainer: (id: string) => void; + phoneNumber?: string[]; + memoryNotificationList: any[]; + cpuNotificationList: any[]; + stoppedNotificationList: any[]; }; export interface UserInfo { diff --git a/src/components/views/SysAdmin.tsx b/src/components/views/SysAdmin.tsx index 6f6a9177..43479f83 100644 --- a/src/components/views/SysAdmin.tsx +++ b/src/components/views/SysAdmin.tsx @@ -338,7 +338,7 @@ const SysAdmin = () => { stopRunningContainer={stopRunningContainer} stoppedList={stoppedList} runStopped={helper.runStopped} - // refreshRunningContainers={refreshRunningContainers} + refreshRunningContainers={refreshRunningContainers} runStoppedContainer={runStoppedContainer} phoneNumber={phoneNumber} memoryNotificationList={memoryNotificationList} From 7042523ce3eefc65114440cf2a61abaf899686c6 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Sat, 3 Dec 2022 14:58:38 -0600 Subject: [PATCH 077/110] convert ProcessLogsTable file to tsx Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/display/ProcessLogsTable.tsx | 47 ++++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/components/display/ProcessLogsTable.tsx b/src/components/display/ProcessLogsTable.tsx index 991d1490..4ce7b845 100644 --- a/src/components/display/ProcessLogsTable.tsx +++ b/src/components/display/ProcessLogsTable.tsx @@ -3,6 +3,7 @@ import { useDispatch } from "react-redux"; import { buildOptionsObj } from "../helper/processLogHelper"; import { getLogs } from "../helper/commands"; import * as actions from "../../redux/actions/actions"; +import "./ProcessLogsCard"; import store from "../../renderer/store"; import { DataGrid } from "@mui/x-data-grid"; @@ -54,7 +55,7 @@ const ProcessLogsTable = () => { ); containerLogsPromise.then((data) => { const newLogs = data; - setLogs(newLogs); + setLogs(newLogs as keyof typeof setLogs); return newLogs; }); }; @@ -66,7 +67,7 @@ const ProcessLogsTable = () => { { field: "message", headerName: "Message", width: 550 }, ]; - const createContainerCheckboxes = (currId) => { + const createContainerCheckboxes = (currId: string) => { // iterate through runningList -> create label and checkbox for each one for (let i = 0; i < runningList.length; i++) { // by default, clicked container should be checked @@ -106,11 +107,11 @@ const ProcessLogsTable = () => { }; // create array to hold labels & boxes to render - const containerSelectors = []; + const containerSelectors: any[] = []; createContainerCheckboxes(id); // handle checkboxes - const handleCheck = (e) => { + const handleCheck = (e: React.ChangeEvent) => { const box = e.target; // if checkbox is changed to true add to button idList if (box.checked === true) { @@ -123,37 +124,51 @@ const ProcessLogsTable = () => { } }; + interface RowsDataType { + container: string; + type: string; + time: string; + message: string; + id: number; + } + + type CSVData = string[]; + // Populating the StdOut Table Data Using stdout.map const tableData = () => { - const newRows = []; - const newCSV = []; + const newRows: RowsDataType[] = []; + const newCSV: CSVData[] = []; if (stdout) { stdout.forEach((log, index) => { - const currCont = runningList.find((el) => el.ID === log.containerName); + const currCont = runningList.find( + (el) => el.ID === log["containerName"] + ); newRows.push({ container: currCont.Name, type: "stdout", - time: log.timeStamp, - message: log.logMsg, + time: log["timeStamp"], + message: log["logMsg"], id: Math.random() * 100, }); - newCSV.push([currCont.Name, "stdout", log.timeStamp, log.logMsg]); + newCSV.push([currCont.Name, "stdout", log["timeStamp"], log["logMsg"]]); }); stderr.forEach((log, index) => { - const currCont = runningList.find((el) => el.ID === log.containerName); + const currCont = runningList.find( + (el) => el.ID === log["containerName"] + ); newRows.push({ container: currCont.Name, type: "stderr", - time: log.timeStamp, - message: log.logMsg, - id: `stderr ${index}`, + time: log["timeStamp"], + message: log["logMsg"], + id: parseInt(`stderr ${index}`), }); - newCSV.push([currCont.Name, "stderr", log.timeStamp, log.logMsg]); + newCSV.push([currCont.Name, "stderr", log["timeStamp"], log["logMsg"]]); }); - setRows(newRows); + setRows(newRows as keyof typeof setRows); setCsvData([["container", "type", "time", "message"], ...newCSV]); } }; From c67fdcc94d6baee5ff40c20027dbc2c7eb7c5b94 Mon Sep 17 00:00:00 2001 From: tiffany chau Date: Sat, 3 Dec 2022 15:31:14 -0600 Subject: [PATCH 078/110] convert ToggleDisplay to tsx Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/display/ToggleDisplay.js | 35 -------------------- src/components/display/ToggleDisplay.tsx | 42 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 35 deletions(-) delete mode 100644 src/components/display/ToggleDisplay.js create mode 100644 src/components/display/ToggleDisplay.tsx diff --git a/src/components/display/ToggleDisplay.js b/src/components/display/ToggleDisplay.js deleted file mode 100644 index 82328851..00000000 --- a/src/components/display/ToggleDisplay.js +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-disable react/prop-types */ -import React, { useState } from "react"; - -const ToggleDisplay = (props) => { - const [toggle, setToggle] = useState(false); - const tog = () => { - setToggle(!toggle); - }; - - return ( -
    -
    tog()} className="toggle-button"> - {toggle - ? () - : () - } -
    - {toggle - ? (
    -
  • Mem Usage / Limit: {props.container.MemUsage}
  • -
  • Net I/O: {props.container.NetIO}
  • -
  • Block I/O: {props.container.BlockIO}
  • -
  • PIDS: {props.container.PIDs}
  • -
    ) - : (<>) - } -
    - ); -}; - -export default ToggleDisplay; diff --git a/src/components/display/ToggleDisplay.tsx b/src/components/display/ToggleDisplay.tsx new file mode 100644 index 00000000..3c7739a8 --- /dev/null +++ b/src/components/display/ToggleDisplay.tsx @@ -0,0 +1,42 @@ +/* eslint-disable react/prop-types */ +import React, { useState } from "react"; +import { RunningListType } from "../tabs/TabTypes"; + +interface ToggleDisplayProps { + container: RunningListType; +} + +const ToggleDisplay = (props: ToggleDisplayProps) => { + const [toggle, setToggle] = useState(false); + const tog = () => { + setToggle(!toggle); + }; + + return ( +
    +
    tog()} className="toggle-button"> + {toggle ? ( + + ) : ( + + )} +
    + {toggle ? ( +
    +
  • Mem Usage / Limit: {props.container.MemUsage}
  • +
  • Net I/O: {props.container.NetIO}
  • +
  • Block I/O: {props.container.BlockIO}
  • +
  • PIDS: {props.container.PIDs}
  • +
    + ) : ( + <> + )} +
    + ); +}; + +export default ToggleDisplay; From bb2d38124f20e433c27aefec2f42f1a9cb4d1f3d Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Mon, 5 Dec 2022 10:47:53 -0600 Subject: [PATCH 079/110] quick --- src/components/helper/HelperTypes.ts | 0 src/components/helper/commands.js | 166 ++++++++++-------- src/components/helper/parseContainerFormat.ts | 10 +- 3 files changed, 95 insertions(+), 81 deletions(-) create mode 100644 src/components/helper/HelperTypes.ts diff --git a/src/components/helper/HelperTypes.ts b/src/components/helper/HelperTypes.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/components/helper/commands.js b/src/components/helper/commands.js index 20669924..891efc60 100644 --- a/src/components/helper/commands.js +++ b/src/components/helper/commands.js @@ -1,19 +1,18 @@ import parseContainerFormat from './parseContainerFormat'; import { filterOneProperty, - listOfVolumeProperties + listOfVolumeProperties, } from './volumeHistoryHelper'; import store from '../../renderer/store'; import { makeArrayOfObjects } from './processLogHelper'; /** * Grabs all active containers on app-start up - * + * * @param {*} runningList * @param {*} callback */ - export const addRunning = (runningList, callback) => { window.nodeMethod.runExec( 'docker stats --no-stream --format "{{json .}},"', @@ -27,12 +26,12 @@ export const addRunning = (runningList, callback) => { return; } // trim whitespace at end out stdout, slice to remove trailing comma and remove spaces - + const dockerOutput = `[${stdout .trim() .slice(0, -1) .replaceAll(' ', '')}]`; - + const convertedValue = JSON.parse(dockerOutput); const newList = []; @@ -55,7 +54,7 @@ export const addRunning = (runningList, callback) => { /** * Refreshes running containers - * + * * @param {*} callback * @param {*} runningList */ @@ -63,11 +62,10 @@ export const addRunning = (runningList, callback) => { const errorsCalled = {}; const errorCheck = (key, error) => { - if(!errorsCalled[key]) { + if (!errorsCalled[key]) { errorsCalled[key] = error.message; alert(`Make sure Docker Desktop is running. \n\n ${error.message}`); - } - else{ + } else { console.log(error.message); } return; @@ -99,7 +97,7 @@ export const refreshRunning = (refreshRunningContainers) => { /** * Refreshes stopped containers - * + * * @param {*} callback */ export const refreshStopped = (refreshStoppedContainers) => { @@ -125,7 +123,7 @@ export const refreshStopped = (refreshStoppedContainers) => { /** * Refreshes images - * + * * @param {*} callback */ export const refreshImages = (callback) => { @@ -139,9 +137,10 @@ export const refreshImages = (callback) => { return; } const value = parseContainerFormat.convert(stdout); + // console.log('stdout in refreshImages: ', stdout); const objArray = ['reps', 'tag', 'imgid', 'size']; const resultImages = []; - + for (let i = 0; i < value.length; i++) { const innerArray = []; if (value[i][0] !== '') { @@ -153,15 +152,17 @@ export const refreshImages = (callback) => { } } - const convertedValue = parseContainerFormat - .convertArrToObj(resultImages, objArray); + const convertedValue = parseContainerFormat.convertArrToObj( + resultImages, + objArray + ); callback(convertedValue); }); }; /** * Removes images - * + * * @param {*} id * @param {*} callback */ @@ -181,7 +182,7 @@ export const remove = (id, callback) => { /** * Stops a container on what user selects - * + * * @param {*} id * @param {*} callback */ @@ -201,14 +202,11 @@ export const stop = (id, callback) => { /** * Starts the container - * + * * @param {*} id * @param {*} callback */ -export const runStopped = ( - id, - runStoppedContainerDispatcher, -) => { +export const runStopped = (id, runStoppedContainerDispatcher) => { window.nodeMethod.runExec(`docker start ${id}`, (error, stdout, stderr) => { if (error) { alert(`${error.message}`); @@ -224,7 +222,7 @@ export const runStopped = ( /** * Run image - * + * * @param {*} id * @param {*} runningList * @param {*} callback_1 @@ -234,25 +232,28 @@ export const runStopped = ( // this function is used to run an image from the image tab export const runIm = (container, runningList, callback_1, callback_2) => { // props.runIm(ele['imgid'], props.runningList, helper.addRunning, props.addRunningContainers) - const {imgid, reps, tag} = container; + const { imgid, reps, tag } = container; console.log(container); - window.nodeMethod.runExec(`docker run --name ${reps}-${tag} ${reps}:${tag}`, (error, stdout, stderr) => { - if (error) { - alert(`${error.message}`); - return; - } - if (stderr) { - console.log(`runIm stderr: ${stderr}`); - return; + window.nodeMethod.runExec( + `docker run --name ${reps}-${tag} ${reps}:${tag}`, + (error, stdout, stderr) => { + if (error) { + alert(`${error.message}`); + return; + } + if (stderr) { + console.log(`runIm stderr: ${stderr}`); + return; + } } - }); + ); callback_1(runningList, callback_2); alert('Running container'); }; /** * Remove Image - * + * * @param {*} id * @param {*} imagesList * @param {*} callback_1 @@ -277,7 +278,7 @@ export const removeIm = (id, imagesList, callback_1, callback_2) => { /** * Handles System Prune - * + * * @param {*} e */ @@ -300,7 +301,7 @@ export const handlePruneClick = (e) => { /** * Pulls image based on the repo you select - * + * * @param {*} repo */ @@ -308,7 +309,9 @@ export const pullImage = (repo) => { window.nodeMethod.runExec(`docker pull ${repo}`, (error, stdout, stderr) => { if (error) { console.log('error occurred in pulling image'); - alert(`Image repo '${repo}' seems to not exist, or may be a private repo.`); + alert( + `Image repo '${repo}' seems to not exist, or may be a private repo.` + ); // alert(`${error.message}`); return; } @@ -316,7 +319,7 @@ export const pullImage = (repo) => { console.log(`pullImage stderr: ${stderr}`); return; } - + alert(`${repo} is currently being downloaded`); console.log(stdout); // if not error, add a loading component until page renders a new component @@ -325,7 +328,7 @@ export const pullImage = (repo) => { /** * Display all containers network based on docker-compose when the application starts - * + * * @param {*} getNetworkContainers */ @@ -375,8 +378,8 @@ export const inspectDockerContainer = (containerId) => { }; /** - * Compose up a docker container network - * + * Compose up a docker container network + * * @param {*} fileLocation * @param {*} ymlFileName */ @@ -388,11 +391,11 @@ export const dockerComposeUp = (fileLocation, ymlFileName) => { 'docker-compose.yml', 'docker-compose.yaml', 'compose.yml', - 'compose.yaml' + 'compose.yaml', ]; let cmd = `cd ${fileLocation} && docker compose up -d`; // if ymlFilename is not a native yml/yaml file name, add -f flag and non-native filename - + if (!nativeYmlFilenames.includes(ymlFileName)) { cmd = `cd ${fileLocation} && docker compose -f ${ymlFileName} up -d`; } @@ -414,13 +417,17 @@ export const dockerComposeUp = (fileLocation, ymlFileName) => { /** * Get list of running container networks - * + * * @param {*} getContainerStacks * @param {*} filePath * @param {*} ymlFileName */ -export const dockerComposeStacks = (getContainerStacks, filePath, ymlFileName) => { +export const dockerComposeStacks = ( + getContainerStacks, + filePath, + ymlFileName +) => { let parseDockerOutput; window.nodeMethod.runExec( @@ -434,8 +441,8 @@ export const dockerComposeStacks = (getContainerStacks, filePath, ymlFileName) = console.log(`dockerComposeStacks stderr: ${stderr}`); return; } - - // create array of running container network objects + + // create array of running container network objects // the array is sorted in alphabetical order based on network Name const dockerOutput = `[${stdout .trim() @@ -446,9 +453,10 @@ export const dockerComposeStacks = (getContainerStacks, filePath, ymlFileName) = // if container network was composed through the application, add a filePath and ymlFileName property to its container network object if (filePath && ymlFileName) { const directoryNameArray = filePath.split('/'); - const containerNetworkName = directoryNameArray[directoryNameArray.length - 1].concat('_default'); - - parseDockerOutput.forEach(obj => { + const containerNetworkName = + directoryNameArray[directoryNameArray.length - 1].concat('_default'); + + parseDockerOutput.forEach((obj) => { if (containerNetworkName === obj.Name) { obj.FilePath = filePath; obj.YmlFileName = ymlFileName; @@ -463,7 +471,7 @@ export const dockerComposeStacks = (getContainerStacks, filePath, ymlFileName) = /** * Compose down selected container network - * + * * @param {*} fileLocation * @param {*} ymlFileName */ @@ -474,10 +482,10 @@ export const dockerComposeDown = (fileLocation, ymlFileName) => { 'docker-compose.yml', 'docker-compose.yaml', 'compose.yml', - 'compose.yaml' + 'compose.yaml', ]; let cmd = `cd ${fileLocation} && docker-compose down`; - // if ymlFilename is not a native yml/yaml file name, add -f flag and non-native filename + // if ymlFilename is not a native yml/yaml file name, add -f flag and non-native filename if (!nativeYmlFilenames.includes(ymlFileName)) { cmd = `cd ${fileLocation} && docker-compose -f ${ymlFileName} down`; } @@ -521,14 +529,14 @@ export const writeToDb = () => { cpu: container.CPUPerc, mem: container.MemPerc, memuse: container.MemUsage, - net: container.NetIO , + net: container.NetIO, block: container.BlockIO, pid: container.PIDs, - timestamp: 'current_timestamp' + timestamp: 'current_timestamp', }; }); if (stoppedContainers.length >= 1) { - stoppedContainers.forEach((container) => { + stoppedContainers.forEach((container) => { containerParameters[container.Names] = { ID: container.ID, names: container.Names, @@ -538,22 +546,21 @@ export const writeToDb = () => { net: '0.00kB/0.00kB', block: '00.0MB/00.0MB', pid: '0', - timestamp: 'current_timestamp' + timestamp: 'current_timestamp', }; }); } fetch('http://localhost:3000/init/addMetrics', { method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, body: JSON.stringify({ - containers: containerParameters - }) - }) - .catch((err) => { - console.log(err); - }); + containers: containerParameters, + }), + }).catch((err) => { + console.log(err); + }); }, interval); }; @@ -564,11 +571,11 @@ export const setDbSessionTimeZone = () => { fetch('http://localhost:3000/init/timezone', { method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, body: JSON.stringify({ - timezone: offsetTimeZoneInHours - }) + timezone: offsetTimeZoneInHours, + }), }) .then((data) => data.json()) .then((response) => { @@ -583,18 +590,18 @@ export const getContainerGitUrl = async (container) => { const response = await fetch('http://localhost:3000/init/github', { method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, body: JSON.stringify({ - githubUrl: container - }) + githubUrl: container, + }), }); return await response.json(); }; /** * Docker command to retrieve the list of running volumes - * + * * @param {*} getVolumeList */ @@ -622,7 +629,7 @@ export const getAllDockerVolumes = (getVolumeList) => { /** * Docker command to retrieve the list of containers running in specified volume - * + * * @param {string} volumeName * @param {callback} getVolumeContainersList */ @@ -644,8 +651,8 @@ export const getVolumeContainers = (volumeName, getVolumeContainersList) => { return getVolumeContainersList( listOfVolumeProperties(volumeName, dockerOutput) ); - - }); + } + ); }; /** @@ -676,12 +683,15 @@ export const getLogs = async (optionsObj, getContainerLogsDispatcher) => { console.error(`exec error: ${error}`); return; } - containerLogs.stdout = [...containerLogs.stdout, ...makeArrayOfObjects(stdout, optionsObj.containerIds[i])]; - containerLogs.stderr = [...containerLogs.stderr, ...makeArrayOfObjects(stderr, optionsObj.containerIds[i])]; + containerLogs.stdout = [ + ...containerLogs.stdout, + ...makeArrayOfObjects(stdout, optionsObj.containerIds[i]), + ]; + containerLogs.stderr = [ + ...containerLogs.stderr, + ...makeArrayOfObjects(stderr, optionsObj.containerIds[i]), + ]; }); } return containerLogs; }; - - - diff --git a/src/components/helper/parseContainerFormat.ts b/src/components/helper/parseContainerFormat.ts index cbfbef39..3b156b68 100644 --- a/src/components/helper/parseContainerFormat.ts +++ b/src/components/helper/parseContainerFormat.ts @@ -1,3 +1,5 @@ + + /** * Parse all the stdout output into array to manipulate data properly. * @@ -21,10 +23,11 @@ const convert = (stdout: string) => { * @param {*} array * @param {*} objArray */ -const convertArrToObj = (array, objArray) => { +const convertArrToObj = (array: string[][], objArray: string[]) => { + // console.log('convertedArr: ', array, objArray); const result = []; for (let i = 0; i < array.length; i++) { - let containerObj = {}; + let containerObj: any = {}; for (let j = 0; j < array[i].length; j++) { containerObj[objArray[j]] = array[i][j]; } @@ -46,7 +49,8 @@ const convertArrToObj = (array, objArray) => { * * @param {*} array */ -const convertToMetricsArr = (array) => { +const convertToMetricsArr = (array: any[]) => { + console.log('metrics array: ', array) let newArr = []; let cpuSum = 0; let memorySum = 0; From bba832ee5bef9bd7ec170a8df7b1a9b56f037143 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Mon, 5 Dec 2022 12:00:08 -0600 Subject: [PATCH 080/110] newUserHelper and parseContainerFormat ts Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- src/components/helper/newUserHelper.ts | 3 +- src/components/helper/parseContainerFormat.ts | 2 +- src/components/login/login.tsx | 115 ++++++++++-------- 3 files changed, 65 insertions(+), 55 deletions(-) diff --git a/src/components/helper/newUserHelper.ts b/src/components/helper/newUserHelper.ts index 76fdff25..ad200569 100644 --- a/src/components/helper/newUserHelper.ts +++ b/src/components/helper/newUserHelper.ts @@ -4,8 +4,9 @@ */ import store from '../../renderer/store'; import * as actions from '../../redux/actions/actions'; +import React from 'react'; -export const handleNewUser = (e: Event) => { +export const handleNewUser = (e: any) => { e.preventDefault(); const username = (document.getElementById('signupUsername')).value; diff --git a/src/components/helper/parseContainerFormat.ts b/src/components/helper/parseContainerFormat.ts index 3b156b68..974daa20 100644 --- a/src/components/helper/parseContainerFormat.ts +++ b/src/components/helper/parseContainerFormat.ts @@ -54,7 +54,7 @@ const convertToMetricsArr = (array: any[]) => { let newArr = []; let cpuSum = 0; let memorySum = 0; - let netArray = [0, 0]; + let netArray: any = [0, 0]; let blockArray = [0, 0]; for (let i = 0; i < array.length; i++) { let cpu = array[i]['CPUPerc'].replace(/([%])+/g, ''); diff --git a/src/components/login/login.tsx b/src/components/login/login.tsx index 57d5e046..0cd1d000 100644 --- a/src/components/login/login.tsx +++ b/src/components/login/login.tsx @@ -15,24 +15,25 @@ import Button from '@mui/material/Button'; import Docketeer from '../../../assets/docketeer-title.png'; interface UserInfo { - _id: number, - username: string, - email: string, - phone: string, - role: string, - role_id: number, - contact_pref: string | null, - mem_threshold: number, - cpu_threshold: number, - container_stops: boolean, - token: string + _id: number; + username: string; + email: string; + phone: string; + role: string; + role_id: number; + contact_pref: string | null; + mem_threshold: number; + cpu_threshold: number; + container_stops: boolean; + token: string; } const Login = () => { const navigate = useNavigate(); const dispatch = useDispatch(); const updateSession = () => dispatch(actions.updateSession()); - const updateUser = (userInfo: UserInfo) => dispatch(actions.updateUser(userInfo)); + const updateUser = (userInfo: UserInfo) => + dispatch(actions.updateUser(userInfo)); // Docketeer 8.0 - We don't think the below useEffect function served a purpose any more and it caused issues with testing. This should probably be deleted // Need to set the app element to body for screen-readers (disability), otherwise modal will throw an error @@ -50,21 +51,23 @@ const Login = () => { // }, []); // callback function invoked when 'login' button is clicked - const handleLogin = (e: React.ChangeEvent | React.ChangeEvent) => { + const handleLogin = ( + e: React.ChangeEvent | React.ChangeEvent + ) => { e.preventDefault(); // prevents form submit from reloading page - console.log("Event:", e) - const usernameInput = document.getElementById("username"); - const passwordInput = document.getElementById("password"); - if(usernameInput != null || passwordInput != null) { + console.log('Event:', e); + const usernameInput = document.getElementById('username'); + const passwordInput = document.getElementById('password'); + if (usernameInput != null || passwordInput != null) { const username: string = (usernameInput as HTMLInputElement).value; const password: string = (passwordInput as HTMLInputElement).value; - + // clears input fields after login - (usernameInput as HTMLInputElement).value = ""; - (passwordInput as HTMLInputElement).value = ""; - - console.log("username:", username); - console.log("password:", password); + (usernameInput as HTMLInputElement).value = ''; + (passwordInput as HTMLInputElement).value = ''; + + console.log('username:', username); + console.log('password:', password); authenticateUser(username, password); } }; @@ -72,16 +75,16 @@ const Login = () => { // callback function which will send request to endpoint http://localhost:3000/login and expect // either SSID in cookie. const authenticateUser = (username: string, password: string) => { - console.log("YOU ARE HERE!") - fetch("http://localhost:3000/login", { - method: "POST", + console.log('YOU ARE HERE!'); + fetch('http://localhost:3000/login', { + method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, body: JSON.stringify({ username: username, - password: password - }) + password: password, + }), }) .then((response) => response.json()) .then((data) => { @@ -103,50 +106,56 @@ const Login = () => { return (
    - +



    -
    -
    -

    Login

    +
    +
    +

    Login

    -
    -
    ) => handleLogin(e)}> - + ) => handleLogin(e)} + > +


    {/* * Submit Button * */}
    @@ -156,4 +165,4 @@ const Login = () => { ); }; -export default Login; \ No newline at end of file +export default Login; From ca8d94318956f2b52e8703e54fb51ce207094b9c Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Mon, 5 Dec 2022 19:34:16 -0600 Subject: [PATCH 081/110] updated to capturing role id and the route to the backend Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- server/app.js | 6 +++ server/controllers/userController.js | 27 ++++++++---- server/routes/signupSysAdminRouter.js | 20 +++++++++ src/components/display/NewUserDisplay.tsx | 27 +++++++++++- .../helper/{commands.js => commands.tsx} | 42 ++++++++++--------- src/components/helper/newUserHelper.js | 10 +++-- 6 files changed, 101 insertions(+), 31 deletions(-) create mode 100644 server/routes/signupSysAdminRouter.js rename src/components/helper/{commands.js => commands.tsx} (88%) diff --git a/server/app.js b/server/app.js index de1018a6..f7fca6c3 100644 --- a/server/app.js +++ b/server/app.js @@ -4,7 +4,10 @@ const cors = require('cors'); const colors = require('colors'); // Routers +// sign up for sysadmin to add new users to their account const signupRouter = require('./routes/signupRouter'); +// sign up for new sysadmin users +const signupSysAdminRouter = require('./routes/signupSysAdminRouter'); const loginRouter = require('./routes/loginRouter'); const adminRouter = require('./routes/adminRouter'); const accountRouter = require('./routes/accountRouter'); @@ -28,7 +31,10 @@ app.use('/test', (req, res) => { app.use('/settings', settingsRouter); app.use('/init', initRouter); +// sign up for sysadmin to add new users to their account app.use('/signup', signupRouter); +// sign up for new sysadmin users +// app.use('/signupsysadmin', signupSysAdminRouter); app.use('/login', loginRouter); app.use('/admin', adminRouter); app.use('/account', accountRouter); diff --git a/server/controllers/userController.js b/server/controllers/userController.js index 9a96feed..0e49f502 100644 --- a/server/controllers/userController.js +++ b/server/controllers/userController.js @@ -10,12 +10,25 @@ const userController = {}; userController.createUser = (req, res, next) => { if (res.locals.error) return next(); - const { username, email, phone } = req.body; + const { username, email, phone, role_id} = req.body; const { hash } = res.locals; + let role; + + switch(role_id) { + case '1': + role = 'system admin'; + break; + case '2': + role = 'admin'; + break; + case '3': + role = 'user'; + break; + } - const createUser = 'INSERT INTO users (username, email, password, phone, role) VALUES ($1, $2, $3, $4, \'user\') RETURNING *;'; - const userDetails = [username, email, hash, phone]; - + const createUser = 'INSERT INTO users (username, email, password, phone, role, role_id) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *;'; + const userDetails = [username, email, hash, phone, role, role_id]; + console.log('USERDETAILS:', userDetails); if (username && hash) { db.query(createUser, userDetails) .then((data) => { @@ -117,9 +130,9 @@ userController.switchUserRole = (req, res, next) => { 'system admin': 1, admin: 2, user: 3 - } + }; - const { _id, role } = req.body + const { _id, role } = req.body; if(res.locals.sysAdmins === 1 && _id == res.locals.id){ res.locals.hasError = true; @@ -128,7 +141,7 @@ userController.switchUserRole = (req, res, next) => { const query = 'UPDATE users SET role = $1, role_id = $2 WHERE _id = $3 RETURNING *;'; - const parameters = [role, roleMap[role], _id] + const parameters = [role, roleMap[role], _id]; db.query(query, parameters) .then((data) => { diff --git a/server/routes/signupSysAdminRouter.js b/server/routes/signupSysAdminRouter.js new file mode 100644 index 00000000..3159d68f --- /dev/null +++ b/server/routes/signupSysAdminRouter.js @@ -0,0 +1,20 @@ +/** + * @module SignupRouter + * @description Routes all requests to signup endpoint + */ +const express = require('express'); +// grab username, verify that it is unique +// hash and set cookies +// hash and salt the password +// store username, password, role_id, and cookie in database +const signupController = require('../controllers/signupController'); +const bcryptController = require('../controllers/bcryptController'); +const userController = require('../controllers/userController'); + +const router = express.Router(); + +router.post('/', + signupController.usernameCheck, + bcryptController.hashNewPassword, + userController.createUser, + (req, res) => res.status(200).json('successfully added new sysadmin account')); \ No newline at end of file diff --git a/src/components/display/NewUserDisplay.tsx b/src/components/display/NewUserDisplay.tsx index b9312669..34d73b21 100644 --- a/src/components/display/NewUserDisplay.tsx +++ b/src/components/display/NewUserDisplay.tsx @@ -8,6 +8,11 @@ import { useSelector, useDispatch } from "react-redux"; // Material UI Imports import TextField from "@mui/material/TextField"; import Button from "@mui/material/Button"; +import FormControlLabel from '@mui/material/FormControlLabel'; +import FormControl from '@mui/material/FormControl'; +import FormLabel from '@mui/material/FormLabel'; +import RadioGroup from "@mui/material/RadioGroup"; +import Radio from "@mui/material/Radio"; import { handleNewUser, @@ -16,6 +21,13 @@ import { checkPhone, } from "../helper/newUserHelper"; +// this will store the value from the user role +let valueRole = '3'; +//setting value of the RadioGroup MUI Component to the one selected by the user +const handleSelect = (event: React.ChangeEvent) => { + valueRole = (event.target as HTMLInputElement).value; +} + const NewUserDisplay = () => { return (
    @@ -94,13 +106,26 @@ const NewUserDisplay = () => { }} />
    + + + } label="System Admin"> + } label="Admin"> + } label="User"> + + +

    -
    -
    + New SysAdmin Sign Up
    From 9ac49bdea296034be5c9309d2c6d46b8cfa6fa85 Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Sat, 10 Dec 2022 15:01:12 -0600 Subject: [PATCH 084/110] completed signup frontend Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/css/styles.css | 8 +- src/components/display/LoginDisplay.tsx | 0 src/components/display/SignUpDisplay.tsx | 106 +++++++++++++++++++++++ src/components/helper/newUserHelper.js | 1 + src/components/login/login.tsx | 38 +++----- src/renderer/App.tsx | 4 +- 6 files changed, 126 insertions(+), 31 deletions(-) create mode 100644 src/components/display/LoginDisplay.tsx create mode 100644 src/components/display/SignUpDisplay.tsx diff --git a/src/components/css/styles.css b/src/components/css/styles.css index 253f118b..ab84f17e 100644 --- a/src/components/css/styles.css +++ b/src/components/css/styles.css @@ -661,8 +661,12 @@ ul { width: fit-content; } -.link-signup { - font-size: small; +.btn-signup { + border: none; + color: rgb(0, 153, 255); + background-color: none; + text-decoration: underline; + cursor: pointer; } .settingsForm { diff --git a/src/components/display/LoginDisplay.tsx b/src/components/display/LoginDisplay.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/display/SignUpDisplay.tsx b/src/components/display/SignUpDisplay.tsx new file mode 100644 index 00000000..67fae22e --- /dev/null +++ b/src/components/display/SignUpDisplay.tsx @@ -0,0 +1,106 @@ +import React from "react"; +import { useNavigate } from 'react-router-dom'; + +// Material UI Imports +import TextField from "@mui/material/TextField"; +import Button from "@mui/material/Button"; + +// Helper Functions +import { + handleNewUser, + checkPasswordLength, + confirmPassword, + checkPhone, + } from "../helper/newUserHelper"; + +const SignUp = () => { + const navigate = useNavigate(); + + return ( +
    +
    +

    Sign Up

    +
    +
    +
    + +
    + +
    + checkPasswordLength()} + sx={{ + m: 1, + }} + /> + +
    + confirmPassword()} + sx={{ + m: 1, + }} + /> + +
    + { + //fixing the property 'value' does not exist on type 'HTMLElement' + const inputValue = ( + document.getElementById("signupPhone") as HTMLTextAreaElement + ).value; + checkPhone(inputValue); + }} + sx={{ + m: 1, + }} + /> +
    + +
    + + +
    +
    + ) +} + +export default SignUp; \ No newline at end of file diff --git a/src/components/helper/newUserHelper.js b/src/components/helper/newUserHelper.js index fc773052..f9cfc9d7 100644 --- a/src/components/helper/newUserHelper.js +++ b/src/components/helper/newUserHelper.js @@ -7,6 +7,7 @@ import * as actions from '../../redux/actions/actions'; export const handleNewUser = (e, roleID) => { e.preventDefault(); + console.log('This is role id:', roleID); const username = document.getElementById('signupUsername').value; const password = document.getElementById('signupPassword').value; diff --git a/src/components/login/login.tsx b/src/components/login/login.tsx index 6f090bb1..d79cef27 100644 --- a/src/components/login/login.tsx +++ b/src/components/login/login.tsx @@ -2,12 +2,13 @@ * @module Login * @description Login component which renders a login page, and sign-up modal. This is the first component that is appended to the dist/.renderer-index-template.html via renderer/index.js */ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { useNavigate } from 'react-router-dom'; -import { useSelector, useDispatch } from 'react-redux'; +import { useDispatch } from 'react-redux'; import * as actions from '../../redux/actions/actions'; +//MUI Elements import TextField from '@mui/material/TextField'; import Button from '@mui/material/Button'; @@ -34,45 +35,25 @@ const Login = () => { const updateSession = () => dispatch(actions.updateSession()); const updateUser = (userInfo: UserInfo) => dispatch(actions.updateUser(userInfo)); - // Docketeer 8.0 - We don't think the below useEffect function served a purpose any more and it caused issues with testing. This should probably be deleted - // Need to set the app element to body for screen-readers (disability), otherwise modal will throw an error - // useEffect(() => { - // fetch("http://localhost:3000/db") - // .then((response) => { - // return response.json(); - // }) - // .then((data) => { - // return console.log("Connected to DB successfully", data); - // }) - // .catch((err) => { - // return console.log("Fetch request to /db failed:", err); - // }); - // }, []); - // callback function invoked when 'login' button is clicked const handleLogin = (e: React.ChangeEvent | React.ChangeEvent) => { - e.preventDefault(); // prevents form submit from reloading page - console.log("Event:", e) + e.preventDefault(); + //check that username and password are inputted const usernameInput = document.getElementById("username"); const passwordInput = document.getElementById("password"); if(usernameInput != null || passwordInput != null) { const username: string = (usernameInput as HTMLInputElement).value; const password: string = (passwordInput as HTMLInputElement).value; - // clears input fields after login (usernameInput as HTMLInputElement).value = ""; (passwordInput as HTMLInputElement).value = ""; - - console.log("username:", username); - console.log("password:", password); + authenticateUser(username, password); } }; // callback function which will send request to endpoint http://localhost:3000/login and expect - // either SSID in cookie. const authenticateUser = (username: string, password: string) => { - console.log("YOU ARE HERE!") fetch("http://localhost:3000/login", { method: "POST", headers: { @@ -88,6 +69,7 @@ const Login = () => { if (Object.prototype.hasOwnProperty.call(data, 'error')) { window.alert(data.error); } else { + // are the two below functions necessary? updateSession(); // loggedIn = true updateUser(data); // update user info in sessions reducer navigate('/'); @@ -130,7 +112,7 @@ const Login = () => { value='belugas' />
    - {/* * Submit Button * */} + {/* * Login Button * */} - New SysAdmin Sign Up + -
    +
    ); diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index c1fd31e5..c3a09c32 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -3,14 +3,16 @@ import { Routes, Route } from 'react-router-dom'; // Components import Login from '../components/login/login'; -import RenderViews from '../components/RenderViews'; import Authentication from '../components/Authentication'; +import SignUp from '../components/display/SignUpDisplay'; +import RenderViews from '../components/RenderViews'; const App = () => { return ( } /> } /> + } /> } /> ); From 53d42253f4e0e8326e405ec7609a742353d14b0c Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Sat, 10 Dec 2022 15:19:08 -0600 Subject: [PATCH 085/110] moved and renamed login file Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/{login/login.tsx => display/LoginDisplay.tsx} | 0 src/renderer/App.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/components/{login/login.tsx => display/LoginDisplay.tsx} (100%) diff --git a/src/components/login/login.tsx b/src/components/display/LoginDisplay.tsx similarity index 100% rename from src/components/login/login.tsx rename to src/components/display/LoginDisplay.tsx diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index c1fd31e5..176eabb8 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Routes, Route } from 'react-router-dom'; // Components -import Login from '../components/login/login'; +import Login from '../components/display/LoginDisplay'; import RenderViews from '../components/RenderViews'; import Authentication from '../components/Authentication'; From 4322ae804254f6a098ae9e4d4ab8da50d8bca074 Mon Sep 17 00:00:00 2001 From: Jack Date: Sat, 10 Dec 2022 17:54:20 -0500 Subject: [PATCH 086/110] add tests and user signup Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- .vscode/settings.json | 3 + __tests__/ContainersTab.test.js | 180 ++------------------ __tests__/ImageTab.test.js | 108 ++++++------ __tests__/ListReducer.test.js | 26 +-- __tests__/MetricsTab.test.js | 9 - __tests__/ProcessLogHelper.test.js | 67 ++++---- __tests__/README.Test.md | 1 - __tests__/ServerRoutes.test.js | 31 ++-- __tests__/UsersTab.test.js | 93 ++++++---- __tests__/VolumeTab.test.js | 8 - __tests__/loginPage.js | 28 ++- package.json | 3 + server/app.js | 7 +- server/controllers/initController.js | 69 ++++---- server/controllers/userController.js | 6 +- server/routes/initRouter.js | 6 +- server/routes/loginRouter.js | 1 + server/server.js | 1 - src/components/css/styles.css | 1 + src/components/display/LineChartDisplay.tsx | 1 + src/components/display/NewUserDisplay.js | 108 +++++++++++- src/components/helper/commands.js | 47 ++--- src/components/helper/newUserHelper.js | 8 +- src/components/helper/processLogHelper.js | 1 - src/components/tabs/Containers.tsx | 2 +- src/components/tabs/Users.js | 7 +- src/components/views/UserView.tsx | 1 + src/main/index.ts | 1 + src/main/preload.js | 6 +- src/renderer/App.tsx | 2 +- webpack.react.config.js | 5 +- 31 files changed, 401 insertions(+), 436 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 __tests__/README.Test.md diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..d190a4d8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "workbench.localHistory.enabled": true +} \ No newline at end of file diff --git a/__tests__/ContainersTab.test.js b/__tests__/ContainersTab.test.js index 0ab129c5..6558b941 100644 --- a/__tests__/ContainersTab.test.js +++ b/__tests__/ContainersTab.test.js @@ -1,14 +1,10 @@ -import React, { Component } from 'react'; +import React from 'react'; import Containers from '../src/components/tabs/Containers'; -import {describe, expect, test, jest} from '@jest/globals'; -import '@testing-library/react'; +import {describe, beforeEach, expect, test, jest} from '@jest/globals'; import '@testing-library/jest-dom'; -import { Chart } from 'react-chartjs-2'; import ToggleDisplay from '../src/components/display/ToggleDisplay'; -// Started to migrate to React-Testing-Library... -import { create } from 'react-test-renderer'; import { fireEvent, render, screen } from '@testing-library/react'; - +// import { Chart } from 'react-chartjs-2'; const props = { runningList: [ @@ -26,81 +22,56 @@ const props = { stoppedList: [ { Names: 'zealous', - ID: 'c902ec744095', // only this property was correctly referenced! + ID: 'c902ec744095', Image: '84c5f6e03bf0', RunningFor: '2 days ago', - Img: '84c5f6e03bf0', // this property is not used... - Created: '2 days ago', // this property is not used - name: 'zealous_pare' // this property is also not used anywhere } ], + container: {MemUsage:1}, stop: jest.fn(), remove: jest.fn(), runStopped: jest.fn() }; -/** Docketeer 7.0 - * This was the previous groups code, we left commented just incase it became useful down the road. - */ - -// Debug test describe('Containers', () => { + beforeEach(()=>{ render(); }); describe('Running List containers', () => { - test('Should have render correct amount of containers', () => { + test('Should have render correct amount of running containers', () => { const runningContainers = screen.getByText('Running Containers', {exact:false}); const text = runningContainers.innerHTML; - // console.log(text) - // screen.debug(runningContainers) expect(text).toEqual(`Running Containers: ${props.runningList.length}`); }); test('Name of container should properly display', ()=>{ const h3 = screen.getAllByRole('heading', { level: 3 }); const name = h3[0].innerHTML; - expect(name).toEqual('blissful_matsumoto'); - console.log(name); - }); - - test('Show details button works', async () => { - - // const mockButton = jest.fn(ToggleDisplay); - // screen.debug(mockButton) - // mockButton(); - // expect(mockButton).toBeCalled; - - // this test is very basic... - // i don't think we can fully check functionality without using chart.js - const buttons = screen.getAllByRole('button'); - const showDetails = buttons[0]; - await fireEvent.click(showDetails); - expect(showDetails).toBeCalled - screen.debug() + expect(name).toEqual('Name: blissful_matsumoto'); }); test('Stop button is called', async () => { const stopButton = document.querySelector('.stop-btn'); await fireEvent.click(stopButton); - screen.debug(); - expect(stopButton).toBeCalled; }); - test('Wanted to test toggle display',() => { - render(); - screen.debug(); - expect(1).toBe(1); - }); + test('Toggle Display button works', () => { + render(); + const button = screen.getAllByRole('button'); + expect(button[4]).toHaveTextContent('Show Details'); + fireEvent.click(button[4]); + expect(button[4]).toHaveTextContent('Hide Details'); + }); }); describe('Stopped List Containers', () => { - xtest('Should have render correct amount of containers', () => { + test('Should have render correct amount of containers', () => { const exitedContainers = screen.getByText('Exited Containers', {exact:false}); const text = exitedContainers.innerHTML; expect(text).toEqual(`Exited Containers: ${props.stoppedList.length}`); @@ -118,125 +89,10 @@ describe('Containers', () => { await fireEvent.click(runButton); await fireEvent.click(removeButton); expect(runButton).toBeCalled; + expect(removeButton).toBeCalled; + }); }); }); -// check if chart autorefreshes? - - - -// function shallowSetup() { -// const props = { -// runningList: [ -// { -// block: '0B/0B', -// ID: 'a802306eeac3', -// CPUPerc: '0.17%', -// MemPerc: '0.11%', -// MemUsage: '2.195MiB/1.944GiB', -// Name: 'blissful_matsumoto', -// NetIO: '796B/0B', -// PIDs: '5' -// } -// ], -// stoppedList: [ -// { -// Name: 'zealous', -// ID: 'c902ec744095', -// Img: '84c5f6e03bf0', -// Created: '2 days ago', -// name: 'zealous_pare' -// } -// ] -// }; -// const reactWrapper = create(); - -// return { -// props, -// reactWrapper -// }; -// } - -// describe('Running containers are being rendered', () => { -// test('Should render
    tag that has title renderContainers in Running', () => { -// // Testing for if there is a container with the title of renderContainer -// render(); - -// const renderContainer = screen.getByTitle('renderContainers'); -// expect(renderContainer).toHaveClass('renderContainers'); -// }); - -// test('Should render the correct number of containers', () => { -// const { container } = render(); -// const containers = container.getElementsByClassName('containers'); -// expect(containers.length).toBe(1); -// }); -// }); - -// describe('It should render the exited containers', () => { -// test('Should have a className run-btn in Stopped component', () => { -// render(); - -// const runBtnRender = screen.getByTestId('run-btn'); - -// expect(runBtnRender).toHaveClass('run-btn'); -// }); - -/** Docketeer 7.0 - * These are all preliminary tests that were not veted out. Could be useful as a starting point. - */ - -// ! NEED TO FIGURE OUT HOW TO ADD ONCLICK TEST -// test('ClassName run-btn in stopped component have onClick function', () => { -// const handleOnClick = jest.fn(); - -// render(); - -// const runBtnRender = screen.queryByText('RUN'); -// // screen.queryByText('RUN'); - -// fireEvent.click(runBtnRender); - -// expect(runBtnRender).toHaveBeenCalledTimes(1); -// }); -// }); - -// describe('It should render the exited containers', () => { -// const { reactWrapper } = shallowSetup(); -// ! test('Should render
    tag in Stopped', () => { -// ! expect(reactWrapper.type()).toEqual('div'); -// ! }); - -//* test('Should have className run-btn in Stopped component', () => { -//* expect( -//* reactWrapper.find('.stopped-button').props().children[0].props.className -//* ).toEqual('run-btn'); -//* }); - -// test('ClassName run-btn in Stopped component have onClick function', () => { -// expect( -// reactWrapper.find('.stopped-button').props().children[0].props.onClick -// ).toBeDefined(); -// }); - -// test('Should have className stop-btn in Stopped component', () => { -// expect( -// reactWrapper.find('.stopped-button').props().children[1].props.className -// ).toEqual('stop-btn'); -// }); - -// test('ClassName stop-btn in Stopped component have onClick function', () => { -// expect( -// reactWrapper.find('.stopped-button').props().children[1].props.onClick -// ).toBeDefined(); -// }); -// }); - -//* Dummy Test -describe('dummy test', () => { - test('dummy test', () => { - expect(2 + 2).toBe(4); - }); -}); \ No newline at end of file diff --git a/__tests__/ImageTab.test.js b/__tests__/ImageTab.test.js index e98d1647..3b6e8a51 100644 --- a/__tests__/ImageTab.test.js +++ b/__tests__/ImageTab.test.js @@ -1,17 +1,8 @@ -/** Docketeer 7.0 - * These tests do not work as enzyme is highly depricated and does not communicate with React 18 - */ - import React from 'react'; -import { describe, expect, test, jest } from '@jest/globals'; +import { describe, beforeEach, expect, test, jest } from '@jest/globals'; import Images from '../src/components/tabs/Images'; -// import ImageUsers from '../src/components/tabs/ImagesUser'; -import '@testing-library/react'; -import '@testing-library/jest-dom'; import { fireEvent, - getByLabelText, - getByTestId, render, screen, } from '@testing-library/react'; @@ -19,9 +10,10 @@ import { const props = { imagesList: [ { - ID: '2718634043dc', - Size: '111 MB', - Name: 'redis', + imgid: '2718634043dc', + size: '111 MB', + reps: 'Redis', + tag: 16.4, }, ], runIm: jest.fn(), @@ -29,57 +21,61 @@ const props = { onClick: jest.fn(), }; -/* ----- search bar ----- */ -describe('Seach bar testing', () => { - test('Search accepts input', async () => { - const { container } = render(); - const search = screen.getByRole('textbox'); - await fireEvent.change(search, { target: { value: 'search' } }); - expect(search.value).toBe('search'); - }); -}); +describe('Images', () => { -/* ----- button testing ------ */ + beforeEach(() => { + render(); + screen.debug(); + }); -describe('run button on click', () => { - test('fires run button functionality', async () => { - const { container } = render(); - const runButton = screen.getByRole('button', { name: 'RUN' }); - await fireEvent.click(runButton); - expect(runButton).toBeCalled; + /* ----- search bar ----- */ + describe('Seach bar testing', () => { + test('Search accepts input', async () => { + const search = screen.getByRole('textbox'); + await fireEvent.change(search, { target: { value: 'search' } }); + expect(search.value).toBe('search'); + }); }); -}); -// currently gets stuck at window.runExec method --> reads undefined -// describe('pull button on click', () => { -// test('fires pull button functionality', () => { -// const { container } = render(); -// const pullButton = screen.getByRole('button', { name: 'Pull' }); -// fireEvent.click(pullButton); -// expect(pullButton).toBeCalled; -// }); -// }); + /* ----- button testing ------ */ -describe('remove button on click', () => { - test('fires remove button functionality', async () => { - const { container } = render(); - const removeButton = screen.getByRole('button', { name: 'REMOVE' }); - await fireEvent.click(removeButton); - expect(removeButton).toBeCalled; + describe('Run button on click', () => { + test('Fires run button functionality', async () => { + const runButton = screen.getByRole('button', { name: 'RUN' }); + await fireEvent.click(runButton); + expect(runButton).toBeCalled; + }); }); -}); -/* ------ actions/reducers ------ */ -describe('Images', () => { - test('renders an image if one is found', () => { - render(); + describe('Remove button on click', () => { + test('Fires remove button functionality', async () => { + const removeButton = screen.getByRole('button', { name: 'REMOVE' }); + await fireEvent.click(removeButton); + expect(removeButton).toBeCalled; + }); }); -}); -//* Dummy Test -describe('dummy test', () => { - test('dummy test', () => { - expect(2 + 2).toBe(4); + // currently gets stuck at window.runExec method --> reads undefined + // describe('pull button on click', () => { + // test('fires pull button functionality', () => { + // const { container } = render(); + // const pullButton = screen.getByRole('button', { name: 'Pull' }); + // fireEvent.click(pullButton); + // expect(pullButton).toBeCalled; + // }); + // }); + + describe('Images', () => { + test('Renders an image if one is found', () => { + const name = screen.getByText('Redis'); + const id = screen.getByText('2718634043dc'); + const size = screen.getByText('111 MB'); + const tag = screen.getByText(16.4); + expect(name).toBeDefined; + expect(id).toBeDefined; + expect(size).toBeDefined; + expect(tag).toBeDefined; + }); }); -}); +}); \ No newline at end of file diff --git a/__tests__/ListReducer.test.js b/__tests__/ListReducer.test.js index 26f4e067..64b53896 100644 --- a/__tests__/ListReducer.test.js +++ b/__tests__/ListReducer.test.js @@ -1,13 +1,6 @@ -/** Docketeer 7.0 - * These tests do not, might be an issue with the word export. - */ - import containerListReducer from '../src/redux/reducers/containerListReducer'; // import containerList reducer import imageListReducer from '../src/redux/reducers/imageListReducer'; // import imageListReducer reducer -import {describe, expect, test, jest} from '@jest/globals'; -import '@testing-library/react'; -import '@testing-library/jest-dom'; -import { fireEvent, render, screen } from '@testing-library/react'; +import {describe, beforeEach, expect, test} from '@jest/globals'; describe('Dockeeter reducer', () => { let state; @@ -21,13 +14,13 @@ describe('Dockeeter reducer', () => { }); describe('Action Types', () => { - it('Should return initial state if type is invalid', () => { + test('Should return initial state if type is invalid', () => { expect(containerListReducer(state, { type: 'FakeActionType' })).toBe(state); }); }); describe('REFRESH_RUNNING_CONTAINERS', () => { - it('Should return a different state with each reducer invocation', () => { + test('Should return a different state with each reducer invocation', () => { expect(state.runningList.length).toEqual(0); let action = { type: 'REFRESH_RUNNING_CONTAINERS', @@ -49,7 +42,7 @@ describe('Dockeeter reducer', () => { }); describe('REFRESH_STOPPED_CONTAINERS', () => { - it('should overwrite the stoppedList array in the state to update it', () => { + test('should overwrite the stoppedList array in the state to update it', () => { expect(state.stoppedList.length).toEqual(0); let action = { type: 'REFRESH_STOPPED_CONTAINERS', @@ -71,7 +64,7 @@ describe('Dockeeter reducer', () => { }); describe('REFRESH_IMAGES', () => { - it('should overwrite the imagesList array in the state to update it', () => { + test('should overwrite the imagesList array in the state to update it', () => { expect(state.imagesList.length).toEqual(0); let action = { type: 'REFRESH_IMAGES', @@ -85,7 +78,7 @@ describe('Dockeeter reducer', () => { }); describe('REMOVE_CONTAINER', () => { - it('should remove the specified container from the stoppedList array in the state', () => { + test('should remove the specified container from the stoppedList array in the state', () => { const newState = { stoppedList: [{ ID: '123' }, { ID: '456' }] }; @@ -98,7 +91,7 @@ describe('Dockeeter reducer', () => { }); describe('STOP_RUNNING_CONTAINER', () => { - it('should remove a specified container from the runningList and add it to the stoppedList', () => { + test('should remove a specified container from the runningList and add it to the stoppedList', () => { let newState = { runningList: [{ ID: '123' }, { ID: '456' }], stoppedList: [] @@ -110,13 +103,12 @@ describe('Dockeeter reducer', () => { }); describe('RUN_STOPPED_CONTAINER', () => { - it('should remove a specified container from the stoppedList', () => { + test('should remove a specified container from the stoppedList', () => { const newState = { runningList: [], stoppedList: [{ ID: '123' }, { ID: '456' }] }; const action = { type: 'RUN_STOPPED_CONTAINER', payload: '123' }; - console.log(newState); expect(containerListReducer(newState, action).stoppedList[0].ID).toEqual( '456' ); @@ -124,7 +116,7 @@ describe('Dockeeter reducer', () => { }); describe('REMOVE_IMAGE', () => { - it('should remove a specified image from the imagesList', () => { + test('should remove a specified image from the imagesList', () => { const newState = { imagesList: [{ id: '123' }, { id: '456' }] }; diff --git a/__tests__/MetricsTab.test.js b/__tests__/MetricsTab.test.js index e5bcd9d4..92b60f58 100644 --- a/__tests__/MetricsTab.test.js +++ b/__tests__/MetricsTab.test.js @@ -1,9 +1,3 @@ -/** Docketeer 7.0 - * These tests do not work as enzyme is highly depricated and does not communicate with React 18 - */ - -/* ----- testing metrics ----- */ - import React, { Component } from 'react'; import Metrics from '../src/components/tabs/Metrics'; import {describe, expect, test, jest} from '@jest/globals'; @@ -17,9 +11,6 @@ const props = { runningList: [{ BlockIO: '1B/2B', ID: '6f49565a501c', CPUPerc: '20.00%', MemPerc: '0.00%', MemUsage: '5B/6B', Name: 'checkpoint_nginx_1', NetIO: '3B/4B', PIDs: '0' }, { BlockIO: '3B/4B', ID: '6f49565a501c', CPUPerc: '30.00%', MemPerc: '20.00%', MemUsage: '5B/6B', Name: 'checkpoint_nginx_2', NetIO: '5B/6B', PIDs: '0' }] }; -// Docketeer 8.0 -// Testing chart.js might be better handled through component rather than testing suite - // describe('Metrics tab should render', () => { // beforeEach(()=>{ // render( diff --git a/__tests__/ProcessLogHelper.test.js b/__tests__/ProcessLogHelper.test.js index 424386af..c8320c24 100644 --- a/__tests__/ProcessLogHelper.test.js +++ b/__tests__/ProcessLogHelper.test.js @@ -6,25 +6,27 @@ import { makeArrayOfObjects, buildOptionsObj } from '../src/components/helper/processLogHelper.js'; -import React from 'react'; +import {describe, beforeEach,afterEach, expect, test} from '@jest/globals'; describe('makeArrayOfObjects', () => { - it('returns an array', () => { - const string = `Hello from Docker! + test('returns an array', () => { + const string = `HelloZ from Docker! This message shows that your installation appears to be working correctly. `; const result = makeArrayOfObjects(string); - console.log(result); expect(result).toBeInstanceOf(Array); + expect(result.length).toEqual(2); expect(result.containerName).toBe(undefined); + expect(result[0].logMsg).toEqual('HelloZ from Docker!'); + expect(result[0].timeStamp).toBeUndefined(); }); - - it('each element in returned array is of type object', () => { + + // Can be addressed through TS + test('each element in returned array is of type object', () => { const processLog = 'this is the timestamp\nthis is the log message\nthis is the second timestamp\nthis is the second log message'; const result = makeArrayOfObjects(processLog); - let output = false; if(result.every((element) => typeof element === 'object')){ @@ -33,29 +35,8 @@ describe('makeArrayOfObjects', () => { expect(output).toEqual(true); }); - +}); - // //We edited the makeArrayOfObjects function and now this fails, not sure why as there still is a key of logMsg and timeStamp - // it('each object in returned array has timeStamp and logMsg properties', () => { - // const processLog = - // 'this_is_the_first_timestampZ this is the first log message\nthere is no second time stamp but there is a second log message'; - // const result = makeArrayOfObjects(processLog); - -// let output = false; - -// if ( -// result.every( -// (element) => -// element.timeStamp && element.logMsg && element.containerName -// ) -// ) { -// output = true; -// } - -// expect(output).toEqual(true); -// }); -// }); - describe('buildOptionsObj', () => { let sinceButton; @@ -89,19 +70,35 @@ describe('buildOptionsObj', () => { document.body.innerHTML = ''; }); - it('when tail button is checked, tail value is added to optionsObj', () => { + test('when tail button is checked, tail value is added to optionsObj', () => { tailButton.checked = true; - const result = buildOptionsObj('containerID'); - expect(result.tail).toEqual('1'); }); - it('when since button is checked, since value is added to since key on optionsObj', () => { + test('when since button is checked, since value is added to since key on optionsObj', () => { sinceButton.checked = true; - const result = buildOptionsObj('containerID'); - expect(result.since).toEqual('72h10m3s'); }); }); + +// //We edited the makeArrayOfObjects function and now this fails, not sure why as there still is a key of logMsg and timeStamp +// test('each object in returned array has timeStamp and logMsg properties', () => { +// const processLog = +// 'this_is_the_first_timestampZ this is the first log message\nthere is no second time stamp but there is a second log message'; +// const result = makeArrayOfObjects(processLog); + +// let output = false; + +// if ( +// result.every( +// (element) => +// element.timeStamp && element.logMsg && element.containerName +// ) +// ) { +// output = true; +// } + +// expect(output).toEqual(true); +// }); \ No newline at end of file diff --git a/__tests__/README.Test.md b/__tests__/README.Test.md deleted file mode 100644 index 02d2d62d..00000000 --- a/__tests__/README.Test.md +++ /dev/null @@ -1 +0,0 @@ -Because of the migration of our new dependencies, the original tests no longer work with Reacat 18. We were in the process of migrating away from enzyme but did not have time to fully implement. Would be a great task to pick up for the next iteration team. \ No newline at end of file diff --git a/__tests__/ServerRoutes.test.js b/__tests__/ServerRoutes.test.js index 34ab3876..8229eb23 100644 --- a/__tests__/ServerRoutes.test.js +++ b/__tests__/ServerRoutes.test.js @@ -2,6 +2,7 @@ const supertest = require('supertest'); const request = require('supertest'); const assert = require('assert'); const express = require('express'); +import {describe, beforeEach, expect, test, jest} from '@jest/globals'; const app = express(); @@ -12,10 +13,10 @@ app.use('/test', (req, res) => { }); describe('/test route', () => { - it('get request to test route', (done) => { + test('get request to test route', (done) => { request(app).get('/test').expect('Content-Type', /json/).expect(200, done); }); - it('post requeust to test route', (done) => { + test('post requeust to test route', (done) => { request(app) .post('/test') .send({ random: 'info' }) @@ -23,7 +24,7 @@ describe('/test route', () => { .expect('Content-Type', /json/) .expect(200, done); }); - it('put request to test route', (done) => { + test('put request to test route', (done) => { request(app) .put('/test') .send({ random: 'info' }) @@ -31,7 +32,7 @@ describe('/test route', () => { .expect('Content-Type', /json/) .expect(200, done); }); - it('delete request to test route', (done) => { + test('delete request to test route', (done) => { request(app) .delete('/test') .send({ random: 'info' }) @@ -46,20 +47,20 @@ describe('/test route', () => { // signup route describe('/signup route', () => { - it('get request', async () => { + test('get request', async () => { await request(app) .get('/signup') .send({ username: 'test', email: 'test@test.com', password: 'password' }) .expect('Content-Type', 'text/html; charset=utf-8'); }); - it('post request', async () => { + test('post request', async () => { await request(app) .post('/signup') .send({ username: 'test', email: 'test@test.com', password: 'password', - phone: '+15555555555', + phone: '+1555555555', }) .set('Accept', 'application/json') .expect('Content-Type', 'text/html; charset=utf-8'); @@ -67,7 +68,14 @@ describe('/signup route', () => { }); // setting route - +describe('Settings route', (done) =>{ + test('GET', async () => { + await request(app) + .get('/settings') + .expect('Content-Type', 'text/html; charset=utf-8') + .expect(200,done) + }) +}) // logout route // login route @@ -219,9 +227,4 @@ describe('/signup route', () => { // }); // }); -//* Dummy Test -describe('dummy test', () => { - test('dummy test', () => { - expect(2 + 2).toBe(4); - }); -}); + diff --git a/__tests__/UsersTab.test.js b/__tests__/UsersTab.test.js index 7fc21248..1c7abfa8 100644 --- a/__tests__/UsersTab.test.js +++ b/__tests__/UsersTab.test.js @@ -4,14 +4,13 @@ import '@testing-library/react'; import '@testing-library/jest-dom'; import { fireEvent, - getByLabelText, - getByTestId, render, screen, - waitFor, } from '@testing-library/react'; import NewUserDisplay from '../src/components/display/NewUserDisplay'; -import * as helpers from '../src/components/helper/newUserHelper'; +import fetchMock from 'jest-fetch-mock'; + +fetchMock.enableMocks(); const props = { onSubmit: jest.fn(), @@ -31,40 +30,74 @@ const props = { /* ----- Create a New User ----- */ describe('Create a New User functionality', () => { - // render NewUserDisplay + beforeEach(() => { render(); }); - // input fields take in text - test('input fields take in text', () => { - const input = document.getElementById('signupEmail'); - expect(input).toHaveValue(''); - }); - // password is at least 6 chars long - test('Password must be 6 characters long', async () => { - const password = document.getElementById('signupPassword'); - await fireEvent.change(password, { target: { value: '123456' } }); - expect(password.value).toBe('123456'); + describe ('Input fields', () => { + test('Email', () => { + const email = document.getElementById('signupEmail'); + fireEvent.change(email, { target: {value: 'email'}}); + expect(email).toHaveValue('email'); + }); + test('Username', async () => { + const username = document.getElementById('signupUsername'); + fireEvent.change(username, { target: {value: 'bad'}}); + expect(username).toHaveValue('bad'); + }); + test('Password', () => { + const password = document.getElementById('signupPassword'); + fireEvent.change(password, { target: {value: 'password'}}); + expect(password).toHaveValue('password'); + }); + test('Password confirmation', () => { + const confirmPassword = document.getElementById('signupPasswordConfirmation'); + fireEvent.change(confirmPassword, { target: {value: 'alsopassword'}}); + expect(confirmPassword).toHaveValue('alsopassword'); + }); + test('Phone', () => { + const phone = document.getElementById('signupPhone'); + fireEvent.change(phone, { target: {value: '123'}}); + expect(phone).toHaveValue('123'); + }); }); - // password and confirm passwords match - test('Password must match Confirm Password', async () => { - const signup = document.getElementById('signupPassword'); - const confirmation = document.getElementById('signupPasswordConfirmation'); - await fireEvent.change(signup, { target: { value: '123456' } }); - await fireEvent.change(confirmation, { target: { value: '123456' } }); - expect(signup.value).toBe(confirmation.value); + + test('Input fields render and accept text', async () => { + const button = screen.getByRole('button', { name: 'Submit' }); + + await fireEvent.click(button); + + const passwordAlert = document.getElementById('password-length-alert'); + + // expect(passwordAlert).toHaveTextContent('Warning: Password must be 6 characters or longer and must include at least one number and one letter') }); + test('check that onSubmit button is working', async () => { - const onSubmit = jest.fn(); + window.alert = jest.fn(); + const email = document.getElementById('signupEmail'); + fireEvent.change(email, { target: {value: 'email'}}); + expect(email).toHaveValue('email'); + + const username = document.getElementById('signupUsername'); + fireEvent.change(username, { target: {value: 'user'}}); + expect(username).toHaveValue('user'); + + const password = document.getElementById('signupPassword'); + fireEvent.change(password, { target: {value: 'password123'}}); + expect(password).toHaveValue('password123'); + + const confirmPassword = document.getElementById('signupPasswordConfirmation'); + fireEvent.change(confirmPassword, { target: {value: 'password123'}}); + expect(confirmPassword).toHaveValue('password123'); + + const phone = document.getElementById('signupPhone'); + fireEvent.change(phone, { target: {value: '+12345678900'}}); + expect(phone).toHaveValue('+12345678900'); + const button = screen.getByRole('button', { name: 'Submit' }); await fireEvent.click(button); - expect(onSubmit).toBeCalled; + fetch.mockResponseOnce(JSON.stringify({ email:'email', username: 'user', password: 'password123', phone: '+12345678900' })); + expect(window.alert).toBeDefined(); }); - //* Dummy Test - describe('dummy test', () => { - test('dummy test', () => { - expect(2 + 2).toBe(4); - }); - }); }); diff --git a/__tests__/VolumeTab.test.js b/__tests__/VolumeTab.test.js index 2e09f2b4..4539234a 100644 --- a/__tests__/VolumeTab.test.js +++ b/__tests__/VolumeTab.test.js @@ -44,11 +44,3 @@ describe('Seach bar testing', () => { expect(search.value).toBe('search'); }); }); - - -//* Dummy Test -describe('dummy test', () => { - test('dummy test', () => { - expect(2 + 2).toBe(4); - }); -}); diff --git a/__tests__/loginPage.js b/__tests__/loginPage.js index d0b7d85a..8759df9b 100644 --- a/__tests__/loginPage.js +++ b/__tests__/loginPage.js @@ -1,20 +1,15 @@ -import '@testing-library/react'; import '@testing-library/jest-dom'; -import React, { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -// import { useSelector, useDispatch } from 'react-redux'; -import {render, fireEvent, screen, getAllByRole} from '@testing-library/react'; -// trying to import ts files is giving issues for time being, probably related to compiling +import React from 'react'; +import {render, fireEvent, screen} from '@testing-library/react'; import App from '../src/renderer/App'; import Login from '../src/components/login/login'; -// import AccountDisplay from '../src/components/display/AccountDisplay'; -import {BrowserRouter, MemoryRouter, Routes, Route, Link} from 'react-router-dom'; +import { MemoryRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import store from '../src/renderer/store'; -import {describe, expect, test, jest} from '@jest/globals'; +import {describe, beforeEach, expect, test, jest} from '@jest/globals'; import fetchMock from 'jest-fetch-mock'; import { act } from 'react-test-renderer'; - +import Docketeer from '../assets/docketeer-title.png'; const mockedUsedNavigate = jest.fn(); // jest.mock('react-router-dom', () => ({ @@ -24,18 +19,16 @@ const mockedUsedNavigate = jest.fn(); fetchMock.enableMocks(); describe('Login Page Renders', () => { - beforeEach( async () => { fetch.resetMocks(); await act(()=>{ render( - - + + ); - }); screen.debug(); }); @@ -60,9 +53,10 @@ describe('Login Page Renders', () => { fireEvent.click(loginButton); }); // need to fix issue of localhost/4000 not rendering anything after you login - // screen.debug( ); - // it is blank, which is expected for the test, but not for the application as a whole + }); - // should fail, look into this test + test('Docketeer Image', async () => { + const image = await screen.findByRole('img'); + expect(image).toHaveProperty('width'); }); }); \ No newline at end of file diff --git a/package.json b/package.json index 4b81868b..b549d4c7 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,10 @@ "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", "@types/react-router-dom": "^5.3.3", + "child_process": "^1.0.2", "colors": "^1.4.0", "cors": "^2.8.5", + "node": "^19.2.0", "pg": "^8.8.0", "react": "^18.2.0", "react-chartjs-2": "^4.3.1", @@ -32,6 +34,7 @@ "react-dom": "^18.2.0", "react-error-overlay": "^6.0.11", "react-modal": "^3.15.1", + "react-password-strength-bar": "^0.4.1", "react-redux": "^8.0.4", "react-router": "^6.4.1", "react-router-dom": "^6.4.1", diff --git a/server/app.js b/server/app.js index de1018a6..7dff49c4 100644 --- a/server/app.js +++ b/server/app.js @@ -38,8 +38,11 @@ app.use('/logout', logoutRouter); // Unknown Endpoint Error Handler app.use('/', (req, res) => { - const url = new URL(`${req.protocol}://${req.get('host')}${req.originalUrl}`); - console.log('1',url); + /* + Reads the current URL (explains why electron crashes) + const url = new URL(`${req.protocol}://${req.get('host')}${req.originalUrl}`); + console.log('current url',url); + */ // for development purposes, so we don't have to reopen electron everytime return res.status(404).redirect('/'); // return res.status(404).json('404 Not Found') diff --git a/server/controllers/initController.js b/server/controllers/initController.js index 8765273e..bcacdd90 100644 --- a/server/controllers/initController.js +++ b/server/controllers/initController.js @@ -4,8 +4,8 @@ */ const db = require('../models/psqlQuery'); -const path = require('path') -const { exec } = require('child_process') +const path = require('path'); +const { exec } = require('child_process'); const initController = {}; @@ -29,64 +29,65 @@ initController.initDatabase = (req, res, next) => { initController.timeZone = (req, res, next) => { - const parameter = [req.body.timezone.toString()] - console.log(parameter) + const parameter = [req.body.timezone.toString()]; + console.log(parameter); db.query2(`ALTER DATABASE postgres SET timezone TO ${parameter}`) .then((data) => { return next(); }) .catch((err) => { - console.log(err) + console.log(err); if (err) return next(err); }); }; initController.gitURL = (req, res, next) => { - const parameter = [req.body.githubUrl] - db.query2(`SELECT github_url FROM containers where name = $1`, parameter) + const parameter = [req.body.githubUrl]; + db.query2('SELECT github_url FROM containers where name = $1', parameter) .then((data) => { res.locals.url = data; return next(); }) .catch((err) => { - console.log(err) + console.log(err); if (err) return next(err); }); -} +}; -//inserting the metrics pulled from the running containers and stopped containers from Docker into the Database +// inserting the metrics pulled from the running containers and stopped containers from Docker into the Database initController.addMetrics = (req, res, next) => { + const containers = Object.keys(req.body.containers); - const queryString = `INSERT INTO metrics (container_id, container_name, cpu_pct, memory_pct, memory_usage, net_io, block_io, pid) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)` + const queryString = 'INSERT INTO metrics (container_id, container_name, cpu_pct, memory_pct, memory_usage, net_io, block_io, pid) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)'; containers.forEach((container) => { - const { ID, names, cpu, mem, memuse, net, block, pid } = req.body.containers[container] - const parameters = [ ID, names, cpu, mem, memuse, net, block, pid ] + const { ID, names, cpu, mem, memuse, net, block, pid } = req.body.containers[container]; + const parameters = [ ID, names, cpu, mem, memuse, net, block, pid ]; db.query2(queryString, parameters) - .then(() => { - }) - .catch((err) => { - console.log(err) - if (err) return next(err); - }); - }) + .then(() => { + }) + .catch((err) => { + console.log(err); + if (err) return next(err); + }); + }); return next(); -} +}; initController.getMetrics = async (req, res, next) => { let queryString = 'SELECT * FROM metrics WHERE (container_name = $1 '; - const queryStringEnd = `AND created_at >= now() - interval '4 hour' ORDER BY "created_at" ASC` - const containerList = req.body.containers + const queryStringEnd = 'AND created_at >= now() - interval \'4 hour\' ORDER BY "created_at" ASC'; + const containerList = req.body.containers; let count = 1; - //if only one checkbox is clicked, this will run + // if only one checkbox is clicked, this will run if (containerList.length === 1) { queryString += ')' + queryStringEnd; await db.query2(queryString, containerList) - .then((data) => { - res.locals.metrics = data; - return next(); - }) + .then((data) => { + res.locals.metrics = data; + return next(); + }); } - //if there are more than one containers selected, this will activate + // if there are more than one containers selected, this will activate else { containerList.slice(1).forEach((container) => { let additionalParameter = `OR container_name = $${count + 1} `; @@ -96,11 +97,11 @@ initController.getMetrics = async (req, res, next) => { }); queryString += queryStringEnd; await db.query2(queryString, containerList) - .then((data) => { - res.locals.metrics = data; - return next(); - }) + .then((data) => { + res.locals.metrics = data; + return next(); + }); } -} +}; module.exports = initController; \ No newline at end of file diff --git a/server/controllers/userController.js b/server/controllers/userController.js index 9a96feed..c73a2be6 100644 --- a/server/controllers/userController.js +++ b/server/controllers/userController.js @@ -117,9 +117,9 @@ userController.switchUserRole = (req, res, next) => { 'system admin': 1, admin: 2, user: 3 - } + }; - const { _id, role } = req.body + const { _id, role } = req.body; if(res.locals.sysAdmins === 1 && _id == res.locals.id){ res.locals.hasError = true; @@ -128,7 +128,7 @@ userController.switchUserRole = (req, res, next) => { const query = 'UPDATE users SET role = $1, role_id = $2 WHERE _id = $3 RETURNING *;'; - const parameters = [role, roleMap[role], _id] + const parameters = [role, roleMap[role], _id]; db.query(query, parameters) .then((data) => { diff --git a/server/routes/initRouter.js b/server/routes/initRouter.js index 81f819bc..43568ff0 100644 --- a/server/routes/initRouter.js +++ b/server/routes/initRouter.js @@ -37,9 +37,9 @@ router.post('/addMetrics', ); router.post('/getMetrics', -initController.getMetrics, -(req, res) => { - return res.status(200).json(res.locals.metrics) + initController.getMetrics, + (req, res) => { + return res.status(200).json(res.locals.metrics); } ); diff --git a/server/routes/loginRouter.js b/server/routes/loginRouter.js index 8dd1f6cc..d7e97ba5 100644 --- a/server/routes/loginRouter.js +++ b/server/routes/loginRouter.js @@ -19,6 +19,7 @@ router.post('/', cookieController.setAdminCookie, bcryptController.hashCookie, (req, res) => { + console.log(res.locals); if (res.locals.error) return res.status(200).json(res.locals); return res.status(200).json(res.locals.user); } diff --git a/server/server.js b/server/server.js index 371750e8..fdfd9be9 100644 --- a/server/server.js +++ b/server/server.js @@ -5,6 +5,5 @@ const PORT = process.env.PORT || 3000; // Open up server on PORT app.listen(PORT, () => { - console.log(process.env); console.log(`server is listening on port ${PORT}`.green.inverse); }); diff --git a/src/components/css/styles.css b/src/components/css/styles.css index 85f7ff3c..86250088 100644 --- a/src/components/css/styles.css +++ b/src/components/css/styles.css @@ -6,6 +6,7 @@ font-family: "Maven Pro", sans-serif; margin: 0; padding: 0; + box-sizing:border-box; } body { diff --git a/src/components/display/LineChartDisplay.tsx b/src/components/display/LineChartDisplay.tsx index e790866b..835f8f9f 100644 --- a/src/components/display/LineChartDisplay.tsx +++ b/src/components/display/LineChartDisplay.tsx @@ -262,6 +262,7 @@ const LineChartDisplay = () => { let date: any = new Date( Date.parse(new Date().toISOString()) - 2629746000 ).toISOString(); + date = new Date(date) date.setHours(date.getHours() - time); //date = date.toISOString(); const urlObj = await helper.getContainerGitUrl(containerName); diff --git a/src/components/display/NewUserDisplay.js b/src/components/display/NewUserDisplay.js index 26bf7ab9..94611ffe 100644 --- a/src/components/display/NewUserDisplay.js +++ b/src/components/display/NewUserDisplay.js @@ -8,6 +8,17 @@ import { useSelector, useDispatch } from 'react-redux'; // Material UI Imports import TextField from '@mui/material/TextField'; import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import Input from '@mui/material/Input'; +import FilledInput from '@mui/material/FilledInput'; +import OutlinedInput from '@mui/material/OutlinedInput'; +import InputLabel from '@mui/material/InputLabel'; +import InputAdornment from '@mui/material/InputAdornment'; +import FormHelperText from '@mui/material/FormHelperText'; +import FormControl from '@mui/material/FormControl'; +import Visibility from '@mui/icons-material/Visibility'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; import { handleNewUser, @@ -16,9 +27,36 @@ import { checkPhone, } from '../helper/newUserHelper'; +import PasswordStrengthBar from 'react-password-strength-bar'; const NewUserDisplay = () => { + // const [password, setPassword] = useState(''); + const [values, setValues] = useState({ + email: '', + username: '', + password: '', + passwordConfirmation: '', + phone: '', + showPassword: false, + }); + + // const handleChange = (prop) => (event) => { + // setValues({ ...values, [prop]: event.target.value }); + // }; + + const handleClickShowPassword = () => { + setValues({ + ...values, + showPassword: !values.showPassword, + }); + }; + + // const handleMouseDownPassword = (event) => { + // event.preventDefault(); + // }; + return ( +
    @@ -35,11 +73,19 @@ const NewUserDisplay = () => { logging in.


    -
    + handleNewUser(e)} + sx={{color:'blue'}} + > + { id='signupUsername' label='Username' variant='outlined' + required + inputProps={{ minLength: 4, maxLength: 16 }} sx={{ m: 1 }} />
    - + Password + { + checkPasswordLength(e); + setValues({...values, password:e.target.value}) + }} + endAdornment={ + + + {values.showPassword ? : } + + + } + label="Password" + /> + + {values.password && } + + + {/* checkPasswordLength()} + required + inputProps={{ minLength: 6, maxLength: 16 }} + + onChange={(e) => { + setPassword(e.target.value); + checkPasswordLength(e);}} sx={{ m: 1 }} - /> - + /> */} + + {/* {password && } + */} +
    confirmPassword()} + required + onChange={(e) => { + setValues({...values, passwordConfirmation:e.target.value}) + confirmPassword(e) + }} sx={{ m: 1 }} /> - + {/* This is sacrilege but I hardcoded this bar and made it hidden to keep the same formatting as above */} + {} +
    { checkPhone(document.getElementById('signupPhone').value); }} @@ -96,14 +187,13 @@ const NewUserDisplay = () => { variant='contained' size='medium' type='submit' - onClick={(e) => handleNewUser(e)} sx={{ m: 1 }} > Submit - +
    ); diff --git a/src/components/helper/commands.js b/src/components/helper/commands.js index 20669924..47019f0f 100644 --- a/src/components/helper/commands.js +++ b/src/components/helper/commands.js @@ -5,7 +5,6 @@ import { } from './volumeHistoryHelper'; import store from '../../renderer/store'; import { makeArrayOfObjects } from './processLogHelper'; - /** * Grabs all active containers on app-start up * @@ -15,7 +14,7 @@ import { makeArrayOfObjects } from './processLogHelper'; export const addRunning = (runningList, callback) => { - window.nodeMethod.runExec( + window.nodeMethod.exec( 'docker stats --no-stream --format "{{json .}},"', (error, stdout, stderr) => { if (error) { @@ -47,7 +46,7 @@ export const addRunning = (runningList, callback) => { } isInTheList ? '' : newList.push(convertedValue[i]); } - console.log('addrunning newlist', newList); + newList.length ? callback(newList) : ''; } ); @@ -74,7 +73,7 @@ const errorCheck = (key, error) => { }; export const refreshRunning = (refreshRunningContainers) => { - window.nodeMethod.runExec( + window.nodeMethod.exec( 'docker stats --no-stream --format "{{json .}},"', (error, stdout, stderr) => { if (error) { @@ -103,7 +102,7 @@ export const refreshRunning = (refreshRunningContainers) => { * @param {*} callback */ export const refreshStopped = (refreshStoppedContainers) => { - window.nodeMethod.runExec( + window.nodeMethod.exec( 'docker ps -f "status=exited" --format "{{json .}},"', (error, stdout, stderr) => { if (error) { @@ -129,7 +128,7 @@ export const refreshStopped = (refreshStoppedContainers) => { * @param {*} callback */ export const refreshImages = (callback) => { - window.nodeMethod.runExec('docker images', (error, stdout, stderr) => { + window.nodeMethod.exec('docker images', (error, stdout, stderr) => { if (error) { errorCheck('refreshImages', error); return; @@ -166,7 +165,7 @@ export const refreshImages = (callback) => { * @param {*} callback */ export const remove = (id, callback) => { - window.nodeMethod.runExec(`docker rm ${id}`, (error, stdout, stderr) => { + window.nodeMethod.exec(`docker rm ${id}`, (error, stdout, stderr) => { if (error) { alert(`${error.message}`); return; @@ -186,7 +185,7 @@ export const remove = (id, callback) => { * @param {*} callback */ export const stop = (id, callback) => { - window.nodeMethod.runExec(`docker stop ${id}`, (error, stdout, stderr) => { + window.nodeMethod.exec(`docker stop ${id}`, (error, stdout, stderr) => { if (error) { alert(`${error.message}`); return; @@ -209,7 +208,7 @@ export const runStopped = ( id, runStoppedContainerDispatcher, ) => { - window.nodeMethod.runExec(`docker start ${id}`, (error, stdout, stderr) => { + window.nodeMethod.exec(`docker start ${id}`, (error, stdout, stderr) => { if (error) { alert(`${error.message}`); return; @@ -235,8 +234,12 @@ export const runStopped = ( export const runIm = (container, runningList, callback_1, callback_2) => { // props.runIm(ele['imgid'], props.runningList, helper.addRunning, props.addRunningContainers) const {imgid, reps, tag} = container; - console.log(container); - window.nodeMethod.runExec(`docker run --name ${reps}-${tag} ${reps}:${tag}`, (error, stdout, stderr) => { + const randomId = Math.floor(Math.random()*100).toFixed(); + const filteredReps = reps + .replace(/[,/#!$%^&*;:{}=`~()]/g, "-") + .replace(/\s{2,}/g, "."); + + window.nodeMethod.exec(`docker run --name ${filteredReps}-${tag}_${randomId} ${reps}:${tag}`, (error, stdout, stderr) => { if (error) { alert(`${error.message}`); return; @@ -259,7 +262,7 @@ export const runIm = (container, runningList, callback_1, callback_2) => { * @param {*} callback_2 */ export const removeIm = (id, imagesList, callback_1, callback_2) => { - window.nodeMethod.runExec(`docker rmi -f ${id}`, (error, stdout, stderr) => { + window.nodeMethod.exec(`docker rmi -f ${id}`, (error, stdout, stderr) => { if (error) { alert( `${error.message}` + @@ -283,7 +286,7 @@ export const removeIm = (id, imagesList, callback_1, callback_2) => { export const handlePruneClick = (e) => { e.preventDefault(); - window.nodeMethod.runExec( + window.nodeMethod.exec( 'docker system prune --force', (error, stdout, stderr) => { if (error) { @@ -305,7 +308,7 @@ export const handlePruneClick = (e) => { */ export const pullImage = (repo) => { - window.nodeMethod.runExec(`docker pull ${repo}`, (error, stdout, stderr) => { + window.nodeMethod.exec(`docker pull ${repo}`, (error, stdout, stderr) => { if (error) { console.log('error occurred in pulling image'); alert(`Image repo '${repo}' seems to not exist, or may be a private repo.`); @@ -330,7 +333,7 @@ export const pullImage = (repo) => { */ export const networkContainers = (getNetworkContainers) => { - window.nodeMethod.runExec( + window.nodeMethod.exec( 'docker network ls --format "{{json .}},"', (error, stdout, stderr) => { if (error) { @@ -358,7 +361,7 @@ export const networkContainers = (getNetworkContainers) => { }; export const inspectDockerContainer = (containerId) => { - window.nodeMethod.runExec( + window.nodeMethod.exec( `docker inspect ${containerId}`, (error, stdout, stderr) => { if (error) { @@ -397,7 +400,7 @@ export const dockerComposeUp = (fileLocation, ymlFileName) => { cmd = `cd ${fileLocation} && docker compose -f ${ymlFileName} up -d`; } - window.nodeMethod.runExec(cmd, (error, stdout, stderr) => { + window.nodeMethod.exec(cmd, (error, stdout, stderr) => { if (error) { console.warn(error.message); return; @@ -423,7 +426,7 @@ export const dockerComposeUp = (fileLocation, ymlFileName) => { export const dockerComposeStacks = (getContainerStacks, filePath, ymlFileName) => { let parseDockerOutput; - window.nodeMethod.runExec( + window.nodeMethod.exec( 'docker network ls --filter "label=com.docker.compose.network" --format "{{json .}},"', (error, stdout, stderr) => { if (error) { @@ -482,7 +485,7 @@ export const dockerComposeDown = (fileLocation, ymlFileName) => { cmd = `cd ${fileLocation} && docker-compose -f ${ymlFileName} down`; } - window.nodeMethod.runExec(cmd, (error, stdout, stderr) => { + window.nodeMethod.exec(cmd, (error, stdout, stderr) => { if (error) { console.warn(error.message); return; @@ -599,7 +602,7 @@ export const getContainerGitUrl = async (container) => { */ export const getAllDockerVolumes = (getVolumeList) => { - window.nodeMethod.runExec( + window.nodeMethod.exec( 'docker volume ls --format "{{json .}},"', (error, stdout, stderr) => { if (error) { @@ -628,7 +631,7 @@ export const getAllDockerVolumes = (getVolumeList) => { */ export const getVolumeContainers = (volumeName, getVolumeContainersList) => { - window.nodeMethod.runExec( + window.nodeMethod.exec( `docker ps -a --filter volume=${volumeName} --format "{{json .}},"`, (error, stdout, stderr) => { if (error) { @@ -671,7 +674,7 @@ export const getLogs = async (optionsObj, getContainerLogsDispatcher) => { : (inputCommandString += '--tail 50 '); inputCommandString += `${optionsObj.containerIds[i]}`; - window.nodeMethod.runExec(inputCommandString, (error, stdout, stderr) => { + window.nodeMethod.exec(inputCommandString, (error, stdout, stderr) => { if (error) { console.error(`exec error: ${error}`); return; diff --git a/src/components/helper/newUserHelper.js b/src/components/helper/newUserHelper.js index 1d8649ed..8d64706c 100644 --- a/src/components/helper/newUserHelper.js +++ b/src/components/helper/newUserHelper.js @@ -33,7 +33,7 @@ export const handleNewUser = (e) => { createNewUser(email, username, password, phone); }; -export const confirmPassword = () => { +export const confirmPassword = (e) => { const password = document.getElementById('signupPassword').value; const confirmationPassword = document.getElementById( 'signupPasswordConfirmation' @@ -50,12 +50,12 @@ export const confirmPassword = () => { return password === confirmationPassword; }; -export const checkPasswordLength = () => { +export const checkPasswordLength = (e) => { const passwordLengthAlert = document.getElementById('password-length-alert'); const password = document.getElementById('signupPassword').value; const regex = /^(?=[a-z\d]{6,}$)(?=\d*[a-z])[a-z]*\d[a-z\d]*$/; - if (!regex.test(password)) { + if (!regex.test(password) && password) { passwordLengthAlert.innerHTML = '\nWarning: Password must be 6 characters or longer \nand must include at least one number and one letter'; } else { @@ -66,7 +66,7 @@ export const checkPasswordLength = () => { export const checkPhone = (phone) => { const regex = /[+][1][\d]{10}$/; - const phoneAlert = document.getElementById('phone-alert'); + const phoneAlert = document.getElementById('signupPhone'); if (phone.match(regex) === null) { phoneAlert.innerHTML = 'Warning: Please enter valid phone number with country code (+1).\nExample: 12345678900'; diff --git a/src/components/helper/processLogHelper.js b/src/components/helper/processLogHelper.js index 16bade43..77363f1a 100644 --- a/src/components/helper/processLogHelper.js +++ b/src/components/helper/processLogHelper.js @@ -57,6 +57,5 @@ export const makeArrayOfObjects = (string, containerName) => { // filter out empty messages const arrayOfLogs = arrayOfObjects.filter((obj) => obj.logMsg !== ''); - console.log('array of logs in processloghelper', arrayOfLogs); return arrayOfLogs; }; diff --git a/src/components/tabs/Containers.tsx b/src/components/tabs/Containers.tsx index 2f1e358d..c1aec0c9 100644 --- a/src/components/tabs/Containers.tsx +++ b/src/components/tabs/Containers.tsx @@ -87,7 +87,7 @@ const Containers = (props: ContainerProps) => { return (
    -

    {container.Name}

    +

    Name: {container.Name}

    ID: {container.ID}

    diff --git a/src/components/tabs/Users.js b/src/components/tabs/Users.js index ea944ae0..0e252780 100644 --- a/src/components/tabs/Users.js +++ b/src/components/tabs/Users.js @@ -39,6 +39,7 @@ const UserTable = () => { ]); const handleRoleChange = (event) => { + console.log('userl', userList) const id = event.id; const role = event.props.value; @@ -55,14 +56,14 @@ const UserTable = () => { }) }) .then((response) => response.json()) - // return response.json(); // This needs to be changed to one line since console.logs have been removed + // return response.json(); // This needs to be changed to one line since console.logs have been removed .then((data) => { // Sets hasError to true/false based on API call. This will be true if the user tries to remove the last sysadmin const hasError = data; if (data) { console.log('no change'); window.alert( - "Uh-oh! You're the LAST sysadmin! Before reassigning yourself you need to assign a new sysadmin." + 'Uh-oh! You\'re the LAST sysadmin! Before reassigning yourself you need to assign a new sysadmin.' ); } else { const payload = { @@ -95,7 +96,7 @@ const UserTable = () => { > Manage Users diff --git a/src/components/views/UserView.tsx b/src/components/views/UserView.tsx index 9dc48570..bfc6b533 100644 --- a/src/components/views/UserView.tsx +++ b/src/components/views/UserView.tsx @@ -295,6 +295,7 @@ const UserView = () => { stop={helper.stop} stopRunningContainer={stopRunningContainer} runningList={runningList} + // @ts-ignore: Unreachable code error addRunningContainers={addRunningContainers} // Stopped Containers runStopped={helper.runStopped} diff --git a/src/main/index.ts b/src/main/index.ts index 0e731e73..861e4674 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -45,6 +45,7 @@ function createMainWindow() { } electron.app.on('ready', createMainWindow) + electron.app.on('renderer-process-crashed', createMainWindow) // MacOS Specific function electron.app.on('window-all-closed', function () { diff --git a/src/main/preload.js b/src/main/preload.js index 1ebe9926..325c4221 100644 --- a/src/main/preload.js +++ b/src/main/preload.js @@ -1,13 +1,15 @@ const { contextBridge, ipcRenderer } = require('electron'); const child_process = require('child_process'); +const { exec } = require('node:child_process'); -let runExec = (command, callback) => { +const runExec = (command, callback) => { return child_process.exec(command, callback); }; // Access in the renderer/react as window.childProcess.exec contextBridge.exposeInMainWorld('nodeMethod', { - runExec: runExec, + runExec: exec, + exec: exec, bool: true, rendInvoke: (input1, input2) => ipcRenderer.invoke(input1, input2) }); diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index c1fd31e5..d701b249 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -5,8 +5,8 @@ import { Routes, Route } from 'react-router-dom'; import Login from '../components/login/login'; import RenderViews from '../components/RenderViews'; import Authentication from '../components/Authentication'; - const App = () => { + return ( } /> diff --git a/webpack.react.config.js b/webpack.react.config.js index 6b486678..91c2959f 100644 --- a/webpack.react.config.js +++ b/webpack.react.config.js @@ -1,6 +1,7 @@ const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); +const { isEmptyBindingElement } = require('typescript'); // console.log(path.join(__dirname, '/src/renderer/index.tsx')); module.exports = { @@ -9,6 +10,7 @@ module.exports = { extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'], mainFields: ['main', 'module', 'browser'], fallback: { + child_process: false, fs: false, tls: false, net: false, @@ -93,7 +95,8 @@ module.exports = { new HtmlWebpackPlugin({ template: './src/renderer/index.html' }), - new webpack.IgnorePlugin({ resourceRegExp: /^pg-native$/ }) + new webpack.IgnorePlugin({ resourceRegExp: /^pg-native$/ }), + // new webpack.DefinePlugin({ // process: { env: {} } // }) From d95757c547a3c5d3333f27dd3255ce10e689007c Mon Sep 17 00:00:00 2001 From: Jack Date: Sat, 10 Dec 2022 18:16:54 -0500 Subject: [PATCH 087/110] scout branch --- src/components/display/NewUserDisplay.js | 390 +++++++++++----------- src/components/display/NewUserDisplay.tsx | 186 ++++++++--- 2 files changed, 333 insertions(+), 243 deletions(-) diff --git a/src/components/display/NewUserDisplay.js b/src/components/display/NewUserDisplay.js index 94611ffe..27a1b01a 100644 --- a/src/components/display/NewUserDisplay.js +++ b/src/components/display/NewUserDisplay.js @@ -1,203 +1,217 @@ -/** - * @module NewUserDisplay - * @description Signup component that will be rendered in SysAdmin view, in Users tab, where sysadmin can create an account for new user in organization - */ -import React, { useEffect, useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; +// /** +// * @module NewUserDisplay +// * @description Signup component that will be rendered in SysAdmin view, in Users tab, where sysadmin can create an account for new user in organization +// */ +// import React, { useEffect, useState } from 'react'; +// import { useSelector, useDispatch } from 'react-redux'; -// Material UI Imports -import TextField from '@mui/material/TextField'; -import Button from '@mui/material/Button'; -import Box from '@mui/material/Box'; -import IconButton from '@mui/material/IconButton'; -import Input from '@mui/material/Input'; -import FilledInput from '@mui/material/FilledInput'; -import OutlinedInput from '@mui/material/OutlinedInput'; -import InputLabel from '@mui/material/InputLabel'; -import InputAdornment from '@mui/material/InputAdornment'; -import FormHelperText from '@mui/material/FormHelperText'; -import FormControl from '@mui/material/FormControl'; -import Visibility from '@mui/icons-material/Visibility'; -import VisibilityOff from '@mui/icons-material/VisibilityOff'; +// // Material UI Imports +// import TextField from '@mui/material/TextField'; +// import Button from '@mui/material/Button'; +// import Box from '@mui/material/Box'; +// import IconButton from '@mui/material/IconButton'; +// import Input from '@mui/material/Input'; +// import FilledInput from '@mui/material/FilledInput'; +// import OutlinedInput from '@mui/material/OutlinedInput'; +// import InputLabel from '@mui/material/InputLabel'; +// import InputAdornment from '@mui/material/InputAdornment'; +// import FormHelperText from '@mui/material/FormHelperText'; +// import FormControl from '@mui/material/FormControl'; +// import Visibility from '@mui/icons-material/Visibility'; +// import VisibilityOff from '@mui/icons-material/VisibilityOff'; +// import PasswordStrengthBar from 'react-password-strength-bar'; +// import RadioGroup from "@mui/material/RadioGroup"; +// import Radio from "@mui/material/Radio"; +// import FormControlLabel from '@mui/material/FormControlLabel'; +// import FormLabel from '@mui/material/FormLabel'; -import { - handleNewUser, - checkPasswordLength, - confirmPassword, - checkPhone, -} from '../helper/newUserHelper'; +// import { +// handleNewUser, +// checkPasswordLength, +// confirmPassword, +// checkPhone, +// } from '../helper/newUserHelper'; -import PasswordStrengthBar from 'react-password-strength-bar'; +// // this will store the value from the user role +// let valueRole = '3'; +// //setting value of the RadioGroup MUI Component to the one selected by the user +// const handleSelect = (event: React.ChangeEvent) => { +// valueRole = (event.target as HTMLInputElement).value; +// } -const NewUserDisplay = () => { - // const [password, setPassword] = useState(''); - const [values, setValues] = useState({ - email: '', - username: '', - password: '', - passwordConfirmation: '', - phone: '', - showPassword: false, - }); +// const NewUserDisplay = () => { +// // const [password, setPassword] = useState(''); +// const [values, setValues] = useState({ +// email: '', +// username: '', +// password: '', +// passwordConfirmation: '', +// phone: '', +// showPassword: false, +// }); - // const handleChange = (prop) => (event) => { - // setValues({ ...values, [prop]: event.target.value }); - // }; +// const handleClickShowPassword = () => { +// setValues({ +// ...values, +// showPassword: !values.showPassword, +// }); +// }; - const handleClickShowPassword = () => { - setValues({ - ...values, - showPassword: !values.showPassword, - }); - }; - - // const handleMouseDownPassword = (event) => { - // event.preventDefault(); - // }; - - return ( +// return ( -
    -
    -
    -

    Create a New User

    -
    -

    - Create a new Docketeer account for an employee. Please confirm with the employee that their information is accurate before submitting. -

    -
    -

    - Note: For the password, please choose a random string of 6 characters, - numbers, and symbols. Upon account creation, the user will receive an - email with credentials and be able to update their password when - logging in. -

    -
    - handleNewUser(e)} - sx={{color:'blue'}} - > +//
    +//
    +//
    +//

    Create a New User

    +//
    +//

    +// Create a new Docketeer account for an employee. Please confirm with the employee that their information is accurate before submitting. +//

    +//
    +//

    +// Note: For the password, please choose a random string of 6 characters, +// numbers, and symbols. Upon account creation, the user will receive an +// email with credentials and be able to update their password when +// logging in. +//

    +//
    +// handleNewUser(e)} +// sx={{color:'blue'}} +// > - -
    - -
    +// +//
    +// +//
    - - Password - { - checkPasswordLength(e); - setValues({...values, password:e.target.value}) - }} - endAdornment={ - - - {values.showPassword ? : } - - - } - label="Password" - /> - - {values.password && } - +// +// Password +// { +// checkPasswordLength(e); +// setValues({...values, password:e.target.value}) +// }} +// endAdornment={ +// +// +// {values.showPassword ? : } +// +// +// } +// label="Password" +// /> +// +// {values.password && } +// - {/* { - setPassword(e.target.value); - checkPasswordLength(e);}} - sx={{ - m: 1 - }} - /> */} +// onChange={(e) => { +// setPassword(e.target.value); +// checkPasswordLength(e);}} +// sx={{ +// m: 1 +// }} +// /> */} - {/* {password && } - */} +// {/* {password && } +// */} -
    - { - setValues({...values, passwordConfirmation:e.target.value}) - confirmPassword(e) - }} - sx={{ - m: 1 - }} - /> - {/* This is sacrilege but I hardcoded this bar and made it hidden to keep the same formatting as above */} - {} - -
    - { - checkPhone(document.getElementById('signupPhone').value); - }} - sx={{ - m: 1 - }} - /> -
    - -
    - -
    -
    -
    - ); -}; +//
    +// { +// setValues({...values, passwordConfirmation:e.target.value}) +// confirmPassword(e) +// }} +// sx={{ +// m: 1 +// }} +// /> +// {/* This is sacrilege but I hardcoded this bar and made it hidden to keep the same formatting as above */} +// {} +// +//
    +// { +// checkPhone(document.getElementById('signupPhone').value); +// }} +// sx={{ +// m: 1 +// }} +// /> +// +// +// } label="System Admin"> +// } label="Admin"> +// } label="User"> +// +// +//
    +// +//
    +// +//
    +//
    +//
    +// ); +// }; -export default NewUserDisplay; +// export default NewUserDisplay; diff --git a/src/components/display/NewUserDisplay.tsx b/src/components/display/NewUserDisplay.tsx index 34d73b21..98c78514 100644 --- a/src/components/display/NewUserDisplay.tsx +++ b/src/components/display/NewUserDisplay.tsx @@ -2,24 +2,35 @@ * @module NewUserDisplay * @description Signup component that will be rendered in SysAdmin view, in Users tab, where sysadmin can create an account for new user in organization */ -import React, { useEffect, useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; +import React, { useEffect, useState } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; // Material UI Imports -import TextField from "@mui/material/TextField"; -import Button from "@mui/material/Button"; -import FormControlLabel from '@mui/material/FormControlLabel'; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import Input from '@mui/material/Input'; +import FilledInput from '@mui/material/FilledInput'; +import OutlinedInput from '@mui/material/OutlinedInput'; +import InputLabel from '@mui/material/InputLabel'; +import InputAdornment from '@mui/material/InputAdornment'; +import FormHelperText from '@mui/material/FormHelperText'; import FormControl from '@mui/material/FormControl'; -import FormLabel from '@mui/material/FormLabel'; +import Visibility from '@mui/icons-material/Visibility'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; +import PasswordStrengthBar from 'react-password-strength-bar'; import RadioGroup from "@mui/material/RadioGroup"; import Radio from "@mui/material/Radio"; +import FormControlLabel from '@mui/material/FormControlLabel'; +import FormLabel from '@mui/material/FormLabel'; import { handleNewUser, checkPasswordLength, confirmPassword, checkPhone, -} from "../helper/newUserHelper"; +} from '../helper/newUserHelper'; // this will store the value from the user role let valueRole = '3'; @@ -29,17 +40,34 @@ const handleSelect = (event: React.ChangeEvent) => { } const NewUserDisplay = () => { + // const [password, setPassword] = useState(''); + const [values, setValues] = useState({ + email: '', + username: '', + password: '', + passwordConfirmation: '', + phone: '', + showPassword: false, + }); + + const handleClickShowPassword = () => { + setValues({ + ...values, + showPassword: !values.showPassword, + }); + }; + return ( -
    -
    -
    + +
    +
    +

    Create a New User

    - Create a new Docketeer account for an employee. Please confirm with - the employee that their information is accurate before submitting. + Create a new Docketeer account for an employee. Please confirm with the employee that their information is accurate before submitting.

    -
    +

    Note: For the password, please choose a random string of 6 characters, numbers, and symbols. Upon account creation, the user will receive an @@ -47,65 +75,113 @@ const NewUserDisplay = () => { logging in.


    -
    + handleNewUser(e)} + sx={{color:'blue'}} + > +

    - checkPasswordLength()} + + + Password + { + checkPasswordLength(e); + setValues({...values, password:e.target.value}) + }} + endAdornment={ + + + {values.showPassword ? : } + + + } + label="Password" + /> + + {values.password && } + + + {/* { + setPassword(e.target.value); + checkPasswordLength(e);}} sx={{ - m: 1, + m: 1 }} - /> - + /> */} + + {/* {password && } + */} +
    confirmPassword()} + id='signupPasswordConfirmation' + label='Confirm Password' + variant='outlined' + type='password' + required + onChange={(e) => { + setValues({...values, passwordConfirmation:e.target.value}) + confirmPassword(e) + }} sx={{ - m: 1, + m: 1 }} /> - + {/* This is sacrilege but I hardcoded this bar and made it hidden to keep the same formatting as above */} + {} +
    { - //fixing the property 'value' does not exist on type 'HTMLElement' - const inputValue = ( - document.getElementById("signupPhone") as HTMLTextAreaElement - ).value; - checkPhone(inputValue); + checkPhone(document.getElementById('signupPhone').value); }} sx={{ - m: 1, + m: 1 }} /> -
    {
    - +
    - +
    ); }; export default NewUserDisplay; + From dd09b92112326609c36f152a98ff430739a7c9bf Mon Sep 17 00:00:00 2001 From: Sarah <14sbethm@gmail.com> Date: Sat, 10 Dec 2022 17:56:45 -0600 Subject: [PATCH 088/110] fixed login render error Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/login/login.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/login/login.tsx b/src/components/login/login.tsx index dfc47cf9..d29bf410 100644 --- a/src/components/login/login.tsx +++ b/src/components/login/login.tsx @@ -49,7 +49,6 @@ const Login = () => { (passwordInput as HTMLInputElement).value = ""; authenticateUser(username, password); - } }; // callback function which will send request to endpoint http://localhost:3000/login and expect From b4af46039898b3b70529e4b22432445d0bc544df Mon Sep 17 00:00:00 2001 From: Jack Date: Sat, 10 Dec 2022 22:20:52 -0500 Subject: [PATCH 089/110] merge --- src/components/login/login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/login/login.tsx b/src/components/login/login.tsx index dfc47cf9..3b1a136f 100644 --- a/src/components/login/login.tsx +++ b/src/components/login/login.tsx @@ -49,7 +49,7 @@ const Login = () => { (passwordInput as HTMLInputElement).value = ""; authenticateUser(username, password); - } + }; // callback function which will send request to endpoint http://localhost:3000/login and expect From b448262204f96e549e67c2fe68e0827a72b6fe37 Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Mon, 12 Dec 2022 10:34:02 -0600 Subject: [PATCH 090/110] finished up a few typescript files in helper folder Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- src/components/helper/HelperTypes.ts | 0 src/components/helper/newUserHelper.ts | 7 +++---- 2 files changed, 3 insertions(+), 4 deletions(-) delete mode 100644 src/components/helper/HelperTypes.ts diff --git a/src/components/helper/HelperTypes.ts b/src/components/helper/HelperTypes.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/components/helper/newUserHelper.ts b/src/components/helper/newUserHelper.ts index d46b3e9f..7a92a76d 100644 --- a/src/components/helper/newUserHelper.ts +++ b/src/components/helper/newUserHelper.ts @@ -6,9 +6,8 @@ import store from '../../renderer/store'; import * as actions from '../../redux/actions/actions'; import React from 'react'; -export const handleNewUser = (e, roleID) => { +export const handleNewUser = (e: React.SyntheticEvent, roleID: string) => { e.preventDefault(); - console.log('This is role id:', roleID); const username = (document.getElementById('signupUsername')).value; const password = (document.getElementById('signupPassword')).value; @@ -32,7 +31,7 @@ export const handleNewUser = (e, roleID) => { return; } - createNewUser(email, username, password, phone, role_id); + createNewUser(email, username, password, phone, roleID); }; export const confirmPassword = () => { @@ -78,7 +77,7 @@ export const checkPhone = (phone: string) => { return phone.match(regex) !== null; }; -export const createNewUser = (email, username, password, phone, role_id) => { +export const createNewUser = (email: string, username: string, password: string, phone: string, role_id: string) => { fetch('http://localhost:3000/signup', { method: 'POST', headers: { From 40efe92d1ac9820aa8f17656fd1c7411b9a38faa Mon Sep 17 00:00:00 2001 From: Drew Manley Date: Mon, 12 Dec 2022 10:37:28 -0600 Subject: [PATCH 091/110] merged development branch and fixed conflicts Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> --- src/components/helper/newUserHelper.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/components/helper/newUserHelper.ts b/src/components/helper/newUserHelper.ts index 820ce689..e95c8b40 100644 --- a/src/components/helper/newUserHelper.ts +++ b/src/components/helper/newUserHelper.ts @@ -34,15 +34,9 @@ export const handleNewUser = (e: React.SyntheticEvent, roleID: string) => { createNewUser(email, username, password, phone, roleID); }; -<<<<<<< HEAD:src/components/helper/newUserHelper.ts export const confirmPassword = () => { const password = (document.getElementById('signupPassword')).value; const confirmationPassword = (document.getElementById( -======= -export const confirmPassword = (e) => { - const password = document.getElementById('signupPassword').value; - const confirmationPassword = document.getElementById( ->>>>>>> development:src/components/helper/newUserHelper.js 'signupPasswordConfirmation' )).value; const passwordConfirmationAlert = (document.getElementById( @@ -57,15 +51,9 @@ export const confirmPassword = (e) => { return password === confirmationPassword; }; -<<<<<<< HEAD:src/components/helper/newUserHelper.ts export const checkPasswordLength = () => { const passwordLengthAlert = (document.getElementById('password-length-alert')); const password = (document.getElementById('signupPassword')).value; -======= -export const checkPasswordLength = (e) => { - const passwordLengthAlert = document.getElementById('password-length-alert'); - const password = document.getElementById('signupPassword').value; ->>>>>>> development:src/components/helper/newUserHelper.js const regex = /^(?=[a-z\d]{6,}$)(?=\d*[a-z])[a-z]*\d[a-z\d]*$/; if (!regex.test(password) && password) { @@ -79,11 +67,7 @@ export const checkPasswordLength = (e) => { export const checkPhone = (phone: string) => { const regex = /[+][1][\d]{10}$/; -<<<<<<< HEAD:src/components/helper/newUserHelper.ts const phoneAlert = document.getElementById('phone-alert') as HTMLInputElement; -======= - const phoneAlert = document.getElementById('signupPhone'); ->>>>>>> development:src/components/helper/newUserHelper.js if (phone.match(regex) === null) { phoneAlert.innerHTML = 'Warning: Please enter valid phone number with country code (+1).\nExample: 12345678900'; From a5d8424f1af1a7572b591469f1c3cdd3cb32c515 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 12 Dec 2022 11:58:59 -0500 Subject: [PATCH 092/110] Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/SignUp.tsx | 246 +++++++++++++++------- src/components/display/NewUserDisplay.tsx | 4 - 2 files changed, 167 insertions(+), 83 deletions(-) diff --git a/src/components/SignUp.tsx b/src/components/SignUp.tsx index e8d3a6f7..ad59e35f 100644 --- a/src/components/SignUp.tsx +++ b/src/components/SignUp.tsx @@ -1,9 +1,21 @@ -import React from "react"; +import React, {useState} from "react"; import { useNavigate } from 'react-router-dom'; // Material UI Imports -import TextField from "@mui/material/TextField"; -import Button from "@mui/material/Button"; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import OutlinedInput from '@mui/material/OutlinedInput'; +import InputLabel from '@mui/material/InputLabel'; +import InputAdornment from '@mui/material/InputAdornment'; +import FormControl from '@mui/material/FormControl'; +import Visibility from '@mui/icons-material/Visibility'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; +import PasswordStrengthBar from 'react-password-strength-bar'; +import RadioGroup from "@mui/material/RadioGroup"; +import Radio from "@mui/material/Radio"; +import FormControlLabel from '@mui/material/FormControlLabel'; // Helper Functions import { @@ -13,91 +25,167 @@ import { checkPhone, } from "./helper/newUserHelper"; +// this will store the value from the user role +let valueRole = '3'; +//setting value of the RadioGroup MUI Component to the one selected by the user +const handleSelect = (event: React.ChangeEvent) => { + valueRole = (event.target as HTMLInputElement).value; +} + const SignUp = () => { const navigate = useNavigate(); - + const [values, setValues] = useState({ + email: '', + username: '', + password: '', + passwordConfirmation: '', + phone: '', + showPassword: false, + }); + + const handleClickShowPassword = () => { + setValues({ + ...values, + showPassword: !values.showPassword, + }); + }; return (

    Sign Up

    -
    - -
    - -
    - checkPasswordLength()} - sx={{ - m: 1, - }} - /> - -
    - confirmPassword()} - sx={{ - m: 1, - }} - /> - -
    - { - //fixing the property 'value' does not exist on type 'HTMLElement' - const inputValue = ( - document.getElementById("signupPhone") as HTMLTextAreaElement - ).value; - checkPhone(inputValue); - }} - sx={{ - m: 1, - }} + handleNewUser(e)} + sx={{color:'blue'}} + > + + +
    + +
    + + + Password + { + checkPasswordLength(e); + setValues({...values, password:e.target.value}) + }} + endAdornment={ + + + {values.showPassword ? : } + + + } + label="Password" /> -
    - -
    - - +
    + {values.password && } + + + {/* { + setPassword(e.target.value); + checkPasswordLength(e);}} + sx={{ + m: 1 + }} + /> */} + + {/* {password && } + */} + +
    + { + setValues({...values, passwordConfirmation:e.target.value}) + confirmPassword(e) + }} + sx={{ + m: 1 + }} + /> + {/* This is sacrilege but I hardcoded this bar and made it hidden to keep the same formatting as above */} + {} + +
    + { + checkPhone(document.getElementById('signupPhone').value); + }} + sx={{ + m: 1 + }} + /> +
    + +
    + + +
    ) diff --git a/src/components/display/NewUserDisplay.tsx b/src/components/display/NewUserDisplay.tsx index 98c78514..b4ac20f6 100644 --- a/src/components/display/NewUserDisplay.tsx +++ b/src/components/display/NewUserDisplay.tsx @@ -10,12 +10,9 @@ import TextField from '@mui/material/TextField'; import Button from '@mui/material/Button'; import Box from '@mui/material/Box'; import IconButton from '@mui/material/IconButton'; -import Input from '@mui/material/Input'; -import FilledInput from '@mui/material/FilledInput'; import OutlinedInput from '@mui/material/OutlinedInput'; import InputLabel from '@mui/material/InputLabel'; import InputAdornment from '@mui/material/InputAdornment'; -import FormHelperText from '@mui/material/FormHelperText'; import FormControl from '@mui/material/FormControl'; import Visibility from '@mui/icons-material/Visibility'; import VisibilityOff from '@mui/icons-material/VisibilityOff'; @@ -23,7 +20,6 @@ import PasswordStrengthBar from 'react-password-strength-bar'; import RadioGroup from "@mui/material/RadioGroup"; import Radio from "@mui/material/Radio"; import FormControlLabel from '@mui/material/FormControlLabel'; -import FormLabel from '@mui/material/FormLabel'; import { handleNewUser, From 38a25f3e3cc2a97f4763cf82f0b4af8468a5b966 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 12 Dec 2022 13:33:12 -0500 Subject: [PATCH 093/110] Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/Login.tsx | 25 +++++++++++++++++++---- src/components/SignUp.tsx | 19 +++++++---------- src/components/display/NewUserDisplay.tsx | 15 +++++++++----- src/components/helper/newUserHelper.ts | 3 +-- src/components/login/login.tsx | 2 +- src/components/tabs/Users.js | 1 - src/renderer/App.tsx | 2 +- 7 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/components/Login.tsx b/src/components/Login.tsx index 94b19549..7c61b132 100644 --- a/src/components/Login.tsx +++ b/src/components/Login.tsx @@ -11,6 +11,8 @@ //MUI Elements import TextField from '@mui/material/TextField'; import Button from '@mui/material/Button'; + import { grey } from '@mui/material/colors'; + // @ts-ignore import Docketeer from '../../assets/docketeer-title.png'; @@ -99,7 +101,7 @@ id='username' label='Username' variant='outlined' - + value= 'sysadmin' />

    @@ -108,7 +110,7 @@ label='Password' type='password' variant='outlined' - + value ='belugas' />
    {/* * Login Button * */} @@ -119,12 +121,27 @@ size='medium' onClick={() => handleLogin} sx={{ - m: 1 + marginTop: 1, + marginBottom:1 }} > Login - +
    + +
    diff --git a/src/components/SignUp.tsx b/src/components/SignUp.tsx index ad59e35f..785f5b6c 100644 --- a/src/components/SignUp.tsx +++ b/src/components/SignUp.tsx @@ -13,9 +13,7 @@ import FormControl from '@mui/material/FormControl'; import Visibility from '@mui/icons-material/Visibility'; import VisibilityOff from '@mui/icons-material/VisibilityOff'; import PasswordStrengthBar from 'react-password-strength-bar'; -import RadioGroup from "@mui/material/RadioGroup"; -import Radio from "@mui/material/Radio"; -import FormControlLabel from '@mui/material/FormControlLabel'; + // Helper Functions import { @@ -25,13 +23,6 @@ import { checkPhone, } from "./helper/newUserHelper"; -// this will store the value from the user role -let valueRole = '3'; -//setting value of the RadioGroup MUI Component to the one selected by the user -const handleSelect = (event: React.ChangeEvent) => { - valueRole = (event.target as HTMLInputElement).value; -} - const SignUp = () => { const navigate = useNavigate(); const [values, setValues] = useState({ @@ -49,6 +40,7 @@ const SignUp = () => { showPassword: !values.showPassword, }); }; + return (
    @@ -155,8 +147,10 @@ const SignUp = () => { label='Phone' variant='outlined' required + inputProps={{maxLength: 12 }} + onChange={() => { - checkPhone(document.getElementById('signupPhone').value); + checkPhone(document.getElementById('signupPhone')?.value); }} sx={{ m: 1 @@ -174,7 +168,8 @@ const SignUp = () => { m: 1 }} > - Back + Back +
    ); }; - +} export default Login; diff --git a/src/components/tabs/Users.js b/src/components/tabs/Users.js index 0e252780..48d0f5e9 100644 --- a/src/components/tabs/Users.js +++ b/src/components/tabs/Users.js @@ -39,7 +39,6 @@ const UserTable = () => { ]); const handleRoleChange = (event) => { - console.log('userl', userList) const id = event.id; const role = event.props.value; diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 90b845e2..0b34272c 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -13,7 +13,7 @@ const App = () => { } /> } /> - } /> + } /> } /> ); From 6ff6cb9fca07da116af01f56f57613ce57f8192f Mon Sep 17 00:00:00 2001 From: Cedar Cooper Date: Mon, 12 Dec 2022 12:07:44 -0800 Subject: [PATCH 094/110] Migrated interfaces/types to root folder. Co-authored-by: Sarah Moosa <74516787+Sbethm@users.noreply.github.com> Co-authored-by: Tiffany Chau <102318022+tiffanynchau@users.noreply.github.com> Co-authored-by: Jack Yuan <100592057+jackyuan1@users.noreply.github.com> Co-authored-by: Cedar Cooper <87053156+CedarCooper@users.noreply.github.com> Co-authored-by: Drew Manley <101421661+DrewManley@users.noreply.github.com> --- src/components/Authentication.tsx | 8 +- src/components/Login.tsx | 16 +- src/components/RenderViews.tsx | 7 +- src/components/debug/debugRouter.js | 2 +- src/components/display/AccountDisplay.tsx | 4 +- src/components/display/LineChartDisplay.tsx | 45 +- src/components/display/NewUserDisplay.js | 217 --------- src/components/display/NewUserDisplay.tsx | 36 +- src/components/display/ProcessLogsCard.tsx | 12 +- src/components/display/ProcessLogsTable.tsx | 16 +- src/components/display/ToggleDisplay.tsx | 6 +- src/components/login/login.tsx | 62 +-- src/components/tabs/Containers.tsx | 2 +- src/components/tabs/ContainersUser.tsx | 2 +- src/components/tabs/Images.tsx | 34 +- src/components/tabs/Settings.tsx | 2 +- src/components/tabs/TabTypes.ts | 4 - src/components/tabs/Users.js | 2 +- src/components/views/Admin.tsx | 3 +- src/components/views/SysAdmin.tsx | 2 +- src/components/views/UserView.tsx | 2 +- src/components/views/viewsTypes.ts | 118 ----- src/redux/reducers/containerListReducer.ts | 13 +- src/redux/reducers/dockerComposeReducer.ts | 14 +- src/redux/reducers/graphReducer.ts | 35 +- src/redux/reducers/imageListReducer.js | 41 -- src/redux/reducers/imageListReducer.ts | 10 +- src/redux/reducers/notificationReducer.ts | 10 +- src/redux/reducers/processLogsReducer.ts | 10 +- .../reducers/runningContainersReducer.ts | 13 +- src/redux/reducers/sessionReducer.ts | 21 +- src/redux/reducers/userListReducer.ts | 11 +- src/redux/reducers/userReducer.ts | 16 +- src/redux/reducers/volumeHistoryReducer.ts | 13 +- src/renderer/store.ts | 1 - types.ts | 457 ++++++++++++++++++ 36 files changed, 545 insertions(+), 722 deletions(-) delete mode 100644 src/components/display/NewUserDisplay.js delete mode 100644 src/components/views/viewsTypes.ts delete mode 100644 src/redux/reducers/imageListReducer.js create mode 100644 types.ts diff --git a/src/components/Authentication.tsx b/src/components/Authentication.tsx index ac699e74..87fd43d1 100644 --- a/src/components/Authentication.tsx +++ b/src/components/Authentication.tsx @@ -1,16 +1,12 @@ import React from 'react'; import { Navigate } from 'react-router-dom'; import { useSelector } from 'react-redux'; +import { RootState } from '../../types' -interface RootState { - session: { - isLoggedIn: boolean - } -} const Authentication = () => { // grab session information from state - const session: boolean = useSelector((state: RootState) => state.session.isLoggedIn); + const session: any = useSelector((state: RootState) => state.session.isLoggedIn); // if session is false navigate to login, if session is true navigate to outlet return ( session ? : diff --git a/src/components/Login.tsx b/src/components/Login.tsx index 94b19549..e142dd3e 100644 --- a/src/components/Login.tsx +++ b/src/components/Login.tsx @@ -14,20 +14,10 @@ // @ts-ignore import Docketeer from '../../assets/docketeer-title.png'; + + // import interface + import { UserInfo } from '../../types'; - interface UserInfo { - _id: number, - username: string, - email: string, - phone: string, - role: string, - role_id: number, - contact_pref: string | null, - mem_threshold: number, - cpu_threshold: number, - container_stops: boolean, - token: string - } const Login = () => { const navigate = useNavigate(); diff --git a/src/components/RenderViews.tsx b/src/components/RenderViews.tsx index 3456f2a7..eecfddb2 100644 --- a/src/components/RenderViews.tsx +++ b/src/components/RenderViews.tsx @@ -4,12 +4,7 @@ import { useSelector } from 'react-redux'; import AdminView from './views/Admin'; import UserView from './views/UserView'; import SysAdminView from './views/SysAdmin'; - -interface RootState { - session: { - role: string - } -} +import { RootState } from '../../types'; const RenderViews = ():any => { // grab current user's role diff --git a/src/components/debug/debugRouter.js b/src/components/debug/debugRouter.js index 77e1c3db..0c7e11c8 100644 --- a/src/components/debug/debugRouter.js +++ b/src/components/debug/debugRouter.js @@ -8,7 +8,7 @@ class DebugRouter extends Router { this.history.listen((location, action)=>{ console.log( `The current URL is ${location.pathname}${location.search}${location.hash}` - ) + ); console.log(`The last navigation action was ${action}`, JSON.stringify(this.history, null,2)); }); } diff --git a/src/components/display/AccountDisplay.tsx b/src/components/display/AccountDisplay.tsx index 1a1d8742..6b66c2fe 100644 --- a/src/components/display/AccountDisplay.tsx +++ b/src/components/display/AccountDisplay.tsx @@ -2,8 +2,8 @@ * @module AccountDisplay * @description Account Display for Settings tab, this will host any forms to update account details such as email, passwords, etc. */ -import React, { useEffect, useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; +import React from "react"; +import { useSelector } from "react-redux"; // Material UI Imports import Button from "@mui/material/Button"; diff --git a/src/components/display/LineChartDisplay.tsx b/src/components/display/LineChartDisplay.tsx index 835f8f9f..18245f77 100644 --- a/src/components/display/LineChartDisplay.tsx +++ b/src/components/display/LineChartDisplay.tsx @@ -7,7 +7,7 @@ import * as helper from "../helper/commands"; import { DataGrid } from "@mui/x-data-grid"; import { FormControlLabel, Checkbox } from "@mui/material"; import { RootState } from "../../renderer/store"; -import { AnyAction } from "redux"; +import { auxObjType, obType } from '../../../types'; /** * Displays linegraph and github metrics @@ -26,10 +26,10 @@ const LineChartDisplay = () => { const readIO = useSelector((state: RootState) => state.graphs["graphReadIO"]); const axis = useSelector((state: RootState) => state.graphs["graphAxis"]); const runningList = useSelector( - (state: RootState) => state.containersList.runningList + (state: RootState) => state.containersList['runningList'] ); const stoppedList = useSelector( - (state: RootState) => state.containersList.stoppedList + (state: RootState) => state.containersList['stoppedList'] ); const dispatch = useDispatch(); @@ -64,7 +64,7 @@ const LineChartDisplay = () => { labels: axis, datasets: cpu, }; - const writtenIOObj = { +const writtenIOObj = { labels: axis, datasets: writtenIO, }; @@ -145,27 +145,7 @@ const LineChartDisplay = () => { const containerMetrics = await getContainerMetrics(); - interface auxObjType { - container?: ContainerInterface; - currentContainer?: any; - containerName?: string; - } - - interface ContainerInterface { - memory?: any; - cpu?: any; - writtenIO?: any; - readIO?: any; - } - - // interface ContainerNameInterface{ - - // } - - //const container: keyof auxObjType = 'container' - const auxObj: auxObjType = {}; - //const container: keyof typeof auxObj; Object.keys(activeContainers).forEach((container) => { auxObj[container as keyof typeof auxObj] = { memory: buildLineGraphObj(container), @@ -207,7 +187,7 @@ const LineChartDisplay = () => { buildAxis(timeStamp); }); - let longest = 0; // 32 + let longest = 0; Object.keys(auxObj).forEach((containerName: string) => { if ( @@ -248,9 +228,6 @@ const LineChartDisplay = () => { }); }; - interface obType { - containerName?: any; - } //Fetching the data from github API and turning it into an object with keys of objects that contain the data of each container const fetchGitData = async (containerName: string) => { @@ -273,7 +250,7 @@ const LineChartDisplay = () => { new URLSearchParams({ since: `${date}`, }); - //need an actual url to test this, right now it can't connect + //need a url to test this, right now it can't connect const data = await fetch(url); const jsonData = await data.json(); @@ -322,10 +299,6 @@ const LineChartDisplay = () => { { field: "message", headerName: "Message", width: 525, align: "left" }, ]; - // interface elType { - // name: {}; - // } - gitData = gitUrls.map((el, index: any) => { const name = Object.keys(el); type rowsType = any[]; @@ -399,9 +372,9 @@ const LineChartDisplay = () => { const result: any[] = []; const completeContainerList = [...runningList, ...stoppedList]; completeContainerList.forEach((container, index) => { - const containerNameKey = container.Name - ? container.Name - : container.Names; + const containerNameKey = container['Name'] + ? container['Name'] + : container['Names']; result.push( ) => { -// valueRole = (event.target as HTMLInputElement).value; -// } - -// const NewUserDisplay = () => { -// // const [password, setPassword] = useState(''); -// const [values, setValues] = useState({ -// email: '', -// username: '', -// password: '', -// passwordConfirmation: '', -// phone: '', -// showPassword: false, -// }); - -// const handleClickShowPassword = () => { -// setValues({ -// ...values, -// showPassword: !values.showPassword, -// }); -// }; - -// return ( - -//
    -//
    -//
    -//

    Create a New User

    -//
    -//

    -// Create a new Docketeer account for an employee. Please confirm with the employee that their information is accurate before submitting. -//

    -//
    -//

    -// Note: For the password, please choose a random string of 6 characters, -// numbers, and symbols. Upon account creation, the user will receive an -// email with credentials and be able to update their password when -// logging in. -//

    -//
    -// handleNewUser(e)} -// sx={{color:'blue'}} -// > - -// -//
    -// -//
    - -// -// Password -// { -// checkPasswordLength(e); -// setValues({...values, password:e.target.value}) -// }} -// endAdornment={ -// -// -// {values.showPassword ? : } -// -// -// } -// label="Password" -// /> -// -// {values.password && } -// - -// {/* { -// setPassword(e.target.value); -// checkPasswordLength(e);}} -// sx={{ -// m: 1 -// }} -// /> */} - -// {/* {password && } -// */} - -//
    -// { -// setValues({...values, passwordConfirmation:e.target.value}) -// confirmPassword(e) -// }} -// sx={{ -// m: 1 -// }} -// /> -// {/* This is sacrilege but I hardcoded this bar and made it hidden to keep the same formatting as above */} -// {} -// -//
    -// { -// checkPhone(document.getElementById('signupPhone').value); -// }} -// sx={{ -// m: 1 -// }} -// /> -// -// -// } label="System Admin"> -// } label="Admin"> -// } label="User"> -// -// -//
    -// -//
    -// -//
    -//
    -//
    -// ); -// }; - -// export default NewUserDisplay; - diff --git a/src/components/display/NewUserDisplay.tsx b/src/components/display/NewUserDisplay.tsx index 98c78514..6d6e660d 100644 --- a/src/components/display/NewUserDisplay.tsx +++ b/src/components/display/NewUserDisplay.tsx @@ -10,12 +10,9 @@ import TextField from '@mui/material/TextField'; import Button from '@mui/material/Button'; import Box from '@mui/material/Box'; import IconButton from '@mui/material/IconButton'; -import Input from '@mui/material/Input'; -import FilledInput from '@mui/material/FilledInput'; import OutlinedInput from '@mui/material/OutlinedInput'; import InputLabel from '@mui/material/InputLabel'; import InputAdornment from '@mui/material/InputAdornment'; -import FormHelperText from '@mui/material/FormHelperText'; import FormControl from '@mui/material/FormControl'; import Visibility from '@mui/icons-material/Visibility'; import VisibilityOff from '@mui/icons-material/VisibilityOff'; @@ -23,7 +20,6 @@ import PasswordStrengthBar from 'react-password-strength-bar'; import RadioGroup from "@mui/material/RadioGroup"; import Radio from "@mui/material/Radio"; import FormControlLabel from '@mui/material/FormControlLabel'; -import FormLabel from '@mui/material/FormLabel'; import { handleNewUser, @@ -79,7 +75,7 @@ const NewUserDisplay = () => { className='settingsForm' component= 'form' autoComplete= 'off' - onSubmit={(e) => handleNewUser(e)} + onSubmit={(e: any) => handleNewUser(e, e.target.roleID)} sx={{color:'blue'}} > @@ -111,15 +107,14 @@ const NewUserDisplay = () => { id="signupPassword" type={values.showPassword ? 'text' : 'password'} onChange={(e)=>{ - checkPasswordLength(e); + checkPasswordLength(); setValues({...values, password:e.target.value}) }} endAdornment={ {values.showPassword ? : } @@ -132,25 +127,6 @@ const NewUserDisplay = () => { {values.password && } - {/* { - setPassword(e.target.value); - checkPasswordLength(e);}} - sx={{ - m: 1 - }} - /> */} - - {/* {password && } - */} -
    { required onChange={(e) => { setValues({...values, passwordConfirmation:e.target.value}) - confirmPassword(e) + confirmPassword() }} sx={{ m: 1 }} /> - {/* This is sacrilege but I hardcoded this bar and made it hidden to keep the same formatting as above */} {}
    @@ -176,7 +151,8 @@ const NewUserDisplay = () => { variant='outlined' required onChange={() => { - checkPhone(document.getElementById('signupPhone').value); + const inputValue = (document.getElementById('signupPhone') as HTMLInputElement ).value + checkPhone(inputValue); }} sx={{ m: 1 diff --git a/src/components/display/ProcessLogsCard.tsx b/src/components/display/ProcessLogsCard.tsx index 55c3f362..c050b33e 100644 --- a/src/components/display/ProcessLogsCard.tsx +++ b/src/components/display/ProcessLogsCard.tsx @@ -9,18 +9,8 @@ import React from "react"; import { useNavigate } from "react-router-dom"; import PropTypes from "prop-types"; +import {LogsCardProps} from '../../../types'; -interface containerType { - ID: number; - Name: string; - Names?: string; -} - -interface LogsCardProps { - container: containerType; - index: number; - status: any; -} const ProcessLogsCard = (props: LogsCardProps) => { const navigate = useNavigate(); diff --git a/src/components/display/ProcessLogsTable.tsx b/src/components/display/ProcessLogsTable.tsx index 4ce7b845..53de19c2 100644 --- a/src/components/display/ProcessLogsTable.tsx +++ b/src/components/display/ProcessLogsTable.tsx @@ -9,6 +9,7 @@ import store from "../../renderer/store"; import { DataGrid } from "@mui/x-data-grid"; import { Checkbox, FormControlLabel, FormGroup, Button } from "@mui/material"; // use for container selection import { CSVLink } from "react-csv"; +import { ContainerType, RowsDataType } from "../../../types"; /** * Displays process logs as table @@ -27,7 +28,8 @@ const ProcessLogsTable = () => { const containerID = urlString.split("/"); const id = containerID[containerID.length - 1]; - // access runningList from state + // access runningList from state - store has issue with runningList, ignore until updated + // @ts-ignore const runningList = store.getState().containersList.runningList; const [btnIdList, setBtnIdList] = useState([id]); @@ -124,13 +126,7 @@ const ProcessLogsTable = () => { } }; - interface RowsDataType { - container: string; - type: string; - time: string; - message: string; - id: number; - } + type CSVData = string[]; @@ -142,7 +138,7 @@ const ProcessLogsTable = () => { if (stdout) { stdout.forEach((log, index) => { const currCont = runningList.find( - (el) => el.ID === log["containerName"] + (el: ContainerType) => el.ID === log["containerName"] ); newRows.push({ container: currCont.Name, @@ -156,7 +152,7 @@ const ProcessLogsTable = () => { stderr.forEach((log, index) => { const currCont = runningList.find( - (el) => el.ID === log["containerName"] + (el: ContainerType) => el.ID === log["containerName"] ); newRows.push({ container: currCont.Name, diff --git a/src/components/display/ToggleDisplay.tsx b/src/components/display/ToggleDisplay.tsx index 3c7739a8..35c8b120 100644 --- a/src/components/display/ToggleDisplay.tsx +++ b/src/components/display/ToggleDisplay.tsx @@ -1,10 +1,6 @@ /* eslint-disable react/prop-types */ import React, { useState } from "react"; -import { RunningListType } from "../tabs/TabTypes"; - -interface ToggleDisplayProps { - container: RunningListType; -} +import { ToggleDisplayProps } from "../../../types"; const ToggleDisplay = (props: ToggleDisplayProps) => { const [toggle, setToggle] = useState(false); diff --git a/src/components/login/login.tsx b/src/components/login/login.tsx index 9645a13d..e3ec5f8d 100644 --- a/src/components/login/login.tsx +++ b/src/components/login/login.tsx @@ -11,24 +11,11 @@ import * as actions from '../../redux/actions/actions'; //MUI Elements import TextField from '@mui/material/TextField'; import Button from '@mui/material/Button'; +import {UserInfo} from '../../../types'; // @ts-ignore import Docketeer from '../../../assets/docketeer-title.png'; -interface UserInfo { - _id: number; - username: string; - email: string; - phone: string; - role: string; - role_id: number; - contact_pref: string | null; - mem_threshold: number; - cpu_threshold: number; - container_stops: boolean; - token: string; -} - const Login = () => { const navigate = useNavigate(); const dispatch = useDispatch(); @@ -36,28 +23,8 @@ const Login = () => { const updateUser = (userInfo: UserInfo) => dispatch(actions.updateUser(userInfo)); - // callback function invoked when 'login' button is clicked - const handleLogin = (e: React.ChangeEvent | React.ChangeEvent) => { - e.preventDefault(); // prevents form submit from reloading page - console.log("Event:", e) - const usernameInput = document.getElementById("username"); - const passwordInput = document.getElementById("password"); - if(usernameInput != null || passwordInput != null) { - const username: string = (usernameInput as HTMLInputElement).value; - const password: string = (passwordInput as HTMLInputElement).value; - - // clears input fields after login - (usernameInput as HTMLInputElement).value = ""; - (passwordInput as HTMLInputElement).value = ""; - - console.log("username:", username); - console.log("password:", password); - authenticateUser(username, password); - - }; - - // callback function which will send request to endpoint http://localhost:3000/login and expect - const authenticateUser = (username: string, password: string) => { + // callback function which will send request to endpoint http://localhost:3000/login and expect + const authenticateUser = (username: string, password: string) => { console.log("YOU ARE HERE!") fetch("http://localhost:3000/login", { method: "POST", @@ -74,7 +41,6 @@ const Login = () => { if (Object.prototype.hasOwnProperty.call(data, 'error')) { window.alert(data.error); } else { - // are the two below functions necessary? updateSession(); // loggedIn = true updateUser(data); // update user info in sessions reducer navigate('/'); @@ -85,6 +51,26 @@ const Login = () => { // created a pop window for wrong username/password window.alert('Wrong Password or Username. Please try Again!'); }); + }; + + // callback function invoked when 'login' button is clicked + const handleLogin = (e: React.ChangeEvent | React.ChangeEvent) => { + e.preventDefault(); // prevents form submit from reloading page + console.log("Event:", e) + const usernameInput = document.getElementById("username"); + const passwordInput = document.getElementById("password"); + if(usernameInput != null || passwordInput != null) { + const username: string = (usernameInput as HTMLInputElement).value; + const password: string = (passwordInput as HTMLInputElement).value; + + // clears input fields after login + (usernameInput as HTMLInputElement).value = ""; + (passwordInput as HTMLInputElement).value = ""; + + console.log("username:", username); + console.log("password:", password); + authenticateUser(username, password); + }; return ( @@ -145,5 +131,5 @@ const Login = () => {
    ); }; - +} export default Login; diff --git a/src/components/tabs/Containers.tsx b/src/components/tabs/Containers.tsx index c1aec0c9..b23ca41a 100644 --- a/src/components/tabs/Containers.tsx +++ b/src/components/tabs/Containers.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Chart } from 'react-chartjs-2'; import ToggleDisplay from '../display/ToggleDisplay'; -import { ContainerProps, ContainerType, ChartInfoType } from './TabTypes'; +import { ContainerProps, ContainerType, ChartInfoType } from '../../../types'; /** * Display all running and stopped containers * diff --git a/src/components/tabs/ContainersUser.tsx b/src/components/tabs/ContainersUser.tsx index 0c2e6fc0..5f2c8cb8 100644 --- a/src/components/tabs/ContainersUser.tsx +++ b/src/components/tabs/ContainersUser.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Chart } from 'react-chartjs-2'; import ToggleDisplay from '../display/ToggleDisplay'; -import { ContainerProps, ContainerType, ChartInfoType } from './TabTypes'; +import { ContainerProps, ContainerType, ChartInfoType } from '../../../types'; /** * Display all running and stopped containers * diff --git a/src/components/tabs/Images.tsx b/src/components/tabs/Images.tsx index 0e9de4e7..6886670b 100644 --- a/src/components/tabs/Images.tsx +++ b/src/components/tabs/Images.tsx @@ -1,37 +1,7 @@ /* eslint-disable react/prop-types */ import React, { useState } from 'react'; import * as helper from '../helper/commands'; - -// types to add to the types file -interface ContainerObj { - BlockIO: string, - CPUPerc: string, - Container: string, - ID: string, - MemPerc: string, - MemUsage: string, - Name: string, - NetIO: string, - PIDs: string, - Image?: string, - RunningFor?: string -} - -interface imageObj { - reps: string, - tag: string, - imgid: string, - size: string -} - -interface ImagesProps { - imagesList: imageObj[], - runningList: ContainerObj[], - addRunningContainers: (data: ContainerObj[]) => void, - refreshImagesList: (data: imageObj[]) => void, - runIm: (id: string, runningList: ContainerObj[], callback_1: () => void, callback_2: () => void) => void, - removeIm: ( id: string, imagesList: imageObj[], callback_1: () => void, callback_2: () => void) => void -} +import { ContainerObj, imageObj, ImagesProps } from '../../../types'; /** * Render Images of the user has @@ -112,7 +82,7 @@ const Images = (props: ImagesProps) => {