Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: unit tests for utils functions #92

Merged
merged 9 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/dashboard/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": [["@babel/preset-env", { "targets": { "node": "current" } }]]
}
3 changes: 3 additions & 0 deletions apps/dashboard/babel.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [['@babel/preset-env', { targets: { node: 'current' } }]]
};
21 changes: 21 additions & 0 deletions apps/dashboard/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* For a detailed explanation regarding each configuration property, visit:
* https://jestjs.io/docs/configuration
Toyin5 marked this conversation as resolved.
Show resolved Hide resolved
*/

import type { Config } from 'jest';

const config: Config = {};

export default config;

/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
transform: {
'^.+\\.svelte$': 'svelte-jester',
'^.+\\.ts$': 'ts-jest',
'\\.[jt]sx?$': 'babel-jest'
}
};
13 changes: 12 additions & 1 deletion apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
"export": "vite build --entry \"/ /404 login signup forgot onboarding courses org lms course\"",
"postexport": "mv __sapper__/export/404/index.html __sapper__/export/404.html",
"start": "node build",
"prepare": "svelte-kit sync"
"prepare": "svelte-kit sync",
"test": "jest",
"test:watch": "pnpm run test --watch"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.386.0",
Expand Down Expand Up @@ -57,26 +59,35 @@
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.22.9",
"@babel/preset-env": "^7.22.9",
"@babel/preset-typescript": "^7.23.3",
"@babel/runtime": "^7.22.6",
"@sveltejs/kit": "^1.22.4",
"@tailwindcss/typography": "^0.5.9",
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/svelte": "^4.0.5",
"@types/jest": "^29.5.10",
"@types/lodash": "^4.14.196",
"@types/nodemailer": "^6.4.11",
"autoprefixer": "^10.4.14",
"babel-jest": "^29.7.0",
"carbon-components-svelte": "^0.79.0",
"carbon-icons-svelte": "^12.1.0",
"dotenv": "^16.3.1",
"eslint-config-custom": "workspace:*",
"jest": "^29.7.0",
"postcss": "^8.4.24",
"postcss-load-config": "^4.0.1",
"sass": "^1.64.2",
"supabase": "^1.112.0",
"svelte": "^4.1.2",
"svelte-calendar": "^3.1.6",
"svelte-dnd-action": "^0.9.24",
"svelte-jester": "^3.0.0",
"svelte-loading-spinners": "^0.3.4",
"svelte-preprocess": "^5.0.4",
"tailwindcss": "^3.3.2",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.2",
"tsconfig": "workspace:*",
"tslib": "^2.6.1",
"typescript": "^5.1.6",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import { browser } from '$app/environment';
import Modal from '$lib/components/Modal/index.svelte';
import TextField from '$lib/components/Form/TextField.svelte';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
Expand Down Expand Up @@ -98,7 +99,7 @@
}, 500);
}

$: note = getTextFromHTML($lesson?.materials?.note || '');
$: note = browser ? getTextFromHTML($lesson?.materials?.note || '') : '';
</script>

<Modal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import { browser } from '$app/environment';
import isEmpty from 'lodash/isEmpty';
import { useCompletion } from 'ai/svelte';
import MODES from '$lib/utils/constants/mode.js';
Expand Down Expand Up @@ -84,7 +85,7 @@

if (!document) return false;

return getTextFromHTML(note) === '';
return browser ? getTextFromHTML(note) === '' : false;
}

