@prism-engineer/router
Version:
Type-safe Express.js router with automatic client generation
401 lines • 20.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const router_1 = require("../../router");
const path_1 = __importDefault(require("path"));
const promises_1 = __importDefault(require("fs/promises"));
(0, vitest_1.describe)('Frontend Client - Method Calls', () => {
let tempDir;
let generatedClient;
let mockFetch;
(0, vitest_1.beforeEach)(async () => {
vitest_1.vi.clearAllMocks();
// Mock fetch globally
mockFetch = vitest_1.vi.fn();
global.fetch = mockFetch;
// Create temporary directory for generated client
tempDir = path_1.default.join(process.cwd(), 'temp-test-' + crypto.randomUUID());
await promises_1.default.mkdir(tempDir, { recursive: true });
// Generate client for testing
await router_1.router.compile({
outputDir: tempDir,
name: 'TestClient',
baseUrl: 'http://localhost:3000',
routes: [{
directory: path_1.default.resolve(__dirname, '../../../dist/tests/router/fixtures/api'),
pattern: /.*\.js$/
}]
});
// Create a mock client with the expected structure
generatedClient = createMockClient();
});
(0, vitest_1.afterEach)(async () => {
// Cleanup temporary directory
try {
await promises_1.default.rm(tempDir, { recursive: true, force: true });
}
catch {
// Ignore cleanup errors
}
vitest_1.vi.restoreAllMocks();
});
// Mock client factory for testing
function createMockClient() {
return {
api: {
hello: {
get: vitest_1.vi.fn().mockImplementation(async (options = {}) => {
const url = 'http://localhost:3000/api/hello';
mockFetch.mockResolvedValueOnce({
ok: true,
status: 200,
headers: new Map([['content-type', 'application/json']]),
json: async () => ({ message: 'Hello World' })
});
await fetch(url, {
method: 'GET',
headers: options.headers || {}
});
return { message: 'Hello World' };
})
},
users: {
get: vitest_1.vi.fn().mockImplementation(async (options = {}) => {
const url = new URL('http://localhost:3000/api/users');
if (options.query) {
Object.entries(options.query).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});
}
mockFetch.mockResolvedValueOnce({
ok: true,
status: 200,
headers: new Map([['content-type', 'application/json']]),
json: async () => [{ id: 1, name: 'John Doe', email: 'john@example.com' }]
});
await fetch(url.toString(), {
method: 'GET',
headers: options.headers || {}
});
return [{ id: 1, name: 'John Doe', email: 'john@example.com' }];
}),
post: vitest_1.vi.fn().mockImplementation(async (options = {}) => {
mockFetch.mockResolvedValueOnce({
ok: true,
status: 201,
headers: new Map([['content-type', 'application/json']]),
json: async () => ({ id: 2, ...options.body })
});
await fetch('http://localhost:3000/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...options.headers
},
body: JSON.stringify(options.body)
});
return { id: 2, ...options.body };
}),
_userId_: {
get: vitest_1.vi.fn().mockImplementation(async (userId, options = {}) => {
mockFetch.mockResolvedValueOnce({
ok: true,
status: 200,
headers: new Map([['content-type', 'application/json']]),
json: async () => ({ id: parseInt(userId), name: 'John Doe', email: 'john@example.com' })
});
await fetch(`http://localhost:3000/api/users/${userId}`, {
method: 'GET',
headers: options.headers || {}
});
return { id: parseInt(userId), name: 'John Doe', email: 'john@example.com' };
})
}
},
v1: {
posts: {
get: vitest_1.vi.fn().mockImplementation(async (options = {}) => {
mockFetch.mockResolvedValueOnce({
ok: true,
status: 200,
headers: new Map([['content-type', 'application/json']]),
json: async () => [{ id: 1, title: 'Test Post V1', content: 'Test content', version: 'v1' }]
});
await fetch('http://localhost:3000/api/v1/posts', {
method: 'GET',
headers: options.headers || {}
});
return [{ id: 1, title: 'Test Post V1', content: 'Test content', version: 'v1' }];
})
}
},
v2: {
posts: {
get: vitest_1.vi.fn().mockImplementation(async (options = {}) => {
mockFetch.mockResolvedValueOnce({
ok: true,
status: 200,
headers: new Map([['content-type', 'application/json']]),
json: async () => [{ id: 1, title: 'Test Post V2', content: 'Test content', version: 'v2' }]
});
await fetch('http://localhost:3000/api/v2/posts', {
method: 'GET',
headers: options.headers || {}
});
return [{ id: 1, title: 'Test Post V2', content: 'Test content', version: 'v2' }];
})
}
}
},
admin: {
dashboard: {
get: vitest_1.vi.fn().mockImplementation(async (options = {}) => {
mockFetch.mockResolvedValueOnce({
ok: true,
status: 200,
headers: new Map([['content-type', 'application/json']]),
json: async () => ({ stats: { users: 100, posts: 500 } })
});
await fetch('http://localhost:3000/admin/dashboard', {
method: 'GET',
headers: options.headers || {}
});
return { stats: { users: 100, posts: 500 } };
})
}
}
};
}
(0, vitest_1.it)('should call GET method without parameters', async () => {
const result = await generatedClient.api.hello.get();
(0, vitest_1.expect)(generatedClient.api.hello.get).toHaveBeenCalledWith();
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/hello', {
method: 'GET',
headers: {}
});
(0, vitest_1.expect)(result).toEqual({ message: 'Hello World' });
});
(0, vitest_1.it)('should call GET method with query parameters', async () => {
const result = await generatedClient.api.users.get({
query: { page: 1, limit: 10, search: 'john' }
});
(0, vitest_1.expect)(generatedClient.api.users.get).toHaveBeenCalledWith({
query: { page: 1, limit: 10, search: 'john' }
});
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/users?page=1&limit=10&search=john', vitest_1.expect.objectContaining({ method: 'GET' }));
(0, vitest_1.expect)(result).toEqual([{ id: 1, name: 'John Doe', email: 'john@example.com' }]);
});
(0, vitest_1.it)('should call GET method with headers', async () => {
const result = await generatedClient.api.hello.get({
headers: {
'Authorization': 'Bearer token123',
'X-API-Version': 'v1'
}
});
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/hello', {
method: 'GET',
headers: {
'Authorization': 'Bearer token123',
'X-API-Version': 'v1'
}
});
(0, vitest_1.expect)(result).toEqual({ message: 'Hello World' });
});
(0, vitest_1.it)('should call POST method with request body', async () => {
const userData = { name: 'Jane Doe', email: 'jane@example.com' };
const result = await generatedClient.api.users.post({
body: userData
});
(0, vitest_1.expect)(generatedClient.api.users.post).toHaveBeenCalledWith({
body: userData
});
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
(0, vitest_1.expect)(result).toEqual({ id: 2, ...userData });
});
(0, vitest_1.it)('should call POST method with body and headers', async () => {
const userData = { name: 'Alice Smith', email: 'alice@example.com' };
const result = await generatedClient.api.users.post({
body: userData,
headers: {
'Authorization': 'Bearer token123',
'X-Request-ID': 'req-456'
}
});
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123',
'X-Request-ID': 'req-456'
},
body: JSON.stringify(userData)
});
(0, vitest_1.expect)(result).toEqual({ id: 2, ...userData });
});
(0, vitest_1.it)('should call method with path parameters', async () => {
const userId = '123';
const result = await generatedClient.api.users._userId_.get(userId);
(0, vitest_1.expect)(generatedClient.api.users._userId_.get).toHaveBeenCalledWith(userId);
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith(`http://localhost:3000/api/users/${userId}`, {
method: 'GET',
headers: {}
});
(0, vitest_1.expect)(result).toEqual({ id: 123, name: 'John Doe', email: 'john@example.com' });
});
(0, vitest_1.it)('should call method with path parameters and options', async () => {
const userId = '456';
const result = await generatedClient.api.users._userId_.get(userId, {
headers: { 'Authorization': 'Bearer token123' }
});
(0, vitest_1.expect)(generatedClient.api.users._userId_.get).toHaveBeenCalledWith(userId, {
headers: { 'Authorization': 'Bearer token123' }
});
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith(`http://localhost:3000/api/users/${userId}`, {
method: 'GET',
headers: { 'Authorization': 'Bearer token123' }
});
(0, vitest_1.expect)(result).toEqual({ id: 456, name: 'John Doe', email: 'john@example.com' });
});
(0, vitest_1.it)('should call nested route methods (v1 routes)', async () => {
const result = await generatedClient.api.v1.posts.get();
(0, vitest_1.expect)(generatedClient.api.v1.posts.get).toHaveBeenCalledWith();
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/posts', {
method: 'GET',
headers: {}
});
(0, vitest_1.expect)(result).toEqual([{ id: 1, title: 'Test Post V1', content: 'Test content', version: 'v1' }]);
});
(0, vitest_1.it)('should call nested route methods (v2 routes)', async () => {
const result = await generatedClient.api.v2.posts.get();
(0, vitest_1.expect)(generatedClient.api.v2.posts.get).toHaveBeenCalledWith();
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v2/posts', {
method: 'GET',
headers: {}
});
(0, vitest_1.expect)(result).toEqual([{ id: 1, title: 'Test Post V2', content: 'Test content', version: 'v2' }]);
});
(0, vitest_1.it)('should call admin route methods', async () => {
const result = await generatedClient.admin.dashboard.get();
(0, vitest_1.expect)(generatedClient.admin.dashboard.get).toHaveBeenCalledWith();
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/admin/dashboard', {
method: 'GET',
headers: {}
});
(0, vitest_1.expect)(result).toEqual({ stats: { users: 100, posts: 500 } });
});
(0, vitest_1.it)('should handle method calls with optional parameters', async () => {
// Test calling with undefined options
const result = await generatedClient.api.hello.get(undefined);
(0, vitest_1.expect)(generatedClient.api.hello.get).toHaveBeenCalledWith(undefined);
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/hello', {
method: 'GET',
headers: {}
});
(0, vitest_1.expect)(result).toEqual({ message: 'Hello World' });
});
(0, vitest_1.it)('should handle method calls with empty options', async () => {
const result = await generatedClient.api.users.get({});
(0, vitest_1.expect)(generatedClient.api.users.get).toHaveBeenCalledWith({});
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/users', {
method: 'GET',
headers: {}
});
(0, vitest_1.expect)(result).toEqual([{ id: 1, name: 'John Doe', email: 'john@example.com' }]);
});
(0, vitest_1.it)('should handle method calls with partial options', async () => {
const result = await generatedClient.api.users.get({
query: { page: 1 } // Only some query parameters
});
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/users?page=1', vitest_1.expect.objectContaining({ method: 'GET' }));
(0, vitest_1.expect)(result).toEqual([{ id: 1, name: 'John Doe', email: 'john@example.com' }]);
});
(0, vitest_1.it)('should handle concurrent method calls', async () => {
const promises = [
generatedClient.api.hello.get(),
generatedClient.api.users.get(),
generatedClient.admin.dashboard.get()
];
const results = await Promise.all(promises);
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(3);
(0, vitest_1.expect)(results).toHaveLength(3);
(0, vitest_1.expect)(results[0]).toEqual({ message: 'Hello World' });
(0, vitest_1.expect)(results[1]).toEqual([{ id: 1, name: 'John Doe', email: 'john@example.com' }]);
(0, vitest_1.expect)(results[2]).toEqual({ stats: { users: 100, posts: 500 } });
});
(0, vitest_1.it)('should handle method calls with complex query parameters', async () => {
const complexQuery = {
search: 'john doe',
filters: 'active,verified',
sort: 'created_desc',
page: 2,
limit: 25,
include: 'profile,settings'
};
await generatedClient.api.users.get({ query: complexQuery });
const expectedUrl = 'http://localhost:3000/api/users?' +
'search=john+doe&filters=active%2Cverified&sort=created_desc&page=2&limit=25&include=profile%2Csettings';
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith(expectedUrl, vitest_1.expect.objectContaining({ method: 'GET' }));
});
(0, vitest_1.it)('should handle method calls with various HTTP methods', async () => {
const postData = { name: 'Test User', email: 'test@example.com' };
// Test different HTTP methods if they exist in the client
await generatedClient.api.users.post({ body: postData });
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(postData)
});
});
(0, vitest_1.it)('should handle method calls with custom Content-Type headers', async () => {
await generatedClient.api.users.post({
body: { name: 'Custom User' },
headers: { 'Content-Type': 'application/vnd.api+json' }
});
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/vnd.api+json' },
body: JSON.stringify({ name: 'Custom User' })
});
});
(0, vitest_1.it)('should maintain method call context and state', async () => {
// First call
await generatedClient.api.hello.get({ headers: { 'X-Call': '1' } });
// Second call with different headers
await generatedClient.api.hello.get({ headers: { 'X-Call': '2' } });
(0, vitest_1.expect)(mockFetch).toHaveBeenNthCalledWith(1, 'http://localhost:3000/api/hello', {
method: 'GET',
headers: { 'X-Call': '1' }
});
(0, vitest_1.expect)(mockFetch).toHaveBeenNthCalledWith(2, 'http://localhost:3000/api/hello', {
method: 'GET',
headers: { 'X-Call': '2' }
});
});
(0, vitest_1.it)('should handle method calls with URL encoding', async () => {
const userId = '123/abc'; // ID with special characters
await generatedClient.api.users._userId_.get(userId);
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/users/123/abc', {
method: 'GET',
headers: {}
});
});
(0, vitest_1.it)('should handle method calls with boolean and number query parameters', async () => {
await generatedClient.api.users.get({
query: {
active: true,
limit: 50,
verified: false,
score: 95.5
}
});
(0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/users?active=true&limit=50&verified=false&score=95.5', vitest_1.expect.objectContaining({ method: 'GET' }));
});
});
//# sourceMappingURL=client-method-calls.test.js.map