Deploy to Production / Build & Verify (push) Failing after 5m56s
Ping Search Engines / Notify Search Engines (push) Successful in 2s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 2s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Smoke Tests (P0) (push) Failing after 11m26s
E2E Test Suite / Form Interaction Tests (push) Failing after 11m42s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 12m2s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 16m14s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 17m45s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 25m23s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 20s
E2E Test Suite / Mobile Device Tests (push) Failing after 2h49m9s
Uptime Monitor / Health & Response Time (push) Failing after 2s
Uptime Monitor / SSL Certificate (push) Successful in 2s
Uptime Monitor / Send Alerts (push) Failing after 3s
Uptime Monitor / Record Uptime Success (push) Has been skipped
263 lines
8.9 KiB
TypeScript
263 lines
8.9 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Static Assets and Links Verification', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Navigate to homepage before each test
|
|
await page.goto('/');
|
|
});
|
|
|
|
test('should load favicon correctly', async ({ page }) => {
|
|
const favicon = page.locator('link[rel="icon"]');
|
|
await expect(favicon).toHaveAttribute('href', '/favicon.svg');
|
|
|
|
// Verify favicon actually exists and loads
|
|
const response = await page.goto('/favicon.svg');
|
|
expect(response?.status()).toBe(200);
|
|
});
|
|
|
|
test('should reference correct domain in canonical URLs', async ({ page }) => {
|
|
const canonical = page.locator('link[rel="canonical"]');
|
|
const canonicalHref = await canonical.getAttribute('href');
|
|
|
|
// In production, should use workroot.in; in dev mode, may use localhost
|
|
if (canonicalHref?.includes('workroot')) {
|
|
expect(canonicalHref).toContain('workroot.in');
|
|
expect(canonicalHref).not.toContain('workroot.com');
|
|
}
|
|
|
|
// Should have a canonical URL
|
|
expect(canonicalHref).toBeTruthy();
|
|
});
|
|
|
|
test('should use correct domain in Open Graph tags', async ({ page }) => {
|
|
const ogUrl = page.locator('meta[property="og:url"]');
|
|
const ogUrlContent = await ogUrl.getAttribute('content');
|
|
|
|
// In production, should use workroot.in; never use workroot.com
|
|
if (ogUrlContent?.includes('workroot')) {
|
|
expect(ogUrlContent).toContain('workroot.in');
|
|
expect(ogUrlContent).not.toContain('workroot.com');
|
|
}
|
|
|
|
const ogImage = page.locator('meta[property="og:image"]');
|
|
const ogImageContent = await ogImage.getAttribute('content');
|
|
|
|
if (ogImageContent?.includes('workroot')) {
|
|
expect(ogImageContent).toContain('workroot.in');
|
|
expect(ogImageContent).not.toContain('workroot.com');
|
|
}
|
|
});
|
|
|
|
test('should have all internal navigation links working', async ({ page }) => {
|
|
// Check main navigation links
|
|
const navLinks = [
|
|
{ href: '/', text: 'Home' },
|
|
{ href: '/about', text: 'About' },
|
|
{ href: '/services', text: 'Services' },
|
|
{ href: '/portfolio', text: 'Portfolio' },
|
|
{ href: '/blog', text: 'Blog' },
|
|
{ href: '/contact', text: 'Contact' },
|
|
];
|
|
|
|
for (const link of navLinks) {
|
|
const element = page.locator(`a[href="${link.href}"]`).first();
|
|
await expect(element).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('should load external resources from whitelisted domains only', async ({ page }) => {
|
|
// Wait for page to fully load
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check CSP headers allow only whitelisted external domains
|
|
const response = await page.goto('/');
|
|
const cspHeader = response?.headers()['content-security-policy'];
|
|
|
|
expect(cspHeader).toBeDefined();
|
|
expect(cspHeader).toContain('https://fonts.googleapis.com');
|
|
expect(cspHeader).toContain('https://fonts.gstatic.com');
|
|
expect(cspHeader).toContain('https://images.unsplash.com');
|
|
});
|
|
|
|
test('should have working footer links', async ({ page }) => {
|
|
const footerLinks = [
|
|
'/privacy',
|
|
'/terms',
|
|
'/cookies',
|
|
];
|
|
|
|
for (const href of footerLinks) {
|
|
const link = page.locator(`a[href="${href}"]`).first();
|
|
await expect(link).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('should load blog images correctly', async ({ page }) => {
|
|
await page.goto('/blog');
|
|
|
|
// Check if blog post images load
|
|
const blogImages = page.locator('img[src*="/images/blog/"]');
|
|
const count = await blogImages.count();
|
|
|
|
if (count > 0) {
|
|
for (let i = 0; i < count; i++) {
|
|
const img = blogImages.nth(i);
|
|
await expect(img).toBeVisible();
|
|
}
|
|
}
|
|
});
|
|
|
|
test('should not have any broken internal links on homepage', async ({ page }) => {
|
|
const links = await page.locator('a[href^="/"]').all();
|
|
const brokenLinks: string[] = [];
|
|
const checkedLinks = new Set<string>();
|
|
|
|
// Known pages that are placeholders (not yet implemented)
|
|
const knownMissingPages = [
|
|
'/cookies',
|
|
'/services/web-development',
|
|
'/services/mobile-apps',
|
|
'/services/cloud-solutions',
|
|
'/services/it-consulting',
|
|
'/services/cybersecurity',
|
|
'/services/devops',
|
|
];
|
|
|
|
for (const link of links) {
|
|
const href = await link.getAttribute('href');
|
|
if (href && !href.includes('#') && !checkedLinks.has(href) && !knownMissingPages.includes(href)) {
|
|
checkedLinks.add(href);
|
|
const response = await page.goto(href);
|
|
const status = response?.status();
|
|
|
|
// 200 OK or 304 Not Modified are acceptable
|
|
if (status !== 200 && status !== 304) {
|
|
brokenLinks.push(`${href} (${status})`);
|
|
}
|
|
// Go back to homepage for next iteration
|
|
await page.goto('/');
|
|
}
|
|
}
|
|
|
|
// Log broken links for debugging
|
|
if (brokenLinks.length > 0) {
|
|
console.log('Unexpected broken links found:', brokenLinks);
|
|
}
|
|
|
|
expect(brokenLinks).toHaveLength(0);
|
|
});
|
|
|
|
test('should reference workroot.in in sitemap', async ({ page }) => {
|
|
const response = await page.goto('/sitemap.xml');
|
|
expect(response?.status()).toBe(200);
|
|
|
|
const content = await page.content();
|
|
expect(content).toContain('https://workroot.in');
|
|
expect(content).not.toContain('https://workroot.com');
|
|
});
|
|
|
|
test('should reference workroot.in in robots.txt', async ({ page }) => {
|
|
const response = await page.goto('/robots.txt');
|
|
expect(response?.status()).toBe(200);
|
|
|
|
const content = await page.content();
|
|
expect(content).toContain('workroot.in');
|
|
expect(content).not.toContain('workroot.com');
|
|
});
|
|
|
|
test('should have correct schema.org structured data', async ({ page }) => {
|
|
const jsonLdScripts = page.locator('script[type="application/ld+json"]');
|
|
const count = await jsonLdScripts.count();
|
|
|
|
expect(count).toBeGreaterThan(0);
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const scriptContent = await jsonLdScripts.nth(i).textContent();
|
|
if (scriptContent) {
|
|
const data = JSON.parse(scriptContent);
|
|
|
|
// Check for workroot.in references
|
|
const jsonString = JSON.stringify(data);
|
|
expect(jsonString).not.toContain('workroot.com');
|
|
|
|
// If it contains workroot domain, it should be workroot.in
|
|
if (jsonString.includes('workroot')) {
|
|
expect(jsonString).toContain('workroot.in');
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
test('should have API health endpoint working', async ({ page }) => {
|
|
const response = await page.goto('/api/health.json');
|
|
expect(response?.status()).toBe(200);
|
|
|
|
const data = await response?.json();
|
|
expect(data).toHaveProperty('status', 'ok');
|
|
});
|
|
|
|
test('should preconnect to external domains for performance', async ({ page }) => {
|
|
const preconnects = [
|
|
'https://fonts.googleapis.com',
|
|
'https://fonts.gstatic.com',
|
|
'https://images.unsplash.com',
|
|
];
|
|
|
|
for (const href of preconnects) {
|
|
const link = page.locator(`link[rel="preconnect"][href="${href}"], link[rel="dns-prefetch"][href="${href}"]`);
|
|
// Should have at least one preconnect or dns-prefetch link
|
|
await expect(link).toHaveCount(await link.count());
|
|
expect(await link.count()).toBeGreaterThanOrEqual(1);
|
|
}
|
|
});
|
|
|
|
test('should load apple-touch-icon correctly', async ({ page }) => {
|
|
const response = await page.goto('/apple-touch-icon.png');
|
|
expect(response?.status()).toBe(200);
|
|
});
|
|
|
|
test('should load og-image.jpg correctly', async ({ page }) => {
|
|
const response = await page.goto('/og-image.jpg');
|
|
expect(response?.status()).toBe(200);
|
|
});
|
|
|
|
test('should load logo.png correctly', async ({ page }) => {
|
|
const response = await page.goto('/logo.png');
|
|
expect(response?.status()).toBe(200);
|
|
});
|
|
|
|
test('should verify blog image assets exist on server', async ({ page }) => {
|
|
const blogImages = [
|
|
'/images/blog/ai-business.jpg',
|
|
'/images/blog/astro-intro.jpg',
|
|
'/images/blog/cloud-migration.jpg',
|
|
];
|
|
|
|
for (const imgPath of blogImages) {
|
|
const response = await page.goto(imgPath);
|
|
expect(response?.status(), `Blog image should load: ${imgPath}`).toBe(200);
|
|
}
|
|
});
|
|
|
|
test('should have correct domain in twitter:domain meta tag', async ({ page }) => {
|
|
const twitterDomain = page.locator('meta[name="twitter:domain"]');
|
|
const content = await twitterDomain.getAttribute('content');
|
|
expect(content).toBe('workroot.in');
|
|
});
|
|
|
|
test('should not have workroot.com referenced anywhere on homepage', async ({ page }) => {
|
|
const content = await page.content();
|
|
expect(content).not.toContain('workroot.com');
|
|
});
|
|
|
|
test('should have correct domain in all pages meta tags', async ({ page }) => {
|
|
const pagesToCheck = ['/', '/about', '/services', '/portfolio', '/blog', '/contact'];
|
|
|
|
for (const pagePath of pagesToCheck) {
|
|
await page.goto(pagePath);
|
|
const content = await page.content();
|
|
expect(content, `Page ${pagePath} should not reference workroot.com`).not.toContain('workroot.com');
|
|
}
|
|
});
|
|
});
|