Skip to content

Commit

Permalink
feat(tests): add unit tests for timetable utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert27 committed Jan 16, 2025
1 parent 96d2b04 commit 07895e8
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 10 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Run tests
on:
push:
branches: [develop, main]
pull_request:
branches: [develop, main]
jobs:
test:
name: Run tests and collect coverage
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Node
uses: actions/setup-node@v4

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run tests
run: bunx jest --coverage

- name: Upload results to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ credentials/
# The following patterns were generated by expo-cli

expo-env.d.ts
# @end expo-cli
# @end expo-cli

coverage/**/*
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ android {
applicationId 'app.neuland'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 275
versionCode 279
versionName "0.11.2"
}
signingConfigs {
Expand Down
2 changes: 1 addition & 1 deletion app.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"android": {
"package": "app.neuland",
"userInterfaceStyle": "automatic",
"versionCode": 275
"versionCode": 279
},
"sdkVersion": "52.0.0",
"experiments": {
Expand Down
Binary file modified bun.lockb
Binary file not shown.
31 changes: 27 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
"licences": "npm-license-crawler -onlyDirectDependencies -json src/data/licenses.json --exclude docs/",
"prepare": "husky",
"codegen": "graphql-codegen --config codegen.yml",
"changelog": "git cliff --output CHANGELOG.md"
"changelog": "git cliff --output CHANGELOG.md",
"test": "jest --watch --coverage=false --changedSince=origin/main",
"testDebug": "jest -o --watch --coverage=false",
"testFinal": "jest",
"updateSnapshots": "jest -u --coverage=false"
},
"dependencies": {
"@aptabase/react-native": "^0.3.10",
Expand Down Expand Up @@ -125,7 +129,8 @@
"@types/bun": "latest",
"@types/color": "^3.0.6",
"@types/geojson": "^7946.0.15",
"@types/node": "^22.10.6",
"@types/jest": "^29.5.14",
"@types/node": "^22.10.7",
"@types/prop-types": "^15.7.14",
"@types/react": "~18.3.18",
"@types/sanitize-html": "^2.13.0",
Expand All @@ -149,7 +154,9 @@
"expo-dev-client": "~5.0.9",
"graphql-codegen-typescript-operation-types": "^2.0.1",
"husky": "^9.1.7",
"lint-staged": "^15.3.0",
"jest": "~29.7.0",
"jest-expo": "~52.0.3",
"lint-staged": "^15.4.0",
"prettier": "3.4.2",
"prop-types": "^15.8.1",
"typescript": "^5.7.3"
Expand All @@ -166,5 +173,21 @@
"@th3rdwave/[email protected]": "patches/@th3rdwave%[email protected]",
"@aptabase/[email protected]": "patches/@aptabase%[email protected]",
"[email protected]": "patches/[email protected]"
}
},
"jest": {
"preset": "jest-expo",
"transformIgnorePatterns": [
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg)"
],
"collectCoverage": true,
"collectCoverageFrom": [
"**/*.{ts,tsx,js,jsx}",
"!**/coverage/**",
"!**/node_modules/**",
"!**/babel.config.js",
"!**/expo-env.d.ts",
"!**/.expo/**"
]
}

}
4 changes: 3 additions & 1 deletion src/localization/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ export const resources = {
export const defaultNS = 'en'
export type LanguageKey = keyof typeof resources

const languageCode = getLocales()[0].languageCode ?? ''
const locales = getLocales()
const languageCode =
(locales && locales.length > 0 ? locales[0].languageCode : '') ?? ''
const fallbackLanguage = defaultNS
const language = Object.keys(resources).includes(languageCode)
? languageCode
Expand Down
2 changes: 1 addition & 1 deletion src/types/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export interface ExamTimetableEntry extends Exam {

export interface TimetableSections {
title: Date
data: TimetableEntry[] | ExamEntry[]
data: (TimetableEntry | ExamEntry)[]
}

export interface CalendarEvent {
Expand Down
182 changes: 182 additions & 0 deletions src/utils/__tests__/timetable-utils-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { Exam, FriendlyTimetableEntry, TimetableSections } from '@/types/utils'
import moment from 'moment'

import {
generateKey,
getGroupedTimetable,
isValidRoom,
} from '../timetable-utils'

describe('getGroupedTimetable', () => {
it('should correctly group and sort timetable entries and exams by date', () => {
const timetable: FriendlyTimetableEntry[] = [
{
date: new Date('2023-10-01T10:00:00Z'),
startDate: new Date('2023-10-01T10:00:00Z'),
endDate: new Date('2023-10-01T11:00:00Z'),
name: 'Lecture 1',
shortName: 'Lec 1',
rooms: ['Room 1'],
lecturer: 'Dr. Smith',
course: 'Course 1',
studyGroup: 'Group 1',
sws: '2',
ects: '3',
goal: 'Goal 1',
contents: 'Contents 1',
literature: 'Literature 1',
},
{
date: new Date('2023-10-02T12:00:00Z'),
startDate: new Date('2023-10-02T12:00:00Z'),
endDate: new Date('2023-10-02T13:00:00Z'),
name: 'Lecture 2',
shortName: 'Lec 2',
rooms: ['Room 2'],
lecturer: 'Dr. Johnson',
course: 'Course 2',
studyGroup: 'Group 2',
sws: '2',
ects: '3',
goal: 'Goal 2',
contents: 'Contents 2',
literature: 'Literature 2',
},
]

const exams: Exam[] = [
{
date: new Date('2023-10-01T08:00:00Z'),
type: 'LN - schriftliche Prüfung, 90 Minuten',
name: 'Exam 1',
rooms: 'Room 3',
seat: 'Seat 1',
notes: 'Notes 1',
examiners: ['Examiner 1'],
enrollment: new Date('2023-09-01T08:00:00Z'),
aids: ['Aid 1'],
},
{
date: new Date('2023-10-02T09:00:00Z'),
type: 'SP - schrP90 - schriftliche Prüfung, 90 Minuten',
name: 'Exam 2',
rooms: 'Room 4',
seat: 'Seat 2',
notes: 'Notes 2',
examiners: ['Examiner 2'],
enrollment: new Date('2023-09-02T09:00:00Z'),
aids: ['Aid 2'],
},
]

const expectedOutput: TimetableSections[] = [
{
title: new Date('2023-10-01'),
data: [
{
date: new Date('2023-10-01T08:00:00Z'),
type: 'LN - schriftliche Prüfung, 90 Minuten',
name: 'Exam 1',
rooms: 'Room 3',
seat: 'Seat 1',
notes: 'Notes 1',
examiners: ['Examiner 1'],
enrollment: new Date('2023-09-01T08:00:00Z'),
aids: ['Aid 1'],
endDate: moment('2023-10-01T08:00:00Z')
.add(90, 'minutes')
.toDate(),
eventType: 'exam',
},
{
date: new Date('2023-10-01T10:00:00Z'),
startDate: new Date('2023-10-01T10:00:00Z'),
endDate: new Date('2023-10-01T11:00:00Z'),
name: 'Lecture 1',
shortName: 'Lec 1',
rooms: ['Room 1'],
lecturer: 'Dr. Smith',
course: 'Course 1',
studyGroup: 'Group 1',
sws: '2',
ects: '3',
goal: 'Goal 1',
contents: 'Contents 1',
literature: 'Literature 1',
eventType: 'timetable',
},
],
},
{
title: new Date('2023-10-02'),
data: [
{
date: new Date('2023-10-02T09:00:00Z'),
type: 'SP - schrP90 - schriftliche Prüfung, 90 Minuten',
name: 'Exam 2',
rooms: 'Room 4',
seat: 'Seat 2',
notes: 'Notes 2',
examiners: ['Examiner 2'],
enrollment: new Date('2023-09-02T09:00:00Z'),
aids: ['Aid 2'],
endDate: moment('2023-10-02T09:00:00Z')
.add(90, 'minutes')
.toDate(),
eventType: 'exam',
},
{
date: new Date('2023-10-02T12:00:00Z'),
startDate: new Date('2023-10-02T12:00:00Z'),
endDate: new Date('2023-10-02T13:00:00Z'),
name: 'Lecture 2',
shortName: 'Lec 2',
rooms: ['Room 2'],
lecturer: 'Dr. Johnson',
course: 'Course 2',
studyGroup: 'Group 2',
sws: '2',
ects: '3',
goal: 'Goal 2',
contents: 'Contents 2',
literature: 'Literature 2',
eventType: 'timetable',
},
],
},
]

const result = getGroupedTimetable(timetable, exams)
expect(result).toEqual(expectedOutput)
})
})

describe('generateKey', () => {
it('should generate a unique key for a lecture', () => {
const lectureName = 'Lecture 1'
const startDate = new Date('2023-10-01T10:00:00Z')
const room = 'Room 1'
const expectedKey = 'Lecture 1-1696154400000-Room 1'
const result = generateKey(lectureName, startDate, room)
expect(result).toBe(expectedKey)
})
})

describe('isValidRoom', () => {
it('should return true for valid room strings', () => {
expect(isValidRoom('A101')).toBe(true)
expect(isValidRoom('B202')).toBe(true)
expect(isValidRoom('C303')).toBe(true)
expect(isValidRoom('AU101')).toBe(true)
expect(isValidRoom('BU202')).toBe(true)
expect(isValidRoom('A32')).toBe(true)
})

it('should return false for invalid room strings', () => {
expect(isValidRoom('101')).toBe(false)
expect(isValidRoom('Online')).toBe(false)
expect(isValidRoom('AB1234')).toBe(false)
expect(isValidRoom('A1U01')).toBe(false)
expect(isValidRoom('')).toBe(false)
})
})
2 changes: 1 addition & 1 deletion src/utils/timetable-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import API from '@/api/authenticated-api'
import {
type CalendarEvent,
type Exam,
Expand All @@ -7,6 +6,7 @@ import {
} from '@/types/utils'
import moment from 'moment'

import API from '../api/authenticated-api'
import { combineDateTime } from './date-utils'

/**
Expand Down

0 comments on commit 07895e8

Please sign in to comment.