Merge pull request #97 from SELab-2/test/testen-voor-datalaag-#87

test: Datalaag testen
This commit is contained in:
Laure Jablonski 2025-03-13 15:34:21 +01:00 committed by GitHub
commit 17786e604f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 2456 additions and 3082 deletions

View file

@ -60,6 +60,14 @@ De tech-stack bestaat uit:
Voor meer informatie over de keuze van deze tech-stack, Voor meer informatie over de keuze van deze tech-stack,
zie [designkeuzes](https://github.com/SELab-2/Dwengo-1/wiki/Developer:-Design-keuzes). zie [designkeuzes](https://github.com/SELab-2/Dwengo-1/wiki/Developer:-Design-keuzes).
## Testen
Voer volgende commando's uit om de <frontend/backend> te testen:
```
npm run test:unit
```
## Bijdragen aan Dwengo-1 ## Bijdragen aan Dwengo-1
Zie [CONTRIBUTING.md](./CONTRIBUTING.md) voor meer informatie over hoe je kan bijdragen aan Dwengo-1. Zie [CONTRIBUTING.md](./CONTRIBUTING.md) voor meer informatie over hoe je kan bijdragen aan Dwengo-1.

View file

@ -21,6 +21,14 @@ npm run build
npm run start npm run start
``` ```
### Tests
Voer volgend commando uit om de unit tests uit te voeren:
```
npm run test:unit
```
## Keycloak configuratie ## Keycloak configuratie
Tijdens development is het voldoende om gebruik te maken van de keycloak configuratie die automatisch ingeladen wordt. Tijdens development is het voldoende om gebruik te maken van de keycloak configuratie die automatisch ingeladen wordt.

View file

@ -14,12 +14,11 @@
"test:unit": "vitest" "test:unit": "vitest"
}, },
"dependencies": { "dependencies": {
"@mikro-orm/core": "6.4.6", "@mikro-orm/core": "6.4.9",
"@mikro-orm/postgresql": "6.4.6", "@mikro-orm/knex": "6.4.9",
"@mikro-orm/reflection": "6.4.6", "@mikro-orm/postgresql": "6.4.9",
"@mikro-orm/sqlite": "6.4.6", "@mikro-orm/reflection": "6.4.9",
"@types/cors": "^2.8.17", "@mikro-orm/sqlite": "6.4.9",
"@types/js-yaml": "^4.0.9",
"axios": "^1.8.2", "axios": "^1.8.2",
"cors": "^2.8.5", "cors": "^2.8.5",
"cross": "^1.0.0", "cross": "^1.0.0",
@ -40,8 +39,10 @@
"winston-loki": "^6.1.3" "winston-loki": "^6.1.3"
}, },
"devDependencies": { "devDependencies": {
"@mikro-orm/cli": "6.4.6", "@mikro-orm/cli": "6.4.9",
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0", "@types/express": "^5.0.0",
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.13.4", "@types/node": "^22.13.4",
"@types/response-time": "^2.3.8", "@types/response-time": "^2.3.8",
"globals": "^15.15.0", "globals": "^15.15.0",

View file

@ -5,10 +5,12 @@ import { Teacher } from '../../entities/users/teacher.entity.js';
export class AnswerRepository extends DwengoEntityRepository<Answer> { export class AnswerRepository extends DwengoEntityRepository<Answer> {
public createAnswer(answer: { toQuestion: Question; author: Teacher; content: string }): Promise<Answer> { public createAnswer(answer: { toQuestion: Question; author: Teacher; content: string }): Promise<Answer> {
const answerEntity = new Answer(); const answerEntity = this.create({
answerEntity.toQuestion = answer.toQuestion; toQuestion: answer.toQuestion,
answerEntity.author = answer.author; author: answer.author,
answerEntity.content = answer.content; content: answer.content,
timestamp: new Date(),
});
return this.insert(answerEntity); return this.insert(answerEntity);
} }
public findAllAnswersToQuestion(question: Question): Promise<Answer[]> { public findAllAnswersToQuestion(question: Question): Promise<Answer[]> {

View file

@ -5,7 +5,14 @@ import { Student } from '../../entities/users/student.entity.js';
export class QuestionRepository extends DwengoEntityRepository<Question> { export class QuestionRepository extends DwengoEntityRepository<Question> {
public createQuestion(question: { loId: LearningObjectIdentifier; author: Student; content: string }): Promise<Question> { public createQuestion(question: { loId: LearningObjectIdentifier; author: Student; content: string }): Promise<Question> {
const questionEntity = new Question(); const questionEntity = this.create({
learningObjectHruid: question.loId.hruid,
learningObjectLanguage: question.loId.language,
learningObjectVersion: question.loId.version,
author: question.author,
content: question.content,
timestamp: new Date(),
});
questionEntity.learningObjectHruid = question.loId.hruid; questionEntity.learningObjectHruid = question.loId.hruid;
questionEntity.learningObjectLanguage = question.loId.language; questionEntity.learningObjectLanguage = question.loId.language;
questionEntity.learningObjectVersion = question.loId.version; questionEntity.learningObjectVersion = question.loId.version;

View file

@ -61,7 +61,7 @@ export const getTeacherRepository = repositoryGetter<Teacher, TeacherRepository>
/* Classes */ /* Classes */
export const getClassRepository = repositoryGetter<Class, ClassRepository>(Class); export const getClassRepository = repositoryGetter<Class, ClassRepository>(Class);
export const getClassJoinRequestRepository = repositoryGetter<ClassJoinRequest, ClassJoinRequestRepository>(ClassJoinRequest); export const getClassJoinRequestRepository = repositoryGetter<ClassJoinRequest, ClassJoinRequestRepository>(ClassJoinRequest);
export const getTeacherInvitationRepository = repositoryGetter<TeacherInvitation, TeacherInvitationRepository>(TeacherInvitationRepository); export const getTeacherInvitationRepository = repositoryGetter<TeacherInvitation, TeacherInvitationRepository>(TeacherInvitation);
/* Assignments */ /* Assignments */
export const getAssignmentRepository = repositoryGetter<Assignment, AssignmentRepository>(Assignment); export const getAssignmentRepository = repositoryGetter<Assignment, AssignmentRepository>(Assignment);

View file

@ -7,6 +7,9 @@ import { TeacherInvitationRepository } from '../../data/classes/teacher-invitati
* Invitation of a teacher into a class (in order to teach it). * Invitation of a teacher into a class (in order to teach it).
*/ */
@Entity({ repository: () => TeacherInvitationRepository }) @Entity({ repository: () => TeacherInvitationRepository })
@Entity({
repository: () => TeacherInvitationRepository,
})
export class TeacherInvitation { export class TeacherInvitation {
@ManyToOne({ @ManyToOne({
entity: () => Teacher, entity: () => Teacher,

View file

@ -17,8 +17,8 @@ export class Answer {
}) })
toQuestion!: Question; toQuestion!: Question;
@PrimaryKey({ type: 'integer' }) @PrimaryKey({ type: 'integer', autoincrement: true })
sequenceNumber!: number; sequenceNumber?: number;
@Property({ type: 'datetime' }) @Property({ type: 'datetime' })
timestamp: Date = new Date(); timestamp: Date = new Date();

View file

@ -17,8 +17,8 @@ export class Question {
@PrimaryKey({ type: 'number' }) @PrimaryKey({ type: 'number' })
learningObjectVersion: number = 1; learningObjectVersion: number = 1;
@PrimaryKey({ type: 'integer' }) @PrimaryKey({ type: 'integer', autoincrement: true })
sequenceNumber!: number; sequenceNumber?: number;
@ManyToOne({ @ManyToOne({
entity: () => Student, entity: () => Student,

View file

@ -7,4 +7,12 @@ import { TeacherRepository } from '../../data/users/teacher-repository.js';
export class Teacher extends User { export class Teacher extends User {
@ManyToMany(() => Class) @ManyToMany(() => Class)
classes!: Collection<Class>; classes!: Collection<Class>;
constructor(
public username: string,
public firstName: string,
public lastName: string
) {
super();
}
} }

View file

@ -24,6 +24,7 @@ import { LearningPath } from './entities/content/learning-path.entity.js';
import { Answer } from './entities/questions/answer.entity.js'; import { Answer } from './entities/questions/answer.entity.js';
import { Question } from './entities/questions/question.entity.js'; import { Question } from './entities/questions/question.entity.js';
import { SqliteAutoincrementSubscriber } from './sqlite-autoincrement-workaround.js';
const entities = [ const entities = [
User, User,
@ -47,6 +48,7 @@ function config(testingMode: boolean = false): Options {
return { return {
driver: SqliteDriver, driver: SqliteDriver,
dbName: getEnvVar(EnvVars.DbName), dbName: getEnvVar(EnvVars.DbName),
subscribers: [new SqliteAutoincrementSubscriber()],
entities: entities, entities: entities,
// EntitiesTs: entitiesTs, // EntitiesTs: entitiesTs,

View file

@ -0,0 +1,41 @@
import { EntityProperty, EventArgs, EventSubscriber } from '@mikro-orm/core';
/**
* The tests are ran on an in-memory SQLite database. However, SQLite does not allow fields which are part of composite
* primary keys to be autoincremented (while PostgreSQL, which we use in production, does). This Subscriber works around
* the issue by remembering the highest values for every autoincremented part of a primary key and assigning them when
* creating a new entity.
*
* However, it is important to note the following limitations:
* - this class can only be used for in-memory SQLite databases since the information on what the highest sequence
* number for each of the properties is, is only saved transiently.
* - automatically setting the generated "autoincremented" value for properties only works when the entity is created
* via an entityManager.create(...) or repo.create(...) method. Otherwise, onInit will not be called and therefore,
* the sequence number will not be filled in.
*/
export class SqliteAutoincrementSubscriber implements EventSubscriber {
private sequenceNumbersForEntityType: Map<string, number> = new Map();
/**
* When an entity with an autoincremented property which is part of the composite private key is created,
* automatically fill this property so we won't face not-null-constraint exceptions when persisting it.
*/
onInit<T extends object>(args: EventArgs<T>): void {
if (!args.meta.compositePK) {
return; // If there is not a composite primary key, autoincrement works fine with SQLite anyway.
}
for (const prop of Object.values(args.meta.properties)) {
const property = prop as EntityProperty<T>;
if (property.primary && property.autoincrement && !(args.entity as Record<string, any>)[property.name]) {
// Obtain and increment sequence number of this entity.
const propertyKey = args.meta.class.name + '.' + property.name;
const nextSeqNumber = this.sequenceNumbersForEntityType.get(propertyKey) || 0;
this.sequenceNumbersForEntityType.set(propertyKey, nextSeqNumber + 1);
// Set the property accordingly.
(args.entity as Record<string, any>)[property.name] = nextSeqNumber + 1;
}
}
}
}

View file

@ -0,0 +1,42 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { setupTestApp } from '../../setup-tests';
import { AssignmentRepository } from '../../../src/data/assignments/assignment-repository';
import { getAssignmentRepository, getClassRepository } from '../../../src/data/repositories';
import { ClassRepository } from '../../../src/data/classes/class-repository';
describe('AssignmentRepository', () => {
let assignmentRepository: AssignmentRepository;
let classRepository: ClassRepository;
beforeAll(async () => {
await setupTestApp();
assignmentRepository = getAssignmentRepository();
classRepository = getClassRepository();
});
it('should return the requested assignment', async () => {
const class_ = await classRepository.findById('id02');
const assignment = await assignmentRepository.findByClassAndId(class_!, 2);
expect(assignment).toBeTruthy();
expect(assignment!.title).toBe('tool');
});
it('should return all assignments for a class', async () => {
const class_ = await classRepository.findById('id02');
const assignments = await assignmentRepository.findAllAssignmentsInClass(class_!);
expect(assignments).toBeTruthy();
expect(assignments).toHaveLength(1);
expect(assignments[0].title).toBe('tool');
});
it('should not find removed assignment', async () => {
const class_ = await classRepository.findById('id01');
await assignmentRepository.deleteByClassAndId(class_!, 3);
const assignment = await assignmentRepository.findByClassAndId(class_!, 3);
expect(assignment).toBeNull();
});
});

View file

@ -0,0 +1,49 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { setupTestApp } from '../../setup-tests';
import { GroupRepository } from '../../../src/data/assignments/group-repository';
import { getAssignmentRepository, getClassRepository, getGroupRepository } from '../../../src/data/repositories';
import { AssignmentRepository } from '../../../src/data/assignments/assignment-repository';
import { ClassRepository } from '../../../src/data/classes/class-repository';
describe('GroupRepository', () => {
let groupRepository: GroupRepository;
let assignmentRepository: AssignmentRepository;
let classRepository: ClassRepository;
beforeAll(async () => {
await setupTestApp();
groupRepository = getGroupRepository();
assignmentRepository = getAssignmentRepository();
classRepository = getClassRepository();
});
it('should return the requested group', async () => {
const class_ = await classRepository.findById('id01');
const assignment = await assignmentRepository.findByClassAndId(class_!, 1);
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment!, 1);
expect(group).toBeTruthy();
});
it('should return all groups for assignment', async () => {
const class_ = await classRepository.findById('id01');
const assignment = await assignmentRepository.findByClassAndId(class_!, 1);
const groups = await groupRepository.findAllGroupsForAssignment(assignment!);
expect(groups).toBeTruthy();
expect(groups).toHaveLength(3);
});
it('should not find removed group', async () => {
const class_ = await classRepository.findById('id02');
const assignment = await assignmentRepository.findByClassAndId(class_!, 2);
await groupRepository.deleteByAssignmentAndGroupNumber(assignment!, 1);
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment!, 1);
expect(group).toBeNull();
});
});

View file

@ -0,0 +1,70 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { setupTestApp } from '../../setup-tests';
import { SubmissionRepository } from '../../../src/data/assignments/submission-repository';
import {
getAssignmentRepository,
getClassRepository,
getGroupRepository,
getStudentRepository,
getSubmissionRepository,
} from '../../../src/data/repositories';
import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier';
import { Language } from '../../../src/entities/content/language';
import { StudentRepository } from '../../../src/data/users/student-repository';
import { GroupRepository } from '../../../src/data/assignments/group-repository';
import { AssignmentRepository } from '../../../src/data/assignments/assignment-repository';
import { ClassRepository } from '../../../src/data/classes/class-repository';
describe('SubmissionRepository', () => {
let submissionRepository: SubmissionRepository;
let studentRepository: StudentRepository;
let groupRepository: GroupRepository;
let assignmentRepository: AssignmentRepository;
let classRepository: ClassRepository;
beforeAll(async () => {
await setupTestApp();
submissionRepository = getSubmissionRepository();
studentRepository = getStudentRepository();
groupRepository = getGroupRepository();
assignmentRepository = getAssignmentRepository();
classRepository = getClassRepository();
});
it('should find the requested submission', async () => {
const id = new LearningObjectIdentifier('id03', Language.English, '1');
const submission = await submissionRepository.findSubmissionByLearningObjectAndSubmissionNumber(id, 1);
expect(submission).toBeTruthy();
expect(submission?.content).toBe('sub1');
});
it('should find the most recent submission for a student', async () => {
const id = new LearningObjectIdentifier('id02', Language.English, '1');
const student = await studentRepository.findByUsername('Noordkaap');
const submission = await submissionRepository.findMostRecentSubmissionForStudent(id, student!);
expect(submission).toBeTruthy();
expect(submission?.submissionTime.getDate()).toBe(25);
});
it('should find the most recent submission for a group', async () => {
const id = new LearningObjectIdentifier('id03', Language.English, '1');
const class_ = await classRepository.findById('id01');
const assignment = await assignmentRepository.findByClassAndId(class_!, 1);
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment!, 1);
const submission = await submissionRepository.findMostRecentSubmissionForGroup(id, group!);
expect(submission).toBeTruthy();
expect(submission?.submissionTime.getDate()).toBe(25);
});
it('should not find a deleted submission', async () => {
const id = new LearningObjectIdentifier('id01', Language.English, '1');
await submissionRepository.deleteSubmissionByLearningObjectAndSubmissionNumber(id, 1);
const submission = await submissionRepository.findSubmissionByLearningObjectAndSubmissionNumber(id, 1);
expect(submission).toBeNull();
});
});

View file

@ -0,0 +1,47 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { setupTestApp } from '../../setup-tests';
import { ClassJoinRequestRepository } from '../../../src/data/classes/class-join-request-repository';
import { getClassJoinRequestRepository, getClassRepository, getStudentRepository } from '../../../src/data/repositories';
import { StudentRepository } from '../../../src/data/users/student-repository';
import { Class } from '../../../src/entities/classes/class.entity';
import { ClassRepository } from '../../../src/data/classes/class-repository';
import { Student } from '../../../src/entities/users/student.entity';
describe('ClassJoinRequestRepository', () => {
let classJoinRequestRepository: ClassJoinRequestRepository;
let studentRepository: StudentRepository;
let cassRepository: ClassRepository;
beforeAll(async () => {
await setupTestApp();
classJoinRequestRepository = getClassJoinRequestRepository();
studentRepository = getStudentRepository();
cassRepository = getClassRepository();
});
it('should list all requests from student to join classes', async () => {
const student = await studentRepository.findByUsername('PinkFloyd');
const requests = await classJoinRequestRepository.findAllRequestsBy(student!);
expect(requests).toBeTruthy();
expect(requests).toHaveLength(2);
});
it('should list all requests to a single class', async () => {
const class_ = await cassRepository.findById('id02');
const requests = await classJoinRequestRepository.findAllOpenRequestsTo(class_!);
expect(requests).toBeTruthy();
expect(requests).toHaveLength(2);
});
it('should not find a removed request', async () => {
const student = await studentRepository.findByUsername('SmashingPumpkins');
const class_ = await cassRepository.findById('id03');
await classJoinRequestRepository.deleteBy(student!, class_!);
const request = await classJoinRequestRepository.findAllRequestsBy(student!);
expect(request).toHaveLength(0);
});
});

View file

@ -0,0 +1,34 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { ClassRepository } from '../../../src/data/classes/class-repository';
import { setupTestApp } from '../../setup-tests';
import { getClassRepository } from '../../../src/data/repositories';
describe('ClassRepository', () => {
let classRepository: ClassRepository;
beforeAll(async () => {
await setupTestApp();
classRepository = getClassRepository();
});
it('should return nothing because id does not exist', async () => {
const classVar = await classRepository.findById('test_id');
expect(classVar).toBeNull();
});
it('should return requested class', async () => {
const classVar = await classRepository.findById('id01');
expect(classVar).toBeTruthy();
expect(classVar?.displayName).toBe('class01');
});
it('class should be gone after deletion', async () => {
await classRepository.deleteById('id04');
const classVar = await classRepository.findById('id04');
expect(classVar).toBeNull();
});
});

View file

@ -0,0 +1,54 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { setupTestApp } from '../../setup-tests';
import { getClassRepository, getTeacherInvitationRepository, getTeacherRepository } from '../../../src/data/repositories';
import { TeacherInvitationRepository } from '../../../src/data/classes/teacher-invitation-repository';
import { TeacherRepository } from '../../../src/data/users/teacher-repository';
import { ClassRepository } from '../../../src/data/classes/class-repository';
describe('ClassRepository', () => {
let teacherInvitationRepository: TeacherInvitationRepository;
let teacherRepository: TeacherRepository;
let classRepository: ClassRepository;
beforeAll(async () => {
await setupTestApp();
teacherInvitationRepository = getTeacherInvitationRepository();
teacherRepository = getTeacherRepository();
classRepository = getClassRepository();
});
it('should return all invitations from a teacher', async () => {
const teacher = await teacherRepository.findByUsername('LimpBizkit');
const invitations = await teacherInvitationRepository.findAllInvitationsBy(teacher!);
expect(invitations).toBeTruthy();
expect(invitations).toHaveLength(2);
});
it('should return all invitations for a teacher', async () => {
const teacher = await teacherRepository.findByUsername('FooFighters');
const invitations = await teacherInvitationRepository.findAllInvitationsFor(teacher!);
expect(invitations).toBeTruthy();
expect(invitations).toHaveLength(2);
});
it('should return all invitations for a class', async () => {
const class_ = await classRepository.findById('id02');
const invitations = await teacherInvitationRepository.findAllInvitationsForClass(class_!);
expect(invitations).toBeTruthy();
expect(invitations).toHaveLength(2);
});
it('should not find a removed invitation', async () => {
const class_ = await classRepository.findById('id01');
const sender = await teacherRepository.findByUsername('FooFighters');
const receiver = await teacherRepository.findByUsername('LimpBizkit');
await teacherInvitationRepository.deleteBy(class_!, sender!, receiver!);
const invitation = await teacherInvitationRepository.findAllInvitationsBy(sender!);
expect(invitation).toHaveLength(0);
});
});

View file

@ -0,0 +1,31 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { setupTestApp } from '../../setup-tests.js';
import { getAttachmentRepository, getLearningObjectRepository } from '../../../src/data/repositories.js';
import { AttachmentRepository } from '../../../src/data/content/attachment-repository.js';
import { LearningObjectRepository } from '../../../src/data/content/learning-object-repository.js';
import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier.js';
import { Language } from '../../../src/entities/content/language.js';
describe('AttachmentRepository', () => {
let attachmentRepository: AttachmentRepository;
let learningObjectRepository: LearningObjectRepository;
beforeAll(async () => {
await setupTestApp();
attachmentRepository = getAttachmentRepository();
learningObjectRepository = getLearningObjectRepository();
});
it('should return the requested attachment', async () => {
const id = new LearningObjectIdentifier('id02', Language.English, '1');
const learningObject = await learningObjectRepository.findByIdentifier(id);
const attachment = await attachmentRepository.findByMostRecentVersionOfLearningObjectAndName(
learningObject!,
Language.English,
'attachment01'
);
expect(attachment).toBeTruthy();
});
});

View file

@ -0,0 +1,32 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { LearningObjectRepository } from '../../../src/data/content/learning-object-repository';
import { getLearningObjectRepository } from '../../../src/data/repositories';
import { setupTestApp } from '../../setup-tests';
import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier';
import { Language } from '../../../src/entities/content/language';
describe('LearningObjectRepository', () => {
let learningObjectRepository: LearningObjectRepository;
beforeAll(async () => {
await setupTestApp();
learningObjectRepository = getLearningObjectRepository();
});
const id01 = new LearningObjectIdentifier('id01', Language.English, '1');
const id02 = new LearningObjectIdentifier('test_id', Language.English, '1');
it('should return the learning object that matches identifier 1', async () => {
const learningObject = await learningObjectRepository.findByIdentifier(id01);
expect(learningObject).toBeTruthy();
expect(learningObject?.title).toBe('Undertow');
expect(learningObject?.description).toBe('debute');
});
it('should return nothing because the identifier does not exist in the database', async () => {
const learningObject = await learningObjectRepository.findByIdentifier(id02);
expect(learningObject).toBeNull();
});
});

View file

@ -0,0 +1,28 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { getLearningPathRepository } from '../../../src/data/repositories';
import { LearningPathRepository } from '../../../src/data/content/learning-path-repository';
import { setupTestApp } from '../../setup-tests';
import { Language } from '../../../src/entities/content/language';
describe('LearningPathRepository', () => {
let learningPathRepository: LearningPathRepository;
beforeAll(async () => {
await setupTestApp();
learningPathRepository = getLearningPathRepository();
});
it('should return nothing because no match for hruid and language', async () => {
const learningPath = await learningPathRepository.findByHruidAndLanguage('test_id', Language.Dutch);
expect(learningPath).toBeNull();
});
it('should return requested learning path', async () => {
const learningPath = await learningPathRepository.findByHruidAndLanguage('id01', Language.English);
expect(learningPath).toBeTruthy();
expect(learningPath?.title).toBe('repertoire Tool');
expect(learningPath?.description).toBe('all about Tool');
});
});

View file

@ -0,0 +1,66 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { setupTestApp } from '../../setup-tests';
import { AnswerRepository } from '../../../src/data/questions/answer-repository';
import { getAnswerRepository, getQuestionRepository, getTeacherRepository } from '../../../src/data/repositories';
import { QuestionRepository } from '../../../src/data/questions/question-repository';
import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier';
import { Language } from '../../../src/entities/content/language';
import { TeacherRepository } from '../../../src/data/users/teacher-repository';
describe('AnswerRepository', () => {
let answerRepository: AnswerRepository;
let questionRepository: QuestionRepository;
let teacherRepository: TeacherRepository;
beforeAll(async () => {
await setupTestApp();
answerRepository = getAnswerRepository();
questionRepository = getQuestionRepository();
teacherRepository = getTeacherRepository();
});
it('should find all answers to a question', async () => {
const id = new LearningObjectIdentifier('id05', Language.English, '1');
const questions = await questionRepository.findAllQuestionsAboutLearningObject(id);
const question = questions.filter((it) => it.sequenceNumber == 2)[0];
const answers = await answerRepository.findAllAnswersToQuestion(question);
expect(answers).toBeTruthy();
expect(answers).toHaveLength(2);
expect(answers[0].content).toBeOneOf(['answer', 'answer2']);
expect(answers[1].content).toBeOneOf(['answer', 'answer2']);
});
it('should create an answer to a question', async () => {
const teacher = await teacherRepository.findByUsername('FooFighters');
const id = new LearningObjectIdentifier('id05', Language.English, '1');
const questions = await questionRepository.findAllQuestionsAboutLearningObject(id);
const question = questions[0];
await answerRepository.createAnswer({
toQuestion: question,
author: teacher!,
content: 'created answer',
});
const answers = await answerRepository.findAllAnswersToQuestion(question);
expect(answers).toBeTruthy();
expect(answers).toHaveLength(1);
expect(answers[0].content).toBe('created answer');
});
it('should not find a removed answer', async () => {
const id = new LearningObjectIdentifier('id04', Language.English, '1');
const questions = await questionRepository.findAllQuestionsAboutLearningObject(id);
await answerRepository.removeAnswerByQuestionAndSequenceNumber(questions[0], 1);
const emptyList = await answerRepository.findAllAnswersToQuestion(questions[0]);
expect(emptyList).toHaveLength(0);
});
});

View file

@ -0,0 +1,52 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { setupTestApp } from '../../setup-tests';
import { QuestionRepository } from '../../../src/data/questions/question-repository';
import { getLearningObjectRepository, getQuestionRepository, getStudentRepository } from '../../../src/data/repositories';
import { StudentRepository } from '../../../src/data/users/student-repository';
import { LearningObjectRepository } from '../../../src/data/content/learning-object-repository';
import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier';
import { Language } from '../../../src/entities/content/language';
describe('QuestionRepository', () => {
let questionRepository: QuestionRepository;
let studentRepository: StudentRepository;
let learningObjectRepository: LearningObjectRepository;
beforeAll(async () => {
await setupTestApp();
questionRepository = getQuestionRepository();
studentRepository = getStudentRepository();
learningObjectRepository = getLearningObjectRepository();
});
it('should return all questions part of the given learning object', async () => {
const id = new LearningObjectIdentifier('id05', Language.English, '1');
const questions = await questionRepository.findAllQuestionsAboutLearningObject(id);
expect(questions).toBeTruthy();
expect(questions).toHaveLength(2);
});
it('should create new question', async () => {
const id = new LearningObjectIdentifier('id03', Language.English, '1');
const student = await studentRepository.findByUsername('Noordkaap');
await questionRepository.createQuestion({
loId: id,
author: student!,
content: 'question?',
});
const question = await questionRepository.findAllQuestionsAboutLearningObject(id);
expect(question).toBeTruthy();
expect(question).toHaveLength(1);
});
it('should not find removed question', async () => {
const id = new LearningObjectIdentifier('id04', Language.English, '1');
await questionRepository.removeQuestionByLearningObjectAndSequenceNumber(id, 1);
const question = await questionRepository.findAllQuestionsAboutLearningObject(id);
expect(question).toHaveLength(0);
});
});

View file

@ -1,8 +1,8 @@
import { setupTestApp } from '../setup-tests.js'; import { setupTestApp } from '../../setup-tests.js';
import { Student } from '../../src/entities/users/student.entity.js'; import { Student } from '../../../src/entities/users/student.entity.js';
import { describe, it, expect, beforeAll } from 'vitest'; import { describe, it, expect, beforeAll } from 'vitest';
import { StudentRepository } from '../../src/data/users/student-repository.js'; import { StudentRepository } from '../../../src/data/users/student-repository.js';
import { getStudentRepository } from '../../src/data/repositories.js'; import { getStudentRepository } from '../../../src/data/repositories.js';
const username = 'teststudent'; const username = 'teststudent';
const firstName = 'John'; const firstName = 'John';
@ -15,6 +15,20 @@ describe('StudentRepository', () => {
studentRepository = getStudentRepository(); studentRepository = getStudentRepository();
}); });
it('should not return a student because username does not exist', async () => {
const student = await studentRepository.findByUsername('test');
expect(student).toBeNull();
});
it('should return student from the datbase', async () => {
const student = await studentRepository.findByUsername('Noordkaap');
expect(student).toBeTruthy();
expect(student?.firstName).toBe('Stijn');
expect(student?.lastName).toBe('Meuris');
});
it('should return the queried student after he was added', async () => { it('should return the queried student after he was added', async () => {
await studentRepository.insert(new Student(username, firstName, lastName)); await studentRepository.insert(new Student(username, firstName, lastName));

View file

@ -0,0 +1,47 @@
import { describe, it, expect, beforeAll } from 'vitest';
import { TeacherRepository } from '../../../src/data/users/teacher-repository';
import { setupTestApp } from '../../setup-tests';
import { getTeacherRepository } from '../../../src/data/repositories';
import { Teacher } from '../../../src/entities/users/teacher.entity';
const username = 'testteacher';
const firstName = 'John';
const lastName = 'Doe';
describe('TeacherRepository', () => {
let teacherRepository: TeacherRepository;
beforeAll(async () => {
await setupTestApp();
teacherRepository = getTeacherRepository();
});
it('should not return a teacher because username does not exist', async () => {
const teacher = await teacherRepository.findByUsername('test');
expect(teacher).toBeNull();
});
it('should return teacher from the datbase', async () => {
const teacher = await teacherRepository.findByUsername('FooFighters');
expect(teacher).toBeTruthy();
expect(teacher?.firstName).toBe('Dave');
expect(teacher?.lastName).toBe('Grohl');
});
it('should return the queried teacher after he was added', async () => {
await teacherRepository.insert(new Teacher(username, firstName, lastName));
const retrievedTeacher = await teacherRepository.findByUsername(username);
expect(retrievedTeacher).toBeTruthy();
expect(retrievedTeacher?.firstName).toBe(firstName);
expect(retrievedTeacher?.lastName).toBe(lastName);
});
it('should no longer return the queried teacher after he was removed again', async () => {
await teacherRepository.deleteByUsername('ZesdeMetaal');
const retrievedTeacher = await teacherRepository.findByUsername('ZesdeMetaal');
expect(retrievedTeacher).toBeNull();
});
});

View file

@ -1,7 +1,59 @@
import { initORM } from '../src/orm.js'; import { forkEntityManager, initORM } from '../src/orm.js';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import { makeTestStudents } from './test_assets/users/students.testdata.js';
import { makeTestTeachers } from './test_assets/users/teachers.testdata.js';
import { makeTestLearningObjects } from './test_assets/content/learning-objects.testdata.js';
import { makeTestLearningPaths } from './test_assets/content/learning-paths.testdata.js';
import { makeTestClasses } from './test_assets/classes/classes.testdata.js';
import { makeTestAssignemnts } from './test_assets/assignments/assignments.testdata.js';
import { makeTestGroups } from './test_assets/assignments/groups.testdata.js';
import { makeTestTeacherInvitations } from './test_assets/classes/teacher-invitations.testdata.js';
import { makeTestClassJoinRequests } from './test_assets/classes/class-join-requests.testdata.js';
import { makeTestAttachments } from './test_assets/content/attachments.testdata.js';
import { makeTestQuestions } from './test_assets/questions/questions.testdata.js';
import { makeTestAnswers } from './test_assets/questions/answers.testdata.js';
import { makeTestSubmissions } from './test_assets/assignments/submission.testdata.js';
export async function setupTestApp() { export async function setupTestApp() {
dotenv.config({ path: '.env.test' }); dotenv.config({ path: '.env.test' });
await initORM(true); await initORM(true);
const em = forkEntityManager();
const students = makeTestStudents(em);
const teachers = makeTestTeachers(em);
const learningObjects = makeTestLearningObjects(em);
const learningPaths = makeTestLearningPaths(em);
const classes = makeTestClasses(em, students, teachers);
const assignments = makeTestAssignemnts(em, classes);
const groups = makeTestGroups(em, students, assignments);
assignments[0].groups = groups.slice(0, 3);
assignments[1].groups = groups.slice(3, 4);
const teacherInvitations = makeTestTeacherInvitations(em, teachers, classes);
const classJoinRequests = makeTestClassJoinRequests(em, students, classes);
const attachments = makeTestAttachments(em, learningObjects);
learningObjects[1].attachments = attachments;
const questions = makeTestQuestions(em, students);
const answers = makeTestAnswers(em, teachers, questions);
const submissions = makeTestSubmissions(em, students, groups);
await em.persistAndFlush([
...students,
...teachers,
...learningObjects,
...learningPaths,
...classes,
...assignments,
...groups,
...teacherInvitations,
...classJoinRequests,
...attachments,
...questions,
...answers,
...submissions,
]);
} }

View file

@ -0,0 +1,38 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { Assignment } from '../../../src/entities/assignments/assignment.entity';
import { Class } from '../../../src/entities/classes/class.entity';
import { Language } from '../../../src/entities/content/language';
export function makeTestAssignemnts(em: EntityManager<IDatabaseDriver<Connection>>, classes: Array<Class>): Array<Assignment> {
const assignment01 = em.create(Assignment, {
within: classes[0],
id: 1,
title: 'dire straits',
description: 'reading',
learningPathHruid: 'id02',
learningPathLanguage: Language.English,
groups: [],
});
const assignment02 = em.create(Assignment, {
within: classes[1],
id: 2,
title: 'tool',
description: 'reading',
learningPathHruid: 'id01',
learningPathLanguage: Language.English,
groups: [],
});
const assignment03 = em.create(Assignment, {
within: classes[0],
id: 3,
title: 'delete',
description: 'will be deleted',
learningPathHruid: 'id02',
learningPathLanguage: Language.English,
groups: [],
});
return [assignment01, assignment02, assignment03];
}

View file

@ -0,0 +1,36 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { Group } from '../../../src/entities/assignments/group.entity';
import { Assignment } from '../../../src/entities/assignments/assignment.entity';
import { Student } from '../../../src/entities/users/student.entity';
export function makeTestGroups(
em: EntityManager<IDatabaseDriver<Connection>>,
students: Array<Student>,
assignments: Array<Assignment>
): Array<Group> {
const group01 = em.create(Group, {
assignment: assignments[0],
groupNumber: 1,
members: students.slice(0, 2),
});
const group02 = em.create(Group, {
assignment: assignments[0],
groupNumber: 2,
members: students.slice(2, 4),
});
const group03 = em.create(Group, {
assignment: assignments[0],
groupNumber: 3,
members: students.slice(4, 6),
});
const group04 = em.create(Group, {
assignment: assignments[1],
groupNumber: 4,
members: students.slice(3, 4),
});
return [group01, group02, group03, group04];
}

View file

@ -0,0 +1,65 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { Submission } from '../../../src/entities/assignments/submission.entity';
import { Language } from '../../../src/entities/content/language';
import { Student } from '../../../src/entities/users/student.entity';
import { Group } from '../../../src/entities/assignments/group.entity';
export function makeTestSubmissions(
em: EntityManager<IDatabaseDriver<Connection>>,
students: Array<Student>,
groups: Array<Group>
): Array<Submission> {
const submission01 = em.create(Submission, {
learningObjectHruid: 'id03',
learningObjectLanguage: Language.English,
learningObjectVersion: '1',
submissionNumber: 1,
submitter: students[0],
submissionTime: new Date(2025, 2, 20),
onBehalfOf: groups[0],
content: 'sub1',
});
const submission02 = em.create(Submission, {
learningObjectHruid: 'id03',
learningObjectLanguage: Language.English,
learningObjectVersion: '1',
submissionNumber: 2,
submitter: students[0],
submissionTime: new Date(2025, 2, 25),
onBehalfOf: groups[0],
content: '',
});
const submission03 = em.create(Submission, {
learningObjectHruid: 'id02',
learningObjectLanguage: Language.English,
learningObjectVersion: '1',
submissionNumber: 1,
submitter: students[0],
submissionTime: new Date(2025, 2, 20),
content: '',
});
const submission04 = em.create(Submission, {
learningObjectHruid: 'id02',
learningObjectLanguage: Language.English,
learningObjectVersion: '1',
submissionNumber: 2,
submitter: students[0],
submissionTime: new Date(2025, 2, 25),
content: '',
});
const submission05 = em.create(Submission, {
learningObjectHruid: 'id01',
learningObjectLanguage: Language.English,
learningObjectVersion: '1',
submissionNumber: 1,
submitter: students[1],
submissionTime: new Date(2025, 2, 20),
content: '',
});
return [submission01, submission02, submission03, submission04, submission05];
}

View file

@ -0,0 +1,36 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { ClassJoinRequest, ClassJoinRequestStatus } from '../../../src/entities/classes/class-join-request.entity';
import { Student } from '../../../src/entities/users/student.entity';
import { Class } from '../../../src/entities/classes/class.entity';
export function makeTestClassJoinRequests(
em: EntityManager<IDatabaseDriver<Connection>>,
students: Array<Student>,
classes: Array<Class>
): Array<ClassJoinRequest> {
const classJoinRequest01 = em.create(ClassJoinRequest, {
requester: students[4],
class: classes[1],
status: ClassJoinRequestStatus.Open,
});
const classJoinRequest02 = em.create(ClassJoinRequest, {
requester: students[2],
class: classes[1],
status: ClassJoinRequestStatus.Open,
});
const classJoinRequest03 = em.create(ClassJoinRequest, {
requester: students[4],
class: classes[2],
status: ClassJoinRequestStatus.Open,
});
const classJoinRequest04 = em.create(ClassJoinRequest, {
requester: students[3],
class: classes[2],
status: ClassJoinRequestStatus.Open,
});
return [classJoinRequest01, classJoinRequest02, classJoinRequest03, classJoinRequest04];
}

View file

@ -0,0 +1,48 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { Class } from '../../../src/entities/classes/class.entity';
import { Student } from '../../../src/entities/users/student.entity';
import { Teacher } from '../../../src/entities/users/teacher.entity';
export function makeTestClasses(em: EntityManager<IDatabaseDriver<Connection>>, students: Array<Student>, teachers: Array<Teacher>): Array<Class> {
const studentsClass01 = students.slice(0, 7);
const teacherClass01: Array<Teacher> = teachers.slice(0, 1);
const class01 = em.create(Class, {
classId: 'id01',
displayName: 'class01',
teachers: teacherClass01,
students: studentsClass01,
});
const studentsClass02: Array<Student> = students.slice(0, 2).concat(students.slice(3, 4));
const teacherClass02: Array<Teacher> = teachers.slice(1, 2);
const class02 = em.create(Class, {
classId: 'id02',
displayName: 'class02',
teachers: teacherClass02,
students: studentsClass02,
});
const studentsClass03: Array<Student> = students.slice(1, 4);
const teacherClass03: Array<Teacher> = teachers.slice(2, 3);
const class03 = em.create(Class, {
classId: 'id03',
displayName: 'class03',
teachers: teacherClass03,
students: studentsClass03,
});
const studentsClass04: Array<Student> = students.slice(0, 2);
const teacherClass04: Array<Teacher> = teachers.slice(2, 3);
const class04 = em.create(Class, {
classId: 'id04',
displayName: 'class04',
teachers: teacherClass04,
students: studentsClass04,
});
return [class01, class02, class03, class04];
}

View file

@ -0,0 +1,36 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { TeacherInvitation } from '../../../src/entities/classes/teacher-invitation.entity';
import { Teacher } from '../../../src/entities/users/teacher.entity';
import { Class } from '../../../src/entities/classes/class.entity';
export function makeTestTeacherInvitations(
em: EntityManager<IDatabaseDriver<Connection>>,
teachers: Array<Teacher>,
classes: Array<Class>
): Array<TeacherInvitation> {
const teacherInvitation01 = em.create(TeacherInvitation, {
sender: teachers[1],
receiver: teachers[0],
class: classes[1],
});
const teacherInvitation02 = em.create(TeacherInvitation, {
sender: teachers[1],
receiver: teachers[2],
class: classes[1],
});
const teacherInvitation03 = em.create(TeacherInvitation, {
sender: teachers[2],
receiver: teachers[0],
class: classes[2],
});
const teacherInvitation04 = em.create(TeacherInvitation, {
sender: teachers[0],
receiver: teachers[1],
class: classes[0],
});
return [teacherInvitation01, teacherInvitation02, teacherInvitation03, teacherInvitation04];
}

View file

@ -0,0 +1,14 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { Attachment } from '../../../src/entities/content/attachment.entity';
import { LearningObject } from '../../../src/entities/content/learning-object.entity';
export function makeTestAttachments(em: EntityManager<IDatabaseDriver<Connection>>, learningObjects: Array<LearningObject>): Array<Attachment> {
const attachment01 = em.create(Attachment, {
learningObject: learningObjects[1],
name: 'attachment01',
mimeType: '',
content: Buffer.from(''),
});
return [attachment01];
}

View file

@ -0,0 +1,134 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { LearningObject, ReturnValue } from '../../../src/entities/content/learning-object.entity';
import { Language } from '../../../src/entities/content/language';
import { DwengoContentType } from '../../../src/services/learning-objects/processing/content-type';
export function makeTestLearningObjects(em: EntityManager<IDatabaseDriver<Connection>>): Array<LearningObject> {
const returnValue: ReturnValue = new ReturnValue();
returnValue.callbackSchema = '';
returnValue.callbackUrl = '';
const learningObject01 = em.create(LearningObject, {
hruid: 'id01',
language: Language.English,
version: 1,
admins: [],
title: 'Undertow',
description: 'debute',
contentType: DwengoContentType.TEXT_MARKDOWN,
keywords: [],
teacherExclusive: false,
skosConcepts: [],
educationalGoals: [],
copyright: '',
license: '',
estimatedTime: 45,
returnValue: returnValue,
available: true,
contentLocation: '',
attachments: [],
content: Buffer.from("there's a shadow just behind me, shrouding every step i take, making every promise empty pointing every finger at me"),
});
const learningObject02 = em.create(LearningObject, {
hruid: 'id02',
language: Language.English,
version: 1,
admins: [],
title: 'Aenema',
description: 'second album',
contentType: DwengoContentType.TEXT_MARKDOWN,
keywords: [],
teacherExclusive: false,
skosConcepts: [],
educationalGoals: [],
copyright: '',
license: '',
estimatedTime: 80,
returnValue: returnValue,
available: true,
contentLocation: '',
attachments: [],
content: Buffer.from(
"I've been crawling on my belly clearing out what could've been I've been wallowing in my own confused and insecure delusions"
),
});
const learningObject03 = em.create(LearningObject, {
hruid: 'id03',
language: Language.English,
version: 1,
admins: [],
title: 'love over gold',
description: 'third album',
contentType: DwengoContentType.TEXT_MARKDOWN,
keywords: [],
teacherExclusive: false,
skosConcepts: [],
educationalGoals: [],
copyright: '',
license: '',
estimatedTime: 55,
returnValue: returnValue,
available: true,
contentLocation: '',
attachments: [],
content: Buffer.from(
'he wrote me a prescription, he said you are depressed, \
but I am glad you came to see me to get this off your chest, \
come back and see me later next patient please \
send in another victim of industrial disease'
),
});
const learningObject04 = em.create(LearningObject, {
hruid: 'id04',
language: Language.English,
version: 1,
admins: [],
title: 'making movies',
description: 'fifth album',
contentType: DwengoContentType.TEXT_MARKDOWN,
keywords: [],
teacherExclusive: false,
skosConcepts: [],
educationalGoals: [],
copyright: '',
license: '',
estimatedTime: 55,
returnValue: returnValue,
available: true,
contentLocation: '',
attachments: [],
content: Buffer.from(
'I put my hand upon the lever \
Said let it rock and let it roll \
I had the one-arm bandit fever \
There was an arrow through my heart and my soul'
),
});
const learningObject05 = em.create(LearningObject, {
hruid: 'id05',
language: Language.English,
version: 1,
admins: [],
title: 'on every street',
description: 'sixth album',
contentType: DwengoContentType.TEXT_MARKDOWN,
keywords: [],
teacherExclusive: false,
skosConcepts: [],
educationalGoals: [],
copyright: '',
license: '',
estimatedTime: 55,
returnValue: returnValue,
available: true,
contentLocation: '',
attachments: [],
content: Buffer.from('calling Elvis, is anybody home, calling elvis, I am here all alone'),
});
return [learningObject01, learningObject02, learningObject03, learningObject04, learningObject05];
}

View file

@ -0,0 +1,100 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { LearningPath } from '../../../src/entities/content/learning-path.entity';
import { Language } from '../../../src/entities/content/language';
import { LearningPathTransition } from '../../../src/entities/content/learning-path-transition.entity';
import { LearningPathNode } from '../../../src/entities/content/learning-path-node.entity';
export function makeTestLearningPaths(em: EntityManager<IDatabaseDriver<Connection>>): Array<LearningPath> {
const learningPathNode01: LearningPathNode = new LearningPathNode();
const learningPathNode02: LearningPathNode = new LearningPathNode();
const learningPathNode03: LearningPathNode = new LearningPathNode();
const learningPathNode04: LearningPathNode = new LearningPathNode();
const learningPathNode05: LearningPathNode = new LearningPathNode();
const transitions01: LearningPathTransition = new LearningPathTransition();
const transitions02: LearningPathTransition = new LearningPathTransition();
const transitions03: LearningPathTransition = new LearningPathTransition();
const transitions04: LearningPathTransition = new LearningPathTransition();
const transitions05: LearningPathTransition = new LearningPathTransition();
transitions01.condition = 'true';
transitions01.next = learningPathNode02;
transitions02.condition = 'true';
transitions02.next = learningPathNode02;
transitions03.condition = 'true';
transitions03.next = learningPathNode04;
transitions04.condition = 'true';
transitions04.next = learningPathNode05;
transitions05.condition = 'true';
transitions05.next = learningPathNode05;
learningPathNode01.instruction = '';
learningPathNode01.language = Language.English;
learningPathNode01.learningObjectHruid = 'id01';
learningPathNode01.startNode = true;
learningPathNode01.transitions = [transitions01];
learningPathNode01.version = 1;
learningPathNode02.instruction = '';
learningPathNode02.language = Language.English;
learningPathNode02.learningObjectHruid = 'id02';
learningPathNode02.startNode = false;
learningPathNode02.transitions = [transitions02];
learningPathNode02.version = 1;
learningPathNode03.instruction = '';
learningPathNode03.language = Language.English;
learningPathNode03.learningObjectHruid = 'id03';
learningPathNode03.startNode = true;
learningPathNode03.transitions = [transitions03];
learningPathNode03.version = 1;
learningPathNode04.instruction = '';
learningPathNode04.language = Language.English;
learningPathNode04.learningObjectHruid = 'id04';
learningPathNode04.startNode = false;
learningPathNode04.transitions = [transitions04];
learningPathNode04.version = 1;
learningPathNode05.instruction = '';
learningPathNode05.language = Language.English;
learningPathNode05.learningObjectHruid = 'id05';
learningPathNode05.startNode = false;
learningPathNode05.transitions = [transitions05];
learningPathNode05.version = 1;
const nodes01: Array<LearningPathNode> = [
// LearningPathNode01,
// LearningPathNode02,
];
const learningPath01 = em.create(LearningPath, {
hruid: 'id01',
language: Language.English,
admins: [],
title: 'repertoire Tool',
description: 'all about Tool',
image: '',
nodes: nodes01,
});
const nodes02: Array<LearningPathNode> = [
// LearningPathNode03,
// LearningPathNode04,
// LearningPathNode05,
];
const learningPath02 = em.create(LearningPath, {
hruid: 'id02',
language: Language.English,
admins: [],
title: 'repertoire Dire Straits',
description: 'all about Dire Straits',
image: '',
nodes: nodes02,
});
return [learningPath01, learningPath02];
}

View file

@ -0,0 +1,32 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { Answer } from '../../../src/entities/questions/answer.entity';
import { Teacher } from '../../../src/entities/users/teacher.entity';
import { Question } from '../../../src/entities/questions/question.entity';
export function makeTestAnswers(em: EntityManager<IDatabaseDriver<Connection>>, teachers: Array<Teacher>, questions: Array<Question>): Array<Answer> {
const answer01 = em.create(Answer, {
author: teachers[0],
toQuestion: questions[1],
sequenceNumber: 1,
timestamp: new Date(),
content: 'answer',
});
const answer02 = em.create(Answer, {
author: teachers[0],
toQuestion: questions[1],
sequenceNumber: 2,
timestamp: new Date(),
content: 'answer2',
});
const answer03 = em.create(Answer, {
author: teachers[1],
toQuestion: questions[3],
sequenceNumber: 1,
timestamp: new Date(),
content: 'answer3',
});
return [answer01, answer02, answer03];
}

View file

@ -0,0 +1,48 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { Question } from '../../../src/entities/questions/question.entity';
import { Language } from '../../../src/entities/content/language';
import { Student } from '../../../src/entities/users/student.entity';
export function makeTestQuestions(em: EntityManager<IDatabaseDriver<Connection>>, students: Array<Student>): Array<Question> {
const question01 = em.create(Question, {
learningObjectLanguage: Language.English,
learningObjectVersion: '1',
learningObjectHruid: 'id05',
sequenceNumber: 1,
author: students[0],
timestamp: new Date(),
content: 'question',
});
const question02 = em.create(Question, {
learningObjectLanguage: Language.English,
learningObjectVersion: '1',
learningObjectHruid: 'id05',
sequenceNumber: 2,
author: students[2],
timestamp: new Date(),
content: 'question',
});
const question03 = em.create(Question, {
learningObjectLanguage: Language.English,
learningObjectVersion: '1',
learningObjectHruid: 'id04',
sequenceNumber: 1,
author: students[0],
timestamp: new Date(),
content: 'question',
});
const question04 = em.create(Question, {
learningObjectLanguage: Language.English,
learningObjectVersion: '1',
learningObjectHruid: 'id01',
sequenceNumber: 1,
author: students[1],
timestamp: new Date(),
content: 'question',
});
return [question01, question02, question03, question04];
}

View file

@ -0,0 +1,49 @@
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
import { Student } from '../../../src/entities/users/student.entity';
export function makeTestStudents(em: EntityManager<IDatabaseDriver<Connection>>): Array<Student> {
const student01 = em.create(Student, {
username: 'Noordkaap',
firstName: 'Stijn',
lastName: 'Meuris',
});
const student02 = em.create(Student, {
username: 'DireStraits',
firstName: 'Mark',
lastName: 'Knopfler',
});
const student03 = em.create(Student, {
username: 'Tool',
firstName: 'Maynard',
lastName: 'Keenan',
});
const student04 = em.create(Student, {
username: 'SmashingPumpkins',
firstName: 'Billy',
lastName: 'Corgan',
});
const student05 = em.create(Student, {
username: 'PinkFloyd',
firstName: 'David',
lastName: 'Gilmoure',
});
const student06 = em.create(Student, {
username: 'TheDoors',
firstName: 'Jim',
lastName: 'Morisson',
});
// Do not use for any tests, gets deleted in a unit test
const student07 = em.create(Student, {
username: 'Nirvana',
firstName: 'Kurt',
lastName: 'Cobain',
});
return [student01, student02, student03, student04, student05, student06, student07];
}

View file

@ -0,0 +1,31 @@
import { Teacher } from '../../../src/entities/users/teacher.entity';
import { Connection, EntityManager, IDatabaseDriver } from '@mikro-orm/core';
export function makeTestTeachers(em: EntityManager<IDatabaseDriver<Connection>>): Array<Teacher> {
const teacher01 = em.create(Teacher, {
username: 'FooFighters',
firstName: 'Dave',
lastName: 'Grohl',
});
const teacher02 = em.create(Teacher, {
username: 'LimpBizkit',
firstName: 'Fred',
lastName: 'Durst',
});
const teacher03 = em.create(Teacher, {
username: 'Staind',
firstName: 'Aaron',
lastName: 'Lewis',
});
// Should not be used, gets deleted in a unit test
const teacher04 = em.create(Teacher, {
username: 'ZesdeMetaal',
firstName: 'Wannes',
lastName: 'Cappelle',
});
return [teacher01, teacher02, teacher03, teacher04];
}

View file

@ -5,7 +5,7 @@
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const route = useRoute(); const route = useRoute();
const { t, locale } = useI18n() const { t, locale } = useI18n();
// Instantiate variables to use in html to render right // Instantiate variables to use in html to render right
// Links and content dependent on the role (student or teacher) // Links and content dependent on the role (student or teacher)
@ -30,7 +30,7 @@
// Logic to change the language of the website to the selected language // Logic to change the language of the website to the selected language
const changeLanguage = (langCode: string) => { const changeLanguage = (langCode: string) => {
locale.value = langCode; locale.value = langCode;
localStorage.setItem('user-lang', langCode); localStorage.setItem("user-lang", langCode);
console.log(langCode); console.log(langCode);
}; };
</script> </script>
@ -59,22 +59,22 @@
:to="`/${role}/${userId}/assignment`" :to="`/${role}/${userId}/assignment`"
class="menu_item" class="menu_item"
> >
{{ t('assignments') }} {{ t("assignments") }}
</router-link> </router-link>
</li> </li>
<li> <li>
<router-link <router-link
:to="`/${role}/${userId}/class`" :to="`/${role}/${userId}/class`"
class="menu_item" class="menu_item"
>{{ t('classes') }}</router-link >{{ t("classes") }}</router-link
> >
</li> </li>
<li> <li>
<router-link <router-link
:to="`/${role}/${userId}/discussion`" :to="`/${role}/${userId}/discussion`"
class="menu_item" class="menu_item"
>{{ t('discussions') }} </router-link >{{ t("discussions") }}
> </router-link>
</li> </li>
<li> <li>
<v-menu open-on-hover> <v-menu open-on-hover>

View file

@ -1,4 +1,4 @@
import { createI18n } from 'vue-i18n'; import { createI18n } from "vue-i18n";
// Import translations // Import translations
import en from "@/i18n/locale/en.json"; import en from "@/i18n/locale/en.json";
@ -6,11 +6,11 @@ import nl from "@/i18n/locale/nl.json";
import fr from "@/i18n/locale/fr.json"; import fr from "@/i18n/locale/fr.json";
import de from "@/i18n/locale/de.json"; import de from "@/i18n/locale/de.json";
const savedLocale = localStorage.getItem('user-lang') || 'en'; const savedLocale = localStorage.getItem("user-lang") || "en";
const i18n = createI18n({ const i18n = createI18n({
locale: savedLocale, locale: savedLocale,
fallbackLocale: 'en', fallbackLocale: "en",
messages: { messages: {
en: en, en: en,
nl: nl, nl: nl,

View file

@ -11,7 +11,6 @@ import i18n from "./i18n/i18n.ts";
import App from "./App.vue"; import App from "./App.vue";
import router from "./router"; import router from "./router";
const app = createApp(App); const app = createApp(App);
app.use(router); app.use(router);

4104
package-lock.json generated

File diff suppressed because it is too large Load diff