From 4191be33f76f60cbebe09b6b484c95c1528339be Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Thu, 23 Jan 2025 16:32:01 +0000 Subject: [PATCH] Test to confirm correct UI and cache state after frame deletion (#8949) ### Motivation and context Fix #8872 dealt with incorrect states of UI and cache after frame deletion. The frame was not being deleted and the cache was not updated properly to reflect the changes. ### How has this been tested? #### Set up - open Main task job - save default `cvat.config.jobMetaDataReloadPeriod` value from `cvat-core/src/config.ts` - temporarily set `jobMetaDataReloadPeriod` to a small value (100 ms) for the sake of easier testing ### Test suite - Inside the job, wait for the small job metadata reload period to elapse - Transition to the next frame by clicking the next button on the player - Verify that `GET /data/meta` is sent and that the response has frames in it. - Save current frame number to the variable `frameNum` - Delete the frame: click on the bin, click on 'Delete' button in the modal - Ensure that 'Restore frame' button is absent - Make sure that the frame is deleted by asserting that current frame has number `frameNum + 1` (the next frame in the dataset) - Click Save to send `PATCH` to `/data/meta`. - Intercept the request to validate that response has `deleted_frames` property, that it's an `Array` and that it contains the deleted frame number `frameNum` - Assert that `deleted_frames` array contains one element: `frameNum` #### Tear down - Set `jobMetaDataReloadPeriod` back to the saved default value ### Checklist - [ ] I submit my changes into the `develop` branch - [ ] I have created a changelog fragment - [ ] I have updated the documentation accordingly - [ ] I have added tests to cover my changes - [ ] I have linked related issues (see [GitHub docs]( https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword)) - [ ] I have increased versions of npm packages if it is necessary ([cvat-canvas](https://github.com/cvat-ai/cvat/tree/develop/cvat-canvas#versioning), [cvat-core](https://github.com/cvat-ai/cvat/tree/develop/cvat-core#versioning), [cvat-data](https://github.com/cvat-ai/cvat/tree/develop/cvat-data#versioning) and [cvat-ui](https://github.com/cvat-ai/cvat/tree/develop/cvat-ui#versioning)) ### License - [ ] I submit _my code changes_ under the same [MIT License]( https://github.com/cvat-ai/cvat/blob/develop/LICENSE) that covers the project. Feel free to contact the maintainers if that's a concern. --------- Co-authored-by: Oleg Valiulin Co-authored-by: Boris Sekachev Co-authored-by: Kirill Lakhov --- ...mp_upload_annotation_point_cloud_format.js | 6 +- ...pload_annotation_velodyne_points_format.js | 6 +- .../issue_8785_update_job_metadata.js | 15 +--- .../issues_prs2/issue_8872_delete_frame.js | 70 +++++++++++++++++++ tests/cypress/support/commands.js | 20 ++++-- 5 files changed, 90 insertions(+), 27 deletions(-) create mode 100644 tests/cypress/e2e/issues_prs2/issue_8872_delete_frame.js diff --git a/tests/cypress/e2e/canvas3d_functionality/case_91_canvas3d_functionality_dump_upload_annotation_point_cloud_format.js b/tests/cypress/e2e/canvas3d_functionality/case_91_canvas3d_functionality_dump_upload_annotation_point_cloud_format.js index 96a555c5520..d11d2297a10 100644 --- a/tests/cypress/e2e/canvas3d_functionality/case_91_canvas3d_functionality_dump_upload_annotation_point_cloud_format.js +++ b/tests/cypress/e2e/canvas3d_functionality/case_91_canvas3d_functionality_dump_upload_annotation_point_cloud_format.js @@ -92,8 +92,7 @@ context('Canvas 3D functionality. Dump/upload annotation. "Point Cloud" format', cy.verifyNotification(); cy.get('#cvat-objects-sidebar-state-item-1').should('exist'); cy.removeAnnotations(); - cy.get('button').contains('Save').click(); - cy.get('button').contains('Save').trigger('mouseout'); + cy.clickSaveAnnotationView(); cy.get('#cvat-objects-sidebar-state-item-1').should('not.exist'); }); @@ -114,8 +113,7 @@ context('Canvas 3D functionality. Dump/upload annotation. "Point Cloud" format', cy.openTaskJob(taskName); cy.get('#cvat-objects-sidebar-state-item-1').should('exist'); cy.removeAnnotations(); - cy.get('button').contains('Save').click(); - cy.get('button').contains('Save').trigger('mouseout'); + cy.clickSaveAnnotationView(); }); }); }); diff --git a/tests/cypress/e2e/canvas3d_functionality/case_92_canvas3d_functionality_dump_upload_annotation_velodyne_points_format.js b/tests/cypress/e2e/canvas3d_functionality/case_92_canvas3d_functionality_dump_upload_annotation_velodyne_points_format.js index 6dcbc306d6d..521a22036f4 100644 --- a/tests/cypress/e2e/canvas3d_functionality/case_92_canvas3d_functionality_dump_upload_annotation_velodyne_points_format.js +++ b/tests/cypress/e2e/canvas3d_functionality/case_92_canvas3d_functionality_dump_upload_annotation_velodyne_points_format.js @@ -93,8 +93,7 @@ context('Canvas 3D functionality. Dump/upload annotation. "Velodyne Points" form cy.closeNotification('.ant-notification-notice-info'); cy.get('#cvat-objects-sidebar-state-item-1').should('exist'); cy.removeAnnotations(); - cy.get('button').contains('Save').click(); - cy.get('button').contains('Save').trigger('mouseout'); + cy.clickSaveAnnotationView(); }); it('Upload annotation to task.', () => { @@ -114,8 +113,7 @@ context('Canvas 3D functionality. Dump/upload annotation. "Velodyne Points" form cy.openTaskJob(taskName); cy.get('#cvat-objects-sidebar-state-item-1').should('exist'); cy.removeAnnotations(); - cy.get('button').contains('Save').click(); - cy.get('button').contains('Save').trigger('mouseout'); + cy.clickSaveAnnotationView(); }); }); }); diff --git a/tests/cypress/e2e/issues_prs2/issue_8785_update_job_metadata.js b/tests/cypress/e2e/issues_prs2/issue_8785_update_job_metadata.js index 8cb54ab1974..08b464dcf8a 100644 --- a/tests/cypress/e2e/issues_prs2/issue_8785_update_job_metadata.js +++ b/tests/cypress/e2e/issues_prs2/issue_8785_update_job_metadata.js @@ -9,17 +9,6 @@ import { taskName } from '../../support/const'; context('The UI remains stable even when the metadata request fails.', () => { const issueId = '8785'; - function clickDeleteFrame() { - cy.get('.cvat-player-delete-frame').click(); - cy.get('.cvat-modal-delete-frame').within(() => { - cy.contains('button', 'Delete').click(); - }); - } - function clickSave() { - cy.get('button').contains('Save').click({ force: true }); - cy.get('button').contains('Save').trigger('mouseout'); - } - before(() => { cy.checkDeletedFrameVisibility(); cy.openTaskJob(taskName); @@ -44,10 +33,10 @@ context('The UI remains stable even when the metadata request fails.', () => { cy.intercept(routeMatcher, badResponse).as('patchError'); - clickDeleteFrame(); + cy.clickDeleteFrameAnnotationView(); cy.get('.cvat-player-restore-frame').should('be.visible'); - clickSave(); + cy.clickSaveAnnotationView(); cy.wait('@patchError').then((intercept) => { expect(intercept.response.body).to.equal(badResponse.body); expect(intercept.response.statusCode).to.equal(badResponse.statusCode); diff --git a/tests/cypress/e2e/issues_prs2/issue_8872_delete_frame.js b/tests/cypress/e2e/issues_prs2/issue_8872_delete_frame.js new file mode 100644 index 00000000000..14aff079a82 --- /dev/null +++ b/tests/cypress/e2e/issues_prs2/issue_8872_delete_frame.js @@ -0,0 +1,70 @@ +// Copyright (C) CVAT.ai Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName } from '../../support/const'; + +context('UI and job metadata work correctly when deleting frames', () => { + const chunkReloadPeriod = 100; // 100 ms + let defaultJobMetadataReloadPreiod; + + describe('Attempt to delete any frame after repeated request to /data/meta/', () => { + before(() => { + cy.window().then((window) => { + defaultJobMetadataReloadPreiod = window.cvat.config.jobMetaDataReloadPeriod; + window.cvat.config.jobMetaDataReloadPeriod = chunkReloadPeriod; + }); + }); + + it('Elapse job metadata reload period, delete a frame, validate UI state is and request body ', () => { + let frameNum = null; + function getCurrentFrameNumber() { + cy.get('.cvat-player-frame-selector').within(() => cy.get('[role="spinbutton"]') + .should('have.attr', 'aria-valuenow') + .then((valueFrameNow) => { frameNum = Number(valueFrameNow); })); + } + + cy.intercept('GET', '/api/jobs/**/data/meta**').as('getMeta'); + cy.intercept('PATCH', '/api/jobs/**/data/meta**').as('patchMeta'); + + cy.openTaskJob(taskName); + // Ensure first request is sent after loading the job + cy.wait('@getMeta'); + + cy.goToNextFrame(1); + cy.wait('@getMeta'); + getCurrentFrameNumber(); + + cy.clickDeleteFrameAnnotationView(); + cy.get('.cvat-player-restore-frame').should('not.exist'); + + // Save and intercept request to confirm validate deleted frames + cy.clickSaveAnnotationView(); + cy.wait('@patchMeta').then((interceptDeleted) => { + const deletedFrames = interceptDeleted.request.body.deleted_frames; + + // Check old frame is unavailable + cy.checkFrameNum(frameNum + 1); + + // Check deleted frame are correct + expect(deletedFrames).to.include(frameNum); + }); + // Restore state and save + // Validate UI and that no frames are marked deleted + cy.contains('.cvat-annotation-header-button', 'Undo').click(); + cy.clickSaveAnnotationView(); + cy.wait('@patchMeta').then((interceptRestored) => { + const deletedFrames = interceptRestored.request.body.deleted_frames; + cy.wrap(deletedFrames).should('be.empty'); + cy.checkFrameNum(frameNum); + }); + }); + after(() => { + cy.window().then((window) => { + window.cvat.config.jobMetaDataReloadPeriod = defaultJobMetadataReloadPreiod; + }); + }); + }); +}); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index c9230c91d57..bc929c4b558 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -413,8 +413,7 @@ Cypress.Commands.add('openTask', (taskName, projectSubsetFieldValue) => { Cypress.Commands.add('saveJob', (method = 'PATCH', status = 200, as = 'saveJob') => { cy.intercept(method, '/api/jobs/**').as(as); - cy.get('button').contains('Save').click({ force: true }); - cy.get('button').contains('Save').trigger('mouseout'); + cy.clickSaveAnnotationView(); cy.wait(`@${as}`).its('response.statusCode').should('equal', status); }); @@ -1434,10 +1433,7 @@ Cypress.Commands.add('deleteFrame', (action = 'delete') => { if (action === 'restore') { cy.get('.cvat-player-restore-frame').click(); } else if (action === 'delete') { - cy.get('.cvat-player-delete-frame').click(); - cy.get('.cvat-modal-delete-frame').within(() => { - cy.contains('button', 'Delete').click(); - }); + cy.clickDeleteFrameAnnotationView(); } cy.saveJob('PATCH', 200); cy.wait('@patchMeta').its('response.statusCode').should('equal', 200); @@ -1718,3 +1714,15 @@ Cypress.Commands.overwrite('reload', (orig, options) => { orig(options); cy.closeModalUnsupportedPlatform(); }); + +Cypress.Commands.add('clickDeleteFrameAnnotationView', () => { + cy.get('.cvat-player-delete-frame').click(); + cy.get('.cvat-modal-delete-frame').within(() => { + cy.contains('button', 'Delete').click(); + }); +}); + +Cypress.Commands.add('clickSaveAnnotationView', () => { + cy.get('button').contains('Save').click(); + cy.get('button').contains('Save').trigger('mouseout'); +});