Fix failing tests

This commit is contained in:
Joey Yakimowich-Payne 2025-08-13 13:08:06 -06:00
commit 51d2595d49
2 changed files with 126 additions and 62 deletions

View file

@ -163,6 +163,10 @@ describe('Save to PC and Clipboard Combinations', () => {
// Reset mocks // Reset mocks
mockCreateElement.mockClear(); mockCreateElement.mockClear();
mockCreateElement.mockReturnValue(mockLink); mockCreateElement.mockReturnValue(mockLink);
// Ensure our mock is used even if the test environment adjusted document
if (global.document) {
global.document.createElement = mockCreateElement;
}
mockLink.click.mockClear(); mockLink.click.mockClear();
// Setup clipboard API mocks // Setup clipboard API mocks

View file

@ -3,6 +3,32 @@
* Provides common mocks and utilities for all test files * Provides common mocks and utilities for all test files
*/ */
// Keep global and window navigator in sync regardless of direct reassignment
(() => {
try {
let navigatorStore = (typeof global !== 'undefined' && global.navigator) || (typeof window !== 'undefined' && window.navigator) || {};
if (typeof global !== 'undefined') {
Object.defineProperty(global, 'navigator', {
configurable: true,
get: () => navigatorStore,
set: (val) => {
navigatorStore = val || {};
if (typeof window !== 'undefined') {
try { window.navigator = navigatorStore; } catch (_) {}
}
}
});
}
if (typeof window !== 'undefined') {
Object.defineProperty(window, 'navigator', {
configurable: true,
get: () => navigatorStore,
set: (val) => { navigatorStore = val || {}; }
});
}
} catch (_) {}
})();
// Global test utilities // Global test utilities
global.createMockBlob = (data = 'mock data', type = 'image/png', size = 1024) => { global.createMockBlob = (data = 'mock data', type = 'image/png', size = 1024) => {
const blob = new Blob([data], { type }); const blob = new Blob([data], { type });
@ -56,30 +82,39 @@ global.createMockElement = (options = {}) => {
// Mock Chrome extension APIs consistently // Mock Chrome extension APIs consistently
global.mockChromeAPIs = () => { global.mockChromeAPIs = () => {
const existingChrome = global.chrome || {};
const existingStorage = (existingChrome.storage && existingChrome.storage.sync) || {};
const existingRuntime = existingChrome.runtime || {};
const existingTabs = existingChrome.tabs || {};
global.chrome = { global.chrome = {
storage: { storage: {
sync: { sync: {
get: jest.fn().mockResolvedValue({}), get: existingStorage.get || jest.fn().mockResolvedValue({}),
set: jest.fn().mockResolvedValue() set: existingStorage.set || jest.fn().mockResolvedValue()
} }
}, },
runtime: { runtime: {
sendMessage: jest.fn().mockResolvedValue(), sendMessage: existingRuntime.sendMessage || jest.fn().mockResolvedValue(),
onMessage: { onMessage: {
addListener: jest.fn() addListener: (existingRuntime.onMessage && existingRuntime.onMessage.addListener) || jest.fn()
} }
}, },
tabs: { tabs: {
query: jest.fn().mockResolvedValue([{ id: 123, url: 'https://example.com' }]), query: existingTabs.query || jest.fn().mockResolvedValue([{ id: 123, url: 'https://example.com' }]),
sendMessage: jest.fn().mockResolvedValue() sendMessage: existingTabs.sendMessage || jest.fn().mockResolvedValue()
} }
}; };
}; };
// Mock DOM APIs consistently // Mock DOM APIs consistently
global.mockDOMAPIs = () => { global.mockDOMAPIs = () => {
global.document = { // Document
createElement: jest.fn(() => ({ const existingDocument = global.document || {};
const existingCreateElement = existingDocument.createElement;
global.document = existingDocument;
if (!global.document.createElement) {
global.document.createElement = jest.fn(() => ({
style: {}, style: {},
classList: { add: jest.fn(), remove: jest.fn() }, classList: { add: jest.fn(), remove: jest.fn() },
appendChild: jest.fn(), appendChild: jest.fn(),
@ -92,80 +127,105 @@ global.mockDOMAPIs = () => {
className: '', className: '',
checked: false, checked: false,
value: '' value: ''
})), }));
head: { appendChild: jest.fn() }, }
body: { appendChild: jest.fn() }, if (!global.document.head) global.document.head = { appendChild: jest.fn() };
addEventListener: jest.fn(), if (!global.document.body) global.document.body = { appendChild: jest.fn() };
removeEventListener: jest.fn(), if (!global.document.addEventListener) global.document.addEventListener = jest.fn();
querySelectorAll: jest.fn(() => []), if (!global.document.removeEventListener) global.document.removeEventListener = jest.fn();
getElementById: jest.fn(() => null) if (!global.document.querySelectorAll) global.document.querySelectorAll = jest.fn(() => []);
}; if (!global.document.getElementById) global.document.getElementById = jest.fn(() => null);
global.window = { // Window
getComputedStyle: jest.fn(() => ({})), const existingWindow = global.window || {};
devicePixelRatio: 2, global.window = existingWindow;
isSecureContext: true, // Ensure a shared navigator reference exists early
CSS: { escape: jest.fn(str => str) }, if (!global.navigator) global.navigator = (existingWindow && existingWindow.navigator) || {};
setTimeout: jest.fn((fn, delay) => { if (!global.window.navigator) global.window.navigator = global.navigator;
if (!global.window.getComputedStyle) global.window.getComputedStyle = jest.fn(() => ({}));
if (typeof global.window.devicePixelRatio === 'undefined') global.window.devicePixelRatio = 2;
if (typeof global.window.isSecureContext === 'undefined') global.window.isSecureContext = true;
if (!global.window.CSS) global.window.CSS = { escape: jest.fn(str => str) };
if (!global.window.setTimeout) {
global.window.setTimeout = jest.fn((fn, delay) => {
if (delay === 0) fn(); if (delay === 0) fn();
return 1; return 1;
}), });
clearTimeout: jest.fn(), }
close: jest.fn() if (!global.window.clearTimeout) global.window.clearTimeout = jest.fn();
}; if (!global.window.close) global.window.close = jest.fn();
}; };
// Mock clipboard APIs with different scenarios // Mock clipboard APIs with different scenarios
global.mockClipboardAPI = (scenario = 'supported') => { global.mockClipboardAPI = (scenario = 'supported') => {
switch (scenario) { switch (scenario) {
case 'supported': case 'supported': {
global.navigator = { const existingNavigator = global.navigator || {};
clipboard: { const existingClipboard = existingNavigator.clipboard || {};
write: jest.fn().mockResolvedValue() if (!existingClipboard.write || !jest.isMockFunction?.(existingClipboard.write)) {
} existingClipboard.write = jest.fn().mockResolvedValue();
}; }
global.window.ClipboardItem = jest.fn(); const mergedNavigator = { ...existingNavigator, clipboard: existingClipboard };
global.window.isSecureContext = true; global.navigator = mergedNavigator;
if (!global.window) global.window = {};
global.window.navigator = mergedNavigator;
if (!global.window) global.window = {};
if (!global.window.ClipboardItem) global.window.ClipboardItem = jest.fn();
if (typeof global.window.isSecureContext === 'undefined') global.window.isSecureContext = true;
break; break;
}
case 'no-clipboard': case 'no-clipboard':
global.navigator = {}; global.navigator = {};
if (!global.window) global.window = {};
global.window.navigator = global.navigator;
break; break;
case 'no-clipboarditem': {
case 'no-clipboarditem': const existingNavigator = global.navigator || {};
global.navigator = { const clipboard = existingNavigator.clipboard || { write: jest.fn() };
clipboard: { write: jest.fn() } const mergedNavigator = { ...existingNavigator, clipboard };
}; global.navigator = mergedNavigator;
if (!global.window) global.window = {};
global.window.navigator = mergedNavigator;
if (!global.window) global.window = {};
global.window.ClipboardItem = undefined; global.window.ClipboardItem = undefined;
break; break;
}
case 'insecure-context': case 'insecure-context': {
global.navigator = { const existingNavigator = global.navigator || {};
clipboard: { write: jest.fn() } const clipboard = existingNavigator.clipboard || { write: jest.fn() };
}; const mergedNavigator = { ...existingNavigator, clipboard };
global.window.ClipboardItem = jest.fn(); global.navigator = mergedNavigator;
if (!global.window) global.window = {};
global.window.navigator = mergedNavigator;
if (!global.window) global.window = {};
global.window.ClipboardItem = global.window.ClipboardItem || jest.fn();
global.window.isSecureContext = false; global.window.isSecureContext = false;
break; break;
}
case 'permission-denied': case 'permission-denied': {
global.navigator = { const existingNavigator = global.navigator || {};
clipboard: { const clipboard = { write: jest.fn().mockRejectedValue(new DOMException('Permission denied', 'NotAllowedError')) };
write: jest.fn().mockRejectedValue(new DOMException('Permission denied', 'NotAllowedError')) const mergedNavigator = { ...existingNavigator, clipboard };
} global.navigator = mergedNavigator;
}; if (!global.window) global.window = {};
global.window.ClipboardItem = jest.fn(); global.window.navigator = mergedNavigator;
if (!global.window) global.window = {};
global.window.ClipboardItem = global.window.ClipboardItem || jest.fn();
global.window.isSecureContext = true; global.window.isSecureContext = true;
break; break;
}
case 'security-error': case 'security-error': {
global.navigator = { const existingNavigator = global.navigator || {};
clipboard: { const clipboard = { write: jest.fn().mockRejectedValue(new DOMException('Security error', 'SecurityError')) };
write: jest.fn().mockRejectedValue(new DOMException('Security error', 'SecurityError')) const mergedNavigator = { ...existingNavigator, clipboard };
} global.navigator = mergedNavigator;
}; if (!global.window) global.window = {};
global.window.ClipboardItem = jest.fn(); global.window.navigator = mergedNavigator;
if (!global.window) global.window = {};
global.window.ClipboardItem = global.window.ClipboardItem || jest.fn();
global.window.isSecureContext = true; global.window.isSecureContext = true;
break; break;
}
} }
}; };