Add Site Builder help docs and fix FloatingHelpButton paths
- Add HelpSiteBuilder.tsx with comprehensive documentation for the drag-and-drop page editor (components, publishing, settings) - Fix FloatingHelpButton to use /dashboard/help/* paths on tenant sites - Update HelpComprehensive and HelpAutomations to rename plugins to automations - Add site-crawler utility with cross-subdomain redirect detection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ import { Page, BrowserContext } from '@playwright/test';
|
||||
|
||||
export interface CrawlError {
|
||||
url: string;
|
||||
type: 'console' | 'network' | 'broken-link' | 'page-error';
|
||||
type: 'console' | 'network' | 'broken-link' | 'page-error' | 'redirect';
|
||||
message: string;
|
||||
details?: string;
|
||||
timestamp: Date;
|
||||
@@ -33,6 +33,7 @@ export interface CrawlReport {
|
||||
networkErrors: number;
|
||||
brokenLinks: number;
|
||||
pageErrors: number;
|
||||
redirects: number;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,6 +46,8 @@ export interface CrawlerOptions {
|
||||
screenshotOnError?: boolean;
|
||||
screenshotDir?: string;
|
||||
verbose?: boolean;
|
||||
/** Detect when page redirects to a different subdomain (e.g., tenant to public) */
|
||||
detectCrossSubdomainRedirects?: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_OPTIONS: CrawlerOptions = {
|
||||
@@ -63,6 +66,7 @@ const DEFAULT_OPTIONS: CrawlerOptions = {
|
||||
screenshotOnError: false,
|
||||
screenshotDir: 'test-results/crawler-screenshots',
|
||||
verbose: false,
|
||||
detectCrossSubdomainRedirects: false,
|
||||
};
|
||||
|
||||
export class SiteCrawler {
|
||||
@@ -114,6 +118,24 @@ export class SiteCrawler {
|
||||
}
|
||||
}
|
||||
|
||||
private getSubdomain(url: string): string | null {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
const hostname = parsed.hostname;
|
||||
// Extract subdomain from *.lvh.me
|
||||
if (hostname.endsWith('.lvh.me')) {
|
||||
const parts = hostname.split('.');
|
||||
if (parts.length >= 3) {
|
||||
return parts[0];
|
||||
}
|
||||
}
|
||||
// No subdomain (e.g., lvh.me itself)
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private shouldCrawl(url: string): boolean {
|
||||
// Skip if already visited
|
||||
if (this.visited.has(url)) {
|
||||
@@ -280,6 +302,23 @@ export class SiteCrawler {
|
||||
});
|
||||
}
|
||||
|
||||
// Check for cross-subdomain redirects
|
||||
if (this.options.detectCrossSubdomainRedirects) {
|
||||
const finalUrl = this.page.url();
|
||||
const requestedSubdomain = this.getSubdomain(url);
|
||||
const finalSubdomain = this.getSubdomain(finalUrl);
|
||||
|
||||
if (requestedSubdomain && requestedSubdomain !== finalSubdomain) {
|
||||
errors.push({
|
||||
url,
|
||||
type: 'redirect',
|
||||
message: `Redirected from ${requestedSubdomain}.lvh.me to ${finalSubdomain || 'root'}.lvh.me`,
|
||||
details: `Final URL: ${finalUrl}`,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Wait a bit for React to render and any async operations
|
||||
await this.page.waitForTimeout(500);
|
||||
|
||||
@@ -378,6 +417,7 @@ export class SiteCrawler {
|
||||
networkErrors: 0,
|
||||
brokenLinks: 0,
|
||||
pageErrors: 0,
|
||||
redirects: 0,
|
||||
};
|
||||
|
||||
for (const result of this.results) {
|
||||
@@ -395,6 +435,9 @@ export class SiteCrawler {
|
||||
case 'page-error':
|
||||
summary.pageErrors++;
|
||||
break;
|
||||
case 'redirect':
|
||||
summary.redirects++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -427,7 +470,8 @@ export function formatReport(report: CrawlReport): string {
|
||||
output += ` Console errors: ${report.summary.consoleErrors}\n`;
|
||||
output += ` Network errors: ${report.summary.networkErrors}\n`;
|
||||
output += ` Broken links: ${report.summary.brokenLinks}\n`;
|
||||
output += ` Page errors: ${report.summary.pageErrors}\n\n`;
|
||||
output += ` Page errors: ${report.summary.pageErrors}\n`;
|
||||
output += ` Redirects: ${report.summary.redirects}\n\n`;
|
||||
|
||||
// List pages with errors
|
||||
const pagesWithErrors = report.results.filter(r => r.errors.length > 0);
|
||||
@@ -443,7 +487,8 @@ export function formatReport(report: CrawlReport): string {
|
||||
for (const error of result.errors) {
|
||||
const icon = error.type === 'console' ? '⚠️' :
|
||||
error.type === 'network' ? '🌐' :
|
||||
error.type === 'broken-link' ? '🔴' : '💥';
|
||||
error.type === 'broken-link' ? '🔴' :
|
||||
error.type === 'redirect' ? '↪️' : '💥';
|
||||
output += ` ${icon} [${error.type.toUpperCase()}] ${error.message}\n`;
|
||||
if (error.details) {
|
||||
output += ` Details: ${error.details.substring(0, 200)}\n`;
|
||||
|
||||
Reference in New Issue
Block a user