Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 7s
Build & Deploy / 🧪 QA (push) Successful in 2m28s
Build & Deploy / 🏗️ Build (push) Successful in 6m15s
Build & Deploy / 🚀 Deploy (push) Failing after 7s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
test(og): use real blog slug from sitemap instead of hardcoded hello-world chore(release): bump version to 2.2.10
107 lines
3.7 KiB
TypeScript
107 lines
3.7 KiB
TypeScript
import { describe, it, expect, beforeAll } from 'vitest';
|
||
|
||
const BASE_URL =
|
||
process.env.TEST_URL || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
|
||
|
||
describe('OG Image Generation', () => {
|
||
const locales = ['de', 'en'];
|
||
const productSlugs = ['nay2y'];
|
||
|
||
let isServerUp = false;
|
||
|
||
beforeAll(async () => {
|
||
try {
|
||
const response = await fetch(`${BASE_URL}/health`).catch(() => null);
|
||
if (response && response.ok) {
|
||
const text = await response.text();
|
||
if (text.includes('OK')) {
|
||
isServerUp = true;
|
||
return;
|
||
}
|
||
}
|
||
console.log(
|
||
`\n⚠️ KLZ Application not detected at ${BASE_URL}. Skipping integration tests.\n`,
|
||
);
|
||
} catch (e) {
|
||
isServerUp = false;
|
||
}
|
||
});
|
||
async function verifyImageResponse(response: Response) {
|
||
expect(response.status, `Failed to fetch OG image: ${response.url}`).toBe(200);
|
||
const contentType = response.headers.get('content-type');
|
||
expect(contentType, `Incorrect content type: ${contentType}`).toContain('image/png');
|
||
|
||
const buffer = await response.arrayBuffer();
|
||
const bytes = new Uint8Array(buffer);
|
||
|
||
// Check for PNG signature: 89 50 4E 47 0D 0A 1A 0A
|
||
expect(bytes[0]).toBe(0x89);
|
||
expect(bytes[1]).toBe(0x50);
|
||
expect(bytes[2]).toBe(0x4e);
|
||
expect(bytes[3]).toBe(0x47);
|
||
|
||
// Check that the image is not empty and has a reasonable size
|
||
expect(bytes.length, `Image size too small: ${bytes.length} bytes`).toBeGreaterThan(4000);
|
||
}
|
||
|
||
locales.forEach((locale) => {
|
||
it(`should generate main OG image for ${locale}`, async ({ skip }) => {
|
||
if (!isServerUp) skip();
|
||
const url = `${BASE_URL}/${locale}/opengraph-image`;
|
||
const response = await fetch(url);
|
||
await verifyImageResponse(response);
|
||
}, 30000);
|
||
|
||
it(`should generate product OG image for ${locale} with slug ${productSlugs[0]}`, async ({
|
||
skip,
|
||
}) => {
|
||
if (!isServerUp) skip();
|
||
const url = `${BASE_URL}/${locale}/api/og/product?slug=${productSlugs[0]}`;
|
||
const response = await fetch(url);
|
||
await verifyImageResponse(response);
|
||
}, 30000);
|
||
|
||
it(`should return 400 for product OG image without slug in ${locale}`, async ({ skip }) => {
|
||
if (!isServerUp) skip();
|
||
const url = `${BASE_URL}/${locale}/api/og/product`;
|
||
const response = await fetch(url);
|
||
expect(response.status).toBe(400);
|
||
}, 30000);
|
||
});
|
||
|
||
it('should generate static blog overview OG image', async ({ skip }) => {
|
||
if (!isServerUp) skip();
|
||
const url = `${BASE_URL}/de/blog/opengraph-image`;
|
||
const response = await fetch(url);
|
||
await verifyImageResponse(response);
|
||
}, 30000);
|
||
|
||
it('should generate dynamic blog post OG image with featured photo', async ({ skip }) => {
|
||
if (!isServerUp) skip();
|
||
|
||
// Discover a real blog slug from the sitemap
|
||
const sitemapRes = await fetch(`${BASE_URL}/sitemap.xml`);
|
||
const sitemapXml = await sitemapRes.text();
|
||
const blogMatch = sitemapXml.match(/<loc>[^<]*\/de\/blog\/([^<]+)<\/loc>/);
|
||
const slug = blogMatch ? blogMatch[1] : null;
|
||
|
||
if (!slug) {
|
||
console.log('⚠️ No blog post found in sitemap, skipping dynamic OG test');
|
||
skip();
|
||
return;
|
||
}
|
||
|
||
const url = `${BASE_URL}/de/blog/${slug}/opengraph-image`;
|
||
const response = await fetch(url);
|
||
await verifyImageResponse(response);
|
||
|
||
// Verify the image is substantially large (>50KB) to confirm it actually
|
||
// contains the featured photo and isn't just a tiny fallback/text-only image
|
||
const buffer = await response.clone().arrayBuffer();
|
||
expect(
|
||
buffer.byteLength,
|
||
`OG image for "${slug}" is suspiciously small (${buffer.byteLength} bytes) — likely missing featured photo`,
|
||
).toBeGreaterThan(50000);
|
||
}, 30000);
|
||
});
|