Skip to content

Commit

Permalink
Thr 20 create UI elements (#4190)
Browse files Browse the repository at this point in the history
* Necessary changes to support H5P on the client
  • Loading branch information
marode-cap authored Jul 31, 2023
1 parent 4befc25 commit f3aba94
Show file tree
Hide file tree
Showing 14 changed files with 341 additions and 124 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard';
import request from 'supertest';
import { EntityManager } from '@mikro-orm/mongodb';
import { EntityManager, ObjectId } from '@mikro-orm/mongodb';
import { ExecutionContext, INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { Permission } from '@shared/domain';
Expand All @@ -23,8 +23,8 @@ class API {
}

const setup = () => {
const contentId = '12345';
const notExistingContentId = '12345';
const contentId = new ObjectId(0).toString();
const notExistingContentId = new ObjectId(1).toString();
const badContentId = '';

return { contentId, notExistingContentId, badContentId };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DeepMocked, createMock } from '@golevelup/ts-jest';
import { ContentMetadata } from '@lumieducation/h5p-server/build/src/ContentMetadata';
import { EntityManager } from '@mikro-orm/core';
import { EntityManager, ObjectId } from '@mikro-orm/mongodb';
import { HttpStatus, INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { TestApiClient, UserAndAccountTestFactory } from '@shared/testing';
Expand Down Expand Up @@ -128,43 +128,45 @@ describe('H5PEditor Controller (api)', () => {

const loggedInClient = await testApiClient.login(studentAccount);

return { loggedInClient };
const dummyId = new ObjectId(0).toString();

return { loggedInClient, dummyId };
};

it('should return the content file', async () => {
const { loggedInClient } = await setup();
const { loggedInClient, dummyId } = await setup();

const mockFile = { content: 'Test File', size: 9, name: 'test.txt', birthtime: new Date() };

contentStorage.getFileStream.mockResolvedValueOnce(Readable.from(mockFile.content));
contentStorage.getFileStats.mockResolvedValueOnce({ birthtime: mockFile.birthtime, size: mockFile.size });

const response = await loggedInClient.get(`content/dummyId/${mockFile.name}`);
const response = await loggedInClient.get(`content/${dummyId}/${mockFile.name}`);

expect(response.statusCode).toEqual(HttpStatus.OK);
expect(response.text).toBe(mockFile.content);
});

it('should work with range requests', async () => {
const { loggedInClient } = await setup();
const { loggedInClient, dummyId } = await setup();

const mockFile = { content: 'Test File', size: 9, name: 'test.txt', birthtime: new Date() };

contentStorage.getFileStream.mockResolvedValueOnce(Readable.from(mockFile.content));
contentStorage.getFileStats.mockResolvedValueOnce({ birthtime: mockFile.birthtime, size: mockFile.size });

const response = await loggedInClient.get(`content/dummyId/${mockFile.name}`).set('Range', 'bytes=2-4');
const response = await loggedInClient.get(`content/${dummyId}/${mockFile.name}`).set('Range', 'bytes=2-4');

expect(response.statusCode).toEqual(HttpStatus.PARTIAL_CONTENT);
expect(response.text).toBe(mockFile.content);
});

it('should return 404 if file does not exist', async () => {
const { loggedInClient } = await setup();
const { loggedInClient, dummyId } = await setup();

contentStorage.getFileStats.mockRejectedValueOnce(new Error('Does not exist'));

const response = await loggedInClient.get(`content/dummyId/nonexistant.txt`);
const response = await loggedInClient.get(`content/${dummyId}/nonexistant.txt`);

expect(response.statusCode).toEqual(HttpStatus.NOT_FOUND);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard';
import request from 'supertest';
import { EntityManager } from '@mikro-orm/mongodb';
import { EntityManager, ObjectId } from '@mikro-orm/mongodb';
import { ExecutionContext, INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { Permission } from '@shared/domain';
Expand All @@ -17,17 +17,35 @@ class API {
this.app = app;
}

async emptyEditor() {
return request(this.app.getHttpServer()).get(`/h5p-editor/edit`);
}

async editH5pContent(contentId: string) {
return request(this.app.getHttpServer()).get(`/h5p-editor/${contentId}`);
return request(this.app.getHttpServer()).get(`/h5p-editor/edit/${contentId}`);
}
}

const setup = () => {
const contentId = '12345';
const notExistingContentId = '12345';
const contentId = new ObjectId(0).toString();
const notExistingContentId = new ObjectId(1).toString();
const badContentId = '';

return { contentId, notExistingContentId, badContentId };
const editorModel = {
scripts: ['example.js'],
styles: ['example.css'],
};

const exampleContent = {
h5p: {},
library: 'ExampleLib-1.0',
params: {
metadata: {},
params: { anything: true },
},
};

return { contentId, notExistingContentId, badContentId, editorModel, exampleContent };
};

describe('H5PEditor Controller (api)', () => {
Expand Down Expand Up @@ -67,6 +85,38 @@ describe('H5PEditor Controller (api)', () => {
await app.close();
});

describe('get new h5p editor', () => {
beforeEach(async () => {
await cleanupCollections(em);
const school = schoolFactory.build();
const roles = roleFactory.buildList(1, {
permissions: [Permission.FILESTORAGE_CREATE, Permission.FILESTORAGE_VIEW],
});
const user = userFactory.build({ school, roles });

await em.persistAndFlush([user, school]);
em.clear();

currentUser = mapUserToCurrentUser(user);
});
describe('with valid request params', () => {
it('should return 200 status', async () => {
const { editorModel } = setup();
// @ts-expect-error partial object
h5PEditorUc.getEmptyH5pEditor.mockResolvedValueOnce(editorModel);
const response = await api.emptyEditor();
expect(response.status).toEqual(200);
});
});
describe('with bad request params', () => {
it('should return 500 status', async () => {
h5PEditorUc.getEmptyH5pEditor.mockRejectedValueOnce(new Error('Could not get H5P editor'));
const response = await api.emptyEditor();
expect(response.status).toEqual(500);
});
});
});

describe('get h5p editor', () => {
beforeEach(async () => {
await cleanupCollections(em);
Expand All @@ -83,8 +133,9 @@ describe('H5PEditor Controller (api)', () => {
});
describe('with valid request params', () => {
it('should return 200 status', async () => {
const { contentId } = setup();
h5PEditorUc.getH5pEditor.mockResolvedValueOnce('iFrame');
const { contentId, editorModel, exampleContent } = setup();
// @ts-expect-error partial object
h5PEditorUc.getH5pEditor.mockResolvedValueOnce({ editorModel, content: exampleContent });
const response = await api.editH5pContent(contentId);
expect(response.status).toEqual(200);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard';
import request from 'supertest';
import { EntityManager } from '@mikro-orm/mongodb';
import { EntityManager, ObjectId } from '@mikro-orm/mongodb';
import { ExecutionContext, INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { Permission } from '@shared/domain';
Expand All @@ -23,10 +23,19 @@ class API {
}

const setup = () => {
const contentId = '12345';
const notExistingContentId = '12345';
const contentId = new ObjectId(0).toString();
const notExistingContentId = new ObjectId(1).toString();

return { contentId, notExistingContentId };
const playerResult = {
contentId,
dependencies: [],
downloadPath: '',
embedTypes: ['iframe'],
scripts: ['example.js'],
styles: ['example.css'],
};

return { contentId, notExistingContentId, playerResult };
};

describe('H5PEditor Controller (api)', () => {
Expand Down Expand Up @@ -82,8 +91,9 @@ describe('H5PEditor Controller (api)', () => {
});
describe('with valid request params', () => {
it('should return 200 status', async () => {
const { contentId } = setup();
h5PEditorUc.getH5pPlayer.mockResolvedValueOnce('iFrame');
const { contentId, playerResult } = setup();
// @ts-expect-error partial object
h5PEditorUc.getH5pPlayer.mockResolvedValueOnce(playerResult);
const response = await api.getPlayer(contentId);
expect(response.status).toEqual(200);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard';
import request from 'supertest';
import { EntityManager } from '@mikro-orm/mongodb';
import { EntityManager, ObjectId } from '@mikro-orm/mongodb';
import { ExecutionContext, INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { Permission } from '@shared/domain';
Expand All @@ -14,9 +14,9 @@ import { H5PEditorTestModule } from '../../h5p-editor-test.module';
import { H5PEditorUc } from '../../uc/h5p.uc';

const setup = () => {
const contentId = '12345';
const contentId = new ObjectId(0);
const createContentId = 'create';
const notExistingContentId = '12345';
const notExistingContentId = new ObjectId(1);
const badContentId = '';
const id = '0000000';
const metadata: IContentMetadata = {
Expand All @@ -37,7 +37,7 @@ class API {
this.app = app;
}

async createOrSave(contentId: string) {
async create() {
const body = {
params: {
params: {},
Expand All @@ -46,7 +46,19 @@ class API {
metadata: {},
library: {},
};
return request(this.app.getHttpServer()).post(`/h5p-editor/${contentId}`).send(body);
return request(this.app.getHttpServer()).post(`/h5p-editor/edit/`).send(body);
}

async save(contentId: string) {
const body = {
params: {
params: {},
metadata: {},
},
metadata: {},
library: {},
};
return request(this.app.getHttpServer()).post(`/h5p-editor/edit/${contentId}`).send(body);
}
}

Expand Down Expand Up @@ -103,10 +115,10 @@ describe('H5PEditor Controller (api)', () => {
});
describe('with valid request params', () => {
it('should return 201 status', async () => {
const { createContentId, id, metadata } = setup();
const { id, metadata } = setup();
const result1 = { id, metadata };
h5PEditorUc.saveH5pContentGetMetadata.mockResolvedValueOnce(result1);
const response = await api.createOrSave(createContentId);
h5PEditorUc.createH5pContentGetMetadata.mockResolvedValueOnce(result1);
const response = await api.create();
expect(response.status).toEqual(201);
});
});
Expand All @@ -130,15 +142,15 @@ describe('H5PEditor Controller (api)', () => {
const { contentId, id, metadata } = setup();
const result1 = { id, metadata };
h5PEditorUc.saveH5pContentGetMetadata.mockResolvedValueOnce(result1);
const response = await api.createOrSave(contentId);
const response = await api.save(contentId.toString());
expect(response.status).toEqual(201);
});
});
describe('with bad request params', () => {
it('should return 500 status', async () => {
const { notExistingContentId } = setup();
h5PEditorUc.saveH5pContentGetMetadata.mockRejectedValueOnce(new Error('Could not save H5P content'));
const response = await api.createOrSave(notExistingContentId);
const response = await api.save(notExistingContentId.toString());
expect(response.status).toEqual(500);
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IsMongoId, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';

export class AjaxPostQueryParams {
@IsString()
Expand All @@ -22,6 +22,6 @@ export class AjaxPostQueryParams {
language?: string;

@IsString()
@IsMongoId()
@IsOptional()
id?: string;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { IContentMetadata } from '@lumieducation/h5p-server';
import { ApiProperty } from '@nestjs/swagger';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { SanitizeHtml } from '@shared/controller';
import { IsMongoId, IsNotEmpty, IsObject, IsOptional, IsString, Matches } from 'class-validator';

export class GetH5PContentParams {
@ApiProperty()
@ApiPropertyOptional()
@Matches('([a-z]+-[a-z]+)')
@IsString()
@IsOptional()
Expand All @@ -14,6 +15,18 @@ export class GetH5PContentParams {
contentId!: string;
}

export class GetH5PEditorParams {
@ApiProperty()
@IsMongoId()
contentId!: string;
}

export class SaveH5PEditorParams {
@ApiProperty()
@IsMongoId()
contentId!: string;
}

export class PostH5PContentParams {
@ApiProperty()
@IsMongoId()
Expand All @@ -29,6 +42,7 @@ export class PostH5PContentParams {

@ApiProperty()
@IsString()
@SanitizeHtml()
@IsNotEmpty()
mainLibraryUbername!: string;
}
Expand All @@ -43,11 +57,6 @@ export class PostH5PContentCreateParams {
metadata: IContentMetadata;
};

@ApiProperty()
@IsObject()
@IsOptional()
metadata!: IContentMetadata;

@ApiProperty()
@IsString()
@IsNotEmpty()
Expand Down
Loading

0 comments on commit f3aba94

Please sign in to comment.