From 16406fec964398a2229df0bd9dd78108c22161c6 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Sat, 5 Nov 2022 21:12:57 -0700 Subject: [PATCH] Added basic masks tests (#5237) --- tests/cypress.json | 1 + .../cypress/integration/masks/masks_basics.js | 210 ++++++++++++++++++ tests/cypress/support/commands.js | 24 ++ 3 files changed, 235 insertions(+) create mode 100644 tests/cypress/integration/masks/masks_basics.js diff --git a/tests/cypress.json b/tests/cypress.json index 51ca3eee..93cb1bcc 100644 --- a/tests/cypress.json +++ b/tests/cypress.json @@ -14,6 +14,7 @@ "auth_page.js", "skeletons_pipeline.js", "webhooks.js", + "masks/*.js", "actions_tasks/**/*.js", "actions_tasks2/**/*.js", "actions_tasks3/**/*.js", diff --git a/tests/cypress/integration/masks/masks_basics.js b/tests/cypress/integration/masks/masks_basics.js new file mode 100644 index 00000000..19ce6179 --- /dev/null +++ b/tests/cypress/integration/masks/masks_basics.js @@ -0,0 +1,210 @@ +// Copyright (C) 2022 CVAT.ai Corporation +// +// SPDX-License-Identifier: MIT + +/// + +context('Manipulations with masks', () => { + const taskName = 'Basic actions with masks'; + const serverFiles = ['images/image_1.jpg', 'images/image_2.jpg', 'images/image_3.jpg']; + const drawingActions = [{ + method: 'brush', + coordinates: [[300, 300], [700, 300], [700, 700], [300, 700]], + }, { + method: 'polygon-plus', + coordinates: [[450, 210], [650, 400], [450, 600], [260, 400]], + }, { + method: 'brush-size', + value: 150, + }, { + method: 'eraser', + coordinates: [[500, 500]], + }, { + method: 'brush-size', + value: 10, + }, { + method: 'polygon-minus', + coordinates: [[450, 400], [600, 400], [450, 550], [310, 400]], + }]; + + const editingActions = [{ + method: 'polygon-minus', + coordinates: [[50, 400], [800, 400], [800, 800], [50, 800]], + }]; + + let taskID = null; + let jobID = null; + + function drawMask(instructions) { + for (const instruction of instructions) { + const { method } = instruction; + if (method === 'brush-size') { + const { value } = instruction; + cy.get('.cvat-brush-tools-brush').click(); + cy.get('.cvat-brush-tools-brush-size').within(() => { + cy.get('input').clear().type(`${value}`); + }); + } else { + const { coordinates } = instruction; + if (['brush', 'eraser'].includes(method)) { + if (method === 'eraser') { + cy.get('.cvat-brush-tools-eraser').click(); + } else { + cy.get('.cvat-brush-tools-brush').click(); + } + + cy.get('.cvat-canvas-container').then(([$canvas]) => { + const [initX, initY] = coordinates[0]; + cy.wrap($canvas).trigger('mousemove', { clientX: initX, clientY: initY, bubbles: true }); + cy.wrap($canvas).trigger('mousedown', { + clientX: initX, clientY: initY, button: 0, bubbles: true, + }); + for (const coord of coordinates) { + const [clientX, clientY] = coord; + cy.wrap($canvas).trigger('mousemove', { clientX, clientY, bubbles: true }); + } + cy.wrap($canvas).trigger('mousemove', { clientX: initX, clientY: initY, bubbles: true }); + cy.wrap($canvas).trigger('mouseup', { bubbles: true }); + }); + } else if (['polygon-plus', 'polygon-minus'].includes(method)) { + if (method === 'polygon-plus') { + cy.get('.cvat-brush-tools-polygon-plus').click(); + } else { + cy.get('.cvat-brush-tools-polygon-minus').click(); + } + + cy.get('.cvat-canvas-container').then(($canvas) => { + for (const [x, y] of coordinates) { + cy.wrap($canvas).click(x, y); + } + }); + } + } + } + } + + function startDrawing() { + cy.get('.cvat-draw-mask-control ').trigger('mouseover'); + cy.get('.cvat-draw-mask-popover').should('exist').and('be.visible').within(() => { + cy.get('button').click(); + }); + cy.get('.cvat-brush-tools-toolbox').should('exist').and('be.visible'); + } + + function finishDrawing() { + cy.get('.cvat-brush-tools-brush').click(); + cy.get('.cvat-brush-tools-finish').click(); + } + + before(() => { + cy.visit('auth/login'); + cy.login(); + cy.headlessCreateTask({ + labels: [{ name: 'mask label', attributes: [], type: 'any' }], + name: taskName, + project_id: null, + source_storage: { location: 'local' }, + target_storage: { location: 'local' }, + }, { + server_files: serverFiles, + image_quality: 70, + use_zip_chunks: true, + use_cache: true, + sorting_method: 'lexicographical', + }).then((response) => { + taskID = response.taskID; + [jobID] = response.jobID; + }).then(() => { + cy.visit(`/tasks/${taskID}/jobs/${jobID}`); + cy.get('.cvat-canvas-container').should('exist').and('be.visible'); + }); + }); + + after(() => { + cy.logout(); + cy.getAuthKey().then((response) => { + const authKey = response.body.key; + cy.request({ + method: 'DELETE', + url: `/api/tasks/${taskID}`, + headers: { + Authorization: `Token ${authKey}`, + }, + }); + }); + }); + + beforeEach(() => { + cy.removeAnnotations(); + cy.goCheckFrameNumber(0); + }); + + describe('Draw a couple of masks masks', () => { + it('Drawing a couple of masks. Save job, reopen job, masks must exist', () => { + startDrawing(); + drawMask(drawingActions); + cy.get('.cvat-brush-tools-finish').click(); + cy.get('.cvat-brush-tools-continue').click(); + cy.get('.cvat-brush-tools-toolbox').should('exist').and('be.visible'); + cy.get('#cvat_canvas_shape_1').should('exist').and('be.visible'); + + // it is expected, that after clicking "continue", brush tools are still opened + drawMask(drawingActions); + finishDrawing(); + cy.get('.cvat-brush-tools-toolbox').should('not.be.visible'); + + cy.saveJob(); + cy.reload(); + + for (const id of [1, 2]) { + cy.get(`#cvat_canvas_shape_${id}`).should('exist').and('be.visible'); + } + cy.removeAnnotations(); + }); + + it('Propagate mask to another frame', () => { + startDrawing(); + drawMask(drawingActions); + finishDrawing(); + + cy.get('#cvat-objects-sidebar-state-item-1').find('[aria-label="more"]').trigger('mouseover'); + cy.get('.cvat-object-item-menu').within(() => { + cy.contains('button', 'Propagate').click(); + }); + cy.get('.cvat-propagate-confirm-object-up-to-frame').find('input') + .should('have.attr', 'value', serverFiles.length - 1); + cy.contains('button', 'Yes').click(); + for (let i = 1; i < serverFiles.length; i++) { + cy.goCheckFrameNumber(i); + cy.get('.cvat_canvas_shape').should('exist').and('be.visible'); + } + }); + + it('Copy mask to another frame', () => { + startDrawing(); + drawMask(drawingActions); + finishDrawing(); + + cy.get('#cvat-objects-sidebar-state-item-1').within(() => { + cy.get('[aria-label="more"]').trigger('mouseover'); + }); + cy.get('.cvat-object-item-menu').last().should('be.visible').contains('button', 'Make a copy').click(); + cy.goCheckFrameNumber(serverFiles.length - 1); + cy.get('.cvat-canvas-container').click(); + cy.get('#cvat_canvas_shape_2').should('exist').and('be.visible'); + }); + + it('Editing a drawn mask', () => { + startDrawing(); + drawMask(drawingActions); + finishDrawing(); + + cy.get('#cvat-objects-sidebar-state-item-1').within(() => { + cy.get('[aria-label="more"]').trigger('mouseover'); + }); + cy.get('.cvat-object-item-menu').last().should('be.visible').contains('button', 'Edit').click(); + drawMask(editingActions); + finishDrawing(); + }); + }); +}); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 616cb422..95c6bfe3 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -230,6 +230,30 @@ Cypress.Commands.add( }, ); +Cypress.Commands.add('headlessCreateTask', (taskSpec, dataSpec) => { + cy.window().then(async ($win) => { + const task = new $win.cvat.classes.Task({ + ...taskSpec, + ...dataSpec, + }); + + if (dataSpec.server_files) { + task.serverFiles = dataSpec.server_files; + } + if (dataSpec.client_files) { + task.clientFiles = dataSpec.client_files; + } + + if (dataSpec.remote_files) { + task.remoteFiles = dataSpec.remote_files; + } + + const result = await task.save(); + cy.log(result); + return cy.wrap({ taskID: result.id, jobID: result.jobs.map((job) => job.id) }); + }); +}); + Cypress.Commands.add('openTask', (taskName, projectSubsetFieldValue) => { cy.contains('strong', new RegExp(`^${taskName}$`)) .parents('.cvat-tasks-list-item')