Skip to main content
Glama
pages.test.js23.7 kB
import { vi } from "vitest"; import { PageTools } from "@/tools/pages.js"; describe("PageTools", () => { let pageTools; let mockClient; beforeEach(() => { vi.clearAllMocks(); // Mock WordPress client with all needed methods mockClient = { request: vi.fn(), getPages: vi.fn(), getPage: vi.fn(), createPage: vi.fn(), updatePage: vi.fn(), deletePage: vi.fn(), getPageRevisions: vi.fn(), getUser: vi.fn().mockResolvedValue({ name: "Test User", username: "testuser" }), getSiteUrl: vi.fn().mockReturnValue("https://test-site.com"), config: { baseUrl: "https://test-site.com", }, }; pageTools = new PageTools(); }); describe("getTools", () => { it("should return an array of page tools", () => { const tools = pageTools.getTools(); expect(Array.isArray(tools)).toBe(true); expect(tools).toHaveLength(6); const toolNames = tools.map((tool) => tool.name); expect(toolNames).toContain("wp_list_pages"); expect(toolNames).toContain("wp_get_page"); expect(toolNames).toContain("wp_create_page"); expect(toolNames).toContain("wp_update_page"); expect(toolNames).toContain("wp_delete_page"); expect(toolNames).toContain("wp_get_page_revisions"); }); it("should have proper tool definitions", () => { const tools = pageTools.getTools(); tools.forEach((tool) => { expect(tool).toHaveProperty("name"); expect(tool).toHaveProperty("description"); expect(tool).toHaveProperty("handler"); expect(typeof tool.handler).toBe("function"); }); }); it("should have correct parameter definitions for each tool", () => { const tools = pageTools.getTools(); const toolsByName = {}; tools.forEach((tool) => { toolsByName[tool.name] = tool; }); // wp_list_pages should have optional parameters expect(toolsByName["wp_list_pages"].parameters).toHaveLength(3); const listParams = toolsByName["wp_list_pages"].parameters; expect(listParams.find((p) => p.name === "per_page")).toBeTruthy(); expect(listParams.find((p) => p.name === "search")).toBeTruthy(); expect(listParams.find((p) => p.name === "status")).toBeTruthy(); // wp_get_page should require id const getPageParams = toolsByName["wp_get_page"].parameters; const idParam = getPageParams.find((p) => p.name === "id"); expect(idParam).toBeTruthy(); expect(idParam.required).toBe(true); // wp_create_page should have title and content parameters const createParams = toolsByName["wp_create_page"].parameters; expect(createParams.find((p) => p.name === "title")).toBeTruthy(); expect(createParams.find((p) => p.name === "content")).toBeTruthy(); expect(createParams.find((p) => p.name === "title").required).toBe(true); // wp_update_page should require id const updateParams = toolsByName["wp_update_page"].parameters; expect(updateParams.find((p) => p.name === "id").required).toBe(true); // wp_delete_page should require id const deleteParams = toolsByName["wp_delete_page"].parameters; expect(deleteParams.find((p) => p.name === "id").required).toBe(true); }); }); describe("handleListPages", () => { it("should list pages successfully", async () => { const mockPages = [ { id: 1, title: { rendered: "Test Page 1" }, content: { rendered: "Content 1" }, status: "publish", date: "2024-01-01T00:00:00", link: "https://test-site.com/page-1", }, { id: 2, title: { rendered: "Test Page 2" }, content: { rendered: "Content 2" }, status: "draft", date: "2024-01-02T00:00:00", link: "https://test-site.com/page-2", }, ]; mockClient.getPages.mockResolvedValueOnce(mockPages); const result = await pageTools.handleListPages(mockClient, {}); expect(mockClient.getPages).toHaveBeenCalledWith({}); expect(typeof result).toBe("string"); expect(result).toContain("Found 2 pages:"); expect(result).toContain("Test Page 1"); expect(result).toContain("Test Page 2"); expect(result).toContain("https://test-site.com/page-1"); }); it("should handle empty results", async () => { mockClient.getPages.mockResolvedValueOnce([]); const result = await pageTools.handleListPages(mockClient, {}); expect(typeof result).toBe("string"); expect(result).toContain("No pages found matching the criteria"); }); it("should handle search parameters", async () => { const mockPages = [ { id: 1, title: { rendered: "About Us Page" }, content: { rendered: "Learn about us" }, status: "publish", link: "https://test-site.com/about", }, ]; mockClient.getPages.mockResolvedValueOnce(mockPages); const result = await pageTools.handleListPages(mockClient, { search: "About", per_page: 5, }); expect(mockClient.getPages).toHaveBeenCalledWith({ search: "About", per_page: 5, }); expect(typeof result).toBe("string"); expect(result).toContain("About Us Page"); }); it("should handle status parameters", async () => { const mockPages = [ { id: 1, title: { rendered: "Draft Page" }, status: "draft", link: "https://test-site.com/draft", }, ]; mockClient.getPages.mockResolvedValueOnce(mockPages); const result = await pageTools.handleListPages(mockClient, { status: "draft", }); expect(mockClient.getPages).toHaveBeenCalledWith({ status: "draft", }); expect(typeof result).toBe("string"); expect(result).toContain("Draft Page"); }); it("should handle API errors", async () => { mockClient.getPages.mockRejectedValueOnce(new Error("API Error")); await expect(pageTools.handleListPages(mockClient, {})).rejects.toThrow("Failed to list pages"); }); it("should handle per_page parameter", async () => { const mockPages = Array.from({ length: 20 }, (_, i) => ({ id: i + 1, title: { rendered: `Page ${i + 1}` }, status: "publish", link: `https://test-site.com/page-${i + 1}`, })); mockClient.getPages.mockResolvedValueOnce(mockPages); const result = await pageTools.handleListPages(mockClient, { per_page: 20 }); expect(mockClient.getPages).toHaveBeenCalledWith({ per_page: 20 }); expect(result).toContain("Found 20 pages:"); }); it("should format page status correctly", async () => { const mockPages = [ { id: 1, title: { rendered: "Published Page" }, status: "publish", link: "https://test-site.com/pub" }, { id: 2, title: { rendered: "Private Page" }, status: "private", link: "https://test-site.com/priv" }, ]; mockClient.getPages.mockResolvedValueOnce(mockPages); const result = await pageTools.handleListPages(mockClient, {}); expect(result).toContain("(publish)"); expect(result).toContain("(private)"); }); }); describe("handleGetPage", () => { it("should get a page successfully", async () => { const mockPage = { id: 1, title: { rendered: "Test Page" }, content: { rendered: "Test Content" }, status: "publish", date: "2024-01-01T00:00:00", link: "https://test-site.com/test-page", }; mockClient.getPage.mockResolvedValueOnce(mockPage); const result = await pageTools.handleGetPage(mockClient, { id: 1 }); expect(mockClient.getPage).toHaveBeenCalledWith(1); expect(typeof result).toBe("string"); expect(result).toContain("Page Details (ID: 1)"); expect(result).toContain("Test Page"); expect(result).toContain("publish"); expect(result).toContain("https://test-site.com/test-page"); }); it("should handle page not found", async () => { mockClient.getPage.mockRejectedValueOnce(new Error("Page not found")); await expect(pageTools.handleGetPage(mockClient, { id: 999 })).rejects.toThrow("Failed to get page"); }); it("should handle invalid ID parameter", async () => { mockClient.getPage.mockRejectedValueOnce(new Error("Invalid ID")); await expect(pageTools.handleGetPage(mockClient, { id: "invalid" })).rejects.toThrow("Failed to get page"); }); it("should format date correctly", async () => { const mockPage = { id: 1, title: { rendered: "Date Test Page" }, status: "publish", date: "2024-01-15T14:30:00", link: "https://test-site.com/date-test", }; mockClient.getPage.mockResolvedValueOnce(mockPage); const result = await pageTools.handleGetPage(mockClient, { id: 1 }); expect(result).toContain("Date Test Page"); expect(result).toMatch(/Date:.*2024/); // Should contain formatted date with year }); }); describe("handleCreatePage", () => { it("should create a page successfully", async () => { const mockCreatedPage = { id: 123, title: { rendered: "New Page" }, content: { rendered: "New Content" }, status: "publish", link: "https://test-site.com/new-page", }; mockClient.createPage.mockResolvedValueOnce(mockCreatedPage); const pageData = { title: "New Page", content: "New Content", status: "publish", }; const result = await pageTools.handleCreatePage(mockClient, pageData); expect(mockClient.createPage).toHaveBeenCalledWith(pageData); expect(typeof result).toBe("string"); expect(result).toContain("✅ Page created successfully!"); expect(result).toContain("ID: 123"); expect(result).toContain("New Page"); expect(result).toContain("https://test-site.com/new-page"); }); it("should handle validation errors", async () => { mockClient.createPage.mockRejectedValueOnce(new Error("Title is required")); await expect( pageTools.handleCreatePage(mockClient, { title: "", // Invalid empty title content: "Some content", }), ).rejects.toThrow("Failed to create page"); }); it("should handle API creation errors", async () => { mockClient.createPage.mockRejectedValueOnce(new Error("Creation failed")); await expect( pageTools.handleCreatePage(mockClient, { title: "Test Page", content: "Test Content", }), ).rejects.toThrow("Failed to create page"); }); it("should handle pages with different statuses", async () => { const mockCreatedPage = { id: 124, title: { rendered: "Draft Page" }, content: { rendered: "Content" }, status: "draft", link: "https://test-site.com/draft-page", }; mockClient.createPage.mockResolvedValueOnce(mockCreatedPage); const result = await pageTools.handleCreatePage(mockClient, { title: "Draft Page", content: "Content", status: "draft", }); expect(mockClient.createPage).toHaveBeenCalledWith({ title: "Draft Page", content: "Content", status: "draft", }); expect(typeof result).toBe("string"); expect(result).toContain("Draft Page"); expect(result).toContain("ID: 124"); }); it("should handle minimal page creation (title only)", async () => { const mockCreatedPage = { id: 125, title: { rendered: "Minimal Page" }, status: "draft", link: "https://test-site.com/minimal-page", }; mockClient.createPage.mockResolvedValueOnce(mockCreatedPage); const result = await pageTools.handleCreatePage(mockClient, { title: "Minimal Page", }); expect(mockClient.createPage).toHaveBeenCalledWith({ title: "Minimal Page", }); expect(result).toContain("Minimal Page"); }); }); describe("handleUpdatePage", () => { it("should update a page successfully", async () => { const mockUpdatedPage = { id: 1, title: { rendered: "Updated Page" }, content: { rendered: "Updated Content" }, status: "publish", modified: "2024-01-01T12:00:00", link: "https://test-site.com/updated-page", }; mockClient.updatePage.mockResolvedValueOnce(mockUpdatedPage); const updateData = { id: 1, title: "Updated Page", content: "Updated Content", }; const result = await pageTools.handleUpdatePage(mockClient, updateData); expect(mockClient.updatePage).toHaveBeenCalledWith(updateData); expect(typeof result).toBe("string"); expect(result).toContain("✅ Page 1 updated successfully"); }); it("should handle update errors", async () => { mockClient.updatePage.mockRejectedValueOnce(new Error("Update failed")); await expect( pageTools.handleUpdatePage(mockClient, { id: 1, title: "Updated Page", }), ).rejects.toThrow("Failed to update page"); }); it("should handle missing ID", async () => { mockClient.updatePage.mockRejectedValueOnce(new Error("ID is required")); await expect( pageTools.handleUpdatePage(mockClient, { title: "Updated Page", }), ).rejects.toThrow("Failed to update page"); }); it("should handle status updates", async () => { const mockUpdatedPage = { id: 2, title: { rendered: "Status Update Page" }, status: "publish", }; mockClient.updatePage.mockResolvedValueOnce(mockUpdatedPage); const result = await pageTools.handleUpdatePage(mockClient, { id: 2, status: "publish", }); expect(mockClient.updatePage).toHaveBeenCalledWith({ id: 2, status: "publish", }); expect(result).toContain("✅ Page 2 updated successfully"); }); }); describe("handleDeletePage", () => { it("should delete a page successfully (move to trash)", async () => { const mockDeleteResult = { deleted: true, previous: { id: 1, title: { rendered: "Deleted Page" }, status: "trash", }, }; mockClient.deletePage.mockResolvedValueOnce(mockDeleteResult); const result = await pageTools.handleDeletePage(mockClient, { id: 1 }); expect(mockClient.deletePage).toHaveBeenCalledWith(1, undefined); expect(typeof result).toBe("string"); expect(result).toContain("✅ Page 1 has been moved to trash"); }); it("should handle forced deletion", async () => { const mockDeleteResult = { deleted: true, previous: { id: 1, title: { rendered: "Permanently Deleted Page" }, }, }; mockClient.deletePage.mockResolvedValueOnce(mockDeleteResult); const result = await pageTools.handleDeletePage(mockClient, { id: 1, force: true, }); expect(mockClient.deletePage).toHaveBeenCalledWith(1, true); expect(typeof result).toBe("string"); expect(result).toContain("✅ Page 1 has been permanently deleted"); }); it("should handle deletion errors", async () => { mockClient.deletePage.mockRejectedValueOnce(new Error("Delete failed")); await expect(pageTools.handleDeletePage(mockClient, { id: 1 })).rejects.toThrow("Failed to delete page"); }); it("should handle invalid ID", async () => { mockClient.deletePage.mockRejectedValueOnce(new Error("Invalid ID")); await expect(pageTools.handleDeletePage(mockClient, { id: "invalid" })).rejects.toThrow("Failed to delete page"); }); it("should properly handle force parameter", async () => { const mockDeleteResult = { deleted: true }; mockClient.deletePage.mockResolvedValue(mockDeleteResult); // Test with force: false await pageTools.handleDeletePage(mockClient, { id: 1, force: false }); expect(mockClient.deletePage).toHaveBeenCalledWith(1, false); // Test with force: true await pageTools.handleDeletePage(mockClient, { id: 2, force: true }); expect(mockClient.deletePage).toHaveBeenCalledWith(2, true); }); }); describe("handleGetPageRevisions", () => { it("should get page revisions successfully", async () => { const mockRevisions = [ { id: 101, parent: 1, title: { rendered: "Revision 1" }, content: { rendered: "Revision Content 1" }, author: 1, modified: "2024-01-01T00:00:00", }, { id: 102, parent: 1, title: { rendered: "Revision 2" }, content: { rendered: "Revision Content 2" }, author: 2, modified: "2024-01-02T00:00:00", }, ]; mockClient.getPageRevisions.mockResolvedValueOnce(mockRevisions); const result = await pageTools.handleGetPageRevisions(mockClient, { id: 1 }); expect(mockClient.getPageRevisions).toHaveBeenCalledWith(1); expect(typeof result).toBe("string"); expect(result).toContain("Found 2 revisions for page 1:"); expect(result).toContain("Revision by user ID 1"); expect(result).toContain("Revision by user ID 2"); }); it("should handle no revisions found", async () => { mockClient.getPageRevisions.mockResolvedValueOnce([]); const result = await pageTools.handleGetPageRevisions(mockClient, { id: 1 }); expect(typeof result).toBe("string"); expect(result).toContain("No revisions found for page 1"); }); it("should handle API errors for revisions", async () => { mockClient.getPageRevisions.mockRejectedValueOnce(new Error("Revisions API error")); await expect(pageTools.handleGetPageRevisions(mockClient, { id: 1 })).rejects.toThrow( "Failed to get page revisions", ); }); it("should handle invalid ID for revisions", async () => { mockClient.getPageRevisions.mockRejectedValueOnce(new Error("Invalid ID")); await expect(pageTools.handleGetPageRevisions(mockClient, { id: "invalid" })).rejects.toThrow( "Failed to get page revisions", ); }); it("should format revision dates correctly", async () => { const mockRevisions = [ { id: 101, author: 1, modified: "2024-01-15T14:30:00", }, ]; mockClient.getPageRevisions.mockResolvedValueOnce(mockRevisions); const result = await pageTools.handleGetPageRevisions(mockClient, { id: 1 }); expect(result).toMatch(/2024/); // Should contain formatted date with year }); }); describe("Edge Cases and Error Handling", () => { it("should handle null/undefined parameters gracefully", async () => { await expect(pageTools.handleListPages(mockClient, null)).rejects.toThrow("Failed to list pages"); }); it("should handle client without getSiteUrl method", async () => { const clientWithoutGetSiteUrl = { ...mockClient }; delete clientWithoutGetSiteUrl.getSiteUrl; mockClient.getPages.mockResolvedValueOnce([ { id: 1, title: { rendered: "Test" }, status: "publish", link: "https://test-site.com/test", }, ]); const result = await pageTools.handleListPages(clientWithoutGetSiteUrl, {}); expect(typeof result).toBe("string"); }); it("should handle very large result sets", async () => { const largeMockPages = Array.from({ length: 100 }, (_, i) => ({ id: i + 1, title: { rendered: `Page ${i + 1}` }, content: { rendered: `Content ${i + 1}` }, status: "publish", link: `https://test-site.com/page-${i + 1}`, })); mockClient.getPages.mockResolvedValueOnce(largeMockPages); const result = await pageTools.handleListPages(mockClient, { per_page: 100 }); expect(typeof result).toBe("string"); expect(result).toContain("Found 100 pages:"); }); it("should sanitize HTML content in pages", async () => { const pageWithHtml = { title: "Test Page", content: "<script>alert('xss')</script><p>Safe content</p>", status: "publish", }; const mockCreatedPage = { id: 125, title: { rendered: "Test Page" }, content: { rendered: "<p>Safe content</p>" }, // Script should be removed status: "publish", link: "https://test-site.com/test-page", }; mockClient.createPage.mockResolvedValueOnce(mockCreatedPage); const result = await pageTools.handleCreatePage(mockClient, pageWithHtml); expect(mockClient.createPage).toHaveBeenCalled(); expect(typeof result).toBe("string"); expect(result).toContain("✅ Page created successfully!"); }); it("should handle network timeouts gracefully", async () => { const timeoutError = new Error("Request timeout"); timeoutError.code = "ECONNABORTED"; mockClient.getPages.mockRejectedValueOnce(timeoutError); await expect(pageTools.handleListPages(mockClient, {})).rejects.toThrow("Failed to list pages"); }); it("should handle concurrent requests properly", async () => { const mockPage = { id: 1, title: { rendered: "Test" }, status: "publish", link: "https://test-site.com/test" }; mockClient.getPage.mockResolvedValue(mockPage); const promises = [ pageTools.handleGetPage(mockClient, { id: 1 }), pageTools.handleGetPage(mockClient, { id: 2 }), pageTools.handleGetPage(mockClient, { id: 3 }), ]; const results = await Promise.all(promises); expect(results).toHaveLength(3); expect(mockClient.getPage).toHaveBeenCalledTimes(3); }); }); describe("Performance and Validation", () => { it("should validate pagination parameters", async () => { // Test with valid per_page mockClient.getPages.mockResolvedValueOnce([]); const result = await pageTools.handleListPages(mockClient, { per_page: 50 }); expect(typeof result).toBe("string"); expect(mockClient.getPages).toHaveBeenCalledWith({ per_page: 50 }); }); it("should handle mixed parameter types", async () => { const mockPages = [ { id: 1, title: { rendered: "Mixed Test" }, status: "publish", link: "https://test-site.com/mixed" }, ]; mockClient.getPages.mockResolvedValueOnce(mockPages); const result = await pageTools.handleListPages(mockClient, { per_page: 10, search: "test query", status: "publish", }); expect(mockClient.getPages).toHaveBeenCalledWith({ per_page: 10, search: "test query", status: "publish", }); expect(result).toContain("Mixed Test"); }); it("should maintain consistent response format", async () => { const mockPages = [ { id: 1, title: { rendered: "Format Test" }, status: "publish", link: "https://test-site.com/format" }, ]; mockClient.getPages.mockResolvedValueOnce(mockPages); const result = await pageTools.handleListPages(mockClient, {}); expect(result).toMatch(/Found \d+ pages:/); expect(result).toContain("ID 1:"); expect(result).toContain("**Format Test**"); expect(result).toContain("(publish)"); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/docdyhr/mcp-wordpress'

If you have feedback or need assistance with the MCP directory API, please join our Discord server