mirror of
https://github.com/jo-inc/camofox-browser.git
synced 2026-10-02 04:14:41 +08:00
Auto-generated OpenAPI 3.0 spec from JSDoc annotations on all 31 routes via swagger-jsdoc. Docs at /docs using swagger-stripey. CI split into parallel unit + e2e jobs (133s → 80s). Supersedes #69. Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
197 lines
5.8 KiB
JavaScript
197 lines
5.8 KiB
JavaScript
import { createClient } from '../helpers/client.js';
|
|
import { getSharedEnv } from './sharedEnv.js';
|
|
|
|
describe('Screenshot', () => {
|
|
let serverUrl;
|
|
let testSiteUrl;
|
|
|
|
beforeAll(() => {
|
|
const env = getSharedEnv();
|
|
serverUrl = env.serverUrl;
|
|
testSiteUrl = env.testSiteUrl;
|
|
});
|
|
|
|
// Server lifecycle managed by globalSetup/globalTeardown
|
|
|
|
test('screenshot returns raw PNG binary with correct magic bytes', async () => {
|
|
const client = createClient(serverUrl);
|
|
|
|
try {
|
|
const { tabId } = await client.createTab(`${testSiteUrl}/pageA`);
|
|
|
|
const buffer = await client.screenshot(tabId);
|
|
|
|
expect(buffer).toBeDefined();
|
|
expect(buffer.byteLength).toBeGreaterThan(0);
|
|
|
|
// PNG magic bytes: 0x89 P N G
|
|
const bytes = new Uint8Array(buffer);
|
|
expect(bytes[0]).toBe(0x89);
|
|
expect(bytes[1]).toBe(0x50);
|
|
expect(bytes[2]).toBe(0x4e);
|
|
expect(bytes[3]).toBe(0x47);
|
|
} finally {
|
|
await client.cleanup();
|
|
}
|
|
});
|
|
|
|
test('screenshot response has image/png content type', async () => {
|
|
const client = createClient(serverUrl);
|
|
|
|
try {
|
|
const { tabId } = await client.createTab(`${testSiteUrl}/pageA`);
|
|
|
|
// Raw fetch to check headers directly
|
|
const res = await fetch(
|
|
`${serverUrl}/tabs/${tabId}/screenshot?userId=${client.userId}`
|
|
);
|
|
|
|
expect(res.ok).toBe(true);
|
|
expect(res.headers.get('content-type')).toBe('image/png');
|
|
|
|
const buffer = await res.arrayBuffer();
|
|
expect(buffer.byteLength).toBeGreaterThan(0);
|
|
} finally {
|
|
await client.cleanup();
|
|
}
|
|
});
|
|
|
|
test('screenshot response is NOT valid JSON', async () => {
|
|
const client = createClient(serverUrl);
|
|
|
|
try {
|
|
const { tabId } = await client.createTab(`${testSiteUrl}/pageA`);
|
|
|
|
const res = await fetch(
|
|
`${serverUrl}/tabs/${tabId}/screenshot?userId=${client.userId}`
|
|
);
|
|
expect(res.ok).toBe(true);
|
|
|
|
// This is the exact bug: fetchApi() called res.json() on PNG binary.
|
|
// Verify that parsing as JSON throws.
|
|
const text = await res.clone().text();
|
|
expect(() => JSON.parse(text)).toThrow();
|
|
} finally {
|
|
await client.cleanup();
|
|
}
|
|
});
|
|
|
|
test('screenshot can be base64-encoded for LLM image content blocks', async () => {
|
|
const client = createClient(serverUrl);
|
|
|
|
try {
|
|
const { tabId } = await client.createTab(`${testSiteUrl}/pageA`);
|
|
|
|
const res = await fetch(
|
|
`${serverUrl}/tabs/${tabId}/screenshot?userId=${client.userId}`
|
|
);
|
|
expect(res.ok).toBe(true);
|
|
|
|
const arrayBuffer = await res.arrayBuffer();
|
|
const base64 = Buffer.from(arrayBuffer).toString('base64');
|
|
|
|
// base64 should be a non-empty string
|
|
expect(typeof base64).toBe('string');
|
|
expect(base64.length).toBeGreaterThan(0);
|
|
|
|
// Round-trip: decode back and verify PNG magic bytes
|
|
const decoded = Buffer.from(base64, 'base64');
|
|
expect(decoded[0]).toBe(0x89);
|
|
expect(decoded[1]).toBe(0x50);
|
|
expect(decoded[2]).toBe(0x4e);
|
|
expect(decoded[3]).toBe(0x47);
|
|
} finally {
|
|
await client.cleanup();
|
|
}
|
|
});
|
|
|
|
test('fullPage screenshot is larger than viewport screenshot', async () => {
|
|
const client = createClient(serverUrl);
|
|
|
|
try {
|
|
// Use the scroll page which has lots of content (5000px tall)
|
|
const { tabId } = await client.createTab(`${testSiteUrl}/scroll`);
|
|
|
|
// Viewport screenshot
|
|
const viewportRes = await fetch(
|
|
`${serverUrl}/tabs/${tabId}/screenshot?userId=${client.userId}&fullPage=false`
|
|
);
|
|
const viewportBuf = await viewportRes.arrayBuffer();
|
|
|
|
// Full page screenshot
|
|
const fullPageRes = await fetch(
|
|
`${serverUrl}/tabs/${tabId}/screenshot?userId=${client.userId}&fullPage=true`
|
|
);
|
|
const fullPageBuf = await fullPageRes.arrayBuffer();
|
|
|
|
// Both should be valid PNGs
|
|
expect(new Uint8Array(viewportBuf)[0]).toBe(0x89);
|
|
expect(new Uint8Array(fullPageBuf)[0]).toBe(0x89);
|
|
|
|
// Full page should be larger (more pixels to encode)
|
|
expect(fullPageBuf.byteLength).toBeGreaterThan(viewportBuf.byteLength);
|
|
} finally {
|
|
await client.cleanup();
|
|
}
|
|
});
|
|
|
|
test('screenshot of non-existent tab returns 404 JSON error', async () => {
|
|
const client = createClient(serverUrl);
|
|
|
|
try {
|
|
const res = await fetch(
|
|
`${serverUrl}/tabs/non-existent-tab/screenshot?userId=${client.userId}`
|
|
);
|
|
|
|
expect(res.status).toBe(404);
|
|
expect(res.headers.get('content-type')).toContain('application/json');
|
|
|
|
const data = await res.json();
|
|
expect(data.error).toBeDefined();
|
|
} finally {
|
|
await client.cleanup();
|
|
}
|
|
});
|
|
|
|
test('screenshot works after navigation', async () => {
|
|
const client = createClient(serverUrl);
|
|
|
|
try {
|
|
const { tabId } = await client.createTab(`${testSiteUrl}/pageA`);
|
|
await client.navigate(tabId, `${testSiteUrl}/pageB`);
|
|
|
|
const buffer = await client.screenshot(tabId);
|
|
|
|
expect(buffer).toBeDefined();
|
|
expect(buffer.byteLength).toBeGreaterThan(0);
|
|
|
|
const bytes = new Uint8Array(buffer);
|
|
expect(bytes[0]).toBe(0x89);
|
|
} finally {
|
|
await client.cleanup();
|
|
}
|
|
});
|
|
|
|
test('screenshot works after click interaction', async () => {
|
|
const client = createClient(serverUrl);
|
|
|
|
try {
|
|
const { tabId } = await client.createTab(`${testSiteUrl}/click`);
|
|
|
|
// Get snapshot to build refs, then click the button
|
|
const snapshot = await client.getSnapshot(tabId);
|
|
await client.click(tabId, { selector: '#clickMe' });
|
|
|
|
const buffer = await client.screenshot(tabId);
|
|
|
|
expect(buffer).toBeDefined();
|
|
expect(buffer.byteLength).toBeGreaterThan(0);
|
|
|
|
const bytes = new Uint8Array(buffer);
|
|
expect(bytes[0]).toBe(0x89);
|
|
} finally {
|
|
await client.cleanup();
|
|
}
|
|
});
|
|
});
|