Skip to content

Commit

Permalink
chore: add more unit tests (#7371)
Browse files Browse the repository at this point in the history
* chore: add more unit tests

* chore: text correction
  • Loading branch information
ovflowd authored Jan 3, 2025
1 parent 3e1c9cd commit e20a801
Show file tree
Hide file tree
Showing 21 changed files with 626 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { render, screen } from '@testing-library/react';

import ActiveLink from '..';

// mock usePathname, but retain all the other imports
jest.mock('@/navigation.mjs', () => ({
...jest.requireActual('@/navigation.mjs'),
usePathname: jest.fn(),
Expand Down
4 changes: 1 addition & 3 deletions apps/site/jest.config.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import nextJest from 'next/jest.js';

const createJestConfig = nextJest({
dir: './',
});
const createJestConfig = nextJest({ dir: './' });

/** @type {import('jest').Config} */
const customJestConfig = {
Expand Down
25 changes: 25 additions & 0 deletions apps/site/providers/__tests__/navigationStateProvider.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { render } from '@testing-library/react';

import { NavigationStateProvider } from '@/providers/navigationStateProvider';

describe('NavigationStateProvider', () => {
it('should render without crashing', () => {
const { container } = render(
<NavigationStateProvider>
<div />
</NavigationStateProvider>
);

expect(container).toBeDefined();
});

it('should provide navigation state context', () => {
const { getByText } = render(
<NavigationStateProvider>
<div>Navigation State</div>
</NavigationStateProvider>
);

expect(getByText('Navigation State')).toBeDefined();
});
});
39 changes: 39 additions & 0 deletions apps/site/providers/__tests__/releaseProvider.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { render } from '@testing-library/react';

import { ReleaseProvider, ReleasesProvider } from '@/providers/releaseProvider';

describe('ReleaseProvider', () => {
it('should render without crashing', () => {
const initialRelease = { versionWithPrefix: 'v14.17.0' };
const releases = [initialRelease];
const snippets = [];

const { container } = render(
<ReleasesProvider releases={releases} snippets={snippets}>
<ReleaseProvider initialRelease={initialRelease}>
<div />
</ReleaseProvider>
</ReleasesProvider>
);

expect(container).toBeDefined();
});

it('should set version from parent provider', () => {
const initialRelease = { versionWithPrefix: 'v14.17.0' };
const releases = [initialRelease];
const snippets = [];

const { getByText } = render(
<ReleasesProvider releases={releases} snippets={snippets}>
<ReleaseProvider initialRelease={initialRelease}>
<ReleaseProvider>
<div>Child Provider</div>
</ReleaseProvider>
</ReleaseProvider>
</ReleasesProvider>
);

expect(getByText('Child Provider')).toBeDefined();
});
});
78 changes: 78 additions & 0 deletions apps/site/reducers/__tests__/releaseReducer.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import reducer, { releaseState, getActions } from '@/reducers/releaseReducer';

describe('releaseReducer', () => {
it('should return the initial state', () => {
const initialState = releaseState;
const action = { type: 'UNKNOWN_ACTION' };
expect(reducer(initialState, action)).toEqual(initialState);
});

it('should handle SET_VERSION action', () => {
const initialState = releaseState;
const action = { type: 'SET_VERSION', payload: 'v14.17.0' };
const expectedState = { ...initialState, version: 'v14.17.0' };
expect(reducer(initialState, action)).toEqual(expectedState);
});

it('should handle SET_OS action', () => {
const initialState = releaseState;
const action = { type: 'SET_OS', payload: 'Linux' };
const expectedState = { ...initialState, os: 'Linux' };
expect(reducer(initialState, action)).toEqual(expectedState);
});

it('should handle SET_PLATFORM action', () => {
const initialState = releaseState;
const action = { type: 'SET_PLATFORM', payload: 'arm64' };
const expectedState = { ...initialState, platform: 'arm64' };
expect(reducer(initialState, action)).toEqual(expectedState);
});

it('should handle SET_INSTALL_METHOD action', () => {
const initialState = releaseState;
const action = { type: 'SET_INSTALL_METHOD', payload: 'binary' };
const expectedState = { ...initialState, installMethod: 'binary' };
expect(reducer(initialState, action)).toEqual(expectedState);
});

it('should handle SET_MANAGER action', () => {
const initialState = releaseState;
const action = { type: 'SET_MANAGER', payload: 'Yarn' };
const expectedState = { ...initialState, packageManager: 'Yarn' };
expect(reducer(initialState, action)).toEqual(expectedState);
});
});

describe('getActions', () => {
it('should create actions correctly', () => {
const dispatch = jest.fn();
const actions = getActions(dispatch);

actions.setVersion('v14.17.0');
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_VERSION',
payload: 'v14.17.0',
});

actions.setOS('Linux');
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_OS', payload: 'Linux' });

actions.setPlatform('arm64');
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_PLATFORM',
payload: 'arm64',
});

actions.setInstallMethod('binary');
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_INSTALL_METHOD',
payload: 'binary',
});

actions.setPackageManager('Yarn');
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_MANAGER',
payload: 'Yarn',
});
});
});
25 changes: 24 additions & 1 deletion apps/site/util/__tests__/authorUtils.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import {
mapAuthorToCardAuthors,
getAuthorWithId,
getAuthorWithName,
} from '../authorUtils';
getAuthors,
} from '@/util/authorUtils';

