Files
pluggable-web-controls/submission - backup/test_plugin_display_png_Mocha.js

376 lines
17 KiB
JavaScript

describe('Image Annotation Control', function() {
var $annotatorPNG, $toolbox, $inputUploadImage, $buttonAddArrow, $buttonAddTextbox, $selectAddSymbol, $inputColourPicker, $buttonEraseMode, $buttonSaveImage, $containerAnnotation, $canvasAnnotation;
beforeEach(function() {
$annotatorPNG = $(document).find("." + flagAnnotatorPNG);
$annotatorPNG.annotatorPNG(annotatorSettings);
$toolbox = $annotatorPNG.find("." + flagToolbox);
$inputUploadImage = $toolbox.find("." + flagUploadImage);
$buttonAddArrow = $toolbox.find("." + flagAddArrow);
$buttonAddTextbox = $toolbox.find("." + flagAddTextbox);
$selectAddSymbol = $toolbox.find("." + flagAddSymbol);
$inputColourPicker = $toolbox.find("." + flagColourPicker);
$buttonEraseMode = $toolbox.find("." + flagEraseMode);
$buttonSaveImage = $toolbox.find("." + flagSaveImage);
$containerAnnotation = $annotatorPNG.find("." + flagContainerAnnotation);
$canvasAnnotation = $containerAnnotation.find("." + flagCanvasAnnotation);
});
afterEach(function() {
let canvas = $($containerAnnotation.children()[0].children[0]).clone();
$containerAnnotation.empty();
$containerAnnotation.append(canvas);
});
describe('1. Initialization and Setup', function() {
it('a. Should initialize correctly on a given DOM element', async function() {
expect($annotatorPNG).to.exist;
});
it('b. Should create canvas with correct dimensions', async function() {
let canvas = $canvasAnnotation.data(keyFabric);
expect(canvas.width).to.equal(800);
expect(canvas.height).to.equal(600);
expect($canvasAnnotation.attr('width')).to.equal('800');
expect($canvasAnnotation.attr('height')).to.equal('600');
});
it('c. Should have all UI elements present with correct values after initialization', async function() {
expect($annotatorPNG.find("." + flagToolbox)).to.have.length(1);
expect($annotatorPNG.find("." + flagUploadImage)).to.have.length(1);
expect($annotatorPNG.find("." + flagAddArrow)).to.have.length(1);
expect($annotatorPNG.find("." + flagAddTextbox)).to.have.length(1);
expect($annotatorPNG.find("." + flagAddSymbol)).to.have.length(1);
expect($annotatorPNG.find("." + flagColourPicker)).to.have.length(1);
expect($annotatorPNG.find("." + flagEraseMode)).to.have.length(1);
expect($annotatorPNG.find("." + flagSaveImage)).to.have.length(1);
expect($annotatorPNG.find("." + flagContainerAnnotation)).to.have.length(1);
expect($annotatorPNG.find("." + flagCanvasAnnotation).length > 0).to.be.true;
/*
expect($toolbox).to.exist;
expect($inputUploadImage).to.exist;
expect($buttonAddArrow).to.exist;
expect($buttonAddTextbox).to.exist;
expect($selectAddSymbol).to.exist;
expect($selectAddSymbol.find("option")).to.exist;
expect($inputColourPicker).to.exist;
expect($buttonEraseMode).to.exist;
expect($buttonSaveImage).to.exist;
expect($containerAnnotation).to.exist;
expect($canvasAnnotation).to.exist;
*/
expect($inputUploadImage.val()).to.equal('');
expect($inputColourPicker.val()).to.equal('#ff0000');
expect($buttonSaveImage.text().length > 0).to.be.true; // For unkown settings argument value
expect($buttonSaveImage.text()).to.equal('Save Image');
expect($canvasAnnotation.data(keyFabric)).to.exist;
});
});
describe('2. Image Upload and Display', function() {
it('a. Should successfully upload a PNG file', function(done) {
const file = new File([''], 'screenshot (16).png', { type: 'image/png' });
const fileInput = $inputUploadImage[0];
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
$(fileInput).trigger('change');
setTimeout(() => {
let canvas = $canvasAnnotation.data(keyFabric);
if (canvas) console.log("canvas.backgroundImage: " + canvas.backgroundImage);
expect(canvas.backgroundImage).to.exist;
done();
}, 100);
});
it('b. Should display the uploaded image correctly on the canvas', function(done) {
const file = new File([''], 'screenshot (16).png', { type: 'image/png' });
const fileInput = $inputUploadImage[0];
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
$(fileInput).trigger('change');
setTimeout(() => {
try {
let canvas = $canvasAnnotation.data(keyFabric);
let context = canvas.getContext('2d');
let imageData = context.getImageData(0, 0, canvas.width, canvas.height);
expect(canvas.width).to.equal(800);
expect(canvas.height).to.equal(600);
expect(imageData.data.length).to.equal(800 * 600 * 4);
done();
}
catch (e) {
console.log("Error during canvas initialisation: " + e);
done(e);
}
}, 100);
});
it('d. Should handle invalid file types', async function() {
const file = new File([''], 'test.jpg', { type: 'image/jpeg' });
const fileInput = $inputUploadImage[0];
const confirmStub = sinon.stub(window, 'alert').returns(true);
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
expect(() => $(fileInput).trigger('change')).to.throw();
confirmStub.restore();
});
});
describe('3. Arrow Addition', function() {
it('a. Should create a new arrow on the canvas when "Add Arrow" is clicked', async function() {
let canvas = $canvasAnnotation.data(keyFabric);
console.log("canvas: " + canvas);
await waitForClick($buttonAddArrow);
console.log("canvas after: " + canvas);
expect(canvas.getObjects()).to.have.lengthOf(1);
expect(canvas.getObjects()[0].type).to.equal('path');
});
it('b. Should create arrow with correct default properties', async function() {
let canvas = $canvasAnnotation.data(keyFabric);
await waitForClick($buttonAddArrow);
const arrow = canvas.getObjects()[0];
expect(arrow.fill).to.equal('#ff0000');
expect(arrow.stroke).to.equal('#ff0000');
expect(arrow.strokeWidth).to.equal(2);
});
it('c. Should allow multiple arrows to be added to the canvas', async function() {
let canvas = $canvasAnnotation.data(keyFabric);
await waitForClick($buttonAddArrow);
await waitForClick($buttonAddArrow);
expect(canvas.getObjects()).to.have.lengthOf(2);
});
});
describe('4. Textbox Addition', function() {
it('a. Should create a new textbox on the canvas when "Add Textbox" is clicked', async function() {
let canvas = $canvasAnnotation.data(keyFabric);
await waitForClick($buttonAddTextbox);
expect(canvas.getObjects()).to.have.lengthOf(1);
expect(canvas.getObjects()[0].type).to.equal('textbox');
});
it('b. Should create textbox with correct default properties', async function() {
let canvas = $canvasAnnotation.data(keyFabric);
await waitForClick($buttonAddTextbox);
const textbox = canvas.getObjects()[0];
expect(textbox.fontSize).to.equal(20);
expect(textbox.left).to.equal(100);
expect(textbox.top).to.equal(100);
});
it('c. Should allow text to be entered and edited in the textbox', async function() {
this.timeout(10000);
let canvas = $canvasAnnotation.data(keyFabric);
await waitForClick($buttonAddTextbox);
const textbox = canvas.getObjects()[0];
console.log("textbox: " + textbox);
textbox.text = 'New Text';
console.log("textbox: " + textbox);
canvas.renderAll();
await new Promise(resolve => setTimeout(resolve, 50));
expect(textbox.text).to.equal('New Text');
});
it('d. Should allow multiple textboxes to be added to the canvas', async function() {
let canvas = $canvasAnnotation.data(keyFabric);
await waitForClick($buttonAddTextbox);
await waitForClick($buttonAddTextbox);
expect(canvas.getObjects()).to.have.lengthOf(2);
});
});
describe('5. Element Manipulation', function() {
it('a. Should allow arrows to be selected, moved, resized, and rotated', async function() {
let canvas = $canvasAnnotation.data(keyFabric);
await waitForClick($buttonAddArrow);
const arrow = canvas.getObjects()[0];
arrow.set({ left: 150, top: 150, scaleX: 2, angle: 45 });
canvas.renderAll();
expect(arrow.left).to.equal(150);
expect(arrow.top).to.equal(150);
expect(arrow.scaleX).to.equal(2);
expect(arrow.angle).to.equal(45);
});
it('b. Should allow textboxes to be selected, moved, resized, and rotated', async function() {
let canvas = $canvasAnnotation.data(keyFabric);
await waitForClick($buttonAddTextbox);
const textbox = canvas.getObjects()[0];
textbox.set({ left: 150, top: 150, scaleX: 2, angle: 45 });
canvas.renderAll();
expect(textbox.left).to.equal(150);
expect(textbox.top).to.equal(150);
expect(textbox.scaleX).to.equal(2);
expect(textbox.angle).to.equal(45);
});
});
describe('6. Erasing Elements', function() {
afterEach(function() {
setTimeout(() => {}, 2000);
$annotatorPNG.annotatorPNG(annotatorSettings);
});
it('a. Should remove the currently selected arrow when "Erase Element" is clicked', async function() {
let canvas = $canvasAnnotation.data(keyFabric);
await waitForClick($buttonAddArrow);
canvas.setActiveObject(canvas.getObjects()[0]);
await waitForClick($buttonEraseMode);
expect(canvas.getObjects()).to.have.lengthOf(0);
});
it('b. Should remove the currently selected textbox when "Erase Element" is clicked', function(done) {
this.timeout(10000);
let canvas = $canvasAnnotation.data(keyFabric);
waitForClick($buttonAddTextbox).then(() => {
canvas.setActiveObject(canvas.getObjects()[0]);
canvas.renderAll();
waitForClick($buttonEraseMode).then(() => {
canvas.renderAll();
expect(canvas.getObjects()).to.have.lengthOf(0);
done();
});
});
});
it('c. Should not affect other elements on the canvas when erasing', function(done) {
this.timeout(10000);
let canvas = $canvasAnnotation.data(keyFabric);
waitForClick($buttonAddArrow).then(() => {
console.log("canvas.getObjects()[0]: ", canvas.getObjects()[0]);
waitForClick($buttonAddTextbox).then(() => {
canvas.setActiveObject(canvas.getObjects()[0]);
waitForClick($buttonEraseMode).then(() => {
expect(canvas.getObjects()).to.have.lengthOf(1);
console.log("canvas.getObjects()[0]: ", canvas.getObjects()[0]);
expect(canvas.getObjects()[0].text).to.exist;
done();
});
});
});
});
});
describe('7. Saving Functionality', function() {
it('a. Should trigger confirmation dialog when "Save" is clicked', async function() {
const confirmStub = sinon.stub(window, 'confirm').returns(true);
const alertStub = sinon.stub(window, 'alert').returns(true);
await waitForClick($buttonSaveImage);
expect(confirmStub.calledOnce).to.be.true;
confirmStub.restore();
alertStub.restore();
});
it('b. Should prevent saving when confirmation dialog is canceled', async function() {
const confirmStub = sinon.stub(window, 'confirm').returns(false);
const consoleStub = sinon.stub(console, 'log');
await waitForClick($buttonSaveImage);
expect(consoleStub.called).to.be.false;
confirmStub.restore();
consoleStub.restore();
});
it('c. Should generate a Base64 PNG string when saving is confirmed', async function() {
sinon.stub(window, 'confirm').returns(true);
const alertStub = sinon.stub(window, 'alert').returns(true);
const consoleStub = sinon.stub(console, 'log');
await waitForClick($buttonSaveImage);
expect(consoleStub.calledOnce).to.be.true;
const base64String = consoleStub.getCall(0).args[1];
expect(base64String).to.be.a('string');
expect(base64String).to.match(/^data:image\/png;base64,/);
window.confirm.restore();
consoleStub.restore();
alertStub.restore();
});
});
describe('8. Edge Cases and Error Handling', function() {
it('a. Should handle initialization on invalid DOM element', async function() {
expect(() => $('#non-existent-element').imageAnnotator()).to.throw();
});
});
describe('9. Performance', function() {
it('a. Should capture and generate screenshot within acceptable time', function(done) {
this.timeout(5000);
const startTime = performance.now();
const alertStub = sinon.stub(window, 'alert').returns(true);
const confirmStub = sinon.stub(window, 'confirm').returns(true);
$buttonSaveImage.click();
const endTime = performance.now();
const duration = endTime - startTime;
expect(duration).to.be.below(1000);
alertStub.restore();
confirmStub.restore();
done();
});
});
describe('10. Accessibility', function() {
it('a. Should have appropriate ARIA labels for all interactive elements', async function() {
expect($inputUploadImage.attr('aria-label').length > 0).to.be.true;
expect($buttonAddArrow.attr('aria-label').length > 0).to.be.true;
expect($buttonAddTextbox.attr('aria-label').length > 0).to.be.true;
expect($selectAddSymbol.attr('aria-label').length > 0).to.be.true;
expect($inputColourPicker.attr('aria-label').length > 0).to.be.true;
expect($buttonEraseMode.attr('aria-label').length > 0).to.be.true;
expect($buttonSaveImage.attr('aria-label').length > 0).to.be.true;
expect($canvasAnnotation.attr('aria-label').length > 0).to.be.true;
});
});
describe('11. jQuery Plugin Requirements', function() {
it('a. Should return jQuery object for chaining', function() {
const result = $annotatorPNG.annotatorPNG(annotatorSettings);
expect(result).to.equal($annotatorPNG);
});
it('b. Should initialize plugin on multiple elements', function() {
$('body').append('<div class="test-class"></div><div class="test-class"></div>');
$('.test-class').annotatorPNG(annotatorSettings);
expect($('.test-class').length).to.equal(2);
$('.test-class').remove();
});
});
describe('12. Memory Management', function() {
it('a. Should not leak memory on repeated initialization and destruction', function() {
const initialMemory = performance.memory ? performance.memory.usedJSHeapSize : 0;
for (let i = 0; i < 500; i++) {
$annotatorPNG.annotatorPNG(annotatorSettings);
$canvasAnnotation.data(keyFabric).dispose();
}
const finalMemory = performance.memory ? performance.memory.usedJSHeapSize : 0;
expect(finalMemory - initialMemory).to.be.below(1000000); // 1 MB ish
});
it('b. Should remove all created DOM elements after use', function() {
const initialChildCount = $annotatorPNG[0].childElementCount;
const initialCanvasCount = $containerAnnotation[0].childElementCount;
$annotatorPNG.annotatorPNG(annotatorSettings);
expect($annotatorPNG[0].childElementCount).to.equal(initialChildCount);
expect($containerAnnotation[0].childElementCount).to.equal(initialCanvasCount);
});
});
});