UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

317 lines (256 loc) 8.96 kB
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' // Mock fs before importing the plugin vi.mock('node:fs', async () => { const actual = await vi.importActual('node:fs') return { ...actual, existsSync: vi.fn((path: string) => { return path.includes('test-image.jpg') || path.includes('hero.png') }), } }) describe('imageDataPlugin', () => { beforeEach(() => { vi.resetModules() }) afterEach(() => { vi.clearAllMocks() }) describe('resolveId', () => { it('should return null for non-imagedata imports', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } const result = await (plugin.resolveId as any)('./image.jpg', undefined) expect(result).toBeNull() }) it('should return null for imports with imagedata not as suffix', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } // ?imagedata in the middle should not match const result = await (plugin.resolveId as any)('./image?imagedata.jpg', undefined) expect(result).toBeNull() }) it('should resolve public dir paths starting with /', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } const result = await (plugin.resolveId as any)( '/test-image.jpg?imagedata', undefined ) expect(result).toBe('\0imagedata:/project/public/test-image.jpg') }) it('should resolve relative imports', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } const result = await (plugin.resolveId as any)( './test-image.jpg?imagedata', '/project/src/components/Hero.tsx' ) expect(result).toBe('\0imagedata:/project/src/components/test-image.jpg') }) it('should return null for non-existent files', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } const result = await (plugin.resolveId as any)( '/nonexistent.jpg?imagedata', undefined ) expect(result).toBeNull() }) }) describe('path traversal prevention', () => { it('should block path traversal from public dir', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } const result = await (plugin.resolveId as any)( '/../../../etc/passwd?imagedata', undefined ) expect(result).toBeNull() }) it('should block path traversal from relative imports', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } const result = await (plugin.resolveId as any)( '../../../../etc/passwd?imagedata', '/project/src/components/Hero.tsx' ) expect(result).toBeNull() }) it('should block path traversal from root', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } const result = await (plugin.resolveId as any)( '../../../etc/passwd?imagedata', undefined ) expect(result).toBeNull() }) it('should allow paths within project boundaries', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } // Going up and back down should still work if within bounds // From /project/src/components, ../.. goes to /project, then src/test-image.jpg const result = await (plugin.resolveId as any)( '../../src/test-image.jpg?imagedata', '/project/src/components/Hero.tsx' ) expect(result).toBe('\0imagedata:/project/src/test-image.jpg') }) }) describe('load', () => { it('should return null for non-virtual modules', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } const mockContext = { addWatchFile: vi.fn(), } const result = await (plugin.load as any).call(mockContext, './image.jpg') expect(result).toBeNull() }) it('should return fallback data when sharp fails', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } const mockContext = { addWatchFile: vi.fn(), } // This will fail because the file doesn't actually exist // But it should gracefully fallback const result = await (plugin.load as any).call( mockContext, '\0imagedata:/project/public/test-image.jpg' ) expect(result).toContain('export default') expect(result).toContain('"src":"/test-image.jpg"') expect(result).toContain('"width":') expect(result).toContain('"height":') expect(mockContext.addWatchFile).toHaveBeenCalledWith( '/project/public/test-image.jpg' ) }) }) describe('plugin metadata', () => { it('should have correct name', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() expect(plugin.name).toBe('one:imagedata') }) it('should enforce pre', async () => { const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() expect(plugin.enforce).toBe('pre') }) }) }) describe('imageDataPlugin output format', () => { it('should export a valid JSON object', async () => { vi.resetModules() const { imageDataPlugin } = await import('./imageDataPlugin') const plugin = imageDataPlugin() const mockConfig = { publicDir: '/project/public', root: '/project', } if (plugin.configResolved) { ;(plugin.configResolved as any)(mockConfig) } const mockContext = { addWatchFile: vi.fn(), } const result = await (plugin.load as any).call( mockContext, '\0imagedata:/project/public/test-image.jpg' ) // Extract JSON from the export const jsonMatch = result?.match(/export default (.+)$/) expect(jsonMatch).toBeTruthy() if (jsonMatch) { const parsed = JSON.parse(jsonMatch[1]) expect(parsed).toHaveProperty('src') expect(parsed).toHaveProperty('width') expect(parsed).toHaveProperty('height') expect(parsed).toHaveProperty('blurDataURL') expect(typeof parsed.src).toBe('string') expect(typeof parsed.width).toBe('number') expect(typeof parsed.height).toBe('number') expect(typeof parsed.blurDataURL).toBe('string') } }) })