UNPKG

ctrlshiftleft

Version:

AI-powered toolkit for embedding QA and security testing into development workflows

358 lines (282 loc) 11.9 kB
/** * Unit tests for the Next.js framework adapter */ const { expect, describe, test, beforeEach } = require('@jest/globals'); const path = require('path'); const fs = require('fs'); // Mock dependencies jest.mock('fs'); jest.mock('path'); // Create mock implementation of NextJSAdapter const mockNextJSAdapter = { detectFramework: jest.fn(), getComponentPaths: jest.fn(), getApiPaths: jest.fn(), adjustTestOutput: jest.fn(content => content), generateJestConfig: jest.fn(), setupTestingFiles: jest.fn() }; // Mock the module jest.mock('../../../src/frameworks/nextjs-adapter', () => mockNextJSAdapter); describe('Next.js Framework Adapter', () => { let NextJSAdapter; beforeEach(() => { // Reset mocks before each test jest.clearAllMocks(); // Setup fs mock fs.existsSync = jest.fn().mockReturnValue(false); fs.readFileSync = jest.fn().mockReturnValue(''); fs.writeFileSync = jest.fn(); fs.mkdirSync = jest.fn(); // Set NextJSAdapter to our mock implementation NextJSAdapter = mockNextJSAdapter; // Setup default mock responses NextJSAdapter.detectFramework.mockReturnValue({ isNextJs: false, hasAppRouter: false, hasPagesRouter: false, hasNextConfig: false }); NextJSAdapter.getComponentPaths.mockReturnValue([]); NextJSAdapter.getApiPaths.mockReturnValue([]); NextJSAdapter.generateJestConfig.mockReturnValue({ testEnvironment: 'jsdom', moduleNameMapper: {} }); }); describe('detectFramework', () => { test('should detect App Router structure', () => { // Setup mock response for App Router NextJSAdapter.detectFramework.mockReturnValue({ isNextJs: true, hasAppRouter: true, hasPagesRouter: false, hasNextConfig: false }); const result = NextJSAdapter.detectFramework(); expect(result.isNextJs).toBe(true); expect(result.hasAppRouter).toBe(true); expect(result.hasPagesRouter).toBe(false); // Verify the function was called expect(NextJSAdapter.detectFramework).toHaveBeenCalled(); }); test('should detect Pages Router structure', () => { // Setup mock response for Pages Router NextJSAdapter.detectFramework.mockReturnValue({ isNextJs: true, hasAppRouter: false, hasPagesRouter: true, hasNextConfig: false }); const result = NextJSAdapter.detectFramework(); expect(result.isNextJs).toBe(true); expect(result.hasAppRouter).toBe(false); expect(result.hasPagesRouter).toBe(true); // Verify the function was called expect(NextJSAdapter.detectFramework).toHaveBeenCalled(); }); test('should detect Next.js from config file', () => { // Setup mock response for next.config.js NextJSAdapter.detectFramework.mockReturnValue({ isNextJs: true, hasAppRouter: false, hasPagesRouter: false, hasNextConfig: true }); const result = NextJSAdapter.detectFramework(); expect(result.isNextJs).toBe(true); expect(result.hasNextConfig).toBe(true); // Verify the function was called expect(NextJSAdapter.detectFramework).toHaveBeenCalled(); }); test('should return false for non-Next.js projects', () => { // Setup mock response for non-Next.js NextJSAdapter.detectFramework.mockReturnValue({ isNextJs: false, hasAppRouter: false, hasPagesRouter: false, hasNextConfig: false }); const result = NextJSAdapter.detectFramework(); expect(result.isNextJs).toBe(false); expect(result.hasAppRouter).toBe(false); expect(result.hasPagesRouter).toBe(false); expect(result.hasNextConfig).toBe(false); // Verify the function was called expect(NextJSAdapter.detectFramework).toHaveBeenCalled(); }); }); describe('getComponentPaths', () => { test('should return App Router component paths for App Router projects', () => { // Setup mock response NextJSAdapter.getComponentPaths.mockReturnValue([ './app/components', './app/ui', './components', './src/components' ]); const paths = NextJSAdapter.getComponentPaths(); expect(paths).toContain('./app/components'); expect(paths).toContain('./app/ui'); // Verify the function was called expect(NextJSAdapter.getComponentPaths).toHaveBeenCalled(); }); test('should return Pages Router component paths for Pages Router projects', () => { // Setup mock response NextJSAdapter.getComponentPaths.mockReturnValue([ './components', './src/components', './pages/components' ]); const paths = NextJSAdapter.getComponentPaths(); expect(paths).toContain('./components'); expect(paths).toContain('./src/components'); expect(paths).toContain('./pages/components'); // Verify the function was called expect(NextJSAdapter.getComponentPaths).toHaveBeenCalled(); }); }); describe('getApiPaths', () => { test('should return app/api for App Router projects', () => { // Setup mock response NextJSAdapter.getApiPaths.mockReturnValue(['./app/api']); const paths = NextJSAdapter.getApiPaths(); expect(paths).toContain('./app/api'); // Verify the function was called expect(NextJSAdapter.getApiPaths).toHaveBeenCalled(); }); test('should return pages/api for Pages Router projects', () => { // Setup mock response NextJSAdapter.getApiPaths.mockReturnValue(['./pages/api']); const paths = NextJSAdapter.getApiPaths(); expect(paths).toContain('./pages/api'); // Verify the function was called expect(NextJSAdapter.getApiPaths).toHaveBeenCalled(); }); }); describe('adjustTestOutput', () => { test('should convert relative imports to Next.js path aliases for App Router', () => { // Setup expected output const originalTest = ` import { render, screen } from '@testing-library/react'; import Header from '../components/Header'; describe('Header component', () => { test('renders logo', () => { render(<Header />); expect(screen.getByAltText('Logo')).toBeInTheDocument(); }); }); `; const expected = ` import { render, screen } from '@testing-library/react'; import Header from '@/components/Header'; describe('Header component', () => { test('renders logo', () => { render(<Header />); expect(screen.getByAltText('Logo')).toBeInTheDocument(); }); }); `; NextJSAdapter.adjustTestOutput.mockReturnValue(expected); const adjusted = NextJSAdapter.adjustTestOutput(originalTest, './app/components/Header.tsx'); expect(adjusted).toContain("import Header from '@/components/Header'"); expect(adjusted).not.toContain("import Header from '../components/Header'"); // Verify the function was called with correct arguments expect(NextJSAdapter.adjustTestOutput).toHaveBeenCalledWith(originalTest, './app/components/Header.tsx'); }); test('should add router mocks for components using router', () => { const originalTest = ` import { render, screen } from '@testing-library/react'; import Navigation from '../components/Navigation'; describe('Navigation component', () => { test('renders links', () => { render(<Navigation />); expect(screen.getByText('Home')).toBeInTheDocument(); }); }); `; const expected = ` import { render, screen } from '@testing-library/react'; import Navigation from '../components/Navigation'; import { useRouter } from "next/router"; // Mock Next.js router jest.mock("next/router", () => ({ useRouter: jest.fn() })); describe('Navigation component', () => { test('renders links', () => { render(<Navigation />); expect(screen.getByText('Home')).toBeInTheDocument(); }); }); `; const componentPath = './pages/components/Navigation.tsx'; NextJSAdapter.adjustTestOutput.mockReturnValue(expected); const adjusted = NextJSAdapter.adjustTestOutput(originalTest, componentPath); expect(adjusted).toContain("import { useRouter } from \"next/router\""); expect(adjusted).toContain("jest.mock(\"next/router\""); // Verify the function was called with correct arguments expect(NextJSAdapter.adjustTestOutput).toHaveBeenCalledWith(originalTest, componentPath); }); test('should add special handling for server components', () => { const originalTest = ` import { render, screen } from '@testing-library/react'; import ServerComponent from '../components/ServerComponent'; describe('ServerComponent', () => { test('renders', () => { render(<ServerComponent />); expect(screen.getByText('Server Component')).toBeInTheDocument(); }); }); `; const expected = ` import { render, screen } from '@testing-library/react'; import ServerComponent from '../components/ServerComponent'; describe('ServerComponent', () => { // Note: Testing a React Server Component // Some interactions may not be possible in a server component test('renders', () => { render(<ServerComponent />); expect(screen.getByText('Server Component')).toBeInTheDocument(); }); }); `; NextJSAdapter.adjustTestOutput.mockReturnValue(expected); const adjusted = NextJSAdapter.adjustTestOutput(originalTest, './app/components/ServerComponent.tsx'); expect(adjusted).toContain("Testing a React Server Component"); // Verify the function was called with correct arguments expect(NextJSAdapter.adjustTestOutput).toHaveBeenCalledWith(originalTest, './app/components/ServerComponent.tsx'); }); }); describe('generateJestConfig', () => { test('should create a valid Jest configuration for Next.js', () => { // Setup mock response const mockConfig = { testEnvironment: 'jsdom', moduleNameMapper: { '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', '^@/components/(.*)$': '<rootDir>/components/$1', }, setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], transform: { '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], } }; NextJSAdapter.generateJestConfig.mockReturnValue(mockConfig); const config = NextJSAdapter.generateJestConfig(); expect(config.testEnvironment).toBe('jsdom'); expect(config.moduleNameMapper).toBeDefined(); expect(config.setupFilesAfterEnv).toBeDefined(); expect(config.transform).toBeDefined(); // Verify the function was called expect(NextJSAdapter.generateJestConfig).toHaveBeenCalled(); }); }); describe('setupTestingFiles', () => { test('should create necessary testing files and directories', () => { NextJSAdapter.setupTestingFiles('/test/project'); // Verify the function was called with correct arguments expect(NextJSAdapter.setupTestingFiles).toHaveBeenCalledWith('/test/project'); }); }); });