UNPKG

fastlane

Version:

Fastlane is a fast and flexible API framework for Node.js. It automatically creates Express routes from your project's file structure, making it easy to build APIs quickly and efficiently.

224 lines 9.25 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const attach_1 = require("../attach"); const handler = __importStar(require("../handler")); // Create a spy for Router const mockRouterFn = jest.fn(); const mockRouter = { get: jest.fn(), post: jest.fn(), put: jest.fn(), patch: jest.fn(), delete: jest.fn() }; // Mock fs with a properly implemented readdir function jest.mock('fs', () => { return { promises: { readdir: jest.fn().mockImplementation(() => Promise.resolve([])) } }; }); // Get a reference to the mocked function for use in tests const fsPromises = jest.requireMock('fs').promises; // Mock dependencies jest.mock('express', () => { return { Router: () => { mockRouterFn(); return mockRouter; } }; }); // Mock the methodHandler to properly wrap the handler functions jest.mock('../handler', () => ({ methodHandler: jest.fn().mockImplementation((fn) => { // Return a function that can be used as a route handler const mockHandler = function () { if (fn) return fn(); return undefined; }; return mockHandler; }) })); describe('attachRoutes', () => { let mockMethodHandler; let consoleSpy; beforeEach(() => { jest.resetAllMocks(); mockMethodHandler = handler.methodHandler; consoleSpy = jest.spyOn(console, 'log').mockImplementation(); }); afterEach(() => { consoleSpy.mockRestore(); }); it('should return a router instance', async () => { // Set up the mock to resolve immediately fsPromises.readdir.mockResolvedValue([]); const router = (0, attach_1.attachRoutes)('./routes'); // Wait for any promises to resolve await new Promise(process.nextTick); expect(mockRouterFn).toHaveBeenCalled(); expect(router).toBeDefined(); }); it('should scan the directory for route files', async () => { fsPromises.readdir.mockResolvedValue([]); (0, attach_1.attachRoutes)('./routes'); // Wait for promises to resolve await new Promise(process.nextTick); expect(fsPromises.readdir).toHaveBeenCalledWith('./routes', { recursive: true }); }); it('should attach route handlers for matching files', async () => { // Mock file system with route files fsPromises.readdir.mockResolvedValue(['users/route.ts', 'products/route.js']); // Create mock handler functions const getUserHandler = jest.fn(); const postUserHandler = jest.fn(); const putProductHandler = jest.fn(); const deleteProductHandler = jest.fn(); // Mock user routes const mockUserRoutes = { GET: getUserHandler, POST: postUserHandler }; // Mock product routes const mockProductRoutes = { default: { PUT: putProductHandler, DELETE: deleteProductHandler } }; // Mock the methodHandler to return the wrapped handler mockMethodHandler.mockImplementation((fn) => { return fn; }); // Mock the dynamic import const importFn = jest.fn().mockImplementation((path) => { if (path.includes('users/route.ts')) return Promise.resolve(mockUserRoutes); if (path.includes('products/route.js')) return Promise.resolve(mockProductRoutes); return Promise.resolve({}); }); (0, attach_1.attachRoutes)('./routes', importFn); // Wait for promises to resolve await new Promise(process.nextTick); // Verify routes were attached expect(mockRouter.get).toHaveBeenCalledWith('/users', expect.any(Function)); expect(mockRouter.post).toHaveBeenCalledWith('/users', expect.any(Function)); expect(mockRouter.put).toHaveBeenCalledWith('/products', expect.any(Function)); expect(mockRouter.delete).toHaveBeenCalledWith('/products', expect.any(Function)); // Verify console logs expect(consoleSpy).toHaveBeenCalledWith('Attaching GET /users'); expect(consoleSpy).toHaveBeenCalledWith('Attaching POST /users'); expect(consoleSpy).toHaveBeenCalledWith('Attaching PUT /products'); expect(consoleSpy).toHaveBeenCalledWith('Attaching DELETE /products'); }); it('should handle files that do not match the route pattern', async () => { fsPromises.readdir.mockResolvedValue(['users/index.ts', 'products/route.ts']); const mockProductRoutes = { GET: jest.fn() }; // Mock the dynamic import const importFn = jest.fn((path) => { if (path.includes('products/route.ts')) return Promise.resolve(mockProductRoutes); return Promise.resolve({}); }); // Don't override the methodHandler implementation mockMethodHandler.mockImplementation((fn) => fn); (0, attach_1.attachRoutes)('./routes', importFn); // Wait for promises to resolve await new Promise(process.nextTick); // Only the route file should be processed expect(mockRouter.get).toHaveBeenCalledWith('/products', expect.any(Function)); expect(mockRouter.get).toHaveBeenCalledTimes(1); }); it('should correctly parse the URL path from the file path', async () => { fsPromises.readdir.mockResolvedValue(['users/admin/route.ts', 'api/v1/products/route.js']); const mockUserAdminRoutes = { GET: jest.fn() }; const mockProductsV1Routes = { POST: jest.fn() }; // Mock the dynamic import const importFn = jest.fn((path) => { if (path.includes('users/admin/route.ts')) return Promise.resolve(mockUserAdminRoutes); if (path.includes('api/v1/products/route.js')) return Promise.resolve(mockProductsV1Routes); return Promise.resolve({}); }); // Don't override the methodHandler implementation mockMethodHandler.mockImplementation((fn) => fn); (0, attach_1.attachRoutes)('./routes', importFn); // Wait for promises to resolve await new Promise(process.nextTick); // Verify the paths are correctly parsed expect(mockRouter.get).toHaveBeenCalledWith('/users/admin', expect.any(Function)); expect(mockRouter.post).toHaveBeenCalledWith('/api/v1/products', expect.any(Function)); }); it('should handle both default and direct exports', async () => { fsPromises.readdir.mockResolvedValue(['direct-export/route.ts', 'default-export/route.ts']); const mockDirectExport = { GET: jest.fn() }; const mockDefaultExport = { default: { POST: jest.fn() } }; // Mock the dynamic import const importFn = jest.fn((path) => { if (path.includes('direct-export/route.ts')) return Promise.resolve(mockDirectExport); if (path.includes('default-export/route.ts')) return Promise.resolve(mockDefaultExport); return Promise.resolve({}); }); // Don't override the methodHandler implementation mockMethodHandler.mockImplementation((fn) => fn); (0, attach_1.attachRoutes)('./routes', importFn); // Wait for promises to resolve await new Promise(process.nextTick); // Verify both export styles work expect(mockRouter.get).toHaveBeenCalledWith('/direct-export', expect.any(Function)); expect(mockRouter.post).toHaveBeenCalledWith('/default-export', expect.any(Function)); }); }); //# sourceMappingURL=attach.test.js.map