function isMaterialsEmpty(materials: LessonPage['materials']) {
Expand Down
30 changes: 30 additions & 0 deletions apps/dashboard/src/lib/utils/functions/IsSubmissionEarly.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import isSubmissionEarly from './isSubmissionEarly';

describe('isSubmissionEarly', () => {
test('should return true when createdAt is earlier than dueDate', () => {
const dueDate = Date.now();
const createdAt = new Date('2023-12-15');
const result = isSubmissionEarly(createdAt, dueDate);
expect(result).toBe(true);
});

test('should return true when both createdAt and dueDate are null', () => {
const dueDate = null;
const createdAt = null;
const result = isSubmissionEarly(createdAt, dueDate);
expect(result).toBe(true);
});
test('should return true when createdAt is the same as dueDate', () => {
const dueDate = new Date('2023-12-15');
const createdAt = new Date('2023-12-15');
const result = isSubmissionEarly(createdAt, dueDate);
expect(result).toBe(true);
});

test('should return false when createdAt is later than dueDate', () => {
const dueDate = new Date('2023-12-15');
const createdAt = new Date('2023-12-16');
const result = isSubmissionEarly(createdAt, dueDate);
expect(result).toBe(false);
});
});
18 changes: 18 additions & 0 deletions apps/dashboard/src/lib/utils/functions/course.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { isCourseFree } from './course';

describe('course.ts', () => {
test('Should return True when cost is 0', () => {
const result = isCourseFree(0);
expect(result).toBeTruthy();
});

test('Should return true when cost is negative', () => {
const result = isCourseFree(-10);
expect(result).toBeTruthy();
});

test('Should return True when cost is Not A Number', () => {
const result = isCourseFree(NaN);
expect(result).toBeTruthy();
});
});
3 changes: 0 additions & 3 deletions apps/dashboard/src/lib/utils/functions/course.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { browser } from '$app/environment';
import type { Course } from '../types';

export const isCourseFree = (cost: number) => !(Number(cost) > 0);
Expand All @@ -19,8 +18,6 @@ export const getStudentInviteLink = (_course: Course, orgSiteName: string, origi
};

export const getTextFromHTML = (html: string): string => {
if (!browser) return html;

const dummyDiv = document.createElement('div');
dummyDiv.innerHTML = html;

Expand Down
36 changes: 36 additions & 0 deletions apps/dashboard/src/lib/utils/functions/date.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { calDateDiff, getGreeting } from './date';

describe('calDateDiff', () => {
test('should return the correct time difference when the input date is in the past', () => {
const inputDate = new Date('2022-01-01');
const expectedOutput = '2 years ago';

const result = calDateDiff(inputDate);

expect(result).toBe(expectedOutput);
});

test('should return the correct time difference when the input date is a number', () => {
const inputDate = new Date(1640995200000); // January 1, 2022
const expectedOutput = '2 years ago';

const result = calDateDiff(inputDate);

expect(result).toBe(expectedOutput);
});
});

describe('getGreeting', () => {
test("should return 'Good Morning' when the current time is before 12pm", () => {
jest.useFakeTimers().setSystemTime(new Date('2023-12-10T00:21:01.691Z'));
expect(getGreeting()).toBe('Good Morning');
});
test("should return 'Good Afternoon' when the current time is after 12pm", () => {
jest.useFakeTimers().setSystemTime(new Date('2023-12-10T14:21:01.691Z'));
expect(getGreeting()).toBe('Good Afternoon');
});
test("should return 'Good Evening' when the current time is after 6pm", () => {
jest.useFakeTimers().setSystemTime(new Date('2023-12-10T19:21:01.691Z'));
expect(getGreeting()).toBe('Good Evening');
});
});
46 changes: 46 additions & 0 deletions apps/dashboard/src/lib/utils/functions/formatYoutubeVideo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { formatYoutubeVideo, getEmbedId } from './formatYoutubeVideo';

describe('format youtube video', () => {
test('should return the same url when given a valid youtube embed url', () => {
const url = 'https://www.youtube.com/embed/qajK1J1neAM';
const errors = { video: '' };
const result = formatYoutubeVideo(url, errors);
expect(result).toBe(url);
});

test('should return the correct embed url when given a youtube url with a start time parameter', () => {
const url = 'https://www.youtube.com/watch?v=qajK1J1neAM&start=123';
const errors = { video: '' };
const result = formatYoutubeVideo(url, errors);
expect(result).toBe('https://www.youtube.com/embed/qajK1J1neAM');
});
});

describe('getEmbedId', () => {
test('should return the correct embed id when the url contains "embed"', () => {
const url = 'https://www.youtube.com/embed/qajK1J1neAM';
const expectedEmbedId = 'qajK1J1neAM';

const result = getEmbedId(url);

expect(result).toBe(expectedEmbedId);
});

test('should return an empty string when the url does not contain "watch" or "embed" and is not in the format "youtu.be/"', () => {
const url = 'https://www.youtube.com/invalid-url';
const expectedEmbedId = '';

const result = getEmbedId(url);

expect(result).toBe(expectedEmbedId);
});

test('should return an empty string when the url is an empty string', () => {
const url = '';
const expectedEmbedId = '';

const result = getEmbedId(url);

expect(result).toBe(expectedEmbedId);
});
});
25 changes: 25 additions & 0 deletions apps/dashboard/src/lib/utils/functions/genUniqueId.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import genUniqueId from './genUniqueId';

describe('Generate Unique Ids', () => {
test('should start with letter', () => {
const result = genUniqueId();
const firstChar = result.charAt(0);
expect(firstChar).toMatch(/[a-zA-Z]/);
});

test('should not contain special characters', () => {
const result = genUniqueId();
expect(result).toMatch(/^[a-zA-Z0-9]+$/);
});

test('should return different ids on multiple calls', () => {
const result1 = genUniqueId();
const result2 = genUniqueId();
expect(result1).not.toEqual(result2);
});
test('multiple calls in closer time frames should have same first 3 letters', () => {
const result1 = genUniqueId();
const result2 = genUniqueId();
expect(result1.substring(0, 4)).toEqual(result2.substring(0, 4));
});
});
17 changes: 17 additions & 0 deletions apps/dashboard/src/lib/utils/functions/generateSlug.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import generateSlug from './generateSlug';

describe('generateSlug', () => {
test('should return a string with lowercase letters and hyphens when given a valid title', () => {
const title = 'This is a Valid Title';
const expected = 'this-is-a-valid-title' + `-${new Date().getTime()}`;
const result = generateSlug(title);
expect(result).toBe(expected);
});

test('should include the current timestamp at the end of the string', () => {
const title = 'This is a Valid Title';
const result = generateSlug(title);
const timestamp = result.split('-').pop();
expect(Number(timestamp)).toBeGreaterThan(0);
});
});
26 changes: 26 additions & 0 deletions apps/dashboard/src/lib/utils/functions/generateUUID.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import generateUUID from './generateUUID';
import { isUUID } from './isUUID';

// This test suite tests both the generateUUID and isUUID functions
describe('GenerateUUID', () => {
test('should be true when tested with the uuid function', () => {
const result = generateUUID();
expect(isUUID(result)).toBe(true);
});

test('should generate unique UUIDs each time', () => {
const uuid1 = generateUUID();
const uuid2 = generateUUID();
expect(uuid1).not.toBe(uuid2);
});

test('should return true for a valid UUID', () => {
const uuid = '123e4567-e89b-12d3-a456-426614174000';
expect(isUUID(uuid)).toBe(true);
});

test('should return false for an invalid UUID', () => {
const uuid = 'invalid-uuid';
expect(isUUID(uuid)).toBe(false);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import getCurrencyFormatter from './getCurrencyFormatter';

describe('GetCurrencyFormatter', () => {
test('should return a new instance of Intl.NumberFormat with the correct locale and currency options when currency is "NGN"', () => {
const result = getCurrencyFormatter('NGN');
expect(result).toBeInstanceOf(Intl.NumberFormat);
expect(result.resolvedOptions().locale).toBe('en-NG');
expect(result.resolvedOptions().currency).toBe('NGN');
expect(result.resolvedOptions().minimumFractionDigits).toBe(2);
});

test('should return a new instance of Intl.NumberFormat with the correct locale and currency options when currency is "USD"', () => {
const result = getCurrencyFormatter('USD');
expect(result).toBeInstanceOf(Intl.NumberFormat);
expect(result.resolvedOptions().locale).toBe('en-US');
expect(result.resolvedOptions().currency).toBe('USD');
expect(result.resolvedOptions().minimumFractionDigits).toBe(2);
});

test('should return a new instance of Intl.NumberFormat with the correct locale and currency options when currency is "EUR"', () => {
const result = getCurrencyFormatter('EUR');
expect(result).toBeInstanceOf(Intl.NumberFormat);
expect(result.resolvedOptions().locale).toBe('en-US');
expect(result.resolvedOptions().currency).toBe('EUR');
expect(result.resolvedOptions().minimumFractionDigits).toBe(2);
});
});
23 changes: 23 additions & 0 deletions apps/dashboard/src/lib/utils/functions/isObject.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { isObject } from './isObject';

describe('isObject', () => {
test('should return true for an empty object', () => {
const obj = {};
expect(isObject(obj)).toBe(true);
});

test('should return false for a non-object', () => {
const obj = 'not an object';
expect(isObject(obj)).toBe(false);
});

test('should return true for a nested object', () => {
const obj = { prop1: { prop2: 'value2' } };
expect(isObject(obj)).toBe(true);
});

test('should return true for an object with properties', () => {
const obj = { prop1: 'value1', prop2: 'value2' };
expect(isObject(obj)).toBe(true);
});
});
1 change: 1 addition & 0 deletions apps/dashboard/src/lib/utils/functions/isObject.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//
export function isObject(obj) {
return obj === Object(obj);
}
Loading