diff --git a/packages/devtools-vite/src/app/utils/cache.test.ts b/packages/devtools-vite/src/app/utils/__tests__/cache.test.ts similarity index 98% rename from packages/devtools-vite/src/app/utils/cache.test.ts rename to packages/devtools-vite/src/app/utils/__tests__/cache.test.ts index ab75df1c..6092c376 100644 --- a/packages/devtools-vite/src/app/utils/cache.test.ts +++ b/packages/devtools-vite/src/app/utils/__tests__/cache.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { FixedTupleMap, MaybeWeakMap, TupleMap } from './cache' +import { FixedTupleMap, MaybeWeakMap, TupleMap } from '../cache' describe('maybeWeakMap', () => { it('should work', () => { diff --git a/packages/devtools-vite/src/app/utils/__tests__/color.test.ts b/packages/devtools-vite/src/app/utils/__tests__/color.test.ts new file mode 100644 index 00000000..d1327c02 --- /dev/null +++ b/packages/devtools-vite/src/app/utils/__tests__/color.test.ts @@ -0,0 +1,53 @@ +import { beforeEach, describe, expect, it } from 'vitest' +import { isDark } from '../../composables/dark' +import { getHashColorFromString, getHsla, getPluginColor, predefinedColorMap } from '../color' + +describe('getHashColorFromString', () => { + it('should get the same color with the same string', () => { + expect(getHashColorFromString('Vite')).toBe(getHashColorFromString('Vite')) + }) + it('should get different colors with different strings', () => { + expect(getHashColorFromString('Vite')).not.toBe(getHashColorFromString('Devtools')) + }) +}) + +describe('getHsla', () => { + beforeEach(() => { + isDark.value = false + }) + + it('light mode with default opacity', () => { + expect(getHsla(180)).toBe('hsla(180, 65%, 40%, 1)') + }) + + it('light mode with custom opacity', () => { + expect(getHsla(180, 0.5)).toBe('hsla(180, 65%, 40%, 0.5)') + }) + + it('dark mode with default opacity', () => { + isDark.value = true + expect(getHsla(180)).toBe('hsla(180, 50%, 60%, 1)') + }) + + it('dark mode with custom opacity', () => { + isDark.value = true + expect(getHsla(180, 0.8)).toBe('hsla(180, 50%, 60%, 0.8)') + }) +}) + +describe('getPluginColor', () => { + it('should use predefinedColorMap with known name', () => { + for (const name in predefinedColorMap) { + if (Object.prototype.hasOwnProperty.call(predefinedColorMap, name) + && name === name.replace(/[^a-z]+/gi, '').toLowerCase()) { + if (typeof predefinedColorMap[name] === 'number') { + expect(getPluginColor(`8080-=(🤔)${name}`)).toBe(getHsla(predefinedColorMap[name])) + } + } + } + }) + + it('should use getHashColorFromString with unknown name', () => { + expect(getPluginColor('😄Foo')).toBe(getHashColorFromString('foo')) + }) +}) diff --git a/packages/devtools-vite/src/app/utils/__tests__/filepath.test.ts b/packages/devtools-vite/src/app/utils/__tests__/filepath.test.ts new file mode 100644 index 00000000..12a084d9 --- /dev/null +++ b/packages/devtools-vite/src/app/utils/__tests__/filepath.test.ts @@ -0,0 +1,83 @@ +import { describe, expect, it } from 'vitest' +import { getModuleNameFromPath, isBuiltInModule, isNodeModulePath, parseReadablePath } from '../filepath' + +describe('isNodeModulePath', () => { + it('should return true if includes node_modules path', () => { + expect(isNodeModulePath('/foo/node_modules/bar')).toBeTruthy() + expect(isNodeModulePath('C:\\foo\\node_modules\\bar')).toBeTruthy() + }) + + it('should return true if package names', () => { + expect(isNodeModulePath('vite')).toBeTruthy() + expect(isNodeModulePath('@vitejs/devtools')).toBeTruthy() + expect(isNodeModulePath('#import')).toBeTruthy() + }) + + it('should return false if not node_modules', () => { + expect(isNodeModulePath('/foo/bar')).toBeFalsy() + }) +}) + +describe('getModuleNameFromPath', () => { + it('should return package name', () => { + expect(getModuleNameFromPath('vite')).toBe('vite') + expect(getModuleNameFromPath('@vite/devtools')).toBe('@vite/devtools') + }) + + it('should return undefined with a non-module name', () => { + expect(getModuleNameFromPath('/foo/bar')).toBeUndefined() + expect(getModuleNameFromPath('C:\\foo\\bar')).toBeUndefined() + }) + + it('should return scope package name', () => { + expect(getModuleNameFromPath('/foo/bar/node_modules/@vitejs/devtools')).toBe('@vitejs/devtools') + expect(getModuleNameFromPath('C:\\foo\\node_modules\\@vitejs\\devtools')).toBe('@vitejs/devtools') + }) + + it('should return normal package name', () => { + expect(getModuleNameFromPath('/foo/bar/node_modules/vite')).toBe('vite') + expect(getModuleNameFromPath('C:\\foo\\node_modules\\@vitejs\\devtools')).toBe('@vitejs/devtools') + }) +}) + +describe('isBuiltInModule', () => { + it('should return undefined with undefined name', () => { + expect(isBuiltInModule(undefined)).toBeUndefined() + }) + + it('should return true with a built-in module name', () => { + expect(isBuiltInModule('nuxt')).toBeTruthy() + }) + + it('should return false with a not built-in module name', () => { + expect(isBuiltInModule('foo')).toBeFalsy() + }) +}) + +describe('parseReadablePath', () => { + it('should return path with package names', () => { + expect(parseReadablePath('vite', '/')).toEqual({ moduleName: 'vite', path: 'vite' }) + expect(parseReadablePath('@vitejs/devtools', '/')).toEqual({ moduleName: '@vitejs/devtools', path: '@vitejs/devtools' }) + expect(parseReadablePath('@vitejs\\devtools', '/')).toEqual({ moduleName: '@vitejs/devtools', path: '@vitejs/devtools' }) + expect(parseReadablePath('@vitejs%2Fdevtools', '/')).toEqual({ moduleName: '@vitejs/devtools', path: '@vitejs/devtools' }) + }) + + it('should return path with : unless Windows path', () => { + expect(parseReadablePath('nuxt:index.mjs', '/')).toEqual({ moduleName: 'nuxt:index.mjs', path: 'nuxt:index.mjs' }) + }) + + it('should return moduleName and subpath', () => { + expect(parseReadablePath('/foo/node_modules/vite/dist/index.mjs', '/')).toEqual({ moduleName: 'vite', path: 'vite/dist/index.mjs' }) + expect(parseReadablePath('C:\\foo\\node_modules\\vite\\dist\\index.mjs', 'C:\\')).toEqual({ moduleName: 'vite', path: 'vite/dist/index.mjs' }) + }) + + it('should add ./ for no ./ items', () => { + expect(parseReadablePath('/foo/index.mjs', '/foo')).toEqual({ path: './index.mjs' }) + expect(parseReadablePath('C:\\foo\\index.mjs', 'C:\\foo')).toEqual({ path: './index.mjs' }) + }) + + it('should add replace ./.nuxt to #build for .nuxt items', () => { + expect(parseReadablePath('/foo/.nuxt/index.mjs', '/foo')).toEqual({ path: '#build/index.mjs' }) + expect(parseReadablePath('C:\\foo\\.nuxt\\index.mjs', 'C:\\foo')).toEqual({ path: '#build/index.mjs' }) + }) +}) diff --git a/packages/devtools-vite/src/app/utils/__tests__/format.test.ts b/packages/devtools-vite/src/app/utils/__tests__/format.test.ts new file mode 100644 index 00000000..61e1dc91 --- /dev/null +++ b/packages/devtools-vite/src/app/utils/__tests__/format.test.ts @@ -0,0 +1,98 @@ +import type { ModuleDest, ModuleTreeNode } from '../../../shared/types/data' +import { describe, expect, it } from 'vitest' +import { bytesToHumanSize, getContentByteSize, toTree } from '../format' + +describe('bytesToHumanSize', () => { + it('should return raw bytes (<1024)', () => { + expect(bytesToHumanSize(10)).toEqual([10, 'B']) + }) + + it('should return kb with proper digits', () => { + expect(bytesToHumanSize(1024)).toEqual(['1', 'KB']) + expect(bytesToHumanSize(1024 * 1.5)).toEqual(['1.5', 'KB']) + expect(bytesToHumanSize(1024 * 1.666, 1)).toEqual(['1.7', 'KB']) + }) + + it('should return mb with proper digits', () => { + expect(bytesToHumanSize(1024 * 1024)).toEqual(['1', 'MB']) + expect(bytesToHumanSize(1024 * 1024 * 1.5)).toEqual(['1.5', 'MB']) + expect(bytesToHumanSize(1024 * 1024 * 1.666, 1)).toEqual(['1.7', 'MB']) + }) + + // larger... +}) + +describe('getContentByteSize', () => { + it('should return 0 with empty string', () => { + expect(getContentByteSize('')).toBe(0) + }) + + it('should return bytes', () => { + expect(getContentByteSize('vite')).toBe(4) + }) +}) + +describe('toTree', () => { + it('should work with empty modules', () => { + expect(toTree([], 'Root')).toEqual({ + name: 'Root', + children: {}, + items: [], + }) + }) + + it('should work', () => { + const modules: ModuleDest[] = [ + { + full: '/path/to/project/dist/src/components/Button.js', + path: 'src/components/Button.js', + }, + { + full: '/path/to/project/dist/src/utils/helper.js', + path: 'src/utils/helper.js', + }, + { + full: '/path/to/project/dist/index.js', + path: 'index.js', + }, + ] + + expect(toTree(modules, 'Root')).toEqual({ + name: 'Root', + children: { + src: { + name: 'src', + children: { + components: { + name: 'components', + children: {}, + items: [ + { + full: '/path/to/project/dist/src/components/Button.js', + path: 'src/components/Button.js', + }, + ], + }, + utils: { + name: 'utils', + children: {}, + items: [ + { + full: '/path/to/project/dist/src/utils/helper.js', + path: 'src/utils/helper.js', + }, + ], + }, + }, + items: [], + }, + }, + items: [ + { + full: '/path/to/project/dist/index.js', + path: 'index.js', + }, + ], + } satisfies ModuleTreeNode) + }) +}) diff --git a/packages/devtools-vite/src/app/utils/__tests__/icon.test.ts b/packages/devtools-vite/src/app/utils/__tests__/icon.test.ts new file mode 100644 index 00000000..6783ed58 --- /dev/null +++ b/packages/devtools-vite/src/app/utils/__tests__/icon.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from 'vitest' +import { DefaultFileTypeRule, DefaultPluginType, getFileTypeFromModuleId, getFileTypeFromName, getPluginTypeFromName } from '../icon' + +describe('getFileTypeFromName', () => { + it('should return correct rule by name', () => { + expect(getFileTypeFromName('vue').name).toBe('vue') + expect(getFileTypeFromName('ts').name).toBe('ts') + }) + + it('should return default rule for unknown names', () => { + expect(getFileTypeFromName('unknown')).toBe(DefaultFileTypeRule) + }) +}) + +describe('getFileTypeFromModuleId', () => { + it('should match node_modules', () => { + expect(getFileTypeFromModuleId('/node_modules/vue/dist/vue.js').name).toBe('node_modules') + expect(getFileTypeFromModuleId('C:\\node_modules\\react\\index.js').name).toBe('node_modules') + }) + + it('should match virtual modules', () => { + expect(getFileTypeFromModuleId('virtual:my-module').name).toBe('virtual') + expect(getFileTypeFromModuleId('\0rollup-plugin').name).toBe('virtual') + }) + + it('should match file extensions', () => { + expect(getFileTypeFromModuleId('/src/App.vue').name).toBe('vue') + expect(getFileTypeFromModuleId('/src/index.ts').name).toBe('ts') + expect(getFileTypeFromModuleId('/src/index.tsx').name).toBe('jsx') + expect(getFileTypeFromModuleId('/src/index.js').name).toBe('js') + expect(getFileTypeFromModuleId('/src/style.css').name).toBe('css') + }) + + it('should handle query parameters', () => { + expect(getFileTypeFromModuleId('/src/App.vue?v=123').name).toBe('vue') + expect(getFileTypeFromModuleId('/src/App.vue?').name).toBe('vue') + }) + + it('should match packages', () => { + expect(getFileTypeFromModuleId('vite').name).toBe('package') + expect(getFileTypeFromModuleId('@vitejs/devtools').name).toBe('package') + }) + + it('should return default for unknown files', () => { + expect(getFileTypeFromModuleId('/unknown.xyz')).toBe(DefaultFileTypeRule) + }) +}) + +describe('getPluginTypeFromName', () => { + it('should match plugin types', () => { + expect(getPluginTypeFromName('replace').name).toBe('rollup') + expect(getPluginTypeFromName('vite:css').name).toBe('vite') + expect(getPluginTypeFromName('unocss:core').name).toBe('unocss') + expect(getPluginTypeFromName('nuxt:pages').name).toBe('nuxt') + expect(getPluginTypeFromName('builtin:fs').name).toBe('builtin') + }) + + it('should return default for unknown plugins', () => { + expect(getPluginTypeFromName('custom-plugin')).toBe(DefaultPluginType) + }) +}) diff --git a/packages/devtools-vite/src/app/utils/__tests__/is.test.ts b/packages/devtools-vite/src/app/utils/__tests__/is.test.ts new file mode 100644 index 00000000..10089d72 --- /dev/null +++ b/packages/devtools-vite/src/app/utils/__tests__/is.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from 'vitest' +import { isNumeric } from '../is' + +describe('isNumeric', () => { + it('should return true for numbers', () => { + expect(isNumeric(1)).toBeTruthy() + expect(isNumeric(0)).toBeTruthy() + expect(isNumeric(-1)).toBeTruthy() + }) + + it('should return false for non-numeric strings', () => { + expect(isNumeric('Vite')).toBeFalsy() + expect(isNumeric('123abc')).toBeFalsy() + }) + + it('should return true for numeric strings', () => { + expect(isNumeric('123')).toBeTruthy() + }) + + it('should return false for NaN/Infinity', () => { + expect(isNumeric(Number.NaN)).toBeTruthy() + expect(isNumeric(Infinity)).toBeTruthy() + }) +}) diff --git a/packages/devtools-vite/src/app/utils/color.ts b/packages/devtools-vite/src/app/utils/color.ts index 5d774434..cf54de75 100644 --- a/packages/devtools-vite/src/app/utils/color.ts +++ b/packages/devtools-vite/src/app/utils/color.ts @@ -35,7 +35,7 @@ export function getHsla( * - 240: blue * - 270: purple */ -const predefinedColorMap = { +export const predefinedColorMap = { error: 0, client: 60, bailout: -1,