@ordojs/core
Version:
Core compiler and runtime for OrdoJS framework
316 lines (279 loc) • 10.3 kB
text/typescript
/**
* @fileoverview Tests for FileSystemRouter
*/
import { mkdir, rm } from 'fs/promises';
import path from 'path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { FileSystemRouter } from './fs-router.js';
// Mock the OrdoJSLexer and OrdoJSParser
vi.mock('./lexer.js', () => ({
OrdoJSLexer: class MockLexer {
tokenize() {
return [];
}
}
}));
vi.mock('./parser.js', () => ({
OrdoJSParser: class MockParser {
constructor() {}
parse() {
return {
component: {
name: 'TestComponent',
serverBlock: {
functions: []
}
}
};
}
}
}));
describe('FileSystemRouter', () => {
const testRoutesDir = 'test-routes';
let router: FileSystemRouter;
beforeEach(async () => {
// Create test routes directory
await mkdir(testRoutesDir, { recursive: true });
router = new FileSystemRouter({
routesDir: testRoutesDir,
extensions: ['.ordo'],
generateCatchAll: true,
baseUrl: '/'
});
});
afterEach(async () => {
// Clean up test directory
try {
await rm(testRoutesDir, { recursive: true, force: true });
} catch (error) {
// Ignore cleanup errors
}
});
describe('filePathToRoutePath', () => {
it('should convert index files to root paths', () => {
const testCases = [
{ input: 'index.ordo', expected: '/' },
{ input: 'about/index.ordo', expected: '/about' },
{ input: 'blog/posts/index.ordo', expected: '/blog/posts' }
];
for (const { input, expected } of testCases) {
const filePath = path.join(testRoutesDir, input);
const routePath = (router as any).filePathToRoutePath(filePath);
expect(routePath).toBe(expected);
}
});
it('should convert dynamic routes correctly', () => {
const testCases = [
{ input: 'users/[id].ordo', expected: '/users/:id' },
{ input: 'blog/[slug].ordo', expected: '/blog/:slug' },
{ input: 'posts/[category]/[id].ordo', expected: '/posts/:category/:id' }
];
for (const { input, expected } of testCases) {
const filePath = path.join(testRoutesDir, input);
const routePath = (router as any).filePathToRoutePath(filePath);
expect(routePath).toBe(expected);
}
});
it('should convert catch-all routes correctly', () => {
const testCases = [
{ input: 'docs/[...slug].ordo', expected: '/docs/*slug' },
{ input: '[...path].ordo', expected: '/*path' }
];
for (const { input, expected } of testCases) {
const filePath = path.join(testRoutesDir, input);
const routePath = (router as any).filePathToRoutePath(filePath);
expect(routePath).toBe(expected);
}
});
});
describe('extractRouteParams', () => {
it('should extract dynamic parameters', () => {
const testCases = [
{ path: '/users/:id', expected: ['id'] },
{ path: '/posts/:category/:id', expected: ['category', 'id'] },
{ path: '/docs/*slug', expected: ['slug'] },
{ path: '/static', expected: [] }
];
for (const { path, expected } of testCases) {
const params = (router as any).extractRouteParams(path);
expect(params).toEqual(expected);
}
});
});
describe('calculateRoutePriority', () => {
it('should assign correct priorities', () => {
const testCases = [
{ path: '/about', isDynamic: false, isCatchAll: false, expectedPriority: 2 }, // 1 + 1 slash
{ path: '/users/:id', isDynamic: true, isCatchAll: false, expectedPriority: 5 }, // 2 + 1 param + 2 slashes
{ path: '/docs/*slug', isDynamic: false, isCatchAll: true, expectedPriority: 102 }, // 100 + 2 slashes
{ path: '/', isDynamic: false, isCatchAll: false, expectedPriority: 2 } // 1 + 1 slash
];
for (const { path, isDynamic, isCatchAll, expectedPriority } of testCases) {
const priority = (router as any).calculateRoutePriority(path, isDynamic, isCatchAll);
expect(priority).toBe(expectedPriority);
}
});
});
describe('matchRoute', () => {
it('should match static routes correctly', () => {
const route = {
path: '/about',
isDynamic: false,
isCatchAll: false,
params: []
};
expect((router as any).matchRoute(route, '/about')).toBe(true);
expect((router as any).matchRoute(route, '/about/')).toBe(false);
expect((router as any).matchRoute(route, '/contact')).toBe(false);
});
it('should match dynamic routes correctly', () => {
const route = {
path: '/users/:id',
isDynamic: true,
isCatchAll: false,
params: ['id']
};
expect((router as any).matchRoute(route, '/users/123')).toBe(true);
expect((router as any).matchRoute(route, '/users/abc')).toBe(true);
expect((router as any).matchRoute(route, '/users')).toBe(false);
expect((router as any).matchRoute(route, '/users/123/edit')).toBe(false);
});
it('should match catch-all routes correctly', () => {
const route = {
path: '/docs/*slug',
isDynamic: false,
isCatchAll: true,
params: ['slug']
};
expect((router as any).matchRoute(route, '/docs/getting-started')).toBe(true);
expect((router as any).matchRoute(route, '/docs/api/reference')).toBe(true);
expect((router as any).matchRoute(route, '/docs')).toBe(false);
expect((router as any).matchRoute(route, '/blog/post')).toBe(false);
});
});
describe('extractParams', () => {
it('should extract parameters from matched paths', () => {
const testCases = [
{
route: { path: '/users/:id', params: ['id'], isDynamic: true, isCatchAll: false },
pathname: '/users/123',
expected: { id: '123' }
},
{
route: { path: '/posts/:category/:id', params: ['category', 'id'], isDynamic: true, isCatchAll: false },
pathname: '/posts/tech/456',
expected: { category: 'tech', id: '456' }
},
{
route: { path: '/docs/*slug', params: ['slug'], isDynamic: false, isCatchAll: true },
pathname: '/docs/getting-started/installation',
expected: { slug: 'getting-started/installation' }
},
{
route: { path: '/about', params: [], isDynamic: false, isCatchAll: false },
pathname: '/about',
expected: {}
}
];
for (const { route, pathname, expected } of testCases) {
const params = router.extractParams(route as any, pathname);
expect(params).toEqual(expected);
}
});
});
describe('generateNavigationUtils', () => {
it('should generate navigation utilities with route map', async () => {
// Mock routes
(router as any).routes = [
{ path: '/', componentName: 'HomePage' },
{ path: '/users', componentName: 'UsersPage' },
{ path: '/users/:id', componentName: 'UserDetailPage', params: ['id'] }
];
// Generate navigation utilities
const navigationUtils = router.generateNavigationUtils();
expect(navigationUtils).toContain('export const ROUTES');
expect(navigationUtils).toContain('HomePage');
expect(navigationUtils).toContain('UsersPage');
expect(navigationUtils).toContain('UserDetailPage');
expect(navigationUtils).toContain('export function generateUrl');
expect(navigationUtils).toContain('export function navigate');
expect(navigationUtils).toContain('export function replace');
expect(navigationUtils).toContain('export function getCurrentRoute');
});
});
describe('generateCodeSplittingConfig', () => {
it('should generate code splitting configuration', async () => {
// Mock routes
(router as any).routes = [
{
path: '/',
componentName: 'HomePage',
filePath: path.join(testRoutesDir, 'index.ordo')
},
{
path: '/blog',
componentName: 'BlogPage',
filePath: path.join(testRoutesDir, 'blog', 'index.ordo')
},
{
path: '/admin',
componentName: 'AdminPage',
filePath: path.join(testRoutesDir, 'admin', 'index.ordo')
}
];
// Generate code splitting config
const config = router.generateCodeSplittingConfig();
expect(config).toHaveProperty('index');
expect(config).toHaveProperty('blog');
expect(config).toHaveProperty('admin');
expect(config.index).toContain(path.join(testRoutesDir, 'index.ordo'));
expect(config.blog).toContain(path.join(testRoutesDir, 'blog', 'index.ordo'));
expect(config.admin).toContain(path.join(testRoutesDir, 'admin', 'index.ordo'));
});
});
describe('route filtering methods', () => {
it('should filter routes correctly', async () => {
// Mock routes
(router as any).routes = [
{
path: '/about',
componentName: 'AboutPage',
isDynamic: false,
isCatchAll: false,
prerender: false
},
{
path: '/users/:id',
componentName: 'UserPage',
isDynamic: true,
isCatchAll: false,
prerender: false
},
{
path: '/docs/*slug',
componentName: 'DocsPage',
isDynamic: false,
isCatchAll: true,
prerender: false
},
{
path: '/blog',
componentName: 'BlogPage',
isDynamic: false,
isCatchAll: false,
prerender: true
}
];
// Test filtering methods
const staticRoutes = router.getStaticRoutes();
const dynamicRoutes = router.getDynamicRoutes();
const prerenderRoutes = router.getPrerenderRoutes();
expect(staticRoutes).toHaveLength(2); // about and blog
expect(dynamicRoutes).toHaveLength(1); // users/:id
expect(prerenderRoutes).toHaveLength(1); // blog
expect(staticRoutes.some(r => r.path === '/about')).toBe(true);
expect(dynamicRoutes.some(r => r.path === '/users/:id')).toBe(true);
expect(prerenderRoutes.some(r => r.path === '/blog')).toBe(true);
});
});
});