diff --git a/cvat-core/tests/api/projects.js b/cvat-core/tests/api/projects.js index 5c1374301884..5a01b7d141d4 100644 --- a/cvat-core/tests/api/projects.js +++ b/cvat-core/tests/api/projects.js @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2020 Intel Corporation +// Copyright (C) 2019-2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -168,3 +168,20 @@ describe('Feature: delete a project', () => { expect(result).toHaveLength(0); }); }); + +describe('Feature: delete a label', () => { + test('delete a label', async () => { + let result = await window.cvat.projects.get({ + id: 2, + }); + + const labelsLength = result[0].labels.length; + const deletedLabels = result[0].labels.filter((el) => el.name !== 'bicycle'); + result[0].labels = deletedLabels; + result[0].save(); + result = await window.cvat.projects.get({ + id: 2, + }); + expect(result[0].labels).toHaveLength(labelsLength - 1); + }); +}); diff --git a/cvat-core/tests/api/tasks.js b/cvat-core/tests/api/tasks.js index c309b4150753..eb7374d65a16 100644 --- a/cvat-core/tests/api/tasks.js +++ b/cvat-core/tests/api/tasks.js @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -196,3 +196,20 @@ describe('Feature: delete a task', () => { expect(result).toHaveLength(0); }); }); + +describe('Feature: delete a label', () => { + test('delete a label', async () => { + let result = await window.cvat.tasks.get({ + id: 100, + }); + + const labelsLength = result[0].labels.length; + const deletedLabels = result[0].labels.filter((el) => el.name !== 'person'); + result[0].labels = deletedLabels; + result[0].save(); + result = await window.cvat.tasks.get({ + id: 100, + }); + expect(result[0].labels).toHaveLength(labelsLength - 1); + }); +}); diff --git a/cvat-core/tests/mocks/dummy-data.mock.js b/cvat-core/tests/mocks/dummy-data.mock.js index 33a3c500ce88..2984aeae5c12 100644 --- a/cvat-core/tests/mocks/dummy-data.mock.js +++ b/cvat-core/tests/mocks/dummy-data.mock.js @@ -189,6 +189,12 @@ const projectsDummyData = { }, ], }, + { + id: 2, + name: 'bicycle', + color: '#bb20c0', + attributes: [], + }, ], tasks: [ { diff --git a/cvat-ui/src/components/annotation-page/annotation-page.tsx b/cvat-ui/src/components/annotation-page/annotation-page.tsx index 92410833bf7e..a51d6c22995b 100644 --- a/cvat-ui/src/components/annotation-page/annotation-page.tsx +++ b/cvat-ui/src/components/annotation-page/annotation-page.tsx @@ -80,6 +80,7 @@ export default function AnnotationPageComponent(props: Props): JSX.Element { ), placement: 'topRight', + className: 'cvat-notification-no-labels', }); } }, [job, fetching, prevJob, prevFetching]); diff --git a/cvat-ui/src/components/labels-editor/labels-editor.tsx b/cvat-ui/src/components/labels-editor/labels-editor.tsx index 2eacf74a9b31..8937be10d9b7 100644 --- a/cvat-ui/src/components/labels-editor/labels-editor.tsx +++ b/cvat-ui/src/components/labels-editor/labels-editor.tsx @@ -158,6 +158,7 @@ export default class LabelsEditor extends React.PureComponent= 0) { ModalConfirm({ + className: 'cvat-modal-delete-label', title: `Do you want to delete "${label.name}" label?`, icon: , content: 'This action is irreversible. Annotation corresponding with this label will be deleted.', diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts index 5451eba90020..4df4f4461852 100644 --- a/cvat-ui/src/reducers/notifications-reducer.ts +++ b/cvat-ui/src/reducers/notifications-reducer.ts @@ -665,6 +665,7 @@ export default function (state = defaultState, action: AnyAction): Notifications jobFetching: { message: 'Error during fetching a job', reason: action.payload.error.toString(), + className: 'cvat-notification-notice-fetch-job-failed', }, }, }, diff --git a/tests/cypress/integration/actions_projects/case_57_project_label_deleting_feature.js b/tests/cypress/integration/actions_projects/case_57_project_label_deleting_feature.js new file mode 100644 index 000000000000..8c285a1017b1 --- /dev/null +++ b/tests/cypress/integration/actions_projects/case_57_project_label_deleting_feature.js @@ -0,0 +1,89 @@ +// Copyright (C) 2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { projectName, labelName } from '../../support/const_project'; + +context('Delete a label from a project.', () => { + const caseID = 57; + const taskName = `Task case ${caseID}`; + const attrName = `Attr for ${labelName}`; + const textDefaultValue = 'Some value for type Text'; + const imagesCount = 1; + const imageFileName = `image_${taskName.replace(/\s+/g, '_').toLowerCase()}`; + const width = 800; + const height = 800; + const posX = 10; + const posY = 10; + const color = 'white'; + const archiveName = `${imageFileName}.zip`; + const archivePath = `cypress/fixtures/${archiveName}`; + const imagesFolder = `cypress/fixtures/${imageFileName}`; + const directoryToArchive = imagesFolder; + const advancedConfigurationParams = false; + const forProject = true; + const attachToProject = false; + const multiAttrParams = false; + let projectID = ''; + + function getProjectID(projectName) { + cy.contains('.cvat-project-name', projectName) + .parents('.cvat-project-details') + .should('have.attr', 'cvat-project-id') + .then(($projectID) => { + projectID = $projectID; + }); + } + + before(() => { + cy.imageGenerator(imagesFolder, imageFileName, width, height, color, posX, posY, labelName, imagesCount); + cy.createZipArchive(directoryToArchive, archivePath); + cy.openProject(projectName); + }); + + after(() => { + cy.goToProjectsList(); + cy.deleteProject(projectName, projectID); + }); + + describe(`Testing "Case ${caseID}"`, () => { + it('Create a task from project.', () => { + cy.createAnnotationTask( + taskName, + labelName, + attrName, + textDefaultValue, + archiveName, + multiAttrParams, + advancedConfigurationParams, + forProject, + attachToProject, + projectName, + ); + }); + + it('Delete a label from project.', () => { + cy.openProject(projectName); + getProjectID(projectName); + cy.contains('.cvat-constructor-viewer-item', labelName) + .should('exist') + .and('be.visible') + .find('[aria-label="close"]') + .click(); + cy.get('.cvat-modal-delete-label') + .should('be.visible') + .within(() => { + cy.contains('[type="button"]', 'OK').click(); + }); + cy.contains('.cvat-constructor-viewer-item', labelName).should('not.exist'); + }); + + it('Try to open job with no labels in the project. Successful.', () => { + cy.openTaskJob(taskName); + cy.get('.cvat-disabled-canvas-control').should('exist'); + cy.contains('.cvat-notification-no-labels', 'does not contain any label').should('exist').and('be.visible'); + }); + }); +}); diff --git a/tests/cypress/integration/actions_tasks_objects/case_58_task_label_deleting_feature.js b/tests/cypress/integration/actions_tasks_objects/case_58_task_label_deleting_feature.js new file mode 100644 index 000000000000..9779e402f73b --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/case_58_task_label_deleting_feature.js @@ -0,0 +1,60 @@ +// Copyright (C) 2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +context('Delete a label from a task.', () => { + const caseId = '58'; + const labelName = `Case ${caseId}`; + const taskName = `New annotation task for ${labelName}`; + const attrName = `Attr for ${labelName}`; + const textDefaultValue = 'Some default value for type Text'; + const imagesCount = 1; + const imageFileName = `image_${labelName.replace(' ', '_').toLowerCase()}`; + const width = 800; + const height = 800; + const posX = 10; + const posY = 10; + const color = 'gray'; + const archiveName = `${imageFileName}.zip`; + const archivePath = `cypress/fixtures/${archiveName}`; + const imagesFolder = `cypress/fixtures/${imageFileName}`; + const directoryToArchive = imagesFolder; + + before(() => { + cy.visit('auth/login'); + cy.login(); + cy.imageGenerator(imagesFolder, imageFileName, width, height, color, posX, posY, labelName, imagesCount); + cy.createZipArchive(directoryToArchive, archivePath); + cy.createAnnotationTask(taskName, labelName, attrName, textDefaultValue, archiveName); + cy.openTask(taskName); + }); + + after(() => { + cy.goToTaskList(); + cy.deleteTask(taskName); + }); + + describe(`Testing "${labelName}"`, () => { + it('Delete a label from the task.', () => { + cy.contains('.cvat-constructor-viewer-item', labelName) + .should('exist') + .and('be.visible') + .find('[aria-label="close"]') + .click(); + cy.get('.cvat-modal-delete-label') + .should('be.visible') + .within(() => { + cy.contains('[type="button"]', 'OK').click(); + }); + cy.contains('.cvat-constructor-viewer-item', labelName).should('not.exist'); + }); + + it('Try to open a job with no labels. Successful.', () => { + cy.openJob(); + cy.get('.cvat-disabled-canvas-control').should('exist'); + cy.contains('.cvat-notification-no-labels', 'does not contain any label').should('exist').and('be.visible'); + }); + }); +}); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index d707c8eb018e..fbb896c1043c 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -162,12 +162,14 @@ Cypress.Commands.add('getJobNum', (jobID) => { }); }); -Cypress.Commands.add('openJob', (jobID = 0, removeAnnotations = true) => { +Cypress.Commands.add('openJob', (jobID = 0, removeAnnotations = true, expectedFail = false) => { cy.getJobNum(jobID).then(($job) => { cy.get('.cvat-task-jobs-table-row').contains('a', `Job #${$job}`).click(); }); cy.url().should('include', '/jobs'); - cy.get('.cvat-canvas-container').should('exist'); + expectedFail + ? cy.get('.cvat-canvas-container').should('not.exist') + : cy.get('.cvat-canvas-container').should('exist'); if (removeAnnotations) { cy.document().then((doc) => { const objects = Array.from(doc.querySelectorAll('.cvat_canvas_shape')); @@ -179,9 +181,9 @@ Cypress.Commands.add('openJob', (jobID = 0, removeAnnotations = true) => { } }); -Cypress.Commands.add('openTaskJob', (taskName, jobID = 0, removeAnnotations = true) => { +Cypress.Commands.add('openTaskJob', (taskName, jobID = 0, removeAnnotations = true, expectedFail = false) => { cy.openTask(taskName); - cy.openJob(jobID, removeAnnotations); + cy.openJob(jobID, removeAnnotations, expectedFail); }); Cypress.Commands.add('interactControlButton', (objectType) => {