You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ghost/apps/admin-x-activitypub/test/unit/utils/screenshot.test.ts

192 lines
6.4 KiB

import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
import html2canvas from 'html2canvas-objectfit-fix';
import {takeScreenshot} from '../../../src/utils/screenshot';
// Mock html2canvas
vi.mock('html2canvas-objectfit-fix');
// Mock DOM methods
Object.defineProperty(window, 'URL', {
value: {
createObjectURL: vi.fn(),
revokeObjectURL: vi.fn()
},
writable: true
});
describe('takeScreenshot', function () {
let mockElement: HTMLElement;
let mockCanvas: HTMLCanvasElement;
let mockBlob: Blob;
beforeEach(function () {
// Create mock element
mockElement = document.createElement('div');
// Create mock canvas
mockCanvas = document.createElement('canvas');
mockBlob = new Blob(['fake-image-data'], {type: 'image/png'});
// Mock canvas.toBlob method
mockCanvas.toBlob = vi.fn();
// Mock html2canvas to return our mock canvas
vi.mocked(html2canvas).mockResolvedValue(mockCanvas);
// Mock URL methods
vi.mocked(window.URL.createObjectURL).mockReturnValue('blob:fake-url');
vi.mocked(window.URL.revokeObjectURL).mockImplementation(() => {});
// Mock DOM methods
const originalCreateElement = document.createElement.bind(document);
vi.spyOn(document, 'createElement').mockImplementation((tagName: string) => {
if (tagName === 'a') {
const link = {
href: '',
download: '',
click: vi.fn()
} as unknown as HTMLAnchorElement;
return link;
}
return originalCreateElement(tagName);
});
vi.spyOn(document.body, 'appendChild').mockImplementation(() => undefined as unknown as Node);
vi.spyOn(document.body, 'removeChild').mockImplementation(() => undefined as unknown as Node);
// Mock console.error
vi.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(function () {
vi.clearAllMocks();
});
it('calls html2canvas with correct default options', async function () {
// Mock successful toBlob callback
vi.mocked(mockCanvas.toBlob).mockImplementation((callback) => {
callback(mockBlob);
});
await takeScreenshot(mockElement);
expect(html2canvas).toHaveBeenCalledWith(mockElement, {
backgroundColor: null,
scale: 2,
logging: false,
useCORS: true,
allowTaint: true,
imageTimeout: 0
});
});
it('calls html2canvas with custom options', async function () {
// Mock successful toBlob callback
vi.mocked(mockCanvas.toBlob).mockImplementation((callback) => {
callback(mockBlob);
});
await takeScreenshot(mockElement, {
scale: 3,
backgroundColor: '#ffffff'
});
expect(html2canvas).toHaveBeenCalledWith(mockElement, {
backgroundColor: '#ffffff',
scale: 3,
logging: false,
useCORS: true,
allowTaint: true,
imageTimeout: 0
});
});
it('creates anchor element and triggers download on successful blob creation', async function () {
const mockLink = {
href: '',
download: '',
click: vi.fn()
};
vi.mocked(document.createElement).mockReturnValue(mockLink as unknown as HTMLAnchorElement);
vi.mocked(mockCanvas.toBlob).mockImplementation((callback) => {
callback(mockBlob);
});
await takeScreenshot(mockElement, {filename: 'test-screenshot.png'});
expect(window.URL.createObjectURL).toHaveBeenCalledWith(mockBlob);
expect(mockLink.href).toBe('blob:fake-url');
expect(mockLink.download).toBe('test-screenshot.png');
expect(document.body.appendChild).toHaveBeenCalledWith(mockLink);
expect(mockLink.click).toHaveBeenCalled();
expect(document.body.removeChild).toHaveBeenCalledWith(mockLink);
expect(window.URL.revokeObjectURL).toHaveBeenCalledWith('blob:fake-url');
});
it('uses default filename when none provided', async function () {
const mockLink = {
href: '',
download: '',
click: vi.fn()
};
vi.mocked(document.createElement).mockReturnValue(mockLink as unknown as HTMLAnchorElement);
vi.mocked(mockCanvas.toBlob).mockImplementation((callback) => {
callback(mockBlob);
});
// Mock Date.now for consistent filename
const mockNow = 1234567890;
vi.spyOn(Date, 'now').mockReturnValue(mockNow);
await takeScreenshot(mockElement);
expect(mockLink.download).toBe('screenshot-1234567890.png');
});
it('logs error and throws when html2canvas throws exception', async function () {
const error = new Error('html2canvas failed');
vi.mocked(html2canvas).mockRejectedValue(error);
await expect(takeScreenshot(mockElement)).rejects.toThrow('html2canvas failed');
// eslint-disable-next-line no-console
expect(console.error).toHaveBeenCalledWith('Failed to take screenshot:', error);
});
it('handles null blob from toBlob callback', async function () {
vi.mocked(mockCanvas.toBlob).mockImplementation((callback) => {
callback(null);
});
await expect(takeScreenshot(mockElement)).rejects.toThrow('Failed to create blob from canvas');
});
it('handles error during blob processing', async function () {
const error = new Error('URL creation failed');
vi.mocked(window.URL.createObjectURL).mockImplementation(() => {
throw error;
});
vi.mocked(mockCanvas.toBlob).mockImplementation((callback) => {
callback(mockBlob);
});
await expect(takeScreenshot(mockElement)).rejects.toThrow('URL creation failed');
});
it('handles error during DOM manipulation', async function () {
const error = new Error('DOM manipulation failed');
vi.mocked(document.body.appendChild).mockImplementation(() => {
throw error;
});
vi.mocked(mockCanvas.toBlob).mockImplementation((callback) => {
callback(mockBlob);
});
await expect(takeScreenshot(mockElement)).rejects.toThrow('DOM manipulation failed');
});
});