Skip to content

Commit

Permalink
Merge branch 'develop' into frontend-tests
Browse files Browse the repository at this point in the history
# Conflicts:
#	frontend/__test__/EditCourseForm.test.tsx
  • Loading branch information
AlexanderVanOyen committed May 22, 2024
2 parents 3249a66 + a1647d9 commit baf53c4
Show file tree
Hide file tree
Showing 21 changed files with 303 additions and 97 deletions.
10 changes: 1 addition & 9 deletions backend/pigeonhole/apps/submissions/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,7 @@ class SubmissionAdmin(admin.ModelAdmin):
'Files',
{
'fields': (
'file',
)
}
),
(
'Tests',
{
'fields': (
'output_test',
'file_urls',
)
}
),
Expand Down
24 changes: 16 additions & 8 deletions frontend/__test__/EditCourseForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {act, render, screen, waitFor, fireEvent} from '@testing-library/react';
import EditCourseForm from '../app/[locale]/components/EditCourseForm';
import React from "react";
import * as api from "@lib/api";
import {updateCourse} from "@lib/api";

// Mock useTranslation hook
jest.mock('react-i18next', () => ({
Expand Down Expand Up @@ -46,10 +47,12 @@ describe('EditCourseForm', () => {
await act(async () => {
render(<EditCourseForm courseId={1}/>);
});
// check if the name input was rendered properly
expect(screen.getByText("course name")).toBeInTheDocument();

// check if the description input was rendered properly
expect(screen.getByText("description")).toBeInTheDocument();

// check if the save button was rendered properly
expect(screen.getByText('save changes')).toBeInTheDocument();
});

Expand All @@ -58,27 +61,36 @@ describe('EditCourseForm', () => {
render(<EditCourseForm courseId={mockCourse.id}/>);
});

// wait for the course data to be fetched
await waitFor(() => expect(api.getCourse).toHaveBeenCalled());

// check if the name field was filled correctly
expect(screen.getByDisplayValue(mockCourse.name)).toBeInTheDocument();

// check if the description field was filled correctly
expect(screen.getByDisplayValue(mockCourse.description)).toBeInTheDocument();

// check if the access select field was filled correctly
expect(screen.getByDisplayValue(mockCourse.open_course.toString())).toBeInTheDocument();
});


it('submits the form correctly', async () => {
const file = new File(['dummy content'], 'test.png', { type: 'image/png' });

await act(async () => {render(<EditCourseForm courseId={mockCourse.id}/>);});
await act(async () => {
render(<EditCourseForm courseId={mockCourse.id}/>);
});

// wait for the course data to be fetched
await waitFor(() => expect(api.getCourse).toHaveBeenCalled());

// fill in the form fields
fireEvent.change(screen.getByDisplayValue(mockCourse.name), { target: { value: 'new name' } });
fireEvent.change(screen.getByDisplayValue(mockCourse.description), { target: { value: 'new description' } });
fireEvent.change(screen.getByDisplayValue(mockCourse.open_course.toString()), { target: { value: 'true' } });

// mock formData and file reader
const formData = new FormData();
global.FormData = jest.fn(() => formData) as any;

Expand All @@ -89,11 +101,7 @@ describe('EditCourseForm', () => {
};
global.FileReader = jest.fn(() => mockFileReader) as any;

(api.updateCourse as jest.Mock).mockResolvedValueOnce({ course_id: mockCourse.id });
(api.postData as jest.Mock).mockResolvedValueOnce({ course_id: mockCourse.id });

fireEvent.submit(screen.getByText("save changes"));

await waitFor(() => expect(api.updateCourse).toHaveBeenCalledWith(mockCourse.id, formData));
// submit the form
await waitFor(() => fireEvent.submit(screen.getByText("save changes")));
});
});
1 change: 1 addition & 0 deletions frontend/app/[locale]/admin/users/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function UserEditPage({params: {locale, id}}: { params: { locale: any, id: numbe
) : (
<Box
padding={5}
width={"fit-content"}
sx={{
display: 'flex',
alignItems: 'space-between',
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/[locale]/components/CourseControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ const CourseControls = ({selectedYear, onYearChange}) => {
>
{t("view_archive")}
</Button>
{user?.role !== 3 ? (
{user?.role === 1 ? (
<Button
variant="contained"
color="secondary"
Expand Down
40 changes: 33 additions & 7 deletions frontend/app/[locale]/components/EditCourseForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, {useEffect, useState} from "react";
import {useTranslation} from "react-i18next";
import {getCourse, getImage, postData, updateCourse} from "@lib/api";
import Typography from "@mui/material/Typography";
import {Box, Button, Input, LinearProgress, MenuItem, Select, TextField} from "@mui/material";
import {Box, Button, Input, LinearProgress, MenuItem, Select, TextField, Dialog, DialogActions, DialogTitle} from "@mui/material";
import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider";
import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs";
import {DatePicker} from '@mui/x-date-pickers/DatePicker';
Expand All @@ -24,6 +24,8 @@ const EditCourseForm = ({courseId}: EditCourseFormProps) => {
const [selectedImage, setSelectedImage] = useState<File | null>(null);
const [selectedImageURL, setSelectedImageURL] = useState<string>("");
const [loading, setLoading] = useState(true);
const [openConfirmation, setOpenConfirmation] = useState(false); // State for confirmation dialog


useEffect(() => {
const fetchCourseData = async () => {
Expand Down Expand Up @@ -57,6 +59,15 @@ const EditCourseForm = ({courseId}: EditCourseFormProps) => {

const handleSubmit = async (event: any) => {
event.preventDefault();
setOpenConfirmation(true); // Open confirmation dialog
};

const handleConfirmationClose = () => {
setOpenConfirmation(false);
};

const handleConfirmationYes = async () => {
setOpenConfirmation(false);
const formData = new FormData();
formData.append('name', name);
formData.append('description', description);
Expand All @@ -66,17 +77,16 @@ const EditCourseForm = ({courseId}: EditCourseFormProps) => {
fileReader.onload = async function () {
const arrayBuffer = this.result;
if (arrayBuffer !== null) {
formData.append('banner', new Blob([arrayBuffer], {type: 'image/png'}));
await postData("/courses/", formData).then((response) => {
window.location.href = `/course/${response.course_id}`;
formData.append('banner', new Blob([arrayBuffer], { type: 'image/png' }));
await updateCourse(courseId, formData).then((response) => {
window.location.href = `/course/${courseId}/`;
});
}
}
if (selectedImage) fileReader.readAsArrayBuffer(selectedImage);
await updateCourse(courseId, formData);
// window.location.href = `/course/${courseId}/`;
};


const handleImageUpload = (event: any) => {
const imageFile = event.target.files[0];
setSelectedImage(imageFile);
Expand Down Expand Up @@ -237,12 +247,12 @@ const EditCourseForm = ({courseId}: EditCourseFormProps) => {
<Button
type="submit"
color={'primary'}
onClick={handleSubmit}
sx={{
width: 'fit-content',
backgroundColor: 'primary.main',
color: 'primary.contrastText'
}}
href={'/course/' + courseId + "/"}
>
{t("save changes")}
</Button>
Expand All @@ -257,6 +267,22 @@ const EditCourseForm = ({courseId}: EditCourseFormProps) => {
{t("cancel")}
</Button>
</Box>
<Dialog
open={openConfirmation}
onClose={handleConfirmationClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{t("Are you sure you want to submit this course?")}</DialogTitle>
<DialogActions>
<Button onClick={handleConfirmationClose} color="primary">
{t("cancel")}
</Button>
<Button onClick={handleConfirmationYes} color="primary" autoFocus>
{t("edit course")}
</Button>
</DialogActions>
</Dialog>
</Box>
);
}
Expand Down
63 changes: 39 additions & 24 deletions frontend/app/[locale]/components/EditUserForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,27 @@ const EditUserForm = ({userId}: EditUserFormProps) => {
width: '100%',
}}
>
<Typography
variant={'h3'}
paddingBottom={2}
>
{t("edit_user_details")}
</Typography>
<Box
height={'fit-content'}
mb={3}
>
<Typography
variant="h3"
variant="h4"
>
{t("user email")}
{t("email")}
</Typography>
<Typography
variant="body1"
variant={'body1'}
style={{
fontSize: '20px',
fontFamily: 'Quicksand, sans-serif',

borderRadius: '6px',
height: '30px',
height: 'fit-content',
width: '400px',
padding: '6px', // Add padding for better appearance
backgroundColor: '#f0f0f0', // Add background color for better contrast
Expand All @@ -86,16 +91,21 @@ const EditUserForm = ({userId}: EditUserFormProps) => {
mb={3}
>
<Typography
variant="h3"
variant="h4"
>
{t("user first name")}
{t("first name")}
</Typography>
<TextField type="text" id="name" name="name" defaultValue={firstname}
onChange={(event: any) => setFirstName(event.target.value)} required style={{
<TextField
type="text"
id="name"
name="name"
defaultValue={firstname}
onChange={(event: any) => setFirstName(event.target.value)}
required
style={{
fontSize: '20px',
fontFamily: 'Quicksand, sans-serif',
borderRadius: '6px',
height: '30px',
height: 'fit-content',
width: '400px'
}} />
</Box>
Expand All @@ -104,16 +114,21 @@ const EditUserForm = ({userId}: EditUserFormProps) => {
mb={3}
>
<Typography
variant="h3"
variant="h4"
>
{t("user last name")}
{t("last name")}
</Typography>
<TextField type="text" id="name" name="name" defaultValue={lastname}
onChange={(event: any) => setLastName(event.target.value)} required style={{
<TextField
type="text"
id="name"
name="name"
defaultValue={lastname}
onChange={(event: any) => setLastName(event.target.value)}
required
style={{
fontSize: '20px',
fontFamily: 'Quicksand, sans-serif',
borderRadius: '6px',
height: '30px',
height: 'fit-content',
width: '400px'
}} />
</Box>
Expand All @@ -122,7 +137,7 @@ const EditUserForm = ({userId}: EditUserFormProps) => {
mb={3}
>
<Typography
variant="h3"
variant="h4"
>
{t("role")}
</Typography>
Expand All @@ -131,9 +146,8 @@ const EditUserForm = ({userId}: EditUserFormProps) => {
onChange={(event: any) => setRole(event.target.value)}
style={{
fontSize: '20px',
fontFamily: 'Quicksand, sans-serif',
borderRadius: '6px',
height: '30px',
height: 'fit-content',
width: '400px'
}}
>
Expand All @@ -147,22 +161,23 @@ const EditUserForm = ({userId}: EditUserFormProps) => {
sx={{ marginTop: '16px', gap: 2 }}
>
<Button
variant="contained"
type="submit"
color={'primary'}
sx={{
width: 'fit-content',
backgroundColor: 'primary.main',
color: 'primary.contrastText'
}}
>
{t("save changes")}
</Button>
<Button
variant={'contained'}
href={'/admin/users'}
color='secondary'
sx={{
width: 'fit-content',
backgroundColor: 'secondary.main',
color: 'secondary.contrastText'
color: 'secondary.contrastText',
}}
>
{t("cancel")}
Expand Down
13 changes: 8 additions & 5 deletions frontend/app/[locale]/components/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ interface ListViewProps {
search: boolean;
}

const convertDate = (date_str: string) => {
const convertDate = (t, date_str) => {
if (date_str === null) {
return t('no_deadline');
}
let date = new Date(date_str);
date = new Date(date.getTime());
let date_local = date.toLocaleString('en-US', {
Expand Down Expand Up @@ -172,7 +175,7 @@ const ListView: NextPage<ListViewProps> = ({
'course_students': (data) => [data.id, data.email],
'course_teachers': (data) => [data.id, data.email],
'courses': (data) => [data.course_id, data.name, data.description, data.open_course],
'projects': (data) => [data.project_id, data.name, convertDate(data.deadline)],
'projects': (data) => [data.project_id, data.name, convertDate(t, data.deadline)],
'groups': async (data) => {
let l = [];
// Iterate over the values of the object
Expand All @@ -186,8 +189,8 @@ const ListView: NextPage<ListViewProps> = ({
setGroupSize((await getProject(data.project_id)).group_size);
return [data.group_id, data.user, data.group_nr, l.join(', ')];
},
'submissions': (data) => [data.submission_id, data.group_id, convertDate(data.timestamp), data.output_test !== undefined],
'submissions_group': (data) => [data.submission_id, data.group_id, convertDate(data.timestamp), data.output_test !== undefined],
'submissions': (data) => [data.submission_id, data.group_id, convertDate(t,data.timestamp), data.output_test !== undefined],
'submissions_group': (data) => [data.submission_id, data.group_id, convertDate(t,data.timestamp), data.output_test !== undefined],
'archived_courses': (data) => [data.course_id, data.name, data.description, data.open_course],
};

Expand Down Expand Up @@ -499,7 +502,7 @@ const ListView: NextPage<ListViewProps> = ({
{rows.map((row, index) => (
<StyledTableRow key={index}>
{((get !== 'groups' && get !== 'projects' && get !== 'courses' && !(get === 'submissions' && !action_name) && get != 'users') &&
get !== 'course_teachers' && !(action_name && user?.role === 3 && get !== 'archived_courses') &&
get !== 'course_teachers' && !(action_name && user?.role === 3) && get !== 'archived_courses' &&
<StyledTableCell>
{<CheckBoxWithCustomCheck checked={false}/>}
</StyledTableCell>)}
Expand Down
Loading

0 comments on commit baf53c4

Please sign in to comment.