573 lines
22 KiB
JavaScript
573 lines
22 KiB
JavaScript
// Note: This code assumes you have Mocha, Chai, and Sinon loaded in your test environment
|
|
// along with jQuery and html2canvas. You'll also need to include your screen capture plugin.
|
|
|
|
describe('Screen Capture Plugin', function() {
|
|
var $button, sandbox, originalBodyHTML;
|
|
|
|
beforeEach(function() {
|
|
originalBodyHTML = document.body.innerHTML;
|
|
// Create a test element and initialize the plugin
|
|
$button = $('.' + flagCaptureScreen);
|
|
$button.screenshotButton();
|
|
// $button = $button.find('button');
|
|
|
|
// Create a sinon sandbox for mocking
|
|
sandbox = sinon.createSandbox();
|
|
});
|
|
|
|
afterEach(function() {
|
|
// Clean up
|
|
sandbox.restore();
|
|
});
|
|
|
|
after(function() {
|
|
document.body.innerHTML = originalBodyHTML;
|
|
});
|
|
|
|
// 1. Initialization and Setup
|
|
describe('1. Initialization and Setup', function() {
|
|
it('a. should initialize correctly on a given DOM element', function() {
|
|
expect($button).to.exist;
|
|
});
|
|
|
|
it('b. should create the button with correct text', function() {
|
|
expect($button.text()).to.equal('Capture Screen');
|
|
});
|
|
|
|
it('c. should make the button visible on the page', function() {
|
|
expect($button.is(':visible')).to.be.true;
|
|
});
|
|
});
|
|
|
|
// 2. Button Functionality
|
|
describe('2. Button Functionality', function() {
|
|
it('a. should trigger screenshot process on click', function(done) {
|
|
// const canvasStub = sinon.stub(document.createElement('canvas'));
|
|
sinon.stub(window, 'html2canvas').resolves(document.createElement('canvas'));
|
|
// await waitForClick($button);
|
|
$button.click();
|
|
setTimeout(() => {
|
|
expect(html2canvas.called).to.be.true;
|
|
expect(html2canvas.callCount).to.equal(1);
|
|
html2canvas.restore();
|
|
done();
|
|
}, 1000);
|
|
});
|
|
|
|
it('b. should hide button during screenshot process',
|
|
/*
|
|
async function() {
|
|
sinon.stub(window, 'html2canvas').resolves(document.createElement('canvas'));
|
|
let resolve;
|
|
const promise = new Promise((r) => resolve = r);
|
|
function handleButtonClick() {
|
|
resolve();
|
|
$button.off('click', handleButtonClick);
|
|
}
|
|
$button.on("click", function() {
|
|
handleButtonClick();
|
|
});
|
|
$button.click();
|
|
expect($button.hasClass(flagIsHidden)).to.be.true;
|
|
html2canvas.restore();
|
|
}
|
|
*/
|
|
function(done) {
|
|
sinon.stub(window, 'html2canvas').resolves(document.createElement('canvas'));
|
|
$button.click();
|
|
expect($button.hasClass(flagIsHidden)).to.be.true;
|
|
html2canvas.restore();
|
|
done();
|
|
}
|
|
);
|
|
|
|
it('c. should show button after screenshot is taken', function(done) {
|
|
sinon.stub(window, 'html2canvas').resolves(document.createElement('canvas'));
|
|
/*
|
|
await waitForClick($button).then(() => {
|
|
setTimeout(() => {
|
|
expect($button.hasClass(flagIsHidden)).to.be.false;
|
|
// html2canvas.restore();
|
|
}, 100); // delay for screenshot and callback process
|
|
})
|
|
.catch((err) => {
|
|
html2canvas.restore();
|
|
throw err;
|
|
});
|
|
*/
|
|
$button.click();
|
|
setTimeout(() => {
|
|
expect($button.hasClass(flagIsHidden)).to.be.false;
|
|
html2canvas.restore();
|
|
done();
|
|
}, 1000); // delay for screenshot and callback process
|
|
});
|
|
});
|
|
|
|
// 3. Input Handling
|
|
describe('3. Input Handling', function() {
|
|
let $input, $textarea;
|
|
|
|
beforeEach(function() {
|
|
$input = $('<input type="text" value="test">').appendTo('body');
|
|
$textarea = $('<textarea>test</textarea>').appendTo('body');
|
|
});
|
|
|
|
afterEach(function() {
|
|
$input.remove();
|
|
$textarea.remove();
|
|
});
|
|
|
|
it('a. should clear input and textarea values during screenshot', function(done) {
|
|
sinon.stub(window, 'html2canvas').resolves(document.createElement('canvas'));
|
|
$button.click();
|
|
expect($input.val()).to.equal('');
|
|
expect($textarea.val()).to.equal('');
|
|
html2canvas.restore();
|
|
done();
|
|
});
|
|
|
|
it('b. should restore input and textarea values after screenshot', function(done) {
|
|
sinon.stub(window, 'html2canvas').resolves(document.createElement('canvas'));
|
|
/*
|
|
await waitForClick($button).then(() => {
|
|
setTimeout(() => {
|
|
expect($input.val()).to.equal('test');
|
|
expect($textarea.val()).to.equal('test');
|
|
}, 1000); // delay for screenshot and callback process
|
|
})
|
|
/*
|
|
.catch((err) => {
|
|
html2canvas.restore();
|
|
throw err;
|
|
})*
|
|
;
|
|
*/
|
|
$button.click();
|
|
setTimeout(() => {
|
|
expect($input.val()).to.equal('test');
|
|
expect($textarea.val()).to.equal('test');
|
|
done();
|
|
}, 1000);
|
|
html2canvas.restore();
|
|
});
|
|
});
|
|
|
|
// 4. Modal and Floating Element Handling
|
|
describe('4. Modal and Floating Element Handling', function() {
|
|
let $modal;
|
|
|
|
beforeEach(function() {
|
|
$modal = $('<div class="modal">Modal Content</div>').appendTo('body');
|
|
});
|
|
|
|
afterEach(function() {
|
|
$modal.remove();
|
|
});
|
|
|
|
it('a. should capture visible modals', async function() {
|
|
sinon.stub(window, 'html2canvas').callsFake(function(element) {
|
|
expect($(element).find('.modal').length).to.equal(5);
|
|
return Promise.resolve(document.createElement('canvas'));
|
|
});
|
|
await waitForClick($button);
|
|
html2canvas.restore();
|
|
});
|
|
});
|
|
|
|
// 5. HTML2Canvas Integration
|
|
describe('5. HTML2Canvas Integration', function() {
|
|
it('a. should call html2canvas with correct parameters', async function() {
|
|
const html2canvasStub = sinon.stub(window, 'html2canvas').resolves(document.createElement('canvas'));
|
|
await waitForClick($button);
|
|
html2canvas.restore();
|
|
expect(html2canvasStub.calledWith(document.body)).to.be.true;
|
|
});
|
|
|
|
it('b. should handle html2canvas errors gracefully', async function() {
|
|
sinon.stub(window, 'html2canvas').rejects(new Error('Test error'));
|
|
sinon.stub(console, 'error');
|
|
await waitForClick($button);
|
|
html2canvas.restore();
|
|
expect(console.error.called).to.be.true;
|
|
console.error.restore();
|
|
});
|
|
});
|
|
|
|
// 6. Screenshot Generation
|
|
describe('6. Screenshot Generation', function() {
|
|
/* Base64 string used instead of Blob
|
|
it('a. should create a blob from the canvas', async function() {
|
|
const canvas = document.createElement('canvas');
|
|
sinon.stub(window, 'html2canvas').resolves(canvas);
|
|
sinon.stub(canvas, 'toBlob').callsArgWith(0, new Blob());
|
|
await waitForClick($button);
|
|
html2canvas.restore();
|
|
expect(canvas.toBlob.called).to.be.true;
|
|
canvas.toBlob.restore();
|
|
});
|
|
*/
|
|
|
|
it('b. should generate a Base64 PNG string when saving is confirmed', async function() {
|
|
const consoleStub = sinon.stub(console, 'log');
|
|
await waitForClick($button).then(() => {
|
|
setTimeout(() => {
|
|
expect(consoleStub.callCount).to.equal(2);
|
|
const base64String = consoleStub.getCall(0).args[1];
|
|
expect(base64String).to.be.a('string');
|
|
expect(base64String).to.match(/^data:image\/png;base64,/);
|
|
consoleStub.restore();
|
|
}, 1000); // delay for screenshot and callback process
|
|
});
|
|
});
|
|
});
|
|
|
|
// 7. Download Functionality
|
|
describe('7. Download Functionality', function() {
|
|
it('a. should create a temporary anchor element for download', async function() {
|
|
// Stub document.createElement
|
|
createElementStub = sinon.stub(document, 'createElement').callsFake(function(tagName) {
|
|
if (tagName === 'a') {
|
|
return {
|
|
style: {},
|
|
href: '',
|
|
download: '',
|
|
click: sinon.spy()
|
|
};
|
|
}
|
|
});
|
|
|
|
// Stub document.body.appendChild and removeChild
|
|
appendChildStub = sinon.stub(document.body, 'appendChild');
|
|
removeChildStub = sinon.stub(document.body, 'removeChild');
|
|
|
|
// Mock blob and URL
|
|
blobMock = new Blob(['test'], { type: 'text/plain' });
|
|
urlMock = 'blob:http://example.com/test';
|
|
sinon.stub(URL, 'createObjectURL').returns(urlMock);
|
|
sinon.stub(URL, 'revokeObjectURL');
|
|
|
|
await waitForClick($button).then(() => {
|
|
setTimeout(() => {
|
|
// Assertions
|
|
expect(createElementStub.calledWith('a')).to.be.true;
|
|
expect(appendChildStub.calledOnce).to.be.true;
|
|
expect(removeChildStub.calledOnce).to.be.true;
|
|
|
|
const aElement = appendChildStub.getCall(0).args[0];
|
|
expect(aElement.href).to.equal(urlMock);
|
|
expect(aElement.download).to.equal('screenshot.png'); // Assuming settings.fileName is 'screenshot.png'
|
|
}, 1000);
|
|
});
|
|
/*
|
|
const appendChildStub = sandbox.stub(document.body, 'appendChild').callsFake((element) => {
|
|
console.log("attempting to append element: ", element);
|
|
if (element.tagName === 'IFRAME') {
|
|
// Allow iframe to be appended normally
|
|
document.body.appendChild.wrappedMethod.call(document.body, element);
|
|
}
|
|
});
|
|
sandbox.stub(document.body, 'removeChild');
|
|
sandbox.stub(URL, 'createObjectURL').returns('blob:test');
|
|
sandbox.stub(URL, 'revokeObjectURL');
|
|
|
|
await waitForClick($button).then(() => {
|
|
setTimeout(() => {
|
|
expect(document.body.appendChild.calledOnce).to.be.true;
|
|
expect(document.body.appendChild.args[0][0].tagName).to.equal('A');
|
|
}, 1000); // delay for screenshot and callback process
|
|
});
|
|
*/
|
|
});
|
|
|
|
it('b. should click the temporary anchor element programmatically', async function() {
|
|
const fakeAnchor = {
|
|
style: {},
|
|
click: sinon.spy()
|
|
};
|
|
sandbox.stub(document, 'createElement').returns(fakeAnchor);
|
|
|
|
await waitForClick($button);
|
|
expect(fakeAnchor.click.calledOnce).to.be.true;
|
|
});
|
|
|
|
it('c. should remove the temporary anchor after download', async function() {
|
|
sandbox.stub(document.body, 'removeChild');
|
|
|
|
await waitForClick($button);
|
|
expect(document.body.removeChild.calledOnce).to.be.true;
|
|
});
|
|
});
|
|
|
|
// 8. Cross-browser Compatibility
|
|
describe('8. Cross-browser Compatibility', function() {
|
|
it('a. should work in Chrome-like environments', function() {
|
|
// Assuming we're running tests in a Chrome-like environment
|
|
expect(() => $button.screenshotButton()).to.not.throw();
|
|
});
|
|
|
|
// Additional browser-specific tests would go here
|
|
// These might need to be run in different environments or with browser mocks
|
|
});
|
|
|
|
// 9. Performance
|
|
describe('9. Performance', function() {
|
|
it('a. should capture and generate screenshot within acceptable time', async function() {
|
|
this.timeout(5000); // Adjust timeout as needed
|
|
|
|
const startTime = performance.now();
|
|
|
|
await waitForClick($button);
|
|
|
|
const endTime = performance.now();
|
|
const duration = endTime - startTime;
|
|
expect(duration).to.be.below(1000); // Adjust threshold as needed
|
|
});
|
|
|
|
// Additional performance tests with different page complexities would go here
|
|
});
|
|
|
|
// 10. Error Handling
|
|
describe('10. Error Handling', function() {
|
|
it('a. should log error when html2canvas is not loaded', async function() {
|
|
const consoleErrorStub = sandbox.stub(console, 'error');
|
|
const originalHtml2Canvas = window.html2canvas;
|
|
delete window.html2canvas;
|
|
|
|
await waitForClick($button);
|
|
|
|
expect(consoleErrorStub.calledOnce).to.be.true;
|
|
expect(consoleErrorStub.args[0][0]).to.include('html2canvas is not loaded');
|
|
|
|
// Restore html2canvas
|
|
window.html2canvas = originalHtml2Canvas;
|
|
});
|
|
|
|
it('a. should handle errors during capture process', async function() {
|
|
sandbox.stub(window, 'html2canvas').rejects(new Error('Capture failed'));
|
|
const consoleErrorStub = sandbox.stub(console, 'error');
|
|
|
|
await waitForClick($button);
|
|
|
|
expect(consoleErrorStub.calledOnce).to.be.true;
|
|
expect(consoleErrorStub.args[0][0]).to.include('Capture failed');
|
|
});
|
|
});
|
|
|
|
// Configuration Options
|
|
describe('11. Configuration Options', function() {
|
|
it('11.1 should apply custom button text correctly', function() {
|
|
const customText = 'Capture Screen';
|
|
$button.screenshotButton({ buttonText: customText });
|
|
expect($button.text()).to.equal(customText);
|
|
});
|
|
|
|
it('11.2 should use custom filename for the downloaded image', async function() {
|
|
const customFileName = 'custom-screenshot.png';
|
|
$button.screenshotButton({ fileName: customFileName });
|
|
|
|
// Mock html2canvas and URL.createObjectURL
|
|
global.html2canvas = sinon.stub().resolves({ toBlob: (callback) => callback(new Blob()) });
|
|
global.URL.createObjectURL = sinon.stub().returns('blob:url');
|
|
|
|
// Mock anchor creation and click
|
|
const anchorMock = {
|
|
style: {},
|
|
click: sinon.spy(),
|
|
remove: sinon.spy()
|
|
};
|
|
sinon.stub(document, 'createElement').returns(anchorMock);
|
|
|
|
await waitForClick($button);
|
|
|
|
expect(anchorMock.download).to.equal(customFileName);
|
|
document.createElement.restore();
|
|
});
|
|
|
|
it('11.3 should respect custom modal selectors during capture', async function() {
|
|
const customSelector = '.custom-modal';
|
|
$button.screenshotButton({ modalsSelector: customSelector });
|
|
|
|
// Create a custom modal element
|
|
$('<div class="custom-modal">Custom Modal</div>').appendTo('body');
|
|
|
|
// Mock html2canvas
|
|
global.html2canvas = sinon.spy((element, options) => {
|
|
expect(options.ignoreElements).to.be.a('function');
|
|
const customModal = document.querySelector(customSelector);
|
|
expect(options.ignoreElements(customModal)).to.be.false;
|
|
done();
|
|
return Promise.resolve({ toBlob: () => {} });
|
|
});
|
|
|
|
await waitForClick($button);
|
|
});
|
|
});
|
|
|
|
// Accessibility
|
|
describe('12. Accessibility', function() {
|
|
it('12.1 should have appropriate ARIA attributes', function() {
|
|
$button.screenshotButton(defaultOptions);
|
|
expect($button.attr('role')).to.equal('button');
|
|
expect($button.attr('aria-label')).to.equal(defaultOptions.buttonText);
|
|
});
|
|
|
|
it('12.2 should be keyboard accessible', async function() {
|
|
$button.screenshotButton(defaultOptions);
|
|
$button.on('keydown', function(e) {
|
|
if (e.which === 13) { // Enter key
|
|
done();
|
|
}
|
|
});
|
|
const event = new KeyboardEvent('keydown', { 'keyCode': 13 });
|
|
$button[0].dispatchEvent(event);
|
|
});
|
|
});
|
|
|
|
// Security
|
|
describe('13. Security', function() {
|
|
it('13.1 should not capture sensitive information in inputs and textareas', async function() {
|
|
$button.screenshotButton(defaultOptions);
|
|
|
|
// Create test input and textarea with sensitive information
|
|
$('<input type="text" value="sensitive-info">').appendTo('body');
|
|
$('<textarea>confidential-data</textarea>').appendTo('body');
|
|
|
|
// Mock html2canvas
|
|
global.html2canvas = sinon.spy(() => {
|
|
const inputs = document.querySelectorAll('input, textarea');
|
|
inputs.forEach(input => {
|
|
expect(input.value).to.be.empty;
|
|
});
|
|
done();
|
|
return Promise.resolve({ toBlob: () => {} });
|
|
});
|
|
|
|
await waitForClick($button);
|
|
});
|
|
|
|
it('13.2 should restore input and textarea values after capture', async function() {
|
|
$button.screenshotButton(defaultOptions);
|
|
|
|
const inputValue = 'sensitive-info';
|
|
const textareaValue = 'confidential-data';
|
|
|
|
// Create test input and textarea with sensitive information
|
|
$('<input type="text" value="' + inputValue + '">').appendTo('body');
|
|
$('<textarea>' + textareaValue + '</textarea>').appendTo('body');
|
|
|
|
// Mock html2canvas and URL.createObjectURL
|
|
global.html2canvas = sinon.stub().resolves({ toBlob: (callback) => callback(new Blob()) });
|
|
global.URL.createObjectURL = sinon.stub().returns('blob:url');
|
|
|
|
await waitForClick($button);
|
|
|
|
setTimeout(() => {
|
|
const inputs = document.querySelectorAll('input, textarea');
|
|
inputs.forEach(input => {
|
|
if (input.tagName === 'INPUT') {
|
|
expect(input.value).to.equal(inputValue);
|
|
} else if (input.tagName === 'TEXTAREA') {
|
|
expect(input.value).to.equal(textareaValue);
|
|
}
|
|
});
|
|
done();
|
|
}, 0);
|
|
});
|
|
});
|
|
|
|
// CSS Interaction
|
|
describe('14. CSS Interaction', function() {
|
|
it('14.1 should not break existing page styles', function() {
|
|
const originalStyles = getComputedStyle(document.body);
|
|
$button.screenshotButton();
|
|
const newStyles = getComputedStyle(document.body);
|
|
expect(originalStyles.cssText).to.equal(newStyles.cssText);
|
|
});
|
|
|
|
it('14.2 should apply correct button styling', function() {
|
|
$button.screenshotButton();
|
|
const $button = $button.find('button');
|
|
const buttonStyles = getComputedStyle($button[0]);
|
|
expect(buttonStyles.display).to.not.equal('none');
|
|
// Add more specific style checks as per your plugin's CSS
|
|
});
|
|
});
|
|
|
|
// jQuery Plugin Standards
|
|
describe('15. jQuery Plugin Standards', function() {
|
|
it('15.1 should return jQuery object for chaining', function() {
|
|
const result = $button.screenshotButton();
|
|
expect(result).to.equal($button);
|
|
});
|
|
|
|
it('15.2 should initialize plugin on multiple elements', function() {
|
|
$('body').append('<div class="test-class"></div><div class="test-class"></div>');
|
|
$('.test-class').screenshotButton();
|
|
expect($('.test-class').find('button').length).to.equal(2);
|
|
});
|
|
});
|
|
|
|
// Memory Management
|
|
describe('16. Memory Management', function() {
|
|
it('16.1 should not leak memory on repeated initialization and destruction', function() {
|
|
const initialMemory = performance.memory ? performance.memory.usedJSHeapSize : 0;
|
|
for (let i = 0; i < 100; i++) {
|
|
$button.screenshotButton();
|
|
$button.empty();
|
|
}
|
|
const finalMemory = performance.memory ? performance.memory.usedJSHeapSize : 0;
|
|
expect(finalMemory - initialMemory).to.be.below(1000000); // Less than 1MB increase
|
|
});
|
|
|
|
it('16.2 should remove all created DOM elements after use', function() {
|
|
$button.screenshotButton();
|
|
const initialChildCount = $button[0].childElementCount;
|
|
$button.find('button').click();
|
|
// Assuming the screenshot process is synchronous for this test
|
|
expect($button[0].childElementCount).to.equal(initialChildCount);
|
|
});
|
|
});
|
|
|
|
// Responsiveness
|
|
describe('17. Responsiveness', function() {
|
|
it('17.1 should behave correctly on different screen sizes', function() {
|
|
const viewports = [
|
|
{width: 320, height: 568}, // iPhone 5
|
|
{width: 1024, height: 768}, // iPad
|
|
{width: 1920, height: 1080} // Full HD
|
|
];
|
|
|
|
viewports.forEach(size => {
|
|
$button.width(size.width).height(size.height);
|
|
$button.screenshotButton();
|
|
const $button = $button.find('button');
|
|
expect($button.is(':visible')).to.be.true;
|
|
expect($button.width()).to.be.at.most(size.width);
|
|
});
|
|
});
|
|
|
|
it('17.2 should capture accurate viewport in screenshot', async function() {
|
|
$button.width(800).height(600);
|
|
$button.html('<div id="content" style="width: 100%; height: 100%; background-color: red;"></div>');
|
|
$button.screenshotButton();
|
|
|
|
const $button = $button.find('button');
|
|
await waitForClick($button);
|
|
|
|
// Mock html2canvas to check if it's called with correct dimensions
|
|
sinon.replace(window, 'html2canvas', sinon.fake.returns(Promise.resolve({
|
|
toBlob: (callback) => callback(new Blob(['fakepngdata'], {type: 'image/png'}))
|
|
})));
|
|
|
|
setTimeout(() => {
|
|
expect(html2canvas.calledOnce).to.be.true;
|
|
const args = html2canvas.firstCall.args[1];
|
|
expect(args.width).to.equal(800);
|
|
expect(args.height).to.equal(600);
|
|
sinon.restore();
|
|
done();
|
|
}, 100);
|
|
});
|
|
});
|
|
}); |