From 7a2e0a6a32ea1f06bcb743fa2754821fe588b45a Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Sun, 29 Dec 2024 12:16:43 +0000 Subject: [PATCH 1/2] chore: add more unit tests --- apps/site/jest.config.mjs | 4 +- .../navigationStateProvider.test.mjs | 25 +++ .../__tests__/releaseProvider.test.mjs | 39 +++++ .../__tests__/releaseReducer.test.mjs | 78 ++++++++++ apps/site/util/__tests__/authorUtils.test.mjs | 25 ++- apps/site/util/__tests__/blogUtils.test.mjs | 12 ++ apps/site/util/__tests__/dateUtils.test.mjs | 16 +- apps/site/util/__tests__/debounce.test.mjs | 15 +- apps/site/util/__tests__/deepMerge.test.mjs | 17 +++ apps/site/util/__tests__/detectOS.test.mjs | 92 +++++++---- .../util/__tests__/downloadUtils.test.mjs | 143 ++++++++++++++++++ .../__tests__/getHighEntropyValues.test.mjs | 42 +++++ .../__tests__/getLanguageDisplayName.test.mjs | 25 +++ .../util/__tests__/getNodeApiLink.test.mjs | 18 ++- .../__tests__/getNodeDownloadUrl.test.mjs | 22 ++- .../util/__tests__/getUserPlatform.test.mjs | 15 ++ apps/site/util/__tests__/gitHubUtils.test.mjs | 26 +++- apps/site/util/__tests__/hexToRGBA.test.mjs | 8 + apps/site/util/__tests__/imageUtils.test.mjs | 11 ++ apps/site/util/__tests__/stringUtils.test.mjs | 39 +++++ 20 files changed, 627 insertions(+), 45 deletions(-) create mode 100644 apps/site/providers/__tests__/navigationStateProvider.test.mjs create mode 100644 apps/site/providers/__tests__/releaseProvider.test.mjs create mode 100644 apps/site/reducers/__tests__/releaseReducer.test.mjs create mode 100644 apps/site/util/__tests__/blogUtils.test.mjs create mode 100644 apps/site/util/__tests__/deepMerge.test.mjs create mode 100644 apps/site/util/__tests__/downloadUtils.test.mjs create mode 100644 apps/site/util/__tests__/getHighEntropyValues.test.mjs create mode 100644 apps/site/util/__tests__/getLanguageDisplayName.test.mjs create mode 100644 apps/site/util/__tests__/getUserPlatform.test.mjs diff --git a/apps/site/jest.config.mjs b/apps/site/jest.config.mjs index 5bdcdf737d1ae..68b9b84b76716 100644 --- a/apps/site/jest.config.mjs +++ b/apps/site/jest.config.mjs @@ -1,8 +1,6 @@ import nextJest from 'next/jest.js'; -const createJestConfig = nextJest({ - dir: './', -}); +const createJestConfig = nextJest({ dir: './' }); /** @type {import('jest').Config} */ const customJestConfig = { diff --git a/apps/site/providers/__tests__/navigationStateProvider.test.mjs b/apps/site/providers/__tests__/navigationStateProvider.test.mjs new file mode 100644 index 0000000000000..0d1bcfedecbc9 --- /dev/null +++ b/apps/site/providers/__tests__/navigationStateProvider.test.mjs @@ -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( + +
+ + ); + + expect(container).toBeDefined(); + }); + + it('should provide navigation state context', () => { + const { getByText } = render( + +
Navigation State
+
+ ); + + expect(getByText('Navigation State')).toBeDefined(); + }); +}); diff --git a/apps/site/providers/__tests__/releaseProvider.test.mjs b/apps/site/providers/__tests__/releaseProvider.test.mjs new file mode 100644 index 0000000000000..96db4199e8b6c --- /dev/null +++ b/apps/site/providers/__tests__/releaseProvider.test.mjs @@ -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( + + +
+ + + ); + + expect(container).toBeDefined(); + }); + + it('should set version from parent provider', () => { + const initialRelease = { versionWithPrefix: 'v14.17.0' }; + const releases = [initialRelease]; + const snippets = []; + + const { getByText } = render( + + + +
Child Provider
+
+
+
+ ); + + expect(getByText('Child Provider')).toBeDefined(); + }); +}); diff --git a/apps/site/reducers/__tests__/releaseReducer.test.mjs b/apps/site/reducers/__tests__/releaseReducer.test.mjs new file mode 100644 index 0000000000000..d8688bc9ee874 --- /dev/null +++ b/apps/site/reducers/__tests__/releaseReducer.test.mjs @@ -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', + }); + }); +}); diff --git a/apps/site/util/__tests__/authorUtils.test.mjs b/apps/site/util/__tests__/authorUtils.test.mjs index 7a5a41d35db3a..868c2f2de40c8 100644 --- a/apps/site/util/__tests__/authorUtils.test.mjs +++ b/apps/site/util/__tests__/authorUtils.test.mjs @@ -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', () => { @@ -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'); + }); +}); diff --git a/apps/site/util/__tests__/blogUtils.test.mjs b/apps/site/util/__tests__/blogUtils.test.mjs new file mode 100644 index 0000000000000..5f573ab1e1127 --- /dev/null +++ b/apps/site/util/__tests__/blogUtils.test.mjs @@ -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'); + }); +}); diff --git a/apps/site/util/__tests__/dateUtils.test.mjs b/apps/site/util/__tests__/dateUtils.test.mjs index 5fe75341814bb..7b20dec68f750 100644 --- a/apps/site/util/__tests__/dateUtils.test.mjs +++ b/apps/site/util/__tests__/dateUtils.test.mjs @@ -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'; @@ -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'; @@ -19,7 +19,7 @@ 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'; @@ -27,4 +27,14 @@ describe('dateIsBetween', () => { '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(); + }); }); diff --git a/apps/site/util/__tests__/debounce.test.mjs b/apps/site/util/__tests__/debounce.test.mjs index 401feb699b65c..17188ab8f00a1 100644 --- a/apps/site/util/__tests__/debounce.test.mjs +++ b/apps/site/util/__tests__/debounce.test.mjs @@ -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); @@ -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(); + }); }); diff --git a/apps/site/util/__tests__/deepMerge.test.mjs b/apps/site/util/__tests__/deepMerge.test.mjs new file mode 100644 index 0000000000000..8e895740b3fc7 --- /dev/null +++ b/apps/site/util/__tests__/deepMerge.test.mjs @@ -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 }); + }); +}); diff --git a/apps/site/util/__tests__/detectOS.test.mjs b/apps/site/util/__tests__/detectOS.test.mjs index 9e006f96144ee..dd3ccd94da954 100644 --- a/apps/site/util/__tests__/detectOS.test.mjs +++ b/apps/site/util/__tests__/detectOS.test.mjs @@ -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'); }); }); diff --git a/apps/site/util/__tests__/downloadUtils.test.mjs b/apps/site/util/__tests__/downloadUtils.test.mjs new file mode 100644 index 0000000000000..78f7dd3ab5333 --- /dev/null +++ b/apps/site/util/__tests__/downloadUtils.test.mjs @@ -0,0 +1,143 @@ +import { + parseCompat, + nextItem, + OPERATING_SYSTEMS, + INSTALL_METHODS, + PACKAGE_MANAGERS, + PLATFORMS, +} from '@/util/downloadUtils'; + +describe('parseCompat', () => { + it('should handle all OS, install methods, and package managers', () => { + OPERATING_SYSTEMS.forEach(os => { + INSTALL_METHODS.forEach(method => { + PACKAGE_MANAGERS.forEach(pm => { + const releaseContext = { + os: os.value, + installMethod: method.value, + platform: 'x64', + version: 'v16.0.0', + release: { status: 'LTS' }, + }; + const result = parseCompat([os, pm, method], releaseContext); + expect(Array.isArray(result)).toBe(true); + }); + }); + }); + }); + + it('should validate platform compatibility', () => { + OPERATING_SYSTEMS.forEach(os => { + const platforms = PLATFORMS[os.value] || []; + platforms.forEach(platform => { + const releaseContext = { + os: os.value, + installMethod: '', + platform: platform.value, + version: 'v16.0.0', + release: { status: 'LTS' }, + }; + const result = parseCompat([platform], releaseContext); + expect(result.length).toBe(1); + }); + }); + }); + + describe('extended tests', () => { + it('should disable items if OS is not supported', () => { + const items = [ + { + value: 'testItem', + compatibility: { os: ['MAC'] }, + }, + ]; + const result = parseCompat(items, { + os: 'WIN', + installMethod: '', + platform: '', + version: 'v16.0.0', + release: { status: 'LTS' }, + }); + expect(result[0].disabled).toBe(true); + }); + + it('should disable items if installMethod is not supported', () => { + const items = [ + { + value: 'testItem', + compatibility: { installMethod: ['NVM'] }, + }, + ]; + const result = parseCompat(items, { + os: 'MAC', + installMethod: 'FNM', + platform: '', + version: 'v16.0.0', + release: { status: 'Current' }, + }); + expect(result[0].disabled).toBe(true); + }); + + it('should disable items if platform is not supported', () => { + const items = [ + { + value: 'testItem', + compatibility: { platform: ['arm64'] }, + }, + ]; + const result = parseCompat(items, { + os: 'LINUX', + installMethod: '', + platform: 'x64', + version: 'v16.0.0', + release: { status: 'LTS' }, + }); + expect(result[0].disabled).toBe(true); + }); + + it('should disable items if semver constraint is not satisfied', () => { + const items = [ + { + value: 'testItem', + compatibility: { semver: ['>= 16.9.0'] }, + }, + ]; + const result = parseCompat(items, { + os: 'MAC', + installMethod: '', + platform: 'x64', + version: 'v16.0.0', + release: { status: 'LTS' }, + }); + expect(result[0].disabled).toBe(true); + }); + + it('should disable items if release status is not supported', () => { + const items = [ + { + value: 'testItem', + compatibility: { releases: ['Current'] }, + }, + ]; + const result = parseCompat(items, { + os: 'WIN', + installMethod: '', + platform: 'x64', + version: 'v18.0.0', + release: { status: 'LTS' }, + }); + expect(result[0].disabled).toBe(true); + }); + }); +}); + +describe('nextItem', () => { + it('should find the first valid item if current is invalid', () => { + const items = [ + { value: 'invalid', disabled: true }, + { value: 'valid', disabled: false }, + ]; + expect(nextItem('invalid', items)).toBe('valid'); + expect(nextItem('valid', items)).toBe('valid'); + }); +}); diff --git a/apps/site/util/__tests__/getHighEntropyValues.test.mjs b/apps/site/util/__tests__/getHighEntropyValues.test.mjs new file mode 100644 index 0000000000000..c1f3345d04dd8 --- /dev/null +++ b/apps/site/util/__tests__/getHighEntropyValues.test.mjs @@ -0,0 +1,42 @@ +import { getHighEntropyValues } from '@/util/getHighEntropyValues'; + +describe('getHighEntropyValues', () => { + beforeEach(() => { + Object.defineProperty(global, 'navigator', { + value: { + userAgentData: { + getHighEntropyValues: jest + .fn() + .mockResolvedValue({ platform: 'Win32', architecture: 'x86' }), + }, + }, + configurable: true, + }); + }); + + it('should resolve and return hint values', async () => { + const hints = ['platform']; + const result = await getHighEntropyValues(hints); + expect(result.platform).toBe('Win32'); + }); + + it('should return an empty object on rejection', async () => { + navigator.userAgentData.getHighEntropyValues.mockRejectedValue(null); + const hints = ['platform']; + const result = await getHighEntropyValues(hints); + expect(result.platform).toBeUndefined(); + }); + + it('should return multiple hint values', async () => { + const hints = ['platform', 'architecture']; + const result = await getHighEntropyValues(hints); + expect(result.platform).toBe('Win32'); + expect(result.architecture).toBe('x86'); + }); + + it('should return undefined for unsupported hints', async () => { + const hints = ['unsupportedHint']; + const result = await getHighEntropyValues(hints); + expect(result.unsupportedHint).toBeUndefined(); + }); +}); diff --git a/apps/site/util/__tests__/getLanguageDisplayName.test.mjs b/apps/site/util/__tests__/getLanguageDisplayName.test.mjs new file mode 100644 index 0000000000000..745d23f2c569f --- /dev/null +++ b/apps/site/util/__tests__/getLanguageDisplayName.test.mjs @@ -0,0 +1,25 @@ +import { getLanguageDisplayName } from '@/util/getLanguageDisplayName'; + +// mock usePathname, but retain all the other imports +jest.mock('@/shiki.config.mjs', () => ({ + LANGUAGES: [ + { name: 'javascript', aliases: ['js'], displayName: 'JavaScript' }, + { name: 'typescript', aliases: ['ts'], displayName: 'TypeScript' }, + ], +})); + +describe('getLanguageDisplayName', () => { + it('should return the display name for a known language', () => { + expect(getLanguageDisplayName('javascript')).toBe('JavaScript'); + expect(getLanguageDisplayName('js')).toBe('JavaScript'); + }); + + it('should return the display name for another known language', () => { + expect(getLanguageDisplayName('typescript')).toBe('TypeScript'); + expect(getLanguageDisplayName('ts')).toBe('TypeScript'); + }); + + it('should return the input language if it is not known', () => { + expect(getLanguageDisplayName('unknown')).toBe('unknown'); + }); +}); diff --git a/apps/site/util/__tests__/getNodeApiLink.test.mjs b/apps/site/util/__tests__/getNodeApiLink.test.mjs index 5317855c62aa7..2a23f9b90eb0e 100644 --- a/apps/site/util/__tests__/getNodeApiLink.test.mjs +++ b/apps/site/util/__tests__/getNodeApiLink.test.mjs @@ -1,7 +1,7 @@ import { getNodeApiLink } from '@/util/getNodeApiLink'; describe('getNodeApiLink', () => { - it('returns the correct API link for versions >=0.3.1 and <0.5.1', () => { + it('should return the correct API link for versions >=0.3.1 and <0.5.1', () => { const version = '0.4.0'; const expectedLink = `https://nodejs.org/docs/${version}/api/`; @@ -10,7 +10,12 @@ describe('getNodeApiLink', () => { expect(result).toBe(expectedLink); }); - it('returns the correct API link for versions >=0.1.14 and <0.3.1', () => { + it('should return the correct URL for versions >=0.3.1 and <0.5.1', () => { + const url = getNodeApiLink('v0.4.10'); + expect(url).toContain('/api/'); + }); + + it('should return the correct API link for versions >=0.1.14 and <0.3.1', () => { const version = '0.2.0'; const expectedLink = `https://nodejs.org/docs/${version}/api.html`; @@ -19,7 +24,7 @@ describe('getNodeApiLink', () => { expect(result).toBe(expectedLink); }); - it('returns the correct API link for versions >=1.0.0 and <4.0.0', () => { + it('should return the correct API link for versions >=1.0.0 and <4.0.0', () => { const version = '2.3.0'; const expectedLink = `https://iojs.org/dist/${version}/docs/api/`; @@ -28,7 +33,12 @@ describe('getNodeApiLink', () => { expect(result).toBe(expectedLink); }); - it('returns the correct API link for other versions', () => { + it('should form the correct URL for versions >=1.0.0 and <4.0.0', () => { + const url = getNodeApiLink('v1.2.3'); + expect(url).toContain('iojs.org/dist/v1.2.3/docs/api/'); + }); + + it('should return the correct API link for other versions', () => { const version = '5.0.0'; const expectedLink = `https://nodejs.org/dist/${version}/docs/api/`; diff --git a/apps/site/util/__tests__/getNodeDownloadUrl.test.mjs b/apps/site/util/__tests__/getNodeDownloadUrl.test.mjs index c95fa48e66126..da1c00f317b0f 100644 --- a/apps/site/util/__tests__/getNodeDownloadUrl.test.mjs +++ b/apps/site/util/__tests__/getNodeDownloadUrl.test.mjs @@ -3,7 +3,7 @@ import { getNodeDownloadUrl } from '@/util/getNodeDownloadUrl'; const version = 'v18.16.0'; describe('getNodeDownloadUrl', () => { - it('returns the correct download URL for Mac', () => { + it('should return the correct download URL for Mac', () => { const os = 'MAC'; const bitness = 86; const expectedUrl = 'https://nodejs.org/dist/v18.16.0/node-v18.16.0.pkg'; @@ -11,7 +11,7 @@ describe('getNodeDownloadUrl', () => { expect(getNodeDownloadUrl(version, os, bitness)).toBe(expectedUrl); }); - it('returns the correct download URL for Windows (32-bit)', () => { + it('should return the correct download URL for Windows (32-bit)', () => { const os = 'WIN'; const bitness = 86; const expectedUrl = @@ -20,7 +20,7 @@ describe('getNodeDownloadUrl', () => { expect(getNodeDownloadUrl(version, os, bitness)).toBe(expectedUrl); }); - it('returns the correct download URL for Windows (64-bit)', () => { + it('should return the correct download URL for Windows (64-bit)', () => { const os = 'WIN'; const bitness = 64; const expectedUrl = @@ -29,11 +29,25 @@ describe('getNodeDownloadUrl', () => { expect(getNodeDownloadUrl(version, os, bitness)).toBe(expectedUrl); }); - it('returns the default download URL for other operating systems', () => { + it('should return the default download URL for other operating systems', () => { const os = 'OTHER'; const bitness = 86; const expectedUrl = 'https://nodejs.org/dist/v18.16.0/node-v18.16.0.tar.gz'; expect(getNodeDownloadUrl(version, os, bitness)).toBe(expectedUrl); }); + + describe('MAC', () => { + it('should return .pkg link for installer', () => { + const url = getNodeDownloadUrl('v18.0.0', 'MAC', 'x64', 'installer'); + expect(url).toContain('.pkg'); + }); + }); + + describe('WIN', () => { + it('should return an MSI link for installer', () => { + const url = getNodeDownloadUrl('v18.0.0', 'WIN', 'x64', 'installer'); + expect(url).toContain('.msi'); + }); + }); }); diff --git a/apps/site/util/__tests__/getUserPlatform.test.mjs b/apps/site/util/__tests__/getUserPlatform.test.mjs new file mode 100644 index 0000000000000..5584ea836f717 --- /dev/null +++ b/apps/site/util/__tests__/getUserPlatform.test.mjs @@ -0,0 +1,15 @@ +import { getUserPlatform } from '@/util/getUserPlatform'; + +describe('getUserPlatform', () => { + it('should return arm64 for arm + 64', () => { + expect(getUserPlatform('arm', '64')).toBe('arm64'); + }); + + it('should return x64 for non-arm + 64', () => { + expect(getUserPlatform('amd64', '64')).toBe('x64'); + }); + + it('should return x86 otherwise', () => { + expect(getUserPlatform('amd64', '32')).toBe('x86'); + }); +}); diff --git a/apps/site/util/__tests__/gitHubUtils.test.mjs b/apps/site/util/__tests__/gitHubUtils.test.mjs index a749a403af062..6fc326df82570 100644 --- a/apps/site/util/__tests__/gitHubUtils.test.mjs +++ b/apps/site/util/__tests__/gitHubUtils.test.mjs @@ -5,7 +5,7 @@ import { getGitHubApiDocsUrl, } from '@/util/gitHubUtils'; -describe('GitHub utils', () => { +describe('gitHubUtils', () => { it('getGitHubAvatarUrl returns the correct URL', () => { expect(getGitHubAvatarUrl('octocat')).toBe( 'https://avatars.githubusercontent.com/octocat' @@ -30,4 +30,28 @@ describe('GitHub utils', () => { 'https://api.github.com/repos/nodejs/node/contents/doc/api?ref=assert'; expect(result).toBe(expected); }); + + describe('getGitHubAvatarUrl', () => { + it('should return a valid GitHub avatar URL', () => { + expect(getGitHubAvatarUrl('octocat')).toBe( + 'https://avatars.githubusercontent.com/octocat' + ); + }); + }); + + describe('getGitHubBlobUrl', () => { + it('should return the correct blob URL', () => { + expect(getGitHubBlobUrl('testfile.md')).toContain( + 'blob/main/apps/site/pages/en/testfile.md' + ); + }); + }); + + describe('getGitHubApiDocsUrl', () => { + it('should return the correct API docs URL', () => { + expect(getGitHubApiDocsUrl('v18.x')).toBe( + 'https://api.github.com/repos/nodejs/node/contents/doc/api?ref=v18.x' + ); + }); + }); }); diff --git a/apps/site/util/__tests__/hexToRGBA.test.mjs b/apps/site/util/__tests__/hexToRGBA.test.mjs index 2e7ca9e00b3d2..b477784d388b7 100644 --- a/apps/site/util/__tests__/hexToRGBA.test.mjs +++ b/apps/site/util/__tests__/hexToRGBA.test.mjs @@ -14,4 +14,12 @@ describe('hexToRGBA', () => { expect(rgbaColor).toBe('rgba(255, 255, 255, 0.5)'); }); + + it('should convert hex to RGBA correctly', () => { + expect(hexToRGBA('#FFFFFF')).toBe('rgba(255, 255, 255, 0.9)'); + }); + + it('should handle custom alpha value', () => { + expect(hexToRGBA('#000000', 0.5)).toBe('rgba(0, 0, 0, 0.5)'); + }); }); diff --git a/apps/site/util/__tests__/imageUtils.test.mjs b/apps/site/util/__tests__/imageUtils.test.mjs index 3e2ec00245ca1..f52a7fb9bfb4e 100644 --- a/apps/site/util/__tests__/imageUtils.test.mjs +++ b/apps/site/util/__tests__/imageUtils.test.mjs @@ -41,3 +41,14 @@ describe('isSvgImage', () => { }); }); }); + +describe('imageUtils', () => { + describe('isSvgImage', () => { + it('should detect .svg extension properly', () => { + expect(isSvgImage('icon.svg')).toBe(true); + }); + it('should return false for non-svg source', () => { + expect(isSvgImage('logo.png')).toBe(false); + }); + }); +}); diff --git a/apps/site/util/__tests__/stringUtils.test.mjs b/apps/site/util/__tests__/stringUtils.test.mjs index 27e65f1b0355e..ad2f3ef59aaea 100644 --- a/apps/site/util/__tests__/stringUtils.test.mjs +++ b/apps/site/util/__tests__/stringUtils.test.mjs @@ -72,4 +72,43 @@ describe('String utils', () => { it('dashToCamelCase returns correct camelCase with numbers', () => { expect(dashToCamelCase('foo-123-bar')).toBe('foo123Bar'); }); + + test('getAcronymFromString returns an acronym for a simple string', () => { + const result = getAcronymFromString('Node JS'); + expect(result).toBe('NJ'); + }); + + test('parseRichTextIntoPlainText removes markdown syntax', () => { + const result = parseRichTextIntoPlainText( + 'Hello **world**!\n*italic text*' + ); + expect(result).toBe('Hello world!\nitalic text'); + }); + + test('dashToCamelCase converts dashed strings to camelCase', () => { + const result = dashToCamelCase('es-2015'); + expect(result).toBe('es2015'); + }); + + describe('stringUtils', () => { + describe('getAcronymFromString', () => { + it('should return correct acronym', () => { + const result = getAcronymFromString('Hello World'); + expect(result).toBe('HW'); + }); + }); + + describe('parseRichTextIntoPlainText', () => { + it('should remove markdown and HTML', () => { + const result = parseRichTextIntoPlainText('

Hello

*world*'); + expect(result).toBe('Hello world'); + }); + }); + + describe('dashToCamelCase', () => { + it('should convert dash-case to camelCase', () => { + expect(dashToCamelCase('es-2015-config')).toBe('es2015Config'); + }); + }); + }); }); From 1822483aee0c08f125a3cfd8449a86400f7ee5bb Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Sun, 29 Dec 2024 12:22:05 +0000 Subject: [PATCH 2/2] chore: text correction --- apps/site/components/Common/ActiveLink/__tests__/index.test.mjs | 1 - apps/site/util/__tests__/getLanguageDisplayName.test.mjs | 1 - 2 files changed, 2 deletions(-) diff --git a/apps/site/components/Common/ActiveLink/__tests__/index.test.mjs b/apps/site/components/Common/ActiveLink/__tests__/index.test.mjs index 8ba0755b294c6..df11e03aa6f3f 100644 --- a/apps/site/components/Common/ActiveLink/__tests__/index.test.mjs +++ b/apps/site/components/Common/ActiveLink/__tests__/index.test.mjs @@ -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(), diff --git a/apps/site/util/__tests__/getLanguageDisplayName.test.mjs b/apps/site/util/__tests__/getLanguageDisplayName.test.mjs index 745d23f2c569f..4af7fe416a71b 100644 --- a/apps/site/util/__tests__/getLanguageDisplayName.test.mjs +++ b/apps/site/util/__tests__/getLanguageDisplayName.test.mjs @@ -1,6 +1,5 @@ import { getLanguageDisplayName } from '@/util/getLanguageDisplayName'; -// mock usePathname, but retain all the other imports jest.mock('@/shiki.config.mjs', () => ({ LANGUAGES: [ { name: 'javascript', aliases: ['js'], displayName: 'JavaScript' },