diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 5dda054..e725430 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -42,16 +42,18 @@ jobs: SKIP_PREFLIGHT_CHECK: ${{ secrets.SKIP_PREFLIGHT_CHECK }} run: | touch .env - echo ACCESS_TOKEN_SECRET="$ACCESS_TOKEN_SECRET" >> .env - echo LOCAL_DATABASE="$LOCAL_DATABASE" >> .env - echo LOCAL_DOCKER_PORT="$LOCAL_DOCKER_PORT" >> .env - echo LOCAL_HOST="$LOCAL_HOST" >> .env - echo LOCAL_PORT="$LOCAL_PORT" >> .env - echo LOCAL_ROOT_PASSWORD="$LOCAL_ROOT_PASSWORD" >> .env - echo LOCAL_USER="$LOCAL_USER" >> .env - echo NODE_ENV="$NODE_ENV" >> .env - echo REFRESH_TOKEN_SECRET="$REFRESH_TOKEN_SECRET" >> .env - echo SKIP_PREFLIGHT_CHECK="$SKIP_PREFLIGHT_CHECK" >> .env + { + echo ACCESS_TOKEN_SECRET="$ACCESS_TOKEN_SECRET" + echo LOCAL_DATABASE="$LOCAL_DATABASE" + echo LOCAL_DOCKER_PORT="$LOCAL_DOCKER_PORT" + echo LOCAL_HOST="$LOCAL_HOST" + echo LOCAL_PORT="$LOCAL_PORT" + echo LOCAL_ROOT_PASSWORD="$LOCAL_ROOT_PASSWORD" + echo LOCAL_USER="$LOCAL_USER" + echo NODE_ENV="$NODE_ENV" + echo REFRESH_TOKEN_SECRET="$REFRESH_TOKEN_SECRET" + echo SKIP_PREFLIGHT_CHECK="$SKIP_PREFLIGHT_CHECK" + } >> .env - run: npm test lint: @@ -74,4 +76,4 @@ jobs: VALIDATE_JAVASCRIPT_STANDARD: false CSS_FILE_NAME: .stylelintrc.json JAVASCRIPT_ES_CONFIG_FILE: .eslintrc.json - JSCPD_CONFIG_FILE: .jscpd.json + JSCPD_CONFIG_FILE: .jscpd.json \ No newline at end of file diff --git a/.github/workspace/.eslintrc.json b/.github/workspace/.eslintrc.json index a434700..ad4e778 100644 --- a/.github/workspace/.eslintrc.json +++ b/.github/workspace/.eslintrc.json @@ -1,5 +1,5 @@ { - "parser": "babel-eslint", + "parser": "@babel/eslint-parser", "plugins": [ "react", "react-hooks" @@ -11,7 +11,8 @@ "react-hooks/exhaustive-deps": "warn" }, "parserOptions": { - "ecmaVersion": 6, + "ecmaVersion": 8, + "requireConfigFile": false, "sourceType":"module" } } diff --git a/.github/workspace/.jscpd.json b/.github/workspace/.jscpd.json index 91aa892..50535d9 100644 --- a/.github/workspace/.jscpd.json +++ b/.github/workspace/.jscpd.json @@ -1,3 +1,3 @@ { - "threshold": 0.5 + "threshold": 50 } diff --git a/.github/workspace/.stylelintrc.json b/.github/workspace/.stylelintrc.json index ca5fb71..0a0db04 100644 --- a/.github/workspace/.stylelintrc.json +++ b/.github/workspace/.stylelintrc.json @@ -1,7 +1,7 @@ { "extends": "stylelint-config-standard", "rules": { - "no-missing-end-of-source-newline": "warn", - "value-no-vendor-prefix": "warn" + "no-missing-end-of-source-newline": true, + "value-no-vendor-prefix": true } } diff --git a/Dockerfile b/Dockerfile index dbda93b..1378ae2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,7 @@ ENV PACKAGE_URL https://repo.mysql.com/yum/mysql-8.0-community/docker/x86_64/mys RUN rpmkeys --import http://repo.mysql.com/RPM-GPG-KEY-mysql \ && yum install -y $PACKAGE_URL \ && yum install -y libpwquality \ + && yum clean all \ && rm -rf /var/cache/yum/* RUN mkdir /docker-entrypoint-initdb.d diff --git a/client/public/404.html b/client/public/404.html index 9da9820..e8e094f 100644 --- a/client/public/404.html +++ b/client/public/404.html @@ -3,11 +3,14 @@ B&C Engine Web Application - - - \ No newline at end of file diff --git a/client/public/index.html b/client/public/index.html index d9fcd26..6b88bec 100644 --- a/client/public/index.html +++ b/client/public/index.html @@ -13,6 +13,9 @@ href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" /> + B&C Engine + + - B&C Engine - -
diff --git a/client/src/App.js b/client/src/App.js index 6e2a432..9550e8f 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,28 +1,27 @@ -import React from "react"; -import Dashboard from "./pages/Dashboard"; -import Reports from "./pages/Reports"; -import Users from "./pages/Users"; -import Manage from "./pages/Manage"; -import Login from "./pages/Login"; -import { Navigate, Routes, Route, BrowserRouter } from "react-router-dom"; +import React from 'react' +import Dashboard from './pages/Dashboard' +import Reports from './pages/Reports' +import Users from './pages/Users' +import Manage from './pages/Manage' +import Login from './pages/Login' +import { Navigate, Routes, Route, BrowserRouter } from 'react-router-dom' - -function App() { +function App () { return (
{/* Routes to pages */} - + - }/> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } />
- ); + ) } -export default App; \ No newline at end of file +export default App diff --git a/client/src/components/ConfirmationPopup.js b/client/src/components/ConfirmationPopup.js index 1699e8e..eb66cc2 100644 --- a/client/src/components/ConfirmationPopup.js +++ b/client/src/components/ConfirmationPopup.js @@ -1,24 +1,24 @@ import React from 'react' import '../styles/index.css' -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next' const DeleteUserPopup = ({ open, prompt, title, onAccept, onClose }) => { - const { t } = useTranslation(); + const { t } = useTranslation() - if (!open) return null; + if (!open) return null - return ( - <> -
-
-

{prompt} {title}?

-
- - -
-
- - ) + return ( + <> +
+
+

{prompt} {title}?

+
+ + +
+
+ + ) } -export default DeleteUserPopup \ No newline at end of file +export default DeleteUserPopup diff --git a/client/src/components/DeleteButton.js b/client/src/components/DeleteButton.js index 2d9a439..bf3c645 100644 --- a/client/src/components/DeleteButton.js +++ b/client/src/components/DeleteButton.js @@ -1,22 +1,26 @@ import Icon from '@mdi/react' -import { mdiDeleteEmpty, mdiDelete } from '@mdi/js'; +import { mdiDeleteEmpty, mdiDelete } from '@mdi/js' const DeleteButton = ({ onDelete }) => { - return ( - - ) + ) } export default DeleteButton diff --git a/client/src/components/EditButton.js b/client/src/components/EditButton.js index ddbae93..43ac6f8 100644 --- a/client/src/components/EditButton.js +++ b/client/src/components/EditButton.js @@ -1,22 +1,26 @@ import Icon from '@mdi/react' -import { mdiPencil, mdiPencilOutline } from '@mdi/js'; +import { mdiPencil, mdiPencilOutline } from '@mdi/js' const EditButton = ({ onEdit }) => { - return ( - - ) + ) } export default EditButton diff --git a/client/src/components/ExportButton.js b/client/src/components/ExportButton.js index 71efc4d..15ba08f 100644 --- a/client/src/components/ExportButton.js +++ b/client/src/components/ExportButton.js @@ -1,23 +1,27 @@ import Icon from '@mdi/react' -import { mdiFileExport, mdiFileExportOutline } from '@mdi/js'; +import { mdiFileExport, mdiFileExportOutline } from '@mdi/js' const EditButton = ({ onExport, id, styles, iconColor }) => { - return ( - - ) + ) } export default EditButton diff --git a/client/src/components/NavB.js b/client/src/components/NavB.js index 12b1e80..c8db1f7 100644 --- a/client/src/components/NavB.js +++ b/client/src/components/NavB.js @@ -1,199 +1,178 @@ -import { LinkContainer } from "react-router-bootstrap" +import { LinkContainer } from 'react-router-bootstrap' import logo from '../Images/logo.png' import { useState } from 'react' import Axios from 'axios' import { useNavigate } from 'react-router-dom' import Cookies from 'universal-cookie' -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next' import { Container, NavDropdown, Nav, Navbar } from 'react-bootstrap' const NavB = (props) => { - const [page] = useState(props); - const cookies = new Cookies(); - const { t, i18n } = useTranslation(); - - const lngs = { - en: { nativeName: 'English' }, - fr: { nativeName: 'Français' }, - 'en-US': { nativeName: 'English' } - }; - - const DashboardLabel = t('navbar.DashboardLabel'); - const ReportsLabel = t('navbar.ReportsLabel'); - const UsersLabel = t('navbar.UsersLabel'); - const ManageLabel = t('navbar.ManageLabel'); - const GreetingLabel = t('navbar.Greeting'); - const SignOutLabel = t('navbar.SignOutLabel'); - - const [languageTitle, setLanguageTitle] = useState(lngs[i18n.language].nativeName); - - let username; - let role; - - if (page.page !== "login") { - username = cookies.get("username"); - role = cookies.get("role"); - } + const [page] = useState(props) + const cookies = new Cookies() + const { t, i18n } = useTranslation() - Axios.defaults.withCredentials = true; + const lngs = { + en: { nativeName: 'English' }, + fr: { nativeName: 'Français' }, + 'en-US': { nativeName: 'English' } + } - let navigate = useNavigate(); + const DashboardLabel = t('navbar.DashboardLabel') + const ReportsLabel = t('navbar.ReportsLabel') + const UsersLabel = t('navbar.UsersLabel') + const ManageLabel = t('navbar.ManageLabel') + const GreetingLabel = t('navbar.Greeting') + const SignOutLabel = t('navbar.SignOutLabel') - const handleNavClick = (e) => { + const [languageTitle, setLanguageTitle] = useState(lngs[i18n.language].nativeName) - if (page.page === "dashboard") { - e.preventDefault(); - props.handleNavClick(e.target.href); - } + let username + let role - } + if (page.page !== 'login') { + username = cookies.get('username') + role = cookies.get('role') + } - const logout = () => { - let refreshToken = cookies.get("refreshToken"); + Axios.defaults.withCredentials = true - if (refreshToken !== undefined) { - - let conf = { - headers: { - authorization: "Bearer " + refreshToken - } - }; - - Axios.delete(`${process.env.REACT_APP_API}/users/logout`, conf) - .then((response) => { - - if (response.status === 204) { - // Future pop-up animation - } - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - console.log(error.response.status); - } - else { - console.log("Malfunction in the B&C Engine..."); - } - } - else if (error.request) { - console.log("Could not reach b&C Engine..."); - } - }); - } + const navigate = useNavigate() - navigate("/login"); - cookies.remove("refreshToken"); - cookies.remove("accessToken"); - cookies.remove("username"); - cookies.remove("role"); + const handleNavClick = (e) => { + if (page.page === 'dashboard') { + e.preventDefault() + props.handleNavClick(e.target.href) } + } - //For Login page navBar - if (page.page === "login") { - return ( - - - - logo - {' '} B&C Engine - - - - - - ) - } + const logout = () => { + const refreshToken = cookies.get('refreshToken') - // When user is loged in, show app's Admin navBar - else { - return ( - - - - - - logo - {' '} B&C Engine - - - - - - - { // if user is admin, show all tabs else, show only dsahboard and reports - role === "admin" ? - - : - - } - - - - - - - ) + if (refreshToken !== undefined) { + const conf = { + headers: { + authorization: 'Bearer ' + refreshToken + } + } + + Axios.delete(`${process.env.REACT_APP_API}/users/logout`, conf) + .then((response) => { + if (response.status === 204) { + // Future pop-up animation + } + }) + .catch((error) => { + if (error.response) { + if (error.response.status === 403 || error.response.status === 401) { + console.log(error.response.status) + } else { + console.log('Malfunction in the B&C Engine...') + } + } else if (error.request) { + console.log('Could not reach b&C Engine...') + } + }) } + + navigate('/login') + cookies.remove('refreshToken') + cookies.remove('accessToken') + cookies.remove('username') + cookies.remove('role') + } + + return ( + + + {page.page === 'login' + ? <> + + logo + {' '} B&C Engine + + + + + : <> + + + logo + {' '} B&C Engine + + + + + + + + + + + } + + + ) } -export default NavB \ No newline at end of file +export default NavB diff --git a/client/src/components/UnderConstruction.js b/client/src/components/UnderConstruction.js index 6517db2..d9a465d 100644 --- a/client/src/components/UnderConstruction.js +++ b/client/src/components/UnderConstruction.js @@ -2,15 +2,15 @@ import { useState } from 'react' import logo from '../Images/underConstruction.gif' const UnderConstruction = (props) => { - const [state] = useState(props); + const [state] = useState(props) - return ( -
- Under Construction -

{state.pageName} Page Under Construction

-

B&C Dev Team

-
- ) + return ( +
+ Under Construction +

{state.pageName} Page Under Construction

+

B&C Dev Team

+
+ ) } -export default UnderConstruction \ No newline at end of file +export default UnderConstruction diff --git a/client/src/components/UsersForm.js b/client/src/components/UsersForm.js index 020cbfb..2115be7 100644 --- a/client/src/components/UsersForm.js +++ b/client/src/components/UsersForm.js @@ -1,475 +1,465 @@ import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' -import Axios from 'axios'; +import Axios from 'axios' import Cookies from 'universal-cookie' -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next' import Icon from '@mdi/react' -import { mdiEye } from '@mdi/js'; -import { mdiEyeOff } from '@mdi/js'; +import { mdiEye, mdiEyeOff } from '@mdi/js' import { Form, CloseButton, Button, Alert, FloatingLabel } from 'react-bootstrap' const UsersForm = (props) => { - let navigate = useNavigate(); - - const { t } = useTranslation(); - const displayNone = "d-none"; - const cookies = new Cookies(); - - const emptyError = t('error.Empty'); - const notFoundError = t('error.NotFound'); - - // To fix issue where useEffects would trigger when component was loaded - const [isLoadEdit, setIsLoadEdit] = useState(false); - const [isLoadAdd, setIsLoadAdd] = useState(false); - const [isLoadDisable, setIsLoadDisable] = useState(false); - - const [FormTitle, setFormTitle] = useState(""); - const [emailEnable, setEmailEnable] = useState(""); - const [FormSubmit, setFormSubmit] = useState(""); - const [passwordEnable, setPasswordEnable] = useState(""); - const [roleEnable, setRoleEnable] = useState(""); - const [InvalidInput, setInvalidInput] = useState(""); - const [errors, setErrors] = useState({}); - const [showPass, setShowPass] = useState(false); - const [showPass2, setShowPass2] = useState(false); - - const [onConfirmationScreen, setOnConfirmationScreen] = useState(false); - const [submitType, setSubmitType] = useState("submit"); - const [validated, setValidated] = useState(false); - - const [form, setForm] = useState({ - email: "", - password1: "", - password2: "", - role: "" - }); - - const [backEnabled, setBackEnabled] = useState({ - backButton: displayNone + const navigate = useNavigate() + + const { t } = useTranslation() + const displayNone = 'd-none' + const cookies = new Cookies() + + const emptyError = t('error.Empty') + const notFoundError = t('error.NotFound') + + // To fix issue where useEffects would trigger when component was loaded + const [isLoadEdit, setIsLoadEdit] = useState(false) + const [isLoadAdd, setIsLoadAdd] = useState(false) + const [isLoadDisable, setIsLoadDisable] = useState(false) + + const [FormTitle, setFormTitle] = useState('') + const [emailEnable, setEmailEnable] = useState('') + const [FormSubmit, setFormSubmit] = useState('') + const [passwordEnable, setPasswordEnable] = useState('') + const [roleEnable, setRoleEnable] = useState('') + const [InvalidInput, setInvalidInput] = useState('') + const [errors, setErrors] = useState({}) + const [showPass, setShowPass] = useState(false) + const [showPass2, setShowPass2] = useState(false) + + const [onConfirmationScreen, setOnConfirmationScreen] = useState(false) + const [submitType, setSubmitType] = useState('submit') + const [validated, setValidated] = useState(false) + + const [form, setForm] = useState({ + email: '', + password1: '', + password2: '', + role: '' + }) + + const [backEnabled, setBackEnabled] = useState({ + backButton: displayNone + }) + + const enableBackButton = () => { + setBackEnabled({ + backButton: 'btn btn-light py-2 px-5 my-1 shadow-sm border' }) - - const enableBackButton = () => { - setBackEnabled({ - backButton: "btn btn-light py-2 px-5 my-1 shadow-sm border" - }); - } - const disableBackButton = () => { - setBackEnabled({ - backButton: displayNone - }); + } + const disableBackButton = () => { + setBackEnabled({ + backButton: displayNone + }) + } + + // calls disableForm function located in Users.js + const disableForm = () => { + props.disableForm() + } + + const handleAddUser = () => { + // calls enableForm function located in Users.js + props.enableForm() + + setInvalidInput('') + setEmailEnable('') + setPasswordEnable('') + setRoleEnable('') + setFormTitle(t('user.create.Title')) + setFormSubmit(t('user.create.FormSubmit')) + disableBackButton() + setErrors({}) + setForm({ + email: '', + password1: '', + password2: '', + role: '' + }) + } + + useEffect(() => { + if (isLoadAdd) handleAddUser() + else setIsLoadAdd(true) + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.handleAddUser]) + + const handleEditUser = (email, role) => { + // calls enableForm function located in Users.js + props.enableForm() + + setEmailEnable('disable') + setPasswordEnable('') + setRoleEnable('') + setFormTitle(t('user.update.Title')) + setFormSubmit(t('user.update.FormSubmit')) + disableBackButton() + setForm({ + email: email, + password1: '', + password2: '', + role: role + }) + } + + // values email and role are passed from Users.js + useEffect(() => { + if (isLoadEdit) handleEditUser(props.editValues.email, props.editValues.role) + else setIsLoadEdit(true) + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.handleEditUser]) + + const handleSubmit = (event) => { + event.preventDefault() + event.stopPropagation() + + setInvalidInput('') + + const newErrors = findFormErrors() + + if (Object.keys(newErrors).length > 0) { + setErrors(newErrors) + } else if (InvalidInput.length === 0) { + if (FormTitle === t('user.update.Title')) { + setFormTitle(t('user.update.Confirmation')) + } else if (FormTitle === t('user.create.Title')) { + setFormTitle(t('user.create.Confirmation')) + } + + setSubmitType('button') + setOnConfirmationScreen(true) + setFormSubmit(t('form.SubmitButton')) + setEmailEnable('disable') + setPasswordEnable('disable') + setShowPass(false) + setShowPass2(false) + setRoleEnable('disable') + enableBackButton() + setErrors({}) } + } - // calls disableForm function located in Users.js - const disableForm = () => { - props.disableForm(); - } + const handleConfirm = (event) => { + event.preventDefault() + event.stopPropagation() - const handleAddUser = () => { - // calls enableForm function located in Users.js - props.enableForm(); - - setInvalidInput(""); - setEmailEnable(""); - setPasswordEnable(""); - setRoleEnable(""); - setFormTitle(t('user.create.Title')); - setFormSubmit(t('user.create.FormSubmit')); - disableBackButton(); - setErrors({}); - setForm({ - email: "", - password1: "", - password2: "", - role: "" - }); + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') } - useEffect(() => { - if (isLoadAdd) handleAddUser(); - else setIsLoadAdd(true); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.handleAddUser]); - - const handleEditUser = (email, role) => { - // calls enableForm function located in Users.js - props.enableForm(); - - setEmailEnable("disable"); - setPasswordEnable(""); - setRoleEnable(""); - setFormTitle(t('user.update.Title')); - setFormSubmit(t('user.update.FormSubmit')); - disableBackButton(); - setForm({ - email: email, - password1: "", - password2: "", - role: role - }); + const data = { + email: form.email, + password: form.password1, + role: form.role } - // values email and role are passed from Users.js - useEffect(() => { - if (isLoadEdit) handleEditUser(props.editValues.email, props.editValues.role); - else setIsLoadEdit(true); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.handleEditUser]); - - const handleSubmit = (event) => { - event.preventDefault(); - event.stopPropagation(); - - setInvalidInput(""); - - const newErrors = findFormErrors(); - - if (Object.keys(newErrors).length > 0) { - setErrors(newErrors); - } - - else if (InvalidInput.length === 0) { - if (FormTitle === t('user.update.Title')) { - setFormTitle(t('user.update.Confirmation')); - } - else if (FormTitle === t('user.create.Title')) { - setFormTitle(t('user.create.Confirmation')); + if (FormTitle === t('user.update.Confirmation')) { + onUpdateClick() + } else if (FormTitle === t('user.create.Confirmation')) { + Axios.post(`${process.env.REACT_APP_API}/users/`, data, { headers: header }) + .then((response) => { + if (response.status === 200 || response.status === 201) { + disableForm() + } + }) + .catch((error) => { + if (error.response) { + if (error.response.status === 403 || error.response.status === 401) { + setInvalidInput(error.response.data.message || '') + navigate('/login') + } else { + setInvalidInput(error.response.data.message) + + handleGoBack() } - - setSubmitType("button"); - setOnConfirmationScreen(true); - setFormSubmit(t('form.SubmitButton')); - setEmailEnable("disable"); - setPasswordEnable("disable"); - setShowPass(false); - setShowPass2(false); - setRoleEnable("disable"); - enableBackButton(); - setErrors({}); - } + } else if (error.request) { + // The request was made but no response was received + // `error.request` is an instance of XMLHttpRequest in the browser and an instance of + // http.ClientRequest in node.js + setInvalidInput(notFoundError) + } + }) + } + } + + const findFormErrors = () => { + const { email, password1, password2, role } = form + const newErrors = {} + const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})/; + + // email errors + if (!email || email === '') newErrors.email = emptyError + else if (!email.endsWith('@benoit-cote.com')) newErrors.email = t('user.error.InvalidEmailValidation') + + // password errors + if (!password1 || password1 === '') { + newErrors.password1 = emptyError + } else if (!RegExp(passwordPattern).exec(password1)) { + newErrors.password1 = t('user.error.InvalidPasswordValidation') + } + if (password1 !== password2) { + newErrors.password1 = t('error.PasswordMatch') + newErrors.password2 = t('error.PasswordMatch') + } + if (!password2 || password2 === '') { + newErrors.password2 = emptyError } - const handleConfirm = (event) => { - event.preventDefault(); - event.stopPropagation(); + // role errors + if (!role || role === '') newErrors.role = t('user.error.MustSelectRoleValidation') - let header = { - 'authorization': "Bearer " + cookies.get("accessToken") - } + return newErrors + } - let data = { - email: form.email, - password: form.password1, - role: form.role - } + const setField = (field, value) => { + setForm({ + ...form, + [field]: value + }) - if (FormTitle === t('user.update.Confirmation')) { - onUpdateClick(); - } - else if (FormTitle === t('user.create.Confirmation')) { - Axios.post(`${process.env.REACT_APP_API}/users/`, data, { headers: header }) - .then((response) => { - if (response.status === 200 || response.status === 201) { - disableForm(); - } - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - setInvalidInput(error.response.data.message || ""); - navigate("/login"); - } - else { - setInvalidInput(error.response.data.message); - - handleGoBack(); - } - } else if (error.request) { - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - setInvalidInput(notFoundError); - } - }); - } + if (errors[field]) { + setErrors({ + ...errors, + [field]: null + }) } + } - const findFormErrors = () => { - const { email, password1, password2, role } = form; - const newErrors = {}; - - // email errors - if (!email || email === "") newErrors.email = emptyError; - else if (!email.endsWith("@benoit-cote.com")) newErrors.email = t('user.error.InvalidEmailValidation'); - - // password errors - if (!password1 || password1 === "") { - newErrors.password1 = emptyError; + const onUpdateClick = () => { + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') + } - } else if (!RegExp("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})").exec(password1)) { - newErrors.password1 = t('user.error.InvalidPasswordValidation'); + Axios.defaults.withCredentials = true - } - if (password1 !== password2) { - newErrors.password1 = t('error.PasswordMatch'); - newErrors.password2 = t('error.PasswordMatch'); + const user = { + email: form.email, + password: form.password2, + role: form.role + } + Axios.put(`${process.env.REACT_APP_API}/users/modify/${form.email}`, user, { headers: header }) + .then((response) => { + if (response.data === true) { + console.log('User modified successfully!') } - if (!password2 || password2 === "") { - newErrors.password2 = emptyError; + disableForm() + }) + .catch((error) => { + if (error.response) { + if (error.response.status === 401 || error.response.status === 403) { + setInvalidInput(t('user.error.InvalidEmailUpdate')) + } else { + setInvalidInput(notFoundError) + } + } else if (error.request) { + setInvalidInput(t('user.error.RequestFailedUpdate')) } - - // role errors - if (!role || role === "") newErrors.role = t('user.error.MustSelectRoleValidation'); - - return newErrors; + }) + + setValidated(true) + return false + } + + const handleGoBack = () => { + if (FormTitle === t('user.create.Confirmation')) { + setEmailEnable('') + setFormTitle(t('user.create.Title')) + setFormSubmit(t('user.create.FormSubmit')) + } else if (FormTitle === t('user.update.Confirmation')) { + setEmailEnable('disable') + setFormTitle(t('user.update.Title')) + setFormSubmit(t('user.update.FormSubmit')) } - const setField = (field, value) => { - setForm({ - ...form, - [field]: value - }); - - if (!!errors[field]) { - setErrors({ - ...errors, - [field]: null - }); - } + setSubmitType('submit') + setOnConfirmationScreen(false) + setPasswordEnable('') + setRoleEnable('') + disableBackButton() + } + + const showHide = (firstPassword) => { + if (firstPassword) { + if (showPass) setShowPass(false) + else setShowPass(true) + } else { + if (showPass2) setShowPass2(false) + else setShowPass2(true) } - - const onUpdateClick = () => { - let header = { - 'authorization': "Bearer " + cookies.get("accessToken") - } - - Axios.defaults.withCredentials = true; - - let user = { - email: form.email, - password: form.password2, - role: form.role - }; - - Axios.put(`${process.env.REACT_APP_API}/users/modify/${form.email}`, user, { headers: header }) - .then((response) => { - if (response.data === true) { - console.log("User modified successfully!"); - } - disableForm(); - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 401 || error.response.status === 403) { - setInvalidInput(t('user.error.InvalidEmailUpdate')); - } - else { - setInvalidInput(notFoundError); - } - } - else if (error.request) { - setInvalidInput(t('user.error.RequestFailedUpdate')); - } - }); - - setValidated(true); - return false; + } + + useEffect(() => { + if (isLoadDisable) { + setValidated(false) + setOnConfirmationScreen(false) + setSubmitType('submit') + + Array.from(document.querySelectorAll('input')).forEach( + input => (input.value = '') + ) + Array.from(document.querySelectorAll('select')).forEach( + select => (select.value = '') + ) + } else { + setIsLoadDisable(true) } - const handleGoBack = () => { - if (FormTitle === t('user.create.Confirmation')) { - setEmailEnable(""); - setFormTitle(t('user.create.Title')); - setFormSubmit(t('user.create.FormSubmit')); - } - else if (FormTitle === t('user.update.Confirmation')) { - setEmailEnable("disable"); - setFormTitle(t('user.update.Title')); - setFormSubmit(t('user.update.FormSubmit')); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.handleDisableForm]) + + return ( +
+ + +
+ +

{FormTitle}

+ + { + InvalidInput.length > 0 ? + + {InvalidInput} + + : <> } - setSubmitType("submit"); - setOnConfirmationScreen(false); - setPasswordEnable(""); - setRoleEnable(""); - disableBackButton(); - } + + + setField('email', e.target.value)} + value={form.email} + autoComplete='new-email' + disabled={emailEnable} + isInvalid={!!errors.email} + /> + + + {errors.email} + + + + + + + setField('password1', e.target.value)} + autoComplete='new-password' + disabled={passwordEnable} + value={form.password1} + isInvalid={!!errors.password1} + /> + { + passwordEnable + ? <> + : showHide(true)} + size={1} + /> + } - const showHide = (firstPassword) => { - if (firstPassword) { - if (showPass) setShowPass(false); - else setShowPass(true); - } - else { - if (showPass2) setShowPass2(false); - else setShowPass2(true); - } - } + + {errors.password1} + + + + + + + setField('password2', e.target.value)} + autoComplete='off' + value={form.password2} + disabled={passwordEnable} + isInvalid={!!errors.password2} + /> + + { + passwordEnable + ? <> + : showHide(false)} + size={1} + /> + } - useEffect(() => { - if (isLoadDisable) { - setValidated(false); - setOnConfirmationScreen(false); - setSubmitType("submit"); - - Array.from(document.querySelectorAll("input")).forEach( - input => (input.value = "") - ); - Array.from(document.querySelectorAll("select")).forEach( - select => (select.value = "") - ); - } - else { - setIsLoadDisable(true); - } + + {errors.password2} + + + + + + {t('form.Role')} + setField('role', e.target.value)} + value={form.role} + disabled={roleEnable} + isInvalid={!!errors.role} + > + + + + + + + + + +
+ + + - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.handleDisableForm]); - - return ( -
- - - - -

{FormTitle}

- - { - InvalidInput.length > 0 ? - - {InvalidInput} - : - <> - } - - - - setField('email', e.target.value)} - value={form.email} - autoComplete='new-email' - disabled={emailEnable} - isInvalid={!!errors.email} - /> - - - {errors.email} - - - - - - - setField('password1', e.target.value)} - autoComplete='new-password' - disabled={passwordEnable} - value={form.password1} - isInvalid={!!errors.password1} - /> - { - passwordEnable ? - <> : - showHide(true)} - size={1} /> - } - - - - {errors.password1} - - - - - - - setField('password2', e.target.value)} - autoComplete='off' - value={form.password2} - disabled={passwordEnable} - isInvalid={!!errors.password2} - /> - - { - passwordEnable ? - <> : - showHide(false)} - size={1} /> - } - - - - {errors.password2} - - - - - - {t('form.Role')} - setField('role', e.target.value)} - value={form.role} - disabled={roleEnable} - isInvalid={!!errors.role}> - - - - - - - - - - - -
- - - - - -
-
- ); + +
+ ) } -export default UsersForm \ No newline at end of file +export default UsersForm diff --git a/client/src/i18n.js b/client/src/i18n.js index d331fba..f76c564 100644 --- a/client/src/i18n.js +++ b/client/src/i18n.js @@ -1,33 +1,33 @@ -import i18n from 'i18next'; -import { initReactI18next } from 'react-i18next'; -import LanguageDetector from 'i18next-browser-languagedetector'; +import i18n from 'i18next' +import { initReactI18next } from 'react-i18next' +import LanguageDetector from 'i18next-browser-languagedetector' -import translationEN from './locales/en/translation.json'; -import translationFR from './locales/fr/translation.json'; +import translationEN from './locales/en/translation.json' +import translationFR from './locales/fr/translation.json' const resources = { - en: { - translation: translationEN - }, - fr: { - translation: translationFR - } -}; + en: { + translation: translationEN + }, + fr: { + translation: translationFR + } +} i18n - // detect user language - .use(LanguageDetector) - // pass the i18n instance to react-i18next. - .use(initReactI18next) - // init i18next - .init({ - debug: true, - fallbackLng: 'en', - interpolation: { - escapeValue: false, // not needed for react as it escapes by default - }, - // here are all the translations - resources: resources - }); +// detect user language + .use(LanguageDetector) +// pass the i18n instance to react-i18next. + .use(initReactI18next) +// init i18next + .init({ + debug: true, + fallbackLng: 'en', + interpolation: { + escapeValue: false // not needed for react as it escapes by default + }, + // here are all the translations + resources: resources + }) -export default i18n; \ No newline at end of file +export default i18n diff --git a/client/src/index.js b/client/src/index.js index 2134612..3b4afe6 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -1,19 +1,19 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './styles/index.css'; +import React from 'react' +import ReactDOM from 'react-dom' +import './styles/index.css' import './styles/scrollbar.css' -import App from './App'; -import reportWebVitals from './reportWebVitals'; -import './i18n'; +import App from './App' +import reportWebVitals from './reportWebVitals' +import './i18n' ReactDOM.render( , document.getElementById('root') -); +) // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +reportWebVitals() diff --git a/client/src/pages/Dashboard.js b/client/src/pages/Dashboard.js index 78a8e05..f6213b5 100644 --- a/client/src/pages/Dashboard.js +++ b/client/src/pages/Dashboard.js @@ -1,899 +1,860 @@ -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router'; -import { Form, Table, InputGroup, FormControl, FormLabel, Button, ButtonGroup, OverlayTrigger, DropdownButton, Dropdown, Tooltip as ToolTipBootstrap, FormCheck, Col, Row } from 'react-bootstrap'; -import ConfirmationPopup from '../components/ConfirmationPopup'; -import { useTranslation } from 'react-i18next'; -import Axios from 'axios'; -import Cookies from 'universal-cookie'; +import { useEffect, useState } from 'react' +import { useNavigate } from 'react-router' +import { Form, Table, InputGroup, FormControl, FormLabel, Button, ButtonGroup, OverlayTrigger, DropdownButton, Dropdown, Tooltip as ToolTipBootstrap, FormCheck, Col, Row } from 'react-bootstrap' +import ConfirmationPopup from '../components/ConfirmationPopup' +import { useTranslation } from 'react-i18next' +import Axios from 'axios' +import Cookies from 'universal-cookie' import NavB from '../components/NavB' import '../styles/clientTable.css' import '../styles/dashboardPage.css' import { Oval } from 'react-loader-spinner' -import { Bar } from 'react-chartjs-2'; +import { Bar } from 'react-chartjs-2' import { - Chart as ChartJS, - CategoryScale, - LinearScale, - BarElement, - Title, - Tooltip, - Legend, -} from 'chart.js'; + Chart as ChartJS, + CategoryScale, + LinearScale, + BarElement, + Title, + Tooltip, + Legend +} from 'chart.js' ChartJS.register( - CategoryScale, - LinearScale, - BarElement, - Title, - Tooltip, - Legend -); + CategoryScale, + LinearScale, + BarElement, + Title, + Tooltip, + Legend +) const Dashboard = () => { - let navigate = useNavigate(); - const cookies = new Cookies(); - const { t } = useTranslation(); - - // variables used for internationalization - const chartReportNamePlaceHolder = t('dashboard.criteria.NamePlaceHolder'); - const loadChartButtonText = t('dashboard.criteria.LoadChartButton'); - const saveChartButtonText = t('dashboard.criteria.SaveChartButton'); - const chartTitle = t('dashboard.chart.Title'); - const chartXLabel = t('dashboard.chart.XAxisLabel'); - const chartYLabel = t('dashboard.chart.YAxisLabel'); - const months = [ - t('dashboard.chart.months.Jan'), - t('dashboard.chart.months.Feb'), - t('dashboard.chart.months.Mar'), - t('dashboard.chart.months.Apr'), - t('dashboard.chart.months.May'), - t('dashboard.chart.months.Jun'), - t('dashboard.chart.months.Jul'), - t('dashboard.chart.months.Aug'), - t('dashboard.chart.months.Sep'), - t('dashboard.chart.months.Oct'), - t('dashboard.chart.months.Nov'), - t('dashboard.chart.months.Dec') - ]; - const chartFallbackLegendLabel = t('dashboard.chart.FallbackLegendLabel'); - - let colors = [ - 'rgb(255, 192, 159)', - 'rgb(191, 175, 192)', - 'rgb(255, 238, 147)', - 'rgb(160, 206, 217)', - 'rgb(173, 247, 182)' - ]; - - let compareColors = [ - 'rgb(255, 139, 77)', - 'rgb(127, 101, 129)', - 'rgb(255, 224, 51)', - 'rgb(65, 144, 164)', - 'rgb(46, 234, 68)' - ] - - const fallbackChartData = [ - { - label: chartFallbackLegendLabel, - data: [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], - backgroundColor: 'rgb(127, 128, 203)' - } - ]; - - const [chartData, setChartData] = useState(fallbackChartData); - const [chartLoading, setChartLoading] = useState(false); - const [confirmSaveActivated, setConfirmSaveActivated] = useState(false); - const [chartSaved, setChartSaved] = useState(true); - const [navClicked, setNavClicked] = useState(false); - const [pageToNavigateTo, setPageToNavigateTo] = useState("/reports"); - - const currentYear = new Date().getFullYear(); - const currentMonth = new Date().getMonth(); - const earliestYear = 2009; - const [authorized, setAuthorized] = useState(false); - const [clientNameCountry, setClientNameCountry] = useState([{ name: "", country: "", clientgrading: "" }]); - - - // Criteria - const [employeeSelect, setEmployeeSelect] = useState([]); - const [compareEmployeeChecked, setCompareEmployeeChecked] = useState(false); - - const [criteria, setCriteria] = useState({ - name: "", - startYear: currentYear - 2, - startMonth: 0, - endYear: currentMonth === 0 ? currentYear - 1 : currentYear, - endMonth: currentMonth === 0 ? 11 : currentMonth - 1, - employee1: { - id: -1, - name: "All" - }, - employee2: { - id: compareEmployeeChecked ? -1 : null, - name: compareEmployeeChecked ? "All" : null - }, - country: { - id: -1, - name: "All" - }, - clientType: "Any", - ageOfAccount: "All", - accountType: 'Receivable', - }); - const [errors, setErrors] = useState({}); - - const [yearList, setYearList] = useState([]); - const [monthList, setMonthList] = useState([]); - - const [countries, setCountries] = useState([{ countryCode: "", countryLabel: "" }]); - - const setField = (field, value) => { - if (field === 'employee1' && parseInt(value.id) === -1) { - setCompareEmployeeChecked(false); - setCriteria({ - ...criteria, - [field]: value, - 'employee2': { id: null, name: null } - }); - } - else { - if (field === 'employee2') { - setCompareEmployeeChecked(!compareEmployeeChecked); - } - - setCriteria({ - ...criteria, - [field]: value - }); - } - - if (!!errors[field]) { - setErrors({ - ...errors, - [field]: null - }); - } + const navigate = useNavigate() + const cookies = new Cookies() + const { t } = useTranslation() + + // variables used for internationalization + const chartReportNamePlaceHolder = t('dashboard.criteria.NamePlaceHolder') + const loadChartButtonText = t('dashboard.criteria.LoadChartButton') + const saveChartButtonText = t('dashboard.criteria.SaveChartButton') + const chartTitle = t('dashboard.chart.Title') + const chartXLabel = t('dashboard.chart.XAxisLabel') + const chartYLabel = t('dashboard.chart.YAxisLabel') + const months = [ + t('dashboard.chart.months.Jan'), + t('dashboard.chart.months.Feb'), + t('dashboard.chart.months.Mar'), + t('dashboard.chart.months.Apr'), + t('dashboard.chart.months.May'), + t('dashboard.chart.months.Jun'), + t('dashboard.chart.months.Jul'), + t('dashboard.chart.months.Aug'), + t('dashboard.chart.months.Sep'), + t('dashboard.chart.months.Oct'), + t('dashboard.chart.months.Nov'), + t('dashboard.chart.months.Dec') + ] + const chartFallbackLegendLabel = t('dashboard.chart.FallbackLegendLabel') + + const colors = [ + 'rgb(255, 192, 159)', + 'rgb(191, 175, 192)', + 'rgb(255, 238, 147)', + 'rgb(160, 206, 217)', + 'rgb(173, 247, 182)' + ] + + const compareColors = [ + 'rgb(255, 139, 77)', + 'rgb(127, 101, 129)', + 'rgb(255, 224, 51)', + 'rgb(65, 144, 164)', + 'rgb(46, 234, 68)' + ] + + const fallbackChartData = [ + { + label: chartFallbackLegendLabel, + data: [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], + backgroundColor: 'rgb(127, 128, 203)' + } + ] + + const [chartData, setChartData] = useState(fallbackChartData) + const [chartLoading, setChartLoading] = useState(false) + const [confirmSaveActivated, setConfirmSaveActivated] = useState(false) + const [chartSaved, setChartSaved] = useState(true) + const [navClicked, setNavClicked] = useState(false) + const [pageToNavigateTo, setPageToNavigateTo] = useState('/reports') + + const currentYear = new Date().getFullYear() + const currentMonth = new Date().getMonth() + const earliestYear = 2009 + const [authorized, setAuthorized] = useState(false) + const [clientNameCountry, setClientNameCountry] = useState([{ name: '', country: '', clientgrading: '' }]) + + // Criteria + const [employeeSelect, setEmployeeSelect] = useState([]) + const [compareEmployeeChecked, setCompareEmployeeChecked] = useState(false) + + const [criteria, setCriteria] = useState({ + name: '', + startYear: currentYear - 2, + startMonth: 0, + endYear: currentMonth === 0 ? currentYear - 1 : currentYear, + endMonth: currentMonth === 0 ? 11 : currentMonth - 1, + employee1: { + id: -1, + name: 'All' + }, + employee2: { + id: compareEmployeeChecked ? -1 : null, + name: compareEmployeeChecked ? 'All' : null + }, + country: { + id: -1, + name: 'All' + }, + clientType: 'Any', + ageOfAccount: 'All', + accountType: 'Receivable' + }) + const [errors, setErrors] = useState({}) + + const [yearList, setYearList] = useState([]) + const [monthList, setMonthList] = useState([]) + + const [countries, setCountries] = useState([{ countryCode: '', countryLabel: '' }]) + + const setField = (field, value) => { + if (field === 'employee1' && parseInt(value.id) === -1) { + setCompareEmployeeChecked(false) + setCriteria({ + ...criteria, + [field]: value, + employee2: { id: null, name: null } + }) + } else { + if (field === 'employee2') { + setCompareEmployeeChecked(!compareEmployeeChecked) + } + + setCriteria({ + ...criteria, + [field]: value + }) + } - setChartSaved(false); - }; + if (errors[field]) { + setErrors({ + ...errors, + [field]: null + }) + } - const createEmployeeCriteria = async () => { - let listEmployee = []; + setChartSaved(false) + } - let header = { - 'authorization': "Bearer " + cookies.get("accessToken"), - } + const createEmployeeCriteria = async () => { + const listEmployee = [] - await Axios.get(`${process.env.REACT_APP_API}/invoice/employees`, { headers: header }) - .then((res) => { - if (res.status === 403 && res.status === 401) { - setAuthorized(false); - return; - } - setAuthorized(true); - - for (let i = 0; i < res.data.length; i++) { - listEmployee.push({ - name: res.data[i].name, - id: res.data[i].nameID - }); - } - setEmployeeSelect(listEmployee); - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - alert(error.response.body); - } - else { - alert("Malfunction in the B&C Engine..."); - } - } - else if (error.request) { - alert("Could not reach b&C Engine..."); - } - }); + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') } - //to display the list of all countries in the select box - const countrySelectBox = async () => { - let countryList = []; - - let header = { - 'authorization': "Bearer " + cookies.get("accessToken"), + await Axios.get(`${process.env.REACT_APP_API}/invoices/employees`, { headers: header }) + .then((res) => { + for (let i = 0; i < res.data.length; i++) { + listEmployee.push({ + name: res.data[i].name, + id: res.data[i].nameID + }) } + setEmployeeSelect(listEmployee) + }) + .catch((error) => handleNon2xxResponse(error)) + } - await Axios.get(`${process.env.REACT_APP_API}/invoice/getCountries`, { headers: header }) - .then(async (res) => { - if (res.status === 403 && res.status === 401) { - setAuthorized(false); - return; - } - setAuthorized(true); - - for (let i = 0; i < res.data.length; i++) { - countryList.push({ - countryCode: res.data[i].countryCode, - countryLabel: res.data[i].countryLabel - }); - } - setCountries(countryList); - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - alert(error.response.body); - } - else { - alert("Malfunction in the B&C Engine..."); - } - } - else if (error.request) { - alert("Could not reach b&C Engine..."); - } - }); + // To display the list of all countries in the select box + const countrySelectBox = async () => { + const countryList = [] + + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') } - const chart = async (compare = false) => { - setChartLoading(true); - setChartData(fallbackChartData); - localStorage.setItem("dash_previous_criteria", JSON.stringify(criteria)); - let compareData = []; - let arrayLength = 1; - if (compare) arrayLength = 2; - for (let c = 0; c < arrayLength; c++) { - let clientInfoList = []; - - let header = { - 'authorization': "Bearer " + cookies.get("accessToken"), + await Axios.get(`${process.env.REACT_APP_API}/invoices/getCountries`, { headers: header }) + .then(async (res) => { + for (let i = 0; i < res.data.length; i++) { + countryList.push({ + countryCode: res.data[i].countryCode, + countryLabel: res.data[i].countryLabel + }) + } + setCountries(countryList) + }) + .catch((error) => handleNon2xxResponse(error)) + } + + const chart = async (compare = false) => { + setChartLoading(true) + setChartData(fallbackChartData) + localStorage.setItem('dash_previous_criteria', JSON.stringify(criteria)) + let compareData = [] + let arrayLength = 1 + if (compare) arrayLength = 2 + for (let c = 0; c < arrayLength; c++) { + const clientInfoList = [] + + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') + } + + const startDate = new Date(criteria.startYear, criteria.startMonth, 1).toISOString().split('T')[0] + const endDate = new Date(criteria.endYear, criteria.endMonth, 1).toISOString().split('T')[0] + + let param = { + employeeId: parseInt(criteria.employee1.id) === -1 ? undefined : criteria.employee1.id, + clientType: criteria.clientType === 'Any' ? undefined : criteria.clientType, + countryCode: parseInt(criteria.country.id) === -1 ? undefined : criteria.country.id, + countryLabel: parseInt(criteria.country.id) === -1 ? undefined : criteria.country.name, + ageOfAccount: criteria.ageOfAccount === 'All' ? undefined : criteria.ageOfAccount + } + + if (c === 1) { + param = { + ...param, + employeeId: undefined + } + } + + await Axios.get(`${process.env.REACT_APP_API}/invoices/defaultChartAndTable/${startDate}/${endDate}`, { params: param, headers: header }) + .then((res) => { + if (res.status === 403 && res.status === 401) { + setAuthorized(false) + return + } + setAuthorized(true) + + const datasets = [] + const groupedChartData = res.data[0].chart + + let colorCounter = 0 + + for (let i = 0; i < Object.keys(groupedChartData).length; i++) { + const data = [] + const dataGroup = groupedChartData[Object.keys(groupedChartData)[i]] + const startMonth = parseInt(dataGroup[0].month.toString().substring(4)) - 1 + const endMonth = parseInt(dataGroup[dataGroup.length - 1].month.toString().substring(4)) - 1 + let counter = 0 + + for (let j = 0; j < 12; j++) { + if (j >= startMonth && j <= endMonth) { + data.push(parseFloat(dataGroup[counter].average)) + counter++ + } else { + data.push(0) + } } - let startDate = new Date(criteria.startYear, criteria.startMonth, 1).toISOString().split("T")[0]; - let endDate = new Date(criteria.endYear, criteria.endMonth, 1).toISOString().split("T")[0]; - - let param = { - employeeId: parseInt(criteria.employee1.id) === -1 ? undefined : criteria.employee1.id, - clientType: criteria.clientType === "Any" ? undefined : criteria.clientType, - countryCode: parseInt(criteria.country.id) === -1 ? undefined : criteria.country.id, - countryLabel: parseInt(criteria.country.id) === -1 ? undefined : criteria.country.name, - ageOfAccount: criteria.ageOfAccount === "All" ? undefined : criteria.ageOfAccount - }; - - if (c === 1) { - param = { - ...param, - employeeId: undefined - }; + let datasetLabel = groupedChartData[Object.keys(groupedChartData)[i]][0].group + let colorBG = colors[colorCounter] + + if (compare && c === 0) { + datasetLabel = groupedChartData[Object.keys(groupedChartData)[i]][0].group.toString().concat(' - emp') + colorBG = compareColors[colorCounter] } - await Axios.get(`${process.env.REACT_APP_API}/invoice/defaultChartAndTable/${startDate}/${endDate}`, { params: param, headers: header }) - .then((res) => { - if (res.status === 403 && res.status === 401) { - setAuthorized(false); - return; - } - setAuthorized(true); - - let datasets = []; - let groupedChartData = res.data[0].chart; - - let colorCounter = 0; - - for (let i = 0; i < Object.keys(groupedChartData).length; i++) { - let data = []; - const dataGroup = groupedChartData[Object.keys(groupedChartData)[i]]; - let startMonth = parseInt(dataGroup[0].month.toString().substring(4)) - 1; - let endMonth = parseInt(dataGroup[dataGroup.length - 1].month.toString().substring(4)) - 1; - let counter = 0; - - for (let j = 0; j < 12; j++) { - if (j >= startMonth && j <= endMonth) { - data.push(parseFloat(dataGroup[counter].average)); - counter++; - } - else { - data.push(0); - } - } + datasets.push({ + label: datasetLabel, + data: data, + backgroundColor: colorBG + }) - let datasetLabel = groupedChartData[Object.keys(groupedChartData)[i]][0]['group']; - let colorBG = colors[colorCounter]; + colorCounter++ + if (colorCounter === colors.length) colorCounter = 0 + } - if (compare && c === 0) { - datasetLabel = groupedChartData[Object.keys(groupedChartData)[i]][0]['group'].toString().concat(" - emp"); - colorBG = compareColors[colorCounter] - } + for (let i = 0; i < res.data[0].table.length; i++) { + clientInfoList.push({ + name: res.data[0].table[i].name, + country: res.data[0].table[i].country, + clientgrading: res.data[0].table[i].grading + }) + } - datasets.push({ - label: datasetLabel, - data: data, - backgroundColor: colorBG - }); + if (compare && c === 0) { + compareData = datasets + } else if (compare && c === 1) { + for (let d = 0; d < compareData.length; d++) { + datasets.push(compareData[d]) + } - colorCounter++; - if (colorCounter === colors.length) colorCounter = 0; - } + setChartData(datasets) + setChartLoading(false) + } else if (!compare) { + setChartData(datasets) + setChartLoading(false) + } - for (let i = 0; i < res.data[0].table.length; i++) { - clientInfoList.push({ - name: res.data[0].table[i].name, - country: res.data[0].table[i].country, - clientgrading: res.data[0].table[i].grading - }); - } + localStorage.setItem('dash_previous_chart_data', JSON.stringify(datasets)) - if (compare && c === 0) { - compareData = datasets; - } - else if (compare && c === 1) { - for (let d = 0; d < compareData.length; d++) { - datasets.push(compareData[d]); - } + setClientNameCountry(clientInfoList) + }) + .catch((error) => { + setChartLoading(false) - setChartData(datasets); - setChartLoading(false); - } - else if (!compare) { - setChartData(datasets); - setChartLoading(false); - } + handleNon2xxResponse(error) + }) + /* eslint no-loop-func: 0 */ + } + } - localStorage.setItem("dash_previous_chart_data", JSON.stringify(datasets)); + const initCriteria = async () => { + // init time frame selections + const yearList = [] + for (let i = earliestYear; i <= currentYear; i++) { + yearList.push(i) + } + setYearList(yearList) + setMonthList(months) - setClientNameCountry(clientInfoList); - }) - .catch((error) => { - setChartLoading(false); + countrySelectBox() - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - alert("You are not authorized to perform this action."); - } - else { - alert("Malfunction in the B&C Engine..."); - } - } - else if (error.request) { - alert("Could not reach b&C Engine..."); - } - }); - /*eslint no-loop-func: 0*/ - } - } + createEmployeeCriteria() + } - const initCriteria = async () => { - // init time frame selections - let yearList = []; - for (let i = earliestYear; i <= currentYear; i++) { - yearList.push(i); - } - setYearList(yearList); - setMonthList(months); + const findCriteriaErrors = () => { + const { name, startYear, startMonth, endYear, endMonth } = criteria + const newErrors = {} - countrySelectBox(); + // name errors + if (name.length === 0) { newErrors.name = t('error.Empty') } - createEmployeeCriteria(); - }; + // endYear errors + if (parseInt(endYear) < parseInt(startYear)) { newErrors.endYear = t('dashboard.criteria.EndYearExceedError') } - const findCriteriaErrors = () => { - const { name, startYear, startMonth, endYear, endMonth } = criteria; - let newErrors = {}; + // endMonth errors + if (parseInt(endMonth) < parseInt(startMonth) && parseInt(startYear) === parseInt(endYear)) { newErrors.endMonth = t('dashboard.criteria.EndMonthExceedError') } - // name errors - if (name.length === 0) - newErrors.name = t("error.Empty"); + if (parseInt(endMonth) > parseInt(currentMonth) && parseInt(endYear) === parseInt(currentYear.toString())) { newErrors.endMonth = t('dashboard.criteria.EndMonthExceedCurrentError') } - // endYear errors - if (parseInt(endYear) < parseInt(startYear)) - newErrors.endYear = t("dashboard.criteria.EndYearExceedError"); + // startMonth errors + if (startMonth > currentMonth && startYear === currentYear.toString()) { newErrors.startMonth = t('dashboard.criteria.StartMonthExceedCurrentError') } - // endMonth errors - if (parseInt(endMonth) < parseInt(startMonth) && parseInt(startYear) === parseInt(endYear)) - newErrors.endMonth = t("dashboard.criteria.EndMonthExceedError"); + return newErrors + } - if (parseInt(endMonth) > parseInt(currentMonth) && parseInt(endYear) === parseInt(currentYear.toString())) - newErrors.endMonth = t("dashboard.criteria.EndMonthExceedCurrentError"); + const handleSaveChartReport = async (event) => { + event.preventDefault() + event.stopPropagation() - // startMonth errors - if (startMonth > currentMonth && startYear === currentYear.toString()) - newErrors.startMonth = t("dashboard.criteria.StartMonthExceedCurrentError"); + const newErrors = findCriteriaErrors() - return newErrors; - }; + setErrors(newErrors) + if (Object.keys(newErrors).length !== 0) return - const handleSaveChartReport = async (event) => { - event.preventDefault(); - event.stopPropagation(); + const previouslyLoadedCriteriaStr = localStorage.getItem('dash_previous_criteria') + const previouslyLoadedCriteria = JSON.parse(previouslyLoadedCriteriaStr) + delete previouslyLoadedCriteria.name + const criteriaWithoutName = Object.assign({}, criteria) + delete criteriaWithoutName.name + if (!chartSaved && JSON.stringify(previouslyLoadedCriteria) !== JSON.stringify(criteriaWithoutName)) { + alert("Please load the chart by clicking on the 'Load Chart' button before saving it.") + return + } - const newErrors = findCriteriaErrors(); + setConfirmSaveActivated(true) + } + + const onSaveConfirmClick = async () => { + if (!chartLoading) { + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') + } + + const data = { + chartReport: { + name: criteria.name, + startDate: new Date(criteria.startYear, criteria.startMonth, 1), + endDate: new Date(criteria.endYear, criteria.endMonth, 1), + employee1Id: criteria.employee1.id, + employee1Name: criteria.employee1.name, + employee2Id: compareEmployeeChecked ? -1 : null, + employee2Name: compareEmployeeChecked ? 'All' : null, + countryId: criteria.country.id, + country: criteria.country.name, + clientType: criteria.clientType, + ageOfAccount: criteria.ageOfAccount, + accountType: criteria.accountType, + user_user_id: cookies.get('userId') + }, + chartReportData: chartData + } + + Axios.post(`${process.env.REACT_APP_API}/reports/chartReport`, data, { headers: header }) + .then((response) => { + if (response.status === 200 || response.status === 201) { + setChartSaved(true) + alert('Chart Report has been saved successfully!') + } + }) + .catch((error) => handleNon2xxResponse(error)) + setConfirmSaveActivated(false) + } + } - setErrors(newErrors); - if (Object.keys(newErrors).length !== 0) return; + const loadChartData = async () => { + if (!chartLoading) { + const newErrors = findCriteriaErrors() - const previouslyLoadedCriteriaStr = localStorage.getItem('dash_previous_criteria'); - let previouslyLoadedCriteria = JSON.parse(previouslyLoadedCriteriaStr); - delete previouslyLoadedCriteria['name']; - let criteriaWithoutName = Object.assign({}, criteria); - delete criteriaWithoutName['name']; - if (!chartSaved && JSON.stringify(previouslyLoadedCriteria) !== JSON.stringify(criteriaWithoutName)) { - alert("Please load the chart by clicking on the 'Load Chart' button before saving it.") - return; + if (Object.keys(newErrors).length !== 0) { + if (!(Object.keys(newErrors).length === 1 && Object.keys(newErrors)[0].toString() === 'name')) { + delete newErrors.name + setErrors(newErrors) + return } + } - setConfirmSaveActivated(true); + await chart(compareEmployeeChecked) } + } - const onSaveConfirmClick = async () => { - if (!chartLoading) { - - let header = { - 'authorization': "Bearer " + cookies.get("accessToken"), - } + // handle clicks to other pages when unsaved work on Chart Report + const handleNavClick = async (whereTo) => { + setPageToNavigateTo('/' + whereTo.split('/').at(-1)) + if (chartSaved) { + navigate('/' + whereTo.split('/').at(-1)) + } + setNavClicked(true) + } + + const handleNon2xxResponse = (error) => { + if (error.response) { + if (error.response.status === 403 || error.response.status === 401) { + alert(error.response.body) + } else { + alert('Malfunction in the B&C Engine...') + } + } else if (error.request) { + alert('Could not reach b&C Engine...') + } + } - let data = { - chartReport: { - name: criteria.name, - startDate: new Date(criteria.startYear, criteria.startMonth, 1), - endDate: new Date(criteria.endYear, criteria.endMonth, 1), - employee1Id: criteria.employee1.id, - employee1Name: criteria.employee1.name, - employee2Id: compareEmployeeChecked ? -1 : null, - employee2Name: compareEmployeeChecked ? "All" : null, - countryId: criteria.country.id, - country: criteria.country.name, - clientType: criteria.clientType, - ageOfAccount: criteria.ageOfAccount, - accountType: criteria.accountType, - user_user_id: cookies.get("userId") - }, - chartReportData: chartData - } + useEffect(() => { + if (cookies.get('accessToken') === undefined) { + navigate('/login') + } - Axios.post(`${process.env.REACT_APP_API}/reports/chartReport`, data, { headers: header }) - .then((response) => { - if (response.status === 200 || response.status === 201) { - setChartSaved(true); - alert("Chart Report has been saved successfully!"); + initCriteria() + chart() + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + return ( +
+ +
+
+
+
+

{t('dashboard.criteria.Title')}

+ + + setField('name', e.target.value)} + value={criteria.name} + isInvalid={!!errors.name} + /> + + {errors.name} + + + + + {t('dashboard.criteria.StartDateLabel')} + + + setField('startYear', e.target.value)} + value={criteria.startYear} + isInvalid={!!errors.startYear} + > + + {yearList.map((y, i) => { + return ( + + ) + })} + + + + + {errors.startYear} + + + + setField('startMonth', e.target.value)} + value={criteria.startMonth} + isInvalid={!!errors.startMonth} + > + + {monthList.map((m, i) => { + return () + })} + + + + + {errors.startMonth} + + + + + + + {t('dashboard.criteria.EndDateLabel')} + + + setField('endYear', e.target.value)} + value={criteria.endYear} + isInvalid={!!errors.endYear} + > + + {yearList.map((y, i) => { + return ( + + ) + })} + + + + {errors.endYear} + + + + setField('endMonth', e.target.value)} + value={criteria.endMonth} + isInvalid={!!errors.endMonth} + > + + {monthList.map((m, i) => { + return () + })} + + + + {errors.endMonth} + + + + + + + {t('dashboard.criteria.labels.Country')} + + + { + setField('country', { + id: e.target.value, + name: e.target.options[e.target.selectedIndex].text + }) + }} + > + + {countries.map(c => { + return ( + + ) + })} + + + + + + {t('dashboard.criteria.labels.Employee')} + + + + {criteria.employee1.name} + + : <>} + > + + { + setField('employee1', { + id: e.target.value, + name: e.target.options[e.target.selectedIndex].text + }) + }} + > + + + {employeeSelect.map(e => { + return ( + + ) + })} + + + + + {t('dashboard.criteria.Compare')} + } - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - navigate("/login"); - alert("You are not authorized to perform this action."); + > + + + + + + + + {t('dashboard.criteria.labels.ClientType')} + + { + setField('clientType', e.target.value) + }} + > + + + + + + + + + + {t('dashboard.criteria.labels.Age')} + + { + setField('ageOfAccount', e.target.value) + }} + > + + + + + + + + + + + + + + + + + + +
+
+
+
+ {chartData && + { - if (!chartLoading) { - const newErrors = findCriteriaErrors(); - - if (Object.keys(newErrors).length !== 0) { - if (!(Object.keys(newErrors).length === 1 && Object.keys(newErrors)[0].toString() === 'name')) { - delete newErrors['name']; - setErrors(newErrors); - return; - } - } - - await chart(compareEmployeeChecked); - } - }; - - - // handle clicks to other pages when unsaved work on Chart Report - const handleNavClick = async (whereTo) => { - setPageToNavigateTo("/" + whereTo.split("/").at(-1)); - if (chartSaved) { - navigate("/" + whereTo.split("/").at(-1)) - } - setNavClicked(true) - } - - useEffect(() => { - if (cookies.get("accessToken") === undefined) { - navigate("/login"); - } - - initCriteria(); - chart(); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - - return ( -
- -
-
-
-
-

{t('dashboard.criteria.Title')}

- - - setField('name', e.target.value)} - value={criteria.name} - isInvalid={!!errors.name} - /> - - {errors.name} - - - - - {t('dashboard.criteria.StartDateLabel')} - - - setField('startYear', e.target.value)} - value={criteria.startYear} - isInvalid={!!errors.startYear}> - - {yearList.map((y, i) => { - return ( - - ); - })} - - - - - {errors.startYear} - - - - setField('startMonth', e.target.value)} - value={criteria.startMonth} - isInvalid={!!errors.startMonth}> - - {monthList.map((m, i) => { - return (); - })} - - - - - {errors.startMonth} - - - - - - - {t('dashboard.criteria.EndDateLabel')} - - - setField('endYear', e.target.value)} - value={criteria.endYear} - isInvalid={!!errors.endYear}> - - {yearList.map((y, i) => { - return ( - - ); - })} - - - - {errors.endYear} - - - - setField('endMonth', e.target.value)} - value={criteria.endMonth} - isInvalid={!!errors.endMonth}> - - {monthList.map((m, i) => { - return (); - })} - - - - {errors.endMonth} - - - - - - - - {t('dashboard.criteria.labels.Country')} - - - { - setField('country', { - id: e.target.value, - name: e.target.options[e.target.selectedIndex].text - }); - }}> - - {countries.map(c => { - return ( - - ) - })} - - - - - - {t('dashboard.criteria.labels.Employee')} - - - - {criteria.employee1.name} - : <> - } > - - { - setField('employee1', { - id: e.target.value, - name: e.target.options[e.target.selectedIndex].text - }); - }}> - - - {employeeSelect.map(e => { - return ( - - ) - })} - - - - - {t('dashboard.criteria.Compare')} - - } > - - - - - - - - {t('dashboard.criteria.labels.ClientType')} - - { - setField('clientType', e.target.value); - }}> - - - - - - - - - - {t('dashboard.criteria.labels.Age')} - - { - setField('ageOfAccount', e.target.value); - }}> - - - - - - - - - - - - - - - - - - -
-
-
-
- {chartData && - - } -
-
-
-
- - - - - - - - - - - - - - - - - {clientNameCountry.map((client, index) => { - return ( - - - - - - - - - - ); - })} - -
{t('dashboard.table.Name')}{t('dashboard.table.Country')}{t('dashboard.table.AverageCollectionDays')}{t('dashboard.table.AmountOwed')}{t('dashboard.table.AmountDue')}{t('dashboard.table.ClientGrading')}{t('dashboard.table.CurrentStatus')}
{client.name}{client.country}000{client.clientgrading}Active
- - - - - {t('dashboard.dropdownButtonTable.Option1')} - {t('dashboard.dropdownButtonTable.Option2')} - {t('dashboard.dropdownButtonTable.Option3')} - - - - - -
-
-
-
- { onSaveConfirmClick() }} - onClose={() => { setConfirmSaveActivated(false) }} - /> - { navigate(pageToNavigateTo) }} - onClose={() => { setNavClicked(false) }} /> -
- ) + }} + />} +
+
+
+
+ + + + + + + + + + + + + + + + + {clientNameCountry.map((client, index) => { + return ( + + + + + + + + + + ) + })} + +
{t('dashboard.table.Name')}{t('dashboard.table.Country')}{t('dashboard.table.AverageCollectionDays')}{t('dashboard.table.AmountOwed')}{t('dashboard.table.AmountDue')}{t('dashboard.table.ClientGrading')}{t('dashboard.table.CurrentStatus')}
{client.name}{client.country}000{client.clientgrading}Active
+ + + + + {t('dashboard.dropdownButtonTable.Option1')} + {t('dashboard.dropdownButtonTable.Option2')} + {t('dashboard.dropdownButtonTable.Option3')} + + + + + +
+
+
+
+ { onSaveConfirmClick() }} + onClose={() => { setConfirmSaveActivated(false) }} + /> + { navigate(pageToNavigateTo) }} + onClose={() => { setNavClicked(false) }} + /> +
+ ) } -export default Dashboard \ No newline at end of file +export default Dashboard diff --git a/client/src/pages/Login.js b/client/src/pages/Login.js index 71c6f78..893d162 100644 --- a/client/src/pages/Login.js +++ b/client/src/pages/Login.js @@ -2,204 +2,201 @@ import { useState, useEffect } from 'react' import { useNavigate } from 'react-router-dom' import Axios from 'axios' import Cookies from 'universal-cookie' -import { useTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next' import Icon from '@mdi/react' -import { mdiEye, mdiEyeOff } from '@mdi/js'; +import { mdiEye, mdiEyeOff } from '@mdi/js' import NavB from '../components/NavB' import Form from 'react-bootstrap/Form' import { Alert, Button, FloatingLabel } from 'react-bootstrap' const Login = () => { - const cookies = new Cookies(); - const { t } = useTranslation(); - let navigate = useNavigate(); - - const emptyError = t('error.Empty'); - const incorrectError = t('error.Incorrect'); - const notFoundError = t('error.NotFound'); - - const [showPass, setShowPass] = useState(false); - const [validated, setValidated] = useState(false); - const [validationError, setValidationError] = useState(false); - - const [InvalidCredential, setInvalidCredential] = useState(""); - - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - - const [errorMessage, setErrorMessage] = useState({ - email: emptyError, - password: emptyError, - }); - - Axios.defaults.withCredentials = true; - - const handleSubmit = (event) => { - const form = event.currentTarget; - if (form.checkValidity() === false) { - event.preventDefault(); - event.stopPropagation(); - setValidated(true); - } - else { - setInvalidCredential(""); - event.preventDefault(); - - let data = { - email: email, - password: password, + const cookies = new Cookies() + const { t } = useTranslation() + const navigate = useNavigate() + + const emptyError = t('error.Empty') + const incorrectError = t('error.Incorrect') + const notFoundError = t('error.NotFound') + + const [showPass, setShowPass] = useState(false) + const [validated, setValidated] = useState(false) + const [validationError, setValidationError] = useState(false) + + const [InvalidCredential, setInvalidCredential] = useState('') + + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + + const [errorMessage, setErrorMessage] = useState({ + email: emptyError, + password: emptyError + }) + + Axios.defaults.withCredentials = true + + const handleSubmit = (event) => { + const form = event.currentTarget + if (form.checkValidity() === false) { + event.preventDefault() + event.stopPropagation() + setValidated(true) + } else { + setInvalidCredential('') + event.preventDefault() + + const data = { + email: email, + password: password + } + + Axios.post(`${process.env.REACT_APP_API}/users/authenticate`, data) + .then((response) => { + if (response.data.auth === true) { + const aToken = response.data.aToken.toString() + const rToken = response.headers.authorization.toString() + const username = response.data.authenticatedUser.name.toString() + const role = response.data.authenticatedUser.role.toString() + const userId = response.data.authenticatedUser.userId.toString() + + cookies.set('accessToken', aToken, { path: '/', expires: new Date(new Date().getTime() + 15 * 60 * 1000) }) + cookies.set('refreshToken', rToken, { path: '/' }) + cookies.set('username', username, { path: '/' }) + cookies.set('role', role, { path: '/' }) + cookies.set('userId', userId, { path: '/' }) + + navigate('/dashboard') + } else { + setInvalidCredential(incorrectError) + setErrorMessage({ + email: emptyError, + password: emptyError + }) + } + }).catch((error) => { + if (error.response) { + if (error.response.status === 403 || error.response.status === 401) { + setInvalidCredential(incorrectError) + setErrorMessage({ + email: emptyError, + password: emptyError + }) + } else { + setInvalidCredential(incorrectError) } + } else if (error.request) { + // The request was made but no response was received + // `error.request` is an instance of XMLHttpRequest in the browser and an instance of + // http.ClientRequest in node.js + setInvalidCredential(notFoundError) + } + }) + } - Axios.post(`${process.env.REACT_APP_API}/users/authenticate`, data) - .then((response) => { - if (response.data.auth === true) { - - let aToken = response.data.aToken.toString(); - let rToken = response.headers['authorization'].toString(); - let username = response.data.authenticatedUser.name.toString(); - let role = response.data.authenticatedUser.role.toString(); - let userId = response.data.authenticatedUser.userId.toString(); - - cookies.set("accessToken", aToken, { path: "/", expires: new Date(new Date().getTime() + 15 * 60 * 1000) }); - cookies.set("refreshToken", rToken, { path: "/" }); - cookies.set("username", username, { path: "/" }); - cookies.set("role", role, { path: "/" }); - cookies.set("userId", userId, { path: "/" }); - - navigate("/dashboard"); - } - else { - setInvalidCredential(incorrectError); - setErrorMessage({ - email: emptyError, - password: emptyError - }); - } - }).catch((error) => { - - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - setInvalidCredential(incorrectError); - setErrorMessage({ - email: emptyError, - password: emptyError - }); - } - else { - setInvalidCredential(incorrectError); - } - } - else if (error.request) { - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - setInvalidCredential(notFoundError); - } - }); - } - - return false; - }; - - const showHide = () => { - if (showPass) setShowPass(false); - else setShowPass(true); + return false + } + + const showHide = () => { + if (showPass) setShowPass(false) + else setShowPass(true) + } + + useEffect(() => { + if (InvalidCredential !== '' && !validationError) { + setValidationError(true) + setValidated(false) + setErrorMessage({ + email: '', + password: '' + }) + } else if (validationError) { + setValidated(true) + setValidationError(false) } - useEffect(() => { - if (InvalidCredential !== "" && !validationError) { - setValidationError(true) - setValidated(false) - setErrorMessage({ - email: "", - password: "", - }) - } - else if (validationError) { - setValidated(true); - setValidationError(false); - } - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [InvalidCredential]); - - return ( -
- -
-
-

{t('login.Title')}

- -
- - { - InvalidCredential.length > 0 ? - - {InvalidCredential} - : - <> - } - - - - setEmail(e.target.value)} - /> - - - {errorMessage.email} - - - - - - - setPassword(e.target.value)} - /> - - - - - {errorMessage.password} - - - - -
- -
- -
-
-
-
- ) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [InvalidCredential]) + + return ( +
+ +
+
+

{t('login.Title')}

+ +
+ + { + InvalidCredential.length > 0 + ? + + {InvalidCredential} + + : <> + } + + + + setEmail(e.target.value)} + /> + + + {errorMessage.email} + + + + + + + setPassword(e.target.value)} + /> + + + + + {errorMessage.password} + + + + +
+ +
+ +
+
+
+
+ ) } export default Login diff --git a/client/src/pages/Manage.js b/client/src/pages/Manage.js index 2a8531a..ac7d24b 100644 --- a/client/src/pages/Manage.js +++ b/client/src/pages/Manage.js @@ -5,25 +5,24 @@ import NavB from '../components/NavB' import UnderConstruction from '../components/UnderConstruction' const Manage = () => { - let navigate = useNavigate(); - const cookies = new Cookies(); + const navigate = useNavigate() + const cookies = new Cookies() - useEffect(() => { - if (cookies.get("accessToken") === undefined) { - navigate("/login"); - } - else if (cookies.get("role") !== "admin") { - navigate("/dashboard"); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + useEffect(() => { + if (cookies.get('accessToken') === undefined) { + navigate('/login') + } else if (cookies.get('role') !== 'admin') { + navigate('/dashboard') + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) - return ( -
- - -
- ) + return ( +
+ + +
+ ) } export default Manage diff --git a/client/src/pages/Reports.js b/client/src/pages/Reports.js index 444c520..caa96fc 100644 --- a/client/src/pages/Reports.js +++ b/client/src/pages/Reports.js @@ -1,8 +1,8 @@ import React, { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import Cookies from 'universal-cookie' -import { useTranslation } from 'react-i18next'; -import Axios from 'axios'; +import { useTranslation } from 'react-i18next' +import Axios from 'axios' import { Button, Table, Form, ListGroup, ListGroupItem } from 'react-bootstrap' import { Oval } from 'react-loader-spinner' @@ -14,444 +14,431 @@ import ConfirmationPopup from '../components/ConfirmationPopup' import '../styles/reportsPage.css' const Reports = () => { - const { t } = useTranslation(); - let navigate = useNavigate(); - - const cookies = new Cookies(); - const malfunctionError = t('error.Malfunction'); - const notFoundError = t('error.NotFound'); - - let role = cookies.get("role"); - - const [pdfLoading, setPdfLoading] = useState(false); - const [currentPdfLoading, setCurrentPdfLoading] = useState(); - - const [chartReportId, setChartReportId] = useState(""); - const [chartReportName, setChartReportName] = useState(""); - const [deleteButtonActivated, setDeleteButtonActivated] = useState(false); - - const [chartReports, setChartReports] = useState([{ - chartReportId: "", - name: "", - startDate: new Date(), - endDate: new Date(), - employee1: "", - employee2: "", - country: "", - clientType: "", - ageOfAccount: "", - accountType: "" - }]); - - const [performanceReport, setPerformanceReport] = useState([{ - performanceReportId: "", - employeeId: 0, - averageCollectionDay: "", - annualBillingObjective: "", - monthlyBillingObjective: "", - annualBillingNumber: "", - monthlyBillingNumber: "", - projectedBonus: "" - }]); - - const [reportTypes, setReportTypes] = useState([]); - const [selectedReportType, setSelectedReportType] = useState({}); - const [showReportsManagement, setShowReportsManagement] = useState({ - isAdmin: false, - employee: "container-reportsTable-employee", - admin: "container-reportsTable" - }); - - const createAndDownloadPDF = (ReportId) => { - setPdfLoading(true); - setCurrentPdfLoading(ReportId); - - let header = { - 'authorization': "Bearer " + cookies.get("accessToken"), - } + const { t } = useTranslation() + const navigate = useNavigate() + + const cookies = new Cookies() + const malfunctionError = t('error.Malfunction') + const notFoundError = t('error.NotFound') + + const role = cookies.get('role') + + const [pdfLoading, setPdfLoading] = useState(false) + const [currentPdfLoading, setCurrentPdfLoading] = useState() + + const [chartReportId, setChartReportId] = useState('') + const [chartReportName, setChartReportName] = useState('') + const [deleteButtonActivated, setDeleteButtonActivated] = useState(false) + + const [chartReports, setChartReports] = useState([{ + chartReportId: '', + name: '', + startDate: new Date(), + endDate: new Date(), + employee1: '', + employee2: '', + country: '', + clientType: '', + ageOfAccount: '', + accountType: '' + }]) + + const [performanceReport, setPerformanceReport] = useState([{ + performanceReportId: '', + employeeId: 0, + averageCollectionDay: '', + annualBillingObjective: '', + monthlyBillingObjective: '', + annualBillingNumber: '', + monthlyBillingNumber: '', + projectedBonus: '' + }]) + + const [reportTypes, setReportTypes] = useState([]) + const [selectedReportType, setSelectedReportType] = useState({}) + const [showReportsManagement, setShowReportsManagement] = useState({ + isAdmin: false, + employee: 'container-reports-table-employee', + admin: 'container-reports-table' + }) + + const createAndDownloadPDF = (ReportId) => { + setPdfLoading(true) + setCurrentPdfLoading(ReportId) + + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') + } - let param = { - reportid: ReportId - } + const param = { + reportid: ReportId + } - Axios.defaults.withCredentials = true; - - Axios.post(`${process.env.REACT_APP_API}/reports/createPdf`, param, { headers: header }) - .then((response) => { - if(response.data === true) { - Axios.get(`${process.env.REACT_APP_API}/reports/fetchPdf`, { params: param, headers: header, responseType: 'arraybuffer' }) - .then((res) => { - const pdfBlob = new Blob([res.data], { type: 'application/pdf' }); - - const url = URL.createObjectURL(pdfBlob); - - var element = document.createElement('a'); - document.body.appendChild(element); - element.style = "display: none"; - element.href = url; - element.download = `ChartReport-${ReportId}`; - element.click(); - document.body.removeChild(element); - setPdfLoading(false); - }) - .catch((error) => { - setPdfLoading(false); - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - alert("You are not authorized to perform this action."); - } - else { - alert("Could not fetch pdf file..."); - } - } - else if (error.request) { - alert("Could not fetch pdf file..."); - } - }); - } - else { - alert("Could not fetch pdf file..."); - } + Axios.defaults.withCredentials = true + + Axios.post(`${process.env.REACT_APP_API}/reports/createPdf`, param, { headers: header }) + .then((response) => { + if (response.data === true) { + Axios.get(`${process.env.REACT_APP_API}/reports/fetchPdf`, { params: param, headers: header, responseType: 'arraybuffer' }) + .then((res) => { + const pdfBlob = new Blob([res.data], { type: 'application/pdf' }) + + const url = URL.createObjectURL(pdfBlob) + + const element = document.createElement('a') + document.body.appendChild(element) + element.style = 'display: none' + element.href = url + element.download = `ChartReport-${ReportId}` + element.click() + document.body.removeChild(element) + setPdfLoading(false) }) .catch((error) => { - setPdfLoading(false); - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - alert("You are not authorized to perform this action."); - } - else { - alert("Could not generate pdf file..."); - } + setPdfLoading(false) + if (error.response) { + if (error.response.status === 403 || error.response.status === 401) { + alert('You are not authorized to perform this action.') + } else { + alert('Could not fetch pdf file...') } - else if (error.request) { - alert("Could not fetch Chart Report..."); - } - }); + } else if (error.request) { + alert('Could not fetch pdf file...') + } + }) + } else { + alert('Could not fetch pdf file...') + } + }) + .catch((error) => { + setPdfLoading(false) + if (error.response) { + if (error.response.status === 403 || error.response.status === 401) { + alert('You are not authorized to perform this action.') + } else { + alert('Could not generate pdf file...') + } + } else if (error.request) { + alert('Could not fetch Chart Report...') + } + }) + } + + const handleRefresh = async () => { + await getChartReports() + if (cookies.get('role') === 'admin') { + setShowReportsManagement({ + ...showReportsManagement, + isAdmin: true + }) + await getReportTypesAndRecipients() } + await getPerformanceReportsByAdmin() + } - const handleRefresh = async () => { - await getChartReports(); - if (cookies.get("role") === 'admin') { - setShowReportsManagement({ - ...showReportsManagement, - isAdmin: true - }); - await getReportTypesAndRecipients(); - } - await getPerformanceReportsByAdmin(); + const getPerformanceReportsByAdmin = async () => { + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') } + Axios.defaults.withCredentials = true - const getPerformanceReportsByAdmin = async () => { - let header = { - 'authorization': "Bearer " + cookies.get("accessToken") + await Axios.get(`${process.env.REACT_APP_API}/reports/performanceReport`, { headers: header }) + .then((response) => { + if (response.data) { + setPerformanceReport(response.data) + return } + alert('The response from the B&C Engine was invalid.') + }) + .catch((error) => { + if (error.response) { + if (error.response.status === 403 || error.response.status === 401) { + alert('You are not authorized to perform this action.') + } else { + alert('Malfunction in the B&C Engine.') + } + } else if (error.request) { + alert('Could not reach b&C Engine...') + } + }) + } - Axios.defaults.withCredentials = true; - - await Axios.get(`${process.env.REACT_APP_API}/reports/performanceReport`, { headers: header }) - .then((response) => { - if (response.data) { - setPerformanceReport(response.data); - return; - } - alert("The response from the B&C Engine was invalid."); - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - alert("You are not authorized to perform this action."); - } - else { - alert("Malfunction in the B&C Engine."); - } - } - else if (error.request) { - alert("Could not reach b&C Engine..."); - } - }); + const getReportTypesAndRecipients = async () => { + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') } + Axios.defaults.withCredentials = true - const getReportTypesAndRecipients = async () => { - let header = { - 'authorization': "Bearer " + cookies.get("accessToken") + await Axios.get(`${process.env.REACT_APP_API}/reports/reportTypes`, { headers: header }) + .then((response) => { + if (response.data) { + setReportTypes(response.data) + setSelectedReportType(response.data[0]) + return } + alert('The response from the B&C Engine was invalid.') + }) + .catch((error) => { + if (error.response) { + if (error.response.status === 403 || error.response.status === 401) { + alert('You are not authorized to perform this action.') + } else { + alert('Malfunction in the B&C Engine.') + } + } else if (error.request) { + alert('Could not reach b&C Engine...') + } + }) + } - Axios.defaults.withCredentials = true; - - await Axios.get(`${process.env.REACT_APP_API}/reports/reportTypes`, { headers: header }) - .then((response) => { - if (response.data) { - setReportTypes(response.data); - setSelectedReportType(response.data[0]); - return; - } - alert("The response from the B&C Engine was invalid."); - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - alert("You are not authorized to perform this action."); - } - else { - alert("Malfunction in the B&C Engine."); - } - } - else if (error.request) { - alert("Could not reach b&C Engine..."); - } - }); + const getChartReports = async () => { + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') } - const getChartReports = async () => { - let header = { - 'authorization': "Bearer " + cookies.get("accessToken") - } - - Axios.defaults.withCredentials = true; - await Axios.get(`${process.env.REACT_APP_API}/reports/chartReport`, { headers: header }) - .then((response) => { - setChartReports(response.data); - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - alert("You are not authorized to perform this action."); - } - else { - alert("Malfunction in the B&C Engine."); - } - } - else if (error.request) { - alert("Could not reach b&C Engine..."); - } - }); - }; + Axios.defaults.withCredentials = true + await Axios.get(`${process.env.REACT_APP_API}/reports/chartReport`, { headers: header }) + .then((response) => { + setChartReports(response.data) + }) + .catch((error) => handleNon2xxResponse(error)) + } + + const handleDeleteChartReport = (id, chartReportName) => { + setChartReportId(id) + setChartReportName(chartReportName) + setDeleteButtonActivated(true) + } + + const onDeleteClick = () => { + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') + } - const handleDeleteChartReport = (id, chartReportName) => { - setChartReportId(id); - setChartReportName(chartReportName); - setDeleteButtonActivated(true); + const data = { + chartReportId: chartReportId } - const onDeleteClick = () => { - let header = { - 'authorization': "Bearer " + cookies.get("accessToken") - } + Axios.defaults.withCredentials = true - let data = { - chartReportId: chartReportId + Axios.delete(`${process.env.REACT_APP_API}/reports/delete/${chartReportId}`, { headers: header, data: data }) + .then((response) => { + if (response.status === 200 || response.status === 204) { + handleRefresh() + alert(t('reports.delete.Confirmation')) } + }) + .catch((error) => handleNon2xxResponse(error)) + setDeleteButtonActivated(false) + } + + const handleNon2xxResponse = (error) => { + if (error.response) { + if (error.response.status === 401 || error.response.status === 403) { + alert(t('reports.delete.NotAuthorized')) + } else { + alert(malfunctionError) + } + } else if (error.request) { + alert(notFoundError) + } + } - Axios.defaults.withCredentials = true; - - - Axios.delete(`${process.env.REACT_APP_API}/reports/delete/${chartReportId}`, { headers: header, data: data }) - .then((response) => { - if (response.status === 200 || response.status === 204) { - handleRefresh(); - alert(t('reports.delete.Confirmation')); - } - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 401 || error.response.status === 403) { - alert(t('reports.delete.NotAuthorized')); - } - else { - alert(malfunctionError) - } - } - else if (error.request) { - alert(notFoundError); - } - }); - setDeleteButtonActivated(false); - }; - - - useEffect(() => { - if (cookies.get("accessToken") === undefined) { - navigate("/login"); - } + useEffect(() => { + if (cookies.get('accessToken') === undefined) { + navigate('/login') + } - handleRefresh(); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( -
- -
-
-
-
-

{t('reports.reports.Title')}

- - - - - - - - - - - - - {performanceReport.map((p) => { - return ( - - - - - - - - - ) - })} - -
{t('reports.reports.AverageCollectionDay')}{t('reports.reports.AnnualBillingObjective')}{t('reports.reports.MonthlyBillingObjective')}{t('reports.reports.AnnualBillingNumber')}{t('reports.reports.MonthlyBillingNumber')}{t('reports.reports.ProjectedBonus')}
{p.averageCollectionDay}{p.annualBillingObjective}{p.monthlyBillingObjective}{p.annualBillingNumber}{p.monthlyBillingNumber}{p.projectedBonus}
-
-
- {role === "admin" ? -
-
-

{t('reports.reportsManagement.Title')}

- - {t('reports.reportsManagement.reportType.Title')} - - {reportTypes.map((t) => { - return ( - ) - })} - - - - {t('reports.reportsManagement.frequency.Title')} - - - - - - - {t('reports.reportsManagement.recipients.Title')} - - {selectedReportType.recipients ? Object.keys(selectedReportType.recipients).map((rId, i) => { - return ( - - - - ); - }) : <>} - - -
-
- : - <> - } - -
-
-

{t('reports.chartReports.Title')}

- - - - - - - - - - - - - - - - {chartReports.map(r => { - return ( - - - - - - - - - - - - ); - })} - -
{t('reports.chartReports.Name')}{t('reports.chartReports.Employee')}{t('reports.chartReports.ClientType')}{t('reports.chartReports.Country')}{t('reports.chartReports.AgeOfAccount')}{t('reports.chartReports.AccountType')}{t('reports.chartReports.StartDate')}{t('reports.chartReports.EndDate')} -
- -
-
{r.name}{r.employee1Name}{r.employee2Name === null ? "" : ", " + r.employee2Name}{r.clientType}{r.country}{r.ageOfAccount}{r.accountType}{r.startDate.toString()}{r.endDate.toString()} -
- {pdfLoading - ? - r.chartReportId !== currentPdfLoading - ? - - : - - - - - : - createAndDownloadPDF(r.chartReportId)} /> - } - handleDeleteChartReport(r.chartReportId, r.name)} /> -
-
-
-
-
-
- { onDeleteClick() }} - onClose={() => { setDeleteButtonActivated(false) }} /> + handleRefresh() + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + return ( +
+ +
+
+
+
+

{t('reports.reports.Title')}

+ + + + + + + + + + + + + {performanceReport.map((p) => { + return ( + + + + + + + + + ) + })} + +
{t('reports.reports.AverageCollectionDay')}{t('reports.reports.AnnualBillingObjective')}{t('reports.reports.MonthlyBillingObjective')}{t('reports.reports.AnnualBillingNumber')}{t('reports.reports.MonthlyBillingNumber')}{t('reports.reports.ProjectedBonus')}
{p.averageCollectionDay}{p.annualBillingObjective}{p.monthlyBillingObjective}{p.annualBillingNumber}{p.monthlyBillingNumber}{p.projectedBonus}
+
+
+ {role === 'admin' + ?
+
+

{t('reports.reportsManagement.Title')}

+ + {t('reports.reportsManagement.reportType.Title')} + + {reportTypes.map((t) => { + return ( + + ) + })} + + + + {t('reports.reportsManagement.frequency.Title')} + + + + + + + {t('reports.reportsManagement.recipients.Title')} + + {selectedReportType.recipients + ? Object.keys(selectedReportType.recipients).map((rId, i) => { + return ( + + + + ) + }) + : <>} + + +
+
+ : <>} + +
+
+

{t('reports.chartReports.Title')}

+ + + + + + + + + + + + + + + + {chartReports.map(r => { + return ( + + + + + + + + + + + + ) + })} + +
{t('reports.chartReports.Name')}{t('reports.chartReports.Employee')}{t('reports.chartReports.ClientType')}{t('reports.chartReports.Country')}{t('reports.chartReports.AgeOfAccount')}{t('reports.chartReports.AccountType')}{t('reports.chartReports.StartDate')}{t('reports.chartReports.EndDate')} +
+ +
+
{r.name}{r.employee1Name}{r.employee2Name === null ? '' : ', ' + r.employee2Name}{r.clientType}{r.country}{r.ageOfAccount}{r.accountType}{r.startDate.toString()}{r.endDate.toString()} +
+ {pdfLoading + ? r.chartReportId !== currentPdfLoading + ? + : + + + + : createAndDownloadPDF(r.chartReportId)} />} + handleDeleteChartReport(r.chartReportId, r.name)} /> +
+
+
+
- ) +
+ { onDeleteClick() }} + onClose={() => { setDeleteButtonActivated(false) }} + /> +
+ ) } export default Reports diff --git a/client/src/pages/Users.js b/client/src/pages/Users.js index b964952..a7324fe 100644 --- a/client/src/pages/Users.js +++ b/client/src/pages/Users.js @@ -1,8 +1,8 @@ import { useCallback, useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import Cookies from 'universal-cookie' -import { useTranslation } from 'react-i18next'; -import Axios from 'axios'; +import { useTranslation } from 'react-i18next' +import Axios from 'axios' import { Table, Button } from 'react-bootstrap' @@ -16,247 +16,243 @@ import UsersForm from '../components/UsersForm' import DeleteButton from '../components/DeleteButton' import EditButton from '../components/EditButton' - const Users = () => { - const { t } = useTranslation(); - let navigate = useNavigate(); - let counter = 0; - - const malfunctionError = t('error.Malfunction'); - const cookies = new Cookies(); - const displayNone = "d-none"; - - const [users, setUsers] = useState([{ name: "", email: "", role: "" }]); - const [email, setEmail] = useState(""); - const [deleteButtonActivated, setDeleteButtonActivated] = useState(false); - const [setInvalidInput] = useState(""); - - const [formEnabled, setFormEnabled] = useState({ - table: "container", - form: displayNone - }); - - // Default values sent to the UsersForm.js on the edit form - const [editValues, setEditValues] = useState({ - email: "", - role: "" - }); - - // Calls functions in the UsersForm.js - const [isUpdate, setIsUpdate] = useState(true); - const [isAdd, setIsAdd] = useState(true); - const [isEdit, setIsEdit] = useState(true); - - // Fixes issue where callback needed its call variable to be defined inside of the function - const [setTemp] = useState(true); - - const handleDisableForm = useCallback(() => { - setTemp(isUpdate); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isUpdate]); - const handleAddUser = useCallback(() => { - setTemp(isAdd); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isAdd]); - const handleEditUser = useCallback(() => { - setTemp(isEdit); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isEdit]); - - const addUser = () => { setIsAdd(!isAdd) } - - const editUser = (email, role) => { - setEditValues({ - email: email, - role: role - }); - - // Triggers function call in the UsersForm.js - setIsEdit(!isEdit); + const { t } = useTranslation() + const navigate = useNavigate() + let counter = 0 + + const malfunctionError = t('error.Malfunction') + const cookies = new Cookies() + const displayNone = 'd-none' + + const [users, setUsers] = useState([{ name: '', email: '', role: '' }]) + const [email, setEmail] = useState('') + const [deleteButtonActivated, setDeleteButtonActivated] = useState(false) + const [setInvalidInput] = useState('') + + const [formEnabled, setFormEnabled] = useState({ + table: 'container', + form: displayNone + }) + + // Default values sent to the UsersForm.js on the edit form + const [editValues, setEditValues] = useState({ + email: '', + role: '' + }) + + // Calls functions in the UsersForm.js + const [isUpdate, setIsUpdate] = useState(true) + const [isAdd, setIsAdd] = useState(true) + const [isEdit, setIsEdit] = useState(true) + + // Fixes issue where callback needed its call variable to be defined inside of the function + const [setTemp] = useState(true) + + const handleDisableForm = useCallback(() => { + setTemp(isUpdate) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isUpdate]) + const handleAddUser = useCallback(() => { + setTemp(isAdd) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isAdd]) + const handleEditUser = useCallback(() => { + setTemp(isEdit) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isEdit]) + + const addUser = () => { setIsAdd(!isAdd) } + + const editUser = (email, role) => { + setEditValues({ + email: email, + role: role + }) + + // Triggers function call in the UsersForm.js + setIsEdit(!isEdit) + } + + const enableForm = () => { + setFormEnabled({ + table: 'container-form-enabled-table', + form: 'container-form-enabled-form' + }) + } + + const disableForm = () => { + handleRefresh() + setFormEnabled({ + table: 'container', + form: 'd-none' + }) + + // Triggers function call in the UsersForm.js + setIsUpdate(!isUpdate) + } + + const handleDeleteUser = (email) => { + disableForm() + setEmail(email) + setDeleteButtonActivated(true) + } + + const onDeleteClick = () => { + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') } - const enableForm = () => { - setFormEnabled({ - table: "container-form-enabled-table", - form: "container-form-enabled-form" - }); + const data = { + email: email } - const disableForm = () => { - handleRefresh(); - setFormEnabled({ - table: "container", - form: "d-none" - }); - - // Triggers function call in the UsersForm.js - setIsUpdate(!isUpdate); - } + Axios.defaults.withCredentials = true - const handleDeleteUser = (email) => { - disableForm(); - setEmail(email); - setDeleteButtonActivated(true); - } - - const onDeleteClick = () => { - let header = { - 'authorization': "Bearer " + cookies.get("accessToken") + Axios.delete(`${process.env.REACT_APP_API}/users/delete/${email}`, { headers: header, data: data }) + .then((response) => { + if (response.data === true) { + console.log('User deleted successfully!') } - let data = { - email: email + setDeleteButtonActivated(false) + handleRefresh() + }) + .catch((error) => { + if (error.response) { + if (error.response.status === 401 || error.response.status === 403) { + setInvalidInput(t('user.delete')) + } else { + setInvalidInput(malfunctionError) + } + } else if (error.request) { + setInvalidInput(malfunctionError) } + }) - Axios.defaults.withCredentials = true; - - Axios.delete(`${process.env.REACT_APP_API}/users/delete/${email}`, { headers: header, data: data }) - .then((response) => { - if (response.data === true) { - console.log("User deleted successfully!"); - } - - setDeleteButtonActivated(false); - handleRefresh(); - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 401 || error.response.status === 403) { - setInvalidInput(t('user.delete')); - } - else { - setInvalidInput(malfunctionError); - } - } - else if (error.request) { - setInvalidInput(malfunctionError); - } - }); - - return false; - }; - - const handleRefresh = () => { - let header = { - 'authorization': "Bearer " + cookies.get("accessToken"), - } + return false + } - Axios.defaults.withCredentials = true; - - Axios.get(`${process.env.REACT_APP_API}/users/`, { headers: header }) - .then((response) => { - setUsers(response.data); - }) - .catch((error) => { - if (error.response) { - if (error.response.status === 403 || error.response.status === 401) { - console.log("You are not authorized to perform this action."); - } - else { - console.log("Could not reach b&C Engine..."); - } - } - else if (error.request) { - console.log("Could not reach b&C Engine..."); - } - }); + const handleRefresh = () => { + const header = { + authorization: 'Bearer ' + cookies.get('accessToken') } - useEffect(() => { - if (cookies.get("accessToken") === undefined) { - navigate("/login"); - } - else if (cookies.get("role") !== "admin") { - navigate("/dashboard"); + Axios.defaults.withCredentials = true + + Axios.get(`${process.env.REACT_APP_API}/users/`, { headers: header }) + .then((response) => { + setUsers(response.data) + }) + .catch((error) => { + if (error.response) { + if (error.response.status === 403 || error.response.status === 401) { + console.log('You are not authorized to perform this action.') + } else { + console.log('Could not reach b&C Engine...') + } + } else if (error.request) { + console.log('Could not reach b&C Engine...') } + }) + } + + useEffect(() => { + if (cookies.get('accessToken') === undefined) { + navigate('/login') + } else if (cookies.get('role') !== 'admin') { + navigate('/dashboard') + } + + handleRefresh() + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) - handleRefresh(); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( -
- -
-
-
- - - - - - - - - - - - - - - {users.map(u => { - counter++; - return ( - - - - - - - - - - - ); - })} - -
-
- # -
-
{t('user.table.Name')}{t('user.table.Email')}{t('user.table.Role')} -
- -
-
-
- {counter} -
-
{u.name}{u.email}{u.role} -
- editUser(u.email, u.role)} /> - handleDeleteUser(u.email)} /> -
-
+ return ( +
+ +
+
+
+ + + + + + + + + + + + + + + {users.map(u => { + counter++ + return ( + + + + + + + + + + + ) + })} + +
+
+ #
- - -
-
- +
{t('user.table.Name')}{t('user.table.Email')}{t('user.table.Role')} +
+
- - - { onDeleteClick() }} - onClose={() => { setDeleteButtonActivated(false) }} /> +
+
+ {counter} +
+
{u.name}{u.email}{u.role} +
+ editUser(u.email, u.role)} /> + handleDeleteUser(u.email)} /> +
+
+
+
+ +
+
+ +
- ) +
+ { onDeleteClick() }} + onClose={() => { setDeleteButtonActivated(false) }} + /> +
+ ) } -export default Users \ No newline at end of file +export default Users diff --git a/client/src/reportWebVitals.js b/client/src/reportWebVitals.js index 7d7417e..9381231 100644 --- a/client/src/reportWebVitals.js +++ b/client/src/reportWebVitals.js @@ -1,13 +1,13 @@ const reportWebVitals = onPerfEntry => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } - }; - - export default reportWebVitals; \ No newline at end of file + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry) + getFID(onPerfEntry) + getFCP(onPerfEntry) + getLCP(onPerfEntry) + getTTFB(onPerfEntry) + }) + } +} + +export default reportWebVitals diff --git a/client/src/setupProxy.js b/client/src/setupProxy.js index 6754021..7bff128 100644 --- a/client/src/setupProxy.js +++ b/client/src/setupProxy.js @@ -1,10 +1,10 @@ -const { createProxyMiddleware } = require('http-proxy-middleware'); +const { createProxyMiddleware } = require('http-proxy-middleware') module.exports = function (app) { - app.use( - createProxyMiddleware('/api', { - target: 'http://localhost:3001', - changeOrigin: true, - }) - ); -}; \ No newline at end of file + app.use( + createProxyMiddleware('/api', { + target: 'http://localhost:3001', + changeOrigin: true + }) + ) +} diff --git a/client/src/styles/clientTable.css b/client/src/styles/clientTable.css index 638ab4f..80a70df 100644 --- a/client/src/styles/clientTable.css +++ b/client/src/styles/clientTable.css @@ -1,56 +1,48 @@ .row-style { - text-align: center; + text-align: center; } -.amount-owed{ - text-align: center; - color: #df0404; +.amount-owed { + text-align: center; + color: #df0404; } -.amount-due{ - text-align: center; - color: #05a105; +.amount-due { + text-align: center; + color: #05a105; } -.client-swap{ - padding: 5px 10px 3px 3px; - width: 100%; - background-color: #fff; - text-align: center; - display: flex; - justify-content: center; +.client-swap { + padding: 5px 10px 3px 3px; + width: 100%; + background-color: #fff; + text-align: center; + display: flex; + justify-content: center; } -.rowsViewerSelectionStyle{ - background-color: white; - border: none; - font-size: 20px; - padding: 1em; - cursor: pointer; +.client-swap .left-button { + background-color: white; + border: none; + color: black; + font-size: 20px; + padding: 1em; + cursor: pointer; } -.client-swap .left-button{ - background-color: white; - border: none; - color: black; - font-size: 20px; - padding: 1em; - cursor: pointer; -} - -.client-swap .right-button{ - background-color: white; - border: none; - color: black; - font-size: 20px; - padding: 1em; - cursor: pointer; +.client-swap .right-button { + background-color: white; + border: none; + color: black; + font-size: 20px; + padding: 1em; + cursor: pointer; } .left-button:hover { - background-color: rgba(0, 0, 0, 0.25); + background-color: rgb(0 0 0 / 25%); } .right-button:hover { - background-color: rgba(0, 0, 0, 0.25); + background-color: rgb(0 0 0 / 25%); } \ No newline at end of file diff --git a/client/src/styles/dashboardPage.css b/client/src/styles/dashboardPage.css index 5fb6850..e33e2a1 100644 --- a/client/src/styles/dashboardPage.css +++ b/client/src/styles/dashboardPage.css @@ -26,7 +26,7 @@ justify-self: center; } -.mainDiv { +.main-div { justify-content: center; } @@ -34,9 +34,10 @@ .main { flex-direction: column; } - .container-chart, + + .container-chart, .container-criteria, - .container-table{ + .container-table { width: 100%; } } \ No newline at end of file diff --git a/client/src/styles/index.css b/client/src/styles/index.css index 21a6be4..e909241 100644 --- a/client/src/styles/index.css +++ b/client/src/styles/index.css @@ -6,21 +6,23 @@ html { max-width: 800px !important; } -.mainContainer { +.main-container { display: flex; } .card { border-radius: 1rem; border: 0; -} +} -.submitButton { +.submit-button { font-size: 20px; background-color: lightgrey; } -input, select, button{ +input, +select, +button { border-radius: 0.7rem !important; } @@ -32,75 +34,67 @@ input, select, button{ border-radius: 0.7rem !important; } -.inputSelect { +.input-select { border-radius: 0.7rem !important; - border-color: rgb(206, 212, 218); - max-width: min-content -} - -.inputSelect:hover { - background-color: rgb(228, 228, 228); + border-color: rgb(206 212 218); + max-width: min-content; } -.navLogin { - text-align: center; +.input-select:hover { + background-color: rgb(228 228 228); } -.navContainer { +.nav-container { position: relative; } -.navBrandLogin { +.nav-brand-login { float: none; position: absolute; top: 50%; left: 50%; - transform: translate(-50%, -50%) + transform: translate(-50%, -50%); } -.passwordValidationField.form-control.is-invalid, -.passwordValidationField.was-validated, -.passwordValidationField.form-control:invalid { - padding-right: 4.125rem!important; - background-position: center right 2.25rem!important; +.password-validation-field.form-control.is-invalid, +.password-validation-field.was-validated, +.password-validation-field.form-control:invalid { + padding-right: 4.125rem !important; + background-position: center right 2.25rem !important; } @media (max-width: 768px) { - .navBarGreeting { - display: none; - } - - #loginForm { + #login-form { margin-top: 2rem !important; margin-right: 2rem !important; margin-left: 2rem !important; } #login-card { - margin: 2rem!important; + margin: 2rem !important; } } @media (max-width: 576px) { - #loginForm { + #login-form { margin-top: 1.5rem !important; margin-right: 1rem !important; margin-left: 1rem !important; } #login-card { - margin: 1rem!important; + margin: 1rem !important; } } @media (max-width: 482px) { - #loginForm { + #login-form { margin-top: 1rem !important; margin-right: 0 !important; margin-left: 0 !important; } #login-card { - margin: 0!important; + margin: 0 !important; } } \ No newline at end of file diff --git a/client/src/styles/popup.css b/client/src/styles/popup.css index b326bfd..ecfbfa9 100644 --- a/client/src/styles/popup.css +++ b/client/src/styles/popup.css @@ -1,97 +1,117 @@ .popup { - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: #fff; - padding: 50px; - z-index: 1000; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #fff; + padding: 50px; + z-index: 1000; } - - .popup-inner{ - padding: 32px; - width: 100%; - background-color: #fff; - text-align: center; - display: flex; - justify-content: center; + +.popup-inner { + padding: 32px; + width: 100%; + background-color: #fff; + text-align: center; + display: flex; + justify-content: center; } - - .popup-inner .deleteUserButton { - appearance: none; - background-color: #05a105; - color:#FFFFFF; - border-radius: 15px; - box-sizing: border-box; - cursor: pointer; - font-family: Roobert,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; - font-size: 22px; - font-weight: 600; - line-height: normal; - min-height: 60px; - padding: 16px 24px; - text-align: center; - transition: all 300ms cubic-bezier(.23, 1, 0.32, 1); - touch-action: manipulation; - width: 100%; - will-change: transform; + +.popup-inner .delete-user-button { + appearance: none; + background-color: #05a105; + color: #fff; + border-radius: 15px; + box-sizing: border-box; + cursor: pointer; + font-family: + Roobert, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Helvetica, + Arial, + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol"; + font-size: 22px; + font-weight: 600; + line-height: normal; + min-height: 60px; + padding: 16px 24px; + text-align: center; + transition: all 300ms cubic-bezier(0.23, 1, 0.32, 1); + touch-action: manipulation; + width: 100%; + will-change: transform; } - - .popup-inner .deleteUserButton:disabled { - pointer-events: none; + +.popup-inner .delete-user-button:disabled { + pointer-events: none; } - - .popup-inner .deleteUserButton:hover { - box-shadow: rgba(0, 0, 0, 0.25) 0 8px 15px; - transform: translateY(-2px); + +.popup-inner .delete-user-button:hover { + box-shadow: rgb(0 0 0 / 25%) 0 8px 15px; + transform: translateY(-2px); } - - .popup-inner .deleteUserButton:active { - box-shadow: none; - transform: translateY(0); + +.popup-inner .delete-user-button:active { + box-shadow: none; + transform: translateY(0); } - - .popup-inner .cancelDeleteUserButton { - appearance: none; - background-color: #df0404; - margin-right: 20px; - color:#FFFFFF; - border-radius: 15px; - box-sizing: border-box; - cursor: pointer; - font-family: Roobert,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; - font-size: 22px; - font-weight: 600; - line-height: normal; - min-height: 60px; - padding: 16px 24px; - text-align: center; - transition: all 300ms cubic-bezier(.23, 1, 0.32, 1); - touch-action: manipulation; - width: 100%; - will-change: transform; + +.popup-inner .cancel-delete-user-button { + appearance: none; + background-color: #df0404; + margin-right: 20px; + color: #fff; + border-radius: 15px; + box-sizing: border-box; + cursor: pointer; + font-family: + Roobert, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Helvetica, + Arial, + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol"; + font-size: 22px; + font-weight: 600; + line-height: normal; + min-height: 60px; + padding: 16px 24px; + text-align: center; + transition: all 300ms cubic-bezier(0.23, 1, 0.32, 1); + touch-action: manipulation; + width: 100%; + will-change: transform; } - - .popup-inner .cancelDeleteUserButton:disabled { - pointer-events: none; + +.popup-inner .cancel-delete-user-button:disabled { + pointer-events: none; } - - .popup-inner .cancelDeleteUserButton:hover { - box-shadow: rgba(0, 0, 0, 0.25) 0 8px 15px; - transform: translateY(-2px); + +.popup-inner .cancel-delete-user-button:hover { + box-shadow: rgb(0 0 0 / 25%) 0 8px 15px; + transform: translateY(-2px); } - - .popup-inner .cancelDeleteUserButton:active { - box-shadow: none; - transform: translateY(0); + +.popup-inner .cancel-delete-user-button:active { + box-shadow: none; + transform: translateY(0); } - - .obscureBackground { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.7); - z-index: 1000; + +.obscure-background { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgb(0 0 0 / 70%); + z-index: 1000; } \ No newline at end of file diff --git a/client/src/styles/reportsPage.css b/client/src/styles/reportsPage.css index d90b3b0..ca9fbf1 100644 --- a/client/src/styles/reportsPage.css +++ b/client/src/styles/reportsPage.css @@ -1,64 +1,57 @@ -.loadingChartReport { - height: 36px; - align-items: center; - display: flex; - margin: 0 4px; - padding: 0 6px; +.loading-chart-report { + height: 36px; + align-items: center; + display: flex; + margin: 0 4px; + padding: 0 6px; } -#reportTypesTable { - width: 100% +#report-types-table { + width: 100%; } -#reportRecipientList { - border-radius: 0.7rem; - max-height: 15rem; - overflow-y: scroll; - overflow-x: hidden; +#report-recipient-list { + border-radius: 0.7rem; + max-height: 15rem; + overflow-y: scroll; + overflow-x: hidden; } -.container-reportsPage{ - flex-flow: row wrap; - display: flex; - width: 100%; - justify-self: center; - max-width: 2100px; +.container-reports-management { + width: 40%; } -.container-reportsManagement { - width: 40%; +.container-reports-table-employee { + width: 100%; } -.container-reportsTable-employee { - width: 100%; +.container-reports-table { + width: 60%; + float: left; } -.container-reportsTable { - width: 60%; - float: left; -} - -.container-chartReports { - width: 100%; - float: left; +.container-chart-reports { + width: 100%; + float: left; } .performance-table-columns { - text-align: center; + text-align: center; } h4 { - margin-bottom: 0; - padding-bottom: 0.3rem; + margin-bottom: 0; + padding-bottom: 0.3rem; } @media all and (max-width: 1000px) { - .container-reportsPage { - flex-direction: column; - } - .container-reportsManagement, - .container-reportsTable, - .container-chartReports{ - width: 100%; - } + /* .container-reportsPage { + flex-direction: column; + } */ + + .container-reports-management, + .container-reports-table, + .container-chart-reports { + width: 100%; } +} \ No newline at end of file diff --git a/client/src/styles/scrollbar.css b/client/src/styles/scrollbar.css index 4fea193..969a0a5 100644 --- a/client/src/styles/scrollbar.css +++ b/client/src/styles/scrollbar.css @@ -1,20 +1,20 @@ /* width */ ::-webkit-scrollbar { - width: 10px; - } - - /* Track */ - ::-webkit-scrollbar-track { - background: lightgrey; - } - - /* Handle */ - ::-webkit-scrollbar-thumb { - background: #888; - border-radius: 20px; - } - - /* Handle on hover */ - ::-webkit-scrollbar-thumb:hover { - background: #555; - } \ No newline at end of file + width: 10px; +} + +/* Track */ +::-webkit-scrollbar-track { + background: lightgrey; +} + +/* Handle */ +::-webkit-scrollbar-thumb { + background: #888; + border-radius: 20px; +} + +/* Handle on hover */ +::-webkit-scrollbar-thumb:hover { + background: #555; +} \ No newline at end of file diff --git a/client/src/styles/usersPage.css b/client/src/styles/usersPage.css index e33bb01..ba03a7c 100644 --- a/client/src/styles/usersPage.css +++ b/client/src/styles/usersPage.css @@ -5,31 +5,33 @@ float: left; } -.uForm { +.user-form { height: 700px !important; } -.uTable { +.user-table { height: 700px !important; overflow-y: auto; } -.showHideBTN { +.show-hide-button { position: absolute; - - /* These are set relative to the height of the input box to bound the box neatly inside. This is aesthetic to me but you may change the dimensions of course. */ + + /* These are set relative to the height of the input box to bound the box neatly inside. + This is aesthetic to me but you may change the dimensions of course. */ right: 0.3rem; top: 1.15rem; color: gray; - - /* content in the icon div is centered, without bootstrap or font-awesome you may wish to add your own text in the span */ + + /* content in the icon div is centered, without bootstrap or + font-awesome you may wish to add your own text in the span */ display: flex; justify-content: center; align-items: center; box-sizing: border-box; } -.inputWithShowHide { +.input-with-show-hide { position: relative; box-sizing: border-box; } @@ -43,15 +45,15 @@ thead { } @media (max-width: 1000px) { - .mainContainer { + .main-container { display: block !important; } .container-form-enabled-form, .container-form-enabled-table { width: 100%; - padding-right: var(--bs-gutter-x,.75rem); - padding-left: var(--bs-gutter-x,.75rem); + padding-right: var(--bs-gutter-x, 0.75rem); + padding-left: var(--bs-gutter-x, 0.75rem); margin-right: auto; margin-left: auto; float: none; diff --git a/config.js b/config.js index b2e929b..0b72d4d 100644 --- a/config.js +++ b/config.js @@ -1,12 +1,12 @@ -// This file is used to configure the environment files +// This file is used to configure the environment files // Mainly for the local .env file located at the root level -const dotenv = require('dotenv'); -const result = dotenv.config({ path: require('find-config')('.env') }); +const dotenv = require('dotenv') +const result = dotenv.config({ path: require('find-config')('.env') }) if (result.error) { - throw result.error; + throw result.error } -const { parsed: envs } = result; -module.exports = envs; \ No newline at end of file +const { parsed: envs } = result +module.exports = envs diff --git a/package-lock.json b/package-lock.json index b19c879..43b9159 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,9 @@ "tedious": "^14.0.0" }, "devDependencies": { + "@babel/eslint-parser": "^7.17.0", "chai": "^4.3.4", + "eslint-plugin-react-hooks": "^4.3.0", "jest": "^27.4.3", "mock-require": "^3.0.3", "nodemon": "^2.0.15", @@ -527,6 +529,64 @@ "node": ">=0.10.0" } }, + "node_modules/@babel/eslint-parser": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.17.0.tgz", + "integrity": "sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==", + "dev": true, + "dependencies": { + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.11.0", + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/eslint-parser/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { "version": "7.16.8", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", @@ -1070,6 +1130,171 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@eslint/eslintrc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.1.0.tgz", + "integrity": "sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "peer": true + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "peer": true + }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", + "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", + "dev": true, + "peer": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "peer": true + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true, + "peer": true + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1632,6 +1857,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", @@ -1677,7 +1912,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "optional": true, + "devOptional": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2969,6 +3204,19 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/domexception": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", @@ -3219,6 +3467,307 @@ "source-map": "~0.6.1" } }, + "node_modules/eslint": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.9.0.tgz", + "integrity": "sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint/eslintrc": "^1.1.0", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz", + "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "peer": true + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "peer": true + }, + "node_modules/eslint/node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "peer": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "peer": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -3232,6 +3781,31 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", @@ -3435,7 +4009,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "optional": true + "devOptional": true }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", @@ -3473,6 +4047,19 @@ "pend": "~1.2.0" } }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "peer": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-keys": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", @@ -3539,6 +4126,27 @@ "node": ">=8" } }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "peer": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true, + "peer": true + }, "node_modules/follow-redirects": { "version": "1.14.7", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", @@ -3664,6 +4272,13 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true, + "peer": true + }, "node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -4201,12 +4816,49 @@ } ] }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "dev": true }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -5334,7 +5986,14 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "optional": true + "devOptional": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true, + "peer": true }, "node_modules/json-stringify-safe": { "version": "5.0.1", @@ -5573,6 +6232,13 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "peer": true + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -6555,6 +7221,19 @@ "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -7361,6 +8040,19 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, "node_modules/registry-auth-token": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", @@ -8448,6 +9140,13 @@ "node": ">=8" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true, + "peer": true + }, "node_modules/throat": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", @@ -8759,7 +9458,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "optional": true, + "devOptional": true, "dependencies": { "punycode": "^2.1.0" } @@ -8821,6 +9520,13 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true, + "peer": true + }, "node_modules/v8-to-istanbul": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", @@ -9571,12 +10277,53 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/eslint-parser": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.17.0.tgz", + "integrity": "sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==", + "dev": true, + "requires": { + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -9991,6 +10738,129 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@eslint/eslintrc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.1.0.tgz", + "integrity": "sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==", + "dev": true, + "peer": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "peer": true + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "dev": true, + "peer": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "peer": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "peer": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "peer": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "peer": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", + "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", + "dev": true, + "peer": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "peer": true + } + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true, + "peer": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -10470,6 +11340,14 @@ } } }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peer": true, + "requires": {} + }, "acorn-walk": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", @@ -10503,7 +11381,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "optional": true, + "devOptional": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -11504,6 +12382,16 @@ "integrity": "sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==", "dev": true }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "peer": true, + "requires": { + "esutils": "^2.0.2" + } + }, "domexception": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", @@ -11713,12 +12601,251 @@ "source-map": "~0.6.1" } }, + "eslint": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.9.0.tgz", + "integrity": "sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==", + "dev": true, + "peer": true, + "requires": { + "@eslint/eslintrc": "^1.1.0", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "peer": true + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "peer": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "dev": true, + "peer": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "peer": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "peer": true + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "peer": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "peer": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "peer": true + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz", + "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "peer": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "peer": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "peer": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "peer": true + }, + "espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "peer": true, + "requires": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "peer": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", @@ -11887,7 +13014,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "optional": true + "devOptional": true }, "fast-json-stable-stringify": { "version": "2.1.0", @@ -11925,6 +13052,16 @@ "pend": "~1.2.0" } }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "peer": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "fill-keys": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", @@ -11976,6 +13113,24 @@ "path-exists": "^4.0.0" } }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "peer": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true, + "peer": true + }, "follow-redirects": { "version": "1.14.7", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", @@ -12062,6 +13217,13 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true, + "peer": true + }, "gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -12452,12 +13614,39 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "peer": true + }, "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "dev": true }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "peer": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "peer": true + } + } + }, "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -13322,7 +14511,14 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "optional": true + "devOptional": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true, + "peer": true }, "json-stringify-safe": { "version": "5.0.1", @@ -13529,6 +14725,13 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "peer": true + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -14318,6 +15521,16 @@ "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "peer": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -14955,6 +16168,13 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "peer": true + }, "registry-auth-token": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", @@ -15754,6 +16974,13 @@ "minimatch": "^3.0.4" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true, + "peer": true + }, "throat": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", @@ -15997,7 +17224,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "optional": true, + "devOptional": true, "requires": { "punycode": "^2.1.0" } @@ -16049,6 +17276,13 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true, + "peer": true + }, "v8-to-istanbul": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", diff --git a/package.json b/package.json index a088c34..3dd08c9 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,9 @@ "tedious": "^14.0.0" }, "devDependencies": { + "@babel/eslint-parser": "^7.17.0", "chai": "^4.3.4", + "eslint-plugin-react-hooks": "^4.3.0", "jest": "^27.4.3", "mock-require": "^3.0.3", "nodemon": "^2.0.15", diff --git a/server/app.js b/server/app.js index e5b65cb..45059f6 100644 --- a/server/app.js +++ b/server/app.js @@ -1,52 +1,52 @@ -const path = require('path'); -const express = require("express"); -const bodyParser = require("body-parser"); -const logger = require('morgan'); -const userRoutes = require('./routes/user.routes'); -const invoiceRoutes = require('./routes/invoice.routes'); -const reportRoutes = require('./routes/report.routes'); -require("../config.js"); +const path = require('path') +const express = require('express') +const bodyParser = require('body-parser') +const logger = require('morgan') +const userRoutes = require('./routes/user.routes') +const invoiceRoutes = require('./routes/invoice.routes') +const reportRoutes = require('./routes/report.routes') +require('../config.js') module.exports = (database) => { - const app = express(); + const app = express() - app.use(express.static(path.resolve(__dirname, '../client/build'))); - app.use(bodyParser.json()); - app.use(bodyParser.urlencoded({ extended: true })); - app.use(logger('dev')); + app.use(express.static(path.resolve(__dirname, '../client/build'))) + app.use(bodyParser.json()) + app.use(bodyParser.urlencoded({ extended: true })) + app.use(logger('dev')) // Initializing Sequelize (ORM) to create users table and fill it if (database) { - database.sync('localdb'); + database.sync('localdb') } app.use(function (req, res, next) { - res.header('Access-Control-Allow-Origin', "http://localhost:3000"); - res.header('Access-Control-Allow-Credentials', true); - res.header('Access-Control-Allow-Methods', 'DELETE, PUT, POST, GET, OPTIONS, HEAD'); + res.header('Access-Control-Allow-Origin', 'http://localhost:3000') + res.header('Access-Control-Allow-Credentials', true) + res.header('Access-Control-Allow-Methods', 'DELETE, PUT, POST, GET, OPTIONS, HEAD') res.header( 'Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, x-requested-with, Content-Type, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization' - ); - res.header('Access-Control-Expose-Headers', 'Authorization'); - next(); - }); + ) + res.header('Access-Control-Expose-Headers', 'Authorization') + next() + }) // Handles GET requests on '{HOST}:{PORT}/api' - app.get("/api", async (req, res) => { - res.json({ message: "Hello from B&C Engine!" }); - }); + app.get('/api', async (req, res) => { + res.json({ message: 'Hello from B&C Engine!' }) + }) // Routes - app.use('/api/invoice', invoiceRoutes); - app.use('/api/users', userRoutes); - app.use('/api/reports', reportRoutes); + app.use('/api/invoices', invoiceRoutes) + app.use('/api/users', userRoutes) + app.use('/api/reports', reportRoutes) // Handles page refresh on the client side // (view index.hmtl and 404.html located in the client/public folder) app.use(function (req, res) { - res.sendFile(path.resolve(__dirname, '../client/public/404.html')); - }); + res.sendFile(path.resolve(__dirname, '../client/public/404.html')) + }) - return app; -}; + return app +} diff --git a/server/controllers/invoice.controller.js b/server/controllers/invoice.controller.js index 1304a51..bc2df7d 100644 --- a/server/controllers/invoice.controller.js +++ b/server/controllers/invoice.controller.js @@ -1,67 +1,64 @@ -const invoiceService = require('../services/invoice.service'); -const empService = require('../services/emp.service'); - +const invoiceService = require('../services/invoice.service') +const empService = require('../services/emp.service') exports.getAverages = async (req, res) => { - let regexDateStr = /^\d{4}-\d{2}-\d{2}$/; - let regexDate = new RegExp(regexDateStr); + const regexDateStr = /^\d{4}-\d{2}-\d{2}$/ + const regexDate = new RegExp(regexDateStr) - if (!req.params.startDate || !req.params.endDate) - return res.status(400).send({ message: "Content cannot be empty." }); + if (!req.params.startDate || !req.params.endDate) { return res.status(400).send({ message: 'Content cannot be empty.' }) } - if (!regexDate.test(req.params.startDate) || !regexDate.test(req.params.endDate)) - return res.status(400).send({ message: "Wrong format." }); + if (!regexDate.test(req.params.startDate) || !regexDate.test(req.params.endDate)) { return res.status(400).send({ message: 'Wrong format.' }) } - await invoiceService.getAverages(req.params.startDate, req.params.endDate, req.query.employeeId, req.query.clientType, req.query.countryLabel, req.query.countryCode, req.query.ageOfAccount) - .then(response => { - if (response) { - return res.status(200).send(response); - } - return res.status(500).send({ message: "The data could not be fetched." }); - }) - .catch(err => { - return res.status(err.status || 500) - .send({ message: !!err.message ? err.message : "Malfunction in the B&C Engine." }); - }); + await invoiceService.getAverages(req.params.startDate, req.params.endDate, req.query.employeeId, req.query.clientType, req.query.countryLabel, req.query.countryCode, req.query.ageOfAccount) + .then(response => { + if (response) { + return res.status(200).send(response) + } + return res.status(500).send({ message: 'The data could not be fetched.' }) + }) + .catch(err => { + return res.status(err.status || 500) + .send({ message: err.message ? err.message : 'Malfunction in the B&C Engine.' }) + }) } exports.getAllEmployeesDropdown = async (req, res) => { - if (req.user.role === "admin") { - await empService.getAllEmployees() - .then(response => { - if (response) { - return res.status(200).send(response); - } - return res.status(500).send({ message: "The data could not be fetched." }); - }) - .catch(err => { - return res.status(err.status || 500) - .send({ message: !!err.message ? err.message : "Malfunction in the B&C Engine." }); - }); - } else { - await empService.getAllEmployees(req.user.name) - .then(response => { - if (response) { - return res.status(200).send(response); - } - return res.status(500).send({ message: "The data could not be fetched." }); - }) - .catch(err => { - return res.status(err.status || 500) - .send({ message: !!err.message ? err.message : "Malfunction in the B&C Engine." }); - }); - } + if (req.user.role === 'admin') { + await empService.getAllEmployees() + .then(response => { + if (response) { + return res.status(200).send(response) + } + return res.status(500).send({ message: 'The data could not be fetched.' }) + }) + .catch(err => { + return res.status(err.status || 500) + .send({ message: err.message ? err.message : 'Malfunction in the B&C Engine.' }) + }) + } else { + await empService.getAllEmployees(req.user.name) + .then(response => { + if (response) { + return res.status(200).send(response) + } + return res.status(500).send({ message: 'The data could not be fetched.' }) + }) + .catch(err => { + return res.status(err.status || 500) + .send({ message: err.message ? err.message : 'Malfunction in the B&C Engine.' }) + }) + } } exports.getCountriesName = async (req, res) => { - await invoiceService.getCountriesName() - .then(response => { - if (response) { - return res.status(200).send(response); - } - return res.status(500).send({ message: "The data could not be fetched." }); - }) - .catch(err => { - return res.status(err.status || 500).send({ message: !!err.message ? err.message : "Malfunction in the B&C Engine." }); - }); -} \ No newline at end of file + await invoiceService.getCountriesName() + .then(response => { + if (response) { + return res.status(200).send(response) + } + return res.status(500).send({ message: 'The data could not be fetched.' }) + }) + .catch(err => { + return res.status(err.status || 500).send({ message: err.message ? err.message : 'Malfunction in the B&C Engine.' }) + }) +} diff --git a/server/controllers/report.controller.js b/server/controllers/report.controller.js index bd8ca7f..4b092dd 100644 --- a/server/controllers/report.controller.js +++ b/server/controllers/report.controller.js @@ -1,131 +1,121 @@ -const reportService = require('../services/report.service'); -let fs = require('fs'); -require("../../config.js"); +const reportService = require('../services/report.service') +const fs = require('fs') +require('../../config.js') exports.getChartReportsByUserId = async (req, res) => { - if (!req.user || !req.user.userId || req.user.userId === "" || req.user.userId === undefined) - return res.status(400).send({ message: "Content cannot be empty." }); - await reportService.getChartReportsByUserId(req.user.userId) - .then(async response => { - if (response) { - return res.send(response); - } - return res.status(500).send({ message: "The data could not be fetched." }); - }) - .catch(async err => { - return res.status(err.status || 500).send({ message: err.message || "Malfunction in the B&C Engine." }); - }); + if (!req.user || !req.user.userId || req.user.userId === '' || req.user.userId === undefined) { return res.status(400).send({ message: 'Content cannot be empty.' }) } + await reportService.getChartReportsByUserId(req.user.userId) + .then(async response => { + if (response) { + return res.send(response) + } + return res.status(500).send({ message: 'The data could not be fetched.' }) + }) + .catch(async err => { + return res.status(err.status || 500).send({ message: err.message || 'Malfunction in the B&C Engine.' }) + }) } exports.createChartReport = async (req, res) => { - if (!req.user || !req.user.userId || req.user.userId === "" || + if (!req.user || !req.user.userId || req.user.userId === '' || req.user.userId === undefined || !req.body.chartReport || req.body.chartReport === undefined || !req.body.chartReportData || - req.body.chartReportData === undefined) - return res.status(400).send({ message: "Content cannot be empty." }); - - let chartReportCriteria = req.body.chartReport; - let chartReportData = req.body.chartReportData; - - await reportService.createChartReportForUser(chartReportCriteria, chartReportData, req.user.userId) - .then(async response => { - if (response) { - return res.send(response); - } - return res.status(500).send({ message: "The data could not be fetched." }); - }) - .catch(async err => { - return res.status(err.status || 500).send({ message: err.message || "Malfunction in the B&C Engine." }); - }); + req.body.chartReportData === undefined) { return res.status(400).send({ message: 'Content cannot be empty.' }) } + + const chartReportCriteria = req.body.chartReport + const chartReportData = req.body.chartReportData + + await reportService.createChartReportForUser(chartReportCriteria, chartReportData, req.user.userId) + .then(async response => { + if (response) { + return res.send(response) + } + return res.status(500).send({ message: 'The data could not be fetched.' }) + }) + .catch(async err => { + return res.status(err.status || 500).send({ message: err.message || 'Malfunction in the B&C Engine.' }) + }) } exports.getReportTypesWithRecipients = async (req, res) => { - if (!req.user || !req.user.userId || req.userId === "" || + if (!req.user || !req.user.userId || req.userId === '' || req.user.userId === undefined) { - return res.status(400).send({ message: "Content cannot be empty." }); - } - - if (req.user.role !== "admin") - return res.status(403).send({ message: "You are not authorized to fetch from this resource." }); - - await reportService.getReportTypesWithRecipients() - .then(async response => { - if (response) { - return res.send(response); - } - return res.status(500).send({ message: "The data could not be fetched." }); - }) - .catch(err => { - return res.status(err.status || 500).send({ message: err.message || "Malfunction in the B&C Engine." }); - }); + return res.status(400).send({ message: 'Content cannot be empty.' }) + } + + if (req.user.role !== 'admin') { return res.status(403).send({ message: 'You are not authorized to fetch from this resource.' }) } + + await reportService.getReportTypesWithRecipients() + .then(async response => { + if (response) { + return res.send(response) + } + return res.status(500).send({ message: 'The data could not be fetched.' }) + }) + .catch(err => { + return res.status(err.status || 500).send({ message: err.message || 'Malfunction in the B&C Engine.' }) + }) } exports.deleteChartReport = async (req, res) => { - - await reportService.deleteChartReportById(req.body.chartReportId) - .then(response => { - if (response) { - return res.send(response); - } - return res.status(500).send({ message: "The data could not be deleted." }); - }) - .catch(async err => { - return res.status(err.status || 500).send({ message: err.message || "Malfunction in the B&C Engine." }); - }); + await reportService.deleteChartReportById(req.body.chartReportId) + .then(response => { + if (response) { + return res.send(response) + } + return res.status(500).send({ message: 'The data could not be deleted.' }) + }) + .catch(async err => { + return res.status(err.status || 500).send({ message: err.message || 'Malfunction in the B&C Engine.' }) + }) } exports.getPerformanceReportsOfAllUsers = async (req, res) => { - if (!req.user || !req.user.userId || req.user.userId === "" || req.user.userId === undefined) - return res.status(400).send({ message: "Content cannot be empty." }); - await reportService.getPerformanceReportWhenConnectedAsAdmin(req.user.userId) - .then(async response => { - if (response) { - return res.send(response); - } - return res.status(500).send({ message: "The data could not be fetched." }); - }) - .catch(async err => { - return res.status(err.status || 500).send({ message: err.message || "Malfunction in the B&C Engine." }); - }); + if (!req.user || !req.user.userId || req.user.userId === '' || req.user.userId === undefined) { return res.status(400).send({ message: 'Content cannot be empty.' }) } + await reportService.getPerformanceReports(req.user.userId) + .then(async response => { + if (response) { + return res.send(response) + } + return res.status(500).send({ message: 'The data could not be fetched.' }) + }) + .catch(async err => { + return res.status(err.status || 500).send({ message: err.message || 'Malfunction in the B&C Engine.' }) + }) } exports.createChartReportPDF = async (req, res) => { - if (!req.body.reportid) - return res.status(400).send({ message: "Content cannot be empty." }); - - await reportService.createChartReportPDFById(req.body.reportid) - .then(response => { - if(response) { - return res.status(200).send(response); - } - return res.status(500).send({ message: "The data could not be fetched." }); - }) - .catch(err => { - return res.status(err.status || 500).send({ message: err.message || "Malfunction in the B&C Engine." }); - }); + if (!req.body.reportid) { return res.status(400).send({ message: 'Content cannot be empty.' }) } + + await reportService.createChartReportPDFById(req.body.reportid) + .then(response => { + if (response) { + return res.status(200).send(response) + } + return res.status(500).send({ message: 'The data could not be fetched.' }) + }) + .catch(err => { + return res.status(err.status || 500).send({ message: err.message || 'Malfunction in the B&C Engine.' }) + }) } exports.fetchChartReportPDF = async (req, res) => { - // create file path - let filePath; - if(__dirname !== '/home/runner/work/BC-Engine/BC-Engine/server/controllers') { - filePath = `${__dirname.replace("controllers", "")}docs\\pdf_files\\chartReport-${req.query.reportid}.pdf`; - } - else { - filePath = `${__dirname.replace("controllers", "")}docs/pdf_files/chartReport-${req.query.reportid}.pdf`; + // create file path + let filePath + if (__dirname !== '/home/runner/work/BC-Engine/BC-Engine/server/controllers') { + filePath = `${__dirname.replace('controllers', '')}docs\\pdf_files\\chartReport-${req.query.reportid}.pdf` + } else { + filePath = `${__dirname.replace('controllers', '')}docs/pdf_files/chartReport-${req.query.reportid}.pdf` + } + + if (!req.query.reportid) { return res.status(400).send({ message: 'Content cannot be empty.' }) } + + await res.sendFile(filePath, {}, (err) => { + if (err) { + return res.status(err.status || 500).send({ message: err.message || 'File not found.' }) + } else { + fs.unlinkSync(filePath) + return res.status(200) } - - if (!req.query.reportid) - return res.status(400).send({ message: "Content cannot be empty." }); - - await res.sendFile(filePath, {}, (err) => { - if(err) { - return res.status(err.status || 500).send({ message: err.message || "File not found." }); - } - else { - fs.unlinkSync(filePath); - return res.status(200) - } - }); + }) } - diff --git a/server/controllers/user.controller.js b/server/controllers/user.controller.js index 3e50443..8eb15a0 100644 --- a/server/controllers/user.controller.js +++ b/server/controllers/user.controller.js @@ -1,138 +1,133 @@ -const userService = require('../services/user.service'); -const authService = require('../services/auth.service'); +const userService = require('../services/user.service') +const authService = require('../services/auth.service') // Create and Save a new User exports.create = async (req, res) => { - if (req.user.role !== "admin") return res.status(403).send(); - // Validate request - if (!req.body.email || !req.body.password - || !req.body.role) { - return res.status(400).send({ - message: "Content cannot be empty." - }); - } - if (!req.body.email.endsWith("@benoit-cote.com")) { - return res.status(400).send({ - message: "Invalid email format." - }) - } - - // Create a User - const user = { - email: req.body.email, - password: req.body.password, - name: req.emp.name, - role: req.body.role - }; - - // Call service to save User to db - await userService.createUser(user) - .then(response => { - return res.send(response); - }) - .catch(err => { - if (err.message === "Validation error") { - err.message = "User already exists."; - return res.status(400).send(err); - } - else { - return res.status(500).send(err); - } - - }); -}; + if (req.user.role !== 'admin') return res.status(403).send() + // Validate request + if (!req.body.email || !req.body.password || + !req.body.role) { + return res.status(400).send({ + message: 'Content cannot be empty.' + }) + } + if (!req.body.email.endsWith('@benoit-cote.com')) { + return res.status(400).send({ + message: 'Invalid email format.' + }) + } + + // Create a User + const user = { + email: req.body.email, + password: req.body.password, + name: req.emp.name, + role: req.body.role + } + + // Call service to save User to db + await userService.createUser(user) + .then(response => { + return res.send(response) + }) + .catch(err => { + if (err.message === 'Validation error') { + err.message = 'User already exists.' + return res.status(400).send(err) + } else { + return res.status(500).send(err) + } + }) +} // Fetch all Users from db exports.findAll = async (req, res) => { - if (req.user.role !== "admin") return res.status(403).send(); - await userService.getAllUsers() - .then(response => { - return res.status(200).send(response); - }) - .catch(err => { - return res.status(500).send(err); - }); -}; + if (req.user.role !== 'admin') return res.status(403).send() + await userService.getAllUsers() + .then(response => { + return res.status(200).send(response) + }) + .catch(err => { + return res.status(500).send(err) + }) +} // Begining of Authenticate a user exports.authenticateUserWithEmail = async (req, res) => { - const user = req.body; - let authUser = {}; - - if (!user.email) { - res.status(400).send({ - message: "Content cannot be empty." - }); - return; - } - - await userService.authenticateUser(user) - .then(response => { - if (response == null || !response) { - return res.status(401).send({ - auth: false, - message: "No user found" - }); - } - - authUser = response.dataValues; - var [accessToken, refreshToken] = authService.getTokens(authUser); - - const returnUser = { - userId: authUser.userId, - email: authUser.email, - name: authUser.name, - role: authUser.role - }; - return res - .header('authorization', refreshToken) - .send({ - authenticatedUser: returnUser, - aToken: accessToken, - auth: true - }); + const user = req.body + let authUser = {} + + if (!user.email) { + res.status(400).send({ + message: 'Content cannot be empty.' + }) + return + } + + await userService.authenticateUser(user) + .then(response => { + if (response == null || !response) { + return res.status(401).send({ + auth: false, + message: 'No user found' }) - .catch(err => { - return res.status(500).send(err.message); - }); -}; - + } + + authUser = response.dataValues + const [accessToken, refreshToken] = authService.getTokens(authUser) + + const returnUser = { + userId: authUser.userId, + email: authUser.email, + name: authUser.name, + role: authUser.role + } + return res + .header('authorization', refreshToken) + .send({ + authenticatedUser: returnUser, + aToken: accessToken, + auth: true + }) + }) + .catch(err => { + return res.status(500).send(err.message) + }) +} exports.modifyUser = async (req, res) => { - if (req.user.role !== "admin") return res.status(403).send(); - - // Validate request - if (!req.body.email || req.body.email === "") { - return res.status(400).send({ - message: "Content cannot be empty." - }); - } - - const user = { - email: req.body.email, - password: req.body.password, - role: req.body.role - }; - - await userService.modifyUser(user) - .then(response => { - return res.send(response); - }) - .catch(err => { - return res.status(500).send(err); - }); + if (req.user.role !== 'admin') return res.status(403).send() + + // Validate request + if (!req.body.email || req.body.email === '') { + return res.status(400).send({ + message: 'Content cannot be empty.' + }) + } + + const user = { + email: req.body.email, + password: req.body.password, + role: req.body.role + } + + await userService.modifyUser(user) + .then(response => { + return res.send(response) + }) + .catch(err => { + return res.status(500).send(err) + }) } exports.deleteUser = async (req, res) => { - if (req.user.role !== "admin") return res.status(403).send(); - - - await userService.deleteUser(req.body.email) - .then(response => { - return res.send(response); - }) - .catch(err => { - return res.status(500).send(err); - }); + if (req.user.role !== 'admin') return res.status(403).send() + + await userService.deleteUser(req.body.email) + .then(response => { + return res.send(response) + }) + .catch(err => { + return res.status(500).send(err) + }) } - diff --git a/server/data_access_layer/daos/chart_report.dao.js b/server/data_access_layer/daos/chart_report.dao.js index 97a72d5..3ff2624 100644 --- a/server/data_access_layer/daos/chart_report.dao.js +++ b/server/data_access_layer/daos/chart_report.dao.js @@ -1,152 +1,152 @@ -const databases = require("../databases"); -const ChartReportModel = databases['localdb'].chartReports; -const ChartReportDataModel = databases['localdb'].chartReportsData; +const databases = require('../databases') +const ChartReportModel = databases.localdb.chartReports +const ChartReportDataModel = databases.localdb.chartReportsData // Chart Report Related functions exports.getChartReportsByUserId = async (userId, chartReportModel = ChartReportModel) => { - return new Promise((resolve, reject) => { - chartReportModel.findAll({ - where: { - user_user_id: userId - }, - order: [['name', 'ASC']] - }).then(async data => { - if (data) { - resolve(data) - } - resolve(false); - }) - .catch(async err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch data." - }; - reject(response); - }); - }); -}; + return new Promise((resolve, reject) => { + chartReportModel.findAll({ + where: { + user_user_id: userId + }, + order: [['name', 'ASC']] + }).then(async data => { + if (data) { + resolve(data) + } + resolve(false) + }) + .catch(async err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch data.' + } + reject(response) + }) + }) +} exports.createChartReportForUser = async (userId, chartReport, chartReportModel = ChartReportModel) => { - return new Promise((resolve, reject) => { - chartReport.user_user_id = userId; - chartReportModel.create(chartReport) - .then(async data => { - if (data) { - let returnData = { - chartReportId: data.dataValues.chartReportId, - name: data.dataValues.name, - emp1Id: data.dataValues.employee1Id, - emp2Id: data.dataValues.employee2Id - }; - resolve(returnData) - } - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Could not create data." - } - reject(response); - }) - }); + return new Promise((resolve, reject) => { + chartReport.user_user_id = userId + chartReportModel.create(chartReport) + .then(async data => { + if (data) { + const returnData = { + chartReportId: data.dataValues.chartReportId, + name: data.dataValues.name, + emp1Id: data.dataValues.employee1Id, + emp2Id: data.dataValues.employee2Id + } + resolve(returnData) + } + resolve(false) + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not create data.' + } + reject(response) + }) + }) } exports.createDataForChartReport = async (chartReportId, data, chartReportDataModel = ChartReportDataModel) => { - return new Promise((resolve, reject) => { - data.chart_report_id = chartReportId; - chartReportDataModel.bulkCreate(data) - .then(async data => { - if (data) { - let returnData = []; - for (let i = 0; i < data.length; i++) { - returnData.push({ - chartReportDataId: data[i].dataValues.id, - year: data[i].dataValues.year, - employee: data[i].dataValues.employee - }); - } - resolve(returnData) - } - resolve(false); + return new Promise((resolve, reject) => { + data.chart_report_id = chartReportId + chartReportDataModel.bulkCreate(data) + .then(async data => { + if (data) { + const returnData = [] + for (let i = 0; i < data.length; i++) { + returnData.push({ + chartReportDataId: data[i].dataValues.id, + year: data[i].dataValues.year, + employee: data[i].dataValues.employee }) - .catch(async err => { - const response = { - status: err.status || 500, - message: err.message || "Could not create data." - } - reject(response); - }); - }); + } + resolve(returnData) + } + resolve(false) + }) + .catch(async err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not create data.' + } + reject(response) + }) + }) } exports.getChartReportById = async (chartReportId, chartReportModel = ChartReportModel) => { - return new Promise((resolve, reject) => { - chartReportModel.findOne({ - where: { - chart_report_id: chartReportId - } - }) - .then(async data => { - if (data) { - resolve(data); - } - resolve(false); - }) - .catch(async err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch data." - }; - reject(response); - }); - }); + return new Promise((resolve, reject) => { + chartReportModel.findOne({ + where: { + chart_report_id: chartReportId + } + }) + .then(async data => { + if (data) { + resolve(data) + } + resolve(false) + }) + .catch(async err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch data.' + } + reject(response) + }) + }) } exports.getDataForChartReport = async (chartReportId, chartReportDataModel = ChartReportDataModel) => { - return new Promise((resolve, reject) => { - chartReportDataModel.findAll({ - where: { - chart_report_id: chartReportId - } - }) - .then(async data => { - if(data) { - if (data.length !== 0) { - let returnData = []; - for (let i = 0; i < data.length; i++) { - returnData.push(data[i].dataValues); - } - resolve(returnData) - } - } - resolve(false) - }) - .catch(async err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch data." + return new Promise((resolve, reject) => { + chartReportDataModel.findAll({ + where: { + chart_report_id: chartReportId + } + }) + .then(async data => { + if (data) { + if (data.length !== 0) { + const returnData = [] + for (let i = 0; i < data.length; i++) { + returnData.push(data[i].dataValues) } - reject(response); - }); - }); + resolve(returnData) + } + } + resolve(false) + }) + .catch(async err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch data.' + } + reject(response) + }) + }) } -exports.deleteChartReportById = async(chartReportId, chartReportModel = ChartReportModel) => { - return new Promise((resolve, reject) => { - chartReportModel.destroy({where: {chartReportId: chartReportId}}) - .then(async data => { - if (data) { - resolve("Chart report deleted successfully."); - } - resolve(false); - }) - .catch(err =>{ - const response = { - status: err.status || 500, - message: err.message || "Could not delete data." - } - reject(response); - }); - }); -} \ No newline at end of file +exports.deleteChartReportById = async (chartReportId, chartReportModel = ChartReportModel) => { + return new Promise((resolve, reject) => { + chartReportModel.destroy({ where: { chartReportId: chartReportId } }) + .then(async data => { + if (data) { + resolve('Chart report deleted successfully.') + } + resolve(false) + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not delete data.' + } + reject(response) + }) + }) +} diff --git a/server/data_access_layer/daos/country.dao.js b/server/data_access_layer/daos/country.dao.js index c2714a3..3e59060 100644 --- a/server/data_access_layer/daos/country.dao.js +++ b/server/data_access_layer/daos/country.dao.js @@ -1,45 +1,43 @@ -const database = require('../databases')['mssql_bosco']; -const { QueryTypes } = require('sequelize'); - +const database = require('../databases').mssql_bosco +const { QueryTypes } = require('sequelize') exports.getAllCountries = async (db = database) => { - return new Promise(async (resolve, reject) => { - try { - const data = await db.query( - "SELECT DISTINCT C.[COUNTRY_CODE], \ - C.[COUNTRY_LABEL] \ - FROM [Bosco reduction].[dbo].[COUNTRY] C, [Bosco reduction].[dbo].[COUNTRY_SUB] P \ - WHERE C.[COUNTRY_LABEL] NOT IN (SELECT [SUB_COUNTRY_LABEL] FROM [Bosco reduction].[dbo].[COUNTRY_SUB]) \ - AND C.[COUNTRY_CODE] != '**' \ - AND C.[COUNTRY_CODE] != '++' AND C.[COUNTRY_CODE] != 'A5' \ - AND C.[COUNTRY_CODE] != 'A9' AND C.[COUNTRY_CODE] != 'AP' \ - AND C.[COUNTRY_CODE] != 'W5' AND C.[COUNTRY_CODE] != 'WE' \ - AND C.[COUNTRY_CODE] != 'WO' AND C.[COUNTRY_CODE] != 'XX' \ - AND C.[COUNTRY_CODE] != 'YY' AND C.[COUNTRY_CODE] != 'ZZ' \ - ORDER BY C.[COUNTRY_LABEL]", - { - type: QueryTypes.SELECT - } - ); + return new Promise(async (resolve, reject) => { + try { + const queryString = "".concat("SELECT DISTINCT C.[COUNTRY_CODE], C.[COUNTRY_LABEL] ", + "FROM [Bosco reduction].[dbo].[COUNTRY] C, [Bosco reduction].[dbo].[COUNTRY_SUB] P ", + "WHERE C.[COUNTRY_LABEL] NOT IN (SELECT [SUB_COUNTRY_LABEL] FROM [Bosco reduction].[dbo].[COUNTRY_SUB]) ", + "AND C.[COUNTRY_CODE] != '**' ", + "AND C.[COUNTRY_CODE] != '++' AND C.[COUNTRY_CODE] != 'A5' ", + "AND C.[COUNTRY_CODE] != 'A9' AND C.[COUNTRY_CODE] != 'AP' ", + "AND C.[COUNTRY_CODE] != 'W5' AND C.[COUNTRY_CODE] != 'WE' ", + "AND C.[COUNTRY_CODE] != 'WO' AND C.[COUNTRY_CODE] != 'XX' ", + "AND C.[COUNTRY_CODE] != 'YY' AND C.[COUNTRY_CODE] != 'ZZ' ", + "ORDER BY C.[COUNTRY_LABEL]") - if (data) { - let returnData = []; - data.forEach(c => { - returnData.push({ - countryCode: c["COUNTRY_CODE"], - countryLabel: c["COUNTRY_LABEL"] - }); - }); - resolve(returnData); - } - resolve(false); - } - catch (err) { - const response = { - status: err.status || 500, - message: err.message || "some error occured" - } - reject(response); + const data = await db.query(queryString, + { + type: QueryTypes.SELECT } - }); -} \ No newline at end of file + ) + + if (data) { + const returnData = [] + data.forEach(c => { + returnData.push({ + countryCode: c.COUNTRY_CODE, + countryLabel: c.COUNTRY_LABEL + }) + }) + resolve(returnData) + } + resolve(false) + } catch (err) { + const response = { + status: err.status || 500, + message: err.message || 'some error occured' + } + reject(response) + } + }) +} diff --git a/server/data_access_layer/daos/emp.dao.js b/server/data_access_layer/daos/emp.dao.js index 38d9011..6c582ea 100644 --- a/server/data_access_layer/daos/emp.dao.js +++ b/server/data_access_layer/daos/emp.dao.js @@ -1,26 +1,26 @@ -const databases = require("../databases"); -const EmployeeModel = databases['mssql_pat'].employees; +const databases = require('../databases') +const EmployeeModel = databases.mssql_pat.employees exports.getEmployeeByEmail = async (email, empModel = EmployeeModel) => { - return new Promise((resolve, reject) => { - empModel.findOne({ - where: { - email: email - } - }).then(async data => { - if (data) { - let emp = {}; - emp.email = data.dataValues.email; - emp.name = data.dataValues.firstName + " " + data.dataValues.lastName; - resolve(emp); - } - resolve(false); - }).catch(err => { - const response = { - status: err.status || 500, - message: err.message || "some error occured" - } - reject(response); - }); - }); -} \ No newline at end of file + return new Promise((resolve, reject) => { + empModel.findOne({ + where: { + email: email + } + }).then(async data => { + if (data) { + const emp = {} + emp.email = data.dataValues.email + emp.name = data.dataValues.firstName + ' ' + data.dataValues.lastName + resolve(emp) + } + resolve(false) + }).catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'some error occured' + } + reject(response) + }) + }) +} diff --git a/server/data_access_layer/daos/invoice_affect.dao.js b/server/data_access_layer/daos/invoice_affect.dao.js index b03dce7..cdc56db 100644 --- a/server/data_access_layer/daos/invoice_affect.dao.js +++ b/server/data_access_layer/daos/invoice_affect.dao.js @@ -1,102 +1,104 @@ -const database = require('../databases')['mssql_pat'] -const { QueryTypes } = require('sequelize'); +const database = require('../databases').mssql_pat +const { QueryTypes } = require('sequelize') exports.getInvoicesByDate = async (startDate, endDate, employeeId = undefined, clientType = undefined, countryCode = undefined, ageOfAccount = undefined, db = database) => { - return new Promise(async (resolve, reject) => { - try { - let query = this.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount); + return new Promise(async (resolve, reject) => { + try { + const query = this.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount) - const data = await db.query(query.queryString, - { - replacements: query.replacements, - type: QueryTypes.SELECT - } - ); - - if (data) { - let returnData = []; - data.forEach(e => { - returnData.push({ - invoiceDate: e['INVOCIE_DATE'], - actorId: e['ACTOR_ID'], - amount: e['AFFECT_AMOUNT'] - }); - }); - resolve(returnData); - } - resolve(false); - } - catch (err) { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch invoices." - }; - reject(response); + const data = await db.query(query.queryString, + { + replacements: query.replacements, + type: QueryTypes.SELECT } - }); + ) + + if (data) { + const returnData = [] + data.forEach(e => { + returnData.push({ + invoiceDate: e.INVOCIE_DATE, + actorId: e.ACTOR_ID, + amount: e.AFFECT_AMOUNT + }) + }) + resolve(returnData) + } + resolve(false) + } catch (err) { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch invoices.' + } + reject(response) + } + }) } exports.prepareBilledQuery = (startDate, endDate, employeeId, clientType, countryCode, ageOfAccount) => { - let query = { - queryString: "SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - replacements: [startDate, endDate] - }; + const query = { + queryString: 'SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + replacements: [startDate, endDate] + } - let fromString = "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH "; - let whereString = "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' "; + let fromString = 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ' + let whereString = "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' " - if (employeeId !== undefined) { - fromString = fromString.concat(" LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1", - " AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID)", - " LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1", - " ON NQ1.NAME_ID=NC.NAME_ID", - " AND NQ1.QUALITY_TYPE_ID=5 "); - whereString = whereString.concat(" AND NQ1.DROPDOWN_CODE=? "); - query.replacements.push(employeeId); - } + if (employeeId !== undefined) { + fromString = fromString.concat(' LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1', + ' AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID)', + ' LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1', + ' ON NQ1.NAME_ID=NC.NAME_ID', + ' AND NQ1.QUALITY_TYPE_ID=5 ') + whereString = whereString.concat(' AND NQ1.DROPDOWN_CODE=? ') + query.replacements.push(employeeId) + } - if (clientType !== undefined) { - fromString = fromString.includes("LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID)") ? - fromString : fromString.concat(" LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) "); - fromString = fromString.concat(" LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ", - " ON NQ2.NAME_ID=NC.NAME_ID ", - " AND NQ2.QUALITY_TYPE_ID=3 "); - whereString = whereString.concat(" AND NQ2.DROPDOWN_CODE=? "); - query.replacements.push(clientType.toUpperCase()); - } + if (clientType !== undefined) { + fromString = fromString.includes('LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID)') + ? fromString + : fromString.concat(' LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ') + fromString = fromString.concat(' LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ', + ' ON NQ2.NAME_ID=NC.NAME_ID ', + ' AND NQ2.QUALITY_TYPE_ID=3 ') + whereString = whereString.concat(' AND NQ2.DROPDOWN_CODE=? ') + query.replacements.push(clientType.toUpperCase()) + } - if (ageOfAccount !== undefined) { - fromString = fromString.includes("LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID)") ? - fromString : fromString.concat(" LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) "); - fromString = fromString.concat(" LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) "); + if (ageOfAccount !== undefined) { + fromString = fromString.includes('LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID)') + ? fromString + : fromString.concat(' LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ') + fromString = fromString.concat(' LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ') - switch (ageOfAccount) { - case "<30": - whereString = whereString.concat(" AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<30 "); - break; - case "30-60": - whereString = whereString.concat(" AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>=30 ", - "AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<60"); - break; - case "60-90": - whereString = whereString.concat(" AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>=60 ", - "AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<=90"); - break; - case ">90": - whereString = whereString.concat(" AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>90 "); - break; - } + switch (ageOfAccount) { + case '<30': + whereString = whereString.concat(' AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<30 ') + break + case '30-60': + whereString = whereString.concat(' AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>=30 ', + 'AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<60') + break + case '60-90': + whereString = whereString.concat(' AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>=60 ', + 'AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<=90') + break + case '>90': + whereString = whereString.concat(' AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>90 ') + break } + } - if (countryCode !== undefined) { - fromString = fromString.includes("LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID)") ? - fromString : fromString.concat(" LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) "); - fromString = fromString.concat(", [Bosco reduction].[dbo].NAME N "); - whereString = whereString.concat(" AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? "); - query.replacements.push(countryCode); - } + if (countryCode !== undefined) { + fromString = fromString.includes('LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID)') + ? fromString + : fromString.concat(' LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ') + fromString = fromString.concat(', [Bosco reduction].[dbo].NAME N ') + whereString = whereString.concat(' AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? ') + query.replacements.push(countryCode) + } - query.queryString = query.queryString.concat(fromString, whereString); + query.queryString = query.queryString.concat(fromString, whereString) - return query; -} \ No newline at end of file + return query +} diff --git a/server/data_access_layer/daos/name.dao.js b/server/data_access_layer/daos/name.dao.js index 1276eb9..091b443 100644 --- a/server/data_access_layer/daos/name.dao.js +++ b/server/data_access_layer/daos/name.dao.js @@ -1,71 +1,69 @@ -const database = require('../databases')['mssql_bosco']; -const { QueryTypes } = require('sequelize'); +const database = require('../databases').mssql_bosco +const { QueryTypes } = require('sequelize') exports.getClientsInClientIdList = async (clientIDList, db = database) => { - return new Promise(async (resolve, reject) => { - try { - let queryString = "".concat("SELECT DISTINCT N.NAME_ID, ISNULL(N.NAME_1,'')+ISNULL(' '+N.NAME_2,'')+ISNULL(' '+N.NAME_3,'') as NAME, C.COUNTRY_LABEL, NQ.DROPDOWN_CODE as 'GRADING'", - " FROM NAME N LEFT OUTER JOIN NAME_QUALITY NQ ON NQ.NAME_ID=N.NAME_ID AND NQ.QUALITY_TYPE_ID=15, COUNTRY C, NAME_CONNECTION NC", - " WHERE C.COUNTRY_CODE=N.LEGAL_COUNTRY_CODE AND NC.CONNECTION_NAME_ID IN (?) AND NC.NAME_ID=N.NAME_ID ORDER BY NAME"); + return new Promise(async (resolve, reject) => { + try { + const queryString = ''.concat("SELECT DISTINCT N.NAME_ID, ISNULL(N.NAME_1,'')+ISNULL(' '+N.NAME_2,'')+ISNULL(' '+N.NAME_3,'') as NAME, C.COUNTRY_LABEL, NQ.DROPDOWN_CODE as 'GRADING'", + ' FROM NAME N LEFT OUTER JOIN NAME_QUALITY NQ ON NQ.NAME_ID=N.NAME_ID AND NQ.QUALITY_TYPE_ID=15, COUNTRY C, NAME_CONNECTION NC', + ' WHERE C.COUNTRY_CODE=N.LEGAL_COUNTRY_CODE AND NC.CONNECTION_NAME_ID IN (?) AND NC.NAME_ID=N.NAME_ID ORDER BY NAME') - const data = await db.query(queryString, - { - replacements: [clientIDList], - type: QueryTypes.SELECT - } - ); - if (data) { - let returnData = []; - data.forEach(c => { - returnData.push({ - nameId: c["NAME_ID"], - name: c["NAME"], - country: c["COUNTRY_LABEL"], - grading: c["GRADING"] === null ? "N/A" : c["GRADING"] - }); - }); - resolve(returnData); - } - resolve(false); + const data = await db.query(queryString, + { + replacements: [clientIDList], + type: QueryTypes.SELECT } - catch (err) { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch clients." - }; - reject(response); - } - }); + ) + if (data) { + const returnData = [] + data.forEach(c => { + returnData.push({ + nameId: c.NAME_ID, + name: c.NAME, + country: c.COUNTRY_LABEL, + grading: c.GRADING === null ? 'N/A' : c.GRADING + }) + }) + resolve(returnData) + } + resolve(false) + } catch (err) { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch clients.' + } + reject(response) + } + }) } exports.getAllEmployeeNames = async (db = database) => { - return new Promise(async (resolve, reject) => { - try { - const data = await db.query( - "SELECT ISNULL(NAME_1,'') + ISNULL(' '+NAME_2,'') + ISNULL(' '+NAME_3,'') AS FULLNAME, NAME_ID \ - FROM NAME \ - WHERE NAME_ID IN \ - ( SELECT DISTINCT DROPDOWN_CODE FROM NAME_QUALITY \ - WHERE DROPDOWN_ID = 5 ) \ - ORDER BY NAME_1", - { - type: QueryTypes.SELECT - } - ); - if (data) { - let returnData = []; - data.forEach(c => { - returnData.push({ - nameID: c["NAME_ID"], - name: c["FULLNAME"], - }); - }); - resolve(returnData); - } - resolve(false); - } - catch (err) { - reject(err); + return new Promise(async (resolve, reject) => { + try { + const queryString = "".concat("SELECT ISNULL(NAME_1,'') + ISNULL(' '+NAME_2,'') + ISNULL(' '+NAME_3,'') AS FULLNAME, NAME_ID ", + "FROM NAME ", + "WHERE NAME_ID IN ", + "( SELECT DISTINCT DROPDOWN_CODE FROM NAME_QUALITY WHERE DROPDOWN_ID = 5 ) ", + "ORDER BY NAME_1") + + const data = await db.query(queryString, + { + type: QueryTypes.SELECT } - }); -} \ No newline at end of file + ) + if (data) { + const returnData = [] + data.forEach(c => { + returnData.push({ + nameID: c.NAME_ID, + name: c.FULLNAME + }) + }) + resolve(returnData) + } + resolve(false) + } catch (err) { + reject(err) + } + }) +} diff --git a/server/data_access_layer/daos/report.dao.js b/server/data_access_layer/daos/report.dao.js index 58270c2..27bb973 100644 --- a/server/data_access_layer/daos/report.dao.js +++ b/server/data_access_layer/daos/report.dao.js @@ -1,99 +1,98 @@ -const databases = require("../databases"); -const ReportTypeModel = databases['localdb'].reportTypes; -const RecipientsModel = databases['localdb'].recipients; -const ReportTypeRecipientsModel = databases['localdb'].reportTypeRecipients; -const PerformanceReportModel = databases['localdb'].performanceReport; +const databases = require('../databases') +const ReportTypeModel = databases.localdb.reportTypes +const RecipientsModel = databases.localdb.recipients +const ReportTypeRecipientsModel = databases.localdb.reportTypeRecipients +const PerformanceReportModel = databases.localdb.performanceReport exports.getReportTypesWithRecipientIds = async (reportTypesModel = ReportTypeModel, reportTypeRecipients = ReportTypeRecipientsModel) => { - return new Promise((resolve, reject) => { - reportTypesModel.findAll() - .then(async reportTypes => { - if (reportTypes) { - let returnData = { - reportTypes: [], - }; - let types = []; - for (let i = 0; i < reportTypes.length; i++) { - types.push({ - reportTypeId: reportTypes[i].dataValues.reportTypeId, - name: reportTypes[i].dataValues.reportTypeName, - frequency: reportTypes[i].dataValues.frequency - }); - } - returnData.reportTypes = types; - returnData.recipients = await reportTypeRecipients.findAll(); - return returnData; - } - resolve(false); + return new Promise((resolve, reject) => { + reportTypesModel.findAll() + .then(async reportTypes => { + if (reportTypes) { + const returnData = { + reportTypes: [] + } + const types = [] + for (let i = 0; i < reportTypes.length; i++) { + types.push({ + reportTypeId: reportTypes[i].dataValues.reportTypeId, + name: reportTypes[i].dataValues.reportTypeName, + frequency: reportTypes[i].dataValues.frequency }) - .then(async data => { - if (data.recipients) { - for (let i = 0; i < data.reportTypes.length; i++) { - data.reportTypes[i].recipients = {}; - for (let j = 0; j < data.recipients.length; j++) { - if (data.reportTypes[i].reportTypeId === data.recipients[j].dataValues.report_type_id && + } + returnData.reportTypes = types + returnData.recipients = await reportTypeRecipients.findAll() + return returnData + } + resolve(false) + }) + .then(async data => { + if (data.recipients) { + for (let i = 0; i < data.reportTypes.length; i++) { + data.reportTypes[i].recipients = {} + for (let j = 0; j < data.recipients.length; j++) { + if (data.reportTypes[i].reportTypeId === data.recipients[j].dataValues.report_type_id && !(data.recipients[j].dataValues.recipient_id in data.reportTypes[i].recipients)) { - data.reportTypes[i].recipients[data.recipients[j].dataValues.recipient_id] = { name: "" }; - } - } - } - resolve(data.reportTypes); - } - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Malfunction in the B&C Engine." - }; - reject(response); - }) - }); + data.reportTypes[i].recipients[data.recipients[j].dataValues.recipient_id] = { name: '' } + } + } + } + resolve(data.reportTypes) + } + resolve(false) + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Malfunction in the B&C Engine.' + } + reject(response) + }) + }) } exports.getRecipients = async (recipientsModel = RecipientsModel) => { - return new Promise((resolve, reject) => { - recipientsModel.findAll() - .then(async reportRecipients => { - if (reportRecipients) { - let returnData = []; - for (let i = 0; i < reportRecipients.length; i++) { - returnData.push({ - recipientId: reportRecipients[i].recipientId, - name: reportRecipients[i].name, - email: reportRecipients[i].email - }); - } - resolve(returnData); - } - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Malfunction in the B&C Engine." - }; - reject(response); + return new Promise((resolve, reject) => { + recipientsModel.findAll() + .then(async reportRecipients => { + if (reportRecipients) { + const returnData = [] + for (let i = 0; i < reportRecipients.length; i++) { + returnData.push({ + recipientId: reportRecipients[i].recipientId, + name: reportRecipients[i].name, + email: reportRecipients[i].email }) - }); + } + resolve(returnData) + } + resolve(false) + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Malfunction in the B&C Engine.' + } + reject(response) + }) + }) } - // Reports Types related functions -exports.getPerformanceReportsWhenConnectedAsAdmin = async (userId, performanceReportModel = PerformanceReportModel) => { - return new Promise((resolve, reject) => { - performanceReportModel.findAll().then(async data => { - if (data) { - resolve(data) - } - resolve(false); - }) - .catch(async err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch data." - }; - reject(response); - }); - }); -} \ No newline at end of file +exports.getPerformanceReports = async (userId, performanceReportModel = PerformanceReportModel) => { + return new Promise((resolve, reject) => { + performanceReportModel.findAll().then(async data => { + if (data) { + resolve(data) + } + resolve(false) + }) + .catch(async err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch data.' + } + reject(response) + }) + }) +} diff --git a/server/data_access_layer/daos/transac_stat.dao.js b/server/data_access_layer/daos/transac_stat.dao.js index 82c26ba..f9ab906 100644 --- a/server/data_access_layer/daos/transac_stat.dao.js +++ b/server/data_access_layer/daos/transac_stat.dao.js @@ -1,103 +1,102 @@ -const database = require('../databases')['mssql_bosco'] -const { QueryTypes } = require('sequelize'); +const database = require('../databases').mssql_bosco +const { QueryTypes } = require('sequelize') exports.getTransactionsStatByYearMonth = async (yearMonthList, employeeId = undefined, clientType = undefined, countryLabel = undefined, ageOfAccount = undefined, db = database) => { - return new Promise(async (resolve, reject) => { - try { - let query = this.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount); + return new Promise(async (resolve, reject) => { + try { + const query = this.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount) - const data = await db.query(query.queryString, { - replacements: query.replacements, - type: QueryTypes.SELECT - }); + const data = await db.query(query.queryString, { + replacements: query.replacements, + type: QueryTypes.SELECT + }) - if (data) { - let returnData = []; + if (data) { + const returnData = [] - data.forEach(e => { - returnData.push({ - dueCurrent: e['DUE_CURRENT'], - due1Month: e['DUE_1_MONTH'], - due2Month: e['DUE_2_MONTH'], - due3Month: e['DUE_3_MONTH'], - yearMonth: e['YEAR_MONTH'] - }); - }); - resolve(returnData); - } - resolve(false); - } - catch (err) { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch transactions." - }; - reject(response); - } - }) + data.forEach(e => { + returnData.push({ + dueCurrent: e.DUE_CURRENT, + due1Month: e.DUE_1_MONTH, + due2Month: e.DUE_2_MONTH, + due3Month: e.DUE_3_MONTH, + yearMonth: e.YEAR_MONTH + }) + }) + resolve(returnData) + } + resolve(false) + } catch (err) { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch transactions.' + } + reject(response) + } + }) } exports.prepareDuesQuery = (yearMonthList, employeeId, clientType, countryLabel, ageOfAccount) => { - let query = { - queryString: "SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ", - replacements: [yearMonthList] - }; + const query = { + queryString: 'SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ', + replacements: [yearMonthList] + } - let fromString = "FROM ACCOUNTING_CLIENT_STAT ACS "; - let whereString = "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1"; + let fromString = 'FROM ACCOUNTING_CLIENT_STAT ACS ' + let whereString = 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1' - if (employeeId !== undefined) { - fromString = fromString.concat(", NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP "); - whereString = whereString.concat(" AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)", - " AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5", - " AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE", - " AND NQ1.DROPDOWN_CODE=? "); - query.replacements.push(employeeId); - } + if (employeeId !== undefined) { + fromString = fromString.concat(', NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ') + whereString = whereString.concat(' AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)', + ' AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5', + ' AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE', + ' AND NQ1.DROPDOWN_CODE=? ') + query.replacements.push(employeeId) + } - if (clientType !== undefined) { - fromString = fromString.includes("NAME_CONNECTION NC") ? - fromString : fromString.concat(", NAME_CONNECTION NC "); - fromString = fromString.concat(", NAME_QUALITY NQ2 "); + if (clientType !== undefined) { + fromString = fromString.includes('NAME_CONNECTION NC') + ? fromString + : fromString.concat(', NAME_CONNECTION NC ') + fromString = fromString.concat(', NAME_QUALITY NQ2 ') - whereString = whereString.includes("NC.CONNECTION_ID=3") ? - whereString : whereString.concat(" AND NC.CONNECTION_ID=3 "); - whereString = whereString.includes("NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)") ? - whereString : whereString.concat(" AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID) "); - whereString = whereString.concat(" AND NC.NAME_ID=NQ2.NAME_ID", - " AND NQ2.QUALITY_TYPE_ID=3", - " AND NQ2.DROPDOWN_CODE=? "); + whereString = whereString.includes('NC.CONNECTION_ID=3') + ? whereString + : whereString.concat(' AND NC.CONNECTION_ID=3 ') + whereString = whereString.includes('NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)') + ? whereString + : whereString.concat(' AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID) ') + whereString = whereString.concat(' AND NC.NAME_ID=NQ2.NAME_ID', + ' AND NQ2.QUALITY_TYPE_ID=3', + ' AND NQ2.DROPDOWN_CODE=? ') - query.replacements.push(clientType.toUpperCase()); - } + query.replacements.push(clientType.toUpperCase()) + } - if (countryLabel !== undefined) { - fromString = fromString.concat(", ACCOUNTING_NAME AN "); - whereString = whereString.concat(" AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? "); - query.replacements.push(countryLabel); - } + if (countryLabel !== undefined) { + fromString = fromString.concat(', ACCOUNTING_NAME AN ') + whereString = whereString.concat(' AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? ') + query.replacements.push(countryLabel) + } - if (ageOfAccount !== undefined) { - switch (ageOfAccount) { - case "<30": - query.queryString = "SELECT ACS.DUE_CURRENT, 0 as 'DUE_1_MONTH', 0 as 'DUE_2_MONTH', 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH "; - break; - case "30-60": - query.queryString = "SELECT 0 as 'DUE_CURRENT', ACS.DUE_1_MONTH, 0 as 'DUE_2_MONTH', 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH "; - break; - case "60-90": - query.queryString = "SELECT 0 as 'DUE_CURRENT', 0 as 'DUE_1_MONTH', ACS.DUE_2_MONTH, 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH "; - break; - case ">90": - query.queryString = "SELECT 0 as 'DUE_CURRENT', 0 as 'DUE_1_MONTH', 0 as 'DUE_2_MONTH', ACS.DUE_3_MONTH, ACS.YEAR_MONTH "; - break; - default: - query.queryString = query.queryString; - } + if (ageOfAccount !== undefined) { + switch (ageOfAccount) { + case '<30': + query.queryString = "SELECT ACS.DUE_CURRENT, 0 as 'DUE_1_MONTH', 0 as 'DUE_2_MONTH', 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH " + break + case '30-60': + query.queryString = "SELECT 0 as 'DUE_CURRENT', ACS.DUE_1_MONTH, 0 as 'DUE_2_MONTH', 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH " + break + case '60-90': + query.queryString = "SELECT 0 as 'DUE_CURRENT', 0 as 'DUE_1_MONTH', ACS.DUE_2_MONTH, 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH " + break + case '>90': + query.queryString = "SELECT 0 as 'DUE_CURRENT', 0 as 'DUE_1_MONTH', 0 as 'DUE_2_MONTH', ACS.DUE_3_MONTH, ACS.YEAR_MONTH " + break } + } - query.queryString = query.queryString.concat(fromString, whereString); + query.queryString = query.queryString.concat(fromString, whereString) - return query; + return query } - diff --git a/server/data_access_layer/daos/user.dao.js b/server/data_access_layer/daos/user.dao.js index 5485fd6..44f2ed7 100644 --- a/server/data_access_layer/daos/user.dao.js +++ b/server/data_access_layer/daos/user.dao.js @@ -1,120 +1,119 @@ -const databases = require("../databases"); -const UserModel = databases['localdb'].users; +const databases = require('../databases') +const UserModel = databases.localdb.users exports.getUserByEmail = async (email, userModel = UserModel) => { - return new Promise((resolve, reject) => { - userModel.findOne({ - where: { - user_email: email - } - }) - .then(async data => { - if (data) resolve(data); - resolve(false); - }) - .catch(err => { - const response = { - status: 500, - message: err.message || "some error occured" - } - reject(response); - }); - }); -}; + return new Promise((resolve, reject) => { + userModel.findOne({ + where: { + user_email: email + } + }) + .then(async data => { + if (data) resolve(data) + resolve(false) + }) + .catch(err => { + const response = { + status: 500, + message: err.message || 'some error occured' + } + reject(response) + }) + }) +} exports.getAllUsers = async (userModel = UserModel) => { - return new Promise((resolve, reject) => { - userModel.findAll() - .then(async data => { - if (data) { - let returnData = []; - for (let u = 0; u < data.length; u++) { - returnData.push({ - email: data[u].dataValues.email, - name: data[u].dataValues.name, - role: data[u].dataValues.role - }); - } - resolve(returnData); - } - resolve(false); + return new Promise((resolve, reject) => { + userModel.findAll() + .then(async data => { + if (data) { + const returnData = [] + for (let u = 0; u < data.length; u++) { + returnData.push({ + email: data[u].dataValues.email, + name: data[u].dataValues.name, + role: data[u].dataValues.role }) - .catch(err => { - const response = { - status: 500, - data: {}, - message: err.message || "some error occured" - } - reject(response); - }); - }); - + } + resolve(returnData) + } + resolve(false) + }) + .catch(err => { + const response = { + status: 500, + data: {}, + message: err.message || 'some error occured' + } + reject(response) + }) + }) } exports.createUser = async (user, userModel = UserModel) => { - return new Promise((resolve, reject) => { - userModel.create(user) - .then(async data => { - if (data) { - let returnData = { - email: data.dataValues.email, - name: data.dataValues.name, - role: data.dataValues.role - }; + return new Promise((resolve, reject) => { + userModel.create(user) + .then(async data => { + if (data) { + const returnData = { + email: data.dataValues.email, + name: data.dataValues.name, + role: data.dataValues.role + } - resolve(returnData); - } - resolve(false); - }).catch(err => { - const response = { - status: 500, - data: {}, - message: err.message || "some error occured" - } - reject(response); - }) - }); -}; + resolve(returnData) + } + resolve(false) + }).catch(err => { + const response = { + status: 500, + data: {}, + message: err.message || 'some error occured' + } + reject(response) + }) + }) +} exports.updateUser = async (user, userModel = UserModel) => { - return new Promise((resolve, reject) => { - userModel.update(user, - { - where: { email: user.email }, - individualHooks: true - }) - .then(async data => { - if (data) { - resolve("User modified successfully."); - } - resolve("User was not updated."); - }) - .catch(err => { - const response = { - status: 500, - data: {}, - message: err.message || "some error occured" - } - reject(response); - }); - }); + return new Promise((resolve, reject) => { + userModel.update(user, + { + where: { email: user.email }, + individualHooks: true + }) + .then(async data => { + if (data) { + resolve('User modified successfully.') + } + resolve('User was not updated.') + }) + .catch(err => { + const response = { + status: 500, + data: {}, + message: err.message || 'some error occured' + } + reject(response) + }) + }) } exports.deleteUser = async (email, userModel = UserModel) => { - return new Promise((resolve, reject) => { - userModel.destroy({ where: { email: email } }) - .then(async data => { - if (data) { - resolve("User deleted successfully."); - } - resolve("User has failed to be deleted."); - }) - .catch(err => { - const response = { - status: 500, - message: err.message || "some error occured" - } - reject(response); - }); - }); -} \ No newline at end of file + return new Promise((resolve, reject) => { + userModel.destroy({ where: { email: email } }) + .then(async data => { + if (data) { + resolve('User deleted successfully.') + } + resolve('User has failed to be deleted.') + }) + .catch(err => { + const response = { + status: 500, + message: err.message || 'some error occured' + } + reject(response) + }) + }) +} diff --git a/server/data_access_layer/databases.js b/server/data_access_layer/databases.js index 33bea9c..8a43136 100644 --- a/server/data_access_layer/databases.js +++ b/server/data_access_layer/databases.js @@ -1,22 +1,22 @@ -const Sequelize = require("sequelize"); -const env = 'development' || 'production'; +const Sequelize = require('sequelize') +const env = 'development' || 'production' // Load the configuration for the dbs -const config = require('./db.config')[env]; +const config = require('./db.config')[env] -const db = {}; +const db = {} -const databases = Object.keys(config.databases); +const databases = Object.keys(config.databases) for (let i = 0; i < databases.length; i++) { - let database = databases[i]; - let dbPath = config.databases[database]; + const database = databases[i] + const dbPath = config.databases[database] db[database] = new Sequelize(dbPath.DB, dbPath.USER, dbPath.PASSWORD, { host: dbPath.HOST || 'localhost', port: dbPath.port, dialect: dbPath.dialect, - dialectModule: dbPath.dialectModule || "", + dialectModule: dbPath.dialectModule || '', timezone: dbPath.timezone || '+00:00', pool: { max: dbPath.pool ? dbPath.pool.max : 5, @@ -31,21 +31,19 @@ db.Sequelize = Sequelize; // Add any tables to the database here // Add any tables to the local database here -[db['localdb'].users, -db['localdb'].chartReports, -db['localdb'].chartReportsData, -db['localdb'].performanceReport, -db['localdb'].reportTypes, -db['localdb'].recipients, -db['localdb'].reportTypeRecipients] = require("./models/localdb/localdb.model")(db['localdb'], Sequelize); +[db.localdb.users, + db.localdb.chartReports, + db.localdb.chartReportsData, + db.localdb.performanceReport, + db.localdb.reportTypes, + db.localdb.recipients, + db.localdb.reportTypeRecipients] = require('./models/localdb/localdb.model')(db.localdb, Sequelize) // patricia database tables -db['mssql_pat'].employees = require("./models/mssql_pat/employee.model")(db['mssql_pat'], Sequelize); +db.mssql_pat.employees = require('./models/mssql_pat/employee.model')(db.mssql_pat, Sequelize) // Bosco database tables - - db.sync = async (database, options) => { await db[database].sync(options) .then(async () => { @@ -62,10 +60,10 @@ db.sync = async (database, options) => { name: 'JC Benoit', role: 'employee' }], - { - validate: true, - individualHooks: true - }); + { + validate: true, + individualHooks: true + }) }) .then(async (data) => { await db[database].chartReports.bulkCreate([ @@ -110,115 +108,114 @@ db.sync = async (database, options) => { accountType: 'Payable', user_user_id: data[1].userId } - ]); + ]) }) .then(async () => { - let performanceReports = await db['localdb'].performanceReport.bulkCreate([ - { - averageCollectionDay: "32", - annualBillingObjective: "50000", - monthlyBillingObjective: "2000", - annualBillingNumber: "4500", - monthlyBillingNumber: "125", - projectedBonus: "750" - }, - { - averageCollectionDay: "32", - annualBillingObjective: "40000", - monthlyBillingObjective: "3000", - annualBillingNumber: "7500", - monthlyBillingNumber: "333", - projectedBonus: "750" + const performanceReports = await db.localdb.performanceReport.bulkCreate([ + { + averageCollectionDay: '32', + annualBillingObjective: '50000', + monthlyBillingObjective: '2000', + annualBillingNumber: '4500', + monthlyBillingNumber: '125', + projectedBonus: '750' + }, + { + averageCollectionDay: '32', + annualBillingObjective: '40000', + monthlyBillingObjective: '3000', + annualBillingNumber: '7500', + monthlyBillingNumber: '333', + projectedBonus: '750' } - ]); - return performanceReports; + ]) + return performanceReports }) .then(async () => { - let recipients = await db['localdb'].recipients.bulkCreate([ + const recipients = await db.localdb.recipients.bulkCreate([ { - name: "Charles-André Caron", - email: "charles-andre@benoit-cote.com" + name: 'Charles-André Caron', + email: 'charles-andre@benoit-cote.com' }, { - name: "France Coté", - email: "france@benoit-cote.com" + name: 'France Coté', + email: 'france@benoit-cote.com' }, { - name: "Hilal El Ayoubi", - email: "hilal@benoit-cote.com" + name: 'Hilal El Ayoubi', + email: 'hilal@benoit-cote.com' }, { - name: "Ibrahim Tamer", - email: "ibrahim@benoit-cote.com" + name: 'Ibrahim Tamer', + email: 'ibrahim@benoit-cote.com' }, { - name: "Irina Kostko", - email: "irina@benoit-cote.com" + name: 'Irina Kostko', + email: 'irina@benoit-cote.com' }, { - name: "Ismaël Coulibaly", - email: "ismael@benoit-cote.com" + name: 'Ismaël Coulibaly', + email: 'ismael@benoit-cote.com' }, { - name: "Marc Benoît", - email: "marc@benoit-cote.com" + name: 'Marc Benoît', + email: 'marc@benoit-cote.com' }, { - name: "Mailyne Séïde", - email: "marilyne@benoit-cote.com" + name: 'Mailyne Séïde', + email: 'marilyne@benoit-cote.com' }, { - name: "Martin Roy", - email: "martin@benoit-cote.com" + name: 'Martin Roy', + email: 'martin@benoit-cote.com' }, { - name: "Mathieu Audet", - email: "ma@benoit-cote.com" + name: 'Mathieu Audet', + email: 'ma@benoit-cote.com' }, { - name: "Mathieu Miron", - email: "mathieu@benoit-cote.com" + name: 'Mathieu Miron', + email: 'mathieu@benoit-cote.com' }, { - name: "Michel Sofia", - email: "michel@benoit-cote.com" + name: 'Michel Sofia', + email: 'michel@benoit-cote.com' }, { - name: "Philip Conrad", - email: "phil@benoit-cote.com" + name: 'Philip Conrad', + email: 'phil@benoit-cote.com' }, { - name: "Sabrina Lavoie", - email: "slavoie@benoit-cote.com" + name: 'Sabrina Lavoie', + email: 'slavoie@benoit-cote.com' }, { - name: "Suzanne Antal", - email: "suzanne@benoit-cote.com" + name: 'Suzanne Antal', + email: 'suzanne@benoit-cote.com' } - ]); - return recipients; + ]) + return recipients }) .then(async (recipients) => { - let reportTypes = await db['localdb'].reportTypes.create({ + const reportTypes = await db.localdb.reportTypes.create({ reportTypeName: 'Monthly Employee Performance Report', frequency: 0 - }); - return { reportTypes: reportTypes, recipients: recipients }; + }) + return { reportTypes: reportTypes, recipients: recipients } }) .then(async data => { - let reportTypeRecipients = []; + const reportTypeRecipients = [] for (let i = 0; i < data.recipients.length; i++) { reportTypeRecipients.push({ report_type_id: data.reportTypes.reportTypeId, recipient_id: data.recipients[i].recipientId - }); + }) } - await db['localdb'].reportTypeRecipients.bulkCreate(reportTypeRecipients); + await db.localdb.reportTypeRecipients.bulkCreate(reportTypeRecipients) }) .catch((err) => { - console.log(err.message); - }); + console.log(err.message) + }) } -module.exports = db; - +module.exports = db diff --git a/server/data_access_layer/db.config.js b/server/data_access_layer/db.config.js index 0ce6739..4a09f0f 100644 --- a/server/data_access_layer/db.config.js +++ b/server/data_access_layer/db.config.js @@ -1,42 +1,42 @@ -const tedious = require('tedious'); -require("../../config.js") +const tedious = require('tedious') +require('../../config.js') module.exports = { - development: { - databases: { - localdb: { - HOST: process.env.LOCAL_HOST, - USER: process.env.LOCAL_USER, - PASSWORD: process.env.LOCAL_ROOT_PASSWORD, - DB: process.env.LOCAL_DATABASE, - dialect: "mysql", - timezone: "-05:00", - port: process.env.LOCAL_PORT, - pool: { - max: 5, - min: 0, - acquire: 30000, - idle: 10000 - } - }, - mssql_bosco: { - HOST: "localhost", - USER: "bcdev", - PASSWORD: "coolSecurePassword", - DB: "Bosco reduction", - dialect: "mssql", - dialectModule: tedious, - port: 1433 - }, - mssql_pat: { - HOST: "localhost", - USER: "bcdev", - PASSWORD: "coolSecurePassword", - DB: "Patricia reduction", - dialect: "mssql", - dialectModule: tedious, - port: 1433 - } + development: { + databases: { + localdb: { + HOST: process.env.LOCAL_HOST, + USER: process.env.LOCAL_USER, + PASSWORD: process.env.LOCAL_ROOT_PASSWORD, + DB: process.env.LOCAL_DATABASE, + dialect: 'mysql', + timezone: '-05:00', + port: process.env.LOCAL_PORT, + pool: { + max: 5, + min: 0, + acquire: 30000, + idle: 10000 } + }, + mssql_bosco: { + HOST: 'localhost', + USER: 'bcdev', + PASSWORD: 'coolSecurePassword', + DB: 'Bosco reduction', + dialect: 'mssql', + dialectModule: tedious, + port: 1433 + }, + mssql_pat: { + HOST: 'localhost', + USER: 'bcdev', + PASSWORD: 'coolSecurePassword', + DB: 'Patricia reduction', + dialect: 'mssql', + dialectModule: tedious, + port: 1433 + } } -}; \ No newline at end of file + } +} diff --git a/server/data_access_layer/models/localdb/localdb.model.js b/server/data_access_layer/models/localdb/localdb.model.js index c6dadf0..218c87b 100644 --- a/server/data_access_layer/models/localdb/localdb.model.js +++ b/server/data_access_layer/models/localdb/localdb.model.js @@ -1,316 +1,313 @@ -const bcrypt = require('bcrypt'); +const bcrypt = require('bcrypt') module.exports = (localdb, Sequelize) => { - const User = localdb.define("users", { - userId: { - field: 'user_id', - type: Sequelize.UUID, - defaultValue: Sequelize.UUIDV4, - primaryKey: true - }, - email: { - field: 'user_email', - type: Sequelize.STRING, - allowNull: false, - unique: true - }, - password: { - field: 'user_password', - type: Sequelize.STRING - }, - name: { - field: 'user_name', - type: Sequelize.STRING - }, - role: { - field: 'user_role', - type: Sequelize.STRING - } - - }); - - User.addHook('beforeCreate', async (user) => { - if (user.password) { - const salt = await bcrypt.genSalt(10, 'a'); - user.password = await bcrypt.hash(user.password, salt); - } - }); + const User = localdb.define('users', { + userId: { + field: 'user_id', + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + primaryKey: true + }, + email: { + field: 'user_email', + type: Sequelize.STRING, + allowNull: false, + unique: true + }, + password: { + field: 'user_password', + type: Sequelize.STRING + }, + name: { + field: 'user_name', + type: Sequelize.STRING + }, + role: { + field: 'user_role', + type: Sequelize.STRING + } - User.addHook('beforeUpdate', async (user) => { - if (user.password) { - const salt = await bcrypt.genSalt(10, 'a'); - user.password = await bcrypt.hash(user.password, salt); - } - }); + }) - User.prototype.validPassword = async (password, hash) => { - return bcrypt.compareSync(password, hash); + User.addHook('beforeCreate', async (user) => { + if (user.password) { + const salt = await bcrypt.genSalt(10, 'a') + user.password = await bcrypt.hash(user.password, salt) } + }) - const ChartReport = localdb.define("chart_reports", { - chartReportId: { - field: 'chart_report_id', - type: Sequelize.UUID, - defaultValue: Sequelize.UUIDV4, - primaryKey: true - }, - name: { - field: 'chart_report_name', - type: Sequelize.STRING - }, - startDate: { - field: 'chart_report_start', - type: Sequelize.DATEONLY, - allowNull: false - }, - endDate: { - field: 'chart_report_end', - type: Sequelize.DATEONLY, - allowNull: false - }, - employee1Id: { - field: 'chart_report_emp1', - type: Sequelize.INTEGER, - allowNull: false, - defaultValue: -1 - }, - employee1Name: { - field: 'chart_report_emp1_name', - type: Sequelize.STRING, - defaultValue: 'All' - }, - employee2Id: { - field: 'chart_report_emp2', - type: Sequelize.INTEGER, - defaultValue: null - }, - employee2Name: { - field: 'chart_report_emp2_name', - type: Sequelize.STRING, - defaultValue: null - }, - countryId: { - field: 'chart_report_country_id', - type: Sequelize.STRING, - defaultValue: -1 - }, - country: { - field: 'chart_report_country', - type: Sequelize.STRING, - defaultValue: 'All' - }, - clientType: { - field: 'chart_report_client_type', - type: Sequelize.STRING, - defaultValue: 'Any' - }, - ageOfAccount: { - field: 'chart_report_age_of_account', - type: Sequelize.STRING, - defaultValue: 'All' - }, - accountType: { - field: 'chart_report_account_type', - type: Sequelize.STRING, - defaultValue: 'Receivable' - } - }, - { underscored: true }); - - ChartReport.belongsTo(User, { - foreignKey: { - name: 'user_user_id', - allowNull: false, - }, - onDelete: 'CASCADE' - }); + User.addHook('beforeUpdate', async (user) => { + if (user.password) { + const salt = await bcrypt.genSalt(10, 'a') + user.password = await bcrypt.hash(user.password, salt) + } + }) - const ChartReportData = localdb.define("chart_reports_data", { - year: { - field: 'year', - type: Sequelize.INTEGER - }, - employee: { - field: 'emp', - type: Sequelize.INTEGER, - defaultValue: -1 - }, - january: { - field: 'january', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - february: { - field: 'february', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - march: { - field: 'march', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - april: { - field: 'april', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - may: { - field: 'may', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - june: { - field: 'june', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - july: { - field: 'july', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - august: { - field: 'august', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - september: { - field: 'september', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - october: { - field: 'october', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - november: { - field: 'november', - type: Sequelize.FLOAT, - defaultValue: 0 - }, - december: { - field: 'december', - type: Sequelize.FLOAT, - defaultValue: 0 - } - }); + User.prototype.validPassword = async (password, hash) => { + return bcrypt.compareSync(password, hash) + } - ChartReportData.belongsTo(ChartReport, { - foreignKey: { - name: 'chart_report_id', - allowNull: false - }, - onDelete: 'CASCADE' - }); + const ChartReport = localdb.define('chart_reports', { + chartReportId: { + field: 'chart_report_id', + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + primaryKey: true + }, + name: { + field: 'chart_report_name', + type: Sequelize.STRING + }, + startDate: { + field: 'chart_report_start', + type: Sequelize.DATEONLY, + allowNull: false + }, + endDate: { + field: 'chart_report_end', + type: Sequelize.DATEONLY, + allowNull: false + }, + employee1Id: { + field: 'chart_report_emp1', + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: -1 + }, + employee1Name: { + field: 'chart_report_emp1_name', + type: Sequelize.STRING, + defaultValue: 'All' + }, + employee2Id: { + field: 'chart_report_emp2', + type: Sequelize.INTEGER, + defaultValue: null + }, + employee2Name: { + field: 'chart_report_emp2_name', + type: Sequelize.STRING, + defaultValue: null + }, + countryId: { + field: 'chart_report_country_id', + type: Sequelize.STRING, + defaultValue: -1 + }, + country: { + field: 'chart_report_country', + type: Sequelize.STRING, + defaultValue: 'All' + }, + clientType: { + field: 'chart_report_client_type', + type: Sequelize.STRING, + defaultValue: 'Any' + }, + ageOfAccount: { + field: 'chart_report_age_of_account', + type: Sequelize.STRING, + defaultValue: 'All' + }, + accountType: { + field: 'chart_report_account_type', + type: Sequelize.STRING, + defaultValue: 'Receivable' + } + }, + { underscored: true }) + ChartReport.belongsTo(User, { + foreignKey: { + name: 'user_user_id', + allowNull: false + }, + onDelete: 'CASCADE' + }) - const PerformanceReport = localdb.define("performance_reports", { - performanceReportId: { - field: 'performance_report_id', - type: Sequelize.UUID, - defaultValue: Sequelize.UUIDV4, - primaryKey: true - }, - employeeId: { - field: 'employee_id', - type: Sequelize.INTEGER, - defaultValue: null - }, - averageCollectionDay: { - field: 'average_collection_day', - type: Sequelize.STRING, - defaultValue: null - }, - annualBillingObjective: { - field: 'annual_billing_objective', - type: Sequelize.STRING, - defaultValue: null - }, - monthlyBillingObjective: { - field: 'monthly_billing_objective', - type: Sequelize.STRING, - defaultValue: null - }, - annualBillingNumber: { - field: 'annual_billing_number', - type: Sequelize.STRING, - defaultValue: null - }, - monthlyBillingNumber: { - field: 'monthly_billing_number', - type: Sequelize.STRING, - defaultValue: null - }, - projectedBonus: { - field: 'projected_bonus', - type: Sequelize.STRING, - defaultValue: null - } - }); + const ChartReportData = localdb.define('chart_reports_data', { + year: { + field: 'year', + type: Sequelize.INTEGER + }, + employee: { + field: 'emp', + type: Sequelize.INTEGER, + defaultValue: -1 + }, + january: { + field: 'january', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + february: { + field: 'february', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + march: { + field: 'march', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + april: { + field: 'april', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + may: { + field: 'may', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + june: { + field: 'june', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + july: { + field: 'july', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + august: { + field: 'august', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + september: { + field: 'september', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + october: { + field: 'october', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + november: { + field: 'november', + type: Sequelize.FLOAT, + defaultValue: 0 + }, + december: { + field: 'december', + type: Sequelize.FLOAT, + defaultValue: 0 + } + }) - PerformanceReport.belongsTo(User, { - foreignKey: { - name: 'user_user_id', - allowNull: true, - } - }); + ChartReportData.belongsTo(ChartReport, { + foreignKey: { + name: 'chart_report_id', + allowNull: false + }, + onDelete: 'CASCADE' + }) + const PerformanceReport = localdb.define('performance_reports', { + performanceReportId: { + field: 'performance_report_id', + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + primaryKey: true + }, + employeeId: { + field: 'employee_id', + type: Sequelize.INTEGER, + defaultValue: null + }, + averageCollectionDay: { + field: 'average_collection_day', + type: Sequelize.STRING, + defaultValue: null + }, + annualBillingObjective: { + field: 'annual_billing_objective', + type: Sequelize.STRING, + defaultValue: null + }, + monthlyBillingObjective: { + field: 'monthly_billing_objective', + type: Sequelize.STRING, + defaultValue: null + }, + annualBillingNumber: { + field: 'annual_billing_number', + type: Sequelize.STRING, + defaultValue: null + }, + monthlyBillingNumber: { + field: 'monthly_billing_number', + type: Sequelize.STRING, + defaultValue: null + }, + projectedBonus: { + field: 'projected_bonus', + type: Sequelize.STRING, + defaultValue: null + } + }) - const ReportType = localdb.define("report_types", { - reportTypeId: { - field: 'report_type_id', - type: Sequelize.UUID, - defaultValue: Sequelize.UUIDV4, - primaryKey: true - }, - reportTypeName: { - field: 'report_type_name', - type: Sequelize.STRING - }, - frequency: { - field: 'frequency', - type: Sequelize.INTEGER, - defaultValue: 0 - } - }); + PerformanceReport.belongsTo(User, { + foreignKey: { + name: 'user_user_id', + allowNull: true + } + }) - const Recipients = localdb.define("recipients", { - recipientId: { - field: 'recipient_id', - type: Sequelize.UUID, - defaultValue: Sequelize.UUIDV4, - primaryKey: true - }, - name: { - field: 'recipient_name', - type: Sequelize.STRING - }, - email: { - field: 'recipient_email', - type: Sequelize.STRING - } - }) + const ReportType = localdb.define('report_types', { + reportTypeId: { + field: 'report_type_id', + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + primaryKey: true + }, + reportTypeName: { + field: 'report_type_name', + type: Sequelize.STRING + }, + frequency: { + field: 'frequency', + type: Sequelize.INTEGER, + defaultValue: 0 + } + }) - const ReportTypeRecipients = localdb.define("report_type_recipients", {}); + const Recipients = localdb.define('recipients', { + recipientId: { + field: 'recipient_id', + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + primaryKey: true + }, + name: { + field: 'recipient_name', + type: Sequelize.STRING + }, + email: { + field: 'recipient_email', + type: Sequelize.STRING + } + }) - ReportTypeRecipients.belongsTo(ReportType, { - foreignKey: { - name: 'report_type_id', - allowNull: false - }, - onDelete: 'CASCADE' - }); + const ReportTypeRecipients = localdb.define('report_type_recipients', {}) - ReportTypeRecipients.belongsTo(Recipients, { - foreignKey: { - name: 'recipient_id', - allowNull: false - }, - onDelete: 'CASCADE' - }); + ReportTypeRecipients.belongsTo(ReportType, { + foreignKey: { + name: 'report_type_id', + allowNull: false + }, + onDelete: 'CASCADE' + }) + ReportTypeRecipients.belongsTo(Recipients, { + foreignKey: { + name: 'recipient_id', + allowNull: false + }, + onDelete: 'CASCADE' + }) - return [User, ChartReport, ChartReportData, PerformanceReport, ReportType, Recipients, ReportTypeRecipients]; -}; \ No newline at end of file + return [User, ChartReport, ChartReportData, PerformanceReport, ReportType, Recipients, ReportTypeRecipients] +} diff --git a/server/data_access_layer/models/mssql_bosco/client.model.js b/server/data_access_layer/models/mssql_bosco/client.model.js index 61bec61..c97facc 100644 --- a/server/data_access_layer/models/mssql_bosco/client.model.js +++ b/server/data_access_layer/models/mssql_bosco/client.model.js @@ -1,29 +1,29 @@ -module.exports = (mssql_bosco, DataTypes) => { - const Client = mssql_bosco.define("NAME", { - nameId:{ - field: 'NAME_ID', - type: DataTypes.INTEGER - }, - name1: { - field: 'NAME_1', - type: DataTypes.STRING - }, - name2: { - field: 'NAME_2', - type: DataTypes.STRING - }, - name3: { - field: 'NAME_3', - type: DataTypes.STRING - } +module.exports = (mssqlBosco, DataTypes) => { + const Client = mssqlBosco.define('NAME', { + nameId: { + field: 'NAME_ID', + type: DataTypes.INTEGER }, + name1: { + field: 'NAME_1', + type: DataTypes.STRING + }, + name2: { + field: 'NAME_2', + type: DataTypes.STRING + }, + name3: { + field: 'NAME_3', + type: DataTypes.STRING + } + }, { - modelName: 'Client', - tableName: 'NAME', - underscore: true, - timestamps: false - }); + modelName: 'Client', + tableName: 'NAME', + underscore: true, + timestamps: false + }) - Client.removeAttribute('id'); - return Client; -}; \ No newline at end of file + Client.removeAttribute('id') + return Client +} diff --git a/server/data_access_layer/models/mssql_bosco/name.model.js b/server/data_access_layer/models/mssql_bosco/name.model.js index d92aa5a..de458bd 100644 --- a/server/data_access_layer/models/mssql_bosco/name.model.js +++ b/server/data_access_layer/models/mssql_bosco/name.model.js @@ -1,26 +1,26 @@ -module.exports = (mssql_bosco, DataTypes) => { - const Name = mssql_bosco.define("NAME", { - nameID: { - field: 'NAME_ID', - type: DataTypes.INTEGER, - primaryKey: true - }, - firstName: { - field: 'NAME_1', - type: DataTypes.STRING - }, - lastName: { - field: 'NAME_3', - type: DataTypes.STRING - } +module.exports = (mssqlBosco, DataTypes) => { + const Name = mssqlBosco.define('NAME', { + nameID: { + field: 'NAME_ID', + type: DataTypes.INTEGER, + primaryKey: true }, + firstName: { + field: 'NAME_1', + type: DataTypes.STRING + }, + lastName: { + field: 'NAME_3', + type: DataTypes.STRING + } + }, { - modelName: 'Name', - tableName: 'NAME', - underscore: true, - timestamps: false - }); + modelName: 'Name', + tableName: 'NAME', + underscore: true, + timestamps: false + }) - Name.removeAttribute('id'); - return Name; -}; \ No newline at end of file + Name.removeAttribute('id') + return Name +} diff --git a/server/data_access_layer/models/mssql_pat/employee.model.js b/server/data_access_layer/models/mssql_pat/employee.model.js index 92b0a0f..fdecdb8 100644 --- a/server/data_access_layer/models/mssql_pat/employee.model.js +++ b/server/data_access_layer/models/mssql_pat/employee.model.js @@ -1,30 +1,30 @@ -module.exports = (mssql_pat, DataTypes) => { - const Employee = mssql_pat.define("PERSON", { - firstName: { - field: 'PERSON_FIRST_NAME', - type: DataTypes.STRING - }, - lastName: { - field: 'PERSON_LAST_NAME', - type: DataTypes.STRING - }, - email: { - field: 'EMAIL', - type: DataTypes.STRING, - primaryKey: true - }, - isActive: { - field: 'IS_ACTIV', - type: DataTypes.BOOLEAN - } +module.exports = (mssqlPat, DataTypes) => { + const Employee = mssqlPat.define('PERSON', { + firstName: { + field: 'PERSON_FIRST_NAME', + type: DataTypes.STRING }, + lastName: { + field: 'PERSON_LAST_NAME', + type: DataTypes.STRING + }, + email: { + field: 'EMAIL', + type: DataTypes.STRING, + primaryKey: true + }, + isActive: { + field: 'IS_ACTIV', + type: DataTypes.BOOLEAN + } + }, { - modelName: 'Employee', - tableName: 'PERSON', - underscore: true, - timestamps: false - }); + modelName: 'Employee', + tableName: 'PERSON', + underscore: true, + timestamps: false + }) - Employee.removeAttribute('id'); - return Employee; -}; \ No newline at end of file + Employee.removeAttribute('id') + return Employee +} diff --git a/server/docs/chartReportPDF.js b/server/docs/chartReportPDF.js index 97c06c2..6635bc9 100644 --- a/server/docs/chartReportPDF.js +++ b/server/docs/chartReportPDF.js @@ -1,197 +1,186 @@ module.exports = (data, averagesList) => { - // calculate length of for loop for creating averages on the table - const calculatedLength = data.employee2Name === null ? averagesList.length : averagesList.length / 2 - const today = new Date(); - - const months = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December" - ]; - - const colors = [ - 'rgb(255, 139, 77)', - 'rgb(127, 101, 129)', - 'rgb(255, 224, 51)', - 'rgb(65, 144, 164)', - 'rgb(46, 234, 68)' - ]; - - const compareColors = [ - 'rgba(255, 192, 159, 0.6)', - 'rgba(191, 175, 192, 0.6)', - 'rgba(255, 238, 147, 0.6)', - 'rgba(160, 206, 217, 0.6)', - 'rgba(173, 247, 182, 0.6)' - ] - - const colorBorders = [ - 'rgb(255, 139, 77)', - 'rgb(127, 101, 129)', - 'rgb(255, 224, 51)', - 'rgb(65, 144, 164)', - 'rgb(46, 234, 68)' - ] - - const formatTimes = (time) => { - if(time < 10) time = "0" + time; - return time; + // calculate length of for loop for creating averages on the table + const calculatedLength = data.employee2Name === null ? averagesList.length : averagesList.length / 2 + const today = new Date() + + const months = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ] + + const colors = [ + 'rgb(255, 139, 77)', + 'rgb(127, 101, 129)', + 'rgb(255, 224, 51)', + 'rgb(65, 144, 164)', + 'rgb(46, 234, 68)' + ] + + const compareColors = [ + 'rgba(255, 192, 159, 0.6)', + 'rgba(191, 175, 192, 0.6)', + 'rgba(255, 238, 147, 0.6)', + 'rgba(160, 206, 217, 0.6)', + 'rgba(173, 247, 182, 0.6)' + ] + + const colorBorders = [ + 'rgb(255, 139, 77)', + 'rgb(127, 101, 129)', + 'rgb(255, 224, 51)', + 'rgb(65, 144, 164)', + 'rgb(46, 234, 68)' + ] + + const formatTimes = (time) => { + if (time < 10) time = '0' + time + return time + } + + const getDateOrdinal = (date) => { + switch (date) { + case 1: return 'st' + case 2: return 'nd' + case 3: return 'rd' + default: return 'th' } + } - const getDateOrdinal = (date) => { - switch(date) { - case 1: return "st"; - case 2: return "nd"; - case 3: return "rd"; - default: return "th"; - } - } + const getFullDateFormatted = (date) => { + let str = '' + return str.concat( - const getFullDateFormatted = (date) => { - let str = ""; - return str = str.concat( + /* month */ months[date.getMonth()], ' ', + /* date */ formatTimes(date.getDate()), + /* date ordinal */ `${getDateOrdinal(date.getDate())}, `, + /* year */ date.getFullYear(), ' - ', - /* month */ months[date.getMonth()], " ", - /* date */ formatTimes(date.getDate()), - /* date ordinal */ `${getDateOrdinal(date.getDate())}, `, - /* year */ date.getFullYear(), " - ", + /* hours */ formatTimes(date.getHours()), ':', + /* minutes */ formatTimes(date.getMinutes()), ':', + /* seconds */ formatTimes(date.getSeconds())) + } - /* hours */ formatTimes(date.getHours()), ":", - /* minutes */ formatTimes(date.getMinutes()), ":", - /* seconds */ formatTimes(date.getSeconds())); - } + const buildChartDatasets = () => { + let str = '' + let counter = 0 + let counterCompare = 0 + let labelCompare = '' - const buildChartDatasets = () => { - let str = ""; - let counter = 0; - let counterCompare = 0; - let labelCompare = ""; - - for(let i = 0; i < averagesList.length; i++) { - - if(counter === 5) - counter = 0; - - if(counterCompare === 5) - counterCompare = 0; - - if(averagesList[i].employee !== -1) { - labelCompare = " - emp"; - } - - str = str.concat("{label: '", averagesList[i].year, labelCompare, "',", - "data: ["); - - for(let j = 0; j < averagesList[i].data.length; j++) { - str = str.concat(averagesList[i].data[j]); - - if(j + 1 !== averagesList[i].data.length) - str = str.concat(","); - else - str = str.concat("],"); - } - - if(averagesList[i].employee !== -1) { - str = str.concat("backgroundColor: '", colors[counterCompare], "'}"); - counterCompare++ - } - else { - str = str.concat("backgroundColor: '", compareColors[counter], "',", - "borderColor: '", colorBorders[counter], "',", - "borderWidth: ", 1, "}"); - counter++ - } - - if(i + 1 !== averagesList.length) - str = str.concat(","); - } + for (let i = 0; i < averagesList.length; i++) { + if (counter === 5) { counter = 0 } + + if (counterCompare === 5) { counterCompare = 0 } + + if (averagesList[i].employee !== -1) { + labelCompare = ' - emp' + } + + str = str.concat("{label: '", averagesList[i].year, labelCompare, "',", + 'data: [') + + for (let j = 0; j < averagesList[i].data.length; j++) { + str = str.concat(averagesList[i].data[j]) + + if (j + 1 !== averagesList[i].data.length) { str = str.concat(',') } else { str = str.concat('],') } + } + + if (averagesList[i].employee !== -1) { + str = str.concat("backgroundColor: '", colors[counterCompare], "'}") + counterCompare++ + } else { + str = str.concat("backgroundColor: '", compareColors[counter], "',", + "borderColor: '", colorBorders[counter], "',", + 'borderWidth: ', 1, '}') + counter++ + } - return str; + if (i + 1 !== averagesList.length) { str = str.concat(',') } } - const buildTable = () => { - let str = ""; + return str + } - for(let i = 0; i < months.length; i++) { - let averageNormal = 0, - averageCounter = 0, - compareAverage = 0, - compareAverageCounter = 0, - counter = 0, - compareCounter = 0; + const buildTable = () => { + let str = '' - // creating table row - str = str.concat("", "", months[i], "") + for (let i = 0; i < months.length; i++) { + let averageNormal = 0 + let averageCounter = 0 + let compareAverage = 0 + let compareAverageCounter = 0 + let counter = 0 + let compareCounter = 0 - for(let j = 0; j < averagesList.length; j++) { - if(counter === 5) - counter = 0; + // creating table row + str = str.concat('', "", months[i], '') - if(compareCounter === 5) - compareCounter = 0; + for (let j = 0; j < averagesList.length; j++) { + if (counter === 5) { counter = 0 } - // creating table cell... - // for employee - if(averagesList[j].employee !== -1) { - str = str.concat("", averagesList[j].data[i] !== 0 ? averagesList[j].data[i] : "N/A", "") - counter++ + if (compareCounter === 5) { compareCounter = 0 } - if(averagesList[j].data[i] !== 0) { - averageNormal += averagesList[j].data[i]; - averageCounter++ - } + // creating table cell... + // for employee + if (averagesList[j].employee !== -1) { + str = str.concat("", averagesList[j].data[i] !== 0 ? averagesList[j].data[i] : 'N/A', '') + counter++ - if((j + 1) === calculatedLength || ((j + 1) === averagesList.length && data.employee2Name !== null)) { - averageNormal /= averageCounter; - str = str.concat("", averageNormal.toFixed(2), "") - } - } - // for All - else { - str = str.concat("", averagesList[j].data[i] !== 0 ? averagesList[j].data[i] : "N/A", "") - compareCounter++ - - if(averagesList[j].data[i] !== 0) { - compareAverage += averagesList[j].data[i]; - compareAverageCounter++ - } - - if((j + 1) === calculatedLength) { - compareAverage /= compareAverageCounter; - str = str.concat("", compareAverage.toFixed(2), "") - } + if (averagesList[j].data[i] !== 0) { + averageNormal += averagesList[j].data[i] + averageCounter++ + } - } - } - str = str.concat("") + if ((j + 1) === calculatedLength || ((j + 1) === averagesList.length && data.employee2Name !== null)) { + averageNormal /= averageCounter + str = str.concat("", averageNormal.toFixed(2), '') + } + } + // for All + else { + str = str.concat("", averagesList[j].data[i] !== 0 ? averagesList[j].data[i] : 'N/A', '') + compareCounter++ + + if (averagesList[j].data[i] !== 0) { + compareAverage += averagesList[j].data[i] + compareAverageCounter++ + } + + if ((j + 1) === calculatedLength) { + compareAverage /= compareAverageCounter + str = str.concat("", compareAverage.toFixed(2), '') + } } - return str; + } + str = str.concat('') } + return str + } - const buildTableHead = () => { - let str = ""; + const buildTableHead = () => { + let str = '' - for(let i = 0; i < averagesList.length; i++) { - str = str.concat("", averagesList[i].year, "") + for (let i = 0; i < averagesList.length; i++) { + str = str.concat('', averagesList[i].year, '') - if(i + 1 === calculatedLength || i + 1 === averagesList.length && data.employee2Name !== null) { - str = str.concat("AVERAGE") - } - } - return str; + if (i + 1 === calculatedLength || (i + 1 === averagesList.length && data.employee2Name !== null)) { + str = str.concat('AVERAGE') + } } + return str + } - let html = - /*html*/ + const html = + /* html */ ` @@ -216,7 +205,7 @@ module.exports = (data, averagesList) => { height: 11in; margin: 0 auto; color: #001028; - background: #FFFFFF; + background: #fff; font-family: Arial, sans-serif; font-size: 12px; font-family: Arial; @@ -374,11 +363,11 @@ module.exports = (data, averagesList) => {
Start Date ${months[parseInt(data.startDate.substring(5, 7)) - 1]} ${data.startDate.substring(0, 4)}
End Date ${months[parseInt(data.endDate.substring(5, 7)) - 1]} ${data.endDate.substring(0, 4)}
Employee ${data.employee1Name}
- ${data.employee2Name !== null ? `
Compared With ${data.employee2Name}
` : ""} + ${data.employee2Name !== null ? `
Compared With ${data.employee2Name}
` : ''}
Age of Account ${data.ageOfAccount}
Account Type ${data.accountType}
Country ${data.country}
-
Client Type ${data.clientType === "Corr" ? "Correspondant" : data.clientType === "All" ? "All" : "Direct"}
+
Client Type ${data.clientType === 'Corr' ? 'Correspondant' : data.clientType === 'All' ? 'All' : 'Direct'}
@@ -421,7 +410,7 @@ module.exports = (data, averagesList) => { const myChart = new Chart(ctx, { type: 'bar', data: { - labels: [${months.map((m) => {return "'" + m + "'"})}], + labels: [${months.map((m) => { return "'" + m + "'" })}], datasets: [${buildChartDatasets()}] }, options: { @@ -456,11 +445,14 @@ module.exports = (data, averagesList) => { fontSize: 15 } }] + }, + animation: { + duration: 0 } } }); ` - return html -} \ No newline at end of file + return html +} diff --git a/server/routes/invoice.routes.js b/server/routes/invoice.routes.js index f815026..2c54360 100644 --- a/server/routes/invoice.routes.js +++ b/server/routes/invoice.routes.js @@ -1,12 +1,11 @@ -const { response } = require("express"); -const invoiceController = require("../controllers/invoice.controller"); -let router = require("express").Router(); -const authService = require('../services/auth.service'); +const invoiceController = require('../controllers/invoice.controller') +const router = require('express').Router() +const authService = require('../services/auth.service') -router.get("/defaultChartAndTable/:startDate/:endDate", authService.authenticateToken, invoiceController.getAverages); +router.get('/defaultChartAndTable/:startDate/:endDate', authService.authenticateToken, invoiceController.getAverages) -router.get("/getCountries", authService.authenticateToken, invoiceController.getCountriesName); +router.get('/getCountries', authService.authenticateToken, invoiceController.getCountriesName) -router.get("/employees", authService.authenticateToken, invoiceController.getAllEmployeesDropdown); +router.get('/employees', authService.authenticateToken, invoiceController.getAllEmployeesDropdown) -module.exports = router; \ No newline at end of file +module.exports = router diff --git a/server/routes/report.routes.js b/server/routes/report.routes.js index 6358a01..26a242d 100644 --- a/server/routes/report.routes.js +++ b/server/routes/report.routes.js @@ -1,21 +1,21 @@ -const reportController = require("../controllers/report.controller.js"); -let router = require("express").Router(); -const authService = require("../services/auth.service"); +const reportController = require('../controllers/report.controller.js') +const router = require('express').Router() +const authService = require('../services/auth.service') // Chart Reports routes -router.get("/chartReport", authService.authenticateToken, reportController.getChartReportsByUserId); +router.get('/chartReport', authService.authenticateToken, reportController.getChartReportsByUserId) -router.post("/chartReport", authService.authenticateToken, reportController.createChartReport); +router.post('/chartReport', authService.authenticateToken, reportController.createChartReport) -router.get("/fetchPdf", authService.authenticateToken, reportController.fetchChartReportPDF); +router.get('/fetchPdf', authService.authenticateToken, reportController.fetchChartReportPDF) -router.post("/createPdf", authService.authenticateToken, reportController.createChartReportPDF); +router.post('/createPdf', authService.authenticateToken, reportController.createChartReportPDF) -router.delete("/delete/:chartReportId", authService.authenticateToken, reportController.deleteChartReport); +router.delete('/delete/:chartReportId', authService.authenticateToken, reportController.deleteChartReport) // Report Types routes -router.get("/performanceReport", authService.authenticateToken, reportController.getPerformanceReportsOfAllUsers); +router.get('/performanceReport', authService.authenticateToken, reportController.getPerformanceReportsOfAllUsers) -router.get("/reportTypes", authService.authenticateToken, reportController.getReportTypesWithRecipients); +router.get('/reportTypes', authService.authenticateToken, reportController.getReportTypesWithRecipients) -module.exports = router; \ No newline at end of file +module.exports = router diff --git a/server/routes/user.routes.js b/server/routes/user.routes.js index ebe2c62..25d38ac 100644 --- a/server/routes/user.routes.js +++ b/server/routes/user.routes.js @@ -1,27 +1,26 @@ -const userController = require("../controllers/user.controller"); -let router = require("express").Router(); -const authService = require('../services/auth.service'); -const empService = require('../services/emp.service'); - +const userController = require('../controllers/user.controller') +const router = require('express').Router() +const authService = require('../services/auth.service') +const empService = require('../services/emp.service') // Create new User -router.post("/", authService.authenticateToken, empService.checkEmail, userController.create); +router.post('/', authService.authenticateToken, empService.checkEmail, userController.create) // Fetch all users -router.get("/", authService.authenticateToken, userController.findAll); +router.get('/', authService.authenticateToken, userController.findAll) // Authenticate user -router.post("/authenticate", userController.authenticateUserWithEmail); +router.post('/authenticate', userController.authenticateUserWithEmail) // Refresh JWT -router.post("/refresh", authService.refreshToken); +router.post('/refresh', authService.refreshToken) -router.delete("/logout", authService.logout); +router.delete('/logout', authService.logout) -// Send users modification -router.put(`/modify/:email`, authService.authenticateToken, userController.modifyUser); +// Send users modification +router.put('/modify/:email', authService.authenticateToken, userController.modifyUser) // Delete user selected -router.delete("/delete/:email", authService.authenticateToken, userController.deleteUser); +router.delete('/delete/:email', authService.authenticateToken, userController.deleteUser) -module.exports = router; +module.exports = router diff --git a/server/server.js b/server/server.js index 0b9d9ef..ef5af36 100644 --- a/server/server.js +++ b/server/server.js @@ -1,9 +1,9 @@ -const databases = require('./data_access_layer/databases'); -const makeApp = require('./app'); -const PORT = process.env.PORT || 3001; +const databases = require('./data_access_layer/databases') +const makeApp = require('./app') +const PORT = process.env.PORT || 3001 -const app = makeApp(databases); +const app = makeApp(databases) app.listen(PORT, () => { - console.log(`Server listening on ${PORT}`); -}); \ No newline at end of file + console.log(`Server listening on ${PORT}`) +}) diff --git a/server/services/auth.service.js b/server/services/auth.service.js index eaf707c..cb23922 100644 --- a/server/services/auth.service.js +++ b/server/services/auth.service.js @@ -1,74 +1,74 @@ -const jwt = require('jsonwebtoken'); -require("../../config.js") +const jwt = require('jsonwebtoken') +require('../../config.js') const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET -// Refresh tokens should be stored in the db or -//the expiry date of each user's refresh token can be stored with the user +// Refresh tokens should be stored in the db or +// the expiry date of each user's refresh token can be stored with the user let refreshTokens = [] exports.getTokens = (user) => { // Generate JWTs for the authenticated user - const accessToken = jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '900s' }); - const refreshToken = jwt.sign(user, REFRESH_TOKEN_SECRET); + const accessToken = jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '900s' }) + const refreshToken = jwt.sign(user, REFRESH_TOKEN_SECRET) - //it acts as the database for the token, of course it is going to be different - refreshTokens.push(refreshToken); + // it acts as the database for the token, of course it is going to be different + refreshTokens.push(refreshToken) - return [accessToken, refreshToken]; -}; + return [accessToken, refreshToken] +} exports.authenticateToken = async (req, res, next) => { - const authHeader = req.headers['authorization']; - const token = authHeader && authHeader.split(' ')[1]; + const authHeader = req.headers.authorization + const token = authHeader && authHeader.split(' ')[1] if (token == null) { - return res.sendStatus(403); + return res.sendStatus(403) } jwt.verify(token, ACCESS_TOKEN_SECRET, (err, user) => { if (err) { - return res.sendStatus(401); + return res.sendStatus(401) } - req.user = user; - next(); - }); -}; + req.user = user + next() + }) +} exports.refreshToken = async (req, res) => { - const authHeader = req.headers['authorization']; - const token = authHeader && authHeader.split(' ')[1]; + const authHeader = req.headers.authorization + const token = authHeader && authHeader.split(' ')[1] if (token == null) { - return res.sendStatus(401); + return res.sendStatus(401) } if (!refreshTokens.includes(token)) { - return res.sendStatus(403); + return res.sendStatus(403) } jwt.verify(token, REFRESH_TOKEN_SECRET, (err, user) => { if (err) { - return res.sendStatus(403); + return res.sendStatus(403) } - const accessToken = jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '900s' }); + const accessToken = jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '900s' }) return res - .header('authorization', accessToken).send(); - }); -}; + .header('authorization', accessToken).send() + }) +} exports.logout = async (req, res) => { - const authHeader = req.headers['authorization']; - const token = authHeader && authHeader.split(' ')[1]; + const authHeader = req.headers.authorization + const token = authHeader && authHeader.split(' ')[1] - if (token == null || !refreshTokens.find(t => t == token)) { - return res.sendStatus(403); + if (token === null || !refreshTokens.find(t => t === token)) { + return res.sendStatus(403) } - refreshTokens = refreshTokens.filter(t => t != token); + refreshTokens = refreshTokens.filter(t => t !== token) - return res.sendStatus(204); -}; + return res.sendStatus(204) +} // for testing purposes exports.setRefreshTokens = (rToken) => { - refreshTokens = [rToken]; -}; \ No newline at end of file + refreshTokens = [rToken] +} diff --git a/server/services/emp.service.js b/server/services/emp.service.js index 2e05bd5..0f5590b 100644 --- a/server/services/emp.service.js +++ b/server/services/emp.service.js @@ -1,44 +1,43 @@ -const EmpDAO = require("../data_access_layer/daos/emp.dao"); -const NameDAO = require("../data_access_layer/daos/name.dao"); +const EmpDAO = require('../data_access_layer/daos/emp.dao') +const NameDAO = require('../data_access_layer/daos/name.dao') exports.checkEmail = async (req, res, next) => { - EmpDAO.getEmployeeByEmail(req.body.email).then(async data => { - if(data) { - req.emp = data; - return next() - } - const response = { - status: 400, - message: "Employee email doesn't exist." - } - return res.status(400).send(response); - }) + EmpDAO.getEmployeeByEmail(req.body.email).then(async data => { + if (data) { + req.emp = data + return next() + } + const response = { + status: 400, + message: "Employee email doesn't exist." + } + return res.status(400).send(response) + }) .catch(err => { - const response = { - status: 500, - message: err.message || "some error occured" - } - return res.status(500).send(response); + const response = { + status: 500, + message: err.message || 'some error occured' + } + return res.status(500).send(response) }) - -}; +} exports.getAllEmployees = async (name = undefined) => { - return new Promise(async (resolve, reject) => { - await NameDAO.getAllEmployeeNames().then(async data => { - if(data) { - if(name === undefined) resolve(data); - else { - for(let i = 0; i < data.length; i++) { - if(name === data[i].name) { - resolve([data[i]]); - } - } - } + return new Promise(async (resolve, reject) => { + await NameDAO.getAllEmployeeNames().then(async data => { + if (data) { + if (name === undefined) resolve(data) + else { + for (let i = 0; i < data.length; i++) { + if (name === data[i].name) { + resolve([data[i]]) } - resolve(false); - }).catch(err => { - reject(err); - }); - }); -} \ No newline at end of file + } + } + } + resolve(false) + }).catch(err => { + reject(err) + }) + }) +} diff --git a/server/services/invoice.service.js b/server/services/invoice.service.js index 42d683c..b3c72cf 100644 --- a/server/services/invoice.service.js +++ b/server/services/invoice.service.js @@ -1,244 +1,241 @@ -const TransacStatDao = require("../data_access_layer/daos/transac_stat.dao"); -const InvoiceAffectDao = require("../data_access_layer/daos/invoice_affect.dao"); -const ClientDao = require("../data_access_layer/daos/name.dao"); -const CountryDao = require("../data_access_layer/daos/country.dao"); +const TransacStatDao = require('../data_access_layer/daos/transac_stat.dao') +const InvoiceAffectDao = require('../data_access_layer/daos/invoice_affect.dao') +const ClientDao = require('../data_access_layer/daos/name.dao') +const CountryDao = require('../data_access_layer/daos/country.dao') exports.getAverages = async (startDateStr, endDateStr, employeeId = undefined, clientType = undefined, countryLabel = undefined, countryCode = undefined, ageOfAccount = undefined) => { - return new Promise(async (resolve, reject) => { - // List to hold the final response - let returnData = []; - - // Lists used for main calculation - let averagesList = []; - let totalDuesList = []; - let billedList = []; - - // Lists used for the Clients Table - let clientIDList = []; - let clientList = []; - - - let startDate = new Date(`${startDateStr} 00:00:00`); - let endDate = new Date(`${endDateStr} 00:00:00`); - if (startDate > endDate) { - const response = { - status: 400, - message: "Invalid date order." - } - reject(response); - } + return new Promise(async (resolve, reject) => { + // List to hold the final response + const returnData = [] + + // Lists used for main calculation + const averagesList = [] + let totalDuesList = [] + let billedList = [] + + // Lists used for the Clients Table + let clientIDList = [] + let clientList = [] + + const startDate = new Date(`${startDateStr} 00:00:00`) + const endDate = new Date(`${endDateStr} 00:00:00`) + if (startDate > endDate) { + const response = { + status: 400, + message: 'Invalid date order.' + } + reject(response) + } - // make list of yearMonth [201911,202001,202002,...] to select dues - let yearMonthList = this.getYearMonth(startDateStr, endDateStr); - - // Get the list of total dues for each month - await this.getDues(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount).then(async data => { - totalDuesList = data; - }).catch(err => { - reject(err); - }); - - // prepare startDate to get billed amount for each month - startDate.setMonth(startDate.getMonth() - 12); - let theMonth = startDate.getMonth() + 1; - startDateStr = startDate.getFullYear() + "-" + theMonth + "-01"; - - // Get list of amount billed for each month (previous 12 months) - await this.getBilled(startDateStr, endDateStr, yearMonthList, employeeId, clientType, countryCode, ageOfAccount).then(async data => { - billedList = data.billedList; - clientIDList = data.clientIDList; - }).catch(err => { - reject(err); - }); - - // Get List of Client Id, Name, Country, and grading - await this.getClientInformation(clientIDList.length === 0 ? [-1000] : clientIDList) - .then(async data => { - clientList = data; - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch clients." - }; - reject(response); - }); - - // Populate average list with average for each month - if (totalDuesList.length === 0 || billedList.length === 0) return; - let counter = 0; - yearMonthList.forEach(ym => { - let average = totalDuesList[counter].totalDues / billedList[counter].billed * 365; - let year = parseInt(ym.toString().substring(0, 4)); - averagesList.push({ - month: ym, - average: average.toFixed(2), - group: year - }); - counter++; - }); - - // Group averages List by year - const groupedAverages = averagesList.reduce((groups, item) => ({ - ...groups, - [item.group]: [...(groups[item.group] || []), item] - }), {}); - - returnData.push({ - chart: groupedAverages, - table: clientList - }); - - resolve(returnData); - }); + // make list of yearMonth [201911,202001,202002,...] to select dues + const yearMonthList = this.getYearMonth(startDateStr, endDateStr) + + // Get the list of total dues for each month + await this.getDues(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount).then(async data => { + totalDuesList = data + }).catch(err => { + reject(err) + }) + + // prepare startDate to get billed amount for each month + startDate.setMonth(startDate.getMonth() - 12) + const theMonth = startDate.getMonth() + 1 + startDateStr = startDate.getFullYear() + '-' + theMonth + '-01' + + // Get list of amount billed for each month (previous 12 months) + await this.getBilled(startDateStr, endDateStr, yearMonthList, employeeId, clientType, countryCode, ageOfAccount).then(async data => { + billedList = data.billedList + clientIDList = data.clientIDList + }).catch(err => { + reject(err) + }) + + // Get List of Client Id, Name, Country, and grading + await this.getClientInformation(clientIDList.length === 0 ? [-1000] : clientIDList) + .then(async data => { + clientList = data + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch clients.' + } + reject(response) + }) + + // Populate average list with average for each month + if (totalDuesList.length === 0 || billedList.length === 0) return + let counter = 0 + yearMonthList.forEach(ym => { + const average = totalDuesList[counter].totalDues / billedList[counter].billed * 365 + const year = parseInt(ym.toString().substring(0, 4)) + averagesList.push({ + month: ym, + average: average.toFixed(2), + group: year + }) + counter++ + }) + + // Group averages List by year + const groupedAverages = averagesList.reduce((groups, item) => ({ + ...groups, + [item.group]: [...(groups[item.group] || []), item] + }), {}) + + returnData.push({ + chart: groupedAverages, + table: clientList + }) + + resolve(returnData) + }) } exports.getClientInformation = async (clientIDList) => { - return new Promise(async (resolve, reject) => { - await ClientDao.getClientsInClientIdList(clientIDList) - .then(async data => { - if (data) resolve(data); - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch clients." - }; - reject(response); - }) - }); + return new Promise(async (resolve, reject) => { + await ClientDao.getClientsInClientIdList(clientIDList) + .then(async data => { + if (data) resolve(data) + resolve(false) + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch clients.' + } + reject(response) + }) + }) } exports.getDues = async (yearMonthList, employeeId = undefined, clientType = undefined, countryLabel = undefined, ageOfAccount = undefined) => { - return new Promise(async (resolve, reject) => { - let totalDuesList = []; - - await TransacStatDao.getTransactionsStatByYearMonth(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount) - .then(async data => { - if (data) { - yearMonthList.forEach(ym => { - let totalDues = 0; - data.forEach(e => { - if (e.yearMonth === ym) { - totalDues += (e.dueCurrent + e.due1Month + e.due2Month + e.due3Month); - } - }); - totalDuesList.push({ - month: ym.toString(), - totalDues: totalDues.toFixed(2) - }); - }); - resolve(totalDuesList); - } - resolve(false); + return new Promise(async (resolve, reject) => { + const totalDuesList = [] + + await TransacStatDao.getTransactionsStatByYearMonth(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount) + .then(async data => { + if (data) { + yearMonthList.forEach(ym => { + let totalDues = 0 + data.forEach(e => { + if (e.yearMonth === ym) { + totalDues += (e.dueCurrent + e.due1Month + e.due2Month + e.due3Month) + } }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Could not get dues." - }; - reject(response); + totalDuesList.push({ + month: ym.toString(), + totalDues: totalDues.toFixed(2) }) - }); + }) + resolve(totalDuesList) + } + resolve(false) + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not get dues.' + } + reject(response) + }) + }) } exports.getBilled = async (startDateStr, endDateStr, yearMonthList, employeeId = undefined, clientType = undefined, countryCode = undefined, ageOfAccount = undefined) => { + const startDate = new Date(parseInt(startDateStr.substring(5, 7)) + ' ' + parseInt(startDateStr.substring(8)) + ' ' + parseInt(startDateStr.substring(0, 4))) + const endDate = new Date(parseInt(endDateStr.substring(5, 7)) + ' ' + parseInt(endDateStr.substring(8)) + ' ' + parseInt(endDateStr.substring(0, 4))) + endDate.setMonth(endDate.getMonth() - (yearMonthList.length - 1)) + + startDate.setUTCHours(0, 0, 0, 0) + endDate.setUTCHours(0, 0, 0, 0) + + return new Promise(async (resolve, reject) => { + await InvoiceAffectDao.getInvoicesByDate(startDateStr, endDateStr, employeeId, clientType, countryCode, ageOfAccount) + .then(async data => { + if (data) { + const billedList = [] + let clientIDList = [] + let returnData = {} + + yearMonthList.forEach(ym => { + let billed = 0 + data.forEach(i => { + if (i.invoiceDate >= startDate && i.invoiceDate < endDate) { + billed += i.amount + clientIDList.push(i.actorId) + } + }) - let startDate = new Date(parseInt(startDateStr.substring(5, 7)) + " " + parseInt(startDateStr.substring(8)) + " " + parseInt(startDateStr.substring(0, 4))); - let endDate = new Date(parseInt(endDateStr.substring(5, 7)) + " " + parseInt(endDateStr.substring(8)) + " " + parseInt(endDateStr.substring(0, 4))); - endDate.setMonth(endDate.getMonth() - (yearMonthList.length - 1)); - - startDate.setUTCHours(0, 0, 0, 0); - endDate.setUTCHours(0, 0, 0, 0); - - return new Promise(async (resolve, reject) => { - await InvoiceAffectDao.getInvoicesByDate(startDateStr, endDateStr, employeeId, clientType, countryCode, ageOfAccount) - .then(async data => { - if (data) { - let billedList = []; - let clientIDList = []; - let returnData = {}; - - yearMonthList.forEach(ym => { - let billed = 0; - data.forEach(i => { - if (i.invoiceDate >= startDate && i.invoiceDate < endDate) { - billed += i.amount; - clientIDList.push(i.actorId); - } - }); - - billedList.push({ - month: ym, - billed: billed - }); - - startDate.setUTCMonth(startDate.getUTCMonth() + 1); - endDate.setUTCMonth(endDate.getUTCMonth() + 1); - }); - clientIDList = [...new Set(clientIDList)]; - - returnData = { - billedList: billedList, - clientIDList: clientIDList - }; - - resolve(returnData); - } - resolve(false); + billedList.push({ + month: ym, + billed: billed }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch bills." - } - reject(response); - }); - }); -} -exports.getYearMonth = (startDateStr, endDateStr) => { - let yearMonthList = []; + startDate.setUTCMonth(startDate.getUTCMonth() + 1) + endDate.setUTCMonth(endDate.getUTCMonth() + 1) + }) + clientIDList = [...new Set(clientIDList)] - const startYear = parseInt(startDateStr.split('-')[0]); - let startMonth = parseInt(startDateStr.split('-')[1]); - const endYear = parseInt(endDateStr.split('-')[0]); - const endMonth = parseInt(endDateStr.split('-')[1]); + returnData = { + billedList: billedList, + clientIDList: clientIDList + } - // make list of yearMonth [201911,202001,202002,...] to select dues - for (let y = startYear; y <= endYear; y++) { - if (y != startYear) startMonth = 1; - for (let m = startMonth; m <= 12; m++) { - if (y == endYear && m > endMonth) break; - let yearMonthStr = y.toString(); - if (m < 10) yearMonthStr += '0'; - yearMonthStr += m.toString(); - yearMonthList.push(parseInt(yearMonthStr)); + resolve(returnData) + } + resolve(false) + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch bills.' } + reject(response) + }) + }) +} + +exports.getYearMonth = (startDateStr, endDateStr) => { + const yearMonthList = [] + + const startYear = parseInt(startDateStr.split('-')[0]) + let startMonth = parseInt(startDateStr.split('-')[1]) + const endYear = parseInt(endDateStr.split('-')[0]) + const endMonth = parseInt(endDateStr.split('-')[1]) + + // make list of yearMonth [201911,202001,202002,...] to select dues + for (let y = startYear; y <= endYear; y++) { + if (y !== startYear) startMonth = 1 + for (let m = startMonth; m <= 12; m++) { + if (y === endYear && m > endMonth) break + let yearMonthStr = y.toString() + if (m < 10) yearMonthStr += '0' + yearMonthStr += m.toString() + yearMonthList.push(parseInt(yearMonthStr)) } + } - return yearMonthList; + return yearMonthList } - exports.getCountriesName = async () => { - let countryList = []; - - return new Promise(async (resolve, reject) => { - await CountryDao.getAllCountries().then(async data => { - if (data) { - data.forEach(country => { - countryList.push({ - countryCode: country.countryCode, - countryLabel: country.countryLabel - }); - }); - resolve(countryList); - } - resolve(false); - }).catch(err => { - reject(err); - }); - }); -} \ No newline at end of file + const countryList = [] + + return new Promise(async (resolve, reject) => { + await CountryDao.getAllCountries().then(async data => { + if (data) { + data.forEach(country => { + countryList.push({ + countryCode: country.countryCode, + countryLabel: country.countryLabel + }) + }) + resolve(countryList) + } + resolve(false) + }).catch(err => { + reject(err) + }) + }) +} diff --git a/server/services/report.service.js b/server/services/report.service.js index 1a7ef40..eafbaf3 100644 --- a/server/services/report.service.js +++ b/server/services/report.service.js @@ -1,341 +1,320 @@ -const ChartReportDao = require("../data_access_layer/daos/chart_report.dao"); -const ReportDao = require("../data_access_layer/daos/report.dao"); +const ChartReportDao = require('../data_access_layer/daos/chart_report.dao') +const ReportDao = require('../data_access_layer/daos/report.dao') -const pdf = require('html-pdf'); -const pdfTemplate = require("../docs/chartReportPDF"); -require("../../config.js"); +const pdf = require('html-pdf') +const pdfTemplate = require('../docs/chartReportPDF') +require('../../config.js') // Chart Report Related functions exports.getChartReportsByUserId = async (userId) => { - return new Promise(async (resolve, reject) => { - await ChartReportDao.getChartReportsByUserId(userId) - .then(async data => { - if (data) { - resolve(data); - } - resolve(false); - }).catch(async err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch data." - }; - reject(response); - }); - }); + return new Promise(async (resolve, reject) => { + await ChartReportDao.getChartReportsByUserId(userId) + .then(async data => { + if (data) { + resolve(data) + } + resolve(false) + }).catch(async err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch data.' + } + reject(response) + }) + }) } exports.createChartReportForUser = async (criteria, data, userId) => { - return new Promise(async (resolve, reject) => { - if (!this.verifyChartReport(criteria)) - return reject({ - status: 400, - message: "Invalid content." - }); + return new Promise(async (resolve, reject) => { + if (!this.verifyChartReport(criteria)) { + return reject({ + status: 400, + message: 'Invalid content.' + }) + } - await this.createChartReport(userId, criteria) - .then(async createdChartReport => { - if (createdChartReport) { - return [await this.createChartReportData(createdChartReport, data), - createdChartReport]; - } - resolve(false); - }) - .then(async data => { - if (data[0]) { - let returnData = { - createdChartReport: data[1], - createdChartReportData: data[0] - }; - resolve(returnData); - } - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Malfunction in the B&C Engine." - }; - reject(response); - }); - }); + await this.createChartReport(userId, criteria) + .then(async createdChartReport => { + if (createdChartReport) { + return [await this.createChartReportData(createdChartReport, data), + createdChartReport] + } + resolve(false) + }) + .then(async data => { + if (data[0]) { + const returnData = { + createdChartReport: data[1], + createdChartReportData: data[0] + } + resolve(returnData) + } + resolve(false) + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Malfunction in the B&C Engine.' + } + reject(response) + }) + }) } exports.createChartReportData = async (createdChartReport, data) => { - return new Promise(async (resolve, reject) => { - let preparedData = []; + return new Promise(async (resolve, reject) => { + const preparedData = [] - for (let i = 0; i < data.length; i++) { - let isEmpFiltered = data[i].label.toString().split(" - ")[1] === "emp"; - let year = parseInt(data[i].label.toString().split(" - ")[0]); - let set = { - chart_report_id: createdChartReport.chartReportId, - year: year, - employee: createdChartReport.emp1Id === -1 ? -1 : createdChartReport.emp2Id === null ? createdChartReport.emp1Id : isEmpFiltered ? createdChartReport.emp1Id : createdChartReport.emp2Id, - january: data[i].data[0], - february: data[i].data[1], - march: data[i].data[2], - april: data[i].data[3], - may: data[i].data[4], - june: data[i].data[5], - july: data[i].data[6], - august: data[i].data[7], - september: data[i].data[8], - october: data[i].data[9], - november: data[i].data[10], - december: data[i].data[11] - }; - preparedData.push(set); - } + for (let i = 0; i < data.length; i++) { + const isEmpFiltered = data[i].label.toString().split(' - ')[1] === 'emp' + const year = parseInt(data[i].label.toString().split(' - ')[0]) + const set = { + chart_report_id: createdChartReport.chartReportId, + year: year, + employee: createdChartReport.emp1Id === -1 ? -1 : createdChartReport.emp2Id === null ? createdChartReport.emp1Id : isEmpFiltered ? createdChartReport.emp1Id : createdChartReport.emp2Id, + january: data[i].data[0], + february: data[i].data[1], + march: data[i].data[2], + april: data[i].data[3], + may: data[i].data[4], + june: data[i].data[5], + july: data[i].data[6], + august: data[i].data[7], + september: data[i].data[8], + october: data[i].data[9], + november: data[i].data[10], + december: data[i].data[11] + } + preparedData.push(set) + } - await ChartReportDao.createDataForChartReport(createdChartReport.chartReportId, preparedData) - .then(async data => { - if (data) - resolve(data); - else - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Malfunction in the B&C Engine." - } - reject(response); - }); - }); + await ChartReportDao.createDataForChartReport(createdChartReport.chartReportId, preparedData) + .then(async data => { + if (data) { resolve(data) } else { resolve(false) } + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Malfunction in the B&C Engine.' + } + reject(response) + }) + }) } exports.createChartReport = async (userId, criteria) => { - return new Promise(async (resolve, reject) => { - ChartReportDao.createChartReportForUser(userId, criteria) - .then(async data => { - if (data) - resolve(data); - else - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Could not create Chart Report." - } - reject(response); - }); - }); + return new Promise(async (resolve, reject) => { + ChartReportDao.createChartReportForUser(userId, criteria) + .then(async data => { + if (data) { resolve(data) } else { resolve(false) } + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not create Chart Report.' + } + reject(response) + }) + }) } exports.verifyChartReport = (criteria) => { + let verified = true - let verified = true; + if (!('name' in criteria) || !('startDate' in criteria) || !('endDate' in criteria) || + !('employee1Id' in criteria) || !('employee1Name' in criteria) || !('employee2Id' in criteria) || + !('employee2Name' in criteria) || !('clientType' in criteria) || !('ageOfAccount' in criteria) || + !('countryId' in criteria) || !('country' in criteria)) { + verified = false + } - if (!('name' in criteria) || !('startDate' in criteria) || !('endDate' in criteria) - || !('employee1Id' in criteria) || !('employee1Name' in criteria) || !('employee2Id' in criteria) - || !('employee2Name' in criteria) || !('clientType' in criteria) || !('ageOfAccount' in criteria) - || !('countryId' in criteria) || !('country' in criteria)) { - verified = false; - } - - return verified; + return verified } exports.deleteChartReportById = async (chartReportId) => { - return new Promise((resolve, reject) => { - - ChartReportDao.deleteChartReportById(chartReportId) - .then(async data => { - if (data) resolve(data); - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Malfunction in the B&C Engine." - }; - reject(response); - }); - }); + return new Promise((resolve, reject) => { + ChartReportDao.deleteChartReportById(chartReportId) + .then(async data => { + if (data) resolve(data) + resolve(false) + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Malfunction in the B&C Engine.' + } + reject(response) + }) + }) } - // Reports Types related functions -exports.getPerformanceReportWhenConnectedAsAdmin = async (userId) => { - return new Promise(async (resolve, reject) => { - await ReportDao.getPerformanceReportsWhenConnectedAsAdmin(userId) - .then(async data => { - if (data) { - resolve(data); - } - resolve(false); - }).catch(async err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch data." - }; - reject(response); - }); - }); +exports.getPerformanceReports = async (userId) => { + return new Promise(async (resolve, reject) => { + await ReportDao.getPerformanceReports(userId) + .then(async data => { + if (data) { + resolve(data) + } + resolve(false) + }).catch(async err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch data.' + } + reject(response) + }) + }) } exports.getReportTypesWithRecipients = async () => { - return new Promise(async (resolve, reject) => { - this.getReportTypes() - .then(async reportTypes => { - if (reportTypes) { - let returnData = { - reportTypes: reportTypes - }; - returnData.recipients = await this.getRecipients(); - return returnData; - } - resolve(false); - }) - .then(async data => { - if (data.recipients) { - for (let i = 0; i < data.reportTypes.length; i++) { - for (let j = 0; j < data.recipients.length; j++) { - let recipientId = data.recipients[j].recipientId; - if (recipientId in data.reportTypes[i].recipients) { - data.reportTypes[i].recipients[recipientId].name = data.recipients[j].name; - data.reportTypes[i].recipients[recipientId].isRecipient = true; - } - else { - data.reportTypes[i].recipients[recipientId] = { - name: data.recipients[j].name, - isRecipient: false - }; - } - } - } - resolve(data.reportTypes); + return new Promise(async (resolve, reject) => { + this.getReportTypes() + .then(async reportTypes => { + if (reportTypes) { + const returnData = { + reportTypes: reportTypes + } + returnData.recipients = await this.getRecipients() + return returnData + } + resolve(false) + }) + .then(async data => { + if (data.recipients) { + for (let i = 0; i < data.reportTypes.length; i++) { + for (let j = 0; j < data.recipients.length; j++) { + const recipientId = data.recipients[j].recipientId + if (recipientId in data.reportTypes[i].recipients) { + data.reportTypes[i].recipients[recipientId].name = data.recipients[j].name + data.reportTypes[i].recipients[recipientId].isRecipient = true + } else { + data.reportTypes[i].recipients[recipientId] = { + name: data.recipients[j].name, + isRecipient: false } - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Malfunction in the B&C Engine." - }; - reject(response); - }); - }); + } + } + } + resolve(data.reportTypes) + } + resolve(false) + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Malfunction in the B&C Engine.' + } + reject(response) + }) + }) } exports.getReportTypes = async () => { - return new Promise(async (resolve, reject) => { - await ReportDao.getReportTypesWithRecipientIds() - .then(async data => { - if (data) - resolve(data); - else - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch Report Types." - } - reject(response); - }); - }); + return new Promise(async (resolve, reject) => { + await ReportDao.getReportTypesWithRecipientIds() + .then(async data => { + if (data) { resolve(data) } else { resolve(false) } + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch Report Types.' + } + reject(response) + }) + }) } exports.getRecipients = async () => { - return new Promise(async (resolve, reject) => { - await ReportDao.getRecipients() - .then(async data => { - if (data) - resolve(data); - else - resolve(false); - }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Could not fetch Recipients." - } - reject(response); - }); - }); + return new Promise(async (resolve, reject) => { + await ReportDao.getRecipients() + .then(async data => { + if (data) { resolve(data) } else { resolve(false) } + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Could not fetch Recipients.' + } + reject(response) + }) + }) } // Chart Report PDF Generation related functions exports.createChartReportPDFById = async (reportId) => { - let averagesList = []; + let averagesList = [] - let filePath; - if(__dirname !== '/home/runner/work/BC-Engine/BC-Engine/server/services') { - filePath = `${__dirname.replace("services", "")}docs\\pdf_files\\chartReport-${reportId}.pdf`; - } - else { - filePath = `${__dirname.replace("services", "")}docs/pdf_files/chartReport-${reportId}.pdf`; - } + let filePath + if (__dirname !== '/home/runner/work/BC-Engine/BC-Engine/server/services') { + filePath = `${__dirname.replace('services', '')}docs\\pdf_files\\chartReport-${reportId}.pdf` + } else { + filePath = `${__dirname.replace('services', '')}docs/pdf_files/chartReport-${reportId}.pdf` + } - return new Promise(async (resolve, reject) => { - ChartReportDao.getChartReportById(reportId) - .then(async data => { - if (data) { - data = data.dataValues + return new Promise(async (resolve, reject) => { + ChartReportDao.getChartReportById(reportId) + .then(async data => { + if (data) { + data = data.dataValues - await this.getChartReportPDFAverages(reportId) - .then(async (dataAvg) => { - if(dataAvg) - averagesList = dataAvg; - else - resolve(false); - }).then(() => { - pdf.create(pdfTemplate(data, averagesList), {format: "letter"}) - .toFile(filePath, () => { - resolve(true); - }); - }).catch(err => { - reject(err); - }); - } - else - resolve(false); + await this.getChartReportPDFAverages(reportId) + .then(async (dataAvg) => { + if (dataAvg) { averagesList = dataAvg } else { resolve(false) } + }).then(() => { + pdf.create(pdfTemplate(data, averagesList), { format: 'letter' }) + .toFile(filePath, () => { + resolve(true) + }) + }).catch(err => { + reject(err) }) - .catch(err => { - const response = { - status: err.status || 500, - message: err.message || "Malfunction in the B&C Engine." - } - reject(response); - }); - }); + } else { resolve(false) } + }) + .catch(err => { + const response = { + status: err.status || 500, + message: err.message || 'Malfunction in the B&C Engine.' + } + reject(response) + }) + }) } exports.getChartReportPDFAverages = async (reportId) => { - return new Promise(async (resolve, reject) => { - ChartReportDao.getDataForChartReport(reportId) - .then(data => { - let averagesList = []; - if (data) { - for(let i = 0; i < data.length; i++) { - let avgObject = { - year: data[i].year, - employee: data[i].employee, - data: [ - data[i].january, - data[i].february, - data[i].march, - data[i].april, - data[i].may, - data[i].june, - data[i].july, - data[i].august, - data[i].september, - data[i].october, - data[i].november, - data[i].december, - ] - } - averagesList.push(avgObject); - } - resolve(averagesList); + return new Promise(async (resolve, reject) => { + ChartReportDao.getDataForChartReport(reportId) + .then(data => { + const averagesList = [] + if (data) { + for (let i = 0; i < data.length; i++) { + const avgObject = { + year: data[i].year, + employee: data[i].employee, + data: [ + data[i].january, + data[i].february, + data[i].march, + data[i].april, + data[i].may, + data[i].june, + data[i].july, + data[i].august, + data[i].september, + data[i].october, + data[i].november, + data[i].december + ] } - resolve(false); - }) - .catch(err => { - reject(err); - }); - }); -} \ No newline at end of file + averagesList.push(avgObject) + } + resolve(averagesList) + } + resolve(false) + }) + .catch(err => { + reject(err) + }) + }) +} diff --git a/server/services/user.service.js b/server/services/user.service.js index 0d81331..cf511d9 100644 --- a/server/services/user.service.js +++ b/server/services/user.service.js @@ -1,86 +1,82 @@ -const databases = require("../data_access_layer/databases"); -const User = databases['localdb'].users; -const UserDAO = require('../data_access_layer/daos/user.dao'); -const Op = databases.Sequelize.Op; +const UserDAO = require('../data_access_layer/daos/user.dao') exports.createUser = async (user) => { - return new Promise((resolve, reject) => { - UserDAO.createUser(user) - .then(async data => { - if(data)resolve(data); - resolve(false); - }) - .catch(err => { - reject(err); - }); - }); -}; + return new Promise((resolve, reject) => { + UserDAO.createUser(user) + .then(async data => { + if (data) resolve(data) + resolve(false) + }) + .catch(err => { + reject(err) + }) + }) +} exports.getAllUsers = async () => { - return new Promise((resolve, reject) => { - UserDAO.getAllUsers() - .then(async data => { - if(data) { - - // Puts users in alphabetical order for the users table - let sortedUser = data.sort((a, b) => { - let userA = a.name.toUpperCase(); - let userB = b.name.toUpperCase(); - return (userA < userB) ? -1 : (userA > userB) ? 1 : 0; - }); - resolve(sortedUser); - } - resolve(false); - }) - .catch(err => { - reject(err); - }); - }); -}; + return new Promise((resolve, reject) => { + UserDAO.getAllUsers() + .then(async data => { + if (data) { + // Puts users in alphabetical order for the users table + const sortedUser = data.sort((a, b) => { + const userA = a.name.toUpperCase() + const userB = b.name.toUpperCase() + return (userA < userB) ? -1 : (userA > userB) ? 1 : 0 + }) + resolve(sortedUser) + } + resolve(false) + }) + .catch(err => { + reject(err) + }) + }) +} exports.authenticateUser = async (user) => { - return new Promise((resolve, reject) => { - UserDAO.getUserByEmail(user.email) - .then(async data => { - if(!data) resolve(false); - if(!data.password || - !await data.validPassword(user.password, data.password)){ - resolve(false); - } else { - resolve(data); - } - }).catch(err => { - const response = { - status: 500, - message: err.message || "some error occured" - } - reject(response); - }); - }); -}; + return new Promise((resolve, reject) => { + UserDAO.getUserByEmail(user.email) + .then(async data => { + if (!data) resolve(false) + if (!data.password || + !await data.validPassword(user.password, data.password)) { + resolve(false) + } else { + resolve(data) + } + }).catch(err => { + const response = { + status: 500, + message: err.message || 'some error occured' + } + reject(response) + }) + }) +} exports.modifyUser = async (user) => { - return new Promise((resolve, reject) => { - UserDAO.updateUser(user) - .then(async data => { - if(data) resolve(data); - resolve(false); - }) - .catch(err => { - reject(err); - }); - }); -}; + return new Promise((resolve, reject) => { + UserDAO.updateUser(user) + .then(async data => { + if (data) resolve(data) + resolve(false) + }) + .catch(err => { + reject(err) + }) + }) +} exports.deleteUser = async (email) => { - return new Promise((resolve, reject) => { - UserDAO.deleteUser(email) - .then(async data => { - if(data) resolve(data); - resolve(false) - }) - .catch(err => { - reject(err); - }); - }); + return new Promise((resolve, reject) => { + UserDAO.deleteUser(email) + .then(async data => { + if (data) resolve(data) + resolve(false) + }) + .catch(err => { + reject(err) + }) + }) } diff --git a/server/tests/DAL/chart_report.dao.test.js b/server/tests/DAL/chart_report.dao.test.js index 15f40ca..db3e33e 100644 --- a/server/tests/DAL/chart_report.dao.test.js +++ b/server/tests/DAL/chart_report.dao.test.js @@ -1,562 +1,560 @@ -const { sequelize, - dataTypes, - checkModelName, - checkPropertyExists -} = require('sequelize-test-helpers'); - -const [UserModel, ChartReportModel, ChartReportDataModel, PerformanceReportModel, ReportTypeModel, RecipientModel, ReportTypeRecipientModel] = require('../../data_access_layer/models/localdb/localdb.model')(sequelize, dataTypes); -const ChartReportDAO = require('../../data_access_layer/daos/chart_report.dao'); - -let returnedChartReports = [ - { - chartReportId: 'fakeUUID1', - name: 'CR1', - startDate: '2019-12-01', - endDate: '2020-12-01', - employee1Id: 12345, - employee1Name: 'France Cote', - employee2Id: null, - employee2Name: null, - country: 'Canada', - clientType: 'Corr', - ageOfAccount: 'All', - accountType: 'Receivable', - user_user_id: 'fakeUserId' - }, - { - chartReportId: 'fakeUUID2', - name: 'CR1', - startDate: '2019-12-01', - endDate: '2020-12-01', - employee1Id: 12345, - employee1Name: 'France Cote', - employee2Id: -1, - employee2Name: 'All', - country: 'Canada', - clientType: 'Corr', - ageOfAccount: 'All', - accountType: 'Receivable', - user_user_id: 'fakeUserId' +const { + sequelize, + dataTypes, + checkModelName, + checkPropertyExists +} = require('sequelize-test-helpers') + +const Models = require('../../data_access_layer/models/localdb/localdb.model')(sequelize, dataTypes) +const ChartReportDAO = require('../../data_access_layer/daos/chart_report.dao') +const ChartReportModel = Models[1]; + +const returnedChartReports = [ + { + chartReportId: 'fakeUUID1', + name: 'CR1', + startDate: '2019-12-01', + endDate: '2020-12-01', + employee1Id: 12345, + employee1Name: 'France Cote', + employee2Id: null, + employee2Name: null, + country: 'Canada', + clientType: 'Corr', + ageOfAccount: 'All', + accountType: 'Receivable', + user_user_id: 'fakeUserId' + }, + { + chartReportId: 'fakeUUID2', + name: 'CR1', + startDate: '2019-12-01', + endDate: '2020-12-01', + employee1Id: 12345, + employee1Name: 'France Cote', + employee2Id: -1, + employee2Name: 'All', + country: 'Canada', + clientType: 'Corr', + ageOfAccount: 'All', + accountType: 'Receivable', + user_user_id: 'fakeUserId' + } +] + +const returnedOneChartReport = [ + { + chartReportId: 'fakeUUID1', + name: 'CR1', + startDate: '2019-12-01', + endDate: '2020-12-01', + employee1Id: 12345, + employee1Name: 'France Cote', + employee2Id: null, + employee2Name: null, + country: 'Canada', + clientType: 'Corr', + ageOfAccount: 'All', + accountType: 'Receivable', + user_user_id: 'fakeUserId' + } +] + +const returnedChartData = [ + { + dataValues: { + year: 2018, + employee: -1, + data: [ + 0, 0, 0, 0, + 0, 0, 0, 0, + 91.65, 83.36, 88.35, 89 + ] } -]; - -let returnedOneChartReport = [ - { - chartReportId: 'fakeUUID1', - name: 'CR1', - startDate: '2019-12-01', - endDate: '2020-12-01', + }, + { + dataValues: { + year: 2019, + employee: -1, + data: [ + 84.12, 87.92, 93.05, + 99.39, 96.37, 0, + 0, 0, 0, + 0, 0, 0 + ] + } + } +] + +const SequelizeMock = require('sequelize-mock') +const dbMock = new SequelizeMock() +const ChartReportMock = dbMock.define('chart_reports', returnedChartReports) +const ChartReportDataMock = dbMock.define('chart_reports_data', returnedChartReports) + +describe('Test Chart Report DAO', () => { + const ChartReport = new ChartReportModel() + + afterEach(() => { + ChartReportMock.$queryInterface.$clearResults() + ChartReportDataMock.$queryInterface.$clearResults() + }) + + beforeEach(() => { + ChartReportMock.$queryInterface.$clearResults() + ChartReportDataMock.$queryInterface.$clearResults() + }) + + // testing the chart report model properties + checkModelName(ChartReportModel)('chart_reports'); + + ['chartReportId', 'name', 'startDate', 'endDate', 'employee1Id', 'employee1Name', + 'employee2Id', 'employee2Name', 'country', 'clientType', 'ageOfAccount', 'accountType'] + .forEach(checkPropertyExists(ChartReport)) + + describe('CRD1 - getChartReportsByUserId', () => { + describe('CRD1.1 - given a userId', () => { + it('CRD1.1.1 - should return list of chart Reports', async () => { + // arrange + ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(returnedChartReports) + }) + + // act and assert + await expect(ChartReportDAO.getChartReportsByUserId('fakeUserId', ChartReportMock)).resolves + .toEqual(returnedChartReports) + }) + + it('CRD1.1.2 - should resolve false when Model cant fetch data', async () => { + // arrange + ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(false) + }) + + // act and assert + await expect(ChartReportDAO.getChartReportsByUserId('fakeUserId', ChartReportMock)).resolves + .toEqual(false) + }) + + it('CRD1.1.3 - should reject error when Model throws error with defined status and message', async () => { + // arrange + const expectedError = { + status: 404, + message: 'Error.' + } + ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject(expectedError) + }) + + // act and assert + await expect(ChartReportDAO.getChartReportsByUserId('fakeUserId', ChartReportMock)).rejects + .toEqual(expectedError) + }) + + it('CRD1.1.4 - should reject error with 500 status and predefined message when model does not define them', async () => { + // arrange + const expectedError = { + status: 500, + message: 'Could not fetch data.' + } + ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject({}) + }) + + // act and assert + await expect(ChartReportDAO.getChartReportsByUserId('fakeUserId', ChartReportMock)).rejects + .toEqual(expectedError) + }) + }) + }) + + describe('CRD2 - createChartReportForUser', () => { + const fakeCreateChartReportModelResponse = { + dataValues: { + chartReportId: 'fakeUUID', + name: 'CRname', employee1Id: 12345, - employee1Name: 'France Cote', - employee2Id: null, - employee2Name: null, - country: 'Canada', - clientType: 'Corr', - ageOfAccount: 'All', - accountType: 'Receivable', - user_user_id: 'fakeUserId' + employee2Id: -1 + } } -]; -let returnedChartData = [ - { + const fakeChartReport = { + user_user_id: 'fakeUUID' + } + + let fakeChartReportModel = { + create: () => { + return Promise.resolve(fakeCreateChartReportModelResponse) + } + } + + describe('CRD2.1 - given valid userId and chartReport', () => { + it('CRD2.1.1 - when valid response from model, should resolve filtered data', async () => { + // arrange + const expectedResponse = { + chartReportId: fakeCreateChartReportModelResponse.dataValues.chartReportId, + name: fakeCreateChartReportModelResponse.dataValues.name, + emp1Id: fakeCreateChartReportModelResponse.dataValues.employee1Id, + emp2Id: fakeCreateChartReportModelResponse.dataValues.employee2Id + } + + // act and assert + await expect(ChartReportDAO.createChartReportForUser('fakeUUID', fakeChartReport, fakeChartReportModel)) + .resolves.toEqual(expectedResponse) + }) + + it('CRD2.1.2 - when model throws error with specified status and message, should reject specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + fakeChartReportModel = { + create: () => { + return Promise.reject(expectedResponse) + } + } + + // act and assert + await expect(ChartReportDAO.createChartReportForUser('fakeUUID', fakeChartReport, fakeChartReportModel)) + .rejects.toEqual(expectedResponse) + }) + + it('CRD2.1.3 - when model throws error with unspecified status and message, should reject default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not create data.' + } + fakeChartReportModel = { + create: () => { + return Promise.reject({}) + } + } + + // act and assert + await expect(ChartReportDAO.createChartReportForUser('fakeUUID', fakeChartReport, fakeChartReportModel)) + .rejects.toEqual(expectedResponse) + }) + + it('CRD2.1.4 - when model resolves false, should resolve false', async () => { + // arrange + fakeChartReportModel = { + create: () => { + return Promise.resolve(false) + } + } + + // act and assert + await expect(ChartReportDAO.createChartReportForUser('fakeUUID', fakeChartReport, fakeChartReportModel)) + .resolves.toEqual(false) + }) + }) + }) + + describe('CRD3 - createDataForChartReport', () => { + const fakeBulkCreateDataForChartReportModelResponse = [ + { dataValues: { - year: 2018, - employee: -1, - data: [ - 0, 0, 0, 0, - 0, 0, 0, 0, - 91.65, 83.36, 88.35, 89 - ] + id: 1, + year: 2020, + employee: 12345 } - }, - { + }, + { dataValues: { - year: 2019, - employee: -1, - data: [ - 84.12, 87.92, 93.05, - 99.39, 96.37, 0, - 0, 0, 0, - 0, 0, 0 - ] + id: 2, + year: 2020, + employee: -1 } + } + ] + + let fakeChartReportModel = { + bulkCreate: () => { + return Promise.resolve(fakeBulkCreateDataForChartReportModelResponse) + } } -]; -let SequelizeMock = require('sequelize-mock'); -const dbMock = new SequelizeMock(); -var ChartReportMock = dbMock.define('chart_reports', returnedChartReports); -var ChartReportDataMock = dbMock.define('chart_reports_data', returnedChartReports); + describe('CRD3.1 - given valid chartReportId and data', () => { + it('CRD3.1.1 - when valid response from model, should resolve with filtered data', async () => { + // arrange + const expectedResponse = [ + { + chartReportDataId: fakeBulkCreateDataForChartReportModelResponse[0].dataValues.id, + year: fakeBulkCreateDataForChartReportModelResponse[0].dataValues.year, + employee: fakeBulkCreateDataForChartReportModelResponse[0].dataValues.employee + }, + { + chartReportDataId: fakeBulkCreateDataForChartReportModelResponse[1].dataValues.id, + year: fakeBulkCreateDataForChartReportModelResponse[1].dataValues.year, + employee: fakeBulkCreateDataForChartReportModelResponse[1].dataValues.employee + } + ] + + // act and assert + await expect(ChartReportDAO.createDataForChartReport('fakeUUID', {}, fakeChartReportModel)) + .resolves.toEqual(expectedResponse) + }) + + it('CRD3.1.2 - when model throws error with specified status and message, should reject specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + fakeChartReportModel = { + bulkCreate: () => { + return Promise.reject(expectedResponse) + } + } + + // act and assert + await expect(ChartReportDAO.createDataForChartReport('fakeUUID', {}, fakeChartReportModel)) + .rejects.toEqual(expectedResponse) + }) + + it('CRD3.1.3 - when model throws error with unspecified status and message, should reject default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not create data.' + } + fakeChartReportModel = { + bulkCreate: () => { + return Promise.reject({}) + } + } -describe("Test Chart Report DAO", () => { - const ChartReport = new ChartReportModel(); - + // act and assert + await expect(ChartReportDAO.createDataForChartReport('fakeUUID', {}, fakeChartReportModel)) + .rejects.toEqual(expectedResponse) + }) + + it('CRD3.1.4 - when model resolves false, should resolve false', async () => { + // arrange + fakeChartReportModel = { + bulkCreate: () => { + return Promise.resolve(false) + } + } - afterEach(() => { - ChartReportMock.$queryInterface.$clearResults(); - ChartReportDataMock.$queryInterface.$clearResults(); + // act and assert + await expect(ChartReportDAO.createDataForChartReport('fakeUUID', {}, fakeChartReportModel)) + .resolves.toEqual(false) + }) }) + }) - beforeEach(() => { - ChartReportMock.$queryInterface.$clearResults(); - ChartReportDataMock.$queryInterface.$clearResults(); + describe('CRD4 - deleteChartReportById', () => { + const fakeDeleteChartIdModelResponse = { + chartReportId: 'any' + } + let fakeChartReportModel = { + destroy: () => { + return Promise.resolve(fakeDeleteChartIdModelResponse) + } + } + describe('CRD4.1 - given valid chartReportId', () => { + it('UD4.1.1 - should return successful delete message', async () => { + // arrange + const expectedResponse = 'Chart report deleted successfully.' + + fakeChartReportModel = { + destroy: () => { + return Promise.resolve(expectedResponse) + } + } + + // act and assert + await expect(ChartReportDAO.deleteChartReportById('fakeUUID', fakeChartReportModel)) + .resolves.toEqual(expectedResponse) + }) }) - // testing the chart report model properties - checkModelName(ChartReportModel)('chart_reports'); - - ['chartReportId', 'name', 'startDate', 'endDate', 'employee1Id', 'employee1Name', - 'employee2Id', 'employee2Name', 'country', 'clientType', 'ageOfAccount', 'accountType'] - .forEach(checkPropertyExists(ChartReport)); - - - - describe("CRD1 - getChartReportsByUserId", () => { - describe("CRD1.1 - given a userId", () => { - it("CRD1.1.1 - should return list of chart Reports", async () => { - // arrange - ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(returnedChartReports); - }); - - // act and assert - await expect(ChartReportDAO.getChartReportsByUserId('fakeUserId', ChartReportMock)).resolves - .toEqual(returnedChartReports); - }); - - it("CRD1.1.2 - should resolve false when Model cant fetch data", async () => { - // arrange - ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(false); - }); - - // act and assert - await expect(ChartReportDAO.getChartReportsByUserId('fakeUserId', ChartReportMock)).resolves - .toEqual(false); - }); - - it("CRD1.1.3 - should reject error when Model throws error with defined status and message", async () => { - // arrange - let expectedError = { - status: 404, - message: "Error." - }; - ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject(expectedError); - }); - - // act and assert - await expect(ChartReportDAO.getChartReportsByUserId('fakeUserId', ChartReportMock)).rejects - .toEqual(expectedError); - }); - - it("CRD1.1.4 - should reject error with 500 status and predefined message when model does not define them", async () => { - // arrange - let expectedError = { - status: 500, - message: "Could not fetch data." - }; - ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject({}); - }); - - // act and assert - await expect(ChartReportDAO.getChartReportsByUserId('fakeUserId', ChartReportMock)).rejects - .toEqual(expectedError); - }); - }); - }); - - describe("CRD2 - createChartReportForUser", () => { - let fakeCreateChartReportModelResponse = { - dataValues: { - chartReportId: "fakeUUID", - name: "CRname", - employee1Id: 12345, - employee2Id: -1 - } - }; - - let fakeChartReport = { - user_user_id: "fakeUUID" - }; - - let fakeChartReportModel = { - create: () => { - return Promise.resolve(fakeCreateChartReportModelResponse); - } - }; - - describe("CRD2.1 - given valid userId and chartReport", () => { - it("CRD2.1.1 - when valid response from model, should resolve filtered data", async () => { - // arrange - let expectedResponse = { - chartReportId: fakeCreateChartReportModelResponse.dataValues.chartReportId, - name: fakeCreateChartReportModelResponse.dataValues.name, - emp1Id: fakeCreateChartReportModelResponse.dataValues.employee1Id, - emp2Id: fakeCreateChartReportModelResponse.dataValues.employee2Id - }; - - // act and assert - await expect(ChartReportDAO.createChartReportForUser("fakeUUID", fakeChartReport, fakeChartReportModel)) - .resolves.toEqual(expectedResponse); - }); - - it("CRD2.1.2 - when model throws error with specified status and message, should reject specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - fakeChartReportModel = { - create: () => { - return Promise.reject(expectedResponse); - } - }; - - // act and assert - await expect(ChartReportDAO.createChartReportForUser("fakeUUID", fakeChartReport, fakeChartReportModel)) - .rejects.toEqual(expectedResponse); - }); - - it("CRD2.1.3 - when model throws error with unspecified status and message, should reject default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not create data." - }; - fakeChartReportModel = { - create: () => { - return Promise.reject({}); - } - }; - - // act and assert - await expect(ChartReportDAO.createChartReportForUser("fakeUUID", fakeChartReport, fakeChartReportModel)) - .rejects.toEqual(expectedResponse); - }); - - it("CRD2.1.4 - when model resolves false, should resolve false", async () => { - // arrange - fakeChartReportModel = { - create: () => { - return Promise.resolve(false); - } - }; - - // act and assert - await expect(ChartReportDAO.createChartReportForUser("fakeUUID", fakeChartReport, fakeChartReportModel)) - .resolves.toEqual(false); - }); - }); - }); - - describe("CRD3 - createDataForChartReport", () => { - let fakeBulkCreateDataForChartReportModelResponse = [ - { - dataValues: { - id: 1, - year: 2020, - employee: 12345 - } - }, - { - dataValues: { - id: 2, - year: 2020, - employee: -1 - } - } - ]; - - let fakeChartReportModel = { - bulkCreate: () => { - return Promise.resolve(fakeBulkCreateDataForChartReportModelResponse); - } - }; - - describe("CRD3.1 - given valid chartReportId and data", () => { - it("CRD3.1.1 - when valid response from model, should resolve with filtered data", async () => { - // arrange - let expectedResponse = [ - { - chartReportDataId: fakeBulkCreateDataForChartReportModelResponse[0].dataValues.id, - year: fakeBulkCreateDataForChartReportModelResponse[0].dataValues.year, - employee: fakeBulkCreateDataForChartReportModelResponse[0].dataValues.employee - }, - { - chartReportDataId: fakeBulkCreateDataForChartReportModelResponse[1].dataValues.id, - year: fakeBulkCreateDataForChartReportModelResponse[1].dataValues.year, - employee: fakeBulkCreateDataForChartReportModelResponse[1].dataValues.employee - } - ]; - - // act and assert - await expect(ChartReportDAO.createDataForChartReport("fakeUUID", {}, fakeChartReportModel)) - .resolves.toEqual(expectedResponse); - }); - - it("CRD3.1.2 - when model throws error with specified status and message, should reject specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - fakeChartReportModel = { - bulkCreate: () => { - return Promise.reject(expectedResponse); - } - }; - - // act and assert - await expect(ChartReportDAO.createDataForChartReport("fakeUUID", {}, fakeChartReportModel)) - .rejects.toEqual(expectedResponse); - }); - - it("CRD3.1.3 - when model throws error with unspecified status and message, should reject default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not create data." - }; - fakeChartReportModel = { - bulkCreate: () => { - return Promise.reject({}); - } - }; - - // act and assert - await expect(ChartReportDAO.createDataForChartReport("fakeUUID", {}, fakeChartReportModel)) - .rejects.toEqual(expectedResponse); - }); - - it("CRD3.1.4 - when model resolves false, should resolve false", async () => { - // arrange - fakeChartReportModel = { - bulkCreate: () => { - return Promise.resolve(false); - } - }; - - // act and assert - await expect(ChartReportDAO.createDataForChartReport("fakeUUID", {}, fakeChartReportModel)) - .resolves.toEqual(false); - }); - }); - }); - - describe("CRD4 - deleteChartReportById", () => { - let fakeDeleteChartIdModelResponse = { - chartReportId: "any" + describe('CRD4.1 - given invalid chartReportId', () => { + it('CRD4.1.1 - when model resolves false, should resolve with an error message', async () => { + // arrange + const expectedResponse = false + + fakeChartReportModel = { + destroy: () => { + return Promise.resolve(false) + } } - let fakeChartReportModel = { - destroy: () => { - return Promise.resolve(fakeDeleteChartIdModelResponse); - } - }; - describe("CRD4.1 - given valid chartReportId", () => { - it("UD4.1.1 - should return successful delete message", async () => { - // arrange - let expectedResponse = "Chart report deleted successfully." - - fakeChartReportModel = { - destroy: () => { - return Promise.resolve(expectedResponse); - } - }; - - // act and assert - await expect(ChartReportDAO.deleteChartReportById("fakeUUID", fakeChartReportModel)) - .resolves.toEqual(expectedResponse) - }); - }); - - describe("CRD4.1 - given invalid chartReportId", () => { - it("CRD4.1.1 - when model resolves false, should resolve with an error message", async () => { - // arrange - let expectedResponse = false; - - fakeChartReportModel = { - destroy: () => { - return Promise.resolve(false); - } - }; - - // act and assert - expect(ChartReportDAO.deleteChartReportById("fakeUUID", fakeChartReportModel)) - .resolves.toEqual(expectedResponse); - }); - it("CRD4.1.2 - when model throws error with specified status and message, should reject specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - - fakeChartReportModel = { - destroy: () => { - return Promise.reject(expectedResponse); - } - }; - - // act and assert - expect(ChartReportDAO.deleteChartReportById("fakeUUID", fakeChartReportModel)) - .rejects.toEqual(expectedResponse); - }); - - it("CRD4.1.3 - when model throws error with unspecified status and message, should reject default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not delete data." - }; - - fakeChartReportModel = { - destroy: () => { - return Promise.reject({}); - } - }; - - // act and assert - expect(ChartReportDAO.deleteChartReportById("fakeUUID", fakeChartReportModel)) - .rejects.toEqual(expectedResponse); - }); - }); - }); - - describe("CRD5 - getChartReportById", () => { - describe("CRD5.1 - given a ReportId", () => { - it("CRD5.1.1 - should return one chart report", async () => { - // arrange - ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(returnedOneChartReport); - }); - - // act and assert - await expect(ChartReportDAO.getChartReportById('fakeUUID1', ChartReportMock)).resolves - .toEqual(returnedOneChartReport); - }); - - it("CRD5.1.2 - should resolve false when Model cant fetch data", async () => { - // arrange - ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(false); - }); - - // act and assert - await expect(ChartReportDAO.getChartReportById('fakeUUID1', ChartReportMock)).resolves - .toEqual(false); - }); - - it("CRD5.1.3 - should reject error when Model throws error with defined status and message", async () => { - // arrange - let expectedError = { - status: 404, - message: "Error." - }; - ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject(expectedError); - }); - - // act and assert - await expect(ChartReportDAO.getChartReportById('fakeUUID1', ChartReportMock)).rejects - .toEqual(expectedError); - }); - - it("CRD5.1.4 - should reject error with 500 status and predefined message when model does not define them", async () => { - // arrange - let expectedError = { - status: 500, - message: "Could not fetch data." - }; - ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject({}); - }); - - // act and assert - await expect(ChartReportDAO.getChartReportById('fakeUUID1', ChartReportMock)).rejects - .toEqual(expectedError); - }); - }); - }); - - describe("CRD6 - getDataForChartReport", () => { - let expectedChartData = [ - { - year: 2018, - employee: -1, - data: [ - 0, 0, 0, 0, - 0, 0, 0, 0, - 91.65, 83.36, 88.35, 89 - ] - }, - { - year: 2019, - employee: -1, - data: [ - 84.12, 87.92, 93.05, - 99.39, 96.37, 0, - 0, 0, 0, - 0, 0, 0 - ] - } + // act and assert + expect(ChartReportDAO.deleteChartReportById('fakeUUID', fakeChartReportModel)) + .resolves.toEqual(expectedResponse) + }) + it('CRD4.1.2 - when model throws error with specified status and message, should reject specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + + fakeChartReportModel = { + destroy: () => { + return Promise.reject(expectedResponse) + } + } + + // act and assert + expect(ChartReportDAO.deleteChartReportById('fakeUUID', fakeChartReportModel)) + .rejects.toEqual(expectedResponse) + }) + + it('CRD4.1.3 - when model throws error with unspecified status and message, should reject default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not delete data.' + } + + fakeChartReportModel = { + destroy: () => { + return Promise.reject({}) + } + } + + // act and assert + expect(ChartReportDAO.deleteChartReportById('fakeUUID', fakeChartReportModel)) + .rejects.toEqual(expectedResponse) + }) + }) + }) + + describe('CRD5 - getChartReportById', () => { + describe('CRD5.1 - given a ReportId', () => { + it('CRD5.1.1 - should return one chart report', async () => { + // arrange + ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(returnedChartReports[0]) + }) + + // act and assert + await expect(ChartReportDAO.getChartReportById('fakeUUID1', ChartReportMock)).resolves + .toEqual(returnedChartReports[0]) + }) + + it('CRD5.1.2 - should resolve false when Model cant fetch data', async () => { + // arrange + ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(false) + }) + + // act and assert + await expect(ChartReportDAO.getChartReportById('fakeUUID1', ChartReportMock)).resolves + .toEqual(false) + }) + + it('CRD5.1.3 - should reject error when Model throws error with defined status and message', async () => { + // arrange + const expectedError = { + status: 404, + message: 'Error.' + } + ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject(expectedError) + }) + + // act and assert + await expect(ChartReportDAO.getChartReportById('fakeUUID1', ChartReportMock)).rejects + .toEqual(expectedError) + }) + + it('CRD5.1.4 - should reject error with 500 status and predefined message when model does not define them', async () => { + // arrange + const expectedError = { + status: 500, + message: 'Could not fetch data.' + } + ChartReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject({}) + }) + + // act and assert + await expect(ChartReportDAO.getChartReportById('fakeUUID1', ChartReportMock)).rejects + .toEqual(expectedError) + }) + }) + }) + + describe('CRD6 - getDataForChartReport', () => { + const expectedChartData = [ + { + year: 2018, + employee: -1, + data: [ + 0, 0, 0, 0, + 0, 0, 0, 0, + 91.65, 83.36, 88.35, 89 + ] + }, + { + year: 2019, + employee: -1, + data: [ + 84.12, 87.92, 93.05, + 99.39, 96.37, 0, + 0, 0, 0, + 0, 0, 0 ] - describe("CRD6.1 - given a ReportId", () => { - it("CRD6.1.1 - should return a list of chart report data", async () => { - // arrange - ChartReportDataMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(returnedChartData); - }); - - // act and assert - await expect(ChartReportDAO.getDataForChartReport('fakeUUID1', ChartReportDataMock)).resolves - .toEqual(expectedChartData); - }); - - it("CRD6.1.2 - should resolve false when Model cant fetch data", async () => { - // arrange - ChartReportDataMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(false); - }); - - // act and assert - await expect(ChartReportDAO.getDataForChartReport('fakeUUID1', ChartReportDataMock)).resolves - .toEqual(false); - }); - - it("CRD6.1.3 - should reject error when Model throws error with defined status and message", async () => { - // arrange - let expectedError = { - status: 404, - message: "Error." - }; - ChartReportDataMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject(expectedError); - }); - - // act and assert - await expect(ChartReportDAO.getDataForChartReport('fakeUUID1', ChartReportDataMock)).rejects - .toEqual(expectedError); - }); - - it("CRD6.1.4 - should reject error with 500 status and predefined message when model does not define them", async () => { - // arrange - let expectedError = { - status: 500, - message: "Could not fetch data." - }; - ChartReportDataMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject({}); - }); - - // act and assert - await expect(ChartReportDAO.getDataForChartReport('fakeUUID1', ChartReportDataMock)).rejects - .toEqual(expectedError); - }); - }); - }); -}); \ No newline at end of file + } + ] + describe('CRD6.1 - given a ReportId', () => { + it('CRD6.1.1 - should return a list of chart report data', async () => { + // arrange + ChartReportDataMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(returnedChartData) + }) + + // act and assert + await expect(ChartReportDAO.getDataForChartReport('fakeUUID1', ChartReportDataMock)).resolves + .toEqual(expectedChartData) + }) + + it('CRD6.1.2 - should resolve false when Model cant fetch data', async () => { + // arrange + ChartReportDataMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(false) + }) + + // act and assert + await expect(ChartReportDAO.getDataForChartReport('fakeUUID1', ChartReportDataMock)).resolves + .toEqual(false) + }) + + it('CRD6.1.3 - should reject error when Model throws error with defined status and message', async () => { + // arrange + const expectedError = { + status: 404, + message: 'Error.' + } + ChartReportDataMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject(expectedError) + }) + + // act and assert + await expect(ChartReportDAO.getDataForChartReport('fakeUUID1', ChartReportDataMock)).rejects + .toEqual(expectedError) + }) + + it('CRD6.1.4 - should reject error with 500 status and predefined message when model does not define them', async () => { + // arrange + const expectedError = { + status: 500, + message: 'Could not fetch data.' + } + ChartReportDataMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject({}) + }) + + // act and assert + await expect(ChartReportDAO.getDataForChartReport('fakeUUID1', ChartReportDataMock)).rejects + .toEqual(expectedError) + }) + }) + }) +}) diff --git a/server/tests/DAL/country.dao.test.js b/server/tests/DAL/country.dao.test.js index 735372c..b757408 100644 --- a/server/tests/DAL/country.dao.test.js +++ b/server/tests/DAL/country.dao.test.js @@ -1,98 +1,95 @@ -const CountryDao = require("../../data_access_layer/daos/country.dao"); - - -let fakeCountriesList = [ - { - COUNTRY_LABEL: "Albania" - }, - { - COUNTRY_LABEL: "Italy" - }, - { - COUNTRY_LABEL: "Morocco" - } +const CountryDao = require('../../data_access_layer/daos/country.dao') + +const fakeCountriesList = [ + { + COUNTRY_LABEL: 'Albania' + }, + { + COUNTRY_LABEL: 'Italy' + }, + { + COUNTRY_LABEL: 'Morocco' + } ] - -describe("Test Country DAO", () => { - describe("CD1 - getAllCountries", () => { - it("CD1.1 - Should return list of countries", async () => { - - // arrange - let dbStub = { - query: () => { - return fakeCountriesList; - } - }; - - let expectedResponse = [ - { - countryLabel: fakeCountriesList[0].COUNTRY_LABEL - }, - { - countryLabel: fakeCountriesList[1].COUNTRY_LABEL - }, - { - countryLabel: fakeCountriesList[2].COUNTRY_LABEL - } - ]; - - // act - const response = await CountryDao.getAllCountries(dbStub); - - // assert - expect(response).toEqual(expectedResponse); - }); - - it("CD1.2 - should return false when db cant fetch data", async () => { - // arrange - let dbStub = { - query: () => { - return false; - } - }; - - // act and assert - await expect(CountryDao.getAllCountries(dbStub)).resolves - .toEqual(false); - }); - - it("CD1.3 - should return a specific error message when it is of 500", async () => { - // arrange - - let expectedError = { - message: "Error with the db.", - status: 500 - } - - let dbStub = { - query: () => { - return Promise.reject(expectedError); - } - }; - - // act and assert - await expect(CountryDao.getAllCountries(dbStub)).rejects - .toEqual(expectedError); - }); - - it("CD1.4 - should return an unspecific error message", async () => { - // arrange - - let expectedError = { - message: "some error occured", - status: 500 - } - - let dbStub = { - query: () => { - return Promise.reject({}); - } - }; - - // act and assert - await expect(CountryDao.getAllCountries(dbStub)).rejects - .toEqual(expectedError); - }) - }); -}); \ No newline at end of file +describe('Test Country DAO', () => { + describe('CD1 - getAllCountries', () => { + it('CD1.1 - Should return list of countries', async () => { + // arrange + const dbStub = { + query: () => { + return fakeCountriesList + } + } + + const expectedResponse = [ + { + countryLabel: fakeCountriesList[0].COUNTRY_LABEL + }, + { + countryLabel: fakeCountriesList[1].COUNTRY_LABEL + }, + { + countryLabel: fakeCountriesList[2].COUNTRY_LABEL + } + ] + + // act + const response = await CountryDao.getAllCountries(dbStub) + + // assert + expect(response).toEqual(expectedResponse) + }) + + it('CD1.2 - should return false when db cant fetch data', async () => { + // arrange + const dbStub = { + query: () => { + return false + } + } + + // act and assert + await expect(CountryDao.getAllCountries(dbStub)).resolves + .toEqual(false) + }) + + it('CD1.3 - should return a specific error message when it is of 500', async () => { + // arrange + + const expectedError = { + message: 'Error with the db.', + status: 500 + } + + const dbStub = { + query: () => { + return Promise.reject(expectedError) + } + } + + // act and assert + await expect(CountryDao.getAllCountries(dbStub)).rejects + .toEqual(expectedError) + }) + + it('CD1.4 - should return an unspecific error message', async () => { + // arrange + + const expectedError = { + message: 'some error occured', + status: 500 + } + + const dbStub = { + query: () => { + return Promise.reject({}) + } + } + + // act and assert + await expect(CountryDao.getAllCountries(dbStub)).rejects + .toEqual(expectedError) + }) + }) +}) diff --git a/server/tests/DAL/emp.dao.test.js b/server/tests/DAL/emp.dao.test.js index 1aaa2b5..1b15c73 100644 --- a/server/tests/DAL/emp.dao.test.js +++ b/server/tests/DAL/emp.dao.test.js @@ -1,129 +1,128 @@ -const { sequelize, - dataTypes, - checkModelName, - checkPropertyExists, - checkHookDefined -} = require('sequelize-test-helpers'); - -const EmpModel = require('../../data_access_layer/models/mssql_pat/employee.model'); -const EmpDAO = require('../../data_access_layer/daos/emp.dao'); +const { + sequelize, + dataTypes, + checkModelName, + checkPropertyExists +} = require('sequelize-test-helpers') + +const EmpModel = require('../../data_access_layer/models/mssql_pat/employee.model') +const EmpDAO = require('../../data_access_layer/daos/emp.dao') + +const returnedEmp = { + dataValues: { + email: 'myEmail@email.com', + firstName: 'myFName', + lastName: 'myLName' + } +} -let returnedEmp = { +const returnedEmployeeList = [ + { dataValues: { - email: 'myEmail@email.com', - firstName: 'myFName', - lastName: 'myLName' + email: 'Cathia@benoit-cote.com', + firstName: 'Cathia', + lastName: 'Zeppetelli', + isActive: true } -} - -let returnedEmployeeList = [ - { - dataValues: { - email: 'Cathia@benoit-cote.com', - firstName: 'Cathia', - lastName: 'Zeppetelli', - isActive: true - }, - }, - { - dataValues: { - email: 'Giuseppe@benoit-cote.com', - firstName: 'Giuseppe', - lastName: 'Calderone', - isActive: true - }, - }, - { - dataValues: { - email: 'Marilyne@benoit-cote.com', - firstName: 'Marilyne', - lastName: 'Séïde', - isActive: true - }, + }, + { + dataValues: { + email: 'Giuseppe@benoit-cote.com', + firstName: 'Giuseppe', + lastName: 'Calderone', + isActive: true + } + }, + { + dataValues: { + email: 'Marilyne@benoit-cote.com', + firstName: 'Marilyne', + lastName: 'Séïde', + isActive: true } + } ] -let SequelizeMock = require('sequelize-mock'); -const dbMock = new SequelizeMock(); -var EmpMock = dbMock.define('employees', returnedEmp); -var EmpListMock = dbMock.define('employees', returnedEmployeeList) - -describe("Test Employee DAL", () => { - const Model = EmpModel(sequelize, dataTypes); - const instance = new Model(); - - afterEach(() => { - EmpMock.$queryInterface.$clearResults(); - EmpListMock.$queryInterface.$clearResults(); - }); - - beforeEach(() => { - EmpMock.$queryInterface.$clearResults(); - EmpListMock.$queryInterface.$clearResults(); - }); - - // testing the employee model properties - checkModelName(Model)('PERSON'); - ['firstName', 'lastName', 'email', 'isActive'] - .forEach(checkPropertyExists(instance)); - - describe("ED1 - getEmployeeByEmail", () => { - it("ED1.1 - should return employee when it exists", async () => { - // arrange - EmpMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(returnedEmp); - }); - - // act - const resp = await EmpDAO.getEmployeeByEmail("myEmail@email.com", EmpMock); - - // assert - expect(resp.email).toBe("myEmail@email.com"); - expect(resp.name).toBe("myFName myLName"); - }); - - it("ED1.2 - should return false when Employee model can't find employee", async () => { - // arrange - EmpMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(false); - }); - - // act - const resp = await EmpDAO.getEmployeeByEmail("no@email.com", EmpMock); - - // assert - expect(resp).toBeFalsy(); - }); - - it("ED1.3 - should catch specified error thrown by the Employee Model", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - EmpMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject(expectedResponse); - }); - - // act - await expect(EmpDAO.getEmployeeByEmail("someEmail", EmpMock)) - .rejects.toEqual(expectedResponse); - }); - - it("ED1.4 - should catch unspecified error thrown by the Employee Model", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "some error occured" - }; - EmpMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject({}); - }); - - // act - await expect(EmpDAO.getEmployeeByEmail("someEmail", EmpMock)) - .rejects.toEqual(expectedResponse); - }); - }); -}); - +const SequelizeMock = require('sequelize-mock') +const dbMock = new SequelizeMock() +const EmpMock = dbMock.define('employees', returnedEmp) +const EmpListMock = dbMock.define('employees', returnedEmployeeList) + +describe('Test Employee DAL', () => { + const Model = EmpModel(sequelize, dataTypes) + const instance = new Model() + + afterEach(() => { + EmpMock.$queryInterface.$clearResults() + EmpListMock.$queryInterface.$clearResults() + }) + + beforeEach(() => { + EmpMock.$queryInterface.$clearResults() + EmpListMock.$queryInterface.$clearResults() + }) + + // testing the employee model properties + checkModelName(Model)('PERSON'); + ['firstName', 'lastName', 'email', 'isActive'] + .forEach(checkPropertyExists(instance)) + + describe('ED1 - getEmployeeByEmail', () => { + it('ED1.1 - should return employee when it exists', async () => { + // arrange + EmpMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(returnedEmp) + }) + + // act + const resp = await EmpDAO.getEmployeeByEmail('myEmail@email.com', EmpMock) + + // assert + expect(resp.email).toBe('myEmail@email.com') + expect(resp.name).toBe('myFName myLName') + }) + + it("ED1.2 - should return false when Employee model can't find employee", async () => { + // arrange + EmpMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(false) + }) + + // act + const resp = await EmpDAO.getEmployeeByEmail('no@email.com', EmpMock) + + // assert + expect(resp).toBeFalsy() + }) + + it('ED1.3 - should catch specified error thrown by the Employee Model', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + EmpMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject(expectedResponse) + }) + + // act + await expect(EmpDAO.getEmployeeByEmail('someEmail', EmpMock)) + .rejects.toEqual(expectedResponse) + }) + + it('ED1.4 - should catch unspecified error thrown by the Employee Model', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'some error occured' + } + EmpMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject({}) + }) + + // act + await expect(EmpDAO.getEmployeeByEmail('someEmail', EmpMock)) + .rejects.toEqual(expectedResponse) + }) + }) +}) diff --git a/server/tests/DAL/invoice_affect.dao.test.js b/server/tests/DAL/invoice_affect.dao.test.js index 1ee10a2..b98b167 100644 --- a/server/tests/DAL/invoice_affect.dao.test.js +++ b/server/tests/DAL/invoice_affect.dao.test.js @@ -1,387 +1,386 @@ -var { expect, jest } = require('@jest/globals'); - -const InvoiceAffectDao = require("../../data_access_layer/daos/invoice_affect.dao"); - -let fakeInvoiceList = [ - { - INVOCIE_DATE: new Date(2020, 11, 1), - ACTOR_ID: 1, - AFFECT_AMOUNT: 100 - }, - { - INVOCIE_DATE: new Date(2020, 12, 1), - ACTOR_ID: 2, - AFFECT_AMOUNT: 125 - }, - { - INVOCIE_DATE: new Date(2020, 110, 1), - ACTOR_ID: 3, - AFFECT_AMOUNT: 150 +const { expect } = require('@jest/globals') + +const InvoiceAffectDao = require('../../data_access_layer/daos/invoice_affect.dao') + +const fakeInvoiceList = [ + { + INVOCIE_DATE: new Date(2020, 11, 1), + ACTOR_ID: 1, + AFFECT_AMOUNT: 100 + }, + { + INVOCIE_DATE: new Date(2020, 12, 1), + ACTOR_ID: 2, + AFFECT_AMOUNT: 125 + }, + { + INVOCIE_DATE: new Date(2020, 110, 1), + ACTOR_ID: 3, + AFFECT_AMOUNT: 150 + } +] + +describe('Test Invoice Affect DAO', () => { + const fakeQuery = { queryString: 'fakeQuery', replacements: ['fakeReplace'] } + const prepareBilledQuerySpy = jest.spyOn(InvoiceAffectDao, 'prepareBilledQuery') + .mockImplementation(() => { return fakeQuery }) + + let dbStub = { + query: () => { + return fakeInvoiceList } -]; + } + + const startDate = '2020-11-01' + const endDate = '2021-05-01' + const employeeId = 12345 + const clientType = 'DIRECT' + const countryCode = 'CA' + const ageOfAccount = '<30' + + describe('IAD1 - getInvoicesByDate', () => { + describe('IAD1.1 - given valid response from db query', () => { + it('IAD1.1.1 - should return list of invoices', async () => { + // arrange + dbStub = { + query: () => { + return fakeInvoiceList + } + } + + const expectedResponse = [ + { + invoiceDate: fakeInvoiceList[0].INVOCIE_DATE, + actorId: fakeInvoiceList[0].ACTOR_ID, + amount: fakeInvoiceList[0].AFFECT_AMOUNT + }, + { + invoiceDate: fakeInvoiceList[1].INVOCIE_DATE, + actorId: fakeInvoiceList[1].ACTOR_ID, + amount: fakeInvoiceList[1].AFFECT_AMOUNT + }, + { + invoiceDate: fakeInvoiceList[2].INVOCIE_DATE, + actorId: fakeInvoiceList[2].ACTOR_ID, + amount: fakeInvoiceList[2].AFFECT_AMOUNT + } + ] + + // act + const response = await InvoiceAffectDao.getInvoicesByDate(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount, dbStub) + + // assert + expect(response).toEqual(expectedResponse) + expect(prepareBilledQuerySpy).toHaveBeenCalledWith(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount) + }) + }) + + describe('IAD1.2 - given invalid response from db query', () => { + it('IAD1.2.1 - should return false when db cant fetch data', async () => { + // arrange + dbStub = { + query: () => { + return false + } + } + + // act and assert + await expect(InvoiceAffectDao.getInvoicesByDate(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount, dbStub)).resolves + .toEqual(false) + expect(prepareBilledQuerySpy).toHaveBeenCalledWith(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount) + }) + + it('IAD1.2.2 - when db throws error with specified status and message, should reject specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + dbStub = { + query: () => { + return Promise.reject(expectedResponse) + } + } + + // act and assert + await expect(InvoiceAffectDao.getInvoicesByDate(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount, dbStub)) + .rejects.toEqual(expectedResponse) + expect(prepareBilledQuerySpy).toHaveBeenCalledWith(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount) + }) + + it('IAD1.2.3 - when db throws error with unspecified status and message, should reject default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not fetch invoices.' + } + dbStub = { + query: () => { + return Promise.reject({}) + } + } + + // act and assert + await expect(InvoiceAffectDao.getInvoicesByDate(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount, dbStub)) + .rejects.toEqual(expectedResponse) + expect(prepareBilledQuerySpy).toHaveBeenCalledWith(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount) + }) + }) + }) + + describe('IAD2 - prepareBilledQuery', () => { + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' "), + replacements: [startDate, endDate] + } + + describe('IAD2.1 - given startDate and endDate', () => { + it('IAD2.1.1 - should respond with query only filtering by date', async () => { + // arrange + prepareBilledQuerySpy.mockRestore() + const expectedResponse = expectedQuery + + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, undefined, undefined, undefined, undefined) + + // assert + expect(response).toEqual(expectedResponse) + }) + }) + + describe('IAD2.2 - given startDate and endDate, employeeId', () => { + it('IAD2.2.1 - should respond with query only filtering by date and employee', async () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? "), + replacements: [startDate, endDate, employeeId] + } + + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, undefined, undefined, undefined) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('IAD2.3 - given startDate and endDate, employeeId, clientType', () => { + it('IAD2.3.1 - should respond with query only filtering by date and employee and client type', async () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? "), + replacements: [startDate, endDate, employeeId, clientType] + } -describe("Test Invoice Affect DAO", () => { - let fakeQuery = { queryString: "fakeQuery", replacements: ["fakeReplace"] }; - let prepareBilledQuerySpy = jest.spyOn(InvoiceAffectDao, 'prepareBilledQuery') - .mockImplementation(() => { return fakeQuery }); + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, undefined, undefined) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('IAD2.4 - given startDate and endDate, employeeId, clientType, countryCode', () => { + it('IAD2.4.1 - should respond with query only filtering by date and employee and client type and country', async () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ', + ', [Bosco reduction].[dbo].NAME N ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? ", + ' AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? '), + replacements: [startDate, endDate, employeeId, clientType, countryCode] + } + + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, undefined) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('IAD2.5 - given startDate and endDate, employeeId, clientType, countryCode, and ageOfAccount', () => { + it('IAD2.5.1 - when <30, should respond with query only filtering by date and employee and client type and country and age of account under 30 days', async () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ', + ' LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ', + ', [Bosco reduction].[dbo].NAME N ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? ", + ' AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<30 ', + ' AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? '), + replacements: [startDate, endDate, employeeId, clientType, countryCode] + } + + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount) + + // assert + expect(response).toEqual(expectedQuery) + }) + + it('IAD2.5.2 - when 30-60, should respond with query only filtering by date and employee and client type and country and age of account between 30 and 60 days', async () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ', + ' LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ', + ', [Bosco reduction].[dbo].NAME N ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? ", + ' AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>=30 ', + 'AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<60', + ' AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? '), + replacements: [startDate, endDate, employeeId, clientType, countryCode] + } + + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, '30-60') + + // assert + expect(response).toEqual(expectedQuery) + }) + + it('IAD2.5.3 - when 60-90, should respond with query only filtering by date and employee and client type and country and age of account between 60 and 90 days', async () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ', + ' LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ', + ', [Bosco reduction].[dbo].NAME N ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? ", + ' AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>=60 ', + 'AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<=90', + ' AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? '), + replacements: [startDate, endDate, employeeId, clientType, countryCode] + } - let dbStub = { - query: () => { - return fakeInvoiceList; + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, '60-90') + + // assert + expect(response).toEqual(expectedQuery) + }) + + it('IAD2.5.4 - when >90, should respond with query only filtering by date and employee and client type and country and age of account over 90 days', async () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ', + ' LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ', + ', [Bosco reduction].[dbo].NAME N ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? ", + ' AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>90 ', + ' AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? '), + replacements: [startDate, endDate, employeeId, clientType, countryCode] } - }; - - let startDate = "2020-11-01"; - let endDate = "2021-05-01"; - let employeeId = 12345; - let clientType = "DIRECT"; - let countryCode = "CA"; - let ageOfAccount = "<30" - - describe("IAD1 - getInvoicesByDate", () => { - describe("IAD1.1 - given valid response from db query", () => { - it("IAD1.1.1 - should return list of invoices", async () => { - // arrange - dbStub = { - query: () => { - return fakeInvoiceList; - } - }; - - let expectedResponse = [ - { - invoiceDate: fakeInvoiceList[0].INVOCIE_DATE, - actorId: fakeInvoiceList[0].ACTOR_ID, - amount: fakeInvoiceList[0].AFFECT_AMOUNT - }, - { - invoiceDate: fakeInvoiceList[1].INVOCIE_DATE, - actorId: fakeInvoiceList[1].ACTOR_ID, - amount: fakeInvoiceList[1].AFFECT_AMOUNT - }, - { - invoiceDate: fakeInvoiceList[2].INVOCIE_DATE, - actorId: fakeInvoiceList[2].ACTOR_ID, - amount: fakeInvoiceList[2].AFFECT_AMOUNT - } - ]; - - // act - const response = await InvoiceAffectDao.getInvoicesByDate(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount, dbStub); - - // assert - expect(response).toEqual(expectedResponse); - expect(prepareBilledQuerySpy).toHaveBeenCalledWith(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount); - }); - }); - - describe("IAD1.2 - given invalid response from db query", () => { - it("IAD1.2.1 - should return false when db cant fetch data", async () => { - // arrange - dbStub = { - query: () => { - return false; - } - }; - - // act and assert - await expect(InvoiceAffectDao.getInvoicesByDate(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount, dbStub)).resolves - .toEqual(false); - expect(prepareBilledQuerySpy).toHaveBeenCalledWith(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount); - }); - - it("IAD1.2.2 - when db throws error with specified status and message, should reject specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - dbStub = { - query: () => { - return Promise.reject(expectedResponse); - } - }; - - // act and assert - await expect(InvoiceAffectDao.getInvoicesByDate(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount, dbStub)) - .rejects.toEqual(expectedResponse); - expect(prepareBilledQuerySpy).toHaveBeenCalledWith(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount); - }); - - it("IAD1.2.3 - when db throws error with unspecified status and message, should reject default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not fetch invoices." - }; - dbStub = { - query: () => { - return Promise.reject({}); - } - }; - - // act and assert - await expect(InvoiceAffectDao.getInvoicesByDate(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount, dbStub)) - .rejects.toEqual(expectedResponse); - expect(prepareBilledQuerySpy).toHaveBeenCalledWith(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount); - }); - }); - }); - - describe("IAD2 - prepareBilledQuery", () => { - - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' "), - replacements: [startDate, endDate] - }; - - describe("IAD2.1 - given startDate and endDate", () => { - it("IAD2.1.1 - should respond with query only filtering by date", async () => { - // arrange - prepareBilledQuerySpy.mockRestore(); - let expectedResponse = expectedQuery; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, undefined, undefined, undefined, undefined); - - // assert - expect(response).toEqual(expectedResponse); - }); - }); - - describe("IAD2.2 - given startDate and endDate, employeeId", () => { - it("IAD2.2.1 - should respond with query only filtering by date and employee", async () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? "), - replacements: [startDate, endDate, employeeId] - }; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, undefined, undefined, undefined); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("IAD2.3 - given startDate and endDate, employeeId, clientType", () => { - it("IAD2.3.1 - should respond with query only filtering by date and employee and client type", async () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? "), - replacements: [startDate, endDate, employeeId, clientType] - }; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, undefined, undefined); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("IAD2.4 - given startDate and endDate, employeeId, clientType, countryCode", () => { - it("IAD2.4.1 - should respond with query only filtering by date and employee and client type and country", async () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ", - ", [Bosco reduction].[dbo].NAME N ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? ", - " AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? "), - replacements: [startDate, endDate, employeeId, clientType, countryCode] - }; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, undefined); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("IAD2.5 - given startDate and endDate, employeeId, clientType, countryCode, and ageOfAccount", () => { - it("IAD2.5.1 - when <30, should respond with query only filtering by date and employee and client type and country and age of account under 30 days", async () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ", - " LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ", - ", [Bosco reduction].[dbo].NAME N ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? ", - " AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<30 ", - " AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? "), - replacements: [startDate, endDate, employeeId, clientType, countryCode] - }; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, ageOfAccount); - - // assert - expect(response).toEqual(expectedQuery); - }); - - it("IAD2.5.2 - when 30-60, should respond with query only filtering by date and employee and client type and country and age of account between 30 and 60 days", async () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ", - " LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ", - ", [Bosco reduction].[dbo].NAME N ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? ", - " AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>=30 ", - "AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<60", - " AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? "), - replacements: [startDate, endDate, employeeId, clientType, countryCode] - }; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, "30-60"); - - // assert - expect(response).toEqual(expectedQuery); - }); - - it("IAD2.5.3 - when 60-90, should respond with query only filtering by date and employee and client type and country and age of account between 60 and 90 days", async () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ", - " LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ", - ", [Bosco reduction].[dbo].NAME N ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? ", - " AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>=60 ", - "AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<=90", - " AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? "), - replacements: [startDate, endDate, employeeId, clientType, countryCode] - }; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, "60-90"); - - // assert - expect(response).toEqual(expectedQuery); - }); - - it("IAD2.5.4 - when >90, should respond with query only filtering by date and employee and client type and country and age of account over 90 days", async () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ1 ON NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5 ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ", - " LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ", - ", [Bosco reduction].[dbo].NAME N ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ1.DROPDOWN_CODE=? AND NQ2.DROPDOWN_CODE=? ", - " AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)>90 ", - " AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? "), - replacements: [startDate, endDate, employeeId, clientType, countryCode] - }; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, ">90"); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("IAD2.6 - given startDate, endDate, ageOfAccount, but no employeeId", () => { - it("IAD2.6.1 - should respond with query only filtering by date and age of account", async () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ", - " LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' ", - " AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<30 "), - replacements: [startDate, endDate] - }; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, undefined, undefined, undefined, ageOfAccount); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("IAD2.7 - given startDate, endDate and client Type but no employeeId", () => { - it("IAD2.7.1 - should respond with query filtered by date and client type", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ", - " LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ2.DROPDOWN_CODE=? "), - replacements: [startDate, endDate, clientType] - }; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, undefined, clientType, undefined, undefined); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("IAD2.8 - given startDate, endDate and countryCode but no employeeId or clientType", () => { - it("IAD2.8.1 - should respond with query filtered by date and country", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ", - "FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ", - "LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ", - ", [Bosco reduction].[dbo].NAME N ", - "WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ", - "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' ", - " AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? "), - replacements: [startDate, endDate, countryCode] - }; - - // act - const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, undefined, undefined, countryCode, undefined); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - }); -}); + + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, employeeId, clientType, countryCode, '>90') + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('IAD2.6 - given startDate, endDate, ageOfAccount, but no employeeId', () => { + it('IAD2.6.1 - should respond with query only filtering by date and age of account', async () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ', + ' LEFT OUTER JOIN [Bosco reduction].[dbo].ACCOUNTING_CLIENT AC ON AC.TRANSACTION_REF=CONVERT(NVARCHAR,IH.INVOICE_ID) ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' ", + ' AND DATEDIFF(day, IH.INVOCIE_DATE, AC.CLEARING_LAST_TRANSACTION)<30 '), + replacements: [startDate, endDate] + } + + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, undefined, undefined, undefined, ageOfAccount) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('IAD2.7 - given startDate, endDate and client Type but no employeeId', () => { + it('IAD2.7.1 - should respond with query filtered by date and client type', () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ', + ' LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_QUALITY NQ2 ON NQ2.NAME_ID=NC.NAME_ID AND NQ2.QUALITY_TYPE_ID=3 ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' AND NQ2.DROPDOWN_CODE=? "), + replacements: [startDate, endDate, clientType] + } + + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, undefined, clientType, undefined, undefined) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('IAD2.8 - given startDate, endDate and countryCode but no employeeId or clientType', () => { + it('IAD2.8.1 - should respond with query filtered by date and country', () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT IH.INVOCIE_DATE, IH.ACTOR_ID, BIA.AFFECT_AMOUNT ', + 'FROM BOSCO_INVOICE_AFFECT BIA, INVOICE_HEADER IH ', + 'LEFT OUTER JOIN [Bosco reduction].[dbo].NAME_CONNECTION NC ON NC.CONNECTION_ID=1 AND NC.CONNECTION_NAME_ID=CONVERT(nvarchar, IH.ACTOR_ID) ', + ', [Bosco reduction].[dbo].NAME N ', + 'WHERE IH.INVOICE_TYPE in (1,4) AND IH.INVOICE_PREVIEW=0 AND IH.INVOCIE_DATE BETWEEN ? AND ? ', + "AND BIA.INVOICE_ID=IH.INVOICE_ID AND BIA.AFFECT_ACCOUNT LIKE '%1200%' ", + ' AND N.NAME_ID=NC.NAME_ID AND N.LEGAL_COUNTRY_CODE=? '), + replacements: [startDate, endDate, countryCode] + } + + // act + const response = InvoiceAffectDao.prepareBilledQuery(startDate, endDate, undefined, undefined, countryCode, undefined) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + }) +}) diff --git a/server/tests/DAL/name.dao.test.js b/server/tests/DAL/name.dao.test.js index 3714a3b..c7adc47 100644 --- a/server/tests/DAL/name.dao.test.js +++ b/server/tests/DAL/name.dao.test.js @@ -1,166 +1,162 @@ -var { expect, jest } = require('@jest/globals'); - -const NameDao = require("../../data_access_layer/daos/name.dao"); - -let fakeGetClientDbQueryResponse = [ - { - NAME_ID: 20100, - NAME: "National Research Council of Canada", - COUNTRY_LABEL: "Canada", - GRADING: "C" - }, - { - NAME_ID: 20234, - NAME: "Groupe Patrick Ménard Assurances Inc. Groupe Jetté", - COUNTRY_LABEL: "Canada", - GRADING: "B" - }, - { - NAME_ID: 20330, - NAME: "Martin Aubé", - COUNTRY_LABEL: "Japan", - GRADING: null - } -]; - -let returnedEmployeeList = [ - { - NAME_ID: 29910, - FULLNAME: 'Cathia Zeppetelli' - }, - { - NAME_ID: 29569, - FULLNAME: 'France Cote' - } -]; - -let fakeClientIdList = [12345, 12346, 12347]; - - - -describe("Test Name DAO", () => { - - describe("ND1 - getClientInformation", () => { - - describe("ND1.1 - given valid response from db query", () => { - it("ND1.1.1 - Should return list of clients", async () => { - // arrange - let expectedResponse = [ - { - nameId: fakeGetClientDbQueryResponse[0].NAME_ID, - name: fakeGetClientDbQueryResponse[0].NAME, - country: fakeGetClientDbQueryResponse[0].COUNTRY_LABEL, - grading: fakeGetClientDbQueryResponse[0].GRADING - }, - { - nameId: fakeGetClientDbQueryResponse[1].NAME_ID, - name: fakeGetClientDbQueryResponse[1].NAME, - country: fakeGetClientDbQueryResponse[1].COUNTRY_LABEL, - grading: fakeGetClientDbQueryResponse[1].GRADING - }, - { - nameId: fakeGetClientDbQueryResponse[2].NAME_ID, - name: fakeGetClientDbQueryResponse[2].NAME, - country: fakeGetClientDbQueryResponse[2].COUNTRY_LABEL, - grading: "N/A" - } - ]; - let dbStub = { - query: () => { - return Promise.resolve(fakeGetClientDbQueryResponse); - } - }; - - // act and assert - await expect(NameDao.getClientsInClientIdList(fakeClientIdList, dbStub)) - .resolves.toEqual(expectedResponse) - }); - }); - - describe("ND1.2 - given invalid response from db query", () => { - it("ND1.2.1 - should return false when db cant fetch data", async () => { - // arrange - let dbStub = { - query: () => { - return Promise.resolve(false); - } - }; - - // act and assert - await expect(NameDao.getClientsInClientIdList(fakeClientIdList, dbStub)).resolves - .toEqual(false); - }); - - it("ND1.2.2 - should catch error when db throws error", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not fetch clients." - }; - let dbStub = { - query: () => { - return Promise.reject({}); - } - }; - - // act and assert - await expect(NameDao.getClientsInClientIdList(fakeClientIdList, dbStub)).rejects - .toEqual(expectedResponse); - }); - }); - }); - - describe("ND2 - getAllEmployeeNames", () => { - it("ND2.1 - Should return a list of employees", async () => { - // arrange - let dbStub = { - query: () => { - return returnedEmployeeList; - } - }; - - let expectedEmployeeList = [ - { - nameID: 29910, - name: 'Cathia Zeppetelli' - }, - { - nameID: 29569, - name: 'France Cote' - } - ] - - // act - const response = await NameDao.getAllEmployeeNames(dbStub); - - // assert - expect(response).toEqual(expectedEmployeeList); - }); - - it("ND2.2 - should resolve false when Model cant fetch data", async () => { - // arrange - let dbStub = { - query: () => { - return false; - } - }; - - // act and assert - await expect(NameDao.getAllEmployeeNames(dbStub)).resolves - .toEqual(false); - }); - - it("ND2.3 - should reject error with 500 status with error message", async () => { - // arrange - let dbStub = { - query: () => { - throw new Error("Error with the db."); - } - }; - - // act and assert - await expect(NameDao.getAllEmployeeNames(dbStub)).rejects - .toEqual(new Error("Error with the db.")); - }); - }); -}); \ No newline at end of file +const { expect } = require('@jest/globals') + +const NameDao = require('../../data_access_layer/daos/name.dao') + +const fakeGetClientDbQueryResponse = [ + { + NAME_ID: 20100, + NAME: 'National Research Council of Canada', + COUNTRY_LABEL: 'Canada', + GRADING: 'C' + }, + { + NAME_ID: 20234, + NAME: 'Groupe Patrick Ménard Assurances Inc. Groupe Jetté', + COUNTRY_LABEL: 'Canada', + GRADING: 'B' + }, + { + NAME_ID: 20330, + NAME: 'Martin Aubé', + COUNTRY_LABEL: 'Japan', + GRADING: null + } +] + +const returnedEmployeeList = [ + { + NAME_ID: 29910, + FULLNAME: 'Cathia Zeppetelli' + }, + { + NAME_ID: 29569, + FULLNAME: 'France Cote' + } +] + +const fakeClientIdList = [12345, 12346, 12347] + +describe('Test Name DAO', () => { + describe('ND1 - getClientInformation', () => { + describe('ND1.1 - given valid response from db query', () => { + it('ND1.1.1 - Should return list of clients', async () => { + // arrange + const expectedResponse = [ + { + nameId: fakeGetClientDbQueryResponse[0].NAME_ID, + name: fakeGetClientDbQueryResponse[0].NAME, + country: fakeGetClientDbQueryResponse[0].COUNTRY_LABEL, + grading: fakeGetClientDbQueryResponse[0].GRADING + }, + { + nameId: fakeGetClientDbQueryResponse[1].NAME_ID, + name: fakeGetClientDbQueryResponse[1].NAME, + country: fakeGetClientDbQueryResponse[1].COUNTRY_LABEL, + grading: fakeGetClientDbQueryResponse[1].GRADING + }, + { + nameId: fakeGetClientDbQueryResponse[2].NAME_ID, + name: fakeGetClientDbQueryResponse[2].NAME, + country: fakeGetClientDbQueryResponse[2].COUNTRY_LABEL, + grading: 'N/A' + } + ] + const dbStub = { + query: () => { + return Promise.resolve(fakeGetClientDbQueryResponse) + } + } + + // act and assert + await expect(NameDao.getClientsInClientIdList(fakeClientIdList, dbStub)) + .resolves.toEqual(expectedResponse) + }) + }) + + describe('ND1.2 - given invalid response from db query', () => { + it('ND1.2.1 - should return false when db cant fetch data', async () => { + // arrange + const dbStub = { + query: () => { + return Promise.resolve(false) + } + } + + // act and assert + await expect(NameDao.getClientsInClientIdList(fakeClientIdList, dbStub)).resolves + .toEqual(false) + }) + + it('ND1.2.2 - should catch error when db throws error', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not fetch clients.' + } + const dbStub = { + query: () => { + return Promise.reject({}) + } + } + + // act and assert + await expect(NameDao.getClientsInClientIdList(fakeClientIdList, dbStub)).rejects + .toEqual(expectedResponse) + }) + }) + }) + + describe('ND2 - getAllEmployeeNames', () => { + it('ND2.1 - Should return a list of employees', async () => { + // arrange + const dbStub = { + query: () => { + return returnedEmployeeList + } + } + + const expectedEmployeeList = [ + { + nameID: 29910, + name: 'Cathia Zeppetelli' + }, + { + nameID: 29569, + name: 'France Cote' + } + ] + + // act + const response = await NameDao.getAllEmployeeNames(dbStub) + + // assert + expect(response).toEqual(expectedEmployeeList) + }) + + it('ND2.2 - should resolve false when Model cant fetch data', async () => { + // arrange + const dbStub = { + query: () => { + return false + } + } + + // act and assert + await expect(NameDao.getAllEmployeeNames(dbStub)).resolves + .toEqual(false) + }) + + it('ND2.3 - should reject error with 500 status with error message', async () => { + // arrange + const dbStub = { + query: () => { + throw new Error('Error with the db.') + } + } + + // act and assert + await expect(NameDao.getAllEmployeeNames(dbStub)).rejects + .toEqual(new Error('Error with the db.')) + }) + }) +}) diff --git a/server/tests/DAL/report.dao.test.js b/server/tests/DAL/report.dao.test.js index fa84ad7..9c95f65 100644 --- a/server/tests/DAL/report.dao.test.js +++ b/server/tests/DAL/report.dao.test.js @@ -1,434 +1,433 @@ -const { sequelize, - dataTypes, - checkModelName, - checkPropertyExists -} = require('sequelize-test-helpers'); - -const [UserModel, ChartReportModel, ChartReportDataModel, PerformanceReportModel, ReportTypeModel, RecipientModel, ReportTypeRecipientModel] = require('../../data_access_layer/models/localdb/localdb.model')(sequelize, dataTypes); -const ReportDAO = require('../../data_access_layer/daos/report.dao'); - - +const { + sequelize, + dataTypes, + checkModelName, + checkPropertyExists +} = require('sequelize-test-helpers') + +const Models = require('../../data_access_layer/models/localdb/localdb.model')(sequelize, dataTypes) +const ReportDAO = require('../../data_access_layer/daos/report.dao') +const PerformanceReportModel = Models[3]; +const ReportTypeModel = Models[4]; +const RecipientModel = Models[5]; +const ReportTypeRecipientModel = Models[6]; + +const returnedPerformanceReports = [ + { + performanceReportId: 'PerformanceUUID', + employeeId: 1, + averageCollectionDay: '35', + annualBillingObjective: '4500', + monthlyBillingObjective: '300', + annualBillingNumber: '200', + monthlyBillingNumber: '300', + projectedBonus: '650' + }, + { + performanceReportId: 'PerformanceUUID', + employeeId: 2, + averageCollectionDay: '35', + annualBillingObjective: '4500', + monthlyBillingObjective: '300', + annualBillingNumber: '200', + monthlyBillingNumber: '300', + projectedBonus: '650' + } +] + +const SequelizeMock = require('sequelize-mock') +const dbMock = new SequelizeMock() +const PerformanceReportMock = dbMock.define('performance_reports', returnedPerformanceReports) + +describe('Test Report Related Models', () => { + const ReportTypeInstance = new ReportTypeModel() + const RecipientInstance = new RecipientModel() + const PerformanceInstance = new PerformanceReportModel() + checkModelName(PerformanceReportModel)('performance_reports') + checkModelName(ReportTypeModel)('report_types') + checkModelName(RecipientModel)('recipients') + checkModelName(ReportTypeRecipientModel)('report_type_recipients'); + + ['performanceReportId', 'employeeId', 'averageCollectionDay', 'annualBillingObjective', 'monthlyBillingObjective', 'annualBillingNumber', 'monthlyBillingNumber', 'projectedBonus'] + .forEach(checkPropertyExists(PerformanceInstance)); + + ['reportTypeId', 'reportTypeName', 'frequency'] + .forEach(checkPropertyExists(ReportTypeInstance)); + + ['recipientId', 'name', 'email'] + .forEach(checkPropertyExists(RecipientInstance)) +}) + +describe('Test Report DAO', () => { + let fakeReportTypeModel + let fakeReportTypeRecipientModel + + const fakeReportTypeFindAllResponse = [ + { + dataValues: { + reportTypeId: 'ReportUUID1', + reportTypeName: 'ReportName1', + frequency: 0 + } + }, + { + dataValues: { + reportTypeId: 'ReportUUID2', + reportTypeName: 'ReportName2', + frequency: -1 + } + }, + { + dataValues: { + reportTypeId: 'ReportUUID4', + reportTypeName: 'ReportName4', + frequency: 2 + } + } + ] -let returnedPerformanceReports = [ + const fakeReportTypeRecipientFindAllResponse = [ { - performanceReportId: "PerformanceUUID", - employeeId: 1, - averageCollectionDay: "35", - annualBillingObjective: "4500", - monthlyBillingObjective: "300", - annualBillingNumber: "200", - monthlyBillingNumber: "300", - projectedBonus: "650" + dataValues: { + report_type_id: 'ReportUUID1', + recipient_id: 'recUUID1' + } }, { - performanceReportId: "PerformanceUUID", - employeeId: 2, - averageCollectionDay: "35", - annualBillingObjective: "4500", - monthlyBillingObjective: "300", - annualBillingNumber: "200", - monthlyBillingNumber: "300", - projectedBonus: "650" + dataValues: { + report_type_id: 'ReportUUID1', + recipient_id: 'recUUID2' + } + }, + { + dataValues: { + report_type_id: 'ReportUUID1', + recipient_id: 'recUUID3' + } + }, + { + dataValues: { + report_type_id: 'ReportUUID3', + recipient_id: 'recUUID4' + } + }, + { + dataValues: { + report_type_id: 'ReportUUID2', + recipient_id: 'recUUID5' + } } -] - -let SequelizeMock = require('sequelize-mock'); -const dbMock = new SequelizeMock(); -var PerformanceReportMock = dbMock.define('performance_reports', returnedPerformanceReports); - - -describe("Test Report Related Models", () => { - const ReportTypeInstance = new ReportTypeModel(); - const RecipientInstance = new RecipientModel(); - const PerformanceInstance = new PerformanceReportModel(); - checkModelName(PerformanceReportModel)('performance_reports'); - checkModelName(ReportTypeModel)('report_types'); - checkModelName(RecipientModel)('recipients'); - checkModelName(ReportTypeRecipientModel)('report_type_recipients'); - - ['performanceReportId', 'employeeId', 'averageCollectionDay', 'annualBillingObjective', 'monthlyBillingObjective', 'annualBillingNumber', 'monthlyBillingNumber', 'projectedBonus'] - .forEach(checkPropertyExists(PerformanceInstance)); - - ['reportTypeId', 'reportTypeName', 'frequency'] - .forEach(checkPropertyExists(ReportTypeInstance)); - - ['recipientId', 'name', 'email'] - .forEach(checkPropertyExists(RecipientInstance)); -}); - -describe("Test Report DAO", () => { - let fakeReportTypeModel; - let fakeReportTypeRecipientModel; - - let fakeReportTypeFindAllResponse = [ - { - dataValues: { - reportTypeId: "ReportUUID1", - reportTypeName: "ReportName1", - frequency: 0 + ] + describe('RD1 - getReportTypesWithRecipientIds', () => { + describe('RD1.1 - given valid response from reportTypes and reportTypeRecipients models', () => { + it('RD1.1.1 - should respond with report types with recipients', async () => { + // arrange + const expectedResponse = [ + { + reportTypeId: fakeReportTypeFindAllResponse[0].dataValues.reportTypeId, + name: fakeReportTypeFindAllResponse[0].dataValues.reportTypeName, + frequency: fakeReportTypeFindAllResponse[0].dataValues.frequency, + recipients: { + recUUID1: { + name: '' + }, + recUUID2: { + name: '' + }, + recUUID3: { + name: '' + } } - }, - { - dataValues: { - reportTypeId: "ReportUUID2", - reportTypeName: "ReportName2", - frequency: -1 - } - }, - { - dataValues: { - reportTypeId: "ReportUUID4", - reportTypeName: "ReportName4", - frequency: 2 + + }, + { + reportTypeId: fakeReportTypeFindAllResponse[1].dataValues.reportTypeId, + name: fakeReportTypeFindAllResponse[1].dataValues.reportTypeName, + frequency: fakeReportTypeFindAllResponse[1].dataValues.frequency, + recipients: { + recUUID5: { + name: '' + } } + + }, + { + reportTypeId: fakeReportTypeFindAllResponse[2].dataValues.reportTypeId, + name: fakeReportTypeFindAllResponse[2].dataValues.reportTypeName, + frequency: fakeReportTypeFindAllResponse[2].dataValues.frequency, + recipients: {} + } + ] + fakeReportTypeModel = { + findAll: () => { + return Promise.resolve(fakeReportTypeFindAllResponse) + } + } + fakeReportTypeRecipientModel = { + findAll: () => { + return Promise.resolve(fakeReportTypeRecipientFindAllResponse) + } } - ]; - let fakeReportTypeRecipientFindAllResponse = [ - { - dataValues: { - report_type_id: "ReportUUID1", - recipient_id: "recUUID1" - } - }, - { - dataValues: { - report_type_id: "ReportUUID1", - recipient_id: "recUUID2" - } - }, - { - dataValues: { - report_type_id: "ReportUUID1", - recipient_id: "recUUID3" - } - }, - { - dataValues: { - report_type_id: "ReportUUID3", - recipient_id: "recUUID4" - } - }, - { - dataValues: { - report_type_id: "ReportUUID2", - recipient_id: "recUUID5" - } + // act and assert + await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) + .resolves.toEqual(expectedResponse) + }) + }) + + describe('RD1.2 - given invalid response from reportType model', () => { + it('RD1.2.1 - when model resolves false, should resolve false', async () => { + // arrange + fakeReportTypeModel = { + findAll: () => { + return Promise.resolve(false) + } } - ]; - describe("RD1 - getReportTypesWithRecipientIds", () => { - describe("RD1.1 - given valid response from reportTypes and reportTypeRecipients models", () => { - it("RD1.1.1 - should respond with report types with recipients", async () => { - // arrange - let expectedResponse = [ - { - reportTypeId: fakeReportTypeFindAllResponse[0].dataValues.reportTypeId, - name: fakeReportTypeFindAllResponse[0].dataValues.reportTypeName, - frequency: fakeReportTypeFindAllResponse[0].dataValues.frequency, - recipients: { - recUUID1: { - name: "" - }, - recUUID2: { - name: "" - }, - recUUID3: { - name: "" - } - } - - }, - { - reportTypeId: fakeReportTypeFindAllResponse[1].dataValues.reportTypeId, - name: fakeReportTypeFindAllResponse[1].dataValues.reportTypeName, - frequency: fakeReportTypeFindAllResponse[1].dataValues.frequency, - recipients: { - recUUID5: { - name: "" - } - } - - }, - { - reportTypeId: fakeReportTypeFindAllResponse[2].dataValues.reportTypeId, - name: fakeReportTypeFindAllResponse[2].dataValues.reportTypeName, - frequency: fakeReportTypeFindAllResponse[2].dataValues.frequency, - recipients: {} - } - ] - fakeReportTypeModel = { - findAll: () => { - return Promise.resolve(fakeReportTypeFindAllResponse); - } - }; - fakeReportTypeRecipientModel = { - findAll: () => { - return Promise.resolve(fakeReportTypeRecipientFindAllResponse); - } - } - - // act and assert - await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) - .resolves.toEqual(expectedResponse); - }); - }); - - describe("RD1.2 - given invalid response from reportType model", () => { - it("RD1.2.1 - when model resolves false, should resolve false", async () => { - // arrange - fakeReportTypeModel = { - findAll: () => { - return Promise.resolve(false); - } - }; - - // act and assert - await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) - .resolves.toEqual(false); - }); - - it("RD1.2.2 - when model rejects specified status and message, should reject with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - fakeReportTypeModel = { - findAll: () => { - return Promise.reject(expectedResponse); - } - }; - - // act and assert - await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) - .rejects.toEqual(expectedResponse); - }); - - it("RD1.2.3 - when model rejects unspecified status and message, should reject with default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - fakeReportTypeModel = { - findAll: () => { - return Promise.reject({}); - } - }; - - // act and assert - await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) - .rejects.toEqual(expectedResponse); - }); - }); - - describe("RD1.3 - given invalid response from reportTypeRecipients model", () => { - it("RD1.3.1 - when model resolves false, should resolve false", async () => { - // arrange - fakeReportTypeModel = { - findAll: () => { - return Promise.resolve(fakeReportTypeFindAllResponse); - } - }; - fakeReportTypeRecipientModel = { - findAll: () => { - return Promise.resolve(false); - } - } - - // act and assert - await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) - .resolves.toEqual(false); - }); - - it("RD1.3.2 - when model rejects specified status and message, should reject with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - fakeReportTypeRecipientModel = { - findAll: () => { - return Promise.reject(expectedResponse); - } - }; - - // act and assert - await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) - .rejects.toEqual(expectedResponse); - }); - - it("RD1.3.3 - when model rejects unspecified status and message, should reject with default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - fakeReportTypeRecipientModel = { - findAll: () => { - return Promise.reject({}); - } - }; - - // act and assert - await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) - .rejects.toEqual(expectedResponse); - }); - }); - }); - - describe("RD2 - getRecipients", () => { - let fakeRecipientModelResponse = [ - { - recipientId: "recUUID1", - name: "name1", - email: "email1" - }, - { - recipientId: "recUUID2", - name: "name2", - email: "email2" - }, - { - recipientId: "recUUID3", - name: "name3", - email: "email3" - } - ]; - let fakeRecipientModel; - - describe("RD2.1 - given valid response from recipients model", () => { - it("RD2.1.1 - should resolve response from model", async () => { - // arrange - fakeRecipientModel = { - findAll: () => { - return Promise.resolve(fakeRecipientModelResponse); - } - }; - - // act and assert - await expect(ReportDAO.getRecipients(fakeRecipientModel)) - .resolves.toEqual(fakeRecipientModelResponse); - }); - }); - - describe("RD2.2 - given invalid response from recipients model", () => { - it("RD2.2.1 - when model resolves false, should resolve false", async () => { - // arrange - fakeRecipientModel = { - findAll: () => { - return Promise.resolve(false); - } - }; - - // act and assert - await expect(ReportDAO.getRecipients(fakeRecipientModel)) - .resolves.toEqual(false); - }); - - it("RD2.2.2 - when model reject with specified status and message, should reject specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - fakeRecipientModel = { - findAll: () => { - return Promise.reject(expectedResponse); - } - }; - - // act and assert - await expect(ReportDAO.getRecipients(fakeRecipientModel)) - .rejects.toEqual(expectedResponse); - }); - - it("RD2.2.3 - when model reject with unspecified status and message, should reject default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - fakeRecipientModel = { - findAll: () => { - return Promise.reject({}); - } - }; - - // act and assert - await expect(ReportDAO.getRecipients(fakeRecipientModel)) - .rejects.toEqual(expectedResponse); - }); - }); - }); - - - - describe("CRD5 - getPerformanceReportsWhenConnectedAsAdmin", () => { - const PerformanceReport = new PerformanceReportModel(); - - afterEach(() => { - PerformanceReportMock.$queryInterface.$clearResults(); + + // act and assert + await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) + .resolves.toEqual(false) + }) + + it('RD1.2.2 - when model rejects specified status and message, should reject with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + fakeReportTypeModel = { + findAll: () => { + return Promise.reject(expectedResponse) + } + } + + // act and assert + await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) + .rejects.toEqual(expectedResponse) + }) + + it('RD1.2.3 - when model rejects unspecified status and message, should reject with default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + fakeReportTypeModel = { + findAll: () => { + return Promise.reject({}) + } + } + + // act and assert + await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) + .rejects.toEqual(expectedResponse) + }) + }) + + describe('RD1.3 - given invalid response from reportTypeRecipients model', () => { + it('RD1.3.1 - when model resolves false, should resolve false', async () => { + // arrange + fakeReportTypeModel = { + findAll: () => { + return Promise.resolve(fakeReportTypeFindAllResponse) + } + } + fakeReportTypeRecipientModel = { + findAll: () => { + return Promise.resolve(false) + } + } + + // act and assert + await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) + .resolves.toEqual(false) + }) + + it('RD1.3.2 - when model rejects specified status and message, should reject with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + fakeReportTypeRecipientModel = { + findAll: () => { + return Promise.reject(expectedResponse) + } + } + + // act and assert + await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) + .rejects.toEqual(expectedResponse) + }) + + it('RD1.3.3 - when model rejects unspecified status and message, should reject with default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + fakeReportTypeRecipientModel = { + findAll: () => { + return Promise.reject({}) + } + } + + // act and assert + await expect(ReportDAO.getReportTypesWithRecipientIds(fakeReportTypeModel, fakeReportTypeRecipientModel)) + .rejects.toEqual(expectedResponse) + }) + }) + }) + + describe('RD2 - getRecipients', () => { + const fakeRecipientModelResponse = [ + { + recipientId: 'recUUID1', + name: 'name1', + email: 'email1' + }, + { + recipientId: 'recUUID2', + name: 'name2', + email: 'email2' + }, + { + recipientId: 'recUUID3', + name: 'name3', + email: 'email3' + } + ] + let fakeRecipientModel + + describe('RD2.1 - given valid response from recipients model', () => { + it('RD2.1.1 - should resolve response from model', async () => { + // arrange + fakeRecipientModel = { + findAll: () => { + return Promise.resolve(fakeRecipientModelResponse) + } + } + + // act and assert + await expect(ReportDAO.getRecipients(fakeRecipientModel)) + .resolves.toEqual(fakeRecipientModelResponse) + }) + }) + + describe('RD2.2 - given invalid response from recipients model', () => { + it('RD2.2.1 - when model resolves false, should resolve false', async () => { + // arrange + fakeRecipientModel = { + findAll: () => { + return Promise.resolve(false) + } + } + + // act and assert + await expect(ReportDAO.getRecipients(fakeRecipientModel)) + .resolves.toEqual(false) + }) + + it('RD2.2.2 - when model reject with specified status and message, should reject specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + fakeRecipientModel = { + findAll: () => { + return Promise.reject(expectedResponse) + } + } + + // act and assert + await expect(ReportDAO.getRecipients(fakeRecipientModel)) + .rejects.toEqual(expectedResponse) + }) + + it('RD2.2.3 - when model reject with unspecified status and message, should reject default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + fakeRecipientModel = { + findAll: () => { + return Promise.reject({}) + } + } + + // act and assert + await expect(ReportDAO.getRecipients(fakeRecipientModel)) + .rejects.toEqual(expectedResponse) + }) + }) + }) + + describe('CRD5 - getPerformanceReportsWhenConnectedAsAdmin', () => { + const PerformanceReport = new PerformanceReportModel() + + afterEach(() => { + PerformanceReportMock.$queryInterface.$clearResults() + }) + + beforeEach(() => { + PerformanceReportMock.$queryInterface.$clearResults() + }) + + describe('CRD5.1 - given a userId', () => { + it('CRD5.1.1 - should return list of performance reports', async () => { + // arrange + PerformanceReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(returnedPerformanceReports) + }) + + // act and assert + await expect(ReportDAO.getPerformanceReports('fakeUserId', PerformanceReportMock)).resolves + .toEqual(returnedPerformanceReports) + }) + + it('CRD5.1.2 - should resolve false when Model cant fetch data', async () => { + // arrange + PerformanceReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(false) }) - - beforeEach(() => { - PerformanceReportMock.$queryInterface.$clearResults(); + + // act and assert + await expect(ReportDAO.getPerformanceReports('fakeUserId', PerformanceReportMock)).resolves + .toEqual(false) + }) + + it('CRD5.1.3 - should reject error when Model throws error with defined status and message', async () => { + // arrange + const expectedError = { + status: 404, + message: 'Error.' + } + PerformanceReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject(expectedError) }) + // act and assert + await expect(ReportDAO.getPerformanceReports('fakeUserId', PerformanceReportMock)).rejects + .toEqual(expectedError) + }) + + it('CRD1.1.4 - should reject error with 500 status and predefined message when model does not define them', async () => { + // arrange + const expectedError = { + status: 500, + message: 'Could not fetch data.' + } + PerformanceReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject({}) + }) - describe("CRD5.1 - given a userId", () => { - it("CRD5.1.1 - should return list of performance reports", async () => { - // arrange - PerformanceReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(returnedPerformanceReports); - }); - - // act and assert - await expect(ReportDAO.getPerformanceReportsWhenConnectedAsAdmin('fakeUserId', PerformanceReportMock)).resolves - .toEqual(returnedPerformanceReports); - }); - - it("CRD5.1.2 - should resolve false when Model cant fetch data", async () => { - // arrange - PerformanceReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(false); - }); - - // act and assert - await expect(ReportDAO.getPerformanceReportsWhenConnectedAsAdmin('fakeUserId', PerformanceReportMock)).resolves - .toEqual(false); - }); - - it("CRD5.1.3 - should reject error when Model throws error with defined status and message", async () => { - // arrange - let expectedError = { - status: 404, - message: "Error." - }; - PerformanceReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject(expectedError); - }); - - // act and assert - await expect(ReportDAO.getPerformanceReportsWhenConnectedAsAdmin('fakeUserId', PerformanceReportMock)).rejects - .toEqual(expectedError); - }); - - it("CRD1.1.4 - should reject error with 500 status and predefined message when model does not define them", async () => { - // arrange - let expectedError = { - status: 500, - message: "Could not fetch data." - }; - PerformanceReportMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject({}); - }); - - // act and assert - await expect(ReportDAO.getPerformanceReportsWhenConnectedAsAdmin('fakeUserId', PerformanceReportMock)).rejects - .toEqual(expectedError); - }); - }); - }); -}); + // act and assert + await expect(ReportDAO.getPerformanceReports('fakeUserId', PerformanceReportMock)).rejects + .toEqual(expectedError) + }) + }) + }) +}) diff --git a/server/tests/DAL/transac_stat.dao.test.js b/server/tests/DAL/transac_stat.dao.test.js index bc0ecac..ed35b67 100644 --- a/server/tests/DAL/transac_stat.dao.test.js +++ b/server/tests/DAL/transac_stat.dao.test.js @@ -1,396 +1,382 @@ -var { expect, jest } = require('@jest/globals'); - -const TransacStatDao = require("../../data_access_layer/daos/transac_stat.dao"); - - -let returnedAccountStat = { - yearMonth: 202011, - dueCurrent: 100, - due1Month: 200, - due2Month: 50, - due3Month: 20 -}; - -let fakeStatsList = [ - { - YEAR_MONTH: 202011, - DUE_1_MONTH: 100, - DUE_2_MONTH: 200, - DUE_3_MONTH: 50, - DUE_CURRENT: 20 - }, - { - YEAR_MONTH: 202012, - DUE_1_MONTH: 100, - DUE_2_MONTH: 200, - DUE_3_MONTH: 50, - DUE_CURRENT: 20 +const { expect } = require('@jest/globals') + +const TransacStatDao = require('../../data_access_layer/daos/transac_stat.dao') + +const fakeStatsList = [ + { + YEAR_MONTH: 202011, + DUE_1_MONTH: 100, + DUE_2_MONTH: 200, + DUE_3_MONTH: 50, + DUE_CURRENT: 20 + }, + { + YEAR_MONTH: 202012, + DUE_1_MONTH: 100, + DUE_2_MONTH: 200, + DUE_3_MONTH: 50, + DUE_CURRENT: 20 + } +] + +describe('Test Transac Stat DAO', () => { + let dbStub = { + query: () => { + return Promise.resolve(fakeStatsList) } -]; + } + + const yearMonthList = [202011, 202012] + + const fakeQuery = { + queryString: 'fakeQuery', + replacements: ['fakeReplace'] + } + + const prepareDuesQuerySpy = jest.spyOn(TransacStatDao, 'prepareDuesQuery') + .mockImplementation(() => { return fakeQuery }) + + const employeeId = 12345 + const clientType = 'DIRECT' + const countryLabel = 'Canada' + const ageOfAccount = '<30' + + describe('TD1 - getTransactionsStatByYearMonth', () => { + it('TD1.1 - should return list of stats in correct format', async () => { + // arrange + const expectedResponse = [ + { + yearMonth: fakeStatsList[0].YEAR_MONTH, + dueCurrent: fakeStatsList[0].DUE_CURRENT, + due1Month: fakeStatsList[0].DUE_1_MONTH, + due2Month: fakeStatsList[0].DUE_2_MONTH, + due3Month: fakeStatsList[0].DUE_3_MONTH + }, + { + yearMonth: fakeStatsList[1].YEAR_MONTH, + dueCurrent: fakeStatsList[1].DUE_CURRENT, + due1Month: fakeStatsList[1].DUE_1_MONTH, + due2Month: fakeStatsList[1].DUE_2_MONTH, + due3Month: fakeStatsList[1].DUE_3_MONTH + } + ] + // act and assert + await expect(TransacStatDao.getTransactionsStatByYearMonth(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount, dbStub)).resolves + .toEqual(expectedResponse) + expect(prepareDuesQuerySpy).toHaveBeenCalledWith(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount) + }) -describe("Test Transac Stat DAO", () => { - let dbStub = { + it('TD1.2 - should return false when model cant fetch data', async () => { + // arrange + dbStub = { + query: () => { + return Promise.resolve(false) + } + } + + // act and assert + await expect(TransacStatDao.getTransactionsStatByYearMonth(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount, dbStub)).resolves + .toEqual(false) + expect(prepareDuesQuerySpy).toHaveBeenCalledWith(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount) + }) + + it('TD1.3 - when model throws error with specified status and message, should reject with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + dbStub = { query: () => { - return Promise.resolve(fakeStatsList); + return Promise.reject(expectedResponse) + } + } + + // act and assert + await expect(TransacStatDao.getTransactionsStatByYearMonth(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount, dbStub)).rejects + .toEqual(expectedResponse) + expect(prepareDuesQuerySpy).toHaveBeenCalledWith(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount) + }) + + it('TD1.4 - when model throws error with unspecified status and message, should reject with default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not fetch transactions.' + } + dbStub = { + query: () => { + return Promise.reject({}) + } + } + + // act and assert + await expect(TransacStatDao.getTransactionsStatByYearMonth(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount, dbStub)).rejects + .toEqual(expectedResponse) + expect(prepareDuesQuerySpy).toHaveBeenCalledWith(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount) + }) + }) + + describe('TD2 - prepareDuesQuery', () => { + const expectedQuery = { + queryString: ''.concat('SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ', + 'FROM ACCOUNTING_CLIENT_STAT ACS ', + 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1'), + replacements: [yearMonthList] + } + + describe('TD2.1 - given a yearMonthList', () => { + it('TD2.1.1 - should respond with query filtered by yearMonth', () => { + // arrange + prepareDuesQuerySpy.mockRestore() + + // act + const response = TransacStatDao.prepareDuesQuery(yearMonthList) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('TD2.2 - given a yearMonthList and an employeeId', () => { + it('TD2.2.1 - should respond with query filtered by yearMonth and employeeId', () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ', + 'FROM ACCOUNTING_CLIENT_STAT ACS ', + ', NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ', + 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1', + ' AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)', + ' AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5', + ' AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE', + ' AND NQ1.DROPDOWN_CODE=? '), + replacements: [yearMonthList, employeeId] + } + + // act + const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, undefined, undefined) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('TD2.3 - given a yearMonthList, an employeeId and a clientType', () => { + it('TD2.3.1 - should respond with query filtered by yearMonth and employeeId and clientType', () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ', + 'FROM ACCOUNTING_CLIENT_STAT ACS ', + ', NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ', + ', NAME_QUALITY NQ2 ', + 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1', + ' AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)', + ' AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5', + ' AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE', + ' AND NQ1.DROPDOWN_CODE=? ', + ' AND NC.NAME_ID=NQ2.NAME_ID', + ' AND NQ2.QUALITY_TYPE_ID=3', + ' AND NQ2.DROPDOWN_CODE=? '), + replacements: [yearMonthList, employeeId, clientType] + } + + // act + const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, undefined) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('TD2.4 - given a yearMonthList, an employeeId, a clientType and a countryLabel', () => { + it('TD2.4.1 - should respond with query filtered by yearMonth and employeeId and clientType and country', () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ', + 'FROM ACCOUNTING_CLIENT_STAT ACS ', + ', NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ', + ', NAME_QUALITY NQ2 ', + ', ACCOUNTING_NAME AN ', + 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1', + ' AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)', + ' AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5', + ' AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE', + ' AND NQ1.DROPDOWN_CODE=? ', + ' AND NC.NAME_ID=NQ2.NAME_ID', + ' AND NQ2.QUALITY_TYPE_ID=3', + ' AND NQ2.DROPDOWN_CODE=? ', + ' AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? '), + replacements: [yearMonthList, employeeId, clientType, countryLabel] + } + + // act + const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('TD2.5 - given a yearMonthList, an employeeId, a clientType and a countryLabel and ageOfAccount', () => { + it('TD2.5.1 - when <30, should respond with query filtered by yearMonth and employeeId and clientType and country and age of account under 30 days', () => { + // arrange + const expectedQuery = { + queryString: ''.concat("SELECT ACS.DUE_CURRENT, 0 as 'DUE_1_MONTH', 0 as 'DUE_2_MONTH', 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH ", + 'FROM ACCOUNTING_CLIENT_STAT ACS ', + ', NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ', + ', NAME_QUALITY NQ2 ', + ', ACCOUNTING_NAME AN ', + 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1', + ' AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)', + ' AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5', + ' AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE', + ' AND NQ1.DROPDOWN_CODE=? ', + ' AND NC.NAME_ID=NQ2.NAME_ID', + ' AND NQ2.QUALITY_TYPE_ID=3', + ' AND NQ2.DROPDOWN_CODE=? ', + ' AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? '), + replacements: [yearMonthList, employeeId, clientType, countryLabel] + } + + // act + const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount) + + // assert + expect(response).toEqual(expectedQuery) + }) + + it('TD2.5.2 - when 30-60, should respond with query filtered by yearMonth and employeeId and clientType and country and age of account 30-60 days', () => { + // arrange + const expectedQuery = { + queryString: ''.concat("SELECT 0 as 'DUE_CURRENT', ACS.DUE_1_MONTH, 0 as 'DUE_2_MONTH', 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH ", + 'FROM ACCOUNTING_CLIENT_STAT ACS ', + ', NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ', + ', NAME_QUALITY NQ2 ', + ', ACCOUNTING_NAME AN ', + 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1', + ' AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)', + ' AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5', + ' AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE', + ' AND NQ1.DROPDOWN_CODE=? ', + ' AND NC.NAME_ID=NQ2.NAME_ID', + ' AND NQ2.QUALITY_TYPE_ID=3', + ' AND NQ2.DROPDOWN_CODE=? ', + ' AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? '), + replacements: [yearMonthList, employeeId, clientType, countryLabel] + } + + // act + const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, '30-60') + + // assert + expect(response).toEqual(expectedQuery) + }) + + it('TD2.5.3 - when 60-90, should respond with query filtered by yearMonth and employeeId and clientType and country and age of account 60-90 days', () => { + // arrange + const expectedQuery = { + queryString: ''.concat("SELECT 0 as 'DUE_CURRENT', 0 as 'DUE_1_MONTH', ACS.DUE_2_MONTH, 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH ", + 'FROM ACCOUNTING_CLIENT_STAT ACS ', + ', NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ', + ', NAME_QUALITY NQ2 ', + ', ACCOUNTING_NAME AN ', + 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1', + ' AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)', + ' AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5', + ' AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE', + ' AND NQ1.DROPDOWN_CODE=? ', + ' AND NC.NAME_ID=NQ2.NAME_ID', + ' AND NQ2.QUALITY_TYPE_ID=3', + ' AND NQ2.DROPDOWN_CODE=? ', + ' AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? '), + replacements: [yearMonthList, employeeId, clientType, countryLabel] + } + + // act + const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, '60-90') + + // assert + expect(response).toEqual(expectedQuery) + }) + + it('TD2.5.4 - when >90, should respond with query filtered by yearMonth and employeeId and clientType and country and age of account over 90 days', () => { + // arrange + const expectedQuery = { + queryString: ''.concat("SELECT 0 as 'DUE_CURRENT', 0 as 'DUE_1_MONTH', 0 as 'DUE_2_MONTH', ACS.DUE_3_MONTH, ACS.YEAR_MONTH ", + 'FROM ACCOUNTING_CLIENT_STAT ACS ', + ', NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ', + ', NAME_QUALITY NQ2 ', + ', ACCOUNTING_NAME AN ', + 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1', + ' AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)', + ' AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5', + ' AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE', + ' AND NQ1.DROPDOWN_CODE=? ', + ' AND NC.NAME_ID=NQ2.NAME_ID', + ' AND NQ2.QUALITY_TYPE_ID=3', + ' AND NQ2.DROPDOWN_CODE=? ', + ' AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? '), + replacements: [yearMonthList, employeeId, clientType, countryLabel] } - }; - - let yearMonthList = [202011, 202012]; - - let fakeQuery = { - queryString: "fakeQuery", - replacements: ["fakeReplace"] - }; - - let prepareDuesQuerySpy = jest.spyOn(TransacStatDao, 'prepareDuesQuery') - .mockImplementation(() => { return fakeQuery }); - - let employeeId = 12345; - let clientType = 'DIRECT'; - let countryLabel = 'Canada'; - let ageOfAccount = "<30"; - - describe("TD1 - getTransactionsStatByYearMonth", () => { - - - - it("TD1.1 - should return list of stats in correct format", async () => { - // arrange - let expectedResponse = [ - { - yearMonth: fakeStatsList[0].YEAR_MONTH, - dueCurrent: fakeStatsList[0].DUE_CURRENT, - due1Month: fakeStatsList[0].DUE_1_MONTH, - due2Month: fakeStatsList[0].DUE_2_MONTH, - due3Month: fakeStatsList[0].DUE_3_MONTH - }, - { - yearMonth: fakeStatsList[1].YEAR_MONTH, - dueCurrent: fakeStatsList[1].DUE_CURRENT, - due1Month: fakeStatsList[1].DUE_1_MONTH, - due2Month: fakeStatsList[1].DUE_2_MONTH, - due3Month: fakeStatsList[1].DUE_3_MONTH - } - ]; - - // act and assert - await expect(TransacStatDao.getTransactionsStatByYearMonth(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount, dbStub)).resolves - .toEqual(expectedResponse); - expect(prepareDuesQuerySpy).toHaveBeenCalledWith(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount); - }); - - it("TD1.2 - should return false when model cant fetch data", async () => { - // arrange - dbStub = { - query: () => { - return Promise.resolve(false); - } - }; - - // act and assert - await expect(TransacStatDao.getTransactionsStatByYearMonth(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount, dbStub)).resolves - .toEqual(false); - expect(prepareDuesQuerySpy).toHaveBeenCalledWith(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount); - }); - - it("TD1.3 - when model throws error with specified status and message, should reject with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - dbStub = { - query: () => { - return Promise.reject(expectedResponse) - } - }; - - // act and assert - await expect(TransacStatDao.getTransactionsStatByYearMonth(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount, dbStub)).rejects - .toEqual(expectedResponse); - expect(prepareDuesQuerySpy).toHaveBeenCalledWith(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount); - }); - - it("TD1.4 - when model throws error with unspecified status and message, should reject with default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not fetch transactions." - }; - dbStub = { - query: () => { - return Promise.reject({}) - } - }; - - // act and assert - await expect(TransacStatDao.getTransactionsStatByYearMonth(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount, dbStub)).rejects - .toEqual(expectedResponse); - expect(prepareDuesQuerySpy).toHaveBeenCalledWith(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount); - }); - }); - - describe("TD2 - prepareDuesQuery", () => { - let expectedQuery = { - queryString: "".concat("SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ", - "FROM ACCOUNTING_CLIENT_STAT ACS ", - "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1"), - replacements: [yearMonthList] - }; - - describe("TD2.1 - given a yearMonthList", () => { - it("TD2.1.1 - should respond with query filtered by yearMonth", () => { - // arrange - prepareDuesQuerySpy.mockRestore(); - - // act - const response = TransacStatDao.prepareDuesQuery(yearMonthList); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("TD2.2 - given a yearMonthList and an employeeId", () => { - it("TD2.2.1 - should respond with query filtered by yearMonth and employeeId", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ", - "FROM ACCOUNTING_CLIENT_STAT ACS ", - ", NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ", - "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1", - " AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)", - " AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5", - " AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE", - " AND NQ1.DROPDOWN_CODE=? "), - replacements: [yearMonthList, employeeId] - }; - - // act - const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, undefined, undefined); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("TD2.3 - given a yearMonthList, an employeeId and a clientType", () => { - it("TD2.3.1 - should respond with query filtered by yearMonth and employeeId and clientType", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ", - "FROM ACCOUNTING_CLIENT_STAT ACS ", - ", NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ", - ", NAME_QUALITY NQ2 ", - "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1", - " AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)", - " AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5", - " AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE", - " AND NQ1.DROPDOWN_CODE=? ", - " AND NC.NAME_ID=NQ2.NAME_ID", - " AND NQ2.QUALITY_TYPE_ID=3", - " AND NQ2.DROPDOWN_CODE=? "), - replacements: [yearMonthList, employeeId, clientType] - }; - - // act - const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, undefined); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("TD2.4 - given a yearMonthList, an employeeId, a clientType and a countryLabel", () => { - it("TD2.4.1 - should respond with query filtered by yearMonth and employeeId and clientType and country", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ", - "FROM ACCOUNTING_CLIENT_STAT ACS ", - ", NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ", - ", NAME_QUALITY NQ2 ", - ", ACCOUNTING_NAME AN ", - "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1", - " AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)", - " AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5", - " AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE", - " AND NQ1.DROPDOWN_CODE=? ", - " AND NC.NAME_ID=NQ2.NAME_ID", - " AND NQ2.QUALITY_TYPE_ID=3", - " AND NQ2.DROPDOWN_CODE=? ", - " AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? "), - replacements: [yearMonthList, employeeId, clientType, countryLabel] - }; - - // act - const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("TD2.5 - given a yearMonthList, an employeeId, a clientType and a countryLabel and ageOfAccount", () => { - it("TD2.5.1 - when <30, should respond with query filtered by yearMonth and employeeId and clientType and country and age of account under 30 days", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT ACS.DUE_CURRENT, 0 as 'DUE_1_MONTH', 0 as 'DUE_2_MONTH', 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH ", - "FROM ACCOUNTING_CLIENT_STAT ACS ", - ", NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ", - ", NAME_QUALITY NQ2 ", - ", ACCOUNTING_NAME AN ", - "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1", - " AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)", - " AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5", - " AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE", - " AND NQ1.DROPDOWN_CODE=? ", - " AND NC.NAME_ID=NQ2.NAME_ID", - " AND NQ2.QUALITY_TYPE_ID=3", - " AND NQ2.DROPDOWN_CODE=? ", - " AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? "), - replacements: [yearMonthList, employeeId, clientType, countryLabel] - }; - - // act - const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, ageOfAccount); - - // assert - expect(response).toEqual(expectedQuery); - }); - - it("TD2.5.2 - when 30-60, should respond with query filtered by yearMonth and employeeId and clientType and country and age of account 30-60 days", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT 0 as 'DUE_CURRENT', ACS.DUE_1_MONTH, 0 as 'DUE_2_MONTH', 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH ", - "FROM ACCOUNTING_CLIENT_STAT ACS ", - ", NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ", - ", NAME_QUALITY NQ2 ", - ", ACCOUNTING_NAME AN ", - "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1", - " AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)", - " AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5", - " AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE", - " AND NQ1.DROPDOWN_CODE=? ", - " AND NC.NAME_ID=NQ2.NAME_ID", - " AND NQ2.QUALITY_TYPE_ID=3", - " AND NQ2.DROPDOWN_CODE=? ", - " AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? "), - replacements: [yearMonthList, employeeId, clientType, countryLabel] - }; - - // act - const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, "30-60"); - - // assert - expect(response).toEqual(expectedQuery); - }); - - it("TD2.5.3 - when 60-90, should respond with query filtered by yearMonth and employeeId and clientType and country and age of account 60-90 days", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT 0 as 'DUE_CURRENT', 0 as 'DUE_1_MONTH', ACS.DUE_2_MONTH, 0 as 'DUE_3_MONTH', ACS.YEAR_MONTH ", - "FROM ACCOUNTING_CLIENT_STAT ACS ", - ", NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ", - ", NAME_QUALITY NQ2 ", - ", ACCOUNTING_NAME AN ", - "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1", - " AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)", - " AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5", - " AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE", - " AND NQ1.DROPDOWN_CODE=? ", - " AND NC.NAME_ID=NQ2.NAME_ID", - " AND NQ2.QUALITY_TYPE_ID=3", - " AND NQ2.DROPDOWN_CODE=? ", - " AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? "), - replacements: [yearMonthList, employeeId, clientType, countryLabel] - }; - - // act - const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, "60-90"); - - // assert - expect(response).toEqual(expectedQuery); - }); - - it("TD2.5.4 - when >90, should respond with query filtered by yearMonth and employeeId and clientType and country and age of account over 90 days", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT 0 as 'DUE_CURRENT', 0 as 'DUE_1_MONTH', 0 as 'DUE_2_MONTH', ACS.DUE_3_MONTH, ACS.YEAR_MONTH ", - "FROM ACCOUNTING_CLIENT_STAT ACS ", - ", NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ", - ", NAME_QUALITY NQ2 ", - ", ACCOUNTING_NAME AN ", - "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1", - " AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)", - " AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5", - " AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE", - " AND NQ1.DROPDOWN_CODE=? ", - " AND NC.NAME_ID=NQ2.NAME_ID", - " AND NQ2.QUALITY_TYPE_ID=3", - " AND NQ2.DROPDOWN_CODE=? ", - " AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? "), - replacements: [yearMonthList, employeeId, clientType, countryLabel] - }; - - // act - const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, ">90"); - - // assert - expect(response).toEqual(expectedQuery); - }); - - it("TD2.5.5 - when any other string, should respond with query filtered by yearMonth and employeeId and clientType and country and all age of account", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ", - "FROM ACCOUNTING_CLIENT_STAT ACS ", - ", NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ", - ", NAME_QUALITY NQ2 ", - ", ACCOUNTING_NAME AN ", - "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1", - " AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)", - " AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5", - " AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE", - " AND NQ1.DROPDOWN_CODE=? ", - " AND NC.NAME_ID=NQ2.NAME_ID", - " AND NQ2.QUALITY_TYPE_ID=3", - " AND NQ2.DROPDOWN_CODE=? ", - " AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? "), - replacements: [yearMonthList, employeeId, clientType, countryLabel] - }; - - // act - const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, "otherString"); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - describe("TD2.6 - given a yearMonthList and client type but no employeeId", () => { - it("TD2.6.1 - should return query filtered by yearMonth and client Type", () => { - // arrange - let expectedQuery = { - queryString: "".concat("SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ", - "FROM ACCOUNTING_CLIENT_STAT ACS ", - ", NAME_CONNECTION NC ", - ", NAME_QUALITY NQ2 ", - "WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1", - " AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID) ", - " AND NC.NAME_ID=NQ2.NAME_ID", - " AND NQ2.QUALITY_TYPE_ID=3", - " AND NQ2.DROPDOWN_CODE=? "), - replacements: [yearMonthList, clientType] - }; - - // act - const response = TransacStatDao.prepareDuesQuery(yearMonthList, undefined, clientType, undefined); - - // assert - expect(response).toEqual(expectedQuery); - }); - }); - - }); -}); \ No newline at end of file + + // act + const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, '>90') + + // assert + expect(response).toEqual(expectedQuery) + }) + + it('TD2.5.5 - when any other string, should respond with query filtered by yearMonth and employeeId and clientType and country and all age of account', () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ', + 'FROM ACCOUNTING_CLIENT_STAT ACS ', + ', NAME_CONNECTION NC, NAME_QUALITY NQ1, NAME RESP ', + ', NAME_QUALITY NQ2 ', + ', ACCOUNTING_NAME AN ', + 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1', + ' AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID)', + ' AND NQ1.NAME_ID=NC.NAME_ID AND NQ1.QUALITY_TYPE_ID=5', + ' AND CONVERT(NVARCHAR,RESP.NAME_ID)=NQ1.DROPDOWN_CODE', + ' AND NQ1.DROPDOWN_CODE=? ', + ' AND NC.NAME_ID=NQ2.NAME_ID', + ' AND NQ2.QUALITY_TYPE_ID=3', + ' AND NQ2.DROPDOWN_CODE=? ', + ' AND ACS.ACC_NAME_ID=AN.ACC_NAME_ID AND AN.ACC_NAME_COUNTRY=? '), + replacements: [yearMonthList, employeeId, clientType, countryLabel] + } + + // act + const response = TransacStatDao.prepareDuesQuery(yearMonthList, employeeId, clientType, countryLabel, 'otherString') + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + + describe('TD2.6 - given a yearMonthList and client type but no employeeId', () => { + it('TD2.6.1 - should return query filtered by yearMonth and client Type', () => { + // arrange + const expectedQuery = { + queryString: ''.concat('SELECT ACS.DUE_CURRENT, ACS.DUE_1_MONTH, ACS.DUE_2_MONTH, ACS.DUE_3_MONTH, ACS.YEAR_MONTH ', + 'FROM ACCOUNTING_CLIENT_STAT ACS ', + ', NAME_CONNECTION NC ', + ', NAME_QUALITY NQ2 ', + 'WHERE ACS.YEAR_MONTH in (?) AND ACS.CONNECTION_ID=3 AND ACS.STAT_TYPE=1', + ' AND NC.CONNECTION_ID=3 AND NC.CONNECTION_NAME_ID=CONVERT(NVARCHAR, ACS.ACC_NAME_ID) ', + ' AND NC.NAME_ID=NQ2.NAME_ID', + ' AND NQ2.QUALITY_TYPE_ID=3', + ' AND NQ2.DROPDOWN_CODE=? '), + replacements: [yearMonthList, clientType] + } + + // act + const response = TransacStatDao.prepareDuesQuery(yearMonthList, undefined, clientType, undefined) + + // assert + expect(response).toEqual(expectedQuery) + }) + }) + }) +}) diff --git a/server/tests/DAL/user.dao.test.js b/server/tests/DAL/user.dao.test.js index f7f82c5..21004b2 100644 --- a/server/tests/DAL/user.dao.test.js +++ b/server/tests/DAL/user.dao.test.js @@ -1,323 +1,324 @@ -const { sequelize, - dataTypes, - checkModelName, - checkPropertyExists, - checkHookDefined -} = require('sequelize-test-helpers'); - -const [UserModel, ChartReportModel] = require('../../data_access_layer/models/localdb/localdb.model')(sequelize, dataTypes); -const UserDAO = require('../../data_access_layer/daos/user.dao'); - -let returnedUser = { - userId: 1, +const { + sequelize, + dataTypes, + checkModelName, + checkPropertyExists, + checkHookDefined +} = require('sequelize-test-helpers') + +const Models = require('../../data_access_layer/models/localdb/localdb.model')(sequelize, dataTypes) +const UserDAO = require('../../data_access_layer/daos/user.dao') +const UserModel = Models[0]; + +const returnedUser = { + userId: 1, + email: 'myEmail@email.com', + password: 'goodPassword1', + name: 'myName', + role: 'myRole' +} + +const createdUser = { + dataValues: { email: 'myEmail@email.com', - password: 'goodPassword1', name: 'myName', role: 'myRole' + } } -let createdUser = { +const listOfUsers = [ + { dataValues: { - email: 'myEmail@email.com', - name: 'myName', - role: 'myRole' + email: 'myEmail@email.com', + name: 'myName', + role: 'myRole' } -} - -let listOfUsers = [ - { - dataValues: { - email: 'myEmail@email.com', - name: 'myName', - role: 'myRole' - } - }, - { - dataValues: { - email: 'myEmail@email.com', - name: 'myName', - role: 'myRole' - } + }, + { + dataValues: { + email: 'myEmail@email.com', + name: 'myName', + role: 'myRole' } + } ] -let SequelizeMock = require('sequelize-mock'); -const dbMock = new SequelizeMock(); -var UserMock = dbMock.define('users', returnedUser); +const SequelizeMock = require('sequelize-mock') +const dbMock = new SequelizeMock() +const UserMock = dbMock.define('users', returnedUser) + +describe('Test User DAL', () => { + const instance = new UserModel() + + afterEach(() => { + UserMock.$queryInterface.$clearResults() + }) + + beforeEach(() => { + UserMock.$queryInterface.$clearResults() + }) + + // testing the user model properties + checkModelName(UserModel)('users'); + ['userId', 'email', 'password', 'name', 'role'] + .forEach(checkPropertyExists(instance)); + ['beforeCreate', 'beforeUpdate'].forEach(checkHookDefined(instance)) + + describe('UD1 - getUserByEmail', () => { + it('UD1.1 - should return user when existing email', async () => { + // act + const user = await UserDAO.getUserByEmail('myEmail@email.com', UserMock) + + // assert + expect(user.name).toBe('myName') + expect(user.userId).toBe(1) + expect(user.email).toBe('myEmail@email.com') + expect(user.password).toBe('goodPassword1') + expect(user.role).toBe('myRole') + }) + + it('UD1.2 - should return false when non existent email', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(false) + }) + + // act + const resp = await UserDAO.getUserByEmail('no@email.com', UserMock) + + // assert + expect(resp).toBeFalsy() + }) + + it('UD1.3 - should catch error thrown by user model', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return new Error('Error with the UserModel.') + }) + + // act and assert + const response = await UserDAO.getUserByEmail('', UserMock) + + // assert + expect(response.message).toEqual('Error with the UserModel.') + }) + + it('UD1.4 - Should reject error with 500 status and predefined message when model does not define them', async () => { + // arrange + const expectedError = { + status: 500, + message: 'some error occured' + } -describe("Test User DAL", () => { - const instance = new UserModel(); + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject({}) + }) - afterEach(() => { - UserMock.$queryInterface.$clearResults(); + // act and assert + await expect(UserDAO.getUserByEmail('', UserMock)).rejects + .toEqual(expectedError) }) + }) - beforeEach(() => { - UserMock.$queryInterface.$clearResults(); + describe('UD2 - createUser', () => { + it('UD2.1 - should return created user when valid', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(createdUser) + }) + + // act + const resp = await UserDAO.createUser(returnedUser, UserMock) + + // assert + expect(resp.email).toBe(returnedUser.email) + }) + + it('UD2.2 - should catch error thrown by the User Model with custom message', async () => { + // arrange + const mock = { create: () => { return Promise.reject({ message: 'Error with the User Model.' }) } } + + // act and assert + await expect(UserDAO.createUser(returnedUser, mock)).rejects + .toEqual({ + data: {}, + message: 'Error with the User Model.', + status: 500 + }) }) - // testing the user model properties - checkModelName(UserModel)('users'); - ['userId', 'email', 'password', 'name', 'role'] - .forEach(checkPropertyExists(instance)); - ['beforeCreate', 'beforeUpdate'].forEach(checkHookDefined(instance)); - - describe("UD1 - getUserByEmail", () => { - it("UD1.1 - should return user when existing email", async () => { - // act - const user = await UserDAO.getUserByEmail("myEmail@email.com", UserMock); - - // assert - expect(user.name).toBe("myName"); - expect(user.userId).toBe(1); - expect(user.email).toBe("myEmail@email.com"); - expect(user.password).toBe("goodPassword1"); - expect(user.role).toBe("myRole"); - }); - - it("UD1.2 - should return false when non existent email", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(false); - }) - - // act - const resp = await UserDAO.getUserByEmail("no@email.com", UserMock); - - // assert - expect(resp).toBeFalsy(); - }); - - it("UD1.3 - should catch error thrown by user model", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return new Error("Error with the UserModel."); - }); - - // act and assert - const response = await UserDAO.getUserByEmail("", UserMock); - - // assert - expect(response.message).toEqual("Error with the UserModel."); - - }); - - it("UD1.4 - Should reject error with 500 status and predefined message when model does not define them", async () => { - // arrange - let expectedError = { - status: 500, - message: "some error occured" - }; - - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject({}); - }); - - // act and assert - await expect(UserDAO.getUserByEmail("", UserMock)).rejects - .toEqual(expectedError); - }); - }); - - describe("UD2 - createUser", () => { - it("UD2.1 - should return created user when valid", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(createdUser); - }); - - // act - const resp = await UserDAO.createUser(returnedUser, UserMock); - - // assert - expect(resp.email).toBe(returnedUser.email); - }); - - it("UD2.2 - should catch error thrown by the User Model with custom message", async () => { - // arrange - let mock = { create: () => { return Promise.reject({ message: "Error with the User Model." }) } } - - // act and assert - await expect(UserDAO.createUser(returnedUser, mock)).rejects - .toEqual({ - data: {}, - message: "Error with the User Model.", - status: 500 - }); - }); - - it("UD2.3 - should resolves false", async () => { - // arrange - let mock = { create: () => { return Promise.resolve(false) } } - - // act and assert - await expect(UserDAO.createUser(returnedUser, mock)).resolves - .toEqual(false); - }); - - it("UD2.4 - should catch error thrown by the User Model with default message", async () => { - // arrange - let mock = { create: () => { return Promise.reject({}) } } - - // act and assert - await expect(UserDAO.createUser(returnedUser, mock)).rejects - .toEqual({ - data: {}, - message: "some error occured", - status: 500 - }); - }); - }); - - describe("UD3 - getAllUsers", () => { - it("UD3.1 - should return list of users", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(listOfUsers); - }); - - // act - const resp = await UserDAO.getAllUsers(UserMock); - - // assert - expect(resp.length).toBe(2); - }); - - it("UD3.2 - should return false when User Model rejects with false", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(false); - }); - - // act - const resp = await UserDAO.getAllUsers(UserMock); - - // assert - expect(resp).toBeFalsy(); - }); - - it("UD3.3 - should catch error thrown by the User Model", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject(new Error("Error with the User Model.")); - }); - - // act - await UserDAO.getAllUsers(UserMock).catch(err => { - // assert - expect(err.message).toBe("Error with the User Model."); - }); - }); - - it("UD3.4 - should catch error thrown by the User Model with default message", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject(new Error()); - }); - - // act - await UserDAO.getAllUsers(UserMock).catch(err => { - // assert - expect(err.message).toBe("some error occured"); - }); - }); - }); - - describe("UD4 - updateUser", () => { - it("UD4.1 - should return successful update message", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(true); - }); - - // act - const resp = await UserDAO.updateUser(returnedUser, UserMock); - - // assert - expect(resp).toBe("User modified successfully."); - }); - - it("UD4.2 - should return unsuccessful update message when UserModel resolves false", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(false); - }) - - // act - const resp = await UserDAO.updateUser(returnedUser, UserMock); - - // assert - expect(resp).toBe("User was not updated."); - }); - - it("UD4.3 - should catch error thrown by the User Model", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject(new Error("Error with the User Model.")); - }); - - // act - await UserDAO.updateUser(returnedUser, UserMock).catch(err => { - // assert - expect(err.message).toBe("Error with the User Model."); - }); - }); - - it("UD3.4 - should catch error thrown by the User Model with default message", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject(new Error()); - }); - - // act - await UserDAO.updateUser(returnedUser, UserMock).catch(err => { - // assert - expect(err.message).toBe("some error occured"); - }); - }); - }); - - describe("UD5 - deleteUser", () => { - it("UD5.1 - should return successful delete message", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(true); - }); - - // act - const resp = await UserDAO.deleteUser("myEmail@email.com", UserMock); - - // assert - expect(resp).toBe("User deleted successfully."); - }); - - it("UD5.2 - should return false when non existent email", async () => { - // arrange - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.resolve(false); - }) - - // act - const resp = await UserDAO.deleteUser("no@email.com", UserMock); - - // assert - expect(resp).toBe("User has failed to be deleted."); - }); - - it("UD5.3 - Should reject error with 500 status and predefined message when model does not define them", async () => { - // arrange - let expectedError = { - status: 500, - message: "some error occured" - }; - - UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { - return Promise.reject({}); - }); - - // act and assert - await expect(UserDAO.deleteUser("", UserMock)).rejects - .toEqual(expectedError); - }); - }); -}); \ No newline at end of file + it('UD2.3 - should resolves false', async () => { + // arrange + const mock = { create: () => { return Promise.resolve(false) } } + + // act and assert + await expect(UserDAO.createUser(returnedUser, mock)).resolves + .toEqual(false) + }) + + it('UD2.4 - should catch error thrown by the User Model with default message', async () => { + // arrange + const mock = { create: () => { return Promise.reject({}) } } + + // act and assert + await expect(UserDAO.createUser(returnedUser, mock)).rejects + .toEqual({ + data: {}, + message: 'some error occured', + status: 500 + }) + }) + }) + + describe('UD3 - getAllUsers', () => { + it('UD3.1 - should return list of users', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(listOfUsers) + }) + + // act + const resp = await UserDAO.getAllUsers(UserMock) + + // assert + expect(resp.length).toBe(2) + }) + + it('UD3.2 - should return false when User Model rejects with false', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(false) + }) + + // act + const resp = await UserDAO.getAllUsers(UserMock) + + // assert + expect(resp).toBeFalsy() + }) + + it('UD3.3 - should catch error thrown by the User Model', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject(new Error('Error with the User Model.')) + }) + + // act + await UserDAO.getAllUsers(UserMock).catch(err => { + // assert + expect(err.message).toBe('Error with the User Model.') + }) + }) + + it('UD3.4 - should catch error thrown by the User Model with default message', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject(new Error()) + }) + + // act + await UserDAO.getAllUsers(UserMock).catch(err => { + // assert + expect(err.message).toBe('some error occured') + }) + }) + }) + + describe('UD4 - updateUser', () => { + it('UD4.1 - should return successful update message', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(true) + }) + + // act + const resp = await UserDAO.updateUser(returnedUser, UserMock) + + // assert + expect(resp).toBe('User modified successfully.') + }) + + it('UD4.2 - should return unsuccessful update message when UserModel resolves false', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(false) + }) + + // act + const resp = await UserDAO.updateUser(returnedUser, UserMock) + + // assert + expect(resp).toBe('User was not updated.') + }) + + it('UD4.3 - should catch error thrown by the User Model', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject(new Error('Error with the User Model.')) + }) + + // act + await UserDAO.updateUser(returnedUser, UserMock).catch(err => { + // assert + expect(err.message).toBe('Error with the User Model.') + }) + }) + + it('UD3.4 - should catch error thrown by the User Model with default message', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject(new Error()) + }) + + // act + await UserDAO.updateUser(returnedUser, UserMock).catch(err => { + // assert + expect(err.message).toBe('some error occured') + }) + }) + }) + + describe('UD5 - deleteUser', () => { + it('UD5.1 - should return successful delete message', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(true) + }) + + // act + const resp = await UserDAO.deleteUser('myEmail@email.com', UserMock) + + // assert + expect(resp).toBe('User deleted successfully.') + }) + + it('UD5.2 - should return false when non existent email', async () => { + // arrange + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.resolve(false) + }) + + // act + const resp = await UserDAO.deleteUser('no@email.com', UserMock) + + // assert + expect(resp).toBe('User has failed to be deleted.') + }) + + it('UD5.3 - Should reject error with 500 status and predefined message when model does not define them', async () => { + // arrange + const expectedError = { + status: 500, + message: 'some error occured' + } + + UserMock.$queryInterface.$useHandler(function (query, queryOptions, done) { + return Promise.reject({}) + }) + + // act and assert + await expect(UserDAO.deleteUser('', UserMock)).rejects + .toEqual(expectedError) + }) + }) +}) diff --git a/server/tests/controllers/app.test.js b/server/tests/controllers/app.test.js index 2a889dc..12b1878 100644 --- a/server/tests/controllers/app.test.js +++ b/server/tests/controllers/app.test.js @@ -1,51 +1,46 @@ -const makeApp = require('../../app'); -let app = makeApp(); -const supertest = require('supertest'); - - -let response404 = '\"'; -describe("GET /api", () => { - - it("should respond with a 200 status code", async () => { - const response = await supertest(app).get("/api"); - expect(response.status).toBe(200); - }); - - it("should specify json in the context type header", async () => { - return supertest(app).get("/api") - .then(res => expect(res.headers['content-type']) - .toEqual(expect.stringContaining("json"))); - }); - - it("response should have message", async () => { - const response = await supertest(app).get("/api"); - expect(response.body.message).toBe("Hello from B&C Engine!"); - }); - -}); - -describe("Database initialization", () => { - it("Should sync up the database", () => { - // arrange - let counter = 0; - let dbObject = { - sync: () => { - counter++ - return counter; - } - } - - // act and assert - app = makeApp(dbObject) - expect(dbObject.sync()).toEqual(2); - }) -}); - -describe("Return unresolved page", () => { - - it("should respond with a 404 status code", async () => { - const response = await supertest(app).get("/blabla"); - expect(JSON.stringify(response.text).substring(0, 16)).toEqual(response404); - }); - -}); \ No newline at end of file +const makeApp = require('../../app') +let app = makeApp() +const supertest = require('supertest') + +const response404 = '"' +describe('GET /api', () => { + it('should respond with a 200 status code', async () => { + const response = await supertest(app).get('/api') + expect(response.status).toBe(200) + }) + + it('should specify json in the context type header', async () => { + return supertest(app).get('/api') + .then(res => expect(res.headers['content-type']) + .toEqual(expect.stringContaining('json'))) + }) + + it('response should have message', async () => { + const response = await supertest(app).get('/api') + expect(response.body.message).toBe('Hello from B&C Engine!') + }) +}) + +describe('Database initialization', () => { + it('Should sync up the database', () => { + // arrange + let counter = 0 + const dbObject = { + sync: () => { + counter++ + return counter + } + } + + // act and assert + app = makeApp(dbObject) + expect(dbObject.sync()).toEqual(2) + }) +}) + +describe('Return unresolved page', () => { + it('should respond with a 404 status code', async () => { + const response = await supertest(app).get('/blabla') + expect(JSON.stringify(response.text).substring(0, 16)).toEqual(response404) + }) +}) diff --git a/server/tests/controllers/invoice.controller.test.js b/server/tests/controllers/invoice.controller.test.js index 1ee2049..b309fb8 100644 --- a/server/tests/controllers/invoice.controller.test.js +++ b/server/tests/controllers/invoice.controller.test.js @@ -1,501 +1,493 @@ -const sinon = require('sinon'); -const { afterEach, afterAll } = require('jest-circus'); -var { expect, jest } = require('@jest/globals'); -const supertest = require('supertest'); -var MockExpressResponse = require('mock-express-response'); - -const InvController = require('../../controllers/invoice.controller'); -const InvoiceService = require('../../services/invoice.service'); -const AuthService = require('../../services/auth.service'); +const { afterAll } = require('jest-circus') +const { expect } = require('@jest/globals') +const supertest = require('supertest') +const MockExpressResponse = require('mock-express-response') + +const InvController = require('../../controllers/invoice.controller') +const InvoiceService = require('../../services/invoice.service') +const AuthService = require('../../services/auth.service') const EmpService = require('../../services/emp.service') -let reqUser = { - email: "valid@benoit-cote.com", - password: "validPassword1", - name: "validName", - role: "admin" -}; - -let reqUserEmployee = { - user: { - email: "valid@benoit-cote.com", - password: "validPassword1", - name: "validName", - role: "employee" - } -}; +const reqUser = { + email: 'valid@benoit-cote.com', + password: 'validPassword1', + name: 'validName', + role: 'admin' +} + +const reqUserEmployee = { + user: { + email: 'valid@benoit-cote.com', + password: 'validPassword1', + name: 'validName', + role: 'employee' + } +} let authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = reqUser; - return next(); - }); + .mockImplementation((req, res, next) => { + req.user = reqUser + return next() + }) let invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) let empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - -const makeApp = require('../../app'); -let app = makeApp(); -const request = supertest(app); -let res; - -describe("Test Invoice Controller", () => { - - beforeEach(() => { - jest.clearAllMocks(); - res = new MockExpressResponse(); - }); - - afterAll(() => { - process.exit; - }); - - describe("IC1 - Get averages per month", () => { - describe("IC1.1 - given valid start and end dates", () => { - it("IC1.1.1 - should respond with 200 and averages per month", async () => { - // arrange - let expectedInvoiceServiceResponse = [ - { month: 201912, average: "90.1" }, - { month: 202001, average: "92.1" }, - { month: 202002, average: "93.1" }, - { month: 202003, average: "94.1" }, - { month: 202004, average: "95.1" } - ]; - invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedInvoiceServiceResponse); - })); - - // act - const response = await request.get("/api/invoice/defaultChartAndTable/2019-12-01/2020-04-01"); - - // assert - expect(response.status).toBe(200); - expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedInvoiceServiceResponse)); - expect(invoiceServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe("IC1.2 - given invalid start or end date", () => { - it("IC1.2.1 - should respond with 400 when no end date", async () => { - // arrange - let expectedResponse = { - message: "Content cannot be empty." - }; - let req = { - params: { - startDate: '2019-11-01' - } - }; - - // act - const response = await InvController.getAverages(req, res) - - // assert - expect(response.statusCode).toBe(400); - expect(response._responseData.toString('ascii')).toBe(JSON.stringify(expectedResponse)); - }); - - it("IC1.2.2 - should respond with 400 when no dates", async () => { - // arrange - let expectedResponse = { - message: "Content cannot be empty." - }; - let req = { - params: { - } - }; - - // act - const response = await InvController.getAverages(req, res) - - // assert - expect(response.statusCode).toBe(400); - expect(response._responseData.toString('ascii')).toBe(JSON.stringify(expectedResponse)); - }); - - it("1.2.3 - should respond with 400 and appropriate message when invalid date format", async () => { - // arrange - let expectedResponseMessage = "Wrong format."; - - // act - const response = await request.get("/api/invoice/defaultChartAndTable/wonrg format/invalid-date"); - - // assert - expect(response.status).toBe(400); - expect(response.body.message).toBe(expectedResponseMessage); - }); - }); - - describe("IC1.3 - given service cannot fetch data", () => { - it("IC1.3.1 - should respond with 500 and message", async () => { - // arrange - let expectedResponse = "The data could not be fetched."; - invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act - const response = await request.get("/api/invoice/defaultChartAndTable/2019-12-01/2020-04-01"); - - // assert - expect(response.status).toBe(500); - expect(response.body.message).toBe(expectedResponse); - }); - }); - - describe("IC1.4 - given service throws an error", () => { - it("IC1.4.1 - should respond with 500 status code and message when not specified", async () => { - // arrange - let expectedError = { - message: "Malfunction in the B&C Engine." - }; - invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') - .mockImplementation(async () => { - await Promise.reject({}); - }); - - // act - const response = await request.get("/api/invoice/defaultChartAndTable/2019-12-01/2020-04-01"); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedError); - }); - - it("IC1.4.2 - should respond with caught error's status and message", async () => { - // arrange - let expectedError = { - status: 400, - message: "Custom message." - }; - invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') - .mockImplementation(async () => { - await Promise.reject(expectedError); - }); - - // act - const response = await request.get("/api/invoice/defaultChartAndTable/2019-12-01/2020-04-01"); - - // assert - expect(response.status).toBe(400); - expect(response.body.message).toBe(expectedError.message); - }); - }); - - describe("IC1.5 - given valid start and end dates and employeeId", () => { - it("IC1.5.1 - should respond with 200 and averages per month", async () => { - // arrange - let expectedInvoiceServiceResponse = [ - { month: 201912, average: "90.1" }, - { month: 202001, average: "92.1" }, - { month: 202002, average: "93.1" }, - { month: 202003, average: "94.1" }, - { month: 202004, average: "95.1" } - ]; - invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedInvoiceServiceResponse); - })); - - // act - const response = await request.get("/api/invoice/defaultChartAndTable/2019-12-01/2020-04-01?employeeId=22769"); - - // assert - expect(response.status).toBe(200); - expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedInvoiceServiceResponse)); - expect(invoiceServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe("IC2 - Get All Employees for Dropdown ", () => { - describe("IC2.1 - given an admin user", () => { - it("IC2.1.1 - Should respond with 200 and a list of employees", async () => { - // arrange - let expectedEmpServiceResponse = [ - { - dataValues: { - email: 'Cathia@benoit-cote.com', - firstName: 'Cathia', - lastName: 'Zeppetelli', - isActive: true - }, - }, - { - dataValues: { - email: 'Giuseppe@benoit-cote.com', - firstName: 'Giuseppe', - lastName: 'Calderone', - isActive: true - }, - }, - { - dataValues: { - email: 'Marilyne@benoit-cote.com', - firstName: 'Marilyne', - lastName: 'Séïde', - isActive: true - }, - } - ]; - - empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedEmpServiceResponse); - })); - - const response = await request.get("/api/invoice/employees"); - - // assert - expect(response.status).toBe(200); - expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedEmpServiceResponse)); - expect(empServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - it("IC2.1.2 - should respond with 500 and message", async () => { - // arrange - let expectedResponse = "The data could not be fetched."; - empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act - const response = await request.get("/api/invoice/employees"); - - // assert - expect(response.status).toBe(500); - expect(response.body.message).toBe(expectedResponse); - }); - - it("IC2.1.3 - should respond with 500 status code and message when not specified", async () => { - // arrange - let expectedError = { - message: "Malfunction in the B&C Engine." - }; - empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') - .mockImplementation(async () => { - await Promise.reject({}); - }); - - // act - const response = await request.get("/api/invoice/employees"); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedError); - }); - - it("IC2.1.4 - should respond with caught error's status and message", async () => { - // arrange - let expectedError = { - status: 400, - message: "Custom message." - }; - empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') - .mockImplementation(async () => { - await Promise.reject(expectedError); - }); - - // act - const response = await request.get("/api/invoice/employees"); - - // assert - expect(response.status).toBe(400); - expect(response.body.message).toBe(expectedError.message); - }); - }); - - describe("IC2.2 - given an employee user", () => { - it("IC2.2.1 - Should respond with 200 and one employee", async () => { - // arrange - let expectedEmpServiceResponse = [ - { - dataValues: { - email: 'Cathia@benoit-cote.com', - firstName: 'Cathia', - lastName: 'Zeppetelli', - isActive: true - }, - } - ]; - - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = reqUserEmployee; - return next(); - }); - - empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedEmpServiceResponse); - })); - - const response = await request.get("/api/invoice/employees") - .send(reqUserEmployee); - - // assert - expect(response.status).toBe(200); - expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedEmpServiceResponse)); - expect(empServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - it("IC2.2.2 - should respond with 500 and message", async () => { - // arrange - let expectedResponse = "The data could not be fetched."; - empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act - const response = await request.get("/api/invoice/employees"); - - // assert - expect(response.status).toBe(500); - expect(response.body.message).toBe(expectedResponse); - }); - - it("IC2.2.3 - should respond with 500 status code and message when not specified", async () => { - // arrange - let expectedError = { - message: "Malfunction in the B&C Engine." - }; - empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') - .mockImplementation(async () => { - await Promise.reject({}); - }); - - // act - const response = await request.get("/api/invoice/employees"); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedError); - }); - - it("IC2.2.4 - should respond with caught error's status and message", async () => { - // arrange - let expectedError = { - status: 400, - message: "Custom message." - }; - empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') - .mockImplementation(async () => { - await Promise.reject(expectedError); - }); - - // act - const response = await request.get("/api/invoice/employees"); - - // assert - expect(response.status).toBe(400); - expect(response.body.message).toBe(expectedError.message); - }); - }); - }); - - - describe("IC3 - Get All Countries for Dropdown ", () => { - - describe("IC3.1 - given any user", () => { - - it("IC3.1.1 - Should respond with 200 and a list of countries", async () => { - // arrange - let expectedCountryResponse = [ - { - countryLabel: "Canada" - }, - { - countryLabel: "France" - }, - { - countryLabel: "Japan" - } - ]; - - invoiceServiceSpy = jest.spyOn(InvoiceService, 'getCountriesName') - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedCountryResponse); - })); - - const response = await request.get("/api/invoice/getCountries"); - - // assert - expect(response.status).toBe(200); - expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedCountryResponse)); - expect(invoiceServiceSpy).toHaveBeenCalledTimes(1); - }); - - - it("IC3.1.2 - should respond with 500 and message", async () => { - // arrange - let expectedResponse = "The data could not be fetched."; - invoiceServiceSpy = jest.spyOn(InvoiceService, 'getCountriesName') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act - const response = await request.get("/api/invoice/getCountries"); - - // assert - expect(response.status).toBe(500); - expect(response.body.message).toBe(expectedResponse); - }); - - - it("IC3.1.3 - should respond with 500 status code and message when not specified", async () => { - // arrange - let expectedError = { - message: "Malfunction in the B&C Engine." - }; - - invoiceServiceSpy = jest.spyOn(InvoiceService, 'getCountriesName') - .mockImplementation(async () => { - await Promise.reject({}); - }); - - // act - const response = await request.get("/api/invoice/getCountries"); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedError); - }); - - - it("IC3.1.4 - should respond with caught error's status and message", async () => { - // arrange - let expectedError = { - status: 400, - message: "Custom message." - }; - invoiceServiceSpy = jest.spyOn(InvoiceService, 'getCountriesName') - .mockImplementation(async () => { - await Promise.reject(expectedError); - }); - - // act - const response = await request.get("/api/invoice/getCountries"); - - // assert - expect(response.status).toBe(400); - expect(response.body.message).toBe(expectedError.message); - }); - }); - }); -}); + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + +const makeApp = require('../../app') +const app = makeApp() +const request = supertest(app) +let res + +describe('Test Invoice Controller', () => { + beforeEach(() => { + jest.clearAllMocks() + res = new MockExpressResponse() + }) + + afterAll(() => { + process.exit + }) + + describe('IC1 - Get averages per month', () => { + describe('IC1.1 - given valid start and end dates', () => { + it('IC1.1.1 - should respond with 200 and averages per month', async () => { + // arrange + const expectedInvoiceServiceResponse = [ + { month: 201912, average: '90.1' }, + { month: 202001, average: '92.1' }, + { month: 202002, average: '93.1' }, + { month: 202003, average: '94.1' }, + { month: 202004, average: '95.1' } + ] + invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedInvoiceServiceResponse) + })) + + // act + const response = await request.get('/api/invoices/defaultChartAndTable/2019-12-01/2020-04-01') + + // assert + expect(response.status).toBe(200) + expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedInvoiceServiceResponse)) + expect(invoiceServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + }) + + describe('IC1.2 - given invalid start or end date', () => { + it('IC1.2.1 - should respond with 400 when no end date', async () => { + // arrange + const expectedResponse = { + message: 'Content cannot be empty.' + } + const req = { + params: { + startDate: '2019-11-01' + } + } + + // act + const response = await InvController.getAverages(req, res) + + // assert + expect(response.statusCode).toBe(400) + expect(response._responseData.toString('ascii')).toBe(JSON.stringify(expectedResponse)) + }) + + it('IC1.2.2 - should respond with 400 when no dates', async () => { + // arrange + const expectedResponse = { + message: 'Content cannot be empty.' + } + const req = { + params: { + } + } + + // act + const response = await InvController.getAverages(req, res) + + // assert + expect(response.statusCode).toBe(400) + expect(response._responseData.toString('ascii')).toBe(JSON.stringify(expectedResponse)) + }) + + it('1.2.3 - should respond with 400 and appropriate message when invalid date format', async () => { + // arrange + const expectedResponseMessage = 'Wrong format.' + + // act + const response = await request.get('/api/invoices/defaultChartAndTable/wonrg format/invalid-date') + + // assert + expect(response.status).toBe(400) + expect(response.body.message).toBe(expectedResponseMessage) + }) + }) + + describe('IC1.3 - given service cannot fetch data', () => { + it('IC1.3.1 - should respond with 500 and message', async () => { + // arrange + const expectedResponse = 'The data could not be fetched.' + invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act + const response = await request.get('/api/invoices/defaultChartAndTable/2019-12-01/2020-04-01') + + // assert + expect(response.status).toBe(500) + expect(response.body.message).toBe(expectedResponse) + }) + }) + + describe('IC1.4 - given service throws an error', () => { + it('IC1.4.1 - should respond with 500 status code and message when not specified', async () => { + // arrange + const expectedError = { + message: 'Malfunction in the B&C Engine.' + } + invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') + .mockImplementation(async () => { + await Promise.reject({}) + }) + + // act + const response = await request.get('/api/invoices/defaultChartAndTable/2019-12-01/2020-04-01') + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedError) + }) + + it("IC1.4.2 - should respond with caught error's status and message", async () => { + // arrange + const expectedError = { + status: 400, + message: 'Custom message.' + } + invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') + .mockImplementation(async () => { + await Promise.reject(expectedError) + }) + + // act + const response = await request.get('/api/invoices/defaultChartAndTable/2019-12-01/2020-04-01') + + // assert + expect(response.status).toBe(400) + expect(response.body.message).toBe(expectedError.message) + }) + }) + + describe('IC1.5 - given valid start and end dates and employeeId', () => { + it('IC1.5.1 - should respond with 200 and averages per month', async () => { + // arrange + const expectedInvoiceServiceResponse = [ + { month: 201912, average: '90.1' }, + { month: 202001, average: '92.1' }, + { month: 202002, average: '93.1' }, + { month: 202003, average: '94.1' }, + { month: 202004, average: '95.1' } + ] + invoiceServiceSpy = jest.spyOn(InvoiceService, 'getAverages') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedInvoiceServiceResponse) + })) + + // act + const response = await request.get('/api/invoices/defaultChartAndTable/2019-12-01/2020-04-01?employeeId=22769') + + // assert + expect(response.status).toBe(200) + expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedInvoiceServiceResponse)) + expect(invoiceServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + }) + }) + + describe('IC2 - Get All Employees for Dropdown ', () => { + describe('IC2.1 - given an admin user', () => { + it('IC2.1.1 - Should respond with 200 and a list of employees', async () => { + // arrange + const expectedEmpServiceResponse = [ + { + dataValues: { + email: 'Cathia@benoit-cote.com', + firstName: 'Cathia', + lastName: 'Zeppetelli', + isActive: true + } + }, + { + dataValues: { + email: 'Giuseppe@benoit-cote.com', + firstName: 'Giuseppe', + lastName: 'Calderone', + isActive: true + } + }, + { + dataValues: { + email: 'Marilyne@benoit-cote.com', + firstName: 'Marilyne', + lastName: 'Séïde', + isActive: true + } + } + ] + + empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedEmpServiceResponse) + })) + + const response = await request.get('/api/invoices/employees') + + // assert + expect(response.status).toBe(200) + expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedEmpServiceResponse)) + expect(empServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('IC2.1.2 - should respond with 500 and message', async () => { + // arrange + const expectedResponse = 'The data could not be fetched.' + empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act + const response = await request.get('/api/invoices/employees') + + // assert + expect(response.status).toBe(500) + expect(response.body.message).toBe(expectedResponse) + }) + + it('IC2.1.3 - should respond with 500 status code and message when not specified', async () => { + // arrange + const expectedError = { + message: 'Malfunction in the B&C Engine.' + } + empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') + .mockImplementation(async () => { + await Promise.reject({}) + }) + + // act + const response = await request.get('/api/invoices/employees') + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedError) + }) + + it("IC2.1.4 - should respond with caught error's status and message", async () => { + // arrange + const expectedError = { + status: 400, + message: 'Custom message.' + } + empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') + .mockImplementation(async () => { + await Promise.reject(expectedError) + }) + + // act + const response = await request.get('/api/invoices/employees') + + // assert + expect(response.status).toBe(400) + expect(response.body.message).toBe(expectedError.message) + }) + }) + + describe('IC2.2 - given an employee user', () => { + it('IC2.2.1 - Should respond with 200 and one employee', async () => { + // arrange + const expectedEmpServiceResponse = [ + { + dataValues: { + email: 'Cathia@benoit-cote.com', + firstName: 'Cathia', + lastName: 'Zeppetelli', + isActive: true + } + } + ] + + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + req.user = reqUserEmployee + return next() + }) + + empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedEmpServiceResponse) + })) + + const response = await request.get('/api/invoices/employees') + .send(reqUserEmployee) + + // assert + expect(response.status).toBe(200) + expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedEmpServiceResponse)) + expect(empServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('IC2.2.2 - should respond with 500 and message', async () => { + // arrange + const expectedResponse = 'The data could not be fetched.' + empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act + const response = await request.get('/api/invoices/employees') + + // assert + expect(response.status).toBe(500) + expect(response.body.message).toBe(expectedResponse) + }) + + it('IC2.2.3 - should respond with 500 status code and message when not specified', async () => { + // arrange + const expectedError = { + message: 'Malfunction in the B&C Engine.' + } + empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') + .mockImplementation(async () => { + await Promise.reject({}) + }) + + // act + const response = await request.get('/api/invoices/employees') + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedError) + }) + + it("IC2.2.4 - should respond with caught error's status and message", async () => { + // arrange + const expectedError = { + status: 400, + message: 'Custom message.' + } + empServiceSpy = jest.spyOn(EmpService, 'getAllEmployees') + .mockImplementation(async () => { + await Promise.reject(expectedError) + }) + + // act + const response = await request.get('/api/invoices/employees') + + // assert + expect(response.status).toBe(400) + expect(response.body.message).toBe(expectedError.message) + }) + }) + }) + + describe('IC3 - Get All Countries for Dropdown ', () => { + describe('IC3.1 - given any user', () => { + it('IC3.1.1 - Should respond with 200 and a list of countries', async () => { + // arrange + const expectedCountryResponse = [ + { + countryLabel: 'Canada' + }, + { + countryLabel: 'France' + }, + { + countryLabel: 'Japan' + } + ] + + invoiceServiceSpy = jest.spyOn(InvoiceService, 'getCountriesName') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedCountryResponse) + })) + + const response = await request.get('/api/invoices/getCountries') + + // assert + expect(response.status).toBe(200) + expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedCountryResponse)) + expect(invoiceServiceSpy).toHaveBeenCalledTimes(1) + }) + + it('IC3.1.2 - should respond with 500 and message', async () => { + // arrange + const expectedResponse = 'The data could not be fetched.' + invoiceServiceSpy = jest.spyOn(InvoiceService, 'getCountriesName') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act + const response = await request.get('/api/invoices/getCountries') + + // assert + expect(response.status).toBe(500) + expect(response.body.message).toBe(expectedResponse) + }) + + it('IC3.1.3 - should respond with 500 status code and message when not specified', async () => { + // arrange + const expectedError = { + message: 'Malfunction in the B&C Engine.' + } + + invoiceServiceSpy = jest.spyOn(InvoiceService, 'getCountriesName') + .mockImplementation(async () => { + await Promise.reject({}) + }) + + // act + const response = await request.get('/api/invoices/getCountries') + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedError) + }) + + it("IC3.1.4 - should respond with caught error's status and message", async () => { + // arrange + const expectedError = { + status: 400, + message: 'Custom message.' + } + invoiceServiceSpy = jest.spyOn(InvoiceService, 'getCountriesName') + .mockImplementation(async () => { + await Promise.reject(expectedError) + }) + + // act + const response = await request.get('/api/invoices/getCountries') + + // assert + expect(response.status).toBe(400) + expect(response.body.message).toBe(expectedError.message) + }) + }) + }) +}) diff --git a/server/tests/controllers/report.controller.test.js b/server/tests/controllers/report.controller.test.js index 335c923..31088fb 100644 --- a/server/tests/controllers/report.controller.test.js +++ b/server/tests/controllers/report.controller.test.js @@ -1,1039 +1,1033 @@ -const { afterEach, afterAll } = require('jest-circus'); -var { expect, jest } = require('@jest/globals'); -const supertest = require('supertest'); -var MockExpressResponse = require('mock-express-response'); -const pdf = require('html-pdf'); -require("../../../config.js"); - -const ReportController = require('../../controllers/report.controller'); -const ReportService = require('../../services/report.service'); -const AuthService = require('../../services/auth.service'); - -let reqUser = { - userId: "fakeUUID", - email: "valid@benoit-cote.com", - password: "validPassword1", - name: "validName", - role: "admin" -}; +const { afterEach, afterAll } = require('jest-circus') +const { expect } = require('@jest/globals') +const supertest = require('supertest') +const MockExpressResponse = require('mock-express-response') +const pdf = require('html-pdf') + +const ReportController = require('../../controllers/report.controller') +const ReportService = require('../../services/report.service') +const AuthService = require('../../services/auth.service') + +const reqUser = { + userId: 'fakeUUID', + email: 'valid@benoit-cote.com', + password: 'validPassword1', + name: 'validName', + role: 'admin' +} let authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = reqUser; - return next() - }); + .mockImplementation((req, res, next) => { + req.user = reqUser + return next() + }) let reportServiceSpy = jest.spyOn(ReportService, 'getChartReportsByUserId') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) -const makeApp = require('../../app'); -let app = makeApp(); -const request = supertest(app); -let res; +const makeApp = require('../../app') +const app = makeApp() +const request = supertest(app) +let res jest.setTimeout(10000) -describe("Test Report Controller", () => { - - beforeEach(() => { - jest.clearAllMocks(); - res = new MockExpressResponse(); - }); - - afterEach(() => { - }); - - afterAll(() => { - process.exit; - }); - - describe("RC1 - getChartReportsByUSerId", () => { - describe("RC1.1 - given a valid user from auth service", () => { - it("RC1.1.1 - should respond with 200 and body", async () => { - // arrange - let expectedResponse = [ - { - name: 'CR1', - startDate: '2019-12-01', - endDate: '2020-12-01', - employee1Id: 12345, - employee1Name: 'France Cote', - country: 'Canada', - clientType: 'Corr', - ageOfAccount: 'All', - accountType: 'Receivable', - user_user_id: 'fakeUserId' - }, - { - name: 'CR2', - startDate: '2020-12-01', - endDate: '2021-12-01', - employee1Id: -1, - employee1Name: 'All', - employee2Id: 12345, - employee2Name: 'France Cote', - country: 'All', - clientType: 'Direct', - ageOfAccount: '60-90', - accountType: 'Receivable', - user_user_id: 'fakeUserId' - } - ]; - - reportServiceSpy = jest.spyOn(ReportService, 'getChartReportsByUserId') - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedResponse); - })); - - // act - const response = await request.get("/api/reports/chartReport"); - - // assert - expect(response.status).toBe(200); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - it("RC1.1.2 - should respond with 500 and message when service resolves false", async () => { - // arrange - let expectedResponse = { message: "The data could not be fetched." }; - reportServiceSpy = jest.spyOn(ReportService, 'getChartReportsByUserId') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act - const response = await request.get("/api/reports/chartReport"); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - it("RC1.1.3 - when service throws error with specified status and message, should respond with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - reportServiceSpy = jest.spyOn(ReportService, 'getChartReportsByUserId') - .mockImplementation(async () => { - await Promise.reject(expectedResponse); - }); - - // act - const response = await request.get("/api/reports/chartReport"); - - // assert - expect(response.status).toBe(expectedResponse.status); - expect(response.body.message).toEqual(expectedResponse.message); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - it("RC1.1.4 - when service throws error with unspecified status and message, should respond with 500 and default message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - reportServiceSpy = jest.spyOn(ReportService, 'getChartReportsByUserId') - .mockImplementation(async () => { - await Promise.reject({}); - }); - - // act - const response = await request.get("/api/reports/chartReport"); - - // assert - expect(response.status).toBe(expectedResponse.status); - expect(response.body.message).toEqual(expectedResponse.message); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe("RC1.2 - given no valid userId from auth service", () => { - it("RC1.2.1 - should respond with 400 and message", async () => { - // arrange - let req = { - user: { - - } - }; - let expectedResponse = { message: "Content cannot be empty." }; - - // act - const response = await ReportController.getChartReportsByUserId(req, res); - - // assert - expect(response.statusCode).toBe(400); - expect(response._responseData.toString('ascii')).toEqual(JSON.stringify(expectedResponse)); - }); - }) - }); - - describe("RC2 - createChartReport", () => { - let fakeChartReportRequest = { - chartReport: { - name: "CRname", - startDate: new Date(2019, 1, 1).toISOString(), - endDate: new Date(2019, 11, 1).toISOString(), - employee1Id: 12345, - employee1Name: "Emp1", - employee2Id: -1, - employee2Name: "All", - countryId: "CA", - country: "Canada", - clientType: "Corr", - ageOfAccount: "All", - accountType: "Receivable", - user_user_id: "fakeUUID" +describe('Test Report Controller', () => { + beforeEach(() => { + jest.clearAllMocks() + res = new MockExpressResponse() + }) + + afterEach(() => { + }) + + afterAll(() => { + process.exit + }) + + describe('RC1 - getChartReportsByUSerId', () => { + describe('RC1.1 - given a valid user from auth service', () => { + it('RC1.1.1 - should respond with 200 and body', async () => { + // arrange + const expectedResponse = [ + { + name: 'CR1', + startDate: '2019-12-01', + endDate: '2020-12-01', + employee1Id: 12345, + employee1Name: 'France Cote', + country: 'Canada', + clientType: 'Corr', + ageOfAccount: 'All', + accountType: 'Receivable', + user_user_id: 'fakeUserId' + }, + { + name: 'CR2', + startDate: '2020-12-01', + endDate: '2021-12-01', + employee1Id: -1, + employee1Name: 'All', + employee2Id: 12345, + employee2Name: 'France Cote', + country: 'All', + clientType: 'Direct', + ageOfAccount: '60-90', + accountType: 'Receivable', + user_user_id: 'fakeUserId' + } + ] + + reportServiceSpy = jest.spyOn(ReportService, 'getChartReportsByUserId') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedResponse) + })) + + // act + const response = await request.get('/api/reports/chartReport') + + // assert + expect(response.status).toBe(200) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('RC1.1.2 - should respond with 500 and message when service resolves false', async () => { + // arrange + const expectedResponse = { message: 'The data could not be fetched.' } + reportServiceSpy = jest.spyOn(ReportService, 'getChartReportsByUserId') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act + const response = await request.get('/api/reports/chartReport') + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('RC1.1.3 - when service throws error with specified status and message, should respond with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + reportServiceSpy = jest.spyOn(ReportService, 'getChartReportsByUserId') + .mockImplementation(async () => { + await Promise.reject(expectedResponse) + }) + + // act + const response = await request.get('/api/reports/chartReport') + + // assert + expect(response.status).toBe(expectedResponse.status) + expect(response.body.message).toEqual(expectedResponse.message) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('RC1.1.4 - when service throws error with unspecified status and message, should respond with 500 and default message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + reportServiceSpy = jest.spyOn(ReportService, 'getChartReportsByUserId') + .mockImplementation(async () => { + await Promise.reject({}) + }) + + // act + const response = await request.get('/api/reports/chartReport') + + // assert + expect(response.status).toBe(expectedResponse.status) + expect(response.body.message).toEqual(expectedResponse.message) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + }) + + describe('RC1.2 - given no valid userId from auth service', () => { + it('RC1.2.1 - should respond with 400 and message', async () => { + // arrange + const req = { + user: { + + } + } + const expectedResponse = { message: 'Content cannot be empty.' } + + // act + const response = await ReportController.getChartReportsByUserId(req, res) + + // assert + expect(response.statusCode).toBe(400) + expect(response._responseData.toString('ascii')).toEqual(JSON.stringify(expectedResponse)) + }) + }) + }) + + describe('RC2 - createChartReport', () => { + const fakeChartReportRequest = { + chartReport: { + name: 'CRname', + startDate: new Date(2019, 1, 1).toISOString(), + endDate: new Date(2019, 11, 1).toISOString(), + employee1Id: 12345, + employee1Name: 'Emp1', + employee2Id: -1, + employee2Name: 'All', + countryId: 'CA', + country: 'Canada', + clientType: 'Corr', + ageOfAccount: 'All', + accountType: 'Receivable', + user_user_id: 'fakeUUID' + }, + chartReportData: [ + { + label: '2019 - employee', + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + }, + { + label: '2019', + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + } + ] + } + + describe('RC2.1 - given a valid request', () => { + it('RC2.1.1 - should return valid response from report service', async () => { + // arrange + const fakeServiceResponse = { + chartReport: { + chartReportId: 'fakeUUID', + name: 'CRname', + emp1Id: 12345, + emp2Id: -1 + }, + data: [ + { + chartReportDataId: 1, + year: 2019, + employee: 12345 }, - chartReportData: [ - { - label: "2019 - employee", - data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - }, - { - label: "2019", - data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - } - ] - }; - - describe("RC2.1 - given a valid request", () => { - it("RC2.1.1 - should return valid response from report service", async () => { - // arrange - let fakeServiceResponse = { - chartReport: { - chartReportId: "fakeUUID", - name: "CRname", - emp1Id: 12345, - emp2Id: -1 - }, - data: [ - { - chartReportDataId: 1, - year: 2019, - employee: 12345 - }, - { - chartReportDataId: 2, - year: 2019, - employee: -1 - } - ] - }; - - reportServiceSpy = jest.spyOn(ReportService, 'createChartReportForUser') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeServiceResponse); - })); - - // act - const response = await request.post("/api/reports/chartReport").send(fakeChartReportRequest) - - // assert - expect(response.status).toBe(200); - expect(response.body).toEqual(fakeServiceResponse); - expect(reportServiceSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport, fakeChartReportRequest.chartReportData, reqUser.userId); - }); - - it("RC2.1.2 - when service resolves false, should return 500 and message", async () => { - // arrange - reportServiceSpy = jest.spyOn(ReportService, 'createChartReportForUser') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - let expectedResponse = { message: "The data could not be fetched." }; - - // act - const response = await request.post("/api/reports/chartReport").send(fakeChartReportRequest); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport, fakeChartReportRequest.chartReportData, reqUser.userId); - }); - - it("RC2.1.3 - when service throws error with specified status and message, should return error status and message", async () => { - // arrange - let errorThrown = { - status: 600, - message: "Custom message." - }; - reportServiceSpy = jest.spyOn(ReportService, 'createChartReportForUser') - .mockImplementation(async () => { - await Promise.reject(errorThrown); - }); - - // act - const response = await request.post("/api/reports/chartReport") - .send(fakeChartReportRequest); - - // assert - expect(response.status).toBe(errorThrown.status); - expect(response.body).toEqual({ message: errorThrown.message }); - expect(reportServiceSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport, fakeChartReportRequest.chartReportData, reqUser.userId); - }); - - it("RC2.1.4 - when service throws error with unspecified status and message, should return default status and message", async () => { - // arrange - let expectedResponse = { - message: "Malfunction in the B&C Engine." - } - reportServiceSpy = jest.spyOn(ReportService, 'createChartReportForUser') - .mockImplementation(async () => { - await Promise.reject({}); - }); - - // act - const response = await request.post("/api/reports/chartReport") - .send(fakeChartReportRequest); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport, fakeChartReportRequest.chartReportData, reqUser.userId); - }); - }); - - describe("RC2.2 - given an invalid request", () => { - it("RC2.2.1 - when no chartReport, should return 400", async () => { - // arrange - let expectedResponse = { - message: "Content cannot be empty." - }; - let fakeInvalidChartReportRequest = { - chartReportData: [ - { - label: "2019 - employee", - data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - }, - { - label: "2019", - data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - } - ] - }; - - // act - const response = await request.post("/api/reports/chartReport") - .send(fakeInvalidChartReportRequest); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - - it("RC2.2.2 - when no chartReportData, should return 400", async () => { - // arrange - let expectedResponse = { - message: "Content cannot be empty." - }; - let fakeInvalidChartReportRequest = { - chartReport: { - name: "CRname", - startDate: new Date(2019, 1, 1).toISOString(), - endDate: new Date(2019, 11, 1).toISOString(), - employee1Id: 12345, - employee1Name: "Emp1", - employee2Id: -1, - employee2Name: "All", - countryId: "CA", - country: "Canada", - clientType: "Corr", - ageOfAccount: "All", - accountType: "Receivable", - user_user_id: "fakeUUID" - } - }; - - // act - const response = await request.post("/api/reports/chartReport") - .send(fakeInvalidChartReportRequest); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - - it("RC2.2.3 - when no user, should return 400", async () => { - // arrange - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - return next(); - }); - let expectedResponse = { - message: "Content cannot be empty." - }; - - // act - const response = await request.post("/api/reports/chartReport") - .send(fakeChartReportRequest); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - - it("RC2.2.4 - when no userId, should return 400", async () => { - // arrange - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = {}; - return next(); - }); - let expectedResponse = { - message: "Content cannot be empty." - }; - - // act - const response = await request.post("/api/reports/chartReport") - .send(fakeChartReportRequest); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - - it("RC2.2.5 - when userId is empty, should return 400", async () => { - // arrange - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = { - userId: "" - }; - return next(); - }); - let expectedResponse = { - message: "Content cannot be empty." - }; - - // act - const response = await request.post("/api/reports/chartReport") - .send(fakeChartReportRequest); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - - it("RC2.2.6 - when userId is undefined, should return 400", async () => { - // arrange - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = { - userId: undefined - }; - return next(); - }); - let expectedResponse = { - message: "Content cannot be empty." - }; - - // act - const response = await request.post("/api/reports/chartReport") - .send(fakeChartReportRequest); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - }); - }); - - describe("RC3 - deleteChartReport", () => { - let chartReportIdObject = { - chartReportId: "0ba47970-d667-4328-9711-84c4a8968c0d" - }; - - describe("RC3.1 - given a valid request", () => { - it("RC3.1.1 - should return valid response from report service", async () => { - // arrange - reportServiceSpy = jest.spyOn(ReportService, 'deleteChartReportById') - .mockImplementation(() => new Promise((resolve) => { - resolve(chartReportIdObject); - })); - - // act - const response = await request.delete(`/api/reports/delete/${chartReportIdObject.chartReportId}`); - - // assert - expect(response.status).toBe(200); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - }); - - it("RC3.1.2 - when service resolves false, should return 500 and message", async () => { - // arrange - let expectedResponse = { message: "The data could not be deleted." }; - - reportServiceSpy = jest.spyOn(ReportService, 'deleteChartReportById') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act - const response = await request.delete(`/api/reports/delete/${chartReportIdObject.chartReportId}`); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - it("RC3.1.3 - when service throws error with specified status and message, should respond with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Random error" - }; - - reportServiceSpy = jest.spyOn(ReportService, 'deleteChartReportById') - .mockImplementation(async () => { - await Promise.reject(expectedResponse); - }); - - // act - const response = await request.delete(`/api/reports/delete/${chartReportIdObject.chartReportId}`); - - // assert - expect(response.status).toBe(expectedResponse.status); - expect(response.body.message).toEqual(expectedResponse.message); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - - it("RC3.1.4 - when service throws error with unspecified status and message, should respond with 500 and default message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - reportServiceSpy = jest.spyOn(ReportService, 'deleteChartReportById') - .mockImplementation(async () => { - await Promise.reject({}); - }); - - // act - const response = await request.delete(`/api/reports/delete/${chartReportIdObject.chartReportId}`); - - // assert - expect(response.status).toBe(expectedResponse.status); - expect(response.body.message).toEqual(expectedResponse.message); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe("RC4 - getReportTypesWithRecipients", () => { - let fakeReportTypesServiceResponse = [ { - reportTypeId: "someUUID", - name: "someName", - frequency: 0, - recipients: { - "someUUID": { - name: "rName1", - isRecipient: true - }, - "someotherUUID": { - name: "rName2", - isRecipient: false - } - } + chartReportDataId: 2, + year: 2019, + employee: -1 + } + ] + } + + reportServiceSpy = jest.spyOn(ReportService, 'createChartReportForUser') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeServiceResponse) + })) + + // act + const response = await request.post('/api/reports/chartReport').send(fakeChartReportRequest) + + // assert + expect(response.status).toBe(200) + expect(response.body).toEqual(fakeServiceResponse) + expect(reportServiceSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport, fakeChartReportRequest.chartReportData, reqUser.userId) + }) + + it('RC2.1.2 - when service resolves false, should return 500 and message', async () => { + // arrange + reportServiceSpy = jest.spyOn(ReportService, 'createChartReportForUser') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + const expectedResponse = { message: 'The data could not be fetched.' } + + // act + const response = await request.post('/api/reports/chartReport').send(fakeChartReportRequest) + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport, fakeChartReportRequest.chartReportData, reqUser.userId) + }) + + it('RC2.1.3 - when service throws error with specified status and message, should return error status and message', async () => { + // arrange + const errorThrown = { + status: 600, + message: 'Custom message.' + } + reportServiceSpy = jest.spyOn(ReportService, 'createChartReportForUser') + .mockImplementation(async () => { + await Promise.reject(errorThrown) + }) + + // act + const response = await request.post('/api/reports/chartReport') + .send(fakeChartReportRequest) + + // assert + expect(response.status).toBe(errorThrown.status) + expect(response.body).toEqual({ message: errorThrown.message }) + expect(reportServiceSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport, fakeChartReportRequest.chartReportData, reqUser.userId) + }) + + it('RC2.1.4 - when service throws error with unspecified status and message, should return default status and message', async () => { + // arrange + const expectedResponse = { + message: 'Malfunction in the B&C Engine.' + } + reportServiceSpy = jest.spyOn(ReportService, 'createChartReportForUser') + .mockImplementation(async () => { + await Promise.reject({}) + }) + + // act + const response = await request.post('/api/reports/chartReport') + .send(fakeChartReportRequest) + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport, fakeChartReportRequest.chartReportData, reqUser.userId) + }) + }) + + describe('RC2.2 - given an invalid request', () => { + it('RC2.2.1 - when no chartReport, should return 400', async () => { + // arrange + const expectedResponse = { + message: 'Content cannot be empty.' + } + const fakeInvalidChartReportRequest = { + chartReportData: [ + { + label: '2019 - employee', + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + }, + { + label: '2019', + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + } + ] + } + + // act + const response = await request.post('/api/reports/chartReport') + .send(fakeInvalidChartReportRequest) + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + + it('RC2.2.2 - when no chartReportData, should return 400', async () => { + // arrange + const expectedResponse = { + message: 'Content cannot be empty.' + } + const fakeInvalidChartReportRequest = { + chartReport: { + name: 'CRname', + startDate: new Date(2019, 1, 1).toISOString(), + endDate: new Date(2019, 11, 1).toISOString(), + employee1Id: 12345, + employee1Name: 'Emp1', + employee2Id: -1, + employee2Name: 'All', + countryId: 'CA', + country: 'Canada', + clientType: 'Corr', + ageOfAccount: 'All', + accountType: 'Receivable', + user_user_id: 'fakeUUID' + } + } + + // act + const response = await request.post('/api/reports/chartReport') + .send(fakeInvalidChartReportRequest) + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + + it('RC2.2.3 - when no user, should return 400', async () => { + // arrange + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + return next() + }) + const expectedResponse = { + message: 'Content cannot be empty.' + } + + // act + const response = await request.post('/api/reports/chartReport') + .send(fakeChartReportRequest) + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + + it('RC2.2.4 - when no userId, should return 400', async () => { + // arrange + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + req.user = {} + return next() + }) + const expectedResponse = { + message: 'Content cannot be empty.' + } + + // act + const response = await request.post('/api/reports/chartReport') + .send(fakeChartReportRequest) + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + + it('RC2.2.5 - when userId is empty, should return 400', async () => { + // arrange + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + req.user = { + userId: '' + } + return next() + }) + const expectedResponse = { + message: 'Content cannot be empty.' + } + + // act + const response = await request.post('/api/reports/chartReport') + .send(fakeChartReportRequest) + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + + it('RC2.2.6 - when userId is undefined, should return 400', async () => { + // arrange + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + req.user = { + userId: undefined } - ]; - - describe("RC4.1 - given valid admin user", () => { - beforeEach(() => { - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = reqUser; - return next() - }); - }) - - it("RC4.1.1 - when valid response from service, should respond with 200 and response from service", async () => { - // arrange - let expectedResponse = fakeReportTypesServiceResponse; - reportServiceSpy = jest.spyOn(ReportService, 'getReportTypesWithRecipients') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeReportTypesServiceResponse); - })); - - // act - const response = await request.get("/api/reports/reportTypes"); - - // assert - expect(response.status).toBe(200); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalled(); - expect(authSpy).toHaveBeenCalled(); - }); - - it("RC4.1.2 - when service resolves false, should respond with 500 and message", async () => { - // arrange - let expectedResponse = { message: "The data could not be fetched." }; - reportServiceSpy = jest.spyOn(ReportService, 'getReportTypesWithRecipients') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act - const response = await request.get("/api/reports/reportTypes"); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalled(); - expect(authSpy).toHaveBeenCalled(); - }); - - it("RC4.1.3 - when service throws error with specified status and message, should respond with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - reportServiceSpy = jest.spyOn(ReportService, 'getReportTypesWithRecipients') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act - const response = await request.get("/api/reports/reportTypes"); - - // assert - expect(response.status).toBe(600); - expect(response.body.message).toEqual(expectedResponse.message); - expect(reportServiceSpy).toHaveBeenCalled(); - expect(authSpy).toHaveBeenCalled(); - }); - - it("RC4.1.4 - when service throws error with unspecified status and message, should respond with default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - reportServiceSpy = jest.spyOn(ReportService, 'getReportTypesWithRecipients') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act - const response = await request.get("/api/reports/reportTypes"); - - // assert - expect(response.status).toBe(expectedResponse.status); - expect(response.body.message).toEqual(expectedResponse.message); - expect(reportServiceSpy).toHaveBeenCalled(); - expect(authSpy).toHaveBeenCalled(); - }); - }); - - describe("RC4.2 - given invalid userId or role", () => { - it("RC4.2.1 - when no user, should respond with 400 and message", async () => { - // arrange - let expectedResponse = { - message: "Content cannot be empty." - }; - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - return next() - }); - - // act - const response = await request.get("/api/reports/reportTypes"); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedResponse); - expect(authSpy).toHaveBeenCalled(); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - - it("RC4.2.2 - when no userId, should respond with 400 and message", async () => { - // arrange - let expectedResponse = { - message: "Content cannot be empty." - }; - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = {}; - return next() - }); - - // act - const response = await request.get("/api/reports/reportTypes"); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedResponse); - expect(authSpy).toHaveBeenCalled(); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - - it("RC4.2.3 - when userId is empty, should respond with 400 and message", async () => { - // arrange - let expectedResponse = { - message: "Content cannot be empty." - }; - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = { userId: "" }; - return next() - }); - - // act - const response = await request.get("/api/reports/reportTypes"); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedResponse); - expect(authSpy).toHaveBeenCalled(); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - - it("RC4.2.4 - when userId is undefined, should respond with 400 and message", async () => { - // arrange - let expectedResponse = { - message: "Content cannot be empty." - }; - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = { userId: undefined }; - return next() - }); - - // act - const response = await request.get("/api/reports/reportTypes"); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedResponse); - expect(authSpy).toHaveBeenCalled(); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - - it("RC4.2.5 - when user role is not admin, should respond with 403 and message", async () => { - // arrange - let expectedResponse = { - message: "You are not authorized to fetch from this resource." - }; - authSpy = jest.spyOn(AuthService, 'authenticateToken') - .mockImplementation((req, res, next) => { - req.user = { userId: "someUUID", role: "employee" }; - return next() - }); - - // act - const response = await request.get("/api/reports/reportTypes"); - - // assert - expect(response.status).toBe(403); - expect(response.body).toEqual(expectedResponse); - expect(authSpy).toHaveBeenCalled(); - expect(reportServiceSpy).toHaveBeenCalledTimes(0); - }); - }); - }); - - describe("RC5 - createChartReportPDF", () => { - describe("RC5.1 - given a valid reportId", () => { - const fakeReportId = 54564564654; - it("RC5.1.1 - should return valid response 'true' with 200 status", async () => { - // arrange - reportServiceSpy = jest.spyOn(ReportService, 'createChartReportPDFById') - .mockImplementation(() => new Promise((resolve) => { - resolve(true); - })); - - // act - const response = await request.post(`/api/reports/createPdf`) - .send({ reportid: fakeReportId }); - - // assert - expect(response.status).toBe(200); - expect(response.text).toEqual('true'); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - }); - - it("RC5.1.2 - when service resolves false, should return 500 and message", async () => { - // arrange - let expectedResponse = { message: "The data could not be fetched." }; - - reportServiceSpy = jest.spyOn(ReportService, 'createChartReportPDFById') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act - const response = await request.post(`/api/reports/createPdf`) - .send({ reportid: fakeReportId }); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - it("RC5.1.3 - when service throws error with specified status and message, should respond with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Random error" - }; - - reportServiceSpy = jest.spyOn(ReportService, 'createChartReportPDFById') - .mockImplementation(async () => { - await Promise.reject(expectedResponse); - }); - - // act - const response = await request.post(`/api/reports/createPdf`) - .send({ reportid: fakeReportId }); - - // assert - expect(response.status).toBe(expectedResponse.status); - expect(response.body.message).toEqual(expectedResponse.message); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - - it("RC5.1.4 - when service throws error with unspecified status and message, should respond with 500 and default message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - reportServiceSpy = jest.spyOn(ReportService, 'createChartReportPDFById') - .mockImplementation(async () => { - await Promise.reject(expectedResponse); - }); - - // act - const response = await request.post(`/api/reports/createPdf`) - .send({ reportid: fakeReportId }); - - // assert - expect(response.status).toBe(expectedResponse.status); - expect(response.body.message).toEqual(expectedResponse.message); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe("RC5.2 - given no reportId", () => { - it("RC5.2.1 - should return with status 400 and predefined error message", async () => { - // arrange - let expectedError = { message: "Content cannot be empty." } - - // act - const response = await request.post(`/api/reports/createPdf`); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedError); - }); - }); - }); - - describe("RC6 - fetchChartReportPDF", () => { - describe("RC6.1 - given a valid reportId", () => { - const fakeReportId = 54564564654; - - it("RC6.1.1 - should return valid response with status 200", async () => { - // arrange - if (__dirname !== '/home/runner/work/BC-Engine/BC-Engine/server/tests/controllers') { - pdf.create(`
`, { format: "letter" }) - .toFile(`${__dirname.replace("tests\\controllers", "")}docs\\pdf_files\\chartReport-${fakeReportId}.pdf`, () => { }); - } - else { - pdf.create(`
`, { format: "letter" }) - .toFile(`${__dirname.replace("tests/controllers", "")}docs/pdf_files/chartReport-${fakeReportId}.pdf`, () => { }); - } - - // wait for mock pdf file to be created - await new Promise((r) => setTimeout(r, 2000)); - - // act - const response = await request.get(`/api/reports/fetchPdf?reportid=${fakeReportId}`); - - // assert - expect(response.status).toBe(200); - }); - - it("RC6.1.2 - when file can't be fetched and throws error with specified status and message, should respond with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 404, - message: "Not Found" - }; - - // act - const response = await request.get(`/api/reports/fetchPdf?reportid=${fakeReportId}`); - - // assert - expect(response.status).toBe(expectedResponse.status); - expect(response.body.message).toBe(expectedResponse.message); - - }); - }); - - describe("RC6.2 - given no reportId", () => { - it("RC6.2.1 - should return with status 400 and predefined error message", async () => { - // arrange - let expectedError = { message: "Content cannot be empty." } - - // act - const response = await request.get(`/api/reports/fetchPdf`); - - // assert - expect(response.status).toBe(400); - expect(response.body).toEqual(expectedError); - }); - }); - }); - - describe("RC7 - getPerformanceReportsOfAllUsers", () => { - describe("RC7.1 - given a valid user from auth service", () => { - it("RC7.1.1 - should respond with 200 and body", async () => { - // arrange - let expectedResponse = [ - { - performanceReportId: "PerformanceUUID", - employeeId: 1, - averageCollectionDay: "35", - annualBillingObjective: "4500", - monthlyBillingObjective: "300", - annualBillingNumber: "200", - monthlyBillingNumber: "300", - projectedBonus: "650" - }, - { - performanceReportId: "PerformanceUUID", - employeeId: 2, - averageCollectionDay: "35", - annualBillingObjective: "4500", - monthlyBillingObjective: "300", - annualBillingNumber: "200", - monthlyBillingNumber: "300", - projectedBonus: "650" - } - ]; - - reportServiceSpy = jest.spyOn(ReportService, 'getPerformanceReportWhenConnectedAsAdmin') - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedResponse); - })); - - // act - const response = await request.get("/api/reports/performanceReport"); - - // assert - expect(response.status).toBe(200); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - it("RC7.1.2 - should respond with 500 and message when service resolves false", async () => { - // arrange - let expectedResponse = { message: "The data could not be fetched." }; - reportServiceSpy = jest.spyOn(ReportService, 'getPerformanceReportWhenConnectedAsAdmin') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act - const response = await request.get("/api/reports/performanceReport"); - - // assert - expect(response.status).toBe(500); - expect(response.body).toEqual(expectedResponse); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - it("RC7.1.3 - when service throws error with specified status and message, should respond with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - reportServiceSpy = jest.spyOn(ReportService, 'getPerformanceReportWhenConnectedAsAdmin') - .mockImplementation(async () => { - await Promise.reject(expectedResponse); - }); - - // act - const response = await request.get("/api/reports/performanceReport"); - - // assert - expect(response.status).toBe(expectedResponse.status); - expect(response.body.message).toEqual(expectedResponse.message); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - - it("RC7.1.4 - when service throws error with unspecified status and message, should respond with 500 and default message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - reportServiceSpy = jest.spyOn(ReportService, 'getPerformanceReportWhenConnectedAsAdmin') - .mockImplementation(async () => { - await Promise.reject({}); - }); - - // act - const response = await request.get("/api/reports/performanceReport"); - - // assert - expect(response.status).toBe(expectedResponse.status); - expect(response.body.message).toEqual(expectedResponse.message); - expect(reportServiceSpy).toHaveBeenCalledTimes(1); - expect(authSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe("RC7.2 - given no valid userId from auth service", () => { - it("RC7.2.1 - should respond with 400 and message", async () => { - // arrange - let req = { - user: { - - } - }; - let expectedResponse = { message: "Content cannot be empty." }; - - // act - const response = await ReportController.getPerformanceReportsOfAllUsers(req, res); - - // assert - expect(response.statusCode).toBe(400); - expect(response._responseData.toString('ascii')).toEqual(JSON.stringify(expectedResponse)); - }); - }); - }); -}); \ No newline at end of file + return next() + }) + const expectedResponse = { + message: 'Content cannot be empty.' + } + + // act + const response = await request.post('/api/reports/chartReport') + .send(fakeChartReportRequest) + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + }) + }) + + describe('RC3 - deleteChartReport', () => { + const chartReportIdObject = { + chartReportId: '0ba47970-d667-4328-9711-84c4a8968c0d' + } + + describe('RC3.1 - given a valid request', () => { + it('RC3.1.1 - should return valid response from report service', async () => { + // arrange + reportServiceSpy = jest.spyOn(ReportService, 'deleteChartReportById') + .mockImplementation(() => new Promise((resolve) => { + resolve(chartReportIdObject) + })) + + // act + const response = await request.delete(`/api/reports/delete/${chartReportIdObject.chartReportId}`) + + // assert + expect(response.status).toBe(200) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + }) + + it('RC3.1.2 - when service resolves false, should return 500 and message', async () => { + // arrange + const expectedResponse = { message: 'The data could not be deleted.' } + + reportServiceSpy = jest.spyOn(ReportService, 'deleteChartReportById') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act + const response = await request.delete(`/api/reports/delete/${chartReportIdObject.chartReportId}`) + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('RC3.1.3 - when service throws error with specified status and message, should respond with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Random error' + } + + reportServiceSpy = jest.spyOn(ReportService, 'deleteChartReportById') + .mockImplementation(async () => { + await Promise.reject(expectedResponse) + }) + + // act + const response = await request.delete(`/api/reports/delete/${chartReportIdObject.chartReportId}`) + + // assert + expect(response.status).toBe(expectedResponse.status) + expect(response.body.message).toEqual(expectedResponse.message) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('RC3.1.4 - when service throws error with unspecified status and message, should respond with 500 and default message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + reportServiceSpy = jest.spyOn(ReportService, 'deleteChartReportById') + .mockImplementation(async () => { + await Promise.reject({}) + }) + + // act + const response = await request.delete(`/api/reports/delete/${chartReportIdObject.chartReportId}`) + + // assert + expect(response.status).toBe(expectedResponse.status) + expect(response.body.message).toEqual(expectedResponse.message) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + }) + }) + + describe('RC4 - getReportTypesWithRecipients', () => { + const fakeReportTypesServiceResponse = [ + { + reportTypeId: 'someUUID', + name: 'someName', + frequency: 0, + recipients: { + someUUID: { + name: 'rName1', + isRecipient: true + }, + someotherUUID: { + name: 'rName2', + isRecipient: false + } + } + } + ] + + describe('RC4.1 - given valid admin user', () => { + beforeEach(() => { + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + req.user = reqUser + return next() + }) + }) + + it('RC4.1.1 - when valid response from service, should respond with 200 and response from service', async () => { + // arrange + const expectedResponse = fakeReportTypesServiceResponse + reportServiceSpy = jest.spyOn(ReportService, 'getReportTypesWithRecipients') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeReportTypesServiceResponse) + })) + + // act + const response = await request.get('/api/reports/reportTypes') + + // assert + expect(response.status).toBe(200) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalled() + expect(authSpy).toHaveBeenCalled() + }) + + it('RC4.1.2 - when service resolves false, should respond with 500 and message', async () => { + // arrange + const expectedResponse = { message: 'The data could not be fetched.' } + reportServiceSpy = jest.spyOn(ReportService, 'getReportTypesWithRecipients') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act + const response = await request.get('/api/reports/reportTypes') + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalled() + expect(authSpy).toHaveBeenCalled() + }) + + it('RC4.1.3 - when service throws error with specified status and message, should respond with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + reportServiceSpy = jest.spyOn(ReportService, 'getReportTypesWithRecipients') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act + const response = await request.get('/api/reports/reportTypes') + + // assert + expect(response.status).toBe(600) + expect(response.body.message).toEqual(expectedResponse.message) + expect(reportServiceSpy).toHaveBeenCalled() + expect(authSpy).toHaveBeenCalled() + }) + + it('RC4.1.4 - when service throws error with unspecified status and message, should respond with default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + reportServiceSpy = jest.spyOn(ReportService, 'getReportTypesWithRecipients') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act + const response = await request.get('/api/reports/reportTypes') + + // assert + expect(response.status).toBe(expectedResponse.status) + expect(response.body.message).toEqual(expectedResponse.message) + expect(reportServiceSpy).toHaveBeenCalled() + expect(authSpy).toHaveBeenCalled() + }) + }) + + describe('RC4.2 - given invalid userId or role', () => { + it('RC4.2.1 - when no user, should respond with 400 and message', async () => { + // arrange + const expectedResponse = { + message: 'Content cannot be empty.' + } + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + return next() + }) + + // act + const response = await request.get('/api/reports/reportTypes') + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedResponse) + expect(authSpy).toHaveBeenCalled() + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + + it('RC4.2.2 - when no userId, should respond with 400 and message', async () => { + // arrange + const expectedResponse = { + message: 'Content cannot be empty.' + } + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + req.user = {} + return next() + }) + + // act + const response = await request.get('/api/reports/reportTypes') + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedResponse) + expect(authSpy).toHaveBeenCalled() + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + + it('RC4.2.3 - when userId is empty, should respond with 400 and message', async () => { + // arrange + const expectedResponse = { + message: 'Content cannot be empty.' + } + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + req.user = { userId: '' } + return next() + }) + + // act + const response = await request.get('/api/reports/reportTypes') + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedResponse) + expect(authSpy).toHaveBeenCalled() + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + + it('RC4.2.4 - when userId is undefined, should respond with 400 and message', async () => { + // arrange + const expectedResponse = { + message: 'Content cannot be empty.' + } + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + req.user = { userId: undefined } + return next() + }) + + // act + const response = await request.get('/api/reports/reportTypes') + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedResponse) + expect(authSpy).toHaveBeenCalled() + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + + it('RC4.2.5 - when user role is not admin, should respond with 403 and message', async () => { + // arrange + const expectedResponse = { + message: 'You are not authorized to fetch from this resource.' + } + authSpy = jest.spyOn(AuthService, 'authenticateToken') + .mockImplementation((req, res, next) => { + req.user = { userId: 'someUUID', role: 'employee' } + return next() + }) + + // act + const response = await request.get('/api/reports/reportTypes') + + // assert + expect(response.status).toBe(403) + expect(response.body).toEqual(expectedResponse) + expect(authSpy).toHaveBeenCalled() + expect(reportServiceSpy).toHaveBeenCalledTimes(0) + }) + }) + }) + + describe('RC5 - createChartReportPDF', () => { + describe('RC5.1 - given a valid reportId', () => { + const fakeReportId = 54564564654 + it("RC5.1.1 - should return valid response 'true' with 200 status", async () => { + // arrange + reportServiceSpy = jest.spyOn(ReportService, 'createChartReportPDFById') + .mockImplementation(() => new Promise((resolve) => { + resolve(true) + })) + + // act + const response = await request.post('/api/reports/createPdf') + .send({ reportid: fakeReportId }) + + // assert + expect(response.status).toBe(200) + expect(response.text).toEqual('true') + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + }) + + it('RC5.1.2 - when service resolves false, should return 500 and message', async () => { + // arrange + const expectedResponse = { message: 'The data could not be fetched.' } + + reportServiceSpy = jest.spyOn(ReportService, 'createChartReportPDFById') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act + const response = await request.post('/api/reports/createPdf') + .send({ reportid: fakeReportId }) + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('RC5.1.3 - when service throws error with specified status and message, should respond with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Random error' + } + + reportServiceSpy = jest.spyOn(ReportService, 'createChartReportPDFById') + .mockImplementation(async () => { + await Promise.reject(expectedResponse) + }) + + // act + const response = await request.post('/api/reports/createPdf') + .send({ reportid: fakeReportId }) + + // assert + expect(response.status).toBe(expectedResponse.status) + expect(response.body.message).toEqual(expectedResponse.message) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('RC5.1.4 - when service throws error with unspecified status and message, should respond with 500 and default message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + reportServiceSpy = jest.spyOn(ReportService, 'createChartReportPDFById') + .mockImplementation(async () => { + await Promise.reject(expectedResponse) + }) + + // act + const response = await request.post('/api/reports/createPdf') + .send({ reportid: fakeReportId }) + + // assert + expect(response.status).toBe(expectedResponse.status) + expect(response.body.message).toEqual(expectedResponse.message) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + }) + + describe('RC5.2 - given no reportId', () => { + it('RC5.2.1 - should return with status 400 and predefined error message', async () => { + // arrange + const expectedError = { message: 'Content cannot be empty.' } + + // act + const response = await request.post('/api/reports/createPdf') + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedError) + }) + }) + }) + + describe('RC6 - fetchChartReportPDF', () => { + describe('RC6.1 - given a valid reportId', () => { + const fakeReportId = 54564564654 + + it('RC6.1.1 - should return valid response with status 200', async () => { + // arrange + if (__dirname !== '/home/runner/work/BC-Engine/BC-Engine/server/tests/controllers') { + pdf.create('
', { format: 'letter' }) + .toFile(`${__dirname.replace('tests\\controllers', '')}docs\\pdf_files\\chartReport-${fakeReportId}.pdf`, () => { }) + } else { + pdf.create('
', { format: 'letter' }) + .toFile(`${__dirname.replace('tests/controllers', '')}docs/pdf_files/chartReport-${fakeReportId}.pdf`, () => { }) + } + + // wait for mock PDF file to be created + await new Promise((resolve) => setTimeout(resolve, 2000)) + + // act + const response = await request.get(`/api/reports/fetchPdf?reportid=${fakeReportId}`) + + // assert + expect(response.status).toBe(200) + }) + + it("RC6.1.2 - when file can't be fetched and throws error with specified status and message, should respond with specified status and message", async () => { + // arrange + const expectedResponse = { + status: 404, + message: 'Not Found' + } + + // act + const response = await request.get(`/api/reports/fetchPdf?reportid=${fakeReportId}`) + + // assert + expect(response.status).toBe(expectedResponse.status) + expect(response.body.message).toBe(expectedResponse.message) + }) + }) + + describe('RC6.2 - given no reportId', () => { + it('RC6.2.1 - should return with status 400 and predefined error message', async () => { + // arrange + const expectedError = { message: 'Content cannot be empty.' } + + // act + const response = await request.get('/api/reports/fetchPdf') + + // assert + expect(response.status).toBe(400) + expect(response.body).toEqual(expectedError) + }) + }) + }) + + describe('RC7 - getPerformanceReportsOfAllUsers', () => { + describe('RC7.1 - given a valid user from auth service', () => { + it('RC7.1.1 - should respond with 200 and body', async () => { + // arrange + const expectedResponse = [ + { + performanceReportId: 'PerformanceUUID', + employeeId: 1, + averageCollectionDay: '35', + annualBillingObjective: '4500', + monthlyBillingObjective: '300', + annualBillingNumber: '200', + monthlyBillingNumber: '300', + projectedBonus: '650' + }, + { + performanceReportId: 'PerformanceUUID', + employeeId: 2, + averageCollectionDay: '35', + annualBillingObjective: '4500', + monthlyBillingObjective: '300', + annualBillingNumber: '200', + monthlyBillingNumber: '300', + projectedBonus: '650' + } + ] + + reportServiceSpy = jest.spyOn(ReportService, 'getPerformanceReports') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedResponse) + })) + + // act + const response = await request.get('/api/reports/performanceReport') + + // assert + expect(response.status).toBe(200) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('RC7.1.2 - should respond with 500 and message when service resolves false', async () => { + // arrange + const expectedResponse = { message: 'The data could not be fetched.' } + reportServiceSpy = jest.spyOn(ReportService, 'getPerformanceReports') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act + const response = await request.get('/api/reports/performanceReport') + + // assert + expect(response.status).toBe(500) + expect(response.body).toEqual(expectedResponse) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('RC7.1.3 - when service throws error with specified status and message, should respond with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + reportServiceSpy = jest.spyOn(ReportService, 'getPerformanceReports') + .mockImplementation(async () => { + await Promise.reject(expectedResponse) + }) + + // act + const response = await request.get('/api/reports/performanceReport') + + // assert + expect(response.status).toBe(expectedResponse.status) + expect(response.body.message).toEqual(expectedResponse.message) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + + it('RC7.1.4 - when service throws error with unspecified status and message, should respond with 500 and default message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + reportServiceSpy = jest.spyOn(ReportService, 'getPerformanceReports') + .mockImplementation(async () => { + await Promise.reject({}) + }) + + // act + const response = await request.get('/api/reports/performanceReport') + + // assert + expect(response.status).toBe(expectedResponse.status) + expect(response.body.message).toEqual(expectedResponse.message) + expect(reportServiceSpy).toHaveBeenCalledTimes(1) + expect(authSpy).toHaveBeenCalledTimes(1) + }) + }) + + describe('RC7.2 - given no valid userId from auth service', () => { + it('RC7.2.1 - should respond with 400 and message', async () => { + // arrange + const req = { + user: { + + } + } + const expectedResponse = { message: 'Content cannot be empty.' } + + // act + const response = await ReportController.getPerformanceReportsOfAllUsers(req, res) + + // assert + expect(response.statusCode).toBe(400) + expect(response._responseData.toString('ascii')).toEqual(JSON.stringify(expectedResponse)) + }) + }) + }) +}) diff --git a/server/tests/controllers/user.controller.test.js b/server/tests/controllers/user.controller.test.js index 3ae148d..0c116c4 100644 --- a/server/tests/controllers/user.controller.test.js +++ b/server/tests/controllers/user.controller.test.js @@ -1,515 +1,506 @@ -const UserService = require('../../services/user.service'); -const AuthService = require('../../services/auth.service'); -const EmpService = require('../../services/emp.service'); -const UserController = require('../../controllers/user.controller'); -const sinon = require('sinon'); -const { afterEach, afterAll } = require('jest-circus'); -var { expect, jest } = require('@jest/globals'); -const supertest = require('supertest'); -var MockExpressResponse = require('mock-express-response'); - - -let reqUser = { - email: "valid@benoit-cote.com", - password: "validPassword1", - name: "validName", - role: "admin" -}; +const UserService = require('../../services/user.service') +const AuthService = require('../../services/auth.service') +const EmpService = require('../../services/emp.service') +const UserController = require('../../controllers/user.controller') +const sinon = require('sinon') +const { afterEach, afterAll } = require('jest-circus') +const { expect } = require('@jest/globals') +const supertest = require('supertest') +const MockExpressResponse = require('mock-express-response') + +const reqUser = { + email: 'valid@benoit-cote.com', + password: 'validPassword1', + name: 'validName', + role: 'admin' +} const reqEmp = { - email: "emp@benoit-cote.com", - firstName: "FName", - lastName: "LName" + email: 'emp@benoit-cote.com', + firstName: 'FName', + lastName: 'LName' } const reqUserEmployee = { - user: { - email: "valid@email.com", - role: "employee" - } -}; + user: { + email: 'valid@email.com', + role: 'employee' + } +} const resUserFromService = { - dataValues: { - email: "valid@email.com", - password: "validPassword", - name: "validName", - role: "validRole" - } -}; + dataValues: { + email: 'valid@email.com', + password: 'validPassword', + name: 'validName', + role: 'validRole' + } +} -let sandbox = sinon.createSandbox(); -let authStub = sandbox.stub(AuthService, 'authenticateToken') - .callsFake(function (req, res, next) { - req.user = reqUser; - return next(); - }); +const sandbox = sinon.createSandbox() +const authStub = sandbox.stub(AuthService, 'authenticateToken') + .callsFake(function (req, res, next) { + req.user = reqUser + return next() + }) -let empStub = sandbox.stub(EmpService, 'checkEmail') - .callsFake(function (req, res, next) { - req.emp = reqEmp; - return next(); - }); +const empStub = sandbox.stub(EmpService, 'checkEmail') + .callsFake(function (req, res, next) { + req.emp = reqEmp + return next() + }) let userSpy = jest.spyOn(UserService, 'authenticateUser') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) let authSpy = jest.spyOn(AuthService, 'getTokens') - .mockImplementation(() => ["aToken", "rToken"]); - -const makeApp = require('../../app'); -let app = makeApp(); -const request = supertest(app); -let res; - -describe("Test UserController", () => { - - beforeEach(() => { - jest.clearAllMocks(); - res = new MockExpressResponse(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - afterAll(() => { - process.exit; - }); - - describe("UC1 - Create a User", () => { - - describe("UC1.1 - given a valid user body", () => { - it("UC1.1.1 - should respond with a 200 status code with user from user service", async () => { - // arrange - let expectedUser = { - email: reqUser.email, - name: reqUser.name, - role: reqUser.role - }; - userSpy = jest.spyOn(UserService, 'createUser') - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedUser); - })); - - // act - const response = await supertest(app).post("/api/users") - .send(reqUser); - - // assert - expect(response.status).toBe(200); - expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedUser)); - expect(userSpy).toHaveBeenCalledTimes(1); - expect(authStub.called).toBeTruthy(); - expect(empStub.called).toBeTruthy(); - - // this needs to be done manually in each test because it - //doesn't work in the afterEach for some reason - authStub.resetHistory(); - empStub.resetHistory(); - }); - - describe("UC1.1.2 - Given service throws error", () => { - it("UC1.1.2.1 - when error message equals 'Validation error' should respond with 400", async () => { - // arrange - let userSpy = jest.spyOn(UserService, 'createUser') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({message: "Validation error"}); - })); - - // act - const response = await request.post("/api/users") - .send(reqUser); - - // assert - expect(response.status).toBe(400); - expect(response.body.message).toBe("User already exists.") - expect(userSpy).toHaveBeenCalledTimes(1); - }); - - it("UC1.1.2.2 - when error message equals 'Validation error' should respond with 500", async () => { - // arrange - let userSpy = jest.spyOn(UserService, 'createUser') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({message: "error"}); - })); - - // act - const response = await request.post("/api/users") - .send(reqUser); - - // assert - expect(response.status).toBe(500); - expect(response.body.message).toBe("error") - expect(userSpy).toHaveBeenCalledTimes(1); - }); - }) - }); - - describe("UC1.2 - given an invalid user body", () => { - it("UC1.2.1 - should return 400 with message when no email", async () => { - // arrange - let noEmailReqUser = { - password: reqUser.password, - name: reqUser.name, - role: reqUser.role - }; - - // act - const response = await request.post("/api/users") - .send(noEmailReqUser); - - // assert - expect(response.status).toBe(400); - expect(response.body.message).toBe("Content cannot be empty.") - expect(userSpy).toHaveBeenCalledTimes(0); - expect(authStub.called).toBeTruthy(); - expect(empStub.called).toBeTruthy(); - authStub.resetHistory(); - empStub.resetHistory(); - }); - - it("UC1.2.2 - should return 400 with message when no password", async () => { - // arrange - let noPassReqUser = { - email: reqUser.email, - name: reqUser.name, - role: reqUser.role - }; - - // act - const response = await request.post("/api/users") - .send(noPassReqUser); - - // assert - expect(response.status).toBe(400); - expect(response.body.message).toBe("Content cannot be empty.") - expect(userSpy).toHaveBeenCalledTimes(0); - expect(authStub.called).toBeTruthy(); - expect(empStub.called).toBeTruthy(); - authStub.resetHistory(); - empStub.resetHistory(); - }); - - it("UC1.2.3 - should return 400 with message when no role", async () => { - // arrange - let noRoleReqUser = { - email: reqUser.email, - password: reqUser.password, - name: reqUser.name - }; - - // act - const response = await request.post("/api/users") - .send(noRoleReqUser); - - // assert - expect(response.status).toBe(400); - expect(response.body.message).toBe("Content cannot be empty.") - expect(userSpy).toHaveBeenCalledTimes(0); - expect(authStub.called).toBeTruthy(); - expect(empStub.called).toBeTruthy(); - authStub.resetHistory(); - empStub.resetHistory(); - }); - - it("UC1.2.4 - should return 400 with message when email doesn't finish by benoit-cote.com", async () => { - // arrange - let wrongEmailReqUser = { - email: "wrong@format.email", - password: reqUser.password, - name: reqUser.name, - role: reqUser.role - }; - - // act - const response = await request.post("/api/users") - .send(wrongEmailReqUser); - - // assert - expect(response.status).toBe(400); - expect(response.body.message).toBe("Invalid email format.") - expect(userSpy).toHaveBeenCalledTimes(0); - expect(authStub.called).toBeTruthy(); - expect(empStub.called).toBeTruthy(); - authStub.resetHistory(); - empStub.resetHistory(); - }); - }); - - describe("UC1.3 - given I try to add a user but I am not authorized", () => { - it("UC1.3.1 - Should respond with a 403 status code", async () => { - let response = await UserController.create(reqUserEmployee, res); - expect(response.statusCode).toBe(403); - }); - }); - }); - - describe("UC2 - View all Users", () => { - - describe("UC2.1 - Given a token passed", () => { - it("UC2.1.1 - Should respond with a 200 status code when admin", async () => { - // arrange - let ListUser = []; - const listUserLength = 3; - for (let i = 0; i < listUserLength; i++) { - ListUser.push(resUserFromService); - } - userSpy = jest.spyOn(UserService, 'getAllUsers') - .mockImplementation(() => new Promise( - (resolve) => { - resolve(ListUser); - } - )); - - // act - const response = await request.get("/api/users"); - - // assert - expect(response.status).toBe(200); - expect(userSpy).toHaveBeenCalledTimes(1); - expect(JSON.stringify(response.body)).toEqual(JSON.stringify(ListUser)); - }); - - it("UC2.1.2 - Should respond with a 403 status code when employee", async () => { - // act - const response = await UserController.findAll(reqUserEmployee, res); - - // assert - expect(response.statusCode).toBe(403); - }); - - it("UC2.1.3 - Should respond with a 500 status code when user service throws error", async () => { - // arrange - userSpy = jest.spyOn(UserService, 'getAllUsers') - .mockImplementation(async () => { - await Promise.reject({ status: 500 }); - }); - - // act - const response = await request.get("/api/users"); - - // assert - expect(response.status).toBe(500); - }); - }); - }); - - describe("UC3 - Authenticating a User)", () => { - - describe("UC3.1 - given existing email and password", () => { - it("UC3.1.1 - should respond with 200 status code", async () => { - // arrange - userSpy = jest.spyOn(UserService, 'authenticateUser') - .mockImplementation(() => new Promise((resolve) => { - resolve(resUserFromService); - })); - - // act - const response = await request.post("/api/users/authenticate") - .send(reqUser); - - // assert - expect(response.statusCode).toBe(200); - expect(response.body.aToken).toBe("aToken"); - expect(response.get('authorization')).toBe("rToken"); - expect(userSpy).toBeCalledTimes(1); - expect(authSpy).toBeCalledTimes(1); - }); - }); - - describe("UC3.2 - given non existent email and/or wrong password", () => { - it("UC3.2.1 - should respond with 401 status code, when user service can't find user", async () => { - // arrange - userSpy = jest.spyOn(UserService, 'authenticateUser') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - authSpy = jest.spyOn(AuthService, 'getTokens'); - - // act - const response = await request.post("/api/users/authenticate").send(reqUser); - - // assert - expect(userSpy).toBeCalledTimes(1); - expect(authSpy).toBeCalledTimes(0); - expect(response.statusCode).toBe(401); - }); - }); - - describe("UC3.3 - given invalid user", () => { - it("UC3.3.1 - should return 400 and message when no email", async () => { - // arrange - let noEmailReqUser = { - password: reqUser.password - } - userSpy = jest.spyOn(UserService, 'authenticateUser'); - authSpy = jest.spyOn(AuthService, 'getTokens'); - - // act - const response = await request.post("/api/users/authenticate").send(noEmailReqUser); - - // assert - expect(response.statusCode).toBe(400); - expect(response.body.message).toBe("Content cannot be empty."); - expect(userSpy).toBeCalledTimes(0); - expect(authSpy).toBeCalledTimes(0); - }); - }); - - describe("UC3.4 - given an error occurs with the user service", () => { - it("UC3.4.1 - should return 500 and a message", async () => { - // arrange - userSpy = jest.spyOn(UserService, 'authenticateUser') - .mockRejectedValue(new Error("Error with the user service.")); - authSpy = jest.spyOn(AuthService, 'getTokens'); - - // act - const response = await request.post("/api/users/authenticate").send(reqUser); - - // assert - expect(response.statusCode).toBe(500); - expect(response.error.text).toBe("Error with the user service."); - - }); - }); - }); - - describe("UC4 - Modify a User)", () => { - - const expectedUserToModifyValid = { - email: reqUser.email, - password: reqUser.password, - role: reqUser.role - }; - - const modifiedUserInvalid = { - password: "validPassword", - role: "admin" + .mockImplementation(() => ['aToken', 'rToken']) + +const makeApp = require('../../app') +const app = makeApp() +const request = supertest(app) +let res + +describe('Test UserController', () => { + beforeEach(() => { + jest.clearAllMocks() + res = new MockExpressResponse() + }) + + afterEach(() => { + sandbox.restore() + }) + + afterAll(() => { + process.exit + }) + + describe('UC1 - Create a User', () => { + describe('UC1.1 - given a valid user body', () => { + it('UC1.1.1 - should respond with a 200 status code with user from user service', async () => { + // arrange + const expectedUser = { + email: reqUser.email, + name: reqUser.name, + role: reqUser.role + } + userSpy = jest.spyOn(UserService, 'createUser') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedUser) + })) + + // act + const response = await supertest(app).post('/api/users') + .send(reqUser) + + // assert + expect(response.status).toBe(200) + expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedUser)) + expect(userSpy).toHaveBeenCalledTimes(1) + expect(authStub.called).toBeTruthy() + expect(empStub.called).toBeTruthy() + + // this needs to be done manually in each test because it + // doesn't work in the afterEach for some reason + authStub.resetHistory() + empStub.resetHistory() + }) + + describe('UC1.1.2 - Given service throws error', () => { + it("UC1.1.2.1 - when error message equals 'Validation error' should respond with 400", async () => { + // arrange + const userSpy = jest.spyOn(UserService, 'createUser') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({ message: 'Validation error' }) + })) + + // act + const response = await request.post('/api/users') + .send(reqUser) + + // assert + expect(response.status).toBe(400) + expect(response.body.message).toBe('User already exists.') + expect(userSpy).toHaveBeenCalledTimes(1) + }) + + it("UC1.1.2.2 - when error message equals 'Validation error' should respond with 500", async () => { + // arrange + const userSpy = jest.spyOn(UserService, 'createUser') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({ message: 'error' }) + })) + + // act + const response = await request.post('/api/users') + .send(reqUser) + + // assert + expect(response.status).toBe(500) + expect(response.body.message).toBe('error') + expect(userSpy).toHaveBeenCalledTimes(1) + }) + }) + }) + + describe('UC1.2 - given an invalid user body', () => { + it('UC1.2.1 - should return 400 with message when no email', async () => { + // arrange + const noEmailReqUser = { + password: reqUser.password, + name: reqUser.name, + role: reqUser.role + } + + // act + const response = await request.post('/api/users') + .send(noEmailReqUser) + + // assert + expect(response.status).toBe(400) + expect(response.body.message).toBe('Content cannot be empty.') + expect(userSpy).toHaveBeenCalledTimes(0) + expect(authStub.called).toBeTruthy() + expect(empStub.called).toBeTruthy() + authStub.resetHistory() + empStub.resetHistory() + }) + + it('UC1.2.2 - should return 400 with message when no password', async () => { + // arrange + const noPassReqUser = { + email: reqUser.email, + name: reqUser.name, + role: reqUser.role + } + + // act + const response = await request.post('/api/users') + .send(noPassReqUser) + + // assert + expect(response.status).toBe(400) + expect(response.body.message).toBe('Content cannot be empty.') + expect(userSpy).toHaveBeenCalledTimes(0) + expect(authStub.called).toBeTruthy() + expect(empStub.called).toBeTruthy() + authStub.resetHistory() + empStub.resetHistory() + }) + + it('UC1.2.3 - should return 400 with message when no role', async () => { + // arrange + const noRoleReqUser = { + email: reqUser.email, + password: reqUser.password, + name: reqUser.name + } + + // act + const response = await request.post('/api/users') + .send(noRoleReqUser) + + // assert + expect(response.status).toBe(400) + expect(response.body.message).toBe('Content cannot be empty.') + expect(userSpy).toHaveBeenCalledTimes(0) + expect(authStub.called).toBeTruthy() + expect(empStub.called).toBeTruthy() + authStub.resetHistory() + empStub.resetHistory() + }) + + it("UC1.2.4 - should return 400 with message when email doesn't finish by benoit-cote.com", async () => { + // arrange + const wrongEmailReqUser = { + email: 'wrong@format.email', + password: reqUser.password, + name: reqUser.name, + role: reqUser.role + } + + // act + const response = await request.post('/api/users') + .send(wrongEmailReqUser) + + // assert + expect(response.status).toBe(400) + expect(response.body.message).toBe('Invalid email format.') + expect(userSpy).toHaveBeenCalledTimes(0) + expect(authStub.called).toBeTruthy() + expect(empStub.called).toBeTruthy() + authStub.resetHistory() + empStub.resetHistory() + }) + }) + + describe('UC1.3 - given I try to add a user but I am not authorized', () => { + it('UC1.3.1 - Should respond with a 403 status code', async () => { + const response = await UserController.create(reqUserEmployee, res) + expect(response.statusCode).toBe(403) + }) + }) + }) + + describe('UC2 - View all Users', () => { + describe('UC2.1 - Given a token passed', () => { + it('UC2.1.1 - Should respond with a 200 status code when admin', async () => { + // arrange + const ListUser = [] + const listUserLength = 3 + for (let i = 0; i < listUserLength; i++) { + ListUser.push(resUserFromService) + } + userSpy = jest.spyOn(UserService, 'getAllUsers') + .mockImplementation(() => new Promise( + (resolve) => { + resolve(ListUser) + } + )) + + // act + const response = await request.get('/api/users') + + // assert + expect(response.status).toBe(200) + expect(userSpy).toHaveBeenCalledTimes(1) + expect(JSON.stringify(response.body)).toEqual(JSON.stringify(ListUser)) + }) + + it('UC2.1.2 - Should respond with a 403 status code when employee', async () => { + // act + const response = await UserController.findAll(reqUserEmployee, res) + + // assert + expect(response.statusCode).toBe(403) + }) + + it('UC2.1.3 - Should respond with a 500 status code when user service throws error', async () => { + // arrange + userSpy = jest.spyOn(UserService, 'getAllUsers') + .mockImplementation(async () => { + await Promise.reject({ status: 500 }) + }) + + // act + const response = await request.get('/api/users') + + // assert + expect(response.status).toBe(500) + }) + }) + }) + + describe('UC3 - Authenticating a User)', () => { + describe('UC3.1 - given existing email and password', () => { + it('UC3.1.1 - should respond with 200 status code', async () => { + // arrange + userSpy = jest.spyOn(UserService, 'authenticateUser') + .mockImplementation(() => new Promise((resolve) => { + resolve(resUserFromService) + })) + + // act + const response = await request.post('/api/users/authenticate') + .send(reqUser) + + // assert + expect(response.statusCode).toBe(200) + expect(response.body.aToken).toBe('aToken') + expect(response.get('authorization')).toBe('rToken') + expect(userSpy).toBeCalledTimes(1) + expect(authSpy).toBeCalledTimes(1) + }) + }) + + describe('UC3.2 - given non existent email and/or wrong password', () => { + it("UC3.2.1 - should respond with 401 status code, when user service can't find user", async () => { + // arrange + userSpy = jest.spyOn(UserService, 'authenticateUser') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + authSpy = jest.spyOn(AuthService, 'getTokens') + + // act + const response = await request.post('/api/users/authenticate').send(reqUser) + + // assert + expect(userSpy).toBeCalledTimes(1) + expect(authSpy).toBeCalledTimes(0) + expect(response.statusCode).toBe(401) + }) + }) + + describe('UC3.3 - given invalid user', () => { + it('UC3.3.1 - should return 400 and message when no email', async () => { + // arrange + const noEmailReqUser = { + password: reqUser.password + } + userSpy = jest.spyOn(UserService, 'authenticateUser') + authSpy = jest.spyOn(AuthService, 'getTokens') + + // act + const response = await request.post('/api/users/authenticate').send(noEmailReqUser) + + // assert + expect(response.statusCode).toBe(400) + expect(response.body.message).toBe('Content cannot be empty.') + expect(userSpy).toBeCalledTimes(0) + expect(authSpy).toBeCalledTimes(0) + }) + }) + + describe('UC3.4 - given an error occurs with the user service', () => { + it('UC3.4.1 - should return 500 and a message', async () => { + // arrange + userSpy = jest.spyOn(UserService, 'authenticateUser') + .mockRejectedValue(new Error('Error with the user service.')) + authSpy = jest.spyOn(AuthService, 'getTokens') + + // act + const response = await request.post('/api/users/authenticate').send(reqUser) + + // assert + expect(response.statusCode).toBe(500) + expect(response.error.text).toBe('Error with the user service.') + }) + }) + }) + + describe('UC4 - Modify a User)', () => { + const expectedUserToModifyValid = { + email: reqUser.email, + password: reqUser.password, + role: reqUser.role + } + + const modifiedUserInvalid = { + password: 'validPassword', + role: 'admin' + } + + describe('UC4.1 - given user is authenticated and that entries are valid', () => { + it('UC4.1.1 - should respond with a 200 status code with a modified user', async () => { + // arrange + userSpy = jest.spyOn(UserService, 'modifyUser') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedUserToModifyValid) + })) + + // act + const response = await request.put(`/api/users/modify/${reqUser.email}`) + .send(expectedUserToModifyValid) + + expect(response.status).toBe(200) + expect(userSpy).toHaveBeenCalledTimes(1) + expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedUserToModifyValid)) + }) + }) + + describe('UC4.2 - given user is authenticated but email is invalid', () => { + it('UC4.2.1 - should respond with a 400 response message', async () => { + // arrange + userSpy = jest.spyOn(UserService, 'modifyUser') + + // act + const response = await supertest(app).put('/api/users/modify/someEmail') + .send(modifiedUserInvalid) + + // assert + expect(response.status).toBe(400) + expect(userSpy).toHaveBeenCalledTimes(0) + }) + }) + + describe('UC4.3 - given I try to modify the user but I am not authorized', () => { + it('UC4.3.1 - Should respond with a 403 status code', async () => { + // arrange + userSpy = jest.spyOn(UserService, 'modifyUser') + + // act + const response = await UserController.modifyUser(reqUserEmployee, res) + + // assert + expect(response.statusCode).toBe(403) + expect(userSpy).toHaveBeenCalledTimes(0) + }) + }) + + describe('UC4.4 - given I try to call the modifyUser service but the modifyUser service sends an error', () => { + it('UC4.4.1 - Should respond with a 500 response message', async () => { + // arrange + userSpy = jest.spyOn(UserService, 'modifyUser') + .mockImplementation(async () => { + await Promise.reject({ status: 500 }) + }) + + // act + const response = await request.put(`/api/users/modify/${expectedUserToModifyValid.email}`) + .send(expectedUserToModifyValid) + + // assert + expect(response.status).toBe(500) + expect(userSpy).toHaveBeenCalledTimes(1) + }) + }) + }) + + describe('UC5 - Delete a User)', () => { + describe('UC5.1 - given user is authenticated and that the email is valid', () => { + it('UC5.1.1 - should respond with a 200 status code with a deleted user', async () => { + // arrange + const deletedUser = { + email: reqUser.email + } + userSpy = jest.spyOn(UserService, 'deleteUser') + .mockImplementation(() => new Promise((resolve) => { + resolve(deletedUser) + })) + + // act + const response = await request.delete(`/api/users/delete/${deletedUser.email}`) + .send(deletedUser) + + // assert + expect(response.status).toBe(200) + expect(userSpy).toHaveBeenCalledTimes(1) + expect(JSON.stringify(response.body)).toEqual(JSON.stringify(deletedUser)) + }) + }) + + describe('UC5.2 - given I try to delete the user but I am not authorized', () => { + it('UC5.2.1 - Should respond with a 403 status code', async () => { + // act + const response = await UserController.deleteUser(reqUserEmployee, res) + + // assert + expect(response.statusCode).toBe(403) + }) + }) + + describe('UC5.3 - given I try to call the deleteUser in userService but the deleteUser methods sends an error', () => { + it('UC5.3.1 - Should respond with a 500 response message', async () => { + const expectedUserToDelete = { + email: 'first@benoit-cote.com' } - describe("UC4.1 - given user is authenticated and that entries are valid", () => { - it("UC4.1.1 - should respond with a 200 status code with a modified user", async () => { - // arrange - userSpy = jest.spyOn(UserService, "modifyUser") - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedUserToModifyValid); - })); - - // act - const response = await request.put(`/api/users/modify/${reqUser.email}`) - .send(expectedUserToModifyValid); - - expect(response.status).toBe(200); - expect(userSpy).toHaveBeenCalledTimes(1); - expect(JSON.stringify(response.body)).toEqual(JSON.stringify(expectedUserToModifyValid)); - }); - }); - - describe("UC4.2 - given user is authenticated but email is invalid", () => { - it("UC4.2.1 - should respond with a 400 response message", async () => { - // arrange - userSpy = jest.spyOn(UserService, "modifyUser"); - - // act - const response = await supertest(app).put(`/api/users/modify/someEmail`) - .send(modifiedUserInvalid); - - // assert - expect(response.status).toBe(400); - expect(userSpy).toHaveBeenCalledTimes(0); - }); - }); - - describe("UC4.3 - given I try to modify the user but I am not authorized", () => { - it("UC4.3.1 - Should respond with a 403 status code", async () => { - // arrange - userSpy = jest.spyOn(UserService, "modifyUser"); - - // act - let response = await UserController.modifyUser(reqUserEmployee, res); - - // assert - expect(response.statusCode).toBe(403); - expect(userSpy).toHaveBeenCalledTimes(0); - }); - }); - - describe("UC4.4 - given I try to call the modifyUser service but the modifyUser service sends an error", () => { - it("UC4.4.1 - Should respond with a 500 response message", async () => { - // arrange - userSpy = jest.spyOn(UserService, "modifyUser") - .mockImplementation(async () => { - await Promise.reject({ status: 500 }); - }); - - // act - const response = await request.put(`/api/users/modify/${expectedUserToModifyValid.email}`) - .send(expectedUserToModifyValid); - - // assert - expect(response.status).toBe(500); - expect(userSpy).toHaveBeenCalledTimes(1); - }) - }); - }); - - describe("UC5 - Delete a User)", () => { - - describe("UC5.1 - given user is authenticated and that the email is valid", () => { - it("UC5.1.1 - should respond with a 200 status code with a deleted user", async () => { - // arrange - const deletedUser = { - email: reqUser.email - } - userSpy = jest.spyOn(UserService, "deleteUser") - .mockImplementation(() => new Promise((resolve) => { - resolve(deletedUser); - })); - - // act - const response = await request.delete(`/api/users/delete/${deletedUser.email}`) - .send(deletedUser); - - // assert - expect(response.status).toBe(200); - expect(userSpy).toHaveBeenCalledTimes(1); - expect(JSON.stringify(response.body)).toEqual(JSON.stringify(deletedUser)); - }); - }); - - describe("UC5.2 - given I try to delete the user but I am not authorized", () => { - it("UC5.2.1 - Should respond with a 403 status code", async () => { - // act - let response = await UserController.deleteUser(reqUserEmployee, res); - - // assert - expect(response.statusCode).toBe(403); - }); - }); - - describe("UC5.3 - given I try to call the deleteUser in userService but the deleteUser methods sends an error", () => { - it("UC5.3.1 - Should respond with a 500 response message", async () => { - - let expectedUserToDelete = { - email: "first@benoit-cote.com" - } - - userSpy = jest.spyOn(UserService, "deleteUser") - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedUserToDeleteInvalid); - })); - - const response = await supertest(app).delete(`/api/users/delete/${expectedUserToDelete.email}`) - .set("authorization", "Bearer validToken") - .send(expectedUserToDelete); - - expect(response.status).toBe(500); - expect(userSpy).toHaveBeenCalledTimes(1); - expect(authStub.called).toBeTruthy(); - }); - }); - }); -}); \ No newline at end of file + userSpy = jest.spyOn(UserService, 'deleteUser') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + const response = await request.delete(`/api/users/delete/${expectedUserToDelete.email}`) + .set('authorization', 'Bearer validToken') + .send(expectedUserToDelete) + + expect(response.status).toBe(500) + expect(userSpy).toHaveBeenCalledTimes(1) + expect(authStub.called).toBeTruthy() + }) + }) + }) +}) diff --git a/server/tests/services/auth.service.test.js b/server/tests/services/auth.service.test.js index 365dd8a..f4bc5a4 100644 --- a/server/tests/services/auth.service.test.js +++ b/server/tests/services/auth.service.test.js @@ -1,195 +1,175 @@ -const AuthService = require('../../services/auth.service'); -const EmpService = require('../../services/emp.service'); -const UserController = require("../../controllers/user.controller"); -const jwt = require('jsonwebtoken'); -var { expect, jest } = require('@jest/globals'); -const supertest = require('supertest'); -const sinon = require('sinon'); - - -const resUser = { - userId: "validUUID", - email: "valid@email.com", - password: "validPassword", - name: "validName", - role: "admin", - updatedAt: new Date("2020-12-20"), - createdAt: new Date("2020-12-20") -}; +const AuthService = require('../../services/auth.service') +const EmpService = require('../../services/emp.service') +const jwt = require('jsonwebtoken') +const { expect } = require('@jest/globals') +const supertest = require('supertest') +const sinon = require('sinon') + const reqUser = { - email: "valid@email.com", - password: "validPassword", - name: "validName", - role: "admin" -}; + email: 'valid@email.com', + password: 'validPassword', + name: 'validName', + role: 'admin' +} const reqEmp = { - email: "emp@benoit-cote.com", - firstName: "FName", - lastName: "LName" + email: 'emp@benoit-cote.com', + firstName: 'FName', + lastName: 'LName' } - -let sandbox = sinon.createSandbox(); -let empStub = sandbox.stub(EmpService, 'checkEmail') - .callsFake(function(req, res, next){ - req.emp = reqEmp; - return next(); -}); - - -const makeApp = require('../../app'); -let app = makeApp(); -const request = supertest(app); - -describe("Test Authentication Service", () => { - - beforeEach(() => { - jest.clearAllMocks(); - }); - - - afterAll(() => { - process.exit; - }); - - describe("AS1 - refreshToken", () => { - - describe("AS1.1 - given no token in header", () =>{ - it("AS1.1.1 - should return 403 Forbidden", async () =>{ - // act - const response = await request.delete("/api/users/logout"); - - // assert - expect(response.status).toBe(403); - }); - }); - - describe("AS.2 - given token in header", () => { - it("AS2.1 - should return 204 when valid token", async () => { - // arrange - AuthService.setRefreshTokens("test"); - - // act - const response = await request.delete("/api/users/logout") - .set("authorization", `Bearer test`); - - // assert - expect(response.status).toBe(204); - }); - - it("AS2.2 - should return 403 when invalid token", async () => { - // act - const response = await request.delete("/api/users/logout") - .set("authorization", "Bearer invalidToken"); - - // assert - expect(response.status).toBe(403); - }); - }); - }); - - - describe("AS2 - authenticateToken", () => { - - describe("AS2.1 - given no token in header", () =>{ - it("AS2.1.1 - should return 403 Forbidden", async () =>{ - // act - const response = await request.post("/api/users"); - - // assert - expect(response.status).toBe(403); - }); - }); - - describe("AS2.2 - given valid token in header", () => { - it("AS2.2.1 - should return 200 OK", async () => { - // arrange - const [aToken, rToken] = AuthService.getTokens(reqUser); - - // act - const resp = await request.post("/api/users") - .set("authorization", `Bearer ${aToken}`) - .send(reqUser); - - // assert - expect(empStub.called).toBeTruthy(); - empStub.resetHistory(); - }); - - }); - - describe("AS2.3 - given invalid token in header", () =>{ - it("AS2.3.1 - should return 401 Unauthorized", async () =>{ - // act - const response = await request.get("/api/users") - .set("authorization", `Bearer invalidToken`); - - // assert - expect(response.status).toBe(401); - }); - it("AS2.3.2 - should return 403 Forbidden", async () =>{ - // act - const response = await request.get("/api/users") - .set("authorization", `invalidToken`); - - // assert - expect(response.status).toBe(403); - }); - }); - }); - - describe("A32 - refreshToken", () => { - - describe("AS3.1 - given no token in header", () =>{ - it("AS3.1.1 - should return 403 Forbidden", async () =>{ - // act - const response = await request.post("/api/users/refresh") - .set("authorization", `Bearer 545`) - .send(reqEmp); - - // assert - expect(response.status).toBe(403); - }); - - it("AS3.1.2 - should return 401 Forbidden", async () =>{ - // act - const response = await request.post("/api/users/refresh") - .set("authorization", `Bearer `) - .send(reqEmp); - - // assert - expect(response.status).toBe(401); - }); - }); - }); - - describe("AS4 - getTokens", () => { - describe("AS4.1 - given a valid user", () => { - it("AS4.1.1 - should return access and refresh token for given user", () => { - // act - const [aToken, rToken] = AuthService.getTokens(reqUser); - - // assert - const aTokenPayload = jwt.decode(aToken); - const rTokenPayload = jwt.decode(rToken); - - expect(aTokenPayload.email).toBe(reqUser.email); - expect(aTokenPayload.name).toBe(reqUser.name); - expect(aTokenPayload.password).toBe(reqUser.password); - expect(aTokenPayload.role).toBe(reqUser.role); - expect(aTokenPayload.iat); - expect(aTokenPayload.exp); - - expect(rTokenPayload.email).toBe(reqUser.email); - expect(rTokenPayload.name).toBe(reqUser.name); - expect(rTokenPayload.password).toBe(reqUser.password); - expect(rTokenPayload.role).toBe(reqUser.role); - expect(rTokenPayload.iat); - expect(rTokenPayload.exp); - }); - }); - }); -}); - +const sandbox = sinon.createSandbox() +const empStub = sandbox.stub(EmpService, 'checkEmail') + .callsFake(function (req, res, next) { + req.emp = reqEmp + return next() + }) + +const makeApp = require('../../app') +const app = makeApp() +const request = supertest(app) + +describe('Test Authentication Service', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + afterAll(() => { + process.exit + }) + + describe('AS1 - refreshToken', () => { + describe('AS1.1 - given no token in header', () => { + it('AS1.1.1 - should return 403 Forbidden', async () => { + // act + const response = await request.delete('/api/users/logout') + + // assert + expect(response.status).toBe(403) + }) + }) + + describe('AS.2 - given token in header', () => { + it('AS2.1 - should return 204 when valid token', async () => { + // arrange + AuthService.setRefreshTokens('test') + + // act + const response = await request.delete('/api/users/logout') + .set('authorization', 'Bearer test') + + // assert + expect(response.status).toBe(204) + }) + + it('AS2.2 - should return 403 when invalid token', async () => { + // act + const response = await request.delete('/api/users/logout') + .set('authorization', 'Bearer invalidToken') + + // assert + expect(response.status).toBe(403) + }) + }) + }) + + describe('AS2 - authenticateToken', () => { + describe('AS2.1 - given no token in header', () => { + it('AS2.1.1 - should return 403 Forbidden', async () => { + // act + const response = await request.post('/api/users') + + // assert + expect(response.status).toBe(403) + }) + }) + + describe('AS2.2 - given valid token in header', () => { + it('AS2.2.1 - should return 200 OK', async () => { + // arrange + const tokens = AuthService.getTokens(reqUser) + const aToken = tokens[0]; + + // act + await request.post('/api/users') + .set('authorization', `Bearer ${aToken}`) + .send(reqUser) + + // assert + expect(empStub.called).toBeTruthy() + empStub.resetHistory() + }) + }) + + describe('AS2.3 - given invalid token in header', () => { + it('AS2.3.1 - should return 401 Unauthorized', async () => { + // act + const response = await request.get('/api/users') + .set('authorization', 'Bearer invalidToken') + + // assert + expect(response.status).toBe(401) + }) + it('AS2.3.2 - should return 403 Forbidden', async () => { + // act + const response = await request.get('/api/users') + .set('authorization', 'invalidToken') + + // assert + expect(response.status).toBe(403) + }) + }) + }) + + describe('A32 - refreshToken', () => { + describe('AS3.1 - given no token in header', () => { + it('AS3.1.1 - should return 403 Forbidden', async () => { + // act + const response = await request.post('/api/users/refresh') + .set('authorization', 'Bearer 545') + .send(reqEmp) + + // assert + expect(response.status).toBe(403) + }) + + it('AS3.1.2 - should return 401 Forbidden', async () => { + // act + const response = await request.post('/api/users/refresh') + .set('authorization', 'Bearer ') + .send(reqEmp) + + // assert + expect(response.status).toBe(401) + }) + }) + }) + + describe('AS4 - getTokens', () => { + describe('AS4.1 - given a valid user', () => { + it('AS4.1.1 - should return access and refresh token for given user', () => { + // act + const [aToken, rToken] = AuthService.getTokens(reqUser) + + // assert + const aTokenPayload = jwt.decode(aToken) + const rTokenPayload = jwt.decode(rToken) + + expect(aTokenPayload.email).toBe(reqUser.email) + expect(aTokenPayload.name).toBe(reqUser.name) + expect(aTokenPayload.password).toBe(reqUser.password) + expect(aTokenPayload.role).toBe(reqUser.role) + expect(aTokenPayload.iat) + expect(aTokenPayload.exp) + + expect(rTokenPayload.email).toBe(reqUser.email) + expect(rTokenPayload.name).toBe(reqUser.name) + expect(rTokenPayload.password).toBe(reqUser.password) + expect(rTokenPayload.role).toBe(reqUser.role) + expect(rTokenPayload.iat) + expect(rTokenPayload.exp) + }) + }) + }) +}) diff --git a/server/tests/services/emp.service.test.js b/server/tests/services/emp.service.test.js index 75284c9..b6944d0 100644 --- a/server/tests/services/emp.service.test.js +++ b/server/tests/services/emp.service.test.js @@ -1,271 +1,269 @@ -const EmpService = require("../../services/emp.service"); -const AuthService = require("../../services/auth.service"); -const EmpDao = require("../../data_access_layer/daos/emp.dao"); -const NameDao = require("../../data_access_layer/daos/name.dao") -const supertest = require('supertest'); -const { afterAll } = require('jest-circus'); -var { expect, jest } = require('@jest/globals'); -const sinon = require('sinon'); +const EmpService = require('../../services/emp.service') +const AuthService = require('../../services/auth.service') +const EmpDao = require('../../data_access_layer/daos/emp.dao') +const NameDao = require('../../data_access_layer/daos/name.dao') +const supertest = require('supertest') +const { afterAll } = require('jest-circus') +const { expect } = require('@jest/globals') +const sinon = require('sinon') const reqUser = { - email: "emp@benoit-cote.com", - password: "validPassword", - name: "FName LName", - role: "admin" -}; + email: 'emp@benoit-cote.com', + password: 'validPassword', + name: 'FName LName', + role: 'admin' +} const reqEmp = { - dataValues: { - email: "emp@benoit-cote.com", - firstName: "FName", - lastName: "LNameeeeeee" - } -}; + dataValues: { + email: 'emp@benoit-cote.com', + firstName: 'FName', + lastName: 'LNameeeeeee' + } +} const fakeDaoCheckEmailResponse = { - email: "email@email.com", - name: "Cool Name" -}; - -let fakeEmployeeByNameList = [ - { - nameID: 25361, - name: 'myName1 myLastName1' - }, - { - nameID: 95423, - name: 'myName3 myLastName3' - }, - { - nameID: 15169, - name: 'myName2 myLastName2' - }, + email: 'email@email.com', + name: 'Cool Name' +} + +const fakeEmployeeByNameList = [ + { + nameID: 25361, + name: 'myName1 myLastName1' + }, + { + nameID: 95423, + name: 'myName3 myLastName3' + }, + { + nameID: 15169, + name: 'myName2 myLastName2' + } ] -let fakeSingleEmployee = [ - { - nameID: 25361, - name: 'myName1 myLastName1' - } +const fakeSingleEmployee = [ + { + nameID: 25361, + name: 'myName1 myLastName1' + } ] -let sandbox = sinon.createSandbox(); -let authStub = sandbox.stub(AuthService, 'authenticateToken') - .callsFake(function (req, res, next) { - req.user = reqUser; - return next(); - }); +const sandbox = sinon.createSandbox() +const authStub = sandbox.stub(AuthService, 'authenticateToken') + .callsFake(function (req, res, next) { + req.user = reqUser + return next() + }) let empDaoCheckEmailSpy = jest.spyOn(EmpDao, 'getEmployeeByEmail') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeDaoCheckEmailResponse); - })); - -let nameDAOSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeEmployeeByNameList); - })); - -const makeApp = require('../../app'); -const MockExpressResponse = require("mock-express-response"); -app = makeApp(); -const request = supertest(app); -let res; - -describe("Test Employee Service", () => { - - beforeEach(() => { - jest.clearAllMocks(); - res = new MockExpressResponse(); - }); - - afterAll(() => { - process.exit; - }); - - describe("ES1 - checkEmail", () => { - - describe("ES1.1 - given employee email", () => { - it("ES1.1.1 - when existing, should call controller with appropriate req", async () => { - // arrange - empDaoCheckEmailSpy = jest.spyOn(EmpDao, 'getEmployeeByEmail') - .mockImplementation((email) => new Promise((resolve) => { - resolve(fakeDaoCheckEmailResponse); - })); - const serviceReq = { - body: { - email: 'email@email.com' - } - }; - - const next = jest.fn(); - - // act - await EmpService.checkEmail(serviceReq, res, next) - - // assert - expect(next).toHaveBeenCalled() - expect(empDaoCheckEmailSpy).toHaveBeenCalledWith(serviceReq.body.email); - }) - - it("ES1.1.2 - when non existent, should return 400 with message", async () => { - // arrange - empDaoCheckEmailSpy = jest.spyOn(EmpDao, 'getEmployeeByEmail') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - const serviceReq = { - body: { - email: 'nonexistent@email.com' - } - }; - const expectedResponse = { - status: 400, - message: "Employee email doesn't exist." - }; - - // act - const response = await request.post("/api/users").send(serviceReq); - - // assert - expect(empDaoCheckEmailSpy).toHaveBeenCalled(); - expect(response.status).toEqual(400); - expect(response.body).toEqual(expectedResponse); - }); - - it("ES1.1.3 - when dao throws error with message, should catch and return 500 with error message", async () => { - // arrange - empDaoCheckEmailSpy = jest.spyOn(EmpDao, 'getEmployeeByEmail') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({ message: "Error message." }); - })); - const serviceReq = { - body: { - email: 'nonexistent@email.com' - } - }; - const expectedResponse = { - status: 500, - message: "Error message." - }; - - // act - const response = await request.post("/api/users").send(serviceReq); - - // assert - expect(empDaoCheckEmailSpy).toHaveBeenCalled(); - expect(response.status).toEqual(500); - expect(response.body).toEqual(expectedResponse); - }); - - it("ES1.1.4 - when dao throws error with no message, should catch and return 500 with default message", async () => { - // arrange - empDaoCheckEmailSpy = jest.spyOn(EmpDao, 'getEmployeeByEmail') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - const serviceReq = { - body: { - email: 'nonexistent@email.com' - } - }; - const expectedResponse = { - status: 500, - message: "some error occured" - }; - - // act - const response = await request.post("/api/users").send(serviceReq); - - // assert - expect(empDaoCheckEmailSpy).toHaveBeenCalled(); - expect(response.status).toEqual(500); - expect(response.body).toEqual(expectedResponse); - }); - }); - }); - - describe("ES2 - getAllEmployees", () => { - describe("ES2.1 - given no arguments", () => { - it("ES2.1.1 - Should return a list of employees", async () => { - // arrange - NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeEmployeeByNameList); - })); - - // act - const response = await EmpService.getAllEmployees(); - - // assert - expect(response).toEqual(fakeEmployeeByNameList); - }); - - it("ES2.1.2 - should reject with error when dao throws error", async () => { - // arrange - NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') - .mockImplementation(() => new Promise((resolve, rejects) => { - rejects({message: "dao failed"}); - })); - - // act and assert - await expect(EmpService.getAllEmployees()).rejects - .toEqual({message: "dao failed"}) - }); - - it("ES2.1.3 - should resolve with false when dao resolves false", async () => { - // arrange - NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') - .mockImplementation(() => new Promise((resolve, rejects) => { - resolve(false); - })); - - // act and assert - await expect(EmpService.getAllEmployees()).resolves - .toEqual(false); - }); - }); - - describe("ES2.2 - given a name argument", () => { - let name = 'myName1 myLastName1'; - - it("ES2.2.1 - Should return a list of employees", async () => { - // arrange - NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeEmployeeByNameList); - })); - - // act - const response = await EmpService.getAllEmployees(name); - - // assert - expect(response).toEqual(fakeSingleEmployee); - }); - - it("ES2.2.2 - should reject with error when dao throws error", async () => { - // arrange - NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') - .mockImplementation(() => new Promise((resolve, rejects) => { - rejects({message: "dao failed"}); - })); - - // act and assert - await expect(EmpService.getAllEmployees(name)).rejects - .toEqual({message: "dao failed"}) - }); - - it("ES2.2.3 - should resolve with false when dao resolves false", async () => { - // arrange - NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') - .mockImplementation(() => new Promise((resolve, rejects) => { - resolve(false); - })); - - // act and assert - await expect(EmpService.getAllEmployees(name)).resolves - .toEqual(false); - }); - }); - }); -}); \ No newline at end of file + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeDaoCheckEmailResponse) + })) + +let NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeEmployeeByNameList) + })) + +const makeApp = require('../../app') +const MockExpressResponse = require('mock-express-response') +let app = makeApp() +const request = supertest(app) +let res + +describe('Test Employee Service', () => { + beforeEach(() => { + jest.clearAllMocks() + res = new MockExpressResponse() + }) + + afterAll(() => { + process.exit + }) + + describe('ES1 - checkEmail', () => { + describe('ES1.1 - given employee email', () => { + it('ES1.1.1 - when existing, should call controller with appropriate req', async () => { + // arrange + empDaoCheckEmailSpy = jest.spyOn(EmpDao, 'getEmployeeByEmail') + .mockImplementation((email) => new Promise((resolve) => { + resolve(fakeDaoCheckEmailResponse) + })) + const serviceReq = { + body: { + email: 'email@email.com' + } + } + + const next = jest.fn() + + // act + await EmpService.checkEmail(serviceReq, res, next) + + // assert + expect(next).toHaveBeenCalled() + expect(empDaoCheckEmailSpy).toHaveBeenCalledWith(serviceReq.body.email) + }) + + it('ES1.1.2 - when non existent, should return 400 with message', async () => { + // arrange + empDaoCheckEmailSpy = jest.spyOn(EmpDao, 'getEmployeeByEmail') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + const serviceReq = { + body: { + email: 'nonexistent@email.com' + } + } + const expectedResponse = { + status: 400, + message: "Employee email doesn't exist." + } + + // act + const response = await request.post('/api/users').send(serviceReq) + + // assert + expect(empDaoCheckEmailSpy).toHaveBeenCalled() + expect(response.status).toEqual(400) + expect(response.body).toEqual(expectedResponse) + }) + + it('ES1.1.3 - when dao throws error with message, should catch and return 500 with error message', async () => { + // arrange + empDaoCheckEmailSpy = jest.spyOn(EmpDao, 'getEmployeeByEmail') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({ message: 'Error message.' }) + })) + const serviceReq = { + body: { + email: 'nonexistent@email.com' + } + } + const expectedResponse = { + status: 500, + message: 'Error message.' + } + + // act + const response = await request.post('/api/users').send(serviceReq) + + // assert + expect(empDaoCheckEmailSpy).toHaveBeenCalled() + expect(response.status).toEqual(500) + expect(response.body).toEqual(expectedResponse) + }) + + it('ES1.1.4 - when dao throws error with no message, should catch and return 500 with default message', async () => { + // arrange + empDaoCheckEmailSpy = jest.spyOn(EmpDao, 'getEmployeeByEmail') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + const serviceReq = { + body: { + email: 'nonexistent@email.com' + } + } + const expectedResponse = { + status: 500, + message: 'some error occured' + } + + // act + const response = await request.post('/api/users').send(serviceReq) + + // assert + expect(empDaoCheckEmailSpy).toHaveBeenCalled() + expect(response.status).toEqual(500) + expect(response.body).toEqual(expectedResponse) + }) + }) + }) + + describe('ES2 - getAllEmployees', () => { + describe('ES2.1 - given no arguments', () => { + it('ES2.1.1 - Should return a list of employees', async () => { + // arrange + NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeEmployeeByNameList) + })) + + // act + const response = await EmpService.getAllEmployees() + + // assert + expect(response).toEqual(fakeEmployeeByNameList) + }) + + it('ES2.1.2 - should reject with error when dao throws error', async () => { + // arrange + NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') + .mockImplementation(() => new Promise((resolve, rejects) => { + rejects({ message: 'dao failed' }) + })) + + // act and assert + await expect(EmpService.getAllEmployees()).rejects + .toEqual({ message: 'dao failed' }) + }) + + it('ES2.1.3 - should resolve with false when dao resolves false', async () => { + // arrange + NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') + .mockImplementation(() => new Promise((resolve, rejects) => { + resolve(false) + })) + + // act and assert + await expect(EmpService.getAllEmployees()).resolves + .toEqual(false) + }) + }) + + describe('ES2.2 - given a name argument', () => { + const name = 'myName1 myLastName1' + + it('ES2.2.1 - Should return a list of employees', async () => { + // arrange + NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeEmployeeByNameList) + })) + + // act + const response = await EmpService.getAllEmployees(name) + + // assert + expect(response).toEqual(fakeSingleEmployee) + }) + + it('ES2.2.2 - should reject with error when dao throws error', async () => { + // arrange + NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') + .mockImplementation(() => new Promise((resolve, rejects) => { + rejects({ message: 'dao failed' }) + })) + + // act and assert + await expect(EmpService.getAllEmployees(name)).rejects + .toEqual({ message: 'dao failed' }) + }) + + it('ES2.2.3 - should resolve with false when dao resolves false', async () => { + // arrange + NameDaoSpy = jest.spyOn(NameDao, 'getAllEmployeeNames') + .mockImplementation(() => new Promise((resolve, rejects) => { + resolve(false) + })) + + // act and assert + await expect(EmpService.getAllEmployees(name)).resolves + .toEqual(false) + }) + }) + }) +}) diff --git a/server/tests/services/invoice.service.test.js b/server/tests/services/invoice.service.test.js index 9cfe5ad..7e11765 100644 --- a/server/tests/services/invoice.service.test.js +++ b/server/tests/services/invoice.service.test.js @@ -1,838 +1,821 @@ -const sinon = require("sinon"); -var { expect, jest } = require('@jest/globals'); +const sinon = require('sinon') +const { expect } = require('@jest/globals') -const InvoiceService = require("../../services/invoice.service"); -const TransacStatDao = require("../../data_access_layer/daos/transac_stat.dao"); -const InvoiceAffectDao = require("../../data_access_layer/daos/invoice_affect.dao"); -const ClientDao = require("../../data_access_layer/daos/name.dao"); -const CountryDao = require("../../data_access_layer/daos/country.dao"); +const InvoiceService = require('../../services/invoice.service') +const TransacStatDao = require('../../data_access_layer/daos/transac_stat.dao') +const InvoiceAffectDao = require('../../data_access_layer/daos/invoice_affect.dao') +const ClientDao = require('../../data_access_layer/daos/name.dao') +const CountryDao = require('../../data_access_layer/daos/country.dao') -let yearMonthList = [202011, 202012, 202101]; -let clientIDList = [32590, 36052, 37960]; +const yearMonthList = [202011, 202012, 202101] +const clientIDList = [32590, 36052, 37960] - -let fakeBilledList = { - billedList: [ - { - month: 202011, - billed: 100 - }, - { - month: 202012, - billed: 150 - }, - { - month: 202101, - billed: 125 - } - ], - clientIDList: [54998, 63379, 54332] -}; - -let fakeDuesList = [ +const fakeBilledList = { + billedList: [ { - month: 202011, - totalDues: 50 + month: 202011, + billed: 100 }, { - month: 202012, - totalDues: 25 + month: 202012, + billed: 150 }, { - month: 202101, - totalDues: 10 + month: 202101, + billed: 125 } -]; + ], + clientIDList: [54998, 63379, 54332] +} + +const fakeDuesList = [ + { + month: 202011, + totalDues: 50 + }, + { + month: 202012, + totalDues: 25 + }, + { + month: 202101, + totalDues: 10 + } +] -let fakeTransacStatList = [ - { - yearMonth: 202011, - dueCurrent: 50, - due1Month: 100, - due2Month: 30, - due3Month: 10 - }, - { - yearMonth: 202012, - dueCurrent: 50, - due1Month: 100, - due2Month: 30, - due3Month: 10 - }, - { - yearMonth: 202101, - dueCurrent: 50, - due1Month: 100, - due2Month: 30, - due3Month: 10 - } -]; +const fakeTransacStatList = [ + { + yearMonth: 202011, + dueCurrent: 50, + due1Month: 100, + due2Month: 30, + due3Month: 10 + }, + { + yearMonth: 202012, + dueCurrent: 50, + due1Month: 100, + due2Month: 30, + due3Month: 10 + }, + { + yearMonth: 202101, + dueCurrent: 50, + due1Month: 100, + due2Month: 30, + due3Month: 10 + } +] -let fakeInvoiceAffectList = [ - { - invoiceDate: new Date(2020, 9, 15), - actorId: 22222, - amount: 50 - }, - { - invoiceDate: new Date(2020, 10, 15), - actorId: 33333, - amount: 50 - }, - { - invoiceDate: new Date(2020, 11, 15), - actorId: 44444, - amount: 50 - } -]; +const fakeInvoiceAffectList = [ + { + invoiceDate: new Date(2020, 9, 15), + actorId: 22222, + amount: 50 + }, + { + invoiceDate: new Date(2020, 10, 15), + actorId: 33333, + amount: 50 + }, + { + invoiceDate: new Date(2020, 11, 15), + actorId: 44444, + amount: 50 + } +] -let fakeExpectedGetAverageResponse = [ - { - chart: { - 2020: [ - { - month: 202011, - average: (fakeDuesList[0].totalDues / fakeBilledList.billedList[0].billed * 365).toFixed(2), - group: 2020 - }, - { - month: 202012, - average: (fakeDuesList[1].totalDues / fakeBilledList.billedList[1].billed * 365).toFixed(2), - group: 2020 - } - ], - 2021: [ - { - month: 202101, - average: (fakeDuesList[2].totalDues / fakeBilledList.billedList[2].billed * 365).toFixed(2), - group: 2021 - } - ] +const fakeExpectedGetAverageResponse = [ + { + chart: { + 2020: [ + { + month: 202011, + average: (fakeDuesList[0].totalDues / fakeBilledList.billedList[0].billed * 365).toFixed(2), + group: 2020 }, - table: [ - { - nameId: 32590, - name: "IRIS DYNAMICS LTD", - country: "Canada", - grading: "C" - }, - { - nameId: 36052, - name: "Moussa Cisse KEITA", - country: "Canada", - grading: "N/A" - }, - { - nameId: 37960, - name: "FROST BROWN TODD LLC", - country: "United States", - grading: "N/A" - } - ] - } -]; - -let fakeGetClientInformationDaoResponse = [ - { - nameId: 32590, - name: "IRIS DYNAMICS LTD", - country: "Canada", - grading: "C" + { + month: 202012, + average: (fakeDuesList[1].totalDues / fakeBilledList.billedList[1].billed * 365).toFixed(2), + group: 2020 + } + ], + 2021: [ + { + month: 202101, + average: (fakeDuesList[2].totalDues / fakeBilledList.billedList[2].billed * 365).toFixed(2), + group: 2021 + } + ] }, - { + table: [ + { + nameId: 32590, + name: 'IRIS DYNAMICS LTD', + country: 'Canada', + grading: 'C' + }, + { nameId: 36052, - name: "Moussa Cisse KEITA", - country: "Canada", - grading: "N/A" - }, - { + name: 'Moussa Cisse KEITA', + country: 'Canada', + grading: 'N/A' + }, + { nameId: 37960, - name: "FROST BROWN TODD LLC", - country: "United States", - grading: "N/A" - } -]; - -let fakeCountriesList = [ - { - countryLabel: "Albania" - }, - { - countryLabel: "Italy" - }, - { - countryLabel: "Morocco" - } + name: 'FROST BROWN TODD LLC', + country: 'United States', + grading: 'N/A' + } + ] + } ] +const fakeGetClientInformationDaoResponse = [ + { + nameId: 32590, + name: 'IRIS DYNAMICS LTD', + country: 'Canada', + grading: 'C' + }, + { + nameId: 36052, + name: 'Moussa Cisse KEITA', + country: 'Canada', + grading: 'N/A' + }, + { + nameId: 37960, + name: 'FROST BROWN TODD LLC', + country: 'United States', + grading: 'N/A' + } +] -let sandbox = sinon.createSandbox(); +const fakeCountriesList = [ + { + countryLabel: 'Albania' + }, + { + countryLabel: 'Italy' + }, + { + countryLabel: 'Morocco' + } +] +const sandbox = sinon.createSandbox() -//the methods in invoice services +// the methods in invoice services let getBilledSpy = jest.spyOn(InvoiceService, 'getBilled') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeBilledList); - })); + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeBilledList) + })) let getDuesSpy = jest.spyOn(InvoiceService, 'getDues') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeDuesList); - })); + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeDuesList) + })) let getClientInformationSpy = jest.spyOn(InvoiceService, 'getClientInformation') - .mockImplementation(() => new Promise((resolve) => { - console.log("whatf FhASBRFIhWASEBFiwsnebfgiojnaswzeiogjn") - resolve(fakeClientNameCountryList); - })); - + .mockImplementation(() => new Promise((resolve) => { + console.log('whatf FhASBRFIhWASEBFiwsnebfgiojnaswzeiogjn') + resolve(fakeGetClientInformationDaoResponse) + })) -//the dao objects to get data from database +// the dao objects to get data from database let transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeTransacStatList); - })); + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeTransacStatList) + })) let invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeInvoiceAffectList); - })); + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeInvoiceAffectList) + })) let countryDaoSpy = jest.spyOn(CountryDao, 'getAllCountries') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeCountriesList) - })) + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeCountriesList) + })) let getClientsInClientIdListDaoSpy = jest.spyOn(ClientDao, 'getClientsInClientIdList') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeGetClientInformationDaoResponse); - })); - - -describe("Test Invoice Service", () => { - - beforeEach(() => { - jest.clearAllMocks(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - afterAll(() => { - process.exit; - }); - - describe("IS1 - getAverages", () => { - describe("IS1.1 - given two dates", () => { - it("IS1.1.1 - should respond with the list of averages per month with group key", async () => { - //arrange - getClientInformationSpy = jest.spyOn(InvoiceService, 'getClientInformation') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeGetClientInformationDaoResponse); - })); - - // act - const response = await InvoiceService.getAverages("2020-11-01", "2021-01-01"); - - // assert - expect(response).toEqual(fakeExpectedGetAverageResponse); - expect(getDuesSpy).toBeCalledTimes(1); - expect(getDuesSpy).toBeCalledWith(yearMonthList, undefined, undefined, undefined, undefined); - }); - - it("IS1.1.3 - should respond with error thrown by getDues", async () => { - // arrange - getDuesSpy = jest.spyOn(InvoiceService, 'getDues') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({ message: "getDues failed" }); - })); - - // act and assert - await expect(InvoiceService.getAverages("2020-11-01", "2021-01-01")).rejects - .toEqual({ message: "getDues failed" }); - }); - - it("IS1.1.4 - should respond with error thrown by getBilled", async () => { - // arrange - getDuesSpy = jest.spyOn(InvoiceService, 'getDues') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeDuesList); - })); - - getBilledSpy = jest.spyOn(InvoiceService, 'getBilled') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({ message: "getBilled failed" }); - })); - - // act and assert - await expect(InvoiceService.getAverages("2020-11-01", "2021-01-01")).rejects - .toEqual({ message: "getBilled failed" }); - }); - - it("IS1.1.5 - should respond with error message 400 when start date is after end date", async () => { - // arrange - let startDate = "2020-11-01"; - let endDate = "2019-11-01"; - let expectedError = { - status: 400, - message: "Invalid date order." - } - - // act and assert - await expect(InvoiceService.getAverages(startDate, endDate)).rejects - .toEqual(expectedError); - }); - - it("IS1.1.6 - should respond with specified error thrown by getClientInformation", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - getDuesSpy = jest.spyOn(InvoiceService, 'getDues') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeDuesList); - })); - - getBilledSpy = jest.spyOn(InvoiceService, 'getBilled') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeBilledList); - })); - getClientSpy = jest.spyOn(InvoiceService, 'getClientInformation') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - - // act and assert - await expect(InvoiceService.getAverages("2020-11-01", "2021-01-01")).rejects - .toEqual(expectedResponse); - - }); - - it("IS1.1.7 - should respond with unspecified error thrown by getClientInformation", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not fetch clients." - }; - getDuesSpy = jest.spyOn(InvoiceService, 'getDues') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeDuesList); - })); - - getBilledSpy = jest.spyOn(InvoiceService, 'getBilled') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeBilledList); - })); - getClientSpy = jest.spyOn(InvoiceService, 'getClientInformation') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - - // act and assert - await expect(InvoiceService.getAverages("2020-11-01", "2021-01-01")).rejects - .toEqual(expectedResponse); - - }); - }); - - describe("IS1.2 - given an empty clientIdList is passed to getCLientInformation", () => { - it("IS1.2.1 - should call getClientInformation with [-1000]", async () => { - // arrange - getDuesSpy = jest.spyOn(InvoiceService, 'getDues') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeDuesList); - })); - getClientSpy = jest.spyOn(InvoiceService, 'getClientInformation') - .mockImplementation(() => new Promise((resolve) => { - resolve("expectedResponse"); - })); - getBilledSpy = jest.spyOn(InvoiceService, 'getBilled') - .mockImplementation(() => new Promise((resolve) => { - let returnObject = fakeBilledList; - returnObject.clientIDList = []; - resolve(returnObject); - })); - - // act and assert - await InvoiceService.getAverages("2020-11-01", "2021-01-01"); - - expect(getClientInformationSpy).toHaveBeenCalledWith([-1000]); - }); - }); - }); - - describe("IS2 - getDues", () => { - describe("IS2.1 - given a valid yearMonthList and nothing else", () => { - it("IS2.1.1 - should return totalDuesList", async () => { - // arrange - getDuesSpy.mockRestore(); - let expectedList = [ - { - month: "202011", - totalDues: "190.00" - }, - { - month: "202012", - totalDues: "190.00" - }, - { - month: "202101", - totalDues: "190.00" - } - ]; - - // act - const response = await InvoiceService.getDues(yearMonthList); - - // assert - expect(response).toEqual(expectedList); - expect(transacStatDaoSpy).toHaveBeenCalledTimes(1); - expect(transacStatDaoSpy).toHaveBeenCalledWith(yearMonthList, undefined, undefined, undefined, undefined); - }); - - it("IS2.1.2 - when dao throws unspecified error status and message, should reject with default status and message ", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not get dues." - }; - transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(InvoiceService.getDues(yearMonthList)).rejects - .toEqual(expectedResponse); - }); - - it("IS2.1.3 - should resolve with false when dao resolves false", async () => { - // arrange - transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(false); - })); - - // act and assert - await expect(InvoiceService.getDues(yearMonthList)).resolves - .toEqual(false); - }); - }); - - describe("IS2.2 - given a valid yearMonthList and employeeId", () => { - it("IS2.2.1 - should return totalDuesList", async () => { - // arrange - let employeeId = 22769; - let expectedList = [ - { - month: "202011", - totalDues: "190.00" - }, - { - month: "202012", - totalDues: "190.00" - }, - { - month: "202101", - totalDues: "190.00" - } - ]; - - transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(fakeTransacStatList); - })); - - // act - const response = await InvoiceService.getDues(yearMonthList, employeeId); - - // assert - expect(response).toEqual(expectedList); - expect(transacStatDaoSpy).toHaveBeenCalledWith(yearMonthList, employeeId, undefined, undefined, undefined); - }); - }); - - describe("IS2.3 - given a valid yearMonthList and countryCode", () => { - it("IS2.3.1 - should return totalDuesList", async () => { - // arrange - let countryName = "Canada"; - let expectedList = [ - { - month: "202011", - totalDues: "190.00" - }, - { - month: "202012", - totalDues: "190.00" - }, - { - month: "202101", - totalDues: "190.00" - } - ]; - - transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(fakeTransacStatList); - })); - - // act - const response = await InvoiceService.getDues(yearMonthList, undefined, undefined, countryName); - - // assert - expect(response).toEqual(expectedList); - expect(transacStatDaoSpy).toHaveBeenCalledWith(yearMonthList, undefined, undefined, countryName, undefined); - }); - }); - - - describe("IS2.4 - given a valid yearMonthList, employeeId and country", () => { - it("IS2.4.1 - should return totalDuesList", async () => { - // arrange - let employeeId = 22769; - let countryName = "Canada"; - let expectedList = [ - { - month: "202011", - totalDues: "190.00" - }, - { - month: "202012", - totalDues: "190.00" - }, - { - month: "202101", - totalDues: "190.00" - } - ]; - - transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(fakeTransacStatList); - })); - - // act - const response = await InvoiceService.getDues(yearMonthList, employeeId, undefined, countryName); - - // assert - expect(response).toEqual(expectedList); - expect(transacStatDaoSpy).toHaveBeenCalledWith(yearMonthList, employeeId, undefined, countryName, undefined); - }); - }); - }); - - describe("IS3 - getBilled", () => { - describe("IS3.1 - given a valid start and end date str and yearMonthList", () => { - let startDateStr = '2019-11-01'; - let endDateStr = '2021-01-01'; - - it("IS3.1.1 - should return billed", async () => { - // arrange - getBilledSpy.mockRestore(); - let expectedResponse = { - billedList: [ - { - month: 202011, - billed: 50 - }, - { - month: 202012, - billed: 100 - }, - { - month: 202101, - billed: 150 - } - ], - clientIDList: [22222, 33333, 44444] - }; - - // act - const response = await InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList); - - // assert - expect(response).toEqual(expectedResponse); - expect(invoiceAffectDaoSpy).toHaveBeenCalledWith(startDateStr, endDateStr, undefined, undefined, undefined, undefined); - }); - - it("IS3.1.2 - when dao throws error with specified status and message, should reject with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act and assert - await expect(InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList)).rejects - .toEqual(expectedResponse); - }); - - it("IS3.1.3 - when dao throws error with unspecified status and message, should reject with default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not fetch bills." - }; - invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList)).rejects - .toEqual(expectedResponse); - }); - - it("IS3.1.4 - should resolve with false when dao resolves with false", async () => { - // arrange - invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList)).resolves - .toEqual(false); - }); - }); - - describe("IS3.2 - given a valid start and end date str, yearMonthList and employeeId", () => { - let startDateStr = '2019-11-01'; - let endDateStr = '2021-01-01'; - let employeeId = 12345; - - it("IS3.2.1 - should return billed", async () => { - // arrange - - let expectedResponse = { - billedList: [ - { - month: 202011, - billed: 50 - }, - { - month: 202012, - billed: 100 - }, - { - month: 202101, - billed: 150 - } - ], - clientIDList: [22222, 33333, 44444] - }; - - invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(fakeInvoiceAffectList) - })); - - // act - const response = await InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList, employeeId); - - // assert - expect(response).toEqual(expectedResponse); - expect(invoiceAffectDaoSpy).toHaveBeenCalledTimes(1); - expect(invoiceAffectDaoSpy).toHaveBeenCalledWith(startDateStr, endDateStr, employeeId, undefined, undefined, undefined); - }); - }); - describe("IS3.3 - given a valid start and end date str, yearMonthList and country", () => { - let startDateStr = '2019-11-01'; - let endDateStr = '2021-01-01'; - let countryCode = "CA"; - - it("IS3.3.1 - should return billed", async () => { - - // arrange - let expectedResponse = { - billedList: [ - { - month: 202011, - billed: 50 - }, - { - month: 202012, - billed: 100 - }, - { - month: 202101, - billed: 150 - } - ], - clientIDList: [22222, 33333, 44444] - }; - - invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(fakeInvoiceAffectList) - })); - - // act - const response = await InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList, undefined, undefined, countryCode); - - // assert - expect(response).toEqual(expectedResponse); - expect(invoiceAffectDaoSpy).toHaveBeenCalledTimes(1); - expect(invoiceAffectDaoSpy).toHaveBeenCalledWith(startDateStr, endDateStr, undefined, undefined, countryCode, undefined); - }); - }); - - - describe("IS3.4 - given a valid start and end date str, yearMonthList, clientList and country", () => { - let startDateStr = '2019-11-01'; - let endDateStr = '2021-01-01'; - let employeeId = 12345; - let countryCode = "CA"; - - it("IS3.4.1 - should return billed", async () => { - - // arrange - let expectedResponse = { - billedList: [ - { - month: 202011, - billed: 50 - }, - { - month: 202012, - billed: 100 - }, - { - month: 202101, - billed: 150 - } - ], - clientIDList: [22222, 33333, 44444] - }; - - invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(fakeInvoiceAffectList) - })); - - // act - const response = await InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList, employeeId, undefined, countryCode); - - // assert - expect(response).toEqual(expectedResponse); - expect(invoiceAffectDaoSpy).toHaveBeenCalledTimes(1); - expect(invoiceAffectDaoSpy).toHaveBeenCalledWith(startDateStr, endDateStr, employeeId, undefined, countryCode, undefined); - }); - }); - }); - - describe("IS4 - getClientInformation", () => { - describe("IS4.1 - given a valid response from the dao", () => { - it("IS4.1.1 - should resolve list of client information", async () => { - // arrange - getClientInformationSpy.mockRestore(); - let expectedResponse = fakeGetClientInformationDaoResponse; - - // act and assert - const response = await InvoiceService.getClientInformation(clientIDList); - - expect(response).toEqual(expectedResponse); - expect(getClientsInClientIdListDaoSpy).toHaveBeenCalledWith(clientIDList); - }) - }); - - describe("IS4.2 - given a invalid response from the dao", () => { - it("IS4.2.1 - when dao resolves false should resolve false", async () => { - // arrange - getClientsInClientIdListDaoSpy = jest.spyOn(ClientDao, 'getClientsInClientIdList') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - const response = await InvoiceService.getClientInformation(clientIDList); - - expect(response).toEqual(false); - expect(getClientsInClientIdListDaoSpy).toHaveBeenCalledWith(clientIDList); - }); - - it("IS4.2.2 - when dao rejects specified status and message, should reject specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - getClientsInClientIdListDaoSpy = jest.spyOn(ClientDao, 'getClientsInClientIdList') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act and assert - await expect(InvoiceService.getClientInformation(clientIDList)) - .rejects.toEqual(expectedResponse); - expect(getClientsInClientIdListDaoSpy).toHaveBeenCalledWith(clientIDList); - }); - - it("IS4.2.3 - when dao rejects unspecified status and message, should reject default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not fetch clients." - }; - getClientsInClientIdListDaoSpy = jest.spyOn(ClientDao, 'getClientsInClientIdList') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(InvoiceService.getClientInformation(clientIDList)) - .rejects.toEqual(expectedResponse); - expect(getClientsInClientIdListDaoSpy).toHaveBeenCalledWith(clientIDList); - }); - }); - }); - - describe("IS5 - getCountriesName", () => { - describe("IS7.1 - given I enter the application to get the list of countries", () => { - it("IS7.1.1 - should return a list of country in alphabetical order", async () => { - - // act - const response = await InvoiceService.getCountriesName(); - - // assert - expect(response).toEqual(fakeCountriesList); - expect(countryDaoSpy).toHaveBeenCalledTimes(1); - expect(countryDaoSpy).toHaveBeenCalledWith(); - }); - - it("IS7.1.2 - should reject with error when dao throws error", async () => { - - // arrange - countryDaoSpy = jest.spyOn(CountryDao, 'getAllCountries') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({ message: "dao failed" }); - })); - - // act and assert - await expect(InvoiceService.getCountriesName()).rejects - .toEqual({ message: "dao failed" }); - }); - - it("IS7.1.3 - should resolve with false when dao resolves with false", async () => { - - // arrange - countryDaoSpy = jest.spyOn(CountryDao, 'getAllCountries') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(InvoiceService.getCountriesName()).resolves - .toEqual(false); - }); - }); - }); -}); \ No newline at end of file + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeGetClientInformationDaoResponse) + })) + +describe('Test Invoice Service', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + afterEach(() => { + sandbox.restore() + }) + + afterAll(() => { + process.exit + }) + + describe('IS1 - getAverages', () => { + describe('IS1.1 - given two dates', () => { + it('IS1.1.1 - should respond with the list of averages per month with group key', async () => { + // arrange + getClientInformationSpy = jest.spyOn(InvoiceService, 'getClientInformation') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeGetClientInformationDaoResponse) + })) + + // act + const response = await InvoiceService.getAverages('2020-11-01', '2021-01-01') + + // assert + expect(response).toEqual(fakeExpectedGetAverageResponse) + expect(getDuesSpy).toBeCalledTimes(1) + expect(getDuesSpy).toBeCalledWith(yearMonthList, undefined, undefined, undefined, undefined) + }) + + it('IS1.1.3 - should respond with error thrown by getDues', async () => { + // arrange + getDuesSpy = jest.spyOn(InvoiceService, 'getDues') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({ message: 'getDues failed' }) + })) + + // act and assert + await expect(InvoiceService.getAverages('2020-11-01', '2021-01-01')).rejects + .toEqual({ message: 'getDues failed' }) + }) + + it('IS1.1.4 - should respond with error thrown by getBilled', async () => { + // arrange + getDuesSpy = jest.spyOn(InvoiceService, 'getDues') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeDuesList) + })) + + getBilledSpy = jest.spyOn(InvoiceService, 'getBilled') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({ message: 'getBilled failed' }) + })) + + // act and assert + await expect(InvoiceService.getAverages('2020-11-01', '2021-01-01')).rejects + .toEqual({ message: 'getBilled failed' }) + }) + + it('IS1.1.5 - should respond with error message 400 when start date is after end date', async () => { + // arrange + const startDate = '2020-11-01' + const endDate = '2019-11-01' + const expectedError = { + status: 400, + message: 'Invalid date order.' + } + + // act and assert + await expect(InvoiceService.getAverages(startDate, endDate)).rejects + .toEqual(expectedError) + }) + + it('IS1.1.6 - should respond with specified error thrown by getClientInformation', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + getDuesSpy = jest.spyOn(InvoiceService, 'getDues') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeDuesList) + })) + + getBilledSpy = jest.spyOn(InvoiceService, 'getBilled') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeBilledList) + })) + getClientInformationSpy = jest.spyOn(InvoiceService, 'getClientInformation') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(InvoiceService.getAverages('2020-11-01', '2021-01-01')).rejects + .toEqual(expectedResponse) + }) + + it('IS1.1.7 - should respond with unspecified error thrown by getClientInformation', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not fetch clients.' + } + getDuesSpy = jest.spyOn(InvoiceService, 'getDues') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeDuesList) + })) + + getBilledSpy = jest.spyOn(InvoiceService, 'getBilled') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeBilledList) + })) + getClientInformationSpy = jest.spyOn(InvoiceService, 'getClientInformation') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(InvoiceService.getAverages('2020-11-01', '2021-01-01')).rejects + .toEqual(expectedResponse) + }) + }) + + describe('IS1.2 - given an empty clientIdList is passed to getCLientInformation', () => { + it('IS1.2.1 - should call getClientInformation with [-1000]', async () => { + // arrange + getDuesSpy = jest.spyOn(InvoiceService, 'getDues') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeDuesList) + })) + getClientInformationSpy = jest.spyOn(InvoiceService, 'getClientInformation') + .mockImplementation(() => new Promise((resolve) => { + resolve('expectedResponse') + })) + getBilledSpy = jest.spyOn(InvoiceService, 'getBilled') + .mockImplementation(() => new Promise((resolve) => { + const returnObject = fakeBilledList + returnObject.clientIDList = [] + resolve(returnObject) + })) + + // act and assert + await InvoiceService.getAverages('2020-11-01', '2021-01-01') + + expect(getClientInformationSpy).toHaveBeenCalledWith([-1000]) + }) + }) + }) + + describe('IS2 - getDues', () => { + describe('IS2.1 - given a valid yearMonthList and nothing else', () => { + it('IS2.1.1 - should return totalDuesList', async () => { + // arrange + getDuesSpy.mockRestore() + const expectedList = [ + { + month: '202011', + totalDues: '190.00' + }, + { + month: '202012', + totalDues: '190.00' + }, + { + month: '202101', + totalDues: '190.00' + } + ] + + // act + const response = await InvoiceService.getDues(yearMonthList) + + // assert + expect(response).toEqual(expectedList) + expect(transacStatDaoSpy).toHaveBeenCalledTimes(1) + expect(transacStatDaoSpy).toHaveBeenCalledWith(yearMonthList, undefined, undefined, undefined, undefined) + }) + + it('IS2.1.2 - when dao throws unspecified error status and message, should reject with default status and message ', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not get dues.' + } + transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(InvoiceService.getDues(yearMonthList)).rejects + .toEqual(expectedResponse) + }) + + it('IS2.1.3 - should resolve with false when dao resolves false', async () => { + // arrange + transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(false) + })) + + // act and assert + await expect(InvoiceService.getDues(yearMonthList)).resolves + .toEqual(false) + }) + }) + + describe('IS2.2 - given a valid yearMonthList and employeeId', () => { + it('IS2.2.1 - should return totalDuesList', async () => { + // arrange + const employeeId = 22769 + const expectedList = [ + { + month: '202011', + totalDues: '190.00' + }, + { + month: '202012', + totalDues: '190.00' + }, + { + month: '202101', + totalDues: '190.00' + } + ] + + transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(fakeTransacStatList) + })) + + // act + const response = await InvoiceService.getDues(yearMonthList, employeeId) + + // assert + expect(response).toEqual(expectedList) + expect(transacStatDaoSpy).toHaveBeenCalledWith(yearMonthList, employeeId, undefined, undefined, undefined) + }) + }) + + describe('IS2.3 - given a valid yearMonthList and countryCode', () => { + it('IS2.3.1 - should return totalDuesList', async () => { + // arrange + const countryName = 'Canada' + const expectedList = [ + { + month: '202011', + totalDues: '190.00' + }, + { + month: '202012', + totalDues: '190.00' + }, + { + month: '202101', + totalDues: '190.00' + } + ] + + transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(fakeTransacStatList) + })) + + // act + const response = await InvoiceService.getDues(yearMonthList, undefined, undefined, countryName) + + // assert + expect(response).toEqual(expectedList) + expect(transacStatDaoSpy).toHaveBeenCalledWith(yearMonthList, undefined, undefined, countryName, undefined) + }) + }) + + describe('IS2.4 - given a valid yearMonthList, employeeId and country', () => { + it('IS2.4.1 - should return totalDuesList', async () => { + // arrange + const employeeId = 22769 + const countryName = 'Canada' + const expectedList = [ + { + month: '202011', + totalDues: '190.00' + }, + { + month: '202012', + totalDues: '190.00' + }, + { + month: '202101', + totalDues: '190.00' + } + ] + + transacStatDaoSpy = jest.spyOn(TransacStatDao, 'getTransactionsStatByYearMonth') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(fakeTransacStatList) + })) + + // act + const response = await InvoiceService.getDues(yearMonthList, employeeId, undefined, countryName) + + // assert + expect(response).toEqual(expectedList) + expect(transacStatDaoSpy).toHaveBeenCalledWith(yearMonthList, employeeId, undefined, countryName, undefined) + }) + }) + }) + + describe('IS3 - getBilled', () => { + describe('IS3.1 - given a valid start and end date str and yearMonthList', () => { + const startDateStr = '2019-11-01' + const endDateStr = '2021-01-01' + + it('IS3.1.1 - should return billed', async () => { + // arrange + getBilledSpy.mockRestore() + const expectedResponse = { + billedList: [ + { + month: 202011, + billed: 50 + }, + { + month: 202012, + billed: 100 + }, + { + month: 202101, + billed: 150 + } + ], + clientIDList: [22222, 33333, 44444] + } + + // act + const response = await InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList) + + // assert + expect(response).toEqual(expectedResponse) + expect(invoiceAffectDaoSpy).toHaveBeenCalledWith(startDateStr, endDateStr, undefined, undefined, undefined, undefined) + }) + + it('IS3.1.2 - when dao throws error with specified status and message, should reject with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList)).rejects + .toEqual(expectedResponse) + }) + + it('IS3.1.3 - when dao throws error with unspecified status and message, should reject with default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not fetch bills.' + } + invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList)).rejects + .toEqual(expectedResponse) + }) + + it('IS3.1.4 - should resolve with false when dao resolves with false', async () => { + // arrange + invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList)).resolves + .toEqual(false) + }) + }) + + describe('IS3.2 - given a valid start and end date str, yearMonthList and employeeId', () => { + const startDateStr = '2019-11-01' + const endDateStr = '2021-01-01' + const employeeId = 12345 + + it('IS3.2.1 - should return billed', async () => { + // arrange + + const expectedResponse = { + billedList: [ + { + month: 202011, + billed: 50 + }, + { + month: 202012, + billed: 100 + }, + { + month: 202101, + billed: 150 + } + ], + clientIDList: [22222, 33333, 44444] + } + + invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(fakeInvoiceAffectList) + })) + + // act + const response = await InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList, employeeId) + + // assert + expect(response).toEqual(expectedResponse) + expect(invoiceAffectDaoSpy).toHaveBeenCalledTimes(1) + expect(invoiceAffectDaoSpy).toHaveBeenCalledWith(startDateStr, endDateStr, employeeId, undefined, undefined, undefined) + }) + }) + describe('IS3.3 - given a valid start and end date str, yearMonthList and country', () => { + const startDateStr = '2019-11-01' + const endDateStr = '2021-01-01' + const countryCode = 'CA' + + it('IS3.3.1 - should return billed', async () => { + // arrange + const expectedResponse = { + billedList: [ + { + month: 202011, + billed: 50 + }, + { + month: 202012, + billed: 100 + }, + { + month: 202101, + billed: 150 + } + ], + clientIDList: [22222, 33333, 44444] + } + + invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(fakeInvoiceAffectList) + })) + + // act + const response = await InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList, undefined, undefined, countryCode) + + // assert + expect(response).toEqual(expectedResponse) + expect(invoiceAffectDaoSpy).toHaveBeenCalledTimes(1) + expect(invoiceAffectDaoSpy).toHaveBeenCalledWith(startDateStr, endDateStr, undefined, undefined, countryCode, undefined) + }) + }) + + describe('IS3.4 - given a valid start and end date str, yearMonthList, clientList and country', () => { + const startDateStr = '2019-11-01' + const endDateStr = '2021-01-01' + const employeeId = 12345 + const countryCode = 'CA' + + it('IS3.4.1 - should return billed', async () => { + // arrange + const expectedResponse = { + billedList: [ + { + month: 202011, + billed: 50 + }, + { + month: 202012, + billed: 100 + }, + { + month: 202101, + billed: 150 + } + ], + clientIDList: [22222, 33333, 44444] + } + + invoiceAffectDaoSpy = jest.spyOn(InvoiceAffectDao, 'getInvoicesByDate') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(fakeInvoiceAffectList) + })) + + // act + const response = await InvoiceService.getBilled(startDateStr, endDateStr, yearMonthList, employeeId, undefined, countryCode) + + // assert + expect(response).toEqual(expectedResponse) + expect(invoiceAffectDaoSpy).toHaveBeenCalledTimes(1) + expect(invoiceAffectDaoSpy).toHaveBeenCalledWith(startDateStr, endDateStr, employeeId, undefined, countryCode, undefined) + }) + }) + }) + + describe('IS4 - getClientInformation', () => { + describe('IS4.1 - given a valid response from the dao', () => { + it('IS4.1.1 - should resolve list of client information', async () => { + // arrange + getClientInformationSpy.mockRestore() + const expectedResponse = fakeGetClientInformationDaoResponse + + // act and assert + const response = await InvoiceService.getClientInformation(clientIDList) + + expect(response).toEqual(expectedResponse) + expect(getClientsInClientIdListDaoSpy).toHaveBeenCalledWith(clientIDList) + }) + }) + + describe('IS4.2 - given a invalid response from the dao', () => { + it('IS4.2.1 - when dao resolves false should resolve false', async () => { + // arrange + getClientsInClientIdListDaoSpy = jest.spyOn(ClientDao, 'getClientsInClientIdList') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + const response = await InvoiceService.getClientInformation(clientIDList) + + expect(response).toEqual(false) + expect(getClientsInClientIdListDaoSpy).toHaveBeenCalledWith(clientIDList) + }) + + it('IS4.2.2 - when dao rejects specified status and message, should reject specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + getClientsInClientIdListDaoSpy = jest.spyOn(ClientDao, 'getClientsInClientIdList') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(InvoiceService.getClientInformation(clientIDList)) + .rejects.toEqual(expectedResponse) + expect(getClientsInClientIdListDaoSpy).toHaveBeenCalledWith(clientIDList) + }) + + it('IS4.2.3 - when dao rejects unspecified status and message, should reject default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not fetch clients.' + } + getClientsInClientIdListDaoSpy = jest.spyOn(ClientDao, 'getClientsInClientIdList') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(InvoiceService.getClientInformation(clientIDList)) + .rejects.toEqual(expectedResponse) + expect(getClientsInClientIdListDaoSpy).toHaveBeenCalledWith(clientIDList) + }) + }) + }) + + describe('IS5 - getCountriesName', () => { + describe('IS7.1 - given I enter the application to get the list of countries', () => { + it('IS7.1.1 - should return a list of country in alphabetical order', async () => { + // act + const response = await InvoiceService.getCountriesName() + + // assert + expect(response).toEqual(fakeCountriesList) + expect(countryDaoSpy).toHaveBeenCalledTimes(1) + expect(countryDaoSpy).toHaveBeenCalledWith() + }) + + it('IS7.1.2 - should reject with error when dao throws error', async () => { + // arrange + countryDaoSpy = jest.spyOn(CountryDao, 'getAllCountries') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({ message: 'dao failed' }) + })) + + // act and assert + await expect(InvoiceService.getCountriesName()).rejects + .toEqual({ message: 'dao failed' }) + }) + + it('IS7.1.3 - should resolve with false when dao resolves with false', async () => { + // arrange + countryDaoSpy = jest.spyOn(CountryDao, 'getAllCountries') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(InvoiceService.getCountriesName()).resolves + .toEqual(false) + }) + }) + }) +}) diff --git a/server/tests/services/report.service.test.js b/server/tests/services/report.service.test.js index 3f74a78..e8097ea 100644 --- a/server/tests/services/report.service.test.js +++ b/server/tests/services/report.service.test.js @@ -1,1465 +1,1449 @@ -var { expect, jest } = require('@jest/globals'); -var fs = require('fs'); -require("../../../config.js"); +const { expect } = require('@jest/globals') +const fs = require('fs') -const ReportService = require("../../services/report.service"); -const ChartReportDao = require("../../data_access_layer/daos/chart_report.dao"); -const ReportDao = require("../../data_access_layer/daos/report.dao"); +const ReportService = require('../../services/report.service') +const ChartReportDao = require('../../data_access_layer/daos/chart_report.dao') +const ReportDao = require('../../data_access_layer/daos/report.dao') jest.setTimeout(10000) -describe("Test Report Service", () => { +describe('Test Report Service', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + afterAll(() => { + process.exit + }) + + // Tests for getting saved Chart Reports of a specific user + const expectedChartReports = [ + { + name: 'CR1', + startDate: '2019-12-01', + endDate: '2020-12-01', + employee1Id: 12345, + employee1Name: 'France Cote', + country: 'Canada', + clientType: 'Corr', + ageOfAccount: 'All', + accountType: 'Receivable', + user_user_id: 'fakeUserId' + }, + { + name: 'CR2', + startDate: '2020-12-01', + endDate: '2021-12-01', + employee1Id: -1, + employee1Name: 'All', + employee2Id: 12345, + employee2Name: 'France Cote', + country: 'All', + clientType: 'Direct', + ageOfAccount: '60-90', + accountType: 'Receivable', + user_user_id: 'fakeUserId' + } + ] + + let chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportsByUserId') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedChartReports) + })) + + describe('RS1 - getChartReportsByUserId', () => { + describe('RS1.1 - given a userId', () => { + it('RS1.1.1 - should return list of chartReports', async () => { + // arrange + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportsByUserId') + .mockImplementation(() => new Promise((resolve) => { + resolve(expectedChartReports) + })) + + // act and assert + await expect(ReportService.getChartReportsByUserId('someUserId')).resolves + .toEqual(expectedChartReports) + }) + + it('RS1.1.2 - should resolve false when dao returns false', async () => { + // arrange + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportsByUserId') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.getChartReportsByUserId('someUserId')).resolves + .toEqual(false) + }) + + it('RS1.1.3 - should reject with dao error status and message when dao throws error', async () => { + // arrange + const expectedError = { + status: 404, + message: 'Error message.' + } + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportsByUserId') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedError) + })) + + // act and assert + await expect(ReportService.getChartReportsByUserId('someUserId')).rejects + .toEqual(expectedError) + }) + + it("RS1.1.4 - should reject with status 500 and message when dao error doesn't specify", async () => { + // arrange + const expectedError = { + status: 500, + message: 'Could not fetch data.' + } + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportsByUserId') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.getChartReportsByUserId('someUserId')).rejects + .toEqual(expectedError) + }) + }) - beforeEach(() => { - jest.clearAllMocks(); - }); + describe('RS1.2 - given no userId', () => { + it('RS1.2.1 - should reject with 500 status and message', async () => { + // arrange + const expectedError = { + status: 500, + message: 'Could not fetch data.' + } - afterEach(() => { - jest.clearAllMocks(); + // act and assert + await expect(ReportService.getChartReportsByUserId()).rejects + .toEqual(expectedError) + }) + }) + }) + + // Tests for creating a Chart Report with its associated Char Report Data + const fakeChartReportRequest = { + chartReport: { + name: 'CRname', + startDate: new Date(2019, 1, 1).toISOString(), + endDate: new Date(2019, 11, 1).toISOString(), + employee1Id: 12345, + employee1Name: 'Emp1', + employee2Id: -1, + employee2Name: 'All', + countryId: 'CA', + country: 'Canada', + clientType: 'Corr', + ageOfAccount: 'All', + accountType: 'Receivable', + user_user_id: 'fakeUUID' + }, + chartReportData: [ + { + label: '2019 - emp', + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + }, + { + label: '2019', + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + } + ] + } + + const fakeCreateChartReportDaoResponse = { + chartReportId: 'fakeUUID', + name: 'fakeName', + emp1Id: 12345, + emp2Id: -1 + } + + const fakeCreateDataForChartReportDaoResponse = [ + { + chartReportDataId: 'fakeUUID', + year: 2019, + employee: 12345 + }, + { + chartReportDataId: 'fakeUUID', + year: 2019, + employee: -1 + } + ] + + let createChartReportSpy = jest.spyOn(ReportService, 'createChartReport') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(fakeCreateChartReportDaoResponse) + })) + + let createChartReportDataSpy = jest.spyOn(ReportService, 'createChartReportData') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(fakeCreateDataForChartReportDaoResponse) + })) + let verifyChartReportSpy = jest.spyOn(ReportService, 'verifyChartReport') + .mockImplementation(() => { + return true }) - afterAll(() => { - process.exit; - }); - - // Tests for getting saved Chart Reports of a specific user - let expectedChartReports = [ - { - name: 'CR1', - startDate: '2019-12-01', - endDate: '2020-12-01', - employee1Id: 12345, - employee1Name: 'France Cote', - country: 'Canada', - clientType: 'Corr', - ageOfAccount: 'All', - accountType: 'Receivable', - user_user_id: 'fakeUserId' - }, - { - name: 'CR2', - startDate: '2020-12-01', - endDate: '2021-12-01', - employee1Id: -1, - employee1Name: 'All', - employee2Id: 12345, - employee2Name: 'France Cote', - country: 'All', - clientType: 'Direct', - ageOfAccount: '60-90', - accountType: 'Receivable', - user_user_id: 'fakeUserId' + describe('RS2 - createChartReportForUser', () => { + describe('RS2.1 - given valid criteria', () => { + it('RS2.1.1 - when valid response from functions, should call functions with prepared data and respond with functions response', async () => { + // arrange + const expectedResponse = { + createdChartReport: fakeCreateChartReportDaoResponse, + createdChartReportData: fakeCreateDataForChartReportDaoResponse } - ]; - - let chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportsByUserId') - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedChartReports); - })); - - describe("RS1 - getChartReportsByUserId", () => { - describe("RS1.1 - given a userId", () => { - it("RS1.1.1 - should return list of chartReports", async () => { - // arrange - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportsByUserId') - .mockImplementation(() => new Promise((resolve) => { - resolve(expectedChartReports); - })); - - // act and assert - await expect(ReportService.getChartReportsByUserId("someUserId")).resolves - .toEqual(expectedChartReports); - }); - - it("RS1.1.2 - should resolve false when dao returns false", async () => { - // arrange - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportsByUserId') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.getChartReportsByUserId("someUserId")).resolves - .toEqual(false); - }); - - it("RS1.1.3 - should reject with dao error status and message when dao throws error", async () => { - // arrange - let expectedError = { - status: 404, - message: "Error message." - }; - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportsByUserId') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedError); - })); - - // act and assert - await expect(ReportService.getChartReportsByUserId("someUserId")).rejects - .toEqual(expectedError); - }); - - it("RS1.1.4 - should reject with status 500 and message when dao error doesn't specify", async () => { - // arrange - let expectedError = { - status: 500, - message: "Could not fetch data." - }; - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportsByUserId') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(ReportService.getChartReportsByUserId("someUserId")).rejects - .toEqual(expectedError); - }); - }); - - describe("RS1.2 - given no userId", () => { - it("RS1.2.1 - should reject with 500 status and message", async () => { - // arrange - let expectedError = { - status: 500, - message: "Could not fetch data." - }; - - // act and assert - await expect(ReportService.getChartReportsByUserId()).rejects - .toEqual(expectedError); - }); - }); - }); - - - // Tests for creating a Chart Report with its associated Char Report Data - let fakeChartReportRequest = { - chartReport: { - name: "CRname", - startDate: new Date(2019, 1, 1).toISOString(), - endDate: new Date(2019, 11, 1).toISOString(), - employee1Id: 12345, - employee1Name: "Emp1", - employee2Id: -1, - employee2Name: "All", - countryId: "CA", - country: "Canada", - clientType: "Corr", - ageOfAccount: "All", - accountType: "Receivable", - user_user_id: "fakeUUID" - }, - chartReportData: [ - { - label: "2019 - emp", - data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - }, - { - label: "2019", - data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - } + + // act + const response = await ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, + fakeChartReportRequest.chartReportData, + 'fakeUUID') + + // assert + expect(response).toEqual(expectedResponse) + expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport) + expect(createChartReportSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + expect(createChartReportDataSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData) + }) + + it('RS2.1.2 - when createChartReport throws error with specified status and message, should reject specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + + createChartReportSpy = jest.spyOn(ReportService, 'createChartReport') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, + fakeChartReportRequest.chartReportData, + 'fakeUUID')).rejects.toEqual(expectedResponse) + expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport) + expect(createChartReportSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + expect(createChartReportDataSpy).toHaveBeenCalledTimes(0) + }) + + it('RS2.1.3 - when createChartReport throws error with unspecified status and message, should reject default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + createChartReportSpy = jest.spyOn(ReportService, 'createChartReport') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, + fakeChartReportRequest.chartReportData, + 'fakeUUID')).rejects.toEqual(expectedResponse) + expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport) + expect(createChartReportSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + expect(createChartReportDataSpy).toHaveBeenCalledTimes(0) + }) + + it('RS2.1.4 - when createChartReport resolves false, should resolve false', async () => { + // arrange + createChartReportSpy = jest.spyOn(ReportService, 'createChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, + fakeChartReportRequest.chartReportData, 'fakeUUID')).resolves.toBe(false) + expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport) + expect(createChartReportSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + expect(createChartReportDataSpy).toHaveBeenCalledTimes(0) + }) + + it('RS2.1.5 - when createChartReportData resolves false, should resolve false', async () => { + // arrange + createChartReportSpy = jest.spyOn(ReportService, 'createChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeCreateChartReportDaoResponse) + })) + createChartReportDataSpy = jest.spyOn(ReportService, 'createChartReportData') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, + fakeChartReportRequest.chartReportData, 'fakeUUID')).resolves.toBe(false) + expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport) + expect(createChartReportSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + expect(createChartReportDataSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData) + }) + + it('RS2.1.6 - when createChartReportData rejects error with specified status and message, should reject specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + createChartReportDataSpy = jest.spyOn(ReportService, 'createChartReportData') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, + fakeChartReportRequest.chartReportData, 'fakeUUID')).rejects.toEqual(expectedResponse) + expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport) + expect(createChartReportSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + expect(createChartReportDataSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData) + }) + + it('RS2.1.7 - when createChartReportData rejects error with unspecified status and message, should reject default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + createChartReportDataSpy = jest.spyOn(ReportService, 'createChartReportData') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, + fakeChartReportRequest.chartReportData, 'fakeUUID')).rejects.toEqual(expectedResponse) + expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport) + expect(createChartReportSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + expect(createChartReportDataSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData) + }) + }) + + describe('RS2.2 - given invalid criteria', () => { + it('RS2.2.1 - should reject with status 400 and message', async () => { + // arrange + const expectedResponse = { + status: 400, + message: 'Invalid content.' + } + verifyChartReportSpy = jest.spyOn(ReportService, 'verifyChartReport') + .mockImplementation(() => { + return false + }) + + // act and assert + await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, fakeChartReportRequest.chartReportData)) + .rejects.toEqual(expectedResponse) + }) + }) + }) + + describe('RS3 - createChartReport', () => { + describe('RS3.1 - given valid userId and criteria', () => { + it('RS3.1.1 - when valid response from dao, should resolve response from dao', async () => { + // arrange + createChartReportSpy.mockRestore() + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createChartReportForUser') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeCreateChartReportDaoResponse) + })) + const expectedResponse = fakeCreateChartReportDaoResponse + + // act and assert + const response = await ReportService.createChartReport('fakeUUID', fakeChartReportRequest.chartReport) + + expect(response).toEqual(expectedResponse) + expect(chartReportDaoSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + }) + + it('RS3.1.2 - when dao resolves false, should resolve false', async () => { + // arrange + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createChartReportForUser') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.createChartReport('fakeUUID', fakeChartReportRequest.chartReport)) + .resolves.toBe(false) + expect(chartReportDaoSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + }) + + it('RS3.1.3 - when dao throws error with specified status and message, should reject with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createChartReportForUser') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(ReportService.createChartReport('fakeUUID', fakeChartReportRequest.chartReport)) + .rejects.toEqual(expectedResponse) + expect(chartReportDaoSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + }) + + it('RS3.1.4 - when dao throws error with unspecified status and message, should reject with default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not create Chart Report.' + } + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createChartReportForUser') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.createChartReport('fakeUUID', fakeChartReportRequest.chartReport)) + .rejects.toEqual(expectedResponse) + expect(chartReportDaoSpy).toHaveBeenCalledWith('fakeUUID', fakeChartReportRequest.chartReport) + }) + }) + }) + + describe('RS4 - createChartReportData', () => { + const fakePreparedData = [ + { + chart_report_id: fakeCreateChartReportDaoResponse.chartReportId, + year: 2019, + employee: 12345, + january: 0, + february: 0, + march: 0, + april: 0, + may: 0, + june: 0, + july: 0, + august: 0, + september: 0, + october: 0, + november: 0, + december: 0 + }, + { + chart_report_id: fakeCreateChartReportDaoResponse.chartReportId, + year: 2019, + employee: -1, + january: 0, + february: 0, + march: 0, + april: 0, + may: 0, + june: 0, + july: 0, + august: 0, + september: 0, + october: 0, + november: 0, + december: 0 + } + ] + + describe('RS4.1 - given valid createdChartReport and data', () => { + it('RS4.1.1 - when comparison and valid response from dao, should resolve response from dao', async () => { + // arrange + createChartReportDataSpy.mockRestore() + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeCreateDataForChartReportDaoResponse) + })) + const expectedResponse = fakeCreateDataForChartReportDaoResponse + + // act and assert + await expect(ReportService.createChartReportData(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData)) + .resolves.toEqual(expectedResponse) + expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse.chartReportId, fakePreparedData) + }) + + it('RS4.1.2 - when all employees and valid response from dao, should resolve response from dao', async () => { + // arrange + createChartReportDataSpy.mockRestore() + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeCreateDataForChartReportDaoResponse) + })) + const expectedResponse = fakeCreateDataForChartReportDaoResponse + const fakeCreatedChartReportAllEmployees = { + chartReportId: 'fakeUUID', + name: 'fakeName', + emp1Id: -1, + emp2Id: null + } + const chartReportData = [ + { + label: '2019', + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + } ] - }; - - let fakeCreateChartReportDaoResponse = { - chartReportId: "fakeUUID", - name: "fakeName", - emp1Id: 12345, - emp2Id: -1 - }; - - let fakeCreateDataForChartReportDaoResponse = [ - { - chartReportDataId: "fakeUUID", - year: 2019, - employee: 12345 - }, - { - chartReportDataId: "fakeUUID", - year: 2019, - employee: -1 + const fakePreparedDataAllEmployees = [fakePreparedData[1]] + + // act and assert + await expect(ReportService.createChartReportData(fakeCreatedChartReportAllEmployees, chartReportData)) + .resolves.toEqual(expectedResponse) + expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreatedChartReportAllEmployees.chartReportId, fakePreparedDataAllEmployees) + }) + + it('RS4.1.3 - when one employee and valid response from dao, should resolve response from dao', async () => { + // arrange + createChartReportDataSpy.mockRestore() + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeCreateDataForChartReportDaoResponse) + })) + const expectedResponse = fakeCreateDataForChartReportDaoResponse + const fakeCreatedChartReportOneEmployee = { + chartReportId: 'fakeUUID', + name: 'fakeName', + emp1Id: 12345, + emp2Id: null + } + const chartReportData = [ + { + label: '2019', + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + } + ] + const fakePreparedDataOneEmployee = [fakePreparedData[0]] + + // act and assert + await expect(ReportService.createChartReportData(fakeCreatedChartReportOneEmployee, chartReportData)) + .resolves.toEqual(expectedResponse) + expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreatedChartReportOneEmployee.chartReportId, fakePreparedDataOneEmployee) + }) + + it('RS4.1.4 - when dao resolves false, should resolve false', async () => { + // arrange + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.createChartReportData(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData)) + .resolves.toBe(false) + expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse.chartReportId, fakePreparedData) + }) + + it('RS4.1.5 - when dao rejects with specified status and message, should reject with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(ReportService.createChartReportData(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData)) + .rejects.toEqual(expectedResponse) + expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse.chartReportId, fakePreparedData) + }) + + it('RS4.1.6 - when dao rejects with unspecified status and message, should reject with default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' } - ]; - - let createChartReportSpy = jest.spyOn(ReportService, 'createChartReport') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(fakeCreateChartReportDaoResponse); - })); - - let createChartReportDataSpy = jest.spyOn(ReportService, 'createChartReportData') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(fakeCreateDataForChartReportDaoResponse); - })); - let verifyChartReportSpy = jest.spyOn(ReportService, 'verifyChartReport') - .mockImplementation(() => { - return true; - }); - - describe("RS2 - createChartReportForUser", () => { - describe("RS2.1 - given valid criteria", () => { - it("RS2.1.1 - when valid response from functions, should call functions with prepared data and respond with functions response", async () => { - // arrange - let expectedResponse = { - createdChartReport: fakeCreateChartReportDaoResponse, - createdChartReportData: fakeCreateDataForChartReportDaoResponse - }; - - // act - const response = await ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, - fakeChartReportRequest.chartReportData, - "fakeUUID"); - - // assert - expect(response).toEqual(expectedResponse); - expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport); - expect(createChartReportSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - expect(createChartReportDataSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData); - }); - - it("RS2.1.2 - when createChartReport throws error with specified status and message, should reject specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - - createChartReportSpy = jest.spyOn(ReportService, 'createChartReport') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - - // act and assert - await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, - fakeChartReportRequest.chartReportData, - "fakeUUID")).rejects.toEqual(expectedResponse); - expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport); - expect(createChartReportSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - expect(createChartReportDataSpy).toHaveBeenCalledTimes(0); - }); - - it("RS2.1.3 - when createChartReport throws error with unspecified status and message, should reject default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - createChartReportSpy = jest.spyOn(ReportService, 'createChartReport') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - - // act and assert - await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, - fakeChartReportRequest.chartReportData, - "fakeUUID")).rejects.toEqual(expectedResponse); - expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport); - expect(createChartReportSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - expect(createChartReportDataSpy).toHaveBeenCalledTimes(0); - }); - - it("RS2.1.4 - when createChartReport resolves false, should resolve false", async () => { - // arrange - createChartReportSpy = jest.spyOn(ReportService, 'createChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, - fakeChartReportRequest.chartReportData, "fakeUUID")).resolves.toBe(false); - expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport); - expect(createChartReportSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - expect(createChartReportDataSpy).toHaveBeenCalledTimes(0); - }); - - it("RS2.1.5 - when createChartReportData resolves false, should resolve false", async () => { - // arrange - createChartReportSpy = jest.spyOn(ReportService, 'createChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeCreateChartReportDaoResponse); - })); - createChartReportDataSpy = jest.spyOn(ReportService, 'createChartReportData') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, - fakeChartReportRequest.chartReportData, "fakeUUID")).resolves.toBe(false); - expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport); - expect(createChartReportSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - expect(createChartReportDataSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData); - }); - - it("RS2.1.6 - when createChartReportData rejects error with specified status and message, should reject specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - createChartReportDataSpy = jest.spyOn(ReportService, 'createChartReportData') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act and assert - await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, - fakeChartReportRequest.chartReportData, "fakeUUID")).rejects.toEqual(expectedResponse); - expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport); - expect(createChartReportSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - expect(createChartReportDataSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData); - }); - - it("RS2.1.7 - when createChartReportData rejects error with unspecified status and message, should reject default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - createChartReportDataSpy = jest.spyOn(ReportService, 'createChartReportData') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, - fakeChartReportRequest.chartReportData, "fakeUUID")).rejects.toEqual(expectedResponse); - expect(verifyChartReportSpy).toHaveBeenCalledWith(fakeChartReportRequest.chartReport); - expect(createChartReportSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - expect(createChartReportDataSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData); - }); - }); - - describe("RS2.2 - given invalid criteria", () => { - it("RS2.2.1 - should reject with status 400 and message", async () => { - // arrange - let expectedResponse = { - status: 400, - message: "Invalid content." - }; - verifyChartReportSpy = jest.spyOn(ReportService, 'verifyChartReport') - .mockImplementation(() => { - return false; - }); - - // act and assert - await expect(ReportService.createChartReportForUser(fakeChartReportRequest.chartReport, fakeChartReportRequest.chartReportData)) - .rejects.toEqual(expectedResponse); - }); - }); - }); - - describe("RS3 - createChartReport", () => { - describe("RS3.1 - given valid userId and criteria", () => { - it("RS3.1.1 - when valid response from dao, should resolve response from dao", async () => { - // arrange - createChartReportSpy.mockRestore(); - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createChartReportForUser') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeCreateChartReportDaoResponse); - })); - let expectedResponse = fakeCreateChartReportDaoResponse; - - // act and assert - const response = await ReportService.createChartReport("fakeUUID", fakeChartReportRequest.chartReport); - - expect(response).toEqual(expectedResponse); - expect(chartReportDaoSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - }); - - it("RS3.1.2 - when dao resolves false, should resolve false", async () => { - // arrange - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createChartReportForUser') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.createChartReport("fakeUUID", fakeChartReportRequest.chartReport)) - .resolves.toBe(false); - expect(chartReportDaoSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - }); - - it("RS3.1.3 - when dao throws error with specified status and message, should reject with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createChartReportForUser') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act and assert - await expect(ReportService.createChartReport("fakeUUID", fakeChartReportRequest.chartReport)) - .rejects.toEqual(expectedResponse); - expect(chartReportDaoSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - }); - - it("RS3.1.4 - when dao throws error with unspecified status and message, should reject with default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not create Chart Report." - }; - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createChartReportForUser') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(ReportService.createChartReport("fakeUUID", fakeChartReportRequest.chartReport)) - .rejects.toEqual(expectedResponse); - expect(chartReportDaoSpy).toHaveBeenCalledWith("fakeUUID", fakeChartReportRequest.chartReport); - }); - }) - }); - - describe("RS4 - createChartReportData", () => { - let fakePreparedData = [ - { - chart_report_id: fakeCreateChartReportDaoResponse.chartReportId, - year: 2019, - employee: 12345, - january: 0, - february: 0, - march: 0, - april: 0, - may: 0, - june: 0, - july: 0, - august: 0, - september: 0, - october: 0, - november: 0, - december: 0 - }, - { - chart_report_id: fakeCreateChartReportDaoResponse.chartReportId, - year: 2019, - employee: -1, - january: 0, - february: 0, - march: 0, - april: 0, - may: 0, - june: 0, - july: 0, - august: 0, - september: 0, - october: 0, - november: 0, - december: 0 + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.createChartReportData(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData)) + .rejects.toEqual(expectedResponse) + expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse.chartReportId, fakePreparedData) + }) + }) + }) + + describe('RS5 - verifyChartReport', () => { + describe('RS5.1 - given valid criteria', () => { + it('RS5.1.1 - should return true', () => { + // arrange + verifyChartReportSpy.mockRestore() + + // act and assert + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(true) + }) + }) + + describe('RS5.2 - given invalid criteria', () => { + it('RS5.2.1 - when no name, should return false', () => { + // arrange + const name = fakeChartReportRequest.chartReport.name + delete fakeChartReportRequest.chartReport.name + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.name = name + }) + + it('RS5.2.2 - when no startDate, should return false', () => { + // arrange + const startDate = fakeChartReportRequest.chartReport.startDate + delete fakeChartReportRequest.chartReport.startDate + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.startDate = startDate + }) + + it('RS5.2.3 - when no endDate, should return false', () => { + // arrange + const endDate = fakeChartReportRequest.chartReport.endDate + delete fakeChartReportRequest.chartReport.endDate + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.endDate = endDate + }) + + it('RS5.2.4 - when no employee1Id, should return false', () => { + // arrange + const employee1Id = fakeChartReportRequest.chartReport.employee1Id + delete fakeChartReportRequest.chartReport.employee1Id + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.employee1Id = employee1Id + }) + + it('RS5.2.5 - when no employee1Name, should return false', () => { + // arrange + const employee1Name = fakeChartReportRequest.chartReport.employee1Name + delete fakeChartReportRequest.chartReport.employee1Name + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.employee1Name = employee1Name + }) + + it('RS5.2.6 - when no employee2Id, should return false', () => { + // arrange + const employee2Id = fakeChartReportRequest.chartReport.employee2Id + delete fakeChartReportRequest.chartReport.employee2Id + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.employee2Id = employee2Id + }) + + it('RS5.2.7 - when no employee2Name, should return false', () => { + // arrange + const employee2Name = fakeChartReportRequest.chartReport.employee2Name + delete fakeChartReportRequest.chartReport.employee2Name + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.employee2Name = employee2Name + }) + + it('RS5.2.8 - when no clientType, should return false', () => { + // arrange + const clientType = fakeChartReportRequest.chartReport.clientType + delete fakeChartReportRequest.chartReport.clientType + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.clientType = clientType + }) + + it('RS5.2.9 - when no ageOfAccount, should return false', () => { + // arrange + const ageOfAccount = fakeChartReportRequest.chartReport.ageOfAccount + delete fakeChartReportRequest.chartReport.ageOfAccount + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.ageOfAccount = ageOfAccount + }) + + it('RS5.2.10 - when no countryId, should return false', () => { + // arrange + const countryId = fakeChartReportRequest.chartReport.countryId + delete fakeChartReportRequest.chartReport.countryId + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.countryId = countryId + }) + + it('RS5.2.11 - when no country, should return false', () => { + // arrange + const country = fakeChartReportRequest.chartReport.country + delete fakeChartReportRequest.chartReport.country + + expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false) + fakeChartReportRequest.chartReport.country = country + }) + }) + }) + + describe('RS6 - deleteChartReportById', () => { + const fakeChartIdObject = { + chartReportId: 'fakeUUID' + } + describe('RS6.1 - given valid chart report id', () => { + it('RS6.1.1 - should return an empty promise', () => { + // arrange + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'deleteChartReportById') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve({}) + })) + + const expectedResponse = Promise.resolve({}) + + // act + const response = ReportService.deleteChartReportById(fakeChartIdObject.chartReportId) + + // assert + expect(response).toEqual(expectedResponse) + }) + }) + + describe('RS6.2 - given invalid chart report id', () => { + it('RS6.2.1 - should return Dao error', () => { + // arrange + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'deleteChartReportById') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedData) + })) + + const expectedData = { + status: 500, + message: 'Some error occured' + } + + // act and assert + expect(ReportService.deleteChartReportById(fakeChartIdObject.chartReportId)).rejects + .toEqual(expectedData) + }) + + it('RS6.2.2 - should return false', () => { + // arrange + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'deleteChartReportById') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(false) + })) + + // act and assert + expect(ReportService.deleteChartReportById(fakeChartIdObject.chartReportId)).resolves + .toEqual(false) + }) + + it('RS6.2.3 - when dao rejects with specified status and message, should reject with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'deleteChartReportById') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(ReportService.deleteChartReportById(fakeChartIdObject.chartReportId)) + .rejects.toEqual(expectedResponse) + expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeChartIdObject.chartReportId) + }) + + it('RS6.2.4 - when dao rejects with unspecified status and message, should reject with default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'deleteChartReportById') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.deleteChartReportById(fakeChartIdObject.chartReportId)) + .rejects.toEqual(expectedResponse) + expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeChartIdObject.chartReportId) + }) + }) + }) + + let reportServiceGetReportTypesSpy + let reportServiceGetRecipientsSpy + describe('RS7 - getReportTypesWithRecipients', () => { + const fakeGetReportTypesResponse = [ + { + reportTypeId: 'someUUID', + name: 'someName1', + frequency: 0, + recipients: { + uuid1: { name: '' }, + uuid2: { name: '' }, + uuid3: { name: '' } + } + }, + { + reportTypeId: 'someUUID1', + name: 'someName2', + frequency: 1, + recipients: { + uuid3: { name: '' }, + uuid2: { name: '' }, + uuid5: { name: '' } + } + } + ] + const fakeGetRecipientsResponse = [ + { + recipientId: 'uuid1', + name: 'myName1', + email: 'email1' + }, + { + recipientId: 'uuid2', + name: 'myName2', + email: 'email2' + }, + { + recipientId: 'uuid3', + name: 'myName3', + email: 'email3' + }, + { + recipientId: 'uuid4', + name: 'myName4', + email: 'email4' + }, + { + recipientId: 'uuid5', + name: 'myName5', + email: 'email5' + } + ] + + reportServiceGetReportTypesSpy = jest.spyOn(ReportService, 'getReportTypes') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeGetReportTypesResponse) + })) + reportServiceGetRecipientsSpy = jest.spyOn(ReportService, 'getRecipients') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeGetRecipientsResponse) + })) + + describe('RS7.1 - given valid response from getReportTypes and getRecipients', () => { + it('RS7.1.1 - should respond with list of reportTypes with the recipients', async () => { + // arrange + const expectedResponse = [ + { + reportTypeId: fakeGetReportTypesResponse[0].reportTypeId, + name: fakeGetReportTypesResponse[0].name, + frequency: fakeGetReportTypesResponse[0].frequency, + recipients: { + uuid1: { name: 'myName1', isRecipient: true }, + uuid2: { name: 'myName2', isRecipient: true }, + uuid3: { name: 'myName3', isRecipient: true }, + uuid4: { name: 'myName4', isRecipient: false }, + uuid5: { name: 'myName5', isRecipient: false } } - ]; - - describe("RS4.1 - given valid createdChartReport and data", () => { - it("RS4.1.1 - when comparison and valid response from dao, should resolve response from dao", async () => { - // arrange - createChartReportDataSpy.mockRestore(); - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeCreateDataForChartReportDaoResponse); - })); - let expectedResponse = fakeCreateDataForChartReportDaoResponse; - - // act and assert - await expect(ReportService.createChartReportData(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData)) - .resolves.toEqual(expectedResponse); - expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse.chartReportId, fakePreparedData); - }); - - it("RS4.1.2 - when all employees and valid response from dao, should resolve response from dao", async () => { - // arrange - createChartReportDataSpy.mockRestore(); - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeCreateDataForChartReportDaoResponse); - })); - let expectedResponse = fakeCreateDataForChartReportDaoResponse; - let fakeCreatedChartReportAllEmployees = { - chartReportId: "fakeUUID", - name: "fakeName", - emp1Id: -1, - emp2Id: null - }; - let chartReportData = [ - { - label: "2019", - data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - } - ]; - let fakePreparedDataAllEmployees = [fakePreparedData[1]]; - - - // act and assert - await expect(ReportService.createChartReportData(fakeCreatedChartReportAllEmployees, chartReportData)) - .resolves.toEqual(expectedResponse); - expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreatedChartReportAllEmployees.chartReportId, fakePreparedDataAllEmployees); - }); - - it("RS4.1.3 - when one employee and valid response from dao, should resolve response from dao", async () => { - // arrange - createChartReportDataSpy.mockRestore(); - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeCreateDataForChartReportDaoResponse); - })); - let expectedResponse = fakeCreateDataForChartReportDaoResponse; - let fakeCreatedChartReportOneEmployee = { - chartReportId: "fakeUUID", - name: "fakeName", - emp1Id: 12345, - emp2Id: null - } - let chartReportData = [ - { - label: "2019", - data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - } - ]; - let fakePreparedDataOneEmployee = [fakePreparedData[0]]; - - // act and assert - await expect(ReportService.createChartReportData(fakeCreatedChartReportOneEmployee, chartReportData)) - .resolves.toEqual(expectedResponse); - expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreatedChartReportOneEmployee.chartReportId, fakePreparedDataOneEmployee); - }); - - it("RS4.1.4 - when dao resolves false, should resolve false", async () => { - // arrange - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.createChartReportData(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData)) - .resolves.toBe(false); - expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse.chartReportId, fakePreparedData); - }); - - it("RS4.1.5 - when dao rejects with specified status and message, should reject with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act and assert - await expect(ReportService.createChartReportData(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData)) - .rejects.toEqual(expectedResponse); - expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse.chartReportId, fakePreparedData); - }); - - it("RS4.1.6 - when dao rejects with unspecified status and message, should reject with default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'createDataForChartReport') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(ReportService.createChartReportData(fakeCreateChartReportDaoResponse, fakeChartReportRequest.chartReportData)) - .rejects.toEqual(expectedResponse); - expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeCreateChartReportDaoResponse.chartReportId, fakePreparedData); - }); - }); - }); - - describe("RS5 - verifyChartReport", () => { - - describe("RS5.1 - given valid criteria", () => { - it("RS5.1.1 - should return true", () => { - // arrange - verifyChartReportSpy.mockRestore(); - - // act and assert - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(true); - }); - }); - - describe("RS5.2 - given invalid criteria", () => { - it("RS5.2.1 - when no name, should return false", () => { - // arrange - let name = fakeChartReportRequest.chartReport['name']; - delete fakeChartReportRequest.chartReport['name']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['name'] = name; - }); - - it("RS5.2.2 - when no startDate, should return false", () => { - // arrange - let startDate = fakeChartReportRequest.chartReport['startDate']; - delete fakeChartReportRequest.chartReport['startDate']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['startDate'] = startDate; - }); - - it("RS5.2.3 - when no endDate, should return false", () => { - // arrange - let endDate = fakeChartReportRequest.chartReport['endDate']; - delete fakeChartReportRequest.chartReport['endDate']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['endDate'] = endDate; - }); - - it("RS5.2.4 - when no employee1Id, should return false", () => { - // arrange - let employee1Id = fakeChartReportRequest.chartReport['employee1Id']; - delete fakeChartReportRequest.chartReport['employee1Id']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['employee1Id'] = employee1Id; - }); - - it("RS5.2.5 - when no employee1Name, should return false", () => { - // arrange - let employee1Name = fakeChartReportRequest.chartReport['employee1Name']; - delete fakeChartReportRequest.chartReport['employee1Name']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['employee1Name'] = employee1Name; - }); - - it("RS5.2.6 - when no employee2Id, should return false", () => { - // arrange - let employee2Id = fakeChartReportRequest.chartReport['employee2Id']; - delete fakeChartReportRequest.chartReport['employee2Id']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['employee2Id'] = employee2Id; - }); - - it("RS5.2.7 - when no employee2Name, should return false", () => { - // arrange - let employee2Name = fakeChartReportRequest.chartReport['employee2Name']; - delete fakeChartReportRequest.chartReport['employee2Name']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['employee2Name'] = employee2Name; - }); - - it("RS5.2.8 - when no clientType, should return false", () => { - // arrange - let clientType = fakeChartReportRequest.chartReport['clientType']; - delete fakeChartReportRequest.chartReport['clientType']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['clientType'] = clientType; - }); - - it("RS5.2.9 - when no ageOfAccount, should return false", () => { - // arrange - let ageOfAccount = fakeChartReportRequest.chartReport['ageOfAccount']; - delete fakeChartReportRequest.chartReport['ageOfAccount']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['ageOfAccount'] = ageOfAccount; - }); - - it("RS5.2.10 - when no countryId, should return false", () => { - // arrange - let countryId = fakeChartReportRequest.chartReport['countryId']; - delete fakeChartReportRequest.chartReport['countryId']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['countryId'] = countryId; - }); - - it("RS5.2.11 - when no country, should return false", () => { - // arrange - let country = fakeChartReportRequest.chartReport['country']; - delete fakeChartReportRequest.chartReport['country']; - - expect(ReportService.verifyChartReport(fakeChartReportRequest.chartReport)).toBe(false); - fakeChartReportRequest.chartReport['country'] = country; - }); - }); - }); - - describe("RS6 - deleteChartReportById", () => { - let fakeChartIdObject = { - chartReportId: "fakeUUID" - }; - describe("RS6.1 - given valid chart report id", () => { - it("RS6.1.1 - should return an empty promise", () => { - - //arrange - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'deleteChartReportById') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve({}); - })); - - let expectedResponse = Promise.resolve({}) - - // act - const response = ReportService.deleteChartReportById(fakeChartIdObject.chartReportId); - - // assert - expect(response).toEqual(expectedResponse); - }); - }); - - describe("RS6.2 - given invalid chart report id", () => { - it("RS6.2.1 - should return Dao error", () => { - // arrange - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'deleteChartReportById') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedData); - })); - - let expectedData = { - status: 500, - message: "Some error occured" - } - - //act and assert - expect(ReportService.deleteChartReportById(fakeChartIdObject.chartReportId)).rejects - .toEqual(expectedData); - - }); - - it("RS6.2.2 - should return false", () => { - // arrange - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'deleteChartReportById') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(false); - })); - - //act and assert - expect(ReportService.deleteChartReportById(fakeChartIdObject.chartReportId)).resolves - .toEqual(false); - }); - - it("RS6.2.3 - when dao rejects with specified status and message, should reject with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'deleteChartReportById') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act and assert - await expect(ReportService.deleteChartReportById(fakeChartIdObject.chartReportId)) - .rejects.toEqual(expectedResponse); - expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeChartIdObject.chartReportId); - }); - - it("RS6.2.4 - when dao rejects with unspecified status and message, should reject with default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'deleteChartReportById') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(ReportService.deleteChartReportById(fakeChartIdObject.chartReportId)) - .rejects.toEqual(expectedResponse); - expect(chartReportDaoSpy).toHaveBeenCalledWith(fakeChartIdObject.chartReportId); - }); - }); - }); - - let reportServiceGetReportTypesSpy; - let reportServiceGetRecipientsSpy; - describe("RS7 - getReportTypesWithRecipients", () => { - let fakeGetReportTypesResponse = [ - { - reportTypeId: "someUUID", - name: "someName1", - frequency: 0, - recipients: { - "uuid1": { name: "" }, - "uuid2": { name: "" }, - "uuid3": { name: "" } - } - }, - { - reportTypeId: "someUUID1", - name: "someName2", - frequency: 1, - recipients: { - "uuid3": { name: "" }, - "uuid2": { name: "" }, - "uuid5": { name: "" } - } + }, + { + reportTypeId: fakeGetReportTypesResponse[1].reportTypeId, + name: fakeGetReportTypesResponse[1].name, + frequency: fakeGetReportTypesResponse[1].frequency, + recipients: { + uuid1: { name: 'myName1', isRecipient: false }, + uuid2: { name: 'myName2', isRecipient: true }, + uuid3: { name: 'myName3', isRecipient: true }, + uuid4: { name: 'myName4', isRecipient: false }, + uuid5: { name: 'myName5', isRecipient: true } } - ]; - let fakeGetRecipientsResponse = [ - { - recipientId: "uuid1", - name: "myName1", - email: "email1" - }, - { - recipientId: "uuid2", - name: "myName2", - email: "email2" - }, - { - recipientId: "uuid3", - name: "myName3", - email: "email3" - }, - { - recipientId: "uuid4", - name: "myName4", - email: "email4" - }, - { - recipientId: "uuid5", - name: "myName5", - email: "email5" - }, - ]; + } + ] + + // act and assert + await expect(ReportService.getReportTypesWithRecipients()) + .resolves.toEqual(expectedResponse) + }) + }) + describe('RS7.2 - given invalid response from getReportTypes', () => { + it('RS7.2.1 - when getReportTypes resolves false, should resolve false', async () => { + // arrange + reportServiceGetReportTypesSpy.mockClear() reportServiceGetReportTypesSpy = jest.spyOn(ReportService, 'getReportTypes') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeGetReportTypesResponse); - })); + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.getReportTypesWithRecipients()) + .resolves.toEqual(false) + expect(reportServiceGetReportTypesSpy).toHaveBeenCalled() + expect(reportServiceGetRecipientsSpy).toHaveBeenCalledTimes(0) + }) + + it('RS7.2.2 - when getReportTypes rejects specified status and message, should reject specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + reportServiceGetReportTypesSpy = jest.spyOn(ReportService, 'getReportTypes') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(ReportService.getReportTypesWithRecipients()) + .rejects.toEqual(expectedResponse) + expect(reportServiceGetReportTypesSpy).toHaveBeenCalled() + expect(reportServiceGetRecipientsSpy).toHaveBeenCalledTimes(0) + }) + + it('RS7.2.3 - when getReportTypes rejects unspecified status and message, should reject default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + reportServiceGetReportTypesSpy = jest.spyOn(ReportService, 'getReportTypes') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.getReportTypesWithRecipients()) + .rejects.toEqual(expectedResponse) + expect(reportServiceGetReportTypesSpy).toHaveBeenCalled() + expect(reportServiceGetRecipientsSpy).toHaveBeenCalledTimes(0) + }) + }) + + describe('RS7.3 - given invalid response from getRecipients', () => { + it('RS7.3.1 - when getRecipients resolves false, should resolve false', async () => { + // arrange + reportServiceGetReportTypesSpy = jest.spyOn(ReportService, 'getReportTypes') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeGetReportTypesResponse) + })) reportServiceGetRecipientsSpy = jest.spyOn(ReportService, 'getRecipients') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeGetRecipientsResponse); - })); - - describe("RS7.1 - given valid response from getReportTypes and getRecipients", () => { - it("RS7.1.1 - should respond with list of reportTypes with the recipients", async () => { - // arrange - let expectedResponse = [ - { - reportTypeId: fakeGetReportTypesResponse[0].reportTypeId, - name: fakeGetReportTypesResponse[0].name, - frequency: fakeGetReportTypesResponse[0].frequency, - recipients: { - "uuid1": { name: "myName1", isRecipient: true }, - "uuid2": { name: "myName2", isRecipient: true }, - "uuid3": { name: "myName3", isRecipient: true }, - "uuid4": { name: "myName4", isRecipient: false }, - "uuid5": { name: "myName5", isRecipient: false } - } - }, - { - reportTypeId: fakeGetReportTypesResponse[1].reportTypeId, - name: fakeGetReportTypesResponse[1].name, - frequency: fakeGetReportTypesResponse[1].frequency, - recipients: { - "uuid1": { name: "myName1", isRecipient: false }, - "uuid2": { name: "myName2", isRecipient: true }, - "uuid3": { name: "myName3", isRecipient: true }, - "uuid4": { name: "myName4", isRecipient: false }, - "uuid5": { name: "myName5", isRecipient: true } - } - } - ]; - - // act and assert - await expect(ReportService.getReportTypesWithRecipients()) - .resolves.toEqual(expectedResponse); - }); - }); - - describe("RS7.2 - given invalid response from getReportTypes", () => { - it("RS7.2.1 - when getReportTypes resolves false, should resolve false", async () => { - // arrange - reportServiceGetReportTypesSpy.mockClear(); - reportServiceGetReportTypesSpy = jest.spyOn(ReportService, 'getReportTypes') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.getReportTypesWithRecipients()) - .resolves.toEqual(false); - expect(reportServiceGetReportTypesSpy).toHaveBeenCalled(); - expect(reportServiceGetRecipientsSpy).toHaveBeenCalledTimes(0); - }); - - it("RS7.2.2 - when getReportTypes rejects specified status and message, should reject specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - reportServiceGetReportTypesSpy = jest.spyOn(ReportService, 'getReportTypes') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act and assert - await expect(ReportService.getReportTypesWithRecipients()) - .rejects.toEqual(expectedResponse); - expect(reportServiceGetReportTypesSpy).toHaveBeenCalled(); - expect(reportServiceGetRecipientsSpy).toHaveBeenCalledTimes(0); - }); - - it("RS7.2.3 - when getReportTypes rejects unspecified status and message, should reject default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - reportServiceGetReportTypesSpy = jest.spyOn(ReportService, 'getReportTypes') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(ReportService.getReportTypesWithRecipients()) - .rejects.toEqual(expectedResponse); - expect(reportServiceGetReportTypesSpy).toHaveBeenCalled(); - expect(reportServiceGetRecipientsSpy).toHaveBeenCalledTimes(0); - }); - }); - - describe("RS7.3 - given invalid response from getRecipients", () => { - it("RS7.3.1 - when getRecipients resolves false, should resolve false", async () => { - // arrange - reportServiceGetReportTypesSpy = jest.spyOn(ReportService, 'getReportTypes') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeGetReportTypesResponse); - })); - reportServiceGetRecipientsSpy = jest.spyOn(ReportService, 'getRecipients') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.getReportTypesWithRecipients()) - .resolves.toBe(false); - expect(reportServiceGetReportTypesSpy).toHaveBeenCalled(); - expect(reportServiceGetRecipientsSpy).toHaveBeenCalled(); - }); - - it("RS7.3.2 - when getRecipients rejects specified status and message, should reject specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "bop" - }; - reportServiceGetRecipientsSpy = jest.spyOn(ReportService, 'getRecipients') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act and assert - await expect(ReportService.getReportTypesWithRecipients()) - .rejects.toEqual(expectedResponse); - expect(reportServiceGetReportTypesSpy).toHaveBeenCalled(); - expect(reportServiceGetRecipientsSpy).toHaveBeenCalled(); - }); - - it("RS7.3.3 - when getRecipients rejects unspecified status and message, should reject default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - reportServiceGetRecipientsSpy = jest.spyOn(ReportService, 'getRecipients') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(ReportService.getReportTypesWithRecipients()) - .rejects.toEqual(expectedResponse); - expect(reportServiceGetReportTypesSpy).toHaveBeenCalled(); - expect(reportServiceGetRecipientsSpy).toHaveBeenCalled(); - }); - }); - }); - - describe("RS8 - getReportTypes", () => { - let fakeGetReportTypeWithRecipientIdsDaoResponse = [ - { - reportTypeId: "someUUID", - name: "someName1", - frequency: 0, - recipients: { - "uuid1": { name: "" }, - "uuid2": { name: "" }, - "uuid3": { name: "" } - } - }, - { - reportTypeId: "someUUID1", - name: "someName2", - frequency: 1, - recipients: { - "uuid3": { name: "" }, - "uuid2": { name: "" }, - "uuid5": { name: "" } - } - } - ]; - let getReportTypeWithRecipientIdsDaoSpy; - describe("RS8.1 - given valid response from dao", () => { - it("RS8.1.1 - should resolve response from dao", async () => { - // arrange - reportServiceGetReportTypesSpy.mockRestore(); - getReportTypeWithRecipientIdsDaoSpy = jest.spyOn(ReportDao, 'getReportTypesWithRecipientIds') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeGetReportTypeWithRecipientIdsDaoResponse); - })); - - // act and assert - await expect(ReportService.getReportTypes()) - .resolves.toEqual(fakeGetReportTypeWithRecipientIdsDaoResponse); - }); - }); - - describe("RS8.2 - given invalid response from dao", () => { - it("RS9.2.1 - when dao resolves false, should resolve false", async () => { - // arrange - getReportTypeWithRecipientIdsDaoSpy = jest.spyOn(ReportDao, 'getReportTypesWithRecipientIds') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.getReportTypes()) - .resolves.toEqual(false); - expect(getReportTypeWithRecipientIdsDaoSpy).toHaveBeenCalled(); - }); - - it("RS8.2.2 - when dao rejects specified status and messsage, should reject with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - getReportTypeWithRecipientIdsDaoSpy = jest.spyOn(ReportDao, 'getReportTypesWithRecipientIds') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act and assert - await expect(ReportService.getReportTypes()) - .rejects.toEqual(expectedResponse); - expect(getReportTypeWithRecipientIdsDaoSpy).toHaveBeenCalled(); - }); - - it("RS8.2.3 - when dao rejects unspecified status and messsage, should reject with default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not fetch Report Types." - }; - getReportTypeWithRecipientIdsDaoSpy = jest.spyOn(ReportDao, 'getReportTypesWithRecipientIds') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(ReportService.getReportTypes()) - .rejects.toEqual(expectedResponse); - expect(getReportTypeWithRecipientIdsDaoSpy).toHaveBeenCalled(); - }); - }); - - }); - - describe("RS9 - getRecipients", () => { - let fakeGetRecipientsDaoResponse = [ - { - recipientId: "uuid1", - name: "myName1", - email: "email1" - }, - { - recipientId: "uuid2", - name: "myName2", - email: "email2" - }, - { - recipientId: "uuid3", - name: "myName3", - email: "email3" - }, - { - recipientId: "uuid4", - name: "myName4", - email: "email4" - }, - { - recipientId: "uuid5", - name: "myName5", - email: "email5" - }, - ]; - - let getRecipientsDaotSpy; - describe("RS9.1 - given valid response from dao", () => { - it("RS9.1.1 - should resolve response from dao", async () => { - // arrange - reportServiceGetRecipientsSpy.mockRestore(); - getRecipientsDaotSpy = jest.spyOn(ReportDao, 'getRecipients') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakeGetRecipientsDaoResponse); - })); - - // act and assert - await expect(ReportService.getRecipients()) - .resolves.toEqual(fakeGetRecipientsDaoResponse); - expect(getRecipientsDaotSpy).toHaveBeenCalled(); - - }); - }); - - describe("RS9.2 - given invalidresponse from dao", () => { - it("RS9.2.1 - when dao resolves false, should resolve false", async () => { - // arrange - getRecipientsDaotSpy = jest.spyOn(ReportDao, 'getRecipients') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.getRecipients()) - .resolves.toEqual(false); - expect(getRecipientsDaotSpy).toHaveBeenCalled(); - }); - - it("RS9.2.2 - when dao rejects specified status and messsage, should reject with specified status and message", async () => { - // arrange - let expectedResponse = { - status: 600, - message: "Error." - }; - getRecipientsDaotSpy = jest.spyOn(ReportDao, 'getRecipients') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedResponse); - })); - - // act and assert - await expect(ReportService.getRecipients()) - .rejects.toEqual(expectedResponse); - expect(getRecipientsDaotSpy).toHaveBeenCalled(); - }); - - it("RS9.2.3 - when dao rejects unspecified status and messsage, should reject with default status and message", async () => { - // arrange - let expectedResponse = { - status: 500, - message: "Could not fetch Recipients." - }; - getRecipientsDaotSpy = jest.spyOn(ReportDao, 'getRecipients') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(ReportService.getRecipients()) - .rejects.toEqual(expectedResponse); - expect(getRecipientsDaotSpy).toHaveBeenCalled(); - }); - }); - }); - - describe("RS10 - getPerformanceReportWhenConnectedAsAdmin", () => { - - let fakePerformanceReportResponse = [ - { - performanceReportId: "PerformanceUUID", - employeeId: 1, - averageCollectionDay: "35", - annualBillingObjective: "4500", - monthlyBillingObjective: "300", - annualBillingNumber: "200", - monthlyBillingNumber: "300", - projectedBonus: "650" - }, - { - performanceReportId: "PerformanceUUID", - employeeId: 2, - averageCollectionDay: "35", - annualBillingObjective: "4500", - monthlyBillingObjective: "300", - annualBillingNumber: "200", - monthlyBillingNumber: "300", - projectedBonus: "650" - } - ]; - - describe("RS10.1 - given a userId", () => { - it("RS10.1.1 - should return list of chartReports", async () => { - // arrange - chartReportDaoSpy = jest.spyOn(ReportDao, 'getPerformanceReportsWhenConnectedAsAdmin') - .mockImplementation(() => new Promise((resolve) => { - resolve(fakePerformanceReportResponse); - })); - - // act and assert - await expect(ReportService.getPerformanceReportWhenConnectedAsAdmin("randomUserId")).resolves - .toEqual(fakePerformanceReportResponse); - }); - - it("RS10.1.2 - should resolve false when dao returns false", async () => { - // arrange - chartReportDaoSpy = jest.spyOn(ReportDao, 'getPerformanceReportsWhenConnectedAsAdmin') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.getPerformanceReportWhenConnectedAsAdmin("someUserId")).resolves - .toEqual(false); - }); - - it("RS10.1.3 - should reject with dao error status and message when dao throws error", async () => { - // arrange - let expectedError = { - status: 404, - message: "Error message." - }; - chartReportDaoSpy = jest.spyOn(ReportDao, 'getPerformanceReportsWhenConnectedAsAdmin') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedError); - })); - - // act and assert - await expect(ReportService.getPerformanceReportWhenConnectedAsAdmin("someUserId")).rejects - .toEqual(expectedError); - }); - - it("RS10.1.4 - should reject with status 500 and message when dao error doesn't specify", async () => { - // arrange - let expectedError = { - status: 500, - message: "Could not fetch data." - }; - chartReportDaoSpy = jest.spyOn(ReportDao, 'getPerformanceReportsWhenConnectedAsAdmin') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({}); - })); - - // act and assert - await expect(ReportService.getPerformanceReportWhenConnectedAsAdmin("someUserId")).rejects - .toEqual(expectedError); - }); - }); - - describe("RS10.2 - given no userId", () => { - it("RS10.2.1 - should reject with 500 status and message", async () => { - // arrange - let expectedError = { - status: 500, - message: "Could not fetch data." - }; - - // act and assert - await expect(ReportService.getPerformanceReportWhenConnectedAsAdmin()).rejects - .toEqual(expectedError); - }); - }); - }); - - describe("RS11 - createChartReportPDFById", () => { - let returnedChartReport = { - dataValues: { - chartReportId: 'fakeUUID1', - name: 'CR1', - startDate: '2019-12-01', - endDate: '2020-12-01', - employee1Id: 12345, - employee1Name: 'France Cote', - employee2Id: null, - employee2Name: null, - country: 'Canada', - clientType: 'Corr', - ageOfAccount: 'All', - accountType: 'Receivable', - createdAt: new Date('2022-02-14T16:12:21'), - updatedAt: new Date('2022-02-14T16:12:21'), - user_user_id: 'fakeUserId' - } + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.getReportTypesWithRecipients()) + .resolves.toBe(false) + expect(reportServiceGetReportTypesSpy).toHaveBeenCalled() + expect(reportServiceGetRecipientsSpy).toHaveBeenCalled() + }) + + it('RS7.3.2 - when getRecipients rejects specified status and message, should reject specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'bop' + } + reportServiceGetRecipientsSpy = jest.spyOn(ReportService, 'getRecipients') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(ReportService.getReportTypesWithRecipients()) + .rejects.toEqual(expectedResponse) + expect(reportServiceGetReportTypesSpy).toHaveBeenCalled() + expect(reportServiceGetRecipientsSpy).toHaveBeenCalled() + }) + + it('RS7.3.3 - when getRecipients rejects unspecified status and message, should reject default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + reportServiceGetRecipientsSpy = jest.spyOn(ReportService, 'getRecipients') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.getReportTypesWithRecipients()) + .rejects.toEqual(expectedResponse) + expect(reportServiceGetReportTypesSpy).toHaveBeenCalled() + expect(reportServiceGetRecipientsSpy).toHaveBeenCalled() + }) + }) + }) + + describe('RS8 - getReportTypes', () => { + const fakeGetReportTypeWithRecipientIdsDaoResponse = [ + { + reportTypeId: 'someUUID', + name: 'someName1', + frequency: 0, + recipients: { + uuid1: { name: '' }, + uuid2: { name: '' }, + uuid3: { name: '' } + } + }, + { + reportTypeId: 'someUUID1', + name: 'someName2', + frequency: 1, + recipients: { + uuid3: { name: '' }, + uuid2: { name: '' }, + uuid5: { name: '' } } + } + ] + let getReportTypeWithRecipientIdsDaoSpy + describe('RS8.1 - given valid response from dao', () => { + it('RS8.1.1 - should resolve response from dao', async () => { + // arrange + reportServiceGetReportTypesSpy.mockRestore() + getReportTypeWithRecipientIdsDaoSpy = jest.spyOn(ReportDao, 'getReportTypesWithRecipientIds') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeGetReportTypeWithRecipientIdsDaoResponse) + })) + + // act and assert + await expect(ReportService.getReportTypes()) + .resolves.toEqual(fakeGetReportTypeWithRecipientIdsDaoResponse) + }) + }) - let returnedChartReportData = [ - { - year: 2018, - employee: -1, - data: [ - 0, 0, 0, 0, - 0, 0, 0, 0, - 91.65, 83.36, 88.35, 89 - ] - }, - { - year: 2019, - employee: -1, - data: [ - 84.12, 87.92, 93.05, - 99.39, 96.37, 0, - 0, 0, 0, - 0, 0, 0 - ] - } + describe('RS8.2 - given invalid response from dao', () => { + it('RS9.2.1 - when dao resolves false, should resolve false', async () => { + // arrange + getReportTypeWithRecipientIdsDaoSpy = jest.spyOn(ReportDao, 'getReportTypesWithRecipientIds') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.getReportTypes()) + .resolves.toEqual(false) + expect(getReportTypeWithRecipientIdsDaoSpy).toHaveBeenCalled() + }) + + it('RS8.2.2 - when dao rejects specified status and messsage, should reject with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + getReportTypeWithRecipientIdsDaoSpy = jest.spyOn(ReportDao, 'getReportTypesWithRecipientIds') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(ReportService.getReportTypes()) + .rejects.toEqual(expectedResponse) + expect(getReportTypeWithRecipientIdsDaoSpy).toHaveBeenCalled() + }) + + it('RS8.2.3 - when dao rejects unspecified status and messsage, should reject with default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not fetch Report Types.' + } + getReportTypeWithRecipientIdsDaoSpy = jest.spyOn(ReportDao, 'getReportTypesWithRecipientIds') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.getReportTypes()) + .rejects.toEqual(expectedResponse) + expect(getReportTypeWithRecipientIdsDaoSpy).toHaveBeenCalled() + }) + }) + }) + + describe('RS9 - getRecipients', () => { + const fakeGetRecipientsDaoResponse = [ + { + recipientId: 'uuid1', + name: 'myName1', + email: 'email1' + }, + { + recipientId: 'uuid2', + name: 'myName2', + email: 'email2' + }, + { + recipientId: 'uuid3', + name: 'myName3', + email: 'email3' + }, + { + recipientId: 'uuid4', + name: 'myName4', + email: 'email4' + }, + { + recipientId: 'uuid5', + name: 'myName5', + email: 'email5' + } + ] + + let getRecipientsDaotSpy + describe('RS9.1 - given valid response from dao', () => { + it('RS9.1.1 - should resolve response from dao', async () => { + // arrange + reportServiceGetRecipientsSpy.mockRestore() + getRecipientsDaotSpy = jest.spyOn(ReportDao, 'getRecipients') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakeGetRecipientsDaoResponse) + })) + + // act and assert + await expect(ReportService.getRecipients()) + .resolves.toEqual(fakeGetRecipientsDaoResponse) + expect(getRecipientsDaotSpy).toHaveBeenCalled() + }) + }) + + describe('RS9.2 - given invalidresponse from dao', () => { + it('RS9.2.1 - when dao resolves false, should resolve false', async () => { + // arrange + getRecipientsDaotSpy = jest.spyOn(ReportDao, 'getRecipients') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.getRecipients()) + .resolves.toEqual(false) + expect(getRecipientsDaotSpy).toHaveBeenCalled() + }) + + it('RS9.2.2 - when dao rejects specified status and messsage, should reject with specified status and message', async () => { + // arrange + const expectedResponse = { + status: 600, + message: 'Error.' + } + getRecipientsDaotSpy = jest.spyOn(ReportDao, 'getRecipients') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedResponse) + })) + + // act and assert + await expect(ReportService.getRecipients()) + .rejects.toEqual(expectedResponse) + expect(getRecipientsDaotSpy).toHaveBeenCalled() + }) + + it('RS9.2.3 - when dao rejects unspecified status and messsage, should reject with default status and message', async () => { + // arrange + const expectedResponse = { + status: 500, + message: 'Could not fetch Recipients.' + } + getRecipientsDaotSpy = jest.spyOn(ReportDao, 'getRecipients') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.getRecipients()) + .rejects.toEqual(expectedResponse) + expect(getRecipientsDaotSpy).toHaveBeenCalled() + }) + }) + }) + + describe('RS10 - getPerformanceReportWhenConnectedAsAdmin', () => { + const fakePerformanceReportResponse = [ + { + performanceReportId: 'PerformanceUUID', + employeeId: 1, + averageCollectionDay: '35', + annualBillingObjective: '4500', + monthlyBillingObjective: '300', + annualBillingNumber: '200', + monthlyBillingNumber: '300', + projectedBonus: '650' + }, + { + performanceReportId: 'PerformanceUUID', + employeeId: 2, + averageCollectionDay: '35', + annualBillingObjective: '4500', + monthlyBillingObjective: '300', + annualBillingNumber: '200', + monthlyBillingNumber: '300', + projectedBonus: '650' + } + ] + + describe('RS10.1 - given a userId', () => { + it('RS10.1.1 - should return list of chartReports', async () => { + // arrange + chartReportDaoSpy = jest.spyOn(ReportDao, 'getPerformanceReports') + .mockImplementation(() => new Promise((resolve) => { + resolve(fakePerformanceReportResponse) + })) + + // act and assert + await expect(ReportService.getPerformanceReports('randomUserId')).resolves + .toEqual(fakePerformanceReportResponse) + }) + + it('RS10.1.2 - should resolve false when dao returns false', async () => { + // arrange + chartReportDaoSpy = jest.spyOn(ReportDao, 'getPerformanceReports') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.getPerformanceReports('someUserId')).resolves + .toEqual(false) + }) + + it('RS10.1.3 - should reject with dao error status and message when dao throws error', async () => { + // arrange + const expectedError = { + status: 404, + message: 'Error message.' + } + chartReportDaoSpy = jest.spyOn(ReportDao, 'getPerformanceReports') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedError) + })) + + // act and assert + await expect(ReportService.getPerformanceReports('someUserId')).rejects + .toEqual(expectedError) + }) + + it("RS10.1.4 - should reject with status 500 and message when dao error doesn't specify", async () => { + // arrange + const expectedError = { + status: 500, + message: 'Could not fetch data.' + } + chartReportDaoSpy = jest.spyOn(ReportDao, 'getPerformanceReports') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({}) + })) + + // act and assert + await expect(ReportService.getPerformanceReports('someUserId')).rejects + .toEqual(expectedError) + }) + }) + + describe('RS10.2 - given no userId', () => { + it('RS10.2.1 - should reject with 500 status and message', async () => { + // arrange + const expectedError = { + status: 500, + message: 'Could not fetch data.' + } + + // act and assert + await expect(ReportService.getPerformanceReports()).rejects + .toEqual(expectedError) + }) + }) + }) + + describe('RS11 - createChartReportPDFById', () => { + const returnedChartReport = { + dataValues: { + chartReportId: 'fakeUUID1', + name: 'CR1', + startDate: '2019-12-01', + endDate: '2020-12-01', + employee1Id: 12345, + employee1Name: 'France Cote', + employee2Id: null, + employee2Name: null, + country: 'Canada', + clientType: 'Corr', + ageOfAccount: 'All', + accountType: 'Receivable', + createdAt: new Date('2022-02-14T16:12:21'), + updatedAt: new Date('2022-02-14T16:12:21'), + user_user_id: 'fakeUserId' + } + } + + const returnedChartReportData = [ + { + year: 2018, + employee: -1, + data: [ + 0, 0, 0, 0, + 0, 0, 0, 0, + 91.65, 83.36, 88.35, 89 + ] + }, + { + year: 2019, + employee: -1, + data: [ + 84.12, 87.92, 93.05, + 99.39, 96.37, 0, + 0, 0, 0, + 0, 0, 0 ] + } + ] + + describe('RS11.1 - given a reportId', () => { + it('RS11.1.1 - should return true', async () => { + // arrange + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportById') + .mockImplementation(() => new Promise((resolve) => { + resolve(returnedChartReport) + })) + + jest.spyOn(ChartReportDao, 'getDataForChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(returnedChartReportData) + })) + + // act and assert + await expect(ReportService.createChartReportPDFById('fakeUUID1')).resolves + .toEqual(true) + + // wait for mock pdf file to be created + // await new Promise((r) => setTimeout(r, 2500)); + + // cleanup + if (__dirname !== '/home/runner/work/BC-Engine/BC-Engine/server/tests/services') { + fs.unlinkSync(`${__dirname.replace('tests\\services', '')}docs\\pdf_files\\chartReport-fakeUUID1.pdf`) + } else { + fs.unlinkSync(`${__dirname.replace('tests/services', '')}docs/pdf_files/chartReport-fakeUUID1.pdf`) + } + }) + + it('RS11.1.2 - should resolve false when dao returns false', async () => { + // arrange + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportById') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + jest.spyOn(ChartReportDao, 'getDataForChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(returnedChartReportData) + })) + + // act and assert + await expect(ReportService.createChartReportPDFById('fakeUUID1')).resolves + .toEqual(false) + }) + + it('RS11.1.3 - should reject with dao error status and message when dao throws error', async () => { + // arrange + const expectedError = { + status: 404, + message: 'Error message.' + } + + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportById') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedError) + })) + + jest.spyOn(ChartReportDao, 'getDataForChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(returnedChartReportData) + })) + + // act and assert + await expect(ReportService.createChartReportPDFById('fakeUUID1')).rejects + .toEqual(expectedError) + }) + + it("RS11.1.4 - should reject with status 500 and message when dao error doesn't specify", async () => { + // arrange + const expectedError = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportById') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(() => new Promise.reject()) + })) - describe("RS11.1 - given a reportId", () => { - it("RS11.1.1 - should return true", async () => { - // arrange - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportById') - .mockImplementation(() => new Promise((resolve) => { - resolve(returnedChartReport); - })); - - jest.spyOn(ChartReportDao, 'getDataForChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(returnedChartReportData); - })); - - // act and assert - await expect(ReportService.createChartReportPDFById("fakeUUID1")).resolves - .toEqual(true); - - // wait for mock pdf file to be created - //await new Promise((r) => setTimeout(r, 2500)); - - - - // cleanup - if (__dirname !== '/home/runner/work/BC-Engine/BC-Engine/server/tests/services') { - fs.unlinkSync(`${__dirname.replace("tests\\services", "")}docs\\pdf_files\\chartReport-fakeUUID1.pdf`); - } - else { - fs.unlinkSync(`${__dirname.replace("tests/services", "")}docs/pdf_files/chartReport-fakeUUID1.pdf`); - } - - }); - - it("RS11.1.2 - should resolve false when dao returns false", async () => { - // arrange - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportById') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - jest.spyOn(ChartReportDao, 'getDataForChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(returnedChartReportData); - })); - - // act and assert - await expect(ReportService.createChartReportPDFById("fakeUUID1")).resolves - .toEqual(false); - }); - - it("RS11.1.3 - should reject with dao error status and message when dao throws error", async () => { - // arrange - let expectedError = { - status: 404, - message: "Error message." - }; - - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportById') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedError); - })); - - jest.spyOn(ChartReportDao, 'getDataForChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(returnedChartReportData); - })); - - // act and assert - await expect(ReportService.createChartReportPDFById("fakeUUID1")).rejects - .toEqual(expectedError); - }); - - it("RS11.1.4 - should reject with status 500 and message when dao error doesn't specify", async () => { - // arrange - let expectedError = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportById') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(() => new Promise.reject()); - })); - - jest.spyOn(ChartReportDao, 'getDataForChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(returnedChartReportData); - })); - - // act and assert - await expect(ReportService.createChartReportPDFById("fakeUUID1")).rejects - .toEqual(expectedError); - }); - }); - - describe("RS11.2 - given no reportId", () => { - it("RS11.2.1 - should reject with 500 status and message", async () => { - // arrange - let expectedError = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - - // act and assert - await expect(ReportService.createChartReportPDFById()).rejects - .toEqual(expectedError); - }); - }); - - describe("RS11.3 - getChartReportPDFAverages", () => { - it("RS11.3.1 - when returned data is false should return error", async () => { - // arrange - chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportById') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(true) - })); - - jest.spyOn(ChartReportDao, 'getDataForChartReport') - .mockImplementation(() => new Promise((resolve) => { - resolve(false); - })); - - // act and assert - await expect(ReportService.createChartReportPDFById("fakeUUID1")).resolves - .toEqual(false); - }); - - it("RS11.3.2 - should reject with status 500 and message defined by the dao", async () => { - // arrange - let expectedError = { - status: 500, - message: "Malfunction in the B&C Engine." - }; - - jest.spyOn(ChartReportDao, 'getDataForChartReport') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedError); - })); - - // act and assert - await expect(ReportService.getChartReportPDFAverages("fakeUUID1")).rejects - .toEqual(expectedError); - }); - }); - }); -}); \ No newline at end of file + jest.spyOn(ChartReportDao, 'getDataForChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(returnedChartReportData) + })) + + // act and assert + await expect(ReportService.createChartReportPDFById('fakeUUID1')).rejects + .toEqual(expectedError) + }) + }) + + describe('RS11.2 - given no reportId', () => { + it('RS11.2.1 - should reject with 500 status and message', async () => { + // arrange + const expectedError = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + + // act and assert + await expect(ReportService.createChartReportPDFById()).rejects + .toEqual(expectedError) + }) + }) + + describe('RS11.3 - getChartReportPDFAverages', () => { + it('RS11.3.1 - when returned data is false should return error', async () => { + // arrange + chartReportDaoSpy = jest.spyOn(ChartReportDao, 'getChartReportById') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(true) + })) + + jest.spyOn(ChartReportDao, 'getDataForChartReport') + .mockImplementation(() => new Promise((resolve) => { + resolve(false) + })) + + // act and assert + await expect(ReportService.createChartReportPDFById('fakeUUID1')).resolves + .toEqual(false) + }) + + it('RS11.3.2 - should reject with status 500 and message defined by the dao', async () => { + // arrange + const expectedError = { + status: 500, + message: 'Malfunction in the B&C Engine.' + } + + jest.spyOn(ChartReportDao, 'getDataForChartReport') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedError) + })) + + // act and assert + await expect(ReportService.getChartReportPDFAverages('fakeUUID1')).rejects + .toEqual(expectedError) + }) + }) + }) +}) diff --git a/server/tests/services/user.service.test.js b/server/tests/services/user.service.test.js index 06c061e..f96f944 100644 --- a/server/tests/services/user.service.test.js +++ b/server/tests/services/user.service.test.js @@ -1,373 +1,365 @@ -const UserService = require('../../services/user.service'); -const AuthService = require('../../services/auth.service'); -const UserDAO = require("../../data_access_layer/daos/user.dao"); +const UserService = require('../../services/user.service') +const UserDAO = require('../../data_access_layer/daos/user.dao') -const sinon = require("sinon"); -var { expect, jest } = require('@jest/globals'); +const sinon = require('sinon') +const { expect } = require('@jest/globals') const listReqUserUnsorted = [ - { - email: "Maab@email.com", - password: "validPassword", - name: "Maab bot", - role: "admin" - }, - { - email: "Zigedon@email.com", - password: "validPassword", - name: "Zigedon bergeron", - role: "admin" - }, - { - email: "Kevin@email.com", - password: "validPassword", - name: "Kevin Lola", - role: "admin" - }, - { - email: "Maab@email.com", - password: "validPassword", - name: "Maab attention", - role: "admin" - } + { + email: 'Maab@email.com', + password: 'validPassword', + name: 'Maab bot', + role: 'admin' + }, + { + email: 'Zigedon@email.com', + password: 'validPassword', + name: 'Zigedon bergeron', + role: 'admin' + }, + { + email: 'Kevin@email.com', + password: 'validPassword', + name: 'Kevin Lola', + role: 'admin' + }, + { + email: 'Maab@email.com', + password: 'validPassword', + name: 'Maab attention', + role: 'admin' + } ] const listReqUsersorted = [ - { - email: "Kevin@email.com", - password: "validPassword", - name: "Kevin Lola", - role: "admin" - }, - { - email: "Maab@email.com", - password: "validPassword", - name: "Maab attention", - role: "admin" - }, - { - email: "Maab@email.com", - password: "validPassword", - name: "Maab bot", - role: "admin" - }, - { - email: "Zigedon@email.com", - password: "validPassword", - name: "Zigedon bergeron", - role: "admin" - } + { + email: 'Kevin@email.com', + password: 'validPassword', + name: 'Kevin Lola', + role: 'admin' + }, + { + email: 'Maab@email.com', + password: 'validPassword', + name: 'Maab attention', + role: 'admin' + }, + { + email: 'Maab@email.com', + password: 'validPassword', + name: 'Maab bot', + role: 'admin' + }, + { + email: 'Zigedon@email.com', + password: 'validPassword', + name: 'Zigedon bergeron', + role: 'admin' + } ] const reqUser = { - email: "valid@email.com", - password: "validPassword", - name: "validName", - role: "admin" -}; - -const resUserFromService = { - email: "valid@email.com", - name: "validName", - role: "validRole" + email: 'valid@email.com', + password: 'validPassword', + name: 'validName', + role: 'admin' } -const userDAOError = { - message: "Error with the user model." +const resUserFromService = { + email: 'valid@email.com', + name: 'validName', + role: 'validRole' } -let sandbox = sinon.createSandbox(); - -let userDAOSpy = jest.spyOn(UserDAO, 'createUser'); - -describe("Test User Service", () => { - - beforeEach(() => { - jest.clearAllMocks(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - afterAll(() => { - process.exit; - }); - - describe("US1 - createUser", () => { - - describe("US1.1 - given a valid user", () => { - it("US1.1.1 - should return resolved promise with user information when user model works properly", async () => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'createUser') - .mockImplementation(() => new Promise((resolve) => { - resolve(resUserFromService); - })); - let expectedUser = { - email: resUserFromService.email, - name: resUserFromService.name, - role: resUserFromService.role - } - - // act - const serviceResponse = await UserService.createUser(reqUser); - - // assert - expect(serviceResponse).toEqual(expectedUser); - expect(userDAOSpy).toBeCalledTimes(1); - }); - - it("US1.1.2 - should return Dao error", async () => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'createUser') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({ message: "dao failed" }); - })); - - // act and assert - await expect(UserService.createUser(reqUser)).rejects - .toEqual({ message: "dao failed" }); - }); - - it("US1.1.2 - should return false", async () => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'createUser') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(false); - })); - - // act and assert - await expect(UserService.createUser(reqUser)).resolves - .toEqual(false); - }); - }); - }); - - describe("US2 - View All Users", () => { - describe("US2.1 - given a list of users", () => { - it("US2.1.1 - Should return the full list of users with all their information", async() => { - // arrange - let ListUser = []; - const listUserLength = 3; - for(let i = 0; i < listUserLength; i++){ - ListUser.push(resUserFromService); - } - userDAOSpy = jest.spyOn(UserDAO, 'getAllUsers') - .mockImplementation(() => new Promise( - (resolve) => { - resolve(ListUser); - } - )); - - // act - const serviceResponse = await UserService.getAllUsers(); - - // assert - expect(serviceResponse.length).toBe(listUserLength); - expect(userDAOSpy).toBeCalledTimes(1); - }); - - it("US2.1.2 - Should return a full list of users which are sorted", async() => { - userDAOSpy = jest.spyOn(UserDAO, 'getAllUsers') - .mockImplementation(() => new Promise( - (resolve) => { - resolve(listReqUserUnsorted) - } - )); - - // act - const serviceResponse = await UserService.getAllUsers(); - - // assert - expect(serviceResponse).toEqual(listReqUsersorted); - }); - - it("US2.1.3 - should return Dao error", async () => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'getAllUsers') - .mockImplementation(() => new Promise((resolve, reject) => { - reject({ message: "dao failed" }); - })); - - // act and assert - await expect(UserService.getAllUsers()).rejects - .toEqual({ message: "dao failed" }); - }); - - it("US2.1.3 - should return false", async () => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'getAllUsers') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(false); - })); - - // act and assert - await expect(UserService.getAllUsers()).resolves - .toEqual(false); - }); - }); - }); - - describe("US3 - Authenticate User", () => { - describe("US3.1 - given a valid user", () => { - it("US3.1.1 - should return Dao error (custom error message)", async () => { - // arrange - let expectedData = { - status: 500, - message: "dao error" - } - - userDAOSpy = jest.spyOn(UserDAO, 'getUserByEmail') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedData); - })); - - // act and assert - await expect(UserService.authenticateUser(reqUser)).rejects - .toEqual(expectedData); - }); - - it("US3.1.2 - should return 'some error occured' (default error message)", async () => { - // arrange - let errorData = { - status: 500 - } - let expectedData = { - status: 500, - message: "some error occured" - } - - userDAOSpy = jest.spyOn(UserDAO, 'getUserByEmail') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(errorData); - })); - - // act and assert - await expect(UserService.authenticateUser(reqUser)).rejects - .toEqual(expectedData); - }); - - it("US3.1.3 - should return false", async () => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'getUserByEmail') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(false); - })); - - // act and assert - await expect(UserService.authenticateUser(reqUser)).resolves - .toEqual(false); - }); - - it("US3.1.4 - should return a status 200 with valid data", async () => { - // arrange - let authUser = { - password: 'CoolCool123', - validPassword: () => {return true} - }; - - userDAOSpy = jest.spyOn(UserDAO, 'getUserByEmail') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(authUser); - })); - - // act and assert - await expect(UserService.authenticateUser(reqUser)).resolves - .toEqual(authUser); - }); - }); - }); - - describe("US4 - Modify User", () => { - describe("US4.1 - given a valid user", () => { - it("US4.1.1 - Should return a user", async() => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'updateUser') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(reqUser); - })); - - // act - const serviceResponse = await UserService.modifyUser(reqUser); - - // assert - expect(serviceResponse).toEqual(reqUser); - }); - it("US4.1.3 - should return Dao error", async () => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'updateUser') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedData); - })); - - let expectedData = { - status: 500, - message: "some error occured" - } - - // act and assert - await expect(UserService.modifyUser(reqUser)).rejects - .toEqual(expectedData); - }); - - it("US4.1.3 - should return false", async () => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'updateUser') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(false); - })); - - // act and assert - await expect(UserService.modifyUser(reqUser)).resolves - .toEqual(false); - }); - }); - }); - - describe("US5 - Delete User", () => { - describe("US5.1 - given a valid user", () => { - it("US5.1.1 - Should return a user", async() => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'deleteUser') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(reqUser); - })); - - // act - const serviceResponse = await UserService.deleteUser("valid@email.com"); - - // assert - expect(serviceResponse).toEqual(reqUser); - }); - it("US5.1.3 - should return Dao error", async () => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'deleteUser') - .mockImplementation(() => new Promise((resolve, reject) => { - reject(expectedData); - })); - - let expectedData = { - status: 500, - message: "some error occured" - } - - // act and assert - await expect(UserService.deleteUser("valid@email.com")).rejects - .toEqual(expectedData); - }); - - it("US5.1.3 - should return false", async () => { - // arrange - userDAOSpy = jest.spyOn(UserDAO, 'deleteUser') - .mockImplementation(() => new Promise((resolve, reject) => { - resolve(false); - })); - - // act and assert - await expect(UserService.deleteUser("valid@email.com")).resolves - .toEqual(false); - }); - }); - }); -}); - +const sandbox = sinon.createSandbox() + +let userDAOSpy = jest.spyOn(UserDAO, 'createUser') + +describe('Test User Service', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + afterEach(() => { + sandbox.restore() + }) + + afterAll(() => { + process.exit + }) + + describe('US1 - createUser', () => { + describe('US1.1 - given a valid user', () => { + it('US1.1.1 - should return resolved promise with user information when user model works properly', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'createUser') + .mockImplementation(() => new Promise((resolve) => { + resolve(resUserFromService) + })) + const expectedUser = { + email: resUserFromService.email, + name: resUserFromService.name, + role: resUserFromService.role + } + + // act + const serviceResponse = await UserService.createUser(reqUser) + + // assert + expect(serviceResponse).toEqual(expectedUser) + expect(userDAOSpy).toBeCalledTimes(1) + }) + + it('US1.1.2 - should return Dao error', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'createUser') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({ message: 'dao failed' }) + })) + + // act and assert + await expect(UserService.createUser(reqUser)).rejects + .toEqual({ message: 'dao failed' }) + }) + + it('US1.1.2 - should return false', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'createUser') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(false) + })) + + // act and assert + await expect(UserService.createUser(reqUser)).resolves + .toEqual(false) + }) + }) + }) + + describe('US2 - View All Users', () => { + describe('US2.1 - given a list of users', () => { + it('US2.1.1 - Should return the full list of users with all their information', async () => { + // arrange + const ListUser = [] + const listUserLength = 3 + for (let i = 0; i < listUserLength; i++) { + ListUser.push(resUserFromService) + } + userDAOSpy = jest.spyOn(UserDAO, 'getAllUsers') + .mockImplementation(() => new Promise( + (resolve) => { + resolve(ListUser) + } + )) + + // act + const serviceResponse = await UserService.getAllUsers() + + // assert + expect(serviceResponse.length).toBe(listUserLength) + expect(userDAOSpy).toBeCalledTimes(1) + }) + + it('US2.1.2 - Should return a full list of users which are sorted', async () => { + userDAOSpy = jest.spyOn(UserDAO, 'getAllUsers') + .mockImplementation(() => new Promise( + (resolve) => { + resolve(listReqUserUnsorted) + } + )) + + // act + const serviceResponse = await UserService.getAllUsers() + + // assert + expect(serviceResponse).toEqual(listReqUsersorted) + }) + + it('US2.1.3 - should return Dao error', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'getAllUsers') + .mockImplementation(() => new Promise((resolve, reject) => { + reject({ message: 'dao failed' }) + })) + + // act and assert + await expect(UserService.getAllUsers()).rejects + .toEqual({ message: 'dao failed' }) + }) + + it('US2.1.3 - should return false', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'getAllUsers') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(false) + })) + + // act and assert + await expect(UserService.getAllUsers()).resolves + .toEqual(false) + }) + }) + }) + + describe('US3 - Authenticate User', () => { + describe('US3.1 - given a valid user', () => { + it('US3.1.1 - should return Dao error (custom error message)', async () => { + // arrange + const expectedData = { + status: 500, + message: 'dao error' + } + + userDAOSpy = jest.spyOn(UserDAO, 'getUserByEmail') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedData) + })) + + // act and assert + await expect(UserService.authenticateUser(reqUser)).rejects + .toEqual(expectedData) + }) + + it("US3.1.2 - should return 'some error occured' (default error message)", async () => { + // arrange + const errorData = { + status: 500 + } + const expectedData = { + status: 500, + message: 'some error occured' + } + + userDAOSpy = jest.spyOn(UserDAO, 'getUserByEmail') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(errorData) + })) + + // act and assert + await expect(UserService.authenticateUser(reqUser)).rejects + .toEqual(expectedData) + }) + + it('US3.1.3 - should return false', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'getUserByEmail') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(false) + })) + + // act and assert + await expect(UserService.authenticateUser(reqUser)).resolves + .toEqual(false) + }) + + it('US3.1.4 - should return a status 200 with valid data', async () => { + // arrange + const authUser = { + password: 'CoolCool123', + validPassword: () => { return true } + } + + userDAOSpy = jest.spyOn(UserDAO, 'getUserByEmail') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(authUser) + })) + + // act and assert + await expect(UserService.authenticateUser(reqUser)).resolves + .toEqual(authUser) + }) + }) + }) + + describe('US4 - Modify User', () => { + describe('US4.1 - given a valid user', () => { + it('US4.1.1 - Should return a user', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'updateUser') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(reqUser) + })) + + // act + const serviceResponse = await UserService.modifyUser(reqUser) + + // assert + expect(serviceResponse).toEqual(reqUser) + }) + it('US4.1.3 - should return Dao error', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'updateUser') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedData) + })) + + const expectedData = { + status: 500, + message: 'some error occured' + } + + // act and assert + await expect(UserService.modifyUser(reqUser)).rejects + .toEqual(expectedData) + }) + + it('US4.1.3 - should return false', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'updateUser') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(false) + })) + + // act and assert + await expect(UserService.modifyUser(reqUser)).resolves + .toEqual(false) + }) + }) + }) + + describe('US5 - Delete User', () => { + describe('US5.1 - given a valid user', () => { + it('US5.1.1 - Should return a user', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'deleteUser') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(reqUser) + })) + + // act + const serviceResponse = await UserService.deleteUser('valid@email.com') + + // assert + expect(serviceResponse).toEqual(reqUser) + }) + it('US5.1.3 - should return Dao error', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'deleteUser') + .mockImplementation(() => new Promise((resolve, reject) => { + reject(expectedData) + })) + + const expectedData = { + status: 500, + message: 'some error occured' + } + + // act and assert + await expect(UserService.deleteUser('valid@email.com')).rejects + .toEqual(expectedData) + }) + + it('US5.1.3 - should return false', async () => { + // arrange + userDAOSpy = jest.spyOn(UserDAO, 'deleteUser') + .mockImplementation(() => new Promise((resolve, reject) => { + resolve(false) + })) + + // act and assert + await expect(UserService.deleteUser('valid@email.com')).resolves + .toEqual(false) + }) + }) + }) +})