186 lines
7.1 KiB
TypeScript
186 lines
7.1 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('PNG Export', () => {
|
|
test('renders month and exports PNG within size/time budget', async ({ page, browserName }) => {
|
|
await page.goto('/');
|
|
await expect(page.getByRole('heading', { level: 1 })).toHaveText(/GlowTrack/i);
|
|
|
|
// Step 1: Ensure we have a month view rendered with tiles
|
|
const gridContainer = page.locator('[data-testid="wellbeing-grid"]');
|
|
await expect(gridContainer).toBeVisible();
|
|
|
|
// Ensure at least 28-31 tiles are visible (month view)
|
|
const tiles = page.locator('[data-testid="day-tile"]');
|
|
const tileCount = await tiles.count();
|
|
expect(tileCount).toBeGreaterThanOrEqual(28); // At least a month's worth
|
|
|
|
// Step 2: Add some data to a few tiles to make the export meaningful
|
|
// This creates visual content that should be captured in PNG
|
|
await tiles.first().click();
|
|
|
|
// Set mood if mood controls are available
|
|
const hueInput = page.locator('[data-testid="mood-hue-slider"]');
|
|
if (await hueInput.isVisible()) {
|
|
await hueInput.fill('180'); // Blue mood
|
|
}
|
|
|
|
// Add positive habit if controls are available
|
|
const addPositive = page.locator('[data-testid="add-positive-habit"]');
|
|
if (await addPositive.isVisible()) {
|
|
await addPositive.click();
|
|
}
|
|
|
|
// Close any editor modal/overlay
|
|
const closeButton = page.locator('[data-testid="close-day-editor"]');
|
|
if (await closeButton.isVisible()) {
|
|
await closeButton.click();
|
|
} else {
|
|
// Try clicking outside to close
|
|
await gridContainer.click({ position: { x: 10, y: 10 } });
|
|
}
|
|
|
|
// Step 3: Wait for any visual updates to complete
|
|
await page.waitForTimeout(500);
|
|
|
|
// Step 4: Trigger PNG export
|
|
const exportButton = page.locator('[data-testid="export-png-button"]');
|
|
|
|
// Start timing the export operation
|
|
const startTime = Date.now();
|
|
|
|
// Handle the download that should be triggered by PNG export
|
|
const downloadPromise = page.waitForEvent('download', { timeout: 10000 });
|
|
|
|
if (await exportButton.isVisible()) {
|
|
await exportButton.click();
|
|
|
|
// Wait for download to complete
|
|
const download = await downloadPromise;
|
|
const endTime = Date.now();
|
|
const exportDuration = endTime - startTime;
|
|
|
|
// Step 5: Validate the PNG export meets budgets
|
|
|
|
// Time budget: Export should complete within 5 seconds for a month view
|
|
expect(exportDuration).toBeLessThan(5000);
|
|
|
|
// Size budget: Get the download and check file size
|
|
const path = await download.path();
|
|
if (path) {
|
|
const fs = await import('fs');
|
|
const stats = fs.statSync(path);
|
|
|
|
// Size budget: PNG should be reasonable size (not too small, not too large)
|
|
// Minimum: 1KB (should have actual content)
|
|
// Maximum: 5MB (should be reasonable for a month grid)
|
|
expect(stats.size).toBeGreaterThan(1024); // > 1KB
|
|
expect(stats.size).toBeLessThan(5 * 1024 * 1024); // < 5MB
|
|
|
|
// Verify it's actually a PNG file by checking magic bytes
|
|
const buffer = fs.readFileSync(path);
|
|
const pngSignature = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
|
|
expect(buffer.subarray(0, 8)).toEqual(pngSignature);
|
|
|
|
// Suggested filename should contain date/timestamp
|
|
const suggestedFilename = download.suggestedFilename();
|
|
expect(suggestedFilename).toMatch(/\.png$/i);
|
|
expect(suggestedFilename).toMatch(/glowtrack|grid|export/i);
|
|
}
|
|
} else {
|
|
// If export button not yet implemented, we expect this test to fail
|
|
// This aligns with TDD approach - test should fail until implementation exists
|
|
throw new Error('PNG export button not found - export functionality not yet implemented');
|
|
}
|
|
});
|
|
|
|
test('PNG export handles canvas rendering correctly', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
// This test focuses on the canvas/toBlob functionality specifically
|
|
const gridContainer = page.locator('[data-testid="wellbeing-grid"]');
|
|
await expect(gridContainer).toBeVisible();
|
|
|
|
// Check if canvas element is present (renderer should use Canvas for tiles)
|
|
const canvas = page.locator('canvas');
|
|
|
|
if (await canvas.count() > 0) {
|
|
// Verify canvas has reasonable dimensions for a month grid
|
|
const canvasElement = canvas.first();
|
|
const boundingBox = await canvasElement.boundingBox();
|
|
|
|
if (boundingBox) {
|
|
expect(boundingBox.width).toBeGreaterThan(200); // Reasonable minimum width
|
|
expect(boundingBox.height).toBeGreaterThan(100); // Reasonable minimum height
|
|
|
|
// Verify canvas has actual content (not blank)
|
|
// This is a proxy test - actual implementation would use toBlob
|
|
const canvasData = await page.evaluate(() => {
|
|
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
|
|
if (!canvas) return null;
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
if (!ctx) return null;
|
|
|
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
const data = imageData.data;
|
|
|
|
// Check if canvas has any non-transparent pixels
|
|
let hasContent = false;
|
|
for (let i = 3; i < data.length; i += 4) { // Check alpha channel
|
|
if (data[i] > 0) {
|
|
hasContent = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
width: canvas.width,
|
|
height: canvas.height,
|
|
hasContent
|
|
};
|
|
});
|
|
|
|
if (canvasData) {
|
|
expect(canvasData.hasContent).toBe(true);
|
|
}
|
|
}
|
|
} else {
|
|
// Canvas not yet implemented - this is expected in TDD approach
|
|
console.log('Canvas element not found - renderer not yet implemented');
|
|
}
|
|
});
|
|
|
|
test('PNG export respects screen resolution and quality settings', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
const gridContainer = page.locator('[data-testid="wellbeing-grid"]');
|
|
await expect(gridContainer).toBeVisible();
|
|
|
|
// Test different export quality settings if available
|
|
const qualitySelector = page.locator('[data-testid="export-quality-selector"]');
|
|
const exportButton = page.locator('[data-testid="export-png-button"]');
|
|
|
|
if (await qualitySelector.isVisible() && await exportButton.isVisible()) {
|
|
// Test high quality export
|
|
await qualitySelector.selectOption('high');
|
|
|
|
const downloadPromise = page.waitForEvent('download', { timeout: 10000 });
|
|
await exportButton.click();
|
|
|
|
const download = await downloadPromise;
|
|
const path = await download.path();
|
|
|
|
if (path) {
|
|
const fs = await import('fs');
|
|
const stats = fs.statSync(path);
|
|
|
|
// High quality should produce larger files
|
|
expect(stats.size).toBeGreaterThan(2048); // > 2KB for high quality
|
|
}
|
|
} else {
|
|
// Export quality controls not yet implemented
|
|
console.log('Export quality controls not found - advanced export options not yet implemented');
|
|
}
|
|
});
|
|
});
|