Add copy to clipboard and save to pc toggle.
* escaped node modules from vcs * feat: added copy to clipboard feature * feat: added tests * feat: added feature spec * feat: added toggle for save to pc * feat: added tests * refactor: change toggle from '?' button to the 'switch' component * Fix failing tests * Update gitignore and remove unnecessary files * Remove tooltips because they are unnecessary --------- Co-authored-by: Joey Yakimowich-Payne <jyapayne@gmail.com>
This commit is contained in:
parent
feba59bb8d
commit
a018c36d67
12 changed files with 7780 additions and 22 deletions
414
tests/preference-persistence.test.js
Normal file
414
tests/preference-persistence.test.js
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
/**
|
||||
* Test suite for clipboard preference persistence across browser sessions
|
||||
* Tests storage, retrieval, and default behavior of clipboard preferences
|
||||
*/
|
||||
|
||||
// Mock Chrome APIs
|
||||
global.chrome = {
|
||||
storage: {
|
||||
sync: {
|
||||
get: jest.fn(),
|
||||
set: jest.fn()
|
||||
}
|
||||
},
|
||||
runtime: {
|
||||
sendMessage: jest.fn()
|
||||
},
|
||||
tabs: {
|
||||
query: jest.fn(),
|
||||
sendMessage: jest.fn()
|
||||
}
|
||||
};
|
||||
|
||||
// Mock DOM APIs
|
||||
global.document = {
|
||||
createElement: jest.fn(() => ({
|
||||
style: {},
|
||||
classList: { add: jest.fn(), remove: jest.fn() },
|
||||
appendChild: jest.fn(),
|
||||
click: jest.fn(),
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
id: '',
|
||||
checked: false,
|
||||
value: ''
|
||||
})),
|
||||
head: { appendChild: jest.fn() },
|
||||
body: { appendChild: jest.fn() },
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
querySelectorAll: jest.fn(() => []),
|
||||
getElementById: jest.fn()
|
||||
};
|
||||
|
||||
global.window = {
|
||||
close: jest.fn(),
|
||||
setTimeout: jest.fn((fn) => fn())
|
||||
};
|
||||
|
||||
describe('Clipboard Preference Persistence', () => {
|
||||
let mockClipboardToggle;
|
||||
let mockBackgroundSelect;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Mock DOM elements
|
||||
mockClipboardToggle = {
|
||||
checked: false,
|
||||
addEventListener: jest.fn()
|
||||
};
|
||||
|
||||
mockBackgroundSelect = {
|
||||
value: 'black',
|
||||
addEventListener: jest.fn()
|
||||
};
|
||||
|
||||
global.document.getElementById = jest.fn((id) => {
|
||||
switch (id) {
|
||||
case 'clipboard-toggle':
|
||||
return mockClipboardToggle;
|
||||
case 'background-select':
|
||||
return mockBackgroundSelect;
|
||||
case 'start-selector':
|
||||
return { addEventListener: jest.fn(), style: { display: 'block' } };
|
||||
case 'stop-selector':
|
||||
return { addEventListener: jest.fn(), style: { display: 'none' } };
|
||||
case 'status':
|
||||
return { textContent: '', className: '', style: { display: 'none' } };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('should load default clipboard preference when no saved preference exists', async () => {
|
||||
// Mock storage returning no saved preferences
|
||||
global.chrome.storage.sync.get.mockResolvedValue({});
|
||||
|
||||
// Simulate popup script loading
|
||||
const popupScript = `
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const clipboardToggle = document.getElementById('clipboard-toggle');
|
||||
const saved = await chrome.storage.sync.get(['backgroundPreference', 'copyToClipboard']);
|
||||
clipboardToggle.checked = saved.copyToClipboard !== undefined ? saved.copyToClipboard : true;
|
||||
});
|
||||
`;
|
||||
|
||||
// Execute the popup script logic
|
||||
await new Promise(resolve => {
|
||||
global.document.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'DOMContentLoaded') {
|
||||
callback();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
eval(popupScript);
|
||||
});
|
||||
|
||||
expect(global.chrome.storage.sync.get).toHaveBeenCalledWith(['backgroundPreference', 'copyToClipboard']);
|
||||
expect(mockClipboardToggle.checked).toBe(true); // Default should be true
|
||||
});
|
||||
|
||||
test('should load saved clipboard preference when it exists', async () => {
|
||||
// Mock storage returning saved preferences
|
||||
global.chrome.storage.sync.get.mockResolvedValue({
|
||||
backgroundPreference: 'white',
|
||||
copyToClipboard: false
|
||||
});
|
||||
|
||||
// Simulate popup script loading
|
||||
const popupScript = `
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const clipboardToggle = document.getElementById('clipboard-toggle');
|
||||
const saved = await chrome.storage.sync.get(['backgroundPreference', 'copyToClipboard']);
|
||||
clipboardToggle.checked = saved.copyToClipboard !== undefined ? saved.copyToClipboard : true;
|
||||
});
|
||||
`;
|
||||
|
||||
// Execute the popup script logic
|
||||
await new Promise(resolve => {
|
||||
global.document.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'DOMContentLoaded') {
|
||||
callback();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
eval(popupScript);
|
||||
});
|
||||
|
||||
expect(mockClipboardToggle.checked).toBe(false); // Should load saved value
|
||||
});
|
||||
|
||||
test('should save clipboard preference when toggle is changed', async () => {
|
||||
let changeCallback;
|
||||
mockClipboardToggle.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'change') {
|
||||
changeCallback = callback;
|
||||
}
|
||||
});
|
||||
|
||||
// Simulate popup script initialization
|
||||
const popupScript = `
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const clipboardToggle = document.getElementById('clipboard-toggle');
|
||||
clipboardToggle.addEventListener('change', () => {
|
||||
chrome.storage.sync.set({ copyToClipboard: clipboardToggle.checked });
|
||||
});
|
||||
});
|
||||
`;
|
||||
|
||||
// Execute the popup script logic
|
||||
await new Promise(resolve => {
|
||||
global.document.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'DOMContentLoaded') {
|
||||
callback();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
eval(popupScript);
|
||||
});
|
||||
|
||||
// Simulate user toggling the checkbox
|
||||
mockClipboardToggle.checked = true;
|
||||
changeCallback();
|
||||
|
||||
expect(global.chrome.storage.sync.set).toHaveBeenCalledWith({ copyToClipboard: true });
|
||||
});
|
||||
|
||||
test('should persist clipboard preference across multiple sessions', async () => {
|
||||
// Simulate first session - user enables clipboard
|
||||
global.chrome.storage.sync.get.mockResolvedValueOnce({});
|
||||
|
||||
let firstSessionChangeCallback;
|
||||
mockClipboardToggle.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'change') {
|
||||
firstSessionChangeCallback = callback;
|
||||
}
|
||||
});
|
||||
|
||||
// First session initialization
|
||||
await new Promise(resolve => {
|
||||
global.document.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'DOMContentLoaded') {
|
||||
callback();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
const popupScript = `
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const clipboardToggle = document.getElementById('clipboard-toggle');
|
||||
const saved = await chrome.storage.sync.get(['backgroundPreference', 'copyToClipboard']);
|
||||
clipboardToggle.checked = saved.copyToClipboard !== undefined ? saved.copyToClipboard : true;
|
||||
|
||||
clipboardToggle.addEventListener('change', () => {
|
||||
chrome.storage.sync.set({ copyToClipboard: clipboardToggle.checked });
|
||||
});
|
||||
});
|
||||
`;
|
||||
eval(popupScript);
|
||||
});
|
||||
|
||||
// User disables clipboard in first session
|
||||
mockClipboardToggle.checked = false;
|
||||
firstSessionChangeCallback();
|
||||
|
||||
expect(global.chrome.storage.sync.set).toHaveBeenCalledWith({ copyToClipboard: false });
|
||||
|
||||
// Simulate second session - should load the saved preference
|
||||
jest.clearAllMocks();
|
||||
global.chrome.storage.sync.get.mockResolvedValueOnce({
|
||||
copyToClipboard: false
|
||||
});
|
||||
|
||||
// Reset mock element
|
||||
mockClipboardToggle.checked = true; // Reset to opposite of expected
|
||||
|
||||
// Second session initialization
|
||||
await new Promise(resolve => {
|
||||
global.document.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'DOMContentLoaded') {
|
||||
callback();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
const popupScript = `
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const clipboardToggle = document.getElementById('clipboard-toggle');
|
||||
const saved = await chrome.storage.sync.get(['backgroundPreference', 'copyToClipboard']);
|
||||
clipboardToggle.checked = saved.copyToClipboard !== undefined ? saved.copyToClipboard : true;
|
||||
});
|
||||
`;
|
||||
eval(popupScript);
|
||||
});
|
||||
|
||||
expect(mockClipboardToggle.checked).toBe(false); // Should load saved preference from first session
|
||||
});
|
||||
|
||||
test('should handle storage errors gracefully', async () => {
|
||||
// Mock storage error
|
||||
global.chrome.storage.sync.get.mockRejectedValue(new Error('Storage error'));
|
||||
|
||||
let errorOccurred = false;
|
||||
|
||||
try {
|
||||
const popupScript = `
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
try {
|
||||
const clipboardToggle = document.getElementById('clipboard-toggle');
|
||||
const saved = await chrome.storage.sync.get(['backgroundPreference', 'copyToClipboard']);
|
||||
clipboardToggle.checked = saved.copyToClipboard !== undefined ? saved.copyToClipboard : true;
|
||||
} catch (error) {
|
||||
// Should fall back to default behavior
|
||||
const clipboardToggle = document.getElementById('clipboard-toggle');
|
||||
clipboardToggle.checked = true; // Default value
|
||||
}
|
||||
});
|
||||
`;
|
||||
|
||||
await new Promise(resolve => {
|
||||
global.document.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'DOMContentLoaded') {
|
||||
callback();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
eval(popupScript);
|
||||
});
|
||||
} catch (error) {
|
||||
errorOccurred = true;
|
||||
}
|
||||
|
||||
expect(errorOccurred).toBe(false); // Should handle error gracefully
|
||||
expect(mockClipboardToggle.checked).toBe(true); // Should fall back to default
|
||||
});
|
||||
|
||||
test('should save both background and clipboard preferences independently', async () => {
|
||||
let clipboardChangeCallback;
|
||||
let backgroundChangeCallback;
|
||||
|
||||
mockClipboardToggle.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'change') {
|
||||
clipboardChangeCallback = callback;
|
||||
}
|
||||
});
|
||||
|
||||
mockBackgroundSelect.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'change') {
|
||||
backgroundChangeCallback = callback;
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize popup script
|
||||
const popupScript = `
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const backgroundSelect = document.getElementById('background-select');
|
||||
const clipboardToggle = document.getElementById('clipboard-toggle');
|
||||
|
||||
backgroundSelect.addEventListener('change', () => {
|
||||
chrome.storage.sync.set({ backgroundPreference: backgroundSelect.value });
|
||||
});
|
||||
|
||||
clipboardToggle.addEventListener('change', () => {
|
||||
chrome.storage.sync.set({ copyToClipboard: clipboardToggle.checked });
|
||||
});
|
||||
});
|
||||
`;
|
||||
|
||||
await new Promise(resolve => {
|
||||
global.document.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'DOMContentLoaded') {
|
||||
callback();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
eval(popupScript);
|
||||
});
|
||||
|
||||
// Change background preference
|
||||
mockBackgroundSelect.value = 'transparent';
|
||||
backgroundChangeCallback();
|
||||
|
||||
expect(global.chrome.storage.sync.set).toHaveBeenCalledWith({ backgroundPreference: 'transparent' });
|
||||
|
||||
// Change clipboard preference
|
||||
mockClipboardToggle.checked = false;
|
||||
clipboardChangeCallback();
|
||||
|
||||
expect(global.chrome.storage.sync.set).toHaveBeenCalledWith({ copyToClipboard: false });
|
||||
|
||||
// Verify both calls were made independently
|
||||
expect(global.chrome.storage.sync.set).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test('should include clipboard preference in message to content script', async () => {
|
||||
// Mock tab query and message sending
|
||||
global.chrome.tabs.query.mockResolvedValue([{ id: 123, url: 'https://example.com' }]);
|
||||
global.chrome.tabs.sendMessage.mockResolvedValue();
|
||||
|
||||
let startButtonCallback;
|
||||
const mockStartButton = {
|
||||
addEventListener: jest.fn((event, callback) => {
|
||||
if (event === 'click') {
|
||||
startButtonCallback = callback;
|
||||
}
|
||||
}),
|
||||
style: { display: 'block' }
|
||||
};
|
||||
|
||||
global.document.getElementById = jest.fn((id) => {
|
||||
switch (id) {
|
||||
case 'start-selector':
|
||||
return mockStartButton;
|
||||
case 'clipboard-toggle':
|
||||
return { checked: true }; // Clipboard enabled
|
||||
case 'background-select':
|
||||
return { value: 'white' };
|
||||
case 'status':
|
||||
return { textContent: '', className: '', style: { display: 'none' } };
|
||||
default:
|
||||
return mockClipboardToggle;
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize popup script
|
||||
const popupScript = `
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const startBtn = document.getElementById('start-selector');
|
||||
const backgroundSelect = document.getElementById('background-select');
|
||||
const clipboardToggle = document.getElementById('clipboard-toggle');
|
||||
|
||||
startBtn.addEventListener('click', async () => {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
|
||||
await chrome.tabs.sendMessage(tab.id, {
|
||||
action: 'startSelector',
|
||||
background: backgroundSelect.value,
|
||||
copyToClipboard: clipboardToggle.checked
|
||||
});
|
||||
});
|
||||
});
|
||||
`;
|
||||
|
||||
await new Promise(resolve => {
|
||||
global.document.addEventListener = jest.fn((event, callback) => {
|
||||
if (event === 'DOMContentLoaded') {
|
||||
callback();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
eval(popupScript);
|
||||
});
|
||||
|
||||
// Simulate start button click
|
||||
await startButtonCallback();
|
||||
|
||||
expect(global.chrome.tabs.sendMessage).toHaveBeenCalledWith(123, {
|
||||
action: 'startSelector',
|
||||
background: 'white',
|
||||
copyToClipboard: true
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue