Completely refactored basically everything

This commit is contained in:
Ceferino Patino 2024-02-17 13:23:53 -06:00
commit fb43f05d8a
61 changed files with 19487 additions and 27379 deletions

View file

@ -1,9 +0,0 @@
# syntax=docker/dockerfile:1
FROM node:16-alpine
WORKDIR /app
COPY ["package.json", "./"]
RUN apk update
RUN npm install
COPY . .
EXPOSE 3000/tcp
CMD npm start

15
client/Dockerfile.dev Normal file
View file

@ -0,0 +1,15 @@
# syntax=docker/dockerfile:1
FROM node:16-alpine
WORKDIR /app
COPY ["./package.json", "./package-lock.json*", "./"]
RUN npm install
COPY ["./public", "./public"]
COPY ["./src", "./src"]
EXPOSE 3000/tcp
CMD ["npm", "start"]

15
client/Dockerfile.prod Normal file
View file

@ -0,0 +1,15 @@
# syntax=docker/dockerfile:1
FROM node:16-alpine
WORKDIR /app
COPY ["./package.json", "./package-lock.json*", "./"]
RUN npm install
COPY ["./public", "./public"]
COPY ["./src", "./src"]
EXPOSE 3000/tcp
CMD ["npm", "run" "serve"]

View file

@ -8,9 +8,6 @@
"@mui/icons-material": "^5.4.4",
"@mui/material": "^5.4.4",
"@mui/x-data-grid": "^5.6.0",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^13.5.0",
"axios": "^0.26.1",
"buffer": "^6.0.3",
"js-cookie": "^3.0.1",
@ -20,13 +17,16 @@
"react-helmet": "^6.1.0",
"react-router-dom": "^6.2.2",
"react-scripts": "5.0.0",
"serve": "^14.2.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"eject": "react-scripts eject",
"serve": "serve -s build",
"preserve": "npm run build"
},
"eslintConfig": {
"extends": [
@ -45,5 +45,12 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.1",
"axios-mock-adapter": "^1.22.0",
"jest": "^29.7.0",
"sinon": "^17.0.1"
}
}

View file

@ -1,4 +1,5 @@
import { Link } from 'react-router-dom';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import {
Accordion,
AccordionDetails,
@ -13,9 +14,8 @@ import {
Typography,
useMediaQuery,
} from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import { DataGrid } from '@mui/x-data-grid';
import { Link } from 'react-router-dom';
import { MemberUnit } from '.';
import { DynamicPaper, DynamicStack } from './StyledElements';

View file

@ -1,6 +1,5 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Box, Typography } from '@mui/material';
import { Link } from 'react-router-dom';
import { Form } from '.';
import { RecordMp3 } from '../controllers';

View file

@ -1,7 +1,6 @@
import React from 'react';
import { Outlet } from 'react-router-dom';
import { Box, Grid, Card, Typography, CardContent, Alert } from '@mui/material';
import { Alert, Box, Card, CardContent, Grid, Typography } from '@mui/material';
import { useTheme } from '@mui/material/styles';
import { Outlet } from 'react-router-dom';
function Form({ children, text, error }) {
const theme = useTheme();

View file

@ -1,6 +1,5 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Checkbox, FormControlLabel, Typography } from '@mui/material';
import { Link } from 'react-router-dom';
import { Form } from '.';
import { FormSubmit, FormTextField } from './StyledElements';

View file

@ -1,6 +1,5 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Box, Grid, Stack, Typography } from '@mui/material';
import { Link } from 'react-router-dom';
import OrgUnit from './OrgUnit';
import StackItem from './StackItem';

View file

