Skip to content

Commit

Permalink
Merge pull request #210 from Ohtuilmo/fix/time_logs_validation
Browse files Browse the repository at this point in the history
Fix/time logs validation
  • Loading branch information
Regularmute authored Mar 19, 2024
2 parents 2a1b7fd + 402ac1c commit b2bd978
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 20 deletions.
21 changes: 21 additions & 0 deletions backend/controllers/timeLogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@ const validateTimeEntry = ({ sprint, date, minutes, description }) => {
return null
}

const validateDateRange = (date, start, end) => {
const logDate = new Date(date)
logDate.setHours(0, 0, 0, 0)

const startDate = new Date(start)
startDate.setHours(0, 0, 0, 0)

const endDate = new Date(end)
endDate.setHours(0, 0, 0, 0)

if (logDate < startDate || logDate > endDate) {
return 'The log date is not within sprint start and end date.'
}
return null
}

const fetchFromDb = async (studentNumber) => {
try {
const rawLogs = await db.TimeLog.findAll({
Expand Down Expand Up @@ -122,6 +138,11 @@ timeLogsRouter.post('/', checkLogin, async (req, res) => {
return res.status(404).json({ error: 'Sprint not found.' })
}

const err = validateDateRange(date, sprintRecord.start_date, sprintRecord.end_date)
if (err) {
console.error('error:', err)
return res.status(400).json({ error: err })
}
const sprintId = sprintRecord.id

const timeLog = await db.TimeLog.create({
Expand Down
74 changes: 74 additions & 0 deletions frontend/e2e/cypress/integration/timeLogs.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
describe('Time Logs Page', () => {
beforeEach(() => {
cy.clearLocalStorage()
cy.loginAsRegisteredUser()
cy.createGroup({
name: 'Brand New Group',
topicId: 1,
configurationId: 1,
instructorId: null,
studentIds: ['012345698']
})
cy.visit('/sprints')
cy.get('.sprints-container').should('exist')
cy.get('#sprintNumber').type('1')
cy.get('#startDate').type('2022-01-01')
cy.get('#endDate').type('2022-01-31')
cy.get('.button').click()
cy.wait(200)

cy.visit('/timelogs')
})

//currently just placeholder, no such functionality immpemented
it.skip('does not show log form when there are no sprints', () => {
cy.wait(2500)
cy.get('.timelogs-container-1').should('not.exist')
})

it('creates timeLog successfully', () => {
cy.visit('/timelogs')
cy.get('.timelogs-container-1').should('exist')
cy.get('.input-container').should('exist')
cy.get('.date').type('2022-01-01')
cy.get('.time').type('01:00')
cy.get('.description').type('Test description')
cy.get('.submit-button').click()

cy.get('.notification').should('exist')
cy.get('.notification').should('have.text', 'Time log created successfully')
})

// this test will fail when frontend validation is done properly, remove at that point
it('shows error from backend when creating timeLog fails too short description', () => {
cy.visit('/timelogs')
cy.get('.timelogs-container-1').should('exist')
cy.get('.input-container').should('exist')
cy.get('.date').type('2022-01-01')
cy.get('.time').type('01:00')
cy.get('.description').type('inv')
cy.get('.submit-button').click()

cy.get('.notification').should('exist')
cy.get('.notification').should('have.text', 'Description must be over 5 characters.')
})

it('shows error from backend when creating timeLog fails with data outside sprint', () => {
cy.visit('/timelogs')
cy.get('.timelogs-container-1').should('exist')
cy.get('.input-container').should('exist')
cy.get('.date').type('2022-02-01')
cy.get('.time').type('01:00')
cy.get('.description').type('valid description')
cy.get('.submit-button').click()

cy.get('.notification').should('exist')
cy.get('.notification').should('have.text', 'The log date is not within sprint start and end date.')
})

after(() => {
cy.visit('/sprints')
cy.get('.delete-sprint-button').click()
cy.deleteAllGroups()
})
})
1 change: 1 addition & 0 deletions frontend/src/components/SprintsPage/SprintsDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ const SprintsPage = (props) => {
<td>
<Button
onClick={() => handleDeleteSprint(sprint.id)}
className = 'delete-sprint-button'
variant="contained"
color="secondary"
>
Expand Down
33 changes: 25 additions & 8 deletions frontend/src/components/TimeLogsPage/TimeLogsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
hoursAndMinutesToMinutes,
} from '../../utils/functions'
import './TimeLogsPage.css'
import * as notificationActions from '../../reducers/actions/notificationActions'

const TimeLogsPage = (props) => {
const [allLogs, setAllLogs] = useState([])
Expand All @@ -28,23 +29,26 @@ const TimeLogsPage = (props) => {
try {
await initializeMyGroup()
} catch (error) {
console.error('Error fetching group:', error)
console.error('Error fethcing group:', error.message, ' / ' , error.response.data.error)
notificationActions.setError(error.response.data.error)
}
}
const fetchTimeLogs = async () => {
try {
const fetchedData = await timeLogsService.getTimeLogs()
setAllLogs(fetchedData)
} catch (error) {
console.error('Error fetching logs:', error)
console.error('Error fethcing timelogs:', error.message, ' / ' , error.response.data.error)
notificationActions.setError(error.response.data.error)
}
}
const fetchSprints = async () => {
try {
const fetchedData = await sprintService.getSprints()
setAllSprints(fetchedData)
} catch (error) {
console.error('Error fetching sprints:', error)
console.error('Error fethcing sprints:', error.message, ' / ' , error.response.data.error)
notificationActions.setError(error.response.data.error)
}
}

Expand Down Expand Up @@ -76,14 +80,25 @@ const TimeLogsPage = (props) => {
tags: [],
groupId: group.id,
}

const updatedLogs = await timeLogsService.createTimeLog(log)
setAllLogs(updatedLogs)
try {
const updatedLogs = await timeLogsService.createTimeLog(log)
setAllLogs(updatedLogs)
props.setSuccess('Time log created successfully')
} catch (error) {
console.error('Error creating time log:', error.message, ' / ' , error.response.data.error)
props.setError(error.response.data.error)
}
}

const handleDelete = async (logId) => {
const updatedLogs = await timeLogsService.deleteTimeLog(logId)
setAllLogs(updatedLogs)
try {
const updatedLogs = await timeLogsService.deleteTimeLog(logId)
setAllLogs(updatedLogs)
props.setSuccess('Time log deleted successfully')
} catch (error) {
console.error('Error deleting time log:', error.message, ' / ' , error.response.data.error)
props.setError(error.response.data.error)
}
}

const handleClickNextSprint = () => {
Expand Down Expand Up @@ -139,6 +154,8 @@ const mapStateToProps = (state) => ({

const mapDispatchToProps = {
initializeMyGroup: myGroupActions.initializeMyGroup,
setError: notificationActions.setError,
setSuccess: notificationActions.setSuccess,
}

export default withRouter(
Expand Down
37 changes: 25 additions & 12 deletions frontend/src/services/timeLogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,37 @@ const url = `${BACKEND_API_BASE}/timelogs`


const getTimeLogs = async () => {
const response = await axios.get(url, {
headers: { Authorization: `Bearer ${getUserToken()}` }
})
return response.data
try {
const response = await axios.get(url, {
headers: { Authorization: `Bearer ${getUserToken()}` }
})
return response.data
} catch (error) {
throw error
}
}

const createTimeLog = async (timeEntry) => {
const response = await axios.post(url, timeEntry, {
headers: { Authorization: `Bearer ${getUserToken()}` }
})
return response.data
try {
const response = await axios.post(url, timeEntry, {
headers: { Authorization: `Bearer ${getUserToken()}` }
})
return response.data
} catch (error) {
throw error
}
}

const deleteTimeLog = async (id) => {
const response = await axios.delete(`${url}/${id}`, {
headers: { Authorization: `Bearer ${getUserToken()}` }
})
return response.data
try {
const response = await axios.delete(`${url}/${id}`, {
headers: { Authorization: `Bearer ${getUserToken()}` }
})
return response.data
} catch (error) {
console.error('error in deleteTimeLog', error.response.data.error)
throw error
}
}

export default { getTimeLogs, createTimeLog, deleteTimeLog }

0 comments on commit b2bd978

Please sign in to comment.