Skip to main content
Glama
SiteAuditor.test.js19.8 kB
/** * SiteAuditor Tests * * Tests for the comprehensive SEO site audit functionality including * technical SEO analysis, content quality assessment, site architecture evaluation, * performance monitoring, and accessibility compliance checking. * * @since 2.7.0 */ import { describe, it, expect, beforeEach, vi } from "vitest"; import { SiteAuditor } from "../../../dist/tools/seo/auditors/SiteAuditor.js"; // Mock WordPress client const createMockClient = () => ({ getPosts: vi.fn(), getPages: vi.fn(), authenticate: vi.fn().mockResolvedValue(true), }); // Sample test data const samplePosts = [ { id: 1, title: { rendered: "Complete Guide to WordPress SEO Optimization" }, content: { rendered: ` <h1>Complete Guide to WordPress SEO Optimization</h1> <p>WordPress SEO optimization is crucial for improving your website's search engine visibility. This comprehensive guide covers all essential aspects of technical SEO, content optimization, and performance enhancement. Search engines like Google prioritize well-optimized WordPress sites that provide excellent user experience and valuable content to their audience.</p> <p>Understanding the fundamentals of WordPress SEO helps you create content that ranks higher in search results. This includes optimizing your content structure, using proper heading hierarchy, and ensuring your website loads quickly for both desktop and mobile users.</p> <p>Technical SEO considerations include proper URL structure, meta tags optimization, schema markup implementation, and ensuring your WordPress site is easily crawlable by search engine bots. Content optimization involves keyword research, proper keyword placement, and creating comprehensive content that addresses user search intent effectively.</p> `, }, excerpt: { rendered: "Learn comprehensive WordPress SEO techniques to improve your search rankings and organic traffic.", }, date: "2024-01-15T10:00:00Z", modified: "2024-06-15T14:30:00Z", status: "publish", featured_media: 123, link: "https://example.com/wordpress-seo-guide", }, { id: 2, title: { rendered: "WordPress Performance Tips" }, content: { rendered: ` <h1>WordPress Performance Tips</h1> <p>Site performance optimization.</p> `, }, excerpt: { rendered: "" }, // Missing meta description date: "2022-01-10T09:00:00Z", // Old content modified: "2022-01-10T09:00:00Z", status: "publish", featured_media: 0, // No featured image link: "https://example.com/wordpress-performance-optimization-tips-for-better-speed-and-user-experience", }, { id: 3, title: { rendered: "WordPress Performance Tips" }, // Duplicate title content: { rendered: ` <h1>Duplicate Title Post</h1> <h1>Another H1 Tag</h1> <p>This post has multiple H1 tags which is bad for SEO.</p> <img src="image1.jpg" alt=""> <img src="image2.jpg"> `, }, excerpt: { rendered: "Short" }, // Too short meta description date: "2024-01-05T14:00:00Z", modified: "2024-01-05T14:00:00Z", status: "publish", featured_media: 456, link: "https://example.com/duplicate-title", }, ]; const samplePages = [ { id: 101, title: { rendered: "About Us" }, content: { rendered: ` <p>Short about page content that doesn't provide much value or information about the company.</p> `, }, excerpt: { rendered: "Learn more about our company and mission statement for business success." }, date: "2023-06-01T12:00:00Z", modified: "2024-01-15T10:00:00Z", status: "publish", featured_media: 789, link: "https://example.com/about", }, { id: 102, title: { rendered: "Contact" }, content: { rendered: ` <h1>Contact Us</h1> <p>Get in touch with our team for any inquiries, support requests, or business opportunities. We are committed to providing excellent customer service and responding to all communications promptly. Our experienced team is here to help you achieve your goals and answer any questions you may have about our services, products, or solutions.</p> <p>Whether you need technical support, want to learn more about our offerings, or are interested in partnering with us, we welcome the opportunity to connect. Please use the contact form below or reach out directly using the contact information provided. We look forward to hearing from you and building a successful business relationship together.</p> `, }, excerpt: { rendered: "Contact our team for support, inquiries, and business opportunities. We're here to help you succeed.", }, date: "2024-02-01T15:30:00Z", modified: "2024-07-01T09:15:00Z", status: "publish", featured_media: 0, // Missing featured image link: "https://example.com/contact", }, ]; describe("SiteAuditor", () => { let siteAuditor; let mockClient; beforeEach(() => { mockClient = createMockClient(); // Mock successful API responses mockClient.getPosts.mockResolvedValue(samplePosts); mockClient.getPages.mockResolvedValue(samplePages); siteAuditor = new SiteAuditor(mockClient, { includeTechnical: true, includeContent: true, includeArchitecture: true, includePerformance: true, includeAccessibility: true, maxPagesForContentAudit: 50, minSeverityLevel: "low", // Include all issues for testing includeRecommendations: true, }); }); describe("Configuration", () => { it("should initialize with default configuration", () => { const defaultAuditor = new SiteAuditor(mockClient); const config = defaultAuditor.getConfig(); expect(config.includeTechnical).toBe(true); expect(config.includeContent).toBe(true); expect(config.includeArchitecture).toBe(true); expect(config.includePerformance).toBe(true); expect(config.includeAccessibility).toBe(false); expect(config.maxPagesForContentAudit).toBe(50); expect(config.minSeverityLevel).toBe("medium"); expect(config.includeRecommendations).toBe(true); }); it("should allow configuration updates", () => { siteAuditor.updateConfig({ maxPagesForContentAudit: 100, minSeverityLevel: "high", includeAccessibility: true, }); const config = siteAuditor.getConfig(); expect(config.maxPagesForContentAudit).toBe(100); expect(config.minSeverityLevel).toBe("high"); expect(config.includeAccessibility).toBe(true); }); }); describe("Site Audit Execution", () => { it("should perform comprehensive site audit", async () => { const params = { site: "test", }; const auditResult = await siteAuditor.performSiteAudit(params); expect(auditResult).toBeDefined(); expect(auditResult.timestamp).toBeDefined(); expect(auditResult.siteUrl).toBeDefined(); expect(typeof auditResult.overallScore).toBe("number"); expect(auditResult.overallScore).toBeGreaterThanOrEqual(0); expect(auditResult.overallScore).toBeLessThanOrEqual(100); expect(Array.isArray(auditResult.sections)).toBe(true); expect(auditResult.sections.length).toBeGreaterThan(0); expect(Array.isArray(auditResult.issues)).toBe(true); expect(Array.isArray(auditResult.recommendations)).toBe(true); expect(typeof auditResult.summary).toBe("string"); expect(typeof auditResult.processingTime).toBe("number"); }); it("should identify technical SEO issues", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); const technicalSection = auditResult.sections.find((s) => s.name === "Technical SEO"); expect(technicalSection).toBeDefined(); // Should find issues like missing meta descriptions, duplicate titles, thin content const technicalIssues = auditResult.issues.filter((i) => i.category === "technical"); expect(technicalIssues.length).toBeGreaterThan(0); // Check for specific expected issues const metaIssues = technicalIssues.find((i) => i.id === "missing-meta-descriptions"); const duplicateIssues = technicalIssues.find((i) => i.id === "duplicate-titles"); const thinContentIssues = technicalIssues.find((i) => i.id === "thin-content"); expect(metaIssues || duplicateIssues || thinContentIssues).toBeDefined(); }); it("should identify content quality issues", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); const contentSection = auditResult.sections.find((s) => s.name === "Content Quality"); expect(contentSection).toBeDefined(); // Should find issues like outdated content, missing featured images const contentIssues = auditResult.issues.filter((i) => i.category === "content"); expect(contentIssues.length).toBeGreaterThan(0); // Check for specific expected issues const outdatedIssues = contentIssues.find((i) => i.id === "outdated-content"); const imageIssues = contentIssues.find((i) => i.id === "missing-featured-images"); const readabilityIssues = contentIssues.find((i) => i.id === "poor-readability"); expect(outdatedIssues || imageIssues || readabilityIssues).toBeDefined(); }); it("should identify site architecture issues", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); const architectureSection = auditResult.sections.find((s) => s.name === "Site Architecture"); expect(architectureSection).toBeDefined(); // Should identify potential architecture issues const architectureIssues = auditResult.issues.filter((i) => i.category === "architecture"); // Check issue structure architectureIssues.forEach((issue) => { expect(issue).toHaveProperty("id"); expect(issue).toHaveProperty("title"); expect(issue).toHaveProperty("description"); expect(issue).toHaveProperty("severity"); expect(issue).toHaveProperty("category"); expect(issue).toHaveProperty("affectedItems"); expect(issue).toHaveProperty("impact"); expect(["low", "medium", "high", "critical"]).toContain(issue.severity); }); }); it("should identify performance issues", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); const performanceSection = auditResult.sections.find((s) => s.name === "Performance"); expect(performanceSection).toBeDefined(); // Performance analysis may find optimization opportunities const performanceIssues = auditResult.issues.filter((i) => i.category === "performance"); // Validate performance issue structure performanceIssues.forEach((issue) => { expect(issue.category).toBe("performance"); expect(typeof issue.impact).toBe("string"); expect(issue.impact.length).toBeGreaterThan(0); }); }); it("should identify accessibility issues", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); const accessibilitySection = auditResult.sections.find((s) => s.name === "Accessibility"); expect(accessibilitySection).toBeDefined(); // Should find missing alt text issues const accessibilityIssues = auditResult.issues.filter((i) => i.category === "accessibility"); if (accessibilityIssues.length > 0) { const altTextIssues = accessibilityIssues.find((i) => i.id === "missing-alt-text"); if (altTextIssues) { expect(altTextIssues.severity).toBe("high"); expect(altTextIssues.impact).toContain("screen readers"); } } }); it("should filter issues by severity level", async () => { // Test with high severity filter siteAuditor.updateConfig({ minSeverityLevel: "high" }); const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); // All returned issues should be high or critical auditResult.issues.forEach((issue) => { expect(["high", "critical"]).toContain(issue.severity); }); }); it("should generate meaningful recommendations", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); expect(auditResult.recommendations.length).toBeGreaterThan(0); // Recommendations should be actionable auditResult.recommendations.forEach((recommendation) => { expect(typeof recommendation).toBe("string"); expect(recommendation.length).toBeGreaterThan(20); // Should contain action words (more flexible regex) expect( /\b(add|improve|optimize|fix|update|review|implement|ensure|create|expand)\b/i.test(recommendation), ).toBe(true); }); }); it("should calculate overall score correctly", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); expect(auditResult.overallScore).toBeGreaterThanOrEqual(0); expect(auditResult.overallScore).toBeLessThanOrEqual(100); // Score should decrease with more critical issues const criticalIssues = auditResult.issues.filter((i) => i.severity === "critical").length; if (criticalIssues > 0) { expect(auditResult.overallScore).toBeLessThan(95); // Should be penalized } }); it("should generate comprehensive audit summary", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); expect(auditResult.summary).toContain("audit completed"); expect(auditResult.summary).toContain("sections"); expect(auditResult.summary).toContain("issues"); // Should mention issue counts const totalIssues = auditResult.issues.length; const criticalIssues = auditResult.issues.filter((i) => i.severity === "critical").length; if (totalIssues > 0) { expect(auditResult.summary).toContain(totalIssues.toString()); } if (criticalIssues > 0) { expect(auditResult.summary).toContain(criticalIssues.toString()); } }); }); describe("Selective Audit Sections", () => { it("should perform only technical audit when configured", async () => { siteAuditor.updateConfig({ includeTechnical: true, includeContent: false, includeArchitecture: false, includePerformance: false, includeAccessibility: false, }); const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); expect(auditResult.sections.length).toBe(1); expect(auditResult.sections[0].name).toBe("Technical SEO"); // All issues should be technical auditResult.issues.forEach((issue) => { expect(issue.category).toBe("technical"); }); }); it("should skip recommendations when disabled", async () => { siteAuditor.updateConfig({ includeRecommendations: false }); const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); expect(auditResult.recommendations.length).toBe(0); }); }); describe("Error Handling", () => { it("should handle API errors gracefully", async () => { mockClient.getPosts.mockRejectedValue(new Error("API Error")); mockClient.getPages.mockRejectedValue(new Error("API Error")); const params = { site: "test" }; await expect(siteAuditor.performSiteAudit(params)).rejects.toThrow("API Error"); }); it("should handle empty site data", async () => { mockClient.getPosts.mockResolvedValue([]); mockClient.getPages.mockResolvedValue([]); const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); expect(auditResult).toBeDefined(); expect(auditResult.overallScore).toBeGreaterThanOrEqual(0); // Should still have sections even with no content expect(auditResult.sections.length).toBeGreaterThan(0); }); }); describe("Performance", () => { it("should complete audit within reasonable time", async () => { const params = { site: "test" }; const startTime = Date.now(); const auditResult = await siteAuditor.performSiteAudit(params); const endTime = Date.now(); expect(auditResult).toBeDefined(); expect(endTime - startTime).toBeLessThan(10000); // Should complete within 10 seconds // Processing time should be reasonable (allow for very fast execution in tests) expect(auditResult.processingTime).toBeGreaterThanOrEqual(0); expect(auditResult.processingTime).toBeLessThan(10000); }); it("should handle large content sets efficiently", async () => { // Create larger dataset const largePosts = Array.from({ length: 30 }, (_, i) => ({ ...samplePosts[0], id: i + 100, title: { rendered: `Post ${i + 1}` }, content: { rendered: `<h1>Post ${i + 1}</h1><p>Content for post ${i + 1} with sufficient length to analyze.</p>`.repeat( 10, ), }, })); mockClient.getPosts.mockResolvedValue(largePosts); const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); expect(auditResult).toBeDefined(); expect(auditResult.processingTime).toBeLessThan(15000); // Should handle larger datasets efficiently }); }); describe("Content Analysis", () => { it("should analyze content readability correctly", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); // Should identify readability issues if present const readabilityIssues = auditResult.issues.filter((i) => i.id === "poor-readability"); readabilityIssues.forEach((issue) => { expect(issue.category).toBe("content"); expect(issue.impact).toContain("readability"); expect(issue.affectedItems.length).toBeGreaterThan(0); }); }); it("should detect missing alt text in images", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); // Sample data includes images without alt text const altTextIssues = auditResult.issues.filter((i) => i.id === "missing-alt-text"); if (altTextIssues.length > 0) { expect(altTextIssues[0].category).toBe("accessibility"); expect(altTextIssues[0].severity).toBe("high"); expect(altTextIssues[0].impact).toContain("screen readers"); } }); it("should identify heading structure problems", async () => { const params = { site: "test" }; const auditResult = await siteAuditor.performSiteAudit(params); // Sample data includes posts with multiple H1 tags const headingIssues = auditResult.issues.filter((i) => i.id === "heading-structure"); if (headingIssues.length > 0) { expect(headingIssues[0].category).toBe("architecture"); expect(headingIssues[0].impact).toContain("heading structure"); expect(headingIssues[0].affectedItems.length).toBeGreaterThan(0); } }); }); });

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