@ -1,4 +1,5 @@
import React from 'react';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import {
Accordion,
AccordionDetails,
@ -7,8 +8,6 @@ import {
IconButton,
Typography,
} from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
function MemberUnit({ member, onClick }) {
const { id, firstName, lastName, nickname, pronouns, email } = member;

View file

@ -3,8 +3,6 @@ import DarkModeIcon from '@mui/icons-material/DarkMode';
import LightModeIcon from '@mui/icons-material/LightMode';
import LoginIcon from '@mui/icons-material/Login';
import LogoutIcon from '@mui/icons-material/Logout';
import MailIcon from '@mui/icons-material/Mail';
import SearchIcon from '@mui/icons-material/Search';
import MenuIcon from '@mui/icons-material/Menu';
import {
AppBar,

View file

@ -1,6 +1,6 @@
import { Link } from 'react-router-dom';
import { Accordion, AccordionDetails, AccordionSummary, Typography } from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { Accordion, AccordionDetails, AccordionSummary, Typography } from '@mui/material';
import { Link } from 'react-router-dom';
function OrgUnit({ org }) {
const { id, name, memberCount, createdAt } = org;

View file

@ -1,4 +1,3 @@
import React from 'react';
import { Box, Paper, Typography } from '@mui/material';
import { useTheme } from '@mui/material/styles';

View file

@ -1,5 +1,5 @@
import { Typography } from '@mui/material';
import { Link } from 'react-router-dom';
import { Box, Typography } from '@mui/material';
import Form from './Form';
import { FormSubmit, FormTextField } from './StyledElements';

View file

@ -77,4 +77,4 @@ const FormSubmit = ({ color, children, type, other }) => {
);
};
export { Form, FormField, FormButton, DynamicStack, DynamicPaper, FormSubmit, FormTextField };
export { DynamicPaper, DynamicStack, Form, FormButton, FormField, FormSubmit, FormTextField };

View file

@ -1,33 +1,23 @@
import Navbar from './Navbar';
import MobileNavbar from './MobileNavbar';
import LoginForm from './LoginForm';
import MainPageBase from './MainPageBase';
import Dashboard from './Dashboard';
import EditUserForm from './EditUserForm';
import Form from './Form';
import LoginForm from './LoginForm';
import MainPageBase from './MainPageBase';
import MemberUnit from './MemberUnit';
import MobileNavbar from './MobileNavbar';
import Navbar from './Navbar';
import OrgUnit from './OrgUnit';
import StackItem from './StackItem';
import OrganizationList from './OrganizationList';
import RecordMp3 from './RecordMp3';
import RecoverForm from './RecoverForm';
import ResetForm from './ResetForm';
import OrganizationList from './OrganizationList';
import MemberUnit from './MemberUnit';
import Dashboard from './Dashboard';
import StackItem from './StackItem';
import UpdateOrganizationForm from './UpdateOrganizationForm';
export {
Navbar,
MobileNavbar,
LoginForm,
MainPageBase,
EditUserForm,
Form,
OrgUnit,
StackItem,
RecordMp3,
Dashboard, EditUserForm,
Form, LoginForm,
MainPageBase, MemberUnit, MobileNavbar, Navbar, OrgUnit, OrganizationList, RecordMp3,
RecoverForm,
ResetForm,
OrganizationList,
MemberUnit,
Dashboard,
UpdateOrganizationForm,
ResetForm, StackItem, UpdateOrganizationForm
};

View file

@ -1,5 +1,5 @@
import { Box, CssBaseline } from '@mui/material';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { Helmet } from 'react-helmet';
import { Outlet } from 'react-router-dom';

View file

@ -1,19 +1,8 @@
import Cookies from 'js-cookie';
import { useEffect } from 'react';
import Helmet from 'react-helmet';
import { useNavigate } from 'react-router-dom';
import { MainPageDisplay } from '../controllers';
function MainPage() {
const navigate = useNavigate();
useEffect(() => {
if (!Cookies.get('token')) {
navigate('/login');
}
}, []);
return (
<>
<Helmet>

View file

@ -1,25 +1,17 @@
import EditUser from './EditUser';
import Layout from './Layout';
import Login from './Login';
import Register from './Register';
import MainPage from './MainPage';
import EditUser from './EditUser';
import Recover from './Recover';
import Reset from './Reset';
import OwnedOrgs from './OwnedOrgs';
import MemberOrgs from './MemberOrgs';
import OrgDashboard from './OrgDashboard';
import OwnedOrgs from './OwnedOrgs';
import Recover from './Recover';
import Register from './Register';
import Reset from './Reset';
import UpdateOrganization from './UpdateOrganization';
export {
Layout,
Login,
Register,
MainPage,
EditUser,
Recover,
Reset,
OwnedOrgs,
MemberOrgs,
OrgDashboard,
UpdateOrganization,
EditUser, Layout,
Login, MainPage, MemberOrgs,
OrgDashboard, OwnedOrgs, Recover, Register, Reset, UpdateOrganization
};

View file

@ -1,5 +1,5 @@
import { createContext, useContext, useState, useEffect } from 'react';
import Cookies from 'js-cookie';
import { createContext, useContext, useEffect, useState } from 'react';
const ModeContext = createContext();

View file

@ -1,36 +1,43 @@
import { createContext, useContext, useEffect, useState } from 'react';
import Cookies from 'js-cookie';
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Cookies from 'js-cookie';
import { remember } from '../services/userServices';
const initialUserData = { loading: true };
import { createRequest } from '../utils/requests';
const UserContext = createContext();
export default function UserProvider({ children }) {
const [userData, setUserData] = useState(initialUserData);
const [userData, setUserData] = useState({});
const [loading, setLoading] = useState(true);
const navigate = useNavigate();
useEffect(() => {
const loadUserData = async () => {
const { user, error } = await remember();
const loadUserData = useCallback(async () => {
if (!loading) return;
if (error) {
navigate('/login');
Cookies.remove('token');
} else {
setUserData(user || { loading: false });
}
};
if (!Cookies.get('token')) {
setLoading(() => false);
return;
}
if (userData.loading) loadUserData();
}, [userData.loading]);
try {
const instance = createRequest();
const response = await instance.post('/auth/remember');
setUserData(() => response.data.user);
} catch (error) {
Cookies.remove('token');
navigate('/login');
} finally {
setLoading(() => false);
}
}, [navigate, loading]);
useEffect(loadUserData, [loadUserData]);
return (
<UserContext.Provider
value={{
loading,
userData,
setUserData,
}}

View file

@ -1,9 +1,9 @@
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { useCallback, useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { Dashboard } from '../components';
import { getAudio } from '../services/userServices';
import { getOrg, deleteOrg, removeMember } from '../services/orgServices';
import { createRequest } from '../utils/requests';
function DashboardController() {
const { orgID } = useParams();
@ -26,32 +26,33 @@ function DashboardController() {
const [selection, setSelection] = useState(null);
const [open, setOpen] = useState(false);
useEffect(() => {
async function getData() {
await getOrg(orgID)
.then((res) => {
setOrg(() => {
return { ...res.org, memberCount: res.memberCount };
});
const getOrg = useCallback(async () => {
try {
const instance = createRequest();
const response = await instance.get(`/org/${orgID}`);
setRows(() => res.org.member);
setStatus(() => res.owner);
})
.catch((e) => {
console.log(e);
navigate('/');
});
const orgData = { ...response.data.org, memberCount: response.data.memberCount };
setOrg(() => orgData);
setRows(() => response.data.org.member);
setStatus(() => response.data.owner);
} catch (error) {
navigate('/');
}
getData();
}, [navigate, orgID]);
useEffect(getOrg, [getOrg]);
function handleOpen() {
setOpen((initial) => (initial ? false : true));
}
async function handleDelete() {
await deleteOrg(orgID).then(navigate('/'));
try {
const instance = createRequest();
await instance.delete(`/org/${orgID}`);
navigate('/');
} catch (error) {}
}
function copyID() {
@ -67,22 +68,33 @@ function DashboardController() {
async function removeSelected() {
const doomedUserIDs = selection.map((row) => row.id);
await removeMember(orgID, doomedUserIDs);
try {
const instance = createRequest();
await instance.post(`/org/${orgID}/remove`, { doomedUserIDs });
} catch (error) {}
}
async function leaveOrg() {
await removeMember(orgID, {}).then(navigate('/'));
try {
const instance = createRequest();
await instance.post(`/org/${orgID}/remove`, {});
navigate('/');
} catch (error) {}
}
async function play(id) {
await getAudio(id).then((res) => {
const uploadedFile = new File([res], 'userAudio.mp3', {
type: res.type,
try {
const request = await axios.get(
`${process.env.REACT_APP_DOMAIN_ROOT}/public/audio/${id}.mp3`,
{ responseType: 'blob' }
);
const uploadedFile = new File([request.data], 'userAudio.mp3', {
type: request.data.type,
lastModified: Date.now(),
});
const audioFile = new Audio(URL.createObjectURL(uploadedFile));
audioFile.play();
});
} catch (error) {}
}
return (

View file

@ -1,9 +1,9 @@
import { useState, useEffect } from 'react';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { EditUserForm } from '../components';
import { useUser } from '../contexts';
import { updateUserDetails } from '../services/userServices';
import { createRequest } from '../utils/requests';
function EditUserFormController() {
const navigate = useNavigate();
@ -22,31 +22,28 @@ function EditUserFormController() {
const name = e.target.name;
const value = e.target.value;
setForm((form) => {
return { ...form, [name]: value };
});
setForm((form) => ({ ...form, [name]: value }));
}
async function handleSubmit(e) {
e.preventDefault();
const { error } = await updateUserDetails(form);
if (error) {
setError(() => error);
} else {
try {
const instance = createRequest();
await instance.post('/auth/update', form);
navigate('/');
} catch (error) {
setError(() => error.response.data);
}
}
useEffect(() => {
if (!userData.loading) {
setForm(() => ({
firstName: userData.firstName,
lastName: userData.lastName,
email: userData.email,
pronouns: userData.pronouns,
}));
}
setForm(() => ({
firstName: userData.firstName,
lastName: userData.lastName,
email: userData.email,
pronouns: userData.pronouns,
}));
}, [userData]);
return (

View file

@ -1,13 +1,16 @@
import { Buffer } from 'buffer';
import Cookies from 'js-cookie';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { LoginForm } from '../components';
import { useError } from '../contexts';
import { login } from '../services/userServices';
import { useError, useUser } from '../contexts';
import { createRequest } from '../utils/requests';
function LoginFormController({ setToken }) {
const navigate = useNavigate();
const { error, setError } = useError();
const { setUserData } = useUser();
const [form, setForm] = useState({
email: '',
@ -36,12 +39,21 @@ function LoginFormController({ setToken }) {
async function handleSubmit(e) {
e.preventDefault();
const { error } = await login(form);
const encoded = Buffer.from(`${form.email}:${form.password}`).toString('base64');
const instance = createRequest();
instance.defaults.headers.common['Authorization'] = `basic ${encoded}`;
try {
const response = await instance.post('/auth/login', { remember: form.remember });
const expires = form.remember ? 365 : 1;
setUserData(() => response.data.user);
Cookies.set('token', response.data.token, { expires });
if (error) {
setError(() => error);
} else {
navigate('/');
} catch (error) {
setError(() => error.response.data);
}
}

View file

@ -1,14 +1,9 @@
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Cookies from 'js-cookie';
import { useCallback, useEffect, useState } from 'react';
import { MainPageBase } from '../components';
import { getMemberOrgs, getOwnedOrgs } from '../services/userServices';
import { createOrg, addMember } from '../services/orgServices';
import { createRequest } from '../utils/requests';
function MainPageController() {
const navigate = useNavigate();
const [ownedOrgs, setOwnedOrgs] = useState([]);
const [orgs, setOrgs] = useState([]);
const [form, setForm] = useState({
@ -16,14 +11,26 @@ function MainPageController() {
id: '',
});
const getOwnedOrgs = useCallback(async () => {
try {
const instance = createRequest();
const response = await instance.get('/auth/orgs');
setOwnedOrgs(() => response.data.orgs);
} catch (error) {}
}, []);
const getMemberOrgs = useCallback(async () => {
try {
const instance = createRequest();
const response = await instance.get('/auth/orgs/member');
setOrgs(() => response.data.orgs);
} catch (error) {}
}, []);
useEffect(() => {
if (Cookies.get('token')) {
retrieveMemberOrgs();
retrieveOwnedOrgs();
} else {
navigate('/login');
}
}, [navigate]);
getOwnedOrgs();
getMemberOrgs();
}, [getOwnedOrgs, getMemberOrgs]);
function handleChange(e) {
const name = e.target.name;
@ -34,30 +41,16 @@ function MainPageController() {
});
}
async function retrieveOwnedOrgs() {
const { orgs } = await getOwnedOrgs().catch((e) => console.log(e));
setOwnedOrgs(() => orgs);
}
async function retrieveMemberOrgs() {
const { orgs } = await getMemberOrgs().catch((e) => console.log(e));
setOrgs(() => orgs);
}
async function makeOrg(e) {
e.preventDefault();
await createOrg({ orgName: form.name })
.then(retrieveOwnedOrgs)
.catch((e) => console.log(e));
const instance = createRequest();
instance.post('/org/create', form).then(getOwnedOrgs);
}
async function joinOrg(e) {
e.preventDefault();
await addMember.then(retrieveMemberOrgs).catch((e) => console.log(e));
const instance = createRequest();
instance.post(`/org/${form.id}/add`, {}).then(getMemberOrgs);
}
return (

View file

@ -1,18 +1,20 @@
import React, { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { OrganizationList } from '../components';
import { getMemberOrgs } from '../services/userServices';
import { createRequest } from '../utils/requests';
function MemberOrgsDashboard() {
const [orgs, setOrgs] = useState([]);
async function getOrgs() {
getMemberOrgs().then((res) => {
setOrgs(() => res.orgs);
});
}
const getOrgs = useCallback(async () => {
try {
const instance = createRequest();
const response = await instance.get('/auth/orgs');
setOrgs(() => response.data.orgs);
} catch (error) {}
}, []);
useEffect(getOrgs);
useEffect(getOrgs, [getOrgs]);
return <OrganizationList orgs={orgs} />;
}

View file

@ -5,7 +5,7 @@ import { useNavigate } from 'react-router-dom';
import { MobileNavbar, Navbar } from '../components';
import { useMode, useUser } from '../contexts';
import { logout } from '../services/userServices';
import { createRequest } from '../utils/requests';
export default function NavbarController() {
const navigate = useNavigate();
@ -17,7 +17,6 @@ export default function NavbarController() {
const toggleOpen = () => setOpen((initial) => !initial);
const toggleMode = () => {
console.log('Changed mode');
setMode(() => (mode === 'light' ? 'dark' : 'light'));
};
@ -25,10 +24,15 @@ export default function NavbarController() {
const md = useMediaQuery((theme) => theme.breakpoints.only('md'));
const logoutUser = async () => {
await logout();
Cookies.remove('token');
setUserData({});
navigate('/login');
try {
const instance = createRequest();
await instance.post('/auth/logout');
} catch (error) {
} finally {
Cookies.remove('token');
setUserData({});
navigate('/login');
}
};
return md || sm

View file

@ -1,18 +1,20 @@
import React, { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { OrganizationList } from '../components';
import { getOwnedOrgs } from '../services/userServices';
import { createRequest } from '../utils/requests';
function OwnedOrgsDashboard() {
const [orgs, setOrgs] = useState([]);
async function getOrgs() {
getOwnedOrgs().then((res) => {
setOrgs(() => res.orgs);
});
}
const getOrgs = useCallback(async () => {
try {
const instance = createRequest();
const response = await instance.get('/auth/orgs/member');
setOrgs(() => response.data.orgs);
} catch (error) {}
}, []);
useEffect(getOrgs);
useEffect(getOrgs, [getOrgs]);
return <OrganizationList orgs={orgs} />;
}

View file

@ -1,11 +1,10 @@
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import Cookies from 'js-cookie';
import MicRecorder from 'mic-recorder-to-mp3';
import { useCallback, useEffect, useState } from 'react';
import { useUser } from '../contexts';
import { RecordMp3 } from '../components';
import { setAudio, getAudio } from '../services/userServices';
import { useUser } from '../contexts';
import { createRequest } from '../utils/requests';
function RecordMp3Controller() {
const { userData } = useUser();
@ -19,28 +18,29 @@ function RecordMp3Controller() {
const [uploadedFile, setUploadedFile] = useState(null);
const [audioRecording, setAudioRecording] = useState(null);
useEffect(() => {
if (!userData.loading) {
getAudio(userData.id).then((data) => {
setUploadedFile(() => {
// Retrieve audio file of user if exists
return new File([data], 'userAudio.mp3', {
type: data.type,
lastModified: Date.now(),
});
const getData = useCallback(async () => {
try {
const response = await axios.get(
`${process.env.REACT_APP_DOMAIN_ROOT}/public/audio/${userData.id}.mp3`,
{ responseType: 'blob' }
);
setUploadedFile(() => {
return new File([response.data], 'userAudio.mp3', {
type: response.data.type,
lastModified: Date.now(),
});
});
}
}, [userData]);
} catch (error) {}
}, [userData.id]);
useEffect(getData, [getData]);
function togglePlay() {
if (audioRecording && !audioRecording.ended) {
// Reset audio and reset if track is playing
audioRecording.pause();
audioRecording.currentTime = 0;
setAudioRecording(() => null);
} else {
// Set audio to file and play
const audioFile = new Audio(URL.createObjectURL(uploadedFile));
setAudioRecording(() => audioFile);
audioFile.play();
@ -49,7 +49,6 @@ function RecordMp3Controller() {
function record() {
if (recording) {
// Stop recording then set uploaded file to mp3 instance
setRecording(() => false);
recorder
.stop()
@ -63,11 +62,8 @@ function RecordMp3Controller() {
});
});
} else {
// Start recording
setRecording(() => true);
recorder.start().catch((e) => {
console.error(e);
});
recorder.start();
}
}
@ -80,12 +76,13 @@ function RecordMp3Controller() {
async function handleSubmit(e) {
e.preventDefault();
// Create form data object and attach file and user token
var formData = new FormData();
formData.append('audioFile', uploadedFile, 'userAudio.mp3');
formData.append('token', Cookies.get('token'));
await setAudio(formData);
const instance = createRequest();
instance.defaults.headers.common['Content-Type'] = 'multipart/form-data';
return instance.post('/auth/audio', formData);
}
return <RecordMp3 {...{ togglePlay, record, uploadRecord, handleSubmit }} />;

View file

@ -1,12 +1,10 @@
import React, { useState } from 'react';
import { RecoverForm } from '../components';
import { requestReset } from '../services/userServices';
import { createRequest } from '../utils/requests';
function RecoverFormController() {
const [form, setForm] = useState({
email: '',
});
const [form, setForm] = useState({});
const [error, setError] = useState('');
function handleChange(e) {
@ -20,7 +18,13 @@ function RecoverFormController() {
async function handleSubmit(e) {
e.preventDefault();
await requestReset(form);
try {
const instance = createRequest();
await instance.post('/auth/reset', form);
} catch (error) {
setError(() => error.response.data);
}
}
return (

View file

@ -3,7 +3,7 @@ import { useNavigate, useSearchParams } from 'react-router-dom';
import RegisterForm from '../components/RegisterForm';
import { useError } from '../contexts';
import { register } from '../services/userServices';
import { createRequest } from '../utils/requests';
function RegisterFormController() {
const navigate = useNavigate();
@ -32,12 +32,14 @@ function RegisterFormController() {
e.preventDefault();
const { confirmPassword, ...filteredForm } = form;
const { error } = await register({ ...filteredForm, orgID: params.get('orgID') });
const fullForm = { ...filteredForm, orgID: params.get('orgID') };
if (error) {
setError(() => error);
} else {
try {
const instance = createRequest();
await instance.post('/auth/register', fullForm);
navigate('/login');
} catch (error) {
setError(() => error.response.data);
}
}

View file

@ -1,9 +1,9 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { ResetForm } from '../components';
import { resetPassword } from '../services/userServices';
import { useError } from '../contexts';
import { createRequest } from '../utils/requests';
function ResetFormController() {
const navigate = useNavigate();
@ -27,12 +27,12 @@ function ResetFormController() {
async function handleSubmit(e) {
e.preventDefault();
const error = await resetPassword(id, { password: form.password });
if (error) {
setError(() => error);
} else {
try {
const instance = createRequest();
await instance.patch(`/reset/${id}`);
navigate('/login');
} catch (error) {
setError(() => error.response.data);
}
}

View file

@ -1,8 +1,8 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { UpdateOrganizationForm } from '../components';
import { updateOrgDetails } from '../services/orgServices';
import { createRequest } from '../utils/requests';
function UpdateOrganizationFormController() {
const { orgID } = useParams();
@ -18,10 +18,9 @@ function UpdateOrganizationFormController() {
async function handleSubmit(e) {
e.preventDefault();
await updateOrgDetails(orgID, { orgName: name }).then(() => {
navigate(`/org/${orgID}`);
});
const instance = createRequest();
await instance.patch(`/org/${orgID}`, { orgName: name });
navigate(`/org/${orgID}`);
}
return (

View file

@ -1,79 +0,0 @@
import Cookies from 'js-cookie';
import { createRequest } from './requests';
async function addMember(id) {
const instance = createRequest(`/org`);
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
await instance.post(`/${id}/add`);
} catch (error) {
console.log(error.response.data);
return error;
}
}
async function createOrg(data) {
const instance = createRequest('/org');
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
await instance.post('/', data);
} catch (error) {
console.log(error.response.data);
return error;
}
}
async function deleteOrg(id) {
const instance = createRequest('/org');
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
await instance.delete(`/${id}`);
} catch (error) {
console.log(error.response.data);
return error;
}
}
async function getOrg(id) {
const instance = createRequest('/org');
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
const response = await instance.get(`/${id}`);
console.log(response.data);
return response.data;
} catch (error) {
console.log(error.response.data);
return error;
}
}
async function removeMember(id, doomedUsers) {
const instance = createRequest('/org');
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
await instance.post(`/${id}/remove`, { doomedUsers });
} catch (error) {
console.log(error.response.data);
return error;
}
}
async function updateOrgDetails(id, data) {
const instance = createRequest('/org');
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
await instance.patch(`/${id}`, { data });
} catch (error) {
console.log(error.response.data);
return error;
}
}
export { addMember, createOrg, deleteOrg, getOrg, removeMember, updateOrgDetails };

View file

@ -1,13 +0,0 @@
import axios from 'axios';
function createRequest(path) {
var request = axios.create({
baseURL: `${process.env.REACT_APP_API_ROOT}${path}`,
});
request.defaults.headers.common['Content-Type'] = 'application/json';
return request;
}
export { createRequest };

View file

@ -1,180 +0,0 @@
import Cookies from 'js-cookie';
import { Buffer } from 'buffer';
import { createRequest } from './requests';
import axios from 'axios';
async function login({ email, password, remember }) {
const encoded = Buffer.from(`${email}:${password}`).toString('base64');
const instance = createRequest('/auth');
instance.defaults.headers.common['Authorization'] = `basic ${encoded}`;
try {
const { data } = await instance.post('/login', { remember });
if (remember) {
Cookies.set('token', data.token, { expires: 365 });
} else {
Cookies.set('token', data.token, { expires: 1 });
}
return data;
} catch (error) {
console.log(error.response.data);
return error;
}
}
async function register(data) {
const instance = createRequest('/auth');
try {
await instance.post('/register', data);
} catch (error) {
console.log(error.response.data);
return error;
}
}
async function logout() {
const instance = createRequest('/auth');
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
await instance.post('/logout');
} catch (error) {
console.log(error.response.data);
return error;
}
}
async function remember() {
const instance = createRequest('/auth');
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
if (Cookies.get('token')) {
try {
const { data } = await instance.post('/remember');
return data;
} catch (error) {
console.log(error.response.data);
return error.response.data;
}
}
}
async function getUser(userID) {
const instance = createRequest('/auth');
try {
const { data } = await instance.get(`/${userID}`);
return data.user;
} catch (error) {
console.log(error.response.data);
return error.response.data;
}
}
async function requestReset(data) {
const instance = createRequest('/auth');
try {
await instance.post(`/reset`, data);
} catch (error) {
console.log(error.response.data);
return error.response.data;
}
}
async function resetPassword(id, data) {
const instance = createRequest('/auth');
try {
await instance.patch(`/reset/${id}`, data);
} catch (error) {
console.log(error.response.data);
return error.response.data;
}
}
async function setAudio(data) {
const instance = createRequest('/auth');
instance.defaults.headers.common['Content-Type'] = 'multipart/form-data';
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
await instance.post('/audio', data);
} catch (error) {
console.log(error.response.data);
return error.response.data;
}
}
async function getAudio(userID) {
const response = await axios.get(
`${process.env.REACT_APP_DOMAIN_ROOT}/public/audio/${userID}.mp3`,
{
responseType: 'blob',
}
);
return response.data;
}
async function updateUserDetails(data) {
const instance = createRequest('/auth');
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
await instance.post(`/update`, data);
} catch (error) {
console.log(error.response.data);
return error.response.data;
}
}
async function getMemberOrgs() {
const instance = createRequest('/auth');
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
const { data } = await instance.get(`/orgs/member`);
return data;
} catch (error) {
console.log(error.response.data);
return error.response.data;
}
}
async function getOwnedOrgs() {
const instance = createRequest('/auth');
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
try {
const { data } = await instance.get(`/orgs`);
return data;
} catch (error) {
console.log(error.response.data);
return error.response.data;
}
}
export {
login,
logout,
register,
remember,
getUser,
requestReset,
resetPassword,
setAudio,
getAudio,
updateUserDetails,
getMemberOrgs,
getOwnedOrgs,
};

View file

@ -0,0 +1,15 @@
import axios from 'axios';
import Cookies from 'js-cookie';
function createRequest() {
var request = axios.create({
baseURL: `${process.env.REACT_APP_API_ROOT}`,
});
request.defaults.headers.common['Content-Type'] = 'application/json';
request.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`
return request;
}
export { createRequest };

59
docker-compose.prod.yml Normal file
View file

@ -0,0 +1,59 @@
version: '3.8'
services:
client:
restart: unless-stopped
build:
context: ./client
ports:
- 3000:3000
volumes:
- ./client:/app
- client-node:/app/node_modules
networks:
- know-it-all
env_file: .env
environment:
WATCHPACK_POLLING: true
depends_on:
- server
server:
restart: unless-stopped
build:
context: ./server
ports:
- 3000:3001
volumes:
- ./server:/app
- server-node:/app/node_modules
networks:
- know-it-all
env_file: .env
environment:
DB_CONN_STRING: 'mongodb://mongodb:27017'
CHOKIDAR_USEPOLLING: true
depends_on:
- mysqldb
mysqldb:
restart: unless-stopped
image: mysql:5.7
ports:
- 3306:3306
volumes:
- know-it-all-mysql:/var/lib/mysql
networks:
- know-it-all
environment:
MYSQL_ROOT_PASSWORD: 'passw0rd'
MYSQL_DATABASE: dev
healthcheck:
test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost']
timeout: 5s
retries: 10
volumes:
know-it-all-mysql:
client-node:
server-node:
networks:
know-it-all: {}

View file

@ -5,38 +5,40 @@ services:
restart: unless-stopped
build:
context: ./client
dockerfile: Dockerfile.dev
ports:
- 3000:3000
volumes:
- ./client:/app
- client-node:/app/node_modules
- /app/node_modules
networks:
- know-it-all
env_file: ./client/.env
environment:
REACT_APP_API_ROOT: 'http://localhost:3001/api'
REACT_APP_DOMAIN_ROOT: 'http://localhost:3000'
WATCHPACK_POLLING: true
depends_on:
- server
server:
restart: unless-stopped
build:
context: ./server
context: ./server/Dockerfile
ports:
- 3001:3001
- 3001:3000
volumes:
- ./server:/app
- server-node:/app/node_modules
- /app/node_modules
networks:
- know-it-all
env_file: ./server/.env
environment:
DB_CONN_STRING: 'mongodb://mongodb:27017'
CHOKIDAR_USEPOLLING: true
NODE_ENV: development
MYSQL_HOST: mysqldb
MYSQL_USER: root
MYSQL_PASSWORD: passw0rd
MYSQL_DATABASE: dev
depends_on:
- mysqldb
mysqldb:
restart: unless-stopped
image: mysql:5.7
image: mysql:8.3
ports:
- 3306:3306
volumes:
@ -53,7 +55,5 @@ services:
volumes:
know-it-all-mysql:
client-node:
server-node:
networks:
know-it-all: {}

34869
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,13 +7,9 @@
"start": "concurrently \"npm run start -w client\" \"npm run start -w server\"",
"start:server": "npm start server",
"build": "npm run build -w client",
"build:package": "tar --exclude='.env' -czf dist.tar.gz client node_modules server LICENSE package-lock.json package.json",
"dist": "npm run build:package",
"test": "npm run test -w server",
"test": "npm run test -ws",
"migrate": "npm run migrate -w server",
"migrate:undo": "npm run migrate:undo -w server",
"predev": "npm run build",
"predist": "npm ci && npm run build"
"migrate:undo": "npm run migrate:undo -w server"
},
"author": "C4 Patino",
"license": "ISC",
@ -28,7 +24,9 @@
"js-cookie": "^3.0.1"
},
"devDependencies": {
"client": "^0.1.0",
"concurrently": "^7.6.0",
"remove": "^0.1.5",
"sequelize-cli": "^6.4.1"
}
}

View file

@ -4,5 +4,5 @@ WORKDIR /app
COPY ["package.json", "./"]
RUN npm install
COPY . .
EXPOSE 3001/tcp
EXPOSE 3000/tcp
CMD npm run start

View file

@ -3,7 +3,6 @@ const formidable = require('formidable');
function parseFormData(req, res, next) {
// Parse multipart form data into formidable object and set static file save location
var form = formidable();
form.uploadDir = `${__dirname}/../public/`;
form.parse(req, (err, fields, files) => {
req.body = fields;

View file

@ -30,6 +30,7 @@
"devDependencies": {
"jest": "^27.5.1",
"nodemon": "^2.0.15",
"sinon": "^17.0.1",
"supertest": "^6.2.2"
},
"jest": {

View file

@ -5,13 +5,15 @@ function SetAudio(req, res, next) {
const oldLocation = req.files.audioFile.filepath;
// Set new auto file location to static asset folder
const uploadDir = `${__dirname}/../../public/audio`;
const fileName = `${user.id}.mp3`;
const newLocation = `${uploadDir}/${fileName}`;
// Move file from public to folder
fs.rename(oldLocation, newLocation, (err) => {
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
fs.copyFile(oldLocation, newLocation, (err) => {
if (err) console.log(err);
});

View file

@ -16,7 +16,7 @@ describe('Get Member Orgs', function () {
}
});
test('[200] Retrieved joined orgs', async () => {
it('[200] Retrieved joined orgs', async () => {
var org = await createTestOrg('Test Org', {
firstName: 'Test',
lastName: 'User',
@ -45,7 +45,7 @@ describe('Get Member Orgs', function () {
});
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.get('/api/auth/orgs/member')
.send()
@ -53,7 +53,7 @@ describe('Get Member Orgs', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.get('/api/auth/orgs/member')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Get Owned Orgs', function () {
}
});
test('[200] Retrieved owned orgs', async () => {
it('[200] Retrieved owned orgs', async () => {
const user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken({ expires: true });
const org = await user.createOwnedOrg({ name: 'Org' });
@ -41,7 +41,7 @@ describe('Get Owned Orgs', function () {
});
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.get('/api/auth/orgs')
.send()
@ -49,7 +49,7 @@ describe('Get Owned Orgs', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.get('/api/auth/orgs')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Get User Details', function () {
}
});
test('[200] Successfully recieved user details', async () => {
it('[200] Successfully recieved user details', async () => {
const token = await createTestToken({
firstName: 'Test',
lastName: 'User',
@ -40,7 +40,7 @@ describe('Get User Details', function () {
});
});
test('[404] User ID does not exist', async () => {
it('[404] User ID does not exist', async () => {
const token = await createTestToken({
firstName: 'Test',
lastName: 'User',
@ -55,7 +55,7 @@ describe('Get User Details', function () {
.expect(404, errors.NotFound);
});
test('[403] No contact with that user', async () => {
it('[403] No contact with that user', async () => {
const token = await createTestToken({
firstName: 'Test',
lastName: 'User',
@ -71,7 +71,7 @@ describe('Get User Details', function () {
.expect(403, errors.Forbidden);
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.get(`/api/auth/randomString`)
.send()
@ -79,7 +79,7 @@ describe('Get User Details', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.get('/api/auth/randomString')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Login', function () {
}
});
test('[200] Successful login', async () => {
it('[200] Successful login', async () => {
const password = 'password';
const user = await createTestUser('Test', 'User', password);
const encoded = Buffer.from(`${user.email}:${password}`).toString('base64');
@ -35,7 +35,7 @@ describe('Login', function () {
});
});
test('[400] Request missing fields', async () => {
it('[400] Request missing fields', async () => {
await supertest(app)
.post('/api/auth/login')
.send()
@ -43,7 +43,7 @@ describe('Login', function () {
.expect(400, errors.Incomplete);
});
test('[403] Invalid password', async () => {
it('[403] Invalid password', async () => {
const password = 'password';
const user = await createTestUser('Test', 'User', password);
const encoded = Buffer.from(`${user.email}:randomString`).toString('base64');

View file

@ -16,7 +16,7 @@ describe('Logout', function () {
}
});
test('[200] Successful logout', async () => {
it('[200] Successful logout', async () => {
const token = await createTestToken({
firstName: 'Test',
lastName: 'User',
@ -31,7 +31,7 @@ describe('Logout', function () {
.expect(200, 'OK');
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.post('/api/auth/logout')
.send()
@ -39,7 +39,7 @@ describe('Logout', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.post('/api/auth/logout')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Register', function () {
}
});
test('[200] User successfully created', async () => {
it('[200] User successfully created', async () => {
const user = {
firstName: 'Test',
lastName: 'User',
@ -32,7 +32,7 @@ describe('Register', function () {
.expect(200, 'OK');
});
test('[200] Created with organization', async () => {
it('[200] Created with organization', async () => {
const orgOwner = {
firstName: 'Org',
lastName: 'Owner',
@ -55,7 +55,7 @@ describe('Register', function () {
.expect(200, 'OK');
});
test('[200] Nonexistent orgID does not error', async () => {
it('[200] Nonexistent orgID does not error', async () => {
const orgOwner = {
firstName: 'Org',
lastName: 'Owner',
@ -78,7 +78,7 @@ describe('Register', function () {
.expect(200, 'OK');
});
test('[400] Request missing fields', async () => {
it('[400] Request missing fields', async () => {
await supertest(app)
.post('/api/auth/register')
.send()
@ -86,7 +86,7 @@ describe('Register', function () {
.expect(400, errors.Incomplete);
});
test('[409] User already exists', async () => {
it('[409] User already exists', async () => {
await createTestUser('Test', 'User', 'password');
const userInfo = {
firstName: 'Test',

View file

@ -1,4 +1,6 @@
const supertest = require('supertest');
const sinon = require('sinon');
const { CourierClient } = require('@trycourier/courier');
const { sequelize } = require('../../db/models/index');
const app = require('../../app');
@ -6,6 +8,7 @@ const app = require('../../app');
const { createTestUser } = require('../utils');
const errors = require('../../config/error.json');
describe('Request Reset', function () {
beforeEach(async () => {
try {
@ -16,17 +19,7 @@ describe('Request Reset', function () {
}
});
test('[200] Successful reset request', async () => {
const user = await createTestUser('Test', 'User', 'password');
await supertest(app)
.post('/api/auth/reset')
.send({ email: user.email })
.expect('Content-Type', /text/)
.expect(200, 'OK');
});
test('[400] Request missing email', async () => {
it('[400] Request missing email', async () => {
await supertest(app)
.post('/api/auth/reset')
.send()
@ -34,7 +27,7 @@ describe('Request Reset', function () {
.expect(400, errors.Incomplete);
});
test('[404] No user with email', async () => {
it('[404] No user with email', async () => {
await createTestUser('Test', 'User', 'password');
await supertest(app)

View file

@ -16,7 +16,7 @@ describe('Reset Password', function () {
}
});
test('[200] Successful reset password', async () => {
it('[200] Successful reset password', async () => {
const resetRequest = await createTestResetRequest({
firstName: 'Test',
lastName: 'User',
@ -32,7 +32,7 @@ describe('Reset Password', function () {
.expect(200, 'OK');
});
test('[400] Missing password', async () => {
it('[400] Missing password', async () => {
const resetRequest = await createTestResetRequest({
firstName: 'Test',
lastName: 'User',
@ -46,7 +46,7 @@ describe('Reset Password', function () {
.expect(400, errors.Incomplete);
});
test('[500] Reset request does not exist', async () => {
it('[500] Reset request does not exist', async () => {
await supertest(app)
.patch(`/api/auth/reset/randomString`)
.send({

View file

@ -15,7 +15,7 @@ describe('Set Audio', function () {
}
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.post('/api/auth/audio')
.send()
@ -23,7 +23,7 @@ describe('Set Audio', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.post('/api/auth/audio')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Update User Details', function () {
}
});
test('[200] Successfully changed user details', async () => {
it('[200] Successfully changed user details', async () => {
const token = await createTestToken({
firstName: 'Test',
lastName: 'User',
@ -35,7 +35,7 @@ describe('Update User Details', function () {
.expect(200, 'OK');
});
test('[409] Pre-existing user with email', async () => {
it('[409] Pre-existing user with email', async () => {
const token = await createTestToken({
firstName: 'Test',
lastName: 'User',
@ -53,7 +53,7 @@ describe('Update User Details', function () {
.expect(409, errors.DuplicateName);
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.patch('/api/auth/update')
.send()
@ -61,7 +61,7 @@ describe('Update User Details', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.patch('/api/auth/update')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Add Org Member', function () {
}
});
test('[200] User added to organization', async () => {
it('[200] User added to organization', async () => {
var org = await createTestOrg('Org', {
firstName: 'New',
lastName: 'User',
@ -33,7 +33,7 @@ describe('Add Org Member', function () {
.expect(200, 'OK');
});
test('[404] Organization not found', async () => {
it('[404] Organization not found', async () => {
const token = await createTestToken({
firstName: 'Test',
lastName: 'User',
@ -48,7 +48,7 @@ describe('Add Org Member', function () {
.expect(404, errors.NotFound);
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.post('/api/org/randomString/add')
.send()
@ -56,7 +56,7 @@ describe('Add Org Member', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.post('/api/org/randomString/add')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Create Org', function () {
}
});
test('[200] Organization created', async () => {
it('[200] Organization created', async () => {
var user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken();
@ -28,7 +28,7 @@ describe('Create Org', function () {
.expect(200, 'OK');
});
test('[400] Form missing orgName', async () => {
it('[400] Form missing orgName', async () => {
var user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken();
@ -40,7 +40,7 @@ describe('Create Org', function () {
.expect(400, errors.Incomplete);
});
test('[409] Pre-existing organization with that name', async () => {
it('[409] Pre-existing organization with that name', async () => {
var user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken();
await user.createOwnedOrg({
@ -55,7 +55,7 @@ describe('Create Org', function () {
.expect(409, errors.DuplicateName);
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.post('/api/org')
.send()
@ -63,7 +63,7 @@ describe('Create Org', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.post('/api/org')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Delete Org', function () {
}
});
test('[200] Organization deleted', async () => {
it('[200] Organization deleted', async () => {
const user = await createTestUser('Test', 'User', 'password');
const org = await user.createOwnedOrg({
name: 'Org',
@ -35,7 +35,7 @@ describe('Delete Org', function () {
});
});
test('[200] No organization deleted with that ID', async () => {
it('[200] No organization deleted with that ID', async () => {
const user = await createTestUser('Test', 'User', 'password');
const org = await user.createOwnedOrg({
name: 'Org',
@ -54,7 +54,7 @@ describe('Delete Org', function () {
});
});
test('[200] Organization not owned by that user', async () => {
it('[200] Organization not owned by that user', async () => {
const user = await createTestUser('Test', 'User', 'password');
const newUser = await createTestUser('New', 'User', 'password');
const org = await user.createOwnedOrg({
@ -74,7 +74,7 @@ describe('Delete Org', function () {
});
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.delete('/api/org/randomString')
.send()
@ -82,7 +82,7 @@ describe('Delete Org', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.delete('/api/org/randomString')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Get Org', function () {
}
});
test('[200] Organization retrieved', async () => {
it('[200] Organization retrieved', async () => {
const user = await createTestUser('Test', 'User', 'password');
const org = await user.createOwnedOrg({
name: 'Org',
@ -52,7 +52,7 @@ describe('Get Org', function () {
});
});
test('[404] No organization with that id', async () => {
it('[404] No organization with that id', async () => {
const user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken();
@ -64,7 +64,7 @@ describe('Get Org', function () {
.expect(404, errors.NotFound);
});
test('[403] Unknown organization queried', async () => {
it('[403] Unknown organization queried', async () => {
const org = await createTestOrg('Org', {
firstName: 'New',
lastName: 'User',
@ -81,7 +81,7 @@ describe('Get Org', function () {
.expect(403, errors.Forbidden);
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.get('/api/org/randomString')
.send()
@ -89,7 +89,7 @@ describe('Get Org', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.get('/api/org/randomString')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Remove Org Member', function () {
}
});
test('[200] User removed from organization', async () => {
it('[200] User removed from organization', async () => {
const user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken();
const org = await user.createOwnedOrg({
@ -43,7 +43,7 @@ describe('Remove Org Member', function () {
});
});
test('[404] Organization does not exist', async () => {
it('[404] Organization does not exist', async () => {
const user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken();
const org = await user.createOwnedOrg({
@ -63,7 +63,7 @@ describe('Remove Org Member', function () {
.expect(404, errors.NotFound);
});
test('[403] Insufficient permissions', async () => {
it('[403] Insufficient permissions', async () => {
const org = await createTestOrg('Org', {
firstName: 'Test',
lastName: 'User',
@ -84,7 +84,7 @@ describe('Remove Org Member', function () {
.expect(403, errors.Forbidden);
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.post('/api/org/randomString/remove')
.send()
@ -92,7 +92,7 @@ describe('Remove Org Member', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.post('/api/org/randomString/remove')
.set('Authorization', 'bearer randomString')

View file

@ -16,7 +16,7 @@ describe('Update Org Details', function () {
}
});
test('[200] Successfully changed org name', async () => {
it('[200] Successfully changed org name', async () => {
const user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken();
const org = await user.createOwnedOrg({
@ -33,7 +33,7 @@ describe('Update Org Details', function () {
.expect(200, 'OK');
});
test('[400] Form missing information', async () => {
it('[400] Form missing information', async () => {
const user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken();
@ -44,7 +44,7 @@ describe('Update Org Details', function () {
.expect(400, errors.Incomplete);
});
test('[404] No organization exists with that id', async () => {
it('[404] No organization exists with that id', async () => {
const user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken();
@ -58,7 +58,7 @@ describe('Update Org Details', function () {
.expect(404, errors.NotFound);
});
test('[409] Pre-existing organization with that name', async () => {
it('[409] Pre-existing organization with that name', async () => {
const user = await createTestUser('Test', 'User', 'password');
const token = await user.createToken();
await user.createOwnedOrg({ name: 'Org' });
@ -74,7 +74,7 @@ describe('Update Org Details', function () {
.expect(409, errors.DuplicateName);
});
test('[400] Request does not include token', async () => {
it('[400] Request does not include token', async () => {
await supertest(app)
.patch('/api/org/randomString')
.send()
@ -82,7 +82,7 @@ describe('Update Org Details', function () {
.expect(400, errors.Incomplete);
});
test('[401] Token was not found', async () => {
it('[401] Token was not found', async () => {
await supertest(app)
.patch('/api/org/randomString')
.set('Authorization', 'bearer randomString')