Skip to content

Commit

Permalink
EW-1008 Decouple lesson dependency from common-cartridge module (#5300)
Browse files Browse the repository at this point in the history
Add generated API for Lesson to decouple common cartridge module
  • Loading branch information
MajedAlaitwniCap authored and hoeppner-dataport committed Oct 25, 2024
1 parent eb90633 commit ff37ab0
Show file tree
Hide file tree
Showing 33 changed files with 1,803 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './lesson.dto';
export * from './lesson-materials.dto';
export * from './lesson-contents.dto';
export * from './lesson-linked-task.dto';
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export class LessonContentDto {
content: object;

title: string;

component: LessonContentDtoComponent;

hidden: boolean;

constructor(props: LessonContentDto) {
this.content = props.content;
this.title = props.title;
this.component = props.component;
this.hidden = props.hidden;
}
}

export const LessonContentDtoComponentValues = {
ETHERPAD: 'Etherpad',
GEO_GEBRA: 'geoGebra',
INTERNAL: 'internal',
RESOURCES: 'resources',
TEXT: 'text',
NE_XBOARD: 'neXboard',
} as const;

export type LessonContentDtoComponent =
typeof LessonContentDtoComponentValues[keyof typeof LessonContentDtoComponentValues];
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
export class LessonLinkedTaskDto {
name: string;

description: string;

descriptionInputFormat: LessonLinkedTaskDescriptionInputFormatType;

availableDate: string | null;

dueDate: string | null;

private: boolean;

publicSubmissions: boolean | null;

teamSubmissions: boolean | null;

creator: string | null;

courseId: string | null;

submissionIds: string[];

finishedIds: string[];

constructor(props: LessonLinkedTaskDto) {
this.name = props.name;
this.description = props.description;
this.descriptionInputFormat = props.descriptionInputFormat;
this.availableDate = props.availableDate;
this.dueDate = props.dueDate;
this.private = props.private;
this.creator = props.creator;
this.courseId = props.courseId;
this.publicSubmissions = props.publicSubmissions;
this.teamSubmissions = props.teamSubmissions;
this.submissionIds = props.submissionIds;
this.finishedIds = props.finishedIds;
}
}

export const LessonLinkedTaskDescriptionInputFormat = {
PLAIN_TEXT: 'plainText',
RICH_TEXT_CK5_SIMPLE: 'richTextCk5Simple',
RICH_TEXT_CK4: 'richTextCk4',
RICH_TEXT_CK5: 'richTextCk5',
} as const;

export type LessonLinkedTaskDescriptionInputFormatType =
typeof LessonLinkedTaskDescriptionInputFormat[keyof typeof LessonLinkedTaskDescriptionInputFormat];
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export class LessonMaterialsDto {
materialsId: string;

title: string;

relatedResources: string[];

url: string;

client: string;

license: string[];

merlinReference: string;

constructor(props: LessonMaterialsDto) {
this.materialsId = props.materialsId;
this.title = props.title;
this.relatedResources = props.relatedResources;
this.url = props.url;
this.client = props.client;
this.license = props.license;
this.merlinReference = props.merlinReference;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { LessonContentDto } from './lesson-contents.dto';
import { LessonMaterialsDto } from './lesson-materials.dto';

export class LessonDto {
lessonId: string;

name: string;

courseId?: string;

courseGroupId?: string;

hidden: boolean;

position: number;

contents: LessonContentDto[];

materials: LessonMaterialsDto[];

constructor(props: LessonDto) {
this.lessonId = props.lessonId;
this.name = props.name;
this.courseId = props.courseId;
this.courseGroupId = props.courseGroupId;
this.hidden = props.hidden;
this.position = props.position;
this.contents = props.contents;
this.materials = props.materials;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import { faker } from '@faker-js/faker';
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { UnauthorizedException } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Test, TestingModule } from '@nestjs/testing';
import { AxiosResponse } from 'axios';
import { Request } from 'express';
import { LessonClientAdapter } from './lesson-client.adapter';
import { LessonApi, LessonLinkedTaskResponse, LessonResponse } from './lessons-api-client';

describe(LessonClientAdapter.name, () => {
let module: TestingModule;
let sut: LessonClientAdapter;
let lessonApiMock: DeepMocked<LessonApi>;
const jwtToken = faker.string.alphanumeric(20);

beforeAll(async () => {
module = await Test.createTestingModule({
providers: [
LessonClientAdapter,
{
provide: LessonApi,
useValue: createMock<LessonApi>(),
},
{
provide: REQUEST,
useValue: createMock<Request>({
headers: {
authorization: `Bearer ${jwtToken}`,
},
}),
},
],
}).compile();

sut = module.get(LessonClientAdapter);
lessonApiMock = module.get(LessonApi);
});

afterAll(async () => {
await module.close();
});

afterEach(() => {
jest.resetAllMocks();
});

it('should be defined', () => {
expect(sut).toBeDefined();
});

describe('getLessonById', () => {
describe('When getLessonById is called', () => {
const setup = () => {
const response = createMock<AxiosResponse<LessonResponse>>({
data: {
_id: faker.string.uuid(),
id: faker.string.uuid(),
name: faker.lorem.sentence(),
courseId: faker.string.uuid(),
courseGroupId: faker.string.uuid(),
hidden: faker.datatype.boolean(),
position: faker.number.int(),
contents: [
{
content: { text: faker.lorem.sentence() },
_id: faker.string.uuid(),
id: faker.string.uuid(),
title: faker.lorem.sentence(),
component: faker.helpers.arrayElement(['Etherpad', 'geoGebra', 'neXboard']),
hidden: faker.datatype.boolean(),
},
],
materials: [
{
_id: faker.string.uuid(),
id: faker.string.uuid(),
title: faker.lorem.sentence(),
relatedResources: [faker.lorem.sentence()],
url: faker.internet.url(),
client: faker.lorem.sentence(),
license: [faker.lorem.sentence()],
merlinReference: faker.lorem.sentence(),
},
],
},
});

lessonApiMock.lessonControllerGetLesson.mockResolvedValue(response);

return { lessonId: response.data.id };
};

it('should call lessonControllerGetLesson', async () => {
const { lessonId } = setup();

await sut.getLessonById(lessonId);

expect(lessonApiMock.lessonControllerGetLesson).toHaveBeenCalled();
});
});

describe('When getLessonById is called with invalid id', () => {
const setup = () => {
const lessonResponseId = faker.string.uuid();

lessonApiMock.lessonControllerGetLesson.mockRejectedValueOnce(new Error('error'));

return { lessonResponseId };
};

it('should throw an error', async () => {
const { lessonResponseId } = setup();

const result = sut.getLessonById(lessonResponseId);

await expect(result).rejects.toThrowError('error');
});
});

describe('When no JWT token is found', () => {
const setup = () => {
const lessonResponseId = faker.string.uuid();
const request = createMock<Request>({
headers: {},
}) as Request;

const adapter: LessonClientAdapter = new LessonClientAdapter(lessonApiMock, request);

return { lessonResponseId, adapter };
};

it('should throw an UnauthorizedError', async () => {
const { lessonResponseId, adapter } = setup();

await expect(adapter.getLessonById(lessonResponseId)).rejects.toThrowError(UnauthorizedException);
});
});
});

describe('getLessonTasks', () => {
describe('When getLessonTasks is called', () => {
const setup = () => {
const lessonId = faker.string.uuid();
const response = createMock<AxiosResponse<LessonLinkedTaskResponse[]>>({
data: [
{
name: faker.lorem.sentence(),
description: faker.lorem.sentence(),
descriptionInputFormat: faker.helpers.arrayElement(['plainText', 'richTextCk4', 'richTextCk5Simple']),
availableDate: faker.date.recent().toString(),
dueDate: faker.date.future().toString(),
private: faker.datatype.boolean(),
publicSubmissions: faker.datatype.boolean(),
teamSubmissions: faker.datatype.boolean(),
},
],
});

lessonApiMock.lessonControllerGetLessonTasks.mockResolvedValue(response);

return { lessonId };
};

it('should call lessonControllerGetLessonTasks', async () => {
const { lessonId } = setup();

await sut.getLessonTasks(lessonId);

expect(lessonApiMock.lessonControllerGetLessonTasks).toHaveBeenCalled();
});
});

describe('When getLessonTasks is called with invalid id', () => {
const setup = () => {
const lessonResponseId = faker.string.uuid();

lessonApiMock.lessonControllerGetLessonTasks.mockRejectedValueOnce(new Error('error'));

return { lessonResponseId };
};

it('should throw an error', async () => {
const { lessonResponseId } = setup();

const result = sut.getLessonTasks(lessonResponseId);

await expect(result).rejects.toThrowError('error');
});
});

describe('When no JWT token is found', () => {
const setup = () => {
const lessonResponseId = faker.string.uuid();
const request = createMock<Request>({
headers: {},
}) as Request;

const adapter: LessonClientAdapter = new LessonClientAdapter(lessonApiMock, request);

return { lessonResponseId, adapter };
};

it('should throw an UnauthorizedError', async () => {
const { lessonResponseId, adapter } = setup();

await expect(adapter.getLessonTasks(lessonResponseId)).rejects.toThrowError(UnauthorizedException);
});
});
});
});
Loading

0 comments on commit ff37ab0

Please sign in to comment.