describe('mapAuthorToCardAuthors', () => {
it('maps authors to card authors with default avatar source', () => {
Expand Down Expand Up @@ -96,3 +97,25 @@ describe('getAuthorWithName', () => {
]);
});
});

describe('authorUtils', () => {
test('mapAuthorToCardAuthors splits authors by common separators', () => {
const result = mapAuthorToCardAuthors('Node, React & prepared by Vue');
expect(result).toEqual(['Node', 'React', 'Vue']);
});

test('getAuthorWithId returns objects with GitHub avatars', () => {
const result = getAuthorWithId(['someUser'], false);
expect(result[0].image).toContain('github');
});

test('getAuthorWithName returns known author details or fallback to acronym', () => {
const result = getAuthorWithName(['unknownAuthor'], true);
expect(result[0].fallback).toBe('U');
});

test('getAuthors uses getAuthorWithId if usernames array is provided', () => {
const result = getAuthors({ usernames: ['testUser'] });
expect(result[0].nickname).toBe('testUser');
});
});
12 changes: 12 additions & 0 deletions apps/site/util/__tests__/blogUtils.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { mapBlogCategoryToPreviewType } from '@/util/blogUtils';

describe('mapBlogCategoryToPreviewType', () => {
test('returns the correct preview type for recognized categories', () => {
expect(mapBlogCategoryToPreviewType('release')).toBe('release');
expect(mapBlogCategoryToPreviewType('events')).toBe('announcements');
});

test('defaults to announcements for unknown categories', () => {
expect(mapBlogCategoryToPreviewType('random')).toBe('announcements');
});
});
16 changes: 13 additions & 3 deletions apps/site/util/__tests__/dateUtils.test.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { dateIsBetween } from '@/util/dateUtils';

