diff --git a/src/lib/PiscinaUtils/runner.ts b/src/lib/PiscinaUtils/runner.ts index 1f068f25..4b96f049 100644 --- a/src/lib/PiscinaUtils/runner.ts +++ b/src/lib/PiscinaUtils/runner.ts @@ -56,43 +56,66 @@ const piscina = new Piscina({ * Method which sends task to a worker thread (circuit) * @param pubANodes * @param pubBNodes + * @param piscinaInstance */ -async function compareNodesInBackground(pubANodes: number[], pubBNodes: number[]): Promise { - return piscina.run({ pubANodes, pubBNodes }, {name: 'compareNodes'}); +export async function compareNodesInBackground( + pubANodes: number[], + pubBNodes: number[], + piscinaInstance: Piscina = piscina +): Promise { + return piscinaInstance.run({ pubANodes, pubBNodes }, {name: 'compareNodes'}); } /** * Method which sends task to a worker thread (meta) * @param pubAMeta * @param pubBMeta + * @param piscinaInstance */ -async function compareMetaInBackground(pubAMeta: PublicationMeta, pubBMeta: PublicationMeta): Promise { - return piscina.run({ pubA: pubAMeta, pubB: pubBMeta }, {name: 'compareMeta'}); +export async function compareMetaInBackground( + pubAMeta: PublicationMeta, + pubBMeta: PublicationMeta, + piscinaInstance: Piscina = piscina +): Promise { + return piscinaInstance.run({ pubA: pubAMeta, pubB: pubBMeta }, {name: 'compareMeta'}); } /** * Method which sends task to a worker thread (files) * @param pubAFiles * @param pubBFiles + * @param piscinaInstance */ -async function compareFilesInBackground(pubAFiles: PrismaFile[], pubBFiles: PrismaFile[]): Promise { - return piscina.run({ pubAFiles, pubBFiles }, {name: 'compareFiles'}); +export async function compareFilesInBackground( + pubAFiles: PrismaFile[], + pubBFiles: PrismaFile[], + piscinaInstance: Piscina = piscina +): Promise { + return piscinaInstance.run({ pubAFiles, pubBFiles }, {name: 'compareFiles'}); } /** * Method which sends task to a worker thread (complete initial parsing of uploaded/edited publication) * @param pubFiles + * @param piscinaInstance */ -async function initialMaterialFileParseInBackground(pubFiles: PrismaFile[]): Promise { - return piscina.run({ pubFiles }, {name: 'initialParse'}); +export async function initialMaterialFileParseInBackground( + pubFiles: PrismaFile[], + piscinaInstance: Piscina = piscina +): Promise { + return piscinaInstance.run({ pubFiles }, {name: 'initialParse'}); } -export async function enqueueMaterialComparison(publicationId: number, materialId: number): Promise { +export async function enqueueMaterialComparison( + publicationId: number, + materialId: number, + piscinaInstance: Piscina = piscina +): Promise { try{ const currentFiles: PrismaFile[] = await getFilesForMaterial(materialId) // INITIALLY PARSE CURRENT PUBLICATION FILES IN ORDER TO REUSE TOKENS LATER ON - const initialParsing = await initialMaterialFileParseInBackground(currentFiles); + const initialParsing = await initialMaterialFileParseInBackground(currentFiles, piscinaInstance); await handleFileTokens(initialParsing) @@ -127,8 +150,8 @@ export async function enqueueMaterialComparison(publicationId: number, materialI comparisons.push({ fromPubId: publicationId, toPubId: materials[i].publicationId, - similarityFile: compareFilesInBackground(currentMaterial.files, materials[i].files), - similarityMeta: compareMetaInBackground(pubAMeta, pubBMeta) + similarityFile: compareFilesInBackground(currentMaterial.files, materials[i].files, piscinaInstance), + similarityMeta: compareMetaInBackground(pubAMeta, pubBMeta, piscinaInstance) }); } } @@ -169,7 +192,7 @@ export async function enqueueMaterialComparison(publicationId: number, materialI } } -export async function enqueueCircuitComparison(publicationId: number): Promise { +export async function enqueueCircuitComparison(publicationId: number, piscinaInstance: Piscina = piscina): Promise { try{ const circuits = await getAllCircuits([], [], 0, '', '', true); const currentCircuit = await getCircuitByPublicationId(publicationId) @@ -188,7 +211,6 @@ export async function enqueueCircuitComparison(publicationId: number): Promise n.publicationId), - circuits[i].nodes.map(n => n.publicationId)), - similarityMeta: compareMetaInBackground(pubAMeta, pubBMeta) + circuits[i].nodes.map(n => n.publicationId), piscinaInstance), + similarityMeta: compareMetaInBackground(pubAMeta, pubBMeta, piscinaInstance) }); } } diff --git a/src/lib/components/Filter.svelte b/src/lib/components/Filter.svelte deleted file mode 100644 index 7b90a295..00000000 --- a/src/lib/components/Filter.svelte +++ /dev/null @@ -1,201 +0,0 @@ - - -
- {#if type} - - - {:else} - - {/if} - {#if active} -
- {#if all.length > 10} - - {/if} - - {#if display.length === 0} -

No Matching {label.toLowerCase()}

- {:else} -
- {#if label === 'Publisher'} - {#each people as dis, i} - - {/each} - {:else if label === 'Types'} - {#each display as dis, i} - - {/each} - {:else} - {#each display as dis, i} - - {/each} - {/if} -
- {/if} -
- {/if} -
- - \ No newline at end of file diff --git a/src/lib/components/FilterButton.svelte b/src/lib/components/FilterButton.svelte deleted file mode 100644 index 7a07eaff..00000000 --- a/src/lib/components/FilterButton.svelte +++ /dev/null @@ -1,64 +0,0 @@ - - - - diff --git a/src/lib/components/PublicationCard.svelte b/src/lib/components/PublicationCard.svelte index 326ee717..53b0bb72 100644 --- a/src/lib/components/PublicationCard.svelte +++ b/src/lib/components/PublicationCard.svelte @@ -242,12 +242,13 @@ {/if} - +
{#if publication.type === PublicationType.Circuit} {:else} @@ -358,8 +359,11 @@
- CAIT Logo + CAIT Logo {#if isHoveredPfp}
- - - - - diff --git a/src/lib/components/TheoryAppBar.svelte b/src/lib/components/TheoryAppBar.svelte deleted file mode 100644 index 62fcd6ec..00000000 --- a/src/lib/components/TheoryAppBar.svelte +++ /dev/null @@ -1,97 +0,0 @@ - - -
-
-
-
- -
- -
-
-
-
-
-

{Math.round(value * 100)}%

-

{Math.round((1 - value) * 100)}%

-
-
-
-
- -
- -
- -
-
- diff --git a/src/lib/components/page/Header.svelte b/src/lib/components/page/Header.svelte index ff13801f..57d3e299 100644 --- a/src/lib/components/page/Header.svelte +++ b/src/lib/components/page/Header.svelte @@ -243,7 +243,11 @@ {#if dropDown} -
+
{#each navOptions as opt}
diff --git a/src/lib/database/material.ts b/src/lib/database/material.ts index 83d7f164..a20b503a 100644 --- a/src/lib/database/material.ts +++ b/src/lib/database/material.ts @@ -219,7 +219,7 @@ export async function createMaterialPublication( timeEstimate: number; theoryPractice: number; isDraft: boolean; - course: number; + course: number | null; }, prismaContext: Prisma.TransactionClient = prisma, ): Promise { diff --git a/src/lib/database/publication.ts b/src/lib/database/publication.ts index 384f3223..80b4cab3 100644 --- a/src/lib/database/publication.ts +++ b/src/lib/database/publication.ts @@ -1,6 +1,6 @@ import { Prisma } from '@prisma/client'; import { prisma } from '$lib/database/prisma'; -import { getPublicationById, getPublicationByIdLight } from '$lib/database/db'; +import { getPublicationByIdLight } from '$lib/database/db'; //////////////////////////////////////////////// // HELPER METHODS @@ -235,6 +235,8 @@ export async function updateAllTimeSaved(id: string, publicationId: number) { } return 'User saved previously'; } + + throw Error("Publication not found"); } export async function getReportsPublication(publicationId: number): Promise { diff --git a/src/lib/index.ts b/src/lib/index.ts index 052cce0c..49dd1c38 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -14,10 +14,8 @@ import IconLink from '$lib/components/generic/IconLink.svelte'; import UserMenu from '$lib/components/user/UserMenu.svelte'; import Footer from '$lib/components/page/Footer.svelte'; import Section from '$lib/components/generic/Section.svelte'; -import TheoryAppBar from '$lib/components/TheoryAppBar.svelte'; import PublicationCard from '$lib/components/PublicationCard.svelte'; import Comment from '$lib/components/Comment.svelte'; -import Filter from '$lib/components/Filter.svelte'; import CircuitComponent from '$lib/components/circuits/components/CircuitComponent.svelte'; import Download from '$lib/components/generic/Download.svelte'; import HorizontalScroll from '$lib/components/HorizontalScroll.svelte'; @@ -53,7 +51,6 @@ export { DifficultySelection, DiffBar, UserProfileBar, - TheoryAppBar, PublicationCard, SearchBar, UserProp, @@ -63,7 +60,6 @@ export { Footer, Section, lorem, - Filter, CircuitComponent, AddInteractionForm, UsedInCourse, diff --git a/src/lib/util/coursesLogic.ts b/src/lib/util/coursesLogic.ts index 552f5e15..c79ee16a 100644 --- a/src/lib/util/coursesLogic.ts +++ b/src/lib/util/coursesLogic.ts @@ -5,7 +5,14 @@ import type {User} from '$lib/database/user'; export type UserWithProfilePic = User & { profilePicData: string | null}; -export function changeCourse(newCourse: number | null, currentCourse: number | null, LOs: string[], PKs: string[] , courses: CourseWithCoverPic[], maintainers: UserWithProfilePic[]){ +export function changeCourse( + newCourse: number | null, + currentCourse: number | null, + LOs: string[], + PKs: string[] , + courses: CourseWithCoverPic[], + maintainers: UserWithProfilePic[] +){ // Remove learning objectives and prerequisites that are a part of the previous course const prevCourse = courses.find(c => c.id === currentCourse); LOs = LOs.filter(l => !prevCourse?.learningObjectives.includes(l)); diff --git a/tests/unit/circuit.test.ts b/tests/unit/circuit.test.ts index 1017c82d..814ee8b1 100644 --- a/tests/unit/circuit.test.ts +++ b/tests/unit/circuit.test.ts @@ -14,6 +14,25 @@ import { } from '$lib/database/circuit'; import { Difficulty } from '@prisma/client'; +const metadata = { + title: 'Introduction to Quantum Physics', + description: + "A beginner's guide to understanding the basic concepts of quantum physics.", + difficulty: Difficulty.easy, + learningObjectives: [ + 'Understand the fundamental principles of quantum mechanics', + 'Learn about wave-particle duality', + 'Familiarize with the concept of superposition', + 'Gain insights into quantum entanglement', + ], + prerequisites: [ + 'Basic knowledge of classical physics', + 'Familiarity with algebra and calculus', + ], + isDraft: false +}; + + describe('get specific circuit', () => { it('should return a circuit by publicationId', async () => { prisma.circuit.findUnique = vi.fn().mockResolvedValue({ @@ -48,22 +67,7 @@ describe('create specific circuit', () => { const material = await createCircuitPublication( '1', 5, - { - title: 'Introduction to Quantum Physics', - description: - "A beginner's guide to understanding the basic concepts of quantum physics.", - difficulty: Difficulty.easy, - learningObjectives: [ - 'Understand the fundamental principles of quantum mechanics', - 'Learn about wave-particle duality', - 'Familiarize with the concept of superposition', - 'Gain insights into quantum entanglement', - ], - prerequisites: [ - 'Basic knowledge of classical physics', - 'Familiarity with algebra and calculus', - ], - }, + metadata, prisma, ); expect(material).toMatchObject({ id: 2, publicationId: 1 }); @@ -80,22 +84,7 @@ describe('update specific circuit', () => { const material = await updateCircuitByPublicationId( 1, 5, - { - title: 'Introduction to Quantum Physics', - description: - "A beginner's guide to understanding the basic concepts of quantum physics.", - difficulty: Difficulty.easy, - learningObjectives: [ - 'Understand the fundamental principles of quantum mechanics', - 'Learn about wave-particle duality', - 'Familiarize with the concept of superposition', - 'Gain insights into quantum entanglement', - ], - prerequisites: [ - 'Basic knowledge of classical physics', - 'Familiarity with algebra and calculus', - ], - }, + metadata, prisma, ); expect(material).toMatchObject({ id: 2, publicationId: 1 }); diff --git a/tests/unit/components/ToastStoreWrapper.svelte b/tests/unit/components/ToastStoreWrapper.svelte new file mode 100644 index 00000000..26bc492f --- /dev/null +++ b/tests/unit/components/ToastStoreWrapper.svelte @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/tests/unit/components/comment.test.ts b/tests/unit/components/comment.test.ts index 005308d4..2b53dafd 100644 --- a/tests/unit/components/comment.test.ts +++ b/tests/unit/components/comment.test.ts @@ -1,134 +1,128 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { DiffBar } from '$lib'; +import { Comment } from '$lib'; +import { + computePosition, + autoUpdate, + flip, + shift, + offset, + arrow, +} from '@floating-ui/dom'; +import { storePopup } from '@skeletonlabs/skeleton'; +import { userMock } from '../../utility/users.ts'; +import ToastStoreWrapper from './ToastStoreWrapper.svelte'; -describe('Difficulty Components', () => { +storePopup.set({ computePosition, autoUpdate, flip, shift, offset, arrow }); + +describe('Comments', () => { let host: HTMLDivElement; + const comment = { + id: 1, + publicationId: 1, + userId: 1, + likes: 5, + content: 'hahahahaha', + createdAt: new Date(), + updatedAt: new Date(), + }; + const comment2 = { + id: 1, + publicationId: 1, + userId: 1, + likes: 5, + content: 'hahahahaha', + createdAt: new Date(2019, 5, 1, 13, 0), + updatedAt: new Date(2020, 5, 1, 13, 0), + }; + const comment3 = { + id: 1, + publicationId: 1, + userId: 1, + likes: 5, + content: 'hahahahaha', + createdAt: new Date(2020, 5, 1, 12, 0), + updatedAt: new Date(2020, 5, 1, 13, 0), + }; beforeEach(() => { host = document.createElement('div'); document.body.appendChild(host); }); - it('should render easy/green difficulty bar', () => { - const instance = new DiffBar({ + + it('should render comment with no edited ', () => { + const instance = new ToastStoreWrapper({ + target: host, + props: { + component: Comment, + props: { + isReply: false, + commenter: { ...userMock, profilePicData: "" }, + liked: false, + photoUrl: null, + interaction: comment, + } + }, + }); + + expect(instance).toBeTruthy(); + expect(host.innerHTML).not.toContain('Edited'); + expect(host.innerHTML).toContain('just now'); + expect(host.innerHTML).toContain('hahahahaha'); + expect(host.innerHTML).toContain('Reply'); + }); + it('should render comment with edited ', () => { + const instance = new ToastStoreWrapper({ + target: host, + props: { + component: Comment, + props: { + isReply: false, + commenter: { ...userMock, profilePicData: "" }, + liked: false, + photoUrl: null, + interaction: comment2, + } + }, + }); + expect(instance).toBeTruthy(); + expect(host.innerHTML).toContain('Edited'); + expect(host.innerHTML).not.toContain('just now'); + }); + it('should render comment created prior to current moment ', () => { + const instance = new ToastStoreWrapper({ + target: host, + props: { + component: Comment, + props: { + isReply: false, + commenter: { ...userMock, profilePicData: "" }, + liked: false, + photoUrl: null, + interaction: comment3, + } + }, + }); + expect(instance).toBeTruthy(); + expect(host.innerHTML).toContain('Edited'); + expect(host.innerHTML).toContain('ago'); + }); + it('should render reply with no reply option ', () => { + const instance = new ToastStoreWrapper({ target: host, - props: { diff: 'hard' }, + props: { + component: Comment, + props: { + isReply: true, + commenter: { ...userMock, profilePicData: "" }, + liked: false, + photoUrl: null, + interaction: comment, + } + }, }); expect(instance).toBeTruthy(); - expect(host.innerHTML).toContain('bg-error-400'); - expect(host.innerHTML).not.toContain('bg-amber-400'); - expect(host.innerHTML).not.toContain('bg-green-500'); + expect(host.innerHTML).not.toContain('Edited'); + const replyForm = host.querySelector('.col-start-2.hidden'); + expect(replyForm).toBeTruthy(); }); }); - -//temporary test suspension until I figure out how to test skeleton modals if possible -// import { describe, it, expect, beforeEach } from 'vitest'; -// import { Comment } from '$lib'; -// import { -// computePosition, -// autoUpdate, -// flip, -// shift, -// offset, -// arrow, -// } from '@floating-ui/dom'; -// import { storePopup } from '@skeletonlabs/skeleton'; -// storePopup.set({ computePosition, autoUpdate, flip, shift, offset, arrow }); -// -// describe('Comments', () => { -// let host: HTMLDivElement; -// const comment = { -// id: 1, -// publicationId: 1, -// userId: 1, -// likes: 5, -// content: 'hahahahaha', -// createdAt: new Date(), -// updatedAt: new Date(), -// }; -// const comment2 = { -// id: 1, -// publicationId: 1, -// userId: 1, -// likes: 5, -// content: 'hahahahaha', -// createdAt: new Date(), -// updatedAt: new Date(2020, 5, 1, 13, 0), -// }; -// const comment3 = { -// id: 1, -// publicationId: 1, -// userId: 1, -// likes: 5, -// content: 'hahahahaha', -// createdAt: new Date(2020, 5, 1, 12, 0), -// updatedAt: new Date(2020, 5, 1, 13, 0), -// }; -// -// beforeEach(() => { -// host = document.createElement('div'); -// document.body.appendChild(host); -// }); -// -// it('should render comment with no edited ', () => { -// const instance = new Comment({ -// target: host, -// props: { -// isReply: false, -// popupName: 'comm1', -// interaction: comment, -// }, -// }); -// expect(instance).toBeTruthy(); -// expect(host.innerHTML).not.toContain('Edited'); -// expect(host.innerHTML).toContain('just now'); -// expect(host.innerHTML).toContain('5'); -// expect(host.innerHTML).toContain('Tom Viering'); -// expect(host.innerHTML).toContain('Reply'); -// }); -// it('should render comment with edited ', () => { -// const instance = new Comment({ -// target: host, -// props: { -// isReply: false, -// popupName: 'comm2', -// interaction: comment2, -// }, -// }); -// expect(instance).toBeTruthy(); -// expect(host.innerHTML).toContain('Edited'); -// expect(host.innerHTML).toContain('just now'); -// expect(host.innerHTML).toContain('5'); -// expect(host.innerHTML).toContain('Tom Viering'); -// }); -// it('should render comment created prior to current moment ', () => { -// const instance = new Comment({ -// target: host, -// props: { -// isReply: false, -// popupName: 'comm1', -// interaction: comment3, -// }, -// }); -// expect(instance).toBeTruthy(); -// expect(host.innerHTML).toContain('Edited'); -// expect(host.innerHTML).toContain('ago'); -// expect(host.innerHTML).toContain('5'); -// expect(host.innerHTML).toContain('Tom Viering'); -// }); -// it('should render reply with no reply option ', () => { -// const instance = new Comment({ -// target: host, -// props: { -// isReply: true, -// popupName: 'comm1', -// interaction: comment, -// }, -// }); -// expect(instance).toBeTruthy(); -// expect(host.innerHTML).not.toContain('Edited'); -// expect(host.innerHTML).toContain('just now'); -// expect(host.innerHTML).toContain('5'); -// expect(host.innerHTML).toContain('Tom Viering'); -// expect(host.innerHTML).not.toContain('Reply'); -// }); -// }); diff --git a/tests/unit/components/difficulty.test.ts b/tests/unit/components/difficulty.test.ts deleted file mode 100644 index fa0a5a02..00000000 --- a/tests/unit/components/difficulty.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { describe, it, expect, beforeEach } from 'vitest'; -import { DiffBar, DifficultySelection } from '$lib'; - -describe('Difficulty Components', () => { - let host: HTMLDivElement; - - beforeEach(() => { - host = document.createElement('div'); - document.body.appendChild(host); - }); - - it('should render easy/green difficulty bar', () => { - const instance = new DiffBar({ - target: host, - props: { diff: 'easy' }, - }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).not.toContain('bg-error-400'); - expect(host.innerHTML).not.toContain('bg-amber-400'); - expect(host.innerHTML).toContain('bg-green-500'); - }); - it('should render medium/yellow difficulty bar', () => { - const instance = new DiffBar({ - target: host, - props: { diff: 'medium' }, - }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).not.toContain('bg-green'); - expect(host.innerHTML).not.toContain('bg-error-400'); - expect(host.innerHTML).toContain('bg-amber-400'); - }); - it('should render hard/red difficulty bar', () => { - const instance = new DiffBar({ - target: host, - props: { diff: 'hard' }, - }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).not.toContain('bg-green'); - expect(host.innerHTML).not.toContain('bg-amber-400'); - expect(host.innerHTML).toContain('bg-error-400'); - }); - it('should render difficulty selection component and check one of them', () => { - const instance = new DifficultySelection({ - target: host, - props: { difficulty: 'easy' }, - }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).toContain('aria-checked="true"'); - }); -}); diff --git a/tests/unit/components/filter.test.ts b/tests/unit/components/filter.test.ts deleted file mode 100644 index 908245c3..00000000 --- a/tests/unit/components/filter.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; -import { Filter } from '$lib'; - -describe('Filter Component', () => { - let host: HTMLDivElement; - - beforeEach(() => { - host = document.createElement('div'); - document.body.appendChild(host); - }); - - it('should render Filter non active', () => { - const instance = new Filter({ - target: host, - props: { - label: 'Filter', - selected: [], - all: [ - { id: '5', content: 'Option1' }, - { id: '5', content: 'Option2' }, - { id: '5', content: 'Option3' }, - ], - display: [], - active: false, - profilePic: false, - num: 0, - }, - }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).toContain('Filter'); - expect(host.innerHTML).not.toContain('Option1'); - expect(host.innerHTML).not.toContain('Option2'); - expect(host.innerHTML).not.toContain('Option3'); - - expect(host.innerHTML).toContain(''); - }); - - it('should render Filter active with pictures but nothing in the search bar', () => { - const instance = new Filter({ - target: host, - props: { - label: 'Filter', - selected: [], - all: [ - { id: '5', content: 'Option1' }, - { id: '5', content: 'Option2' }, - { id: '5', content: 'Option3' }, - ], - display: [], - active: true, - profilePic: true, - num: 0, - }, - }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).toContain('Filter'); - expect(host.innerHTML).not.toContain('Option1'); - expect(host.innerHTML).not.toContain('Option2'); - expect(host.innerHTML).not.toContain('Option3'); - expect(host.innerHTML).toContain('No Matching filter'); - - expect(host.innerHTML).toContain(''); - }); - - it('should render Filter active with pictures and some display setting', () => { - const instance = new Filter({ - target: host, - props: { - label: 'Filter', - selected: [], - all: [ - { id: '5', content: 'Option1' }, - { id: '5', content: 'Option2' }, - { id: '5', content: 'Option3' }, - ], - display: [ - { id: '5', content: 'Option1' }, - { id: '5', content: 'Option2' }, - ], - active: true, - profilePic: true, - num: 0, - }, - }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).toContain('Filter'); - expect(host.innerHTML).toContain('Option1'); - expect(host.innerHTML).toContain('Option2'); - expect(host.innerHTML).not.toContain('Option3'); - - expect(host.innerHTML).toContain(''); - }); -}); diff --git a/tests/unit/components/header.test.ts b/tests/unit/components/header.test.ts index d8cc63a6..c5773a84 100644 --- a/tests/unit/components/header.test.ts +++ b/tests/unit/components/header.test.ts @@ -1,112 +1,197 @@ -import { expect, it } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { readable, writable } from 'svelte/store'; +import { tick } from 'svelte'; +import { + arrow, + autoUpdate, + computePosition, + flip, + offset, + shift, +} from '@floating-ui/dom'; +import { userMock } from '../../utility/users.ts'; +// Header import is loaded dynamically after SvelteKit store mocks. -it('should render header when signed in', async () => { - // Test goes here - expect(true).toBe(true); +const mockUser = { ...userMock, profilePicData: 'null' }; + + +type PageStoreData = { + url: URL; + data: { session: null | { user: typeof mockUser } }; +}; + +const pageStore = writable({ + url: new URL('http://localhost/'), + data: { session: null }, +}); +const navigatingStore = readable(null); + +vi.mock('$app/stores', () => { + return { + page: pageStore, + navigating: navigatingStore, + }; +}); + +const passthroughPath = (path: string) => path; + +vi.mock('$app/paths', () => { + return { + base: '', + assets: '', + asset: passthroughPath, + resolve: passthroughPath, + resolveRoute: passthroughPath, + match: async () => null, + }; }); -// import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; -// import { -// arrow, -// autoUpdate, -// computePosition, -// flip, -// offset, -// shift, -// } from '@floating-ui/dom'; -// import { storePopup } from '@skeletonlabs/skeleton'; -// import Header from '$lib/components/page/Header.svelte'; -// -// // Create a mock user -// const mockUser = { -// id: 'aaa', -// password: 'password', -// username: 'Vasko Prasko', -// firstName: 'John', -// lastName: 'Doe', -// email: 'john.doe@example.com', -// profilePic: 'https://example.com/profile.jpg', -// reputation: 100, -// isAdmin: false, -// profilePicData: 'null', -// createdAt: new Date(2), -// updatedAt: new Date(2), -// emailVerified: null, -// }; -// -// describe('Header Component', () => { -// let host: HTMLDivElement; -// -// beforeAll(() => { -// // vi.mock('$lib/stores/auth', async () => { -// // return { -// // store: mockAuthStore, -// // authStore: mockAuthStore, -// // }; -// // }); -// }); -// beforeEach(() => { -// // Mock the authStore -// host = document.createElement('div'); -// document.body.appendChild(host); -// }); -// -// it('should render header when signed in', async () => { -// // Render the Header component -// mockAuthStore.mockSetSubscribeValue({ -// user: mockUser, -// session: 'aa', -// }); -// -// storePopup.set({ -// computePosition, -// autoUpdate, -// flip, -// shift, -// offset, -// arrow, -// }); -// -// const instance = new Header({ -// target: host, -// props: {}, -// }); -// -// // Assertion -// expect(instance).toBeTruthy(); -// expect(host.innerHTML).toContain('Home'); -// expect(host.innerHTML).toContain('About'); -// expect(host.innerHTML).toContain('Browse Materials'); -// expect(host.innerHTML).toContain('Browse Circuits'); -// expect(host.innerHTML).toContain('Publish'); -// }); -// -// it('should render sign in header when signed in', async () => { -// mockAuthStore.mockSetSubscribeValue({ -// user: null, -// session: 'aa', -// }); -// -// storePopup.set({ -// computePosition, -// autoUpdate, -// flip, -// shift, -// offset, -// arrow, -// }); -// -// const instance = new Header({ -// target: host, -// props: {}, -// }); -// -// // Assertion -// expect(instance).toBeTruthy(); -// expect(host.innerHTML).toContain('Home'); -// expect(host.innerHTML).toContain('About'); -// expect(host.innerHTML).toContain('Browse Materials'); -// expect(host.innerHTML).toContain('Browse Circuits'); -// expect(host.innerHTML).toContain('Sign In'); -// }); -// }); +const mockSupabase = { + auth: { + signOut: vi.fn(), + }, +} as never; + +describe('Header Component', () => { + let host: HTMLDivElement; + let Header: typeof import('$lib/components/page/Header.svelte').default; + + beforeEach(async () => { + host = document.createElement('div'); + document.body.appendChild(host); + vi.resetModules(); + const { storePopup } = await import('@skeletonlabs/skeleton'); + storePopup.set({ + computePosition, + autoUpdate, + flip, + shift, + offset, + arrow, + }); + Header = (await import('$lib/components/page/Header.svelte')).default; + }); + + it('should render header when signed in', () => { + pageStore.set({ + url: new URL('http://localhost/'), + data: { session: { user: mockUser } }, + }); + + const instance = new Header({ + target: host, + props: { + supabase: mockSupabase, + loggedUser: mockUser, + }, + }); + + expect(instance).toBeTruthy(); + expect(host.innerHTML).toContain('Home'); + expect(host.innerHTML).toContain('Browse'); + expect(host.innerHTML).toContain('About'); + expect(host.innerHTML).toContain('Publish'); + expect(host.querySelector('[data-testid="profile-picture"]')).toBeTruthy(); + }); + + it('should render sign in header when signed out', () => { + pageStore.set({ + url: new URL('http://localhost/'), + data: { session: null }, + }); + + const instance = new Header({ + target: host, + props: { + supabase: mockSupabase, + loggedUser: null as never, + }, + }); + + expect(instance).toBeTruthy(); + expect(host.innerHTML).toContain('Home'); + expect(host.innerHTML).toContain('Browse'); + expect(host.innerHTML).toContain('About'); + expect(host.innerHTML).toContain('Sign In'); + expect(host.querySelector('[data-testid="profile-picture"]')).toBeNull(); + }); + + it('should show mobile dropdown with sign in when signed out', async () => { + pageStore.set({ + url: new URL('http://localhost/'), + data: { session: null }, + }); + + const instance = new Header({ + target: host, + props: { + supabase: mockSupabase, + loggedUser: null as never, + }, + }); + + expect(instance).toBeTruthy(); + const hamburger = host.querySelector('header > button') as HTMLButtonElement; + expect(hamburger).toBeTruthy(); + hamburger.click(); + await tick(); + + expect(host.innerHTML).toContain('Sign In'); + expect(host.innerHTML).toContain('Home'); + expect(host.innerHTML).toContain('Browse'); + }); + + it('should show mobile dropdown log out when signed in', async () => { + pageStore.set({ + url: new URL('http://localhost/'), + data: { session: { user: mockUser } }, + }); + + const instance = new Header({ + target: host, + props: { + supabase: mockSupabase, + loggedUser: mockUser, + }, + }); + + expect(instance).toBeTruthy(); + const hamburger = host.querySelector('header > button') as HTMLButtonElement; + expect(hamburger).toBeTruthy(); + hamburger.click(); + await tick(); + + expect(host.innerHTML).toContain('Log out'); + expect(host.innerHTML).toContain('Publish'); + }); + + it('should close mobile dropdown on second toggle', async () => { + vi.useFakeTimers(); + pageStore.set({ + url: new URL('http://localhost/'), + data: { session: null }, + }); + + const instance = new Header({ + target: host, + props: { + supabase: mockSupabase, + loggedUser: null as never, + }, + }); + + expect(instance).toBeTruthy(); + const hamburger = host.querySelector('header > button') as HTMLButtonElement; + expect(hamburger).toBeTruthy(); + + hamburger.click(); + await tick(); + vi.advanceTimersByTime(1000); + expect(host.querySelector('[data-testid="mobile-menu"]')).toBeTruthy(); + + hamburger.click(); + await tick() + vi.advanceTimersByTime(1000); + expect(host.querySelector('[data-testid="mobile-menu"]')).toBeNull(); + }); +}); diff --git a/tests/unit/components/publishCard.test.ts b/tests/unit/components/publishCard.test.ts index 9cd703c1..67ff5609 100644 --- a/tests/unit/components/publishCard.test.ts +++ b/tests/unit/components/publishCard.test.ts @@ -1,67 +1,207 @@ -import { beforeEach, describe, expect, it } from 'vitest'; -import { PublicationCard } from '$lib'; +import { beforeEach, describe, expect, it, vi, afterEach } from 'vitest'; +import { tick } from 'svelte'; +import { + arrow, + autoUpdate, + computePosition, + flip, + offset, + shift, +} from '@floating-ui/dom'; +import { storePopup } from '@skeletonlabs/skeleton'; +import { + Difficulty, + MaterialType, + PublicationType, + type Material, + type Publication, +} from '@prisma/client'; +import { + generateRandomString, + randomEnumValue, +} from '../../utility/publicationsUtility'; +import { userMock } from '../../utility/users.ts'; -describe('Filter Component', () => { - let host: HTMLDivElement; +const pageState = { + data: { session: { user: { id: '12313' } } }, +}; - beforeEach(() => { - host = document.createElement('div'); - document.body.appendChild(host); - }); +const mockUser = { ...(userMock), profilePicData: '' }; + +vi.mock('$app/state', () => ({ + page: pageState, +})); + +describe('Publication Card', () => { + let host: HTMLDivElement; + let PublicationCard: typeof import('$lib/components/PublicationCard.svelte').default; + + const buildMaterial = (publicationId: number): Material => ({ + id: 1, + copyright: generateRandomString(10), + encapsulatingType: randomEnumValue(MaterialType), + timeEstimate: 3, + theoryPractice: 0.5, + publicationId, + }); + + const buildPublication = ( + overrides: Partial< + Publication & { + materials: Material; + tags: { content: string }[]; + usedInCourse: { course: string }[]; + course: { educationalLevel: string } | null; + } + > = {}, + ) => { + const base: Publication = { + id: 101, + title: generateRandomString(), + description: generateRandomString(40), + difficulty: randomEnumValue(Difficulty), + likes: 4, + learningObjectives: [], + prerequisites: [], + createdAt: new Date(), + updatedAt: new Date(), + isDraft: false, + courseId: null, + publisherId: 'user-1', + type: PublicationType.Material, + }; + + return { + ...base, + materials: buildMaterial(base.id), + tags: [{ content: 'tag-a' }, { content: 'tag-b' }], + usedInCourse: [{ course: 'Course 1' }], + course: { educationalLevel: 'bachelor' }, + ...overrides, + }; + }; + + // Flush microtasks triggered by async handlers. + const flushPromises = () => new Promise((resolve) => setTimeout(resolve, 0)); + + beforeEach(async () => { + host = document.createElement('div'); + document.body.appendChild(host); + vi.resetModules(); + storePopup.set({ + computePosition, + autoUpdate, + flip, + shift, + offset, + arrow, + }); + PublicationCard = (await import('$lib/components/PublicationCard.svelte')) + .default; + vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: true })); + + vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockReturnValue({ + width: 1000, + height: 0, + top: 0, + left: 0, + right: 1000, + bottom: 0, + x: 0, + y: 0, + toJSON: () => {}, + } as DOMRect); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); - it('should render Filter non active', () => { - const instance = new PublicationCard({ - target: host, - props: { - publisher: { - id: '1', - firstName: 'a', - lastName: 'b', - email: 'a', - username: 'a', - reputation: 5, - emailVerified: null, - password: null, - isAdmin: false, - createdAt: new Date(), - updatedAt: new Date(), - profilePicData: '', - aboutMe: 'Hello I am a user', + it('renders core details and draft badge', async () => { + const publication = buildPublication({ isDraft: true }); + + const instance = new PublicationCard({ + target: host, + props: { + publication, + publisher: mockUser, + liked: true, + saved: true, + extensions: ['pdf'], + materialType: 'slides', + imgSrc: null, }, - imgSrc: 'some_image', - publication: { - materials: { - id: 1, - copyright: 'Hello', - encapsulatingType: 'assignment', - publicationId: 1, - timeEstimate: 20, - theoryPractice: 0.5, - }, - savedByAllTime: [], - id: 1, - title: 'aaa', - description: 'string', - difficulty: 'hard', - likes: 2, - learningObjectives: ['s'], - prerequisites: ['p'], - createdAt: new Date(), - updatedAt: new Date(), - publisherId: 'hello', - type: 'Material', - tags: [{ content: 'tag1' }], - usedInCourse: [], + }); + + expect(instance).toBeTruthy(); + expect(host.innerHTML).toContain(publication.title); + expect(host.innerHTML).toContain(publication.description); + expect(host.innerHTML).toContain('Draft'); + expect(host.innerHTML).toContain('View'); + expect(host.innerHTML).toContain('tag-a'); + + const profileImg = host.querySelector( + '[data-testid="publication-card-profile-img"]', + ) as HTMLImageElement | null; + expect(profileImg?.getAttribute('src')).toContain( + '/defaultProfilePic/profile.jpg', + ); + }); + + it('dispatches liked and saved events with updated counts', async () => { + const publication = buildPublication({ likes: 6 }); + const likedHandler = vi.fn(); + const savedHandler = vi.fn(); + + const instance = new PublicationCard({ + target: host, + props: { + publication, + publisher: mockUser, + liked: true, + saved: false, + extensions: ['pdf'], + materialType: 'slides', + imgSrc: null, }, - }, + }); + + instance.$on('liked', likedHandler); + instance.$on('saved', savedHandler); + + const likeButton = Array.from(host.querySelectorAll('button')).find( + (button) => button.textContent?.includes('6'), + ) as HTMLButtonElement; + likeButton.click(); + await flushPromises(); + await tick(); + + expect(host.innerHTML).toContain('5'); + expect(likedHandler).toHaveBeenCalledTimes(1); + expect(likedHandler.mock.calls[0][0].detail).toEqual({ + id: publication.id, + }); + + const saveButton = host.querySelector( + `button[aria-label="Save publication ${publication.title}"]`, + ) as HTMLButtonElement; + saveButton.click(); + await flushPromises(); + await tick(); + + expect(savedHandler).toHaveBeenCalledTimes(1); + expect(savedHandler.mock.calls[0][0].detail).toEqual({ + id: publication.id, + }); + + const fetchMock = vi.mocked(fetch); + expect(fetchMock).toHaveBeenCalledWith( + `/api/user/${pageState.data.session.user.id}/liked/${publication.id}`, + expect.objectContaining({ method: 'POST' }), + ); + expect(fetchMock).toHaveBeenCalledWith( + `/api/user/${pageState.data.session.user.id}/saved/${publication.id}`, + expect.objectContaining({ method: 'POST' }), + ); }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).toContain('DiffBar'); - expect(host.innerHTML).toContain('View'); - expect(host.innerHTML).toContain('Icon'); - expect(host.innerHTML).toContain('6'); - expect(host.innerHTML).not.toContain('icon1'); - - expect(host.innerHTML).toContain(''); }); -}); diff --git a/tests/unit/components/reusable.test.ts b/tests/unit/components/reusable.test.ts index ef17cacf..0407d499 100644 --- a/tests/unit/components/reusable.test.ts +++ b/tests/unit/components/reusable.test.ts @@ -17,7 +17,6 @@ describe('Tag Component', () => { expect(instance).toBeTruthy(); expect(host.innerHTML).toContain('example'); expect(host.innerHTML).not.toContain('button'); - expect(host.innerHTML).toContain(''); }); it('should not display tag if tagText is empty', () => { @@ -28,7 +27,6 @@ describe('Tag Component', () => { expect(instance).toBeTruthy(); // should not render anything besides the comment that the tag is there - expect(host.innerHTML).toContain(''); expect(host.innerHTML).not.toContain('

'); expect(host.innerHTML).not.toContain(' { expect(instance).toBeTruthy(); expect(host.innerHTML).toContain('example'); expect(host.innerHTML).not.toContain('button'); - expect(host.innerHTML).toContain(''); }); }); diff --git a/tests/unit/components/theoryappbar.test.ts b/tests/unit/components/theoryappbar.test.ts deleted file mode 100644 index 23f37471..00000000 --- a/tests/unit/components/theoryappbar.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; -import { TheoryAppBar } from '$lib'; - -describe('TheoryAppBar Component', () => { - let host: HTMLDivElement; - - beforeEach(() => { - host = document.createElement('div'); - document.body.appendChild(host); - }); - - it('should render theoryAppBar', () => { - const instance = new TheoryAppBar({ target: host, props: { value: 0.63 } }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).toContain('Theory'); - expect(host.innerHTML).toContain('Practice'); - expect(host.innerHTML).toContain('63%'); - expect(host.innerHTML).toContain('37%'); - expect(host.innerHTML).toContain(''); - }); - - it('should render theoryAppBar 100% theory', () => { - const instance = new TheoryAppBar({ target: host, props: { value: 0 } }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).toContain('Theory'); - expect(host.innerHTML).toContain('Practice'); - expect(host.innerHTML).toContain('0%'); - expect(host.innerHTML).toContain('100%'); - expect(host.innerHTML).toContain(''); - }); - - it('should render theoryAppBar 100% practice', () => { - const instance = new TheoryAppBar({ target: host, props: { value: 1 } }); - expect(instance).toBeTruthy(); - expect(host.innerHTML).toContain('Theory'); - expect(host.innerHTML).toContain('Practice'); - expect(host.innerHTML).toContain('100%'); - expect(host.innerHTML).toContain('0%'); - expect(host.innerHTML).toContain(''); - }); -}); diff --git a/tests/unit/components/userprops.test.ts b/tests/unit/components/userprops.test.ts index 8a17d202..6ba6532d 100644 --- a/tests/unit/components/userprops.test.ts +++ b/tests/unit/components/userprops.test.ts @@ -9,25 +9,13 @@ import { arrow, } from '@floating-ui/dom'; import { storePopup } from '@skeletonlabs/skeleton'; +import { userMock } from '../../utility/users.ts'; + storePopup.set({ computePosition, autoUpdate, flip, shift, offset, arrow }); + describe('User Components', () => { let host: HTMLDivElement; - const user = { - id: 'aaaaaaaaaa', - firstName: 'Ivan', - lastName: 'Guenov', - email: 'kitajski@gmail.com', - username: 'ivanguenov', - aboutMe: "Hello I am a user", - // profilePic: '', - password: 'password', - reputation: 5, - isAdmin: false, - emailVerified: null, - createdAt: new Date(2), - updatedAt: new Date(2), - }; beforeEach(() => { host = document.createElement('div'); @@ -39,7 +27,7 @@ describe('User Components', () => { target: host, props: { view: 'home', - user: user, + user: userMock, role: 'Maintainer', posts: 7, userPhotoUrl: '', @@ -48,15 +36,15 @@ describe('User Components', () => { expect(instance).toBeTruthy(); expect(host.innerHTML).not.toContain('Maintainer'); expect(host.innerHTML).toContain('Posts'); - expect(host.innerHTML).toContain('Rep: 5'); - expect(host.innerHTML).toContain('Ivan Guenov'); + expect(host.innerHTML).toContain("Rep: " + userMock.reputation); + expect(host.innerHTML).toContain(userMock.firstName); }); it('should render user prop for material view with name and role ', () => { const instance = new UserProp({ target: host, props: { view: 'material', - user: user, + user: userMock, role: 'Maintainer', posts: 7, userPhotoUrl: '', @@ -65,15 +53,15 @@ describe('User Components', () => { expect(instance).toBeTruthy(); expect(host.innerHTML).toContain('Maintainer'); expect(host.innerHTML).not.toContain('Posts'); - expect(host.innerHTML).not.toContain('Rep: 5'); - expect(host.innerHTML).toContain('Ivan Guenov'); + expect(host.innerHTML).not.toContain("Rep: " + userMock.reputation); + expect(host.innerHTML).toContain(userMock.firstName); }); it('should render user prop for publish view with name and role ', () => { const instance = new UserProp({ target: host, props: { view: 'publish', - user: user, + user: userMock, role: 'Maintainer', posts: 7, userPhotoUrl: '', @@ -81,15 +69,15 @@ describe('User Components', () => { }); expect(instance).toBeTruthy(); expect(host.innerHTML).not.toContain('Posts'); - expect(host.innerHTML).not.toContain('Rep: 5'); - expect(host.innerHTML).toContain('Ivan Guenov'); + expect(host.innerHTML).not.toContain("Rep: " + userMock.reputation); + expect(host.innerHTML).toContain(userMock.firstName); }); it('should render user prop for search view with name and role ', () => { const instance = new UserProp({ target: host, props: { view: 'search', - user: user, + user: userMock, role: 'Maintainer', posts: 7, userPhotoUrl: '', @@ -98,7 +86,7 @@ describe('User Components', () => { expect(instance).toBeTruthy(); expect(host.innerHTML).not.toContain('Maintainer'); expect(host.innerHTML).toContain('Posts'); - expect(host.innerHTML).toContain('Rep: 5'); - expect(host.innerHTML).toContain('Ivan Guenov'); + expect(host.innerHTML).toContain("Rep: " + userMock.reputation); + expect(host.innerHTML).toContain(userMock.firstName); }); }); diff --git a/tests/unit/coursesLogic.test.ts b/tests/unit/coursesLogic.test.ts index 42676358..c0cc9ce1 100644 --- a/tests/unit/coursesLogic.test.ts +++ b/tests/unit/coursesLogic.test.ts @@ -1,6 +1,8 @@ import { describe, it, expect } from 'vitest'; import { changeCourse, type UserWithProfilePic } from '$lib/util/coursesLogic'; -import type { CourseWithMaintainersAndProfilePic } from '$lib/database/courses'; +import type { + CourseWithCoverPic, +} from '$lib/database/courses'; @@ -13,11 +15,11 @@ describe('Selecting a new courses', () => { const courses = [ { id: 1, learningObjectives: ['LO1'], prerequisites: ['PK1'], maintainers: [{ id: 'm1', name: 'Maintainer 1' }] }, { id: 2, learningObjectives: ['LO2'], prerequisites: ['PK2'], maintainers: [{ id: 'm2', name: 'Maintainer 2' }] } - ] as unknown as CourseWithMaintainersAndProfilePic[]; + ] as unknown as CourseWithCoverPic[]; const mantainers: UserWithProfilePic[] = []; const res = changeCourse(course, previousCourse, LOs, PKs, courses, mantainers); - console.log(res); + expect(res.course).toBe(1); expect(res.LOs).toEqual(['LO1']); expect(res.PKs).toEqual(['PK1']); diff --git a/tests/unit/db.test.ts b/tests/unit/db.test.ts index c6f73529..9aff3b33 100644 --- a/tests/unit/db.test.ts +++ b/tests/unit/db.test.ts @@ -2,102 +2,21 @@ import { describe, it, expect, vi } from 'vitest'; import { prisma } from '$lib/database'; import { getPublicationById, getAllPublications } from '$lib/database/db'; import Fuse from 'fuse.js'; +import { userMock } from '../utility/users.ts'; -describe('getPublicationById', () => { - it('should return a publication with the given id', async () => { - const mockPublication = { - id: 1, - title: 'Test Publication', - tags: [], - publisher: {}, - }; - prisma.publication.findUnique = vi - .fn() - .mockResolvedValue(mockPublication); - - const result = await getPublicationById(1); +const mockUser = userMock; - expect(result).toEqual(mockPublication); - expect(prisma.publication.findUnique).toHaveBeenCalledWith({ - where: { id: 1 }, - include: { - usedInCourse: true, - tags: true, - publisher: { - include: { - profilePic: true, - }, - }, - maintainers: { - include: { - profilePic: true, - }, - }, - coverPic: true, - comments: { - include: { - replies: { - include: { - user: { - include: { - profilePic: true, - }, - }, - }, - }, - user: { - include: { - profilePic: true, - }, - }, - }, - }, - materials: { - include: { - publication: true, - files: true, - }, - }, - circuit: { - include: { - publication: { - include: { - tags: true, - }, - }, - nodes: { - include: { - publication: { - include: { - tags: true, - materials: true, - circuit: true, - coverPic: true, - publisher: { - include: { - profilePic: true, - }, - }, - usedInCourse: true, - }, - }, - next: true, - }, - }, - }, - }, - }, - }); - }); +describe('getPublicationById', () => { it('should return null if publication is not found', async () => { prisma.publication.findUnique = vi.fn().mockResolvedValue(null); - const result = await getPublicationById(999); + const id = 45674598; + const result = await getPublicationById(id); expect(result).toBeNull(); expect(prisma.publication.findUnique).toHaveBeenCalledWith({ - where: { id: 999 }, + where: { id: id }, include: { usedInCourse: true, tags: true, @@ -133,7 +52,14 @@ describe('getPublicationById', () => { materials: { include: { publication: true, - files: true, + files: { + select: { + path: true, + title: true, + type: true, + }, + }, + fileURLs: true, }, }, circuit: { @@ -164,6 +90,15 @@ describe('getPublicationById', () => { }, }, }, + course: { + select: { + id: true, + courseName: true, + learningObjectives: true, + prerequisites: true, + educationalLevel: true, + }, + }, }, }); }); @@ -171,19 +106,20 @@ describe('getPublicationById', () => { describe('getAllPublications', () => { it('should return publications filtered by publisher IDs', async () => { - const mockPublications = [ - { id: 1, title: 'Test Publication', publisherId: '1', tags: [] }, - ]; + const mockPublications = [{ }]; prisma.publication.findMany = vi .fn() .mockResolvedValue(mockPublications); - const result = await getAllPublications(['1'], ''); + const result = await getAllPublications([mockUser.id], '', 'Most Liked'); expect(result).toEqual(mockPublications); expect(prisma.publication.findMany).toHaveBeenCalledWith({ where: { - AND: [{ publisherId: { in: ['1'] } }], + AND: [{ publisherId: { in: [mockUser.id] } }], + }, + orderBy: { + likes: 'desc', }, include: { tags: true, @@ -191,7 +127,10 @@ describe('getAllPublications', () => { circuit: true, coverPic: true, publisher: { - include: { + select: { + firstName: true, + lastName: true, + username: true, profilePic: true, }, }, @@ -218,7 +157,7 @@ describe('getAllPublications', () => { .fn() .mockResolvedValue(mockPublications); - const result = await getAllPublications([], 'Test'); + const result = await getAllPublications([], 'Test', 'Most Liked'); const searcher = new Fuse(mockPublications, { keys: [ @@ -238,13 +177,19 @@ describe('getAllPublications', () => { where: { AND: [], }, + orderBy: { + likes: 'desc', + }, include: { tags: true, materials: true, circuit: true, coverPic: true, publisher: { - include: { + select: { + firstName: true, + lastName: true, + username: true, profilePic: true, }, }, @@ -265,20 +210,26 @@ describe('getAllPublications', () => { .fn() .mockResolvedValue(mockPublications); - const result = await getAllPublications([], ''); + const result = await getAllPublications([], '', 'Most Liked'); expect(result).toEqual(mockPublications); expect(prisma.publication.findMany).toHaveBeenCalledWith({ where: { AND: [], }, + orderBy: { + likes: 'desc', + }, include: { tags: true, materials: true, circuit: true, coverPic: true, publisher: { - include: { + select: { + firstName: true, + lastName: true, + username: true, profilePic: true, }, }, diff --git a/tests/unit/file.test.ts b/tests/unit/file.test.ts index 99b7f389..63e4c10a 100644 --- a/tests/unit/file.test.ts +++ b/tests/unit/file.test.ts @@ -15,21 +15,31 @@ import path from 'path'; vi.mock('fs'); vi.mock('path'); -// -// describe('bufToBase64', () => { -// it('should convert buffer data to base64', async () => { -// const files = [{ fileId: '1', data: 'test' }]; -// const result = await bufToBase64(files); -// expect(result[0].data).toBe('test'); -// }); -// }); +vi.mock('$lib/database', () => ({ + fileSystem: { + readFile: vi.fn().mockResolvedValue(Buffer.from('test')), + saveFile: vi.fn(), + deleteFile: vi.fn(), + }, + prisma: { + file: { + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + findMany: vi.fn(), + }, + fileChunk: { + deleteMany: vi.fn(), + }, + }, +})); describe('profilePicFetcher', () => { beforeEach(() => { vi.clearAllMocks(); }); - it('should return default profile pic if profilePic is null', () => { + it('should return default profile pic if profilePic is null', async () => { const filePath = path.join( 'static', 'defaultProfilePic', @@ -38,14 +48,14 @@ describe('profilePicFetcher', () => { const mockFileData = Buffer.from('defaultProfilePicData'); fs.readFileSync = vi.fn().mockReturnValue(mockFileData); - const result = profilePicFetcher(null); + const result = await profilePicFetcher(null); expect(result).toEqual({ fileId: filePath, - data: mockFileData.toString('base64'), + data: null, }); }); - it('should return profile pic data if profilePic is provided', () => { + it('should return profile pic data if profilePic is provided', async () => { const profilePic = { path: 'profilePicPath', title: 'ivan', @@ -54,11 +64,13 @@ describe('profilePicFetcher', () => { userId: null, publicationId: null, materialId: null, + courseId: null }; - const mockFileData = Buffer.from('profilePicData'); - fileSystem.readFile = vi.fn().mockReturnValue(mockFileData); + const mockFileData = Buffer.from('U2VjcmV0IHN0cmluZyBpbiBiYXNlNjQ='); + + fileSystem.readFile = vi.fn().mockResolvedValue(mockFileData); - const result = profilePicFetcher(profilePic); + const result = await profilePicFetcher(profilePic); expect(result).toEqual({ fileId: 'profilePicPath', data: mockFileData.toString('base64'), @@ -71,24 +83,22 @@ describe('coverPicFetcher', () => { vi.clearAllMocks(); }); - it('should return default cover pic if coverPic is null', () => { + it('should return default cover pic if coverPic is null', async () => { const encapsulatingType = 'assignment'; const filePath = path.join( 'static', 'defaultCoverPic', `${encapsulatingType}.jpg`, ); - const mockFileData = Buffer.from('defaultCoverPicData'); - fs.readFileSync = vi.fn().mockReturnValue(mockFileData); - const result = coverPicFetcher(encapsulatingType, null); + const result = await coverPicFetcher(encapsulatingType, null); expect(result).toEqual({ fileId: filePath, - data: mockFileData.toString('base64'), + data: null, }); }); - it('should return cover pic data if coverPic is provided', () => { + it('should return cover pic data if coverPic is provided', async () => { const coverPic = { path: 'coverPicPath', title: 'ivan', @@ -97,11 +107,12 @@ describe('coverPicFetcher', () => { userId: null, publicationId: null, materialId: null, + courseId: null }; - const mockFileData = Buffer.from('coverPicData'); - fileSystem.readFile = vi.fn().mockReturnValue(mockFileData); + const mockFileData = Buffer.from('coverPicData'); + fileSystem.readFile = vi.fn().mockResolvedValue(mockFileData); - const result = coverPicFetcher('assignment', coverPic); + const result = await coverPicFetcher('assignment', coverPic); expect(result).toEqual({ fileId: 'coverPicPath', data: mockFileData.toString('base64'), @@ -125,7 +136,7 @@ describe('addCoverPic', () => { publicationId: 1, }); - const result = await addCoverPic('cover.jpg', 'image/jpeg', info, 1); + const result = await addCoverPic('cover.jpg', 'image/jpeg', "user", info, 1); expect(result).toEqual({ path: mockPath, title: 'cover.jpg', @@ -149,7 +160,7 @@ describe('addCoverPic', () => { .mockRejectedValue(new Error('File system error')); await expect( - addCoverPic('cover.jpg', 'image/jpeg', info, 1), + addCoverPic('cover.jpg', 'image/jpeg', 'user', info, 1, false), ).rejects.toThrow('Rollback'); }); }); @@ -220,7 +231,7 @@ describe('addFile', () => { materialId: 1, }); - const result = await addFile('file.jpg', 'image/jpeg', info, 1); + const result = await addFile('file.jpg', 'image/jpeg', "user", info, 1); expect(result).toEqual({ path: mockPath, title: 'file.jpg', @@ -244,8 +255,8 @@ describe('addFile', () => { .mockRejectedValue(new Error('File system error')); await expect( - addFile('file.jpg', 'image/jpeg', info, 1), - ).rejects.toThrow('Rollback'); + addFile('file.jpg', 'image/jpeg', "user", info, 1), + ).rejects.toThrow('Rollback File system'); }); }); @@ -283,8 +294,9 @@ describe('handleFileTokens', () => { }); it('should update file tokens in database', async () => { - const filesToUpdate = [{ filePath: 'filePath', tokens: 'newTokens' }]; + const filesToUpdate = [{ filePath: 'filePath', tokens: 'newTokens', chunks: [] }]; prisma.file.update = vi.fn().mockResolvedValue(undefined); + prisma.fileChunk.deleteMany = vi.fn().mockResolvedValue(undefined); await handleFileTokens(filesToUpdate); diff --git a/tests/unit/material.test.ts b/tests/unit/material.test.ts index 77274042..d514446b 100644 --- a/tests/unit/material.test.ts +++ b/tests/unit/material.test.ts @@ -121,10 +121,12 @@ describe('create materials', () => { 'Basic knowledge of classical physics', 'Familiarity with algebra and calculus', ], - materialType: MaterialType.other, + materialType: [MaterialType.other], copyright: '© 2024 Quantum Learning Inc.', timeEstimate: 120, // time in minutes theoryPractice: 70, + isDraft: false, + course: null }); expect(material).toMatchObject({ id: 3, @@ -142,6 +144,8 @@ describe('update materials', () => { coverPic: 'cover1.jpg', publicationId: 1, }); + prisma.publication.findUnique = vi.fn().mockResolvedValue({}) + prisma.publication.update = vi.fn().mockResolvedValue({}) const material = await updateMaterialByPublicationId(11, { title: 'Introduction to Quantum Physics', @@ -158,10 +162,13 @@ describe('update materials', () => { 'Basic knowledge of classical physics', 'Familiarity with algebra and calculus', ], - materialType: MaterialType.other, + materialType: [MaterialType.other], copyright: '© 2024 Quantum Learning Inc.', timeEstimate: 120, // time in minutes theoryPractice: 70, + fileURLs: [], + isDraft: false, + course: null, }); expect(material).toMatchObject({ id: 3, diff --git a/tests/unit/publication.test.ts b/tests/unit/publication.test.ts index e9de8ea6..07c5baeb 100644 --- a/tests/unit/publication.test.ts +++ b/tests/unit/publication.test.ts @@ -34,11 +34,19 @@ describe('handleConnections', () => { expect(prisma.publication.update).toHaveBeenNthCalledWith(1, { where: { id: 1 }, - data: { tags: { set: [] } }, + data: { maintainers: { set: [] } }, }); expect(prisma.publication.update).toHaveBeenNthCalledWith(2, { where: { id: 1 }, - data: { tags: { connect: [{ content: 'tag1' }] } }, + data: { maintainers: { connect: [] }}, + }); + expect(prisma.publication.update).toHaveBeenNthCalledWith(3, { + where: { id: 1 }, + data: { tags: { set: [] } }, + }); + expect(prisma.publication.update).toHaveBeenNthCalledWith(4, { + where: { id: 1 }, + data: { tags: { connect: [{content: 'tag1' }] }}, }); }); }); @@ -157,18 +165,20 @@ describe('updateAllTimeSaved', () => { it('should update savedByAllTime for a publication', async () => { const mockPublication = { id: 1, savedByAllTime: [] }; - prisma.publication.findUnique = vi + prisma.savedByAllTime.findUnique = vi + .fn() + .mockResolvedValue(null); + prisma.publication.findFirst = vi .fn() .mockResolvedValue(mockPublication); - prisma.publication.update = vi.fn().mockResolvedValue({}); + prisma.savedByAllTime.create = vi.fn().mockResolvedValue({}); const result = await updateAllTimeSaved('user1', 1); expect(result).toEqual({}); - expect(prisma.publication.update).toHaveBeenCalledWith({ - where: { id: 1 }, - data: { savedByAllTime: ['user1'] }, + expect(prisma.savedByAllTime.create).toHaveBeenCalledWith({ + data: { userId: 'user1', publicationId: 1 }, }); }); @@ -177,6 +187,11 @@ describe('updateAllTimeSaved', () => { prisma.publication.findUnique = vi .fn() .mockResolvedValue(mockPublication); + prisma.publication.findFirst = vi + .fn() + .mockResolvedValue(mockPublication); + + prisma.savedByAllTime.findUnique = vi.fn().mockResolvedValue({ userId: 'user', publicationId: 1 }); const result = await updateAllTimeSaved('user', 1); @@ -185,10 +200,9 @@ describe('updateAllTimeSaved', () => { vi.clearAllMocks(); - prisma.publication.findUnique = vi.fn().mockResolvedValue(null); - const result2 = await updateAllTimeSaved('user', 1); + prisma.publication.findFirst = vi.fn().mockResolvedValue(null); - expect(result2).toEqual(undefined); + await expect(updateAllTimeSaved('user', 1)).rejects.toThrowError(); expect(prisma.publication.update).not.toHaveBeenCalled(); }); }); diff --git a/tests/unit/runner.test.ts b/tests/unit/runner.test.ts index 06f5b6d2..ccc1fd9e 100644 --- a/tests/unit/runner.test.ts +++ b/tests/unit/runner.test.ts @@ -5,7 +5,6 @@ import { getCircuitByPublicationId, handleSimilarity } from "$lib/database"; -import {getFilesForMaterial, handleFileTokens} from "$lib/database/file"; import { compareNodesInBackground, compareMetaInBackground, @@ -78,7 +77,7 @@ describe('comparison functions', () => { it('should compare files in background', async () => { const mockResultFile: ResultFile = { similarity: 0.9, - filesToUpdate: [{ filePath: 'path/to/file', tokens: 'tokenString' }] + filesToUpdate: [{ filePath: 'path/to/file', tokens: 'tokenString', chunks: [] }] }; mockPiscinaRun.mockResolvedValue(mockResultFile); const mockFileA = { @@ -88,6 +87,7 @@ describe('comparison functions', () => { text: 'some text', userId: 'user1', publicationId: 1, + courseId: null, materialId: 1 }; const mockFileB = { @@ -97,6 +97,7 @@ describe('comparison functions', () => { text: 'some other text', userId: 'user2', publicationId: 2, + courseId: null, materialId: 2 }; const result = await compareFilesInBackground([mockFileA], [mockFileB], mockPiscinaInstance); @@ -105,7 +106,7 @@ describe('comparison functions', () => { }); it('should parse initial material file in background', async () => { - const mockFileTokenInfo: FileTokenInfo = [{ filePath: 'path/to/file', tokens: 'tokenString' }]; + const mockFileTokenInfo: FileTokenInfo = [{ filePath: 'path/to/file', tokens: 'tokenString', chunks: [] }]; mockPiscinaRun.mockResolvedValue(mockFileTokenInfo); const mockFile = { path: 'path/to/file', @@ -114,6 +115,7 @@ describe('comparison functions', () => { text: 'some text', userId: 'user1', publicationId: 1, + courseId: null, materialId: 1 }; const result = await initialMaterialFileParseInBackground([mockFile], mockPiscinaInstance); @@ -145,6 +147,8 @@ describe('comparison functions', () => { prerequisites: ['PR'], createdAt: new Date(), updatedAt: new Date(), + isDraft: false, + courseId: null, comments: [], publisher: { id: 'pub1', @@ -156,6 +160,8 @@ describe('comparison functions', () => { reputation: 0, password: null, aboutMe: '', + platformId: 'platform1', + institutionId: 'institution1', saved: [], posts: [], liked: [], @@ -175,13 +181,13 @@ describe('comparison functions', () => { Authenticator: [] }, publisherId: 'pub1', + coverPic: null, savedBy: [], likedBy: [], reportedBy: [], type: PublicationType.Material, node: [], savedByAllTime: [], - coverPic: null, similarToThis: [], thisSimilarTo: [], usedInCourse: [] @@ -195,7 +201,7 @@ describe('comparison functions', () => { theoryPractice: null, publication: publication, files: mockFiles - }; + } as any; /////////////////// const mockFiles2 = [{ @@ -207,6 +213,7 @@ describe('comparison functions', () => { profilePic: null, publicationId: 2, coverPic: null, + courseId: null, materialId: 1 }]; const publication2 = { @@ -221,6 +228,8 @@ describe('comparison functions', () => { prerequisites: ['PR'], createdAt: new Date(), updatedAt: new Date(), + isDraft: false, + courseId: null, comments: [], publisher: { id: 'pub1', @@ -251,13 +260,13 @@ describe('comparison functions', () => { Authenticator: [] }, publisherId: 'pub2', + coverPic: null, savedBy: [], likedBy: [], reportedBy: [], type: PublicationType.Material, node: [], savedByAllTime: [], - coverPic: null, similarToThis: [], thisSimilarTo: [], usedInCourse: [] @@ -271,7 +280,7 @@ describe('comparison functions', () => { theoryPractice: null, publication: publication2, files: mockFiles2 - }; + } as any; /////////////////// // Dynamically import the modules @@ -279,32 +288,13 @@ describe('comparison functions', () => { const fileModule = await import('$lib/database/file'); const piscinaUtilsModule = await import('$lib/PiscinaUtils/runner'); - const mockReturnValue = [mockMaterial, mockMaterial2] as unknown as ReturnType; - - const handleSimilarity = vi.spyOn(databaseModule, 'handleSimilarity').mockImplementation(async () => { - return; - }); - - const handleFileTokens = vi.spyOn(fileModule, 'handleFileTokens').mockImplementation(async () => { - return; - }); + const mockReturnValue = [mockMaterial, mockMaterial2] as any; const getFilesMock = vi.spyOn(fileModule, 'getFilesForMaterial').mockResolvedValue(mockFiles); - const initialMaterialMock= vi.spyOn(piscinaUtilsModule, 'initialMaterialFileParseInBackground'); const getAllMaterialsMock= vi.spyOn(databaseModule, 'getAllMaterials').mockImplementation( () => mockReturnValue); const getMaterialPubIdMock = vi.spyOn(databaseModule, 'getMaterialByPublicationId').mockResolvedValue(mockMaterial); - const compareFilesMock = vi.spyOn(piscinaUtilsModule, 'compareFilesInBackground').mockResolvedValue({ similarity: 0.9, filesToUpdate: [] }); - const compareMetaMock = vi.spyOn(piscinaUtilsModule, 'compareMetaInBackground').mockResolvedValue({ - title: 0.9, - description: 0.8, - learningObjectives: 0.7, - prerequisites: 0.6, - tags: 0.5, - difficulty: 0.4 - }); - mockPiscinaRun // first call initial parsing .mockResolvedValueOnce([{ filePath: 'path/to/file', tokens: 'tokenString' }]) @@ -323,33 +313,53 @@ describe('comparison functions', () => { // Assert that the mocks were called with the correct arguments expect(getFilesMock).toHaveBeenCalledWith(1); - expect(initialMaterialMock).toHaveBeenCalledWith(mockFiles, mockPiscinaInstance); - //expect(initialMaterialMock).toHaveBeenCalled(); expect(getAllMaterialsMock).toHaveBeenCalled(); expect(getMaterialPubIdMock).toHaveBeenCalledWith(1); - // expect(compareFilesMock).toHaveBeenCalledWith(mockFiles, mockFiles, mockPiscinaInstance); - // expect(compareMetaMock).toHaveBeenCalledWith({ - // title: 'Title', - // description: 'Description', - // learningObjectives: ['LO'], - // prerequisites: ['PR'], - // tags: ['tag'], - // difficulty: 'EASY' - // }, { - // title: 'Title', - // description: 'Description', - // learningObjectives: ['LO'], - // prerequisites: ['PR'], - // tags: ['tag'], - // difficulty: 'EASY' - // }, mockPiscinaInstance); - expect(handleSimilarity).toHaveBeenCalled(); - expect(handleFileTokens).toHaveBeenCalled(); + expect(mockPiscinaRun).toHaveBeenNthCalledWith(1, { pubFiles: mockFiles }, { name: 'initialParse' }); + expect(mockPiscinaRun).toHaveBeenNthCalledWith(2, { pubAFiles: mockFiles, pubBFiles: mockFiles2 }, { name: 'compareFiles' }); + expect(mockPiscinaRun).toHaveBeenNthCalledWith(3, { + pubA: { + title: 'Title', + description: 'Description', + learningObjectives: ['LO'], + prerequisites: ['PR'], + tags: ['tag'], + difficulty: Difficulty.easy + }, + pubB: { + title: 'Title 2', + description: 'Description 2', + learningObjectives: ['LO'], + prerequisites: ['PR'], + tags: ['tag'], + difficulty: Difficulty.hard + } + }, { name: 'compareMeta' }); + expect(databaseModule.handleSimilarity).toHaveBeenCalled(); + expect(fileModule.handleFileTokens).toHaveBeenCalled(); }); it('should enqueue circuit comparison', async () => { - const mockCircuits = [{ publicationId: 2 }]; + const mockCircuits = [{ publicationId: 2, + publication: { + title: 'Title', + description: 'Description', + learningObjectives: ['LO'], + prerequisites: ['PR'], + tags: [{ content: 'tag' }], + difficulty: 'EASY', + coverPic: null, + id: 2, + likes: 0, + createdAt: new Date(), + updatedAt: new Date(), + isDraft: false, + courseId: null, + publisherId: 'pub2' + }, + nodes: [{ publicationId: 3 }] + }] as any; const mockCircuit = { publication: { title: 'Title', @@ -357,43 +367,56 @@ describe('comparison functions', () => { learningObjectives: ['LO'], prerequisites: ['PR'], tags: [{ content: 'tag' }], - difficulty: 'EASY' + difficulty: 'EASY', + coverPic: null, + id: 1, + likes: 0, + createdAt: new Date(), + updatedAt: new Date(), + isDraft: false, + courseId: null, + publisherId: 'pub1' }, nodes: [{ publicationId: 3 }] - }; - + } as any; vi.mocked(getAllCircuits).mockResolvedValue(mockCircuits); vi.mocked(getCircuitByPublicationId).mockResolvedValue(mockCircuit); - vi.mocked(compareNodesInBackground).mockResolvedValue(0.9); - vi.mocked(compareMetaInBackground).mockResolvedValue({ - title: 0.9, - description: 0.8, - learningObjectives: 0.7, - prerequisites: 0.6, - tags: 0.5, - difficulty: 0.4 - }); - await enqueueCircuitComparison(1); + + mockPiscinaRun + .mockResolvedValueOnce(0.9) + .mockResolvedValueOnce({ + title: 0.9, + description: 0.8, + learningObjectives: 0.7, + prerequisites: 0.6, + tags: 0.5, + difficulty: 0.4 + }); + + await enqueueCircuitComparison(1, mockPiscinaInstance); expect(getAllCircuits).toHaveBeenCalled(); expect(getCircuitByPublicationId).toHaveBeenCalledWith(1); - expect(compareNodesInBackground).toHaveBeenCalledWith([3], [3], mockPiscinaInstance); - expect(compareMetaInBackground).toHaveBeenCalledWith({ - title: 'Title', - description: 'Description', - learningObjectives: ['LO'], - prerequisites: ['PR'], - tags: ['tag'], - difficulty: 'EASY' - }, { - title: 'Title', - description: 'Description', - learningObjectives: ['LO'], - prerequisites: ['PR'], - tags: ['tag'], - difficulty: 'EASY' - }, mockPiscinaInstance); + expect(mockPiscinaRun).toHaveBeenNthCalledWith(1, { pubANodes: [3], pubBNodes: [3] }, { name: 'compareNodes' }); + expect(mockPiscinaRun).toHaveBeenNthCalledWith(2, { + pubA: { + title: 'Title', + description: 'Description', + learningObjectives: ['LO'], + prerequisites: ['PR'], + tags: ['tag'], + difficulty: 'EASY' + }, + pubB: { + title: 'Title', + description: 'Description', + learningObjectives: ['LO'], + prerequisites: ['PR'], + tags: ['tag'], + difficulty: 'EASY' + } + }, { name: 'compareMeta' }); expect(handleSimilarity).toHaveBeenCalled(); }); }); \ No newline at end of file diff --git a/tests/unit/savingFiles.test.ts b/tests/unit/savingFiles.test.ts index 813aa295..f0f603c6 100644 --- a/tests/unit/savingFiles.test.ts +++ b/tests/unit/savingFiles.test.ts @@ -49,15 +49,15 @@ describe('Local File Handling', () => { const pathSaved = await fileSystem.saveFile(binaryData, 'blob2.txt'); createdFiles.push(pathSaved); - const data = fileSystem.readFile(pathSaved); + const data = await fileSystem.readFile(pathSaved); expect(data.toString()).toEqual('Hello I am a blob, my name is blob'); await fileSystem.editFile(pathSaved, Buffer.from('I am a new blob')); - const data2 = fileSystem.readFile(pathSaved); + const data2 = await fileSystem.readFile(pathSaved); expect(data2.toString()).toEqual('I am a new blob'); fileSystem.deleteFile(pathSaved); - expect(() => fileSystem.readFile(pathSaved)).toThrowError(); + expect(async () => await fileSystem.readFile(pathSaved)).rejects.toThrowError(); }); }); diff --git a/tests/unit/tag.test.ts b/tests/unit/tag.test.ts index dd130d03..9306b221 100644 --- a/tests/unit/tag.test.ts +++ b/tests/unit/tag.test.ts @@ -24,8 +24,8 @@ describe('get specific material', () => { { content: 'new tag' }, ]); expect(prisma.tag.findMany).toHaveBeenCalledWith({ - include: { - publication: false, + select: { + content: true, }, }); }); diff --git a/tests/utility/publicationsUtility.ts b/tests/utility/publicationsUtility.ts index 3745a2f6..108d8f23 100644 --- a/tests/utility/publicationsUtility.ts +++ b/tests/utility/publicationsUtility.ts @@ -8,6 +8,7 @@ import { createRandomCourse } from './courses'; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + export function generateRandomString(n: number = 20): string { let result = ''; for (let i = 0; i < n; i++) { @@ -82,6 +83,13 @@ export async function createMaterialData(userId: string): Promise delete: [], edit: [] }, + changeLog: { + globalComment: generateRandomString(), + fileComments: { + added: {}, + deleted: {} + } + }, coverPic: null } } @@ -107,6 +115,7 @@ export async function createUniqueMaterial(userId: string): Promise