describe('dateIsBetween', () => {
it('returns true when the current date is between start and end dates', () => {
it('should return true when the current date is between start and end dates', () => {
const startDate = '2022-01-01T00:00:00.000Z';
const endDate = '2069-01-01T00:00:00.000Z';

Expand All @@ -10,7 +10,7 @@ describe('dateIsBetween', () => {
expect(result).toBe(true);
});

it('returns false when the current date is not between start and end dates', () => {
it('should return false when the current date is not between start and end dates', () => {
const startDate = '2010-01-01T00:00:00.000Z';
const endDate = '2020-01-01T00:00:00.000Z';

Expand All @@ -19,12 +19,22 @@ describe('dateIsBetween', () => {
expect(result).toBe(false);
});

it('throws when either start or end date is invalid', () => {
it('should throw an error when either start or end date is invalid', () => {
const invalidStartDate = 'Invalid Start Date';
const validEndDate = '2024-01-01T00:00:00.000Z';

expect(() => dateIsBetween(invalidStartDate, validEndDate)).toThrow(
'dateIsBetween got called with invalid dates'
);
});

it('should return true if now is between startDate and endDate', () => {
const start = new Date(Date.now() - 1000).toISOString();
const end = new Date(Date.now() + 1000).toISOString();
expect(dateIsBetween(start, end)).toBe(true);
});

it('should throw an error if dates are invalid', () => {
expect(() => dateIsBetween('invalid', 'invalid')).toThrow();
});
});
15 changes: 13 additions & 2 deletions apps/site/util/__tests__/debounce.test.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { debounce } from '@/util/debounce';

describe('debounce', () => {
jest.useFakeTimers();
jest.useFakeTimers();

describe('debounce', () => {
it('should call the function only once', () => {
const fn = jest.fn();
const debouncedFn = debounce(fn, 1000);
Expand All @@ -28,4 +28,15 @@ describe('debounce', () => {

expect(fn).toHaveBeenCalledWith(3);
});

it('should call the function after the delay', () => {
const mockFn = jest.fn();
const debouncedFn = debounce(mockFn, 500);

debouncedFn();
expect(mockFn).not.toBeCalled();

jest.advanceTimersByTime(500);
expect(mockFn).toBeCalled();
});
});
17 changes: 17 additions & 0 deletions apps/site/util/__tests__/deepMerge.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import deepMerge from '@/util/deepMerge';

describe('deepMerge', () => {
it('should merge nested objects', () => {
const obj1 = { a: { b: 1 }, c: 2 };
const obj2 = { a: { d: 3 }, e: 4 };
const result = deepMerge(obj1, obj2);
expect(result).toEqual({ a: { b: 1, d: 3 }, c: 2, e: 4 });
});

it('should overwrite primitive properties', () => {
const obj1 = { a: 1 };
const obj2 = { a: 2 };
const result = deepMerge(obj1, obj2);
expect(result).toEqual({ a: 2 });
});
});
92 changes: 65 additions & 27 deletions apps/site/util/__tests__/detectOS.test.mjs
Original file line number Diff line number Diff line change
@@ -1,31 +1,69 @@
import { detectOsInUserAgent } from '@/util/detectOS';
import { detectOsInUserAgent, detectOS } from '@/util/detectOS';

const userAgentTestCases = [
[
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246',
'WIN',
],
[
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
'MAC',
],
[
'Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36',
'OTHER',
],
[
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1',
'LINUX',
],
[
'Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.3 Mobile/15E148 Safari/604.1',
'MAC',
],
['', 'OTHER'],
['OTHERAgent/1.0', 'OTHER'],
[undefined, 'OTHER'],
];

describe('detectOsInUserAgent', () => {
it.each([
[
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246',
'WIN',
],
[
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
'MAC',
],
[
'Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36',
'OTHER',
],
[
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1',
'LINUX',
],
[
'Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.3 Mobile/15E148 Safari/604.1',
'MAC',
],
['', 'OTHER'],
['OTHERAgent/1.0', 'OTHER'],
[undefined, 'OTHER'],
])('detectOsInUserAgent(%s) returns %s', (os, expected) => {
expect(detectOsInUserAgent(os)).toBe(expected);
it.each(userAgentTestCases)(
'should return %s for userAgent %s',
(userAgent, expected) => {
expect(detectOsInUserAgent(userAgent)).toBe(expected);
}
);

it('should return OTHER if no match is found', () => {
const result = detectOsInUserAgent('no-match');
expect(result).toBe('OTHER');
});

it('should detect Windows', () => {
const result = detectOsInUserAgent('Mozilla Win something');
expect(result).toBe('WIN');
});

it('should detect Linux', () => {
const result = detectOsInUserAgent('Mozilla/5.0 (X11; Linux x86_64)');
expect(result).toBe('LINUX');
});
});

describe('detectOS', () => {
it('should call detectOsInUserAgent', () => {
Object.defineProperty(global, 'navigator', {
value: { userAgent: 'Mac' },
configurable: true,
});
expect(detectOS()).toBe('MAC');
});

it('should return OTHER if navigator is undefined', () => {
Object.defineProperty(global, 'navigator', {
value: undefined,
configurable: true,
});
expect(detectOS()).toBe('OTHER');
});
});
Loading

0 comments on commit e20a801

Please sign in to comment.