From 09794c52c91364cd21996b37136a3efb23878b7d Mon Sep 17 00:00:00 2001 From: Dmitry Kruchinin <33020454+dvkruchinin@users.noreply.github.com> Date: Thu, 31 Dec 2020 14:00:29 +0300 Subject: [PATCH] Cypress test. Review pipeline feature. (#2555) * First step * Second step. * Third step * Fourth step * Fifth step. * Added css classes. Added commands. Finish writing the test. * Some fix * Updated version * Some tests adaptation * Rework saveJob() command * Tests adaptation. * Apply comments Co-authored-by: Kruchinin Co-authored-by: Boris Sekachev --- .../objects-side-bar/issues-list.tsx | 10 +- .../top-bar/annotation-menu.tsx | 3 + cvat-ui/src/components/task-page/job-list.tsx | 2 +- cvat-ui/src/reducers/notifications-reducer.ts | 1 + .../case_15_group_features.js | 2 +- .../case_22_tag_annotation_mode.js | 2 +- .../case_31_label_constructor_color_label.js | 2 +- .../issue_1568_cuboid_dump_annotation.js | 2 +- ...ror_cannot_read_property_at_saving_job.js} | 3 +- .../case_28_review_pipeline_feature.js | 504 ++++++++++++++++++ .../case_4_assign_taks_job_users.js | 2 +- tests/cypress/support/commands.js | 62 +-- .../support/commands_review_pipeline.js | 154 ++++++ tests/cypress/support/index.js | 1 + 14 files changed, 703 insertions(+), 47 deletions(-) rename tests/cypress/integration/actions_tasks_objects/{pr_2203_error_сannot_read_property_at_saving_job.js => pr_2203_error_cannot_read_property_at_saving_job.js} (98%) create mode 100644 tests/cypress/integration/actions_users/registration_involved/case_28_review_pipeline_feature.js create mode 100644 tests/cypress/support/commands_review_pipeline.js diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/issues-list.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/issues-list.tsx index 84f5120c..31a65ad2 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/issues-list.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/issues-list.tsx @@ -55,22 +55,26 @@ export default function LabelsListComponent(): JSX.Element { - + - + {issuesHidden ? ( dispatch(reviewActions.switchIssuesHiddenFlag(false))} /> ) : ( - dispatch(reviewActions.switchIssuesHiddenFlag(true))} /> + dispatch(reviewActions.switchIssuesHiddenFlag(true))} + /> )} diff --git a/cvat-ui/src/components/annotation-page/top-bar/annotation-menu.tsx b/cvat-ui/src/components/annotation-page/top-bar/annotation-menu.tsx index 3ac000f4..6871ff29 100644 --- a/cvat-ui/src/components/annotation-page/top-bar/annotation-menu.tsx +++ b/cvat-ui/src/components/annotation-page/top-bar/annotation-menu.tsx @@ -69,6 +69,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element { Modal.confirm({ title: 'The job has unsaved annotations', content: 'Would you like to save changes before continue?', + className: 'cvat-modal-content-save-job', okButtonProps: { children: 'Save', }, @@ -135,6 +136,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element { content: 'Status will be changed to "completed". Would you like to continue?', okText: 'Continue', cancelText: 'Cancel', + className: 'cvat-modal-content-finish-job', onOk: () => { checkUnsavedChanges(copyParams); }, @@ -145,6 +147,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element { content: 'Status will be changed to "annotations". Would you like to continue?', okText: 'Continue', cancelText: 'Cancel', + className: 'cvat-modal-content-renew-job', onOk: () => { onClickMenu(copyParams); }, diff --git a/cvat-ui/src/components/task-page/job-list.tsx b/cvat-ui/src/components/task-page/job-list.tsx index fca29a40..0b5833fc 100644 --- a/cvat-ui/src/components/task-page/job-list.tsx +++ b/cvat-ui/src/components/task-page/job-list.tsx @@ -264,7 +264,7 @@ function JobListComponent(props: Props & RouteComponentProps): JSX.Element { {`${completed} of ${data.length} jobs`} - +
'cvat-task-jobs-table-row'} columns={columns} dataSource={data} size='small' /> ); } diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts index 1559e399..7ca5a845 100644 --- a/cvat-ui/src/reducers/notifications-reducer.ts +++ b/cvat-ui/src/reducers/notifications-reducer.ts @@ -688,6 +688,7 @@ export default function (state = defaultState, action: AnyAction): Notifications saving: { message: 'Could not save annotations', reason: action.payload.error.toString(), + className: 'cvat-notification-notice-save-annotations-failed', }, }, }, diff --git a/tests/cypress/integration/actions_tasks_objects/case_15_group_features.js b/tests/cypress/integration/actions_tasks_objects/case_15_group_features.js index e8c2597c..7f94ed15 100644 --- a/tests/cypress/integration/actions_tasks_objects/case_15_group_features.js +++ b/tests/cypress/integration/actions_tasks_objects/case_15_group_features.js @@ -172,7 +172,7 @@ context('Group features', () => { describe(`Testing case "${caseId}". Group color feature.`, () => { before(() => { cy.removeAnnotations(); - cy.saveJob(); + cy.saveJob('PUT'); cy.reload(); cy.closeModalUnsupportedPlatform(); cy.get('.cvat-canvas-container').should('exist'); diff --git a/tests/cypress/integration/actions_tasks_objects/case_22_tag_annotation_mode.js b/tests/cypress/integration/actions_tasks_objects/case_22_tag_annotation_mode.js index 718fb25e..a983f992 100644 --- a/tests/cypress/integration/actions_tasks_objects/case_22_tag_annotation_mode.js +++ b/tests/cypress/integration/actions_tasks_objects/case_22_tag_annotation_mode.js @@ -19,7 +19,7 @@ context('Tag annotation mode.', () => { function checkPresenceFrameTags() { cy.get('.cvat-tag-annotation-sidebar-frame-tags').within(() => { - cy.get('span.cvat-tag-annotation-sidebar-frame-tag-label').should('exist'); + cy.get('.cvat-tag-annotation-sidebar-frame-tag-label').should('exist'); }); } diff --git a/tests/cypress/integration/actions_tasks_objects/case_31_label_constructor_color_label.js b/tests/cypress/integration/actions_tasks_objects/case_31_label_constructor_color_label.js index 7c0ba330..94e4c1d9 100644 --- a/tests/cypress/integration/actions_tasks_objects/case_31_label_constructor_color_label.js +++ b/tests/cypress/integration/actions_tasks_objects/case_31_label_constructor_color_label.js @@ -58,7 +58,7 @@ context('Label constructor. Color label.', () => { after('Remove annotation and save job.', () => { cy.removeAnnotations(); - cy.saveJob(); + cy.saveJob('PUT'); }); describe(`Testing case "${caseId}"`, () => { diff --git a/tests/cypress/integration/actions_tasks_objects/issue_1568_cuboid_dump_annotation.js b/tests/cypress/integration/actions_tasks_objects/issue_1568_cuboid_dump_annotation.js index 4908ab3f..bae7864c 100644 --- a/tests/cypress/integration/actions_tasks_objects/issue_1568_cuboid_dump_annotation.js +++ b/tests/cypress/integration/actions_tasks_objects/issue_1568_cuboid_dump_annotation.js @@ -24,7 +24,7 @@ context('Dump annotation if cuboid created', () => { after('Go to task list', () => { cy.removeAnnotations(); - cy.saveJob(); + cy.saveJob('PUT'); }); describe(`Testing issue "${issueId}"`, () => { diff --git a/tests/cypress/integration/actions_tasks_objects/pr_2203_error_сannot_read_property_at_saving_job.js b/tests/cypress/integration/actions_tasks_objects/pr_2203_error_cannot_read_property_at_saving_job.js similarity index 98% rename from tests/cypress/integration/actions_tasks_objects/pr_2203_error_сannot_read_property_at_saving_job.js rename to tests/cypress/integration/actions_tasks_objects/pr_2203_error_cannot_read_property_at_saving_job.js index 8b398179..ab774f7e 100644 --- a/tests/cypress/integration/actions_tasks_objects/pr_2203_error_сannot_read_property_at_saving_job.js +++ b/tests/cypress/integration/actions_tasks_objects/pr_2203_error_cannot_read_property_at_saving_job.js @@ -7,7 +7,6 @@ import { taskName, labelName } from '../../support/const'; context('Check error сannot read property at saving job', () => { - const prId = '2203'; const createRectangleShape2Points = { points: 'By 2 Points', @@ -25,7 +24,7 @@ context('Check error сannot read property at saving job', () => { after('Remove annotations and save job', () => { cy.removeAnnotations(); - cy.saveJob(); + cy.saveJob('PUT'); }); describe(`Testing pr "${prId}"`, () => { diff --git a/tests/cypress/integration/actions_users/registration_involved/case_28_review_pipeline_feature.js b/tests/cypress/integration/actions_users/registration_involved/case_28_review_pipeline_feature.js new file mode 100644 index 00000000..e50a1708 --- /dev/null +++ b/tests/cypress/integration/actions_users/registration_involved/case_28_review_pipeline_feature.js @@ -0,0 +1,504 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +context('Review pipeline feature', () => { + const caseId = '28'; + const labelName = `Case ${caseId}`; + const taskName = 'Review pipeline feature'; + const attrName = `Attr for ${labelName}`; + const textDefaultValue = 'Some default value for type Text'; + const imagesCount = 30; + 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; + const advancedConfigurationParams = { + multiJobs: true, + segmentSize: 10, + }; + + const createRectangleShape2Points = { + points: 'By 2 Points', + type: 'Shape', + labelName: labelName, + firstX: 250, + firstY: 350, + secondX: 350, + secondY: 450, + }; + + const createRectangleShape2PointsSecond = { + points: 'By 2 Points', + type: 'Shape', + labelName: labelName, + firstX: 400, + firstY: 350, + secondX: 500, + secondY: 450, + }; + + const createPointsShape = { + type: 'Shape', + labelName: labelName, + pointsMap: [{ x: 650, y: 350 }], + complete: true, + numberOfPoints: null, + }; + + const createPointsShapeSecond = { + type: 'Shape', + labelName: labelName, + pointsMap: [{ x: 700, y: 350 }], + complete: true, + numberOfPoints: null, + }; + + const createPointsShapeThird = { + type: 'Shape', + labelName: labelName, + pointsMap: [{ x: 750, y: 350 }], + complete: true, + numberOfPoints: null, + }; + + const createPointsShapeFourth = { + type: 'Shape', + labelName: labelName, + pointsMap: [{ x: 700, y: 400 }], + complete: true, + numberOfPoints: null, + }; + + const secondUserName = 'Pipsecuser'; + const thirdUserName = 'Pipthirduser'; + + const secondUser = { + firstName: `${secondUserName} fitstname`, + lastName: `${secondUserName} lastname`, + emailAddr: `${secondUserName.toLowerCase()}@local.local`, + password: 'UfdU21!dds', + }; + const thirdUser = { + firstName: `${thirdUserName} fitstname`, + lastName: `${thirdUserName} lastname`, + emailAddr: `${thirdUserName.toLowerCase()}@local.local`, + password: 'Fv5Df3#f55g', + }; + + const customeIssueDescription = 'Custom issue'; + + const createIssueRectangle = { + type: 'rectangle', + description: 'rectangle issue', + firstX: 550, + firstY: 100, + secondX: 650, + secondY: 200, + }; + + const createIssuePoint = { + type: 'point', + description: 'point issue', + firstX: 700, + firstY: 100, + }; + + before(() => { + cy.clearLocalStorageSnapshot(); + cy.imageGenerator(imagesFolder, imageFileName, width, height, color, posX, posY, labelName, imagesCount); + cy.createZipArchive(directoryToArchive, archivePath); + cy.visit('auth/register'); + }); + + beforeEach(() => { + cy.restoreLocalStorage(); + }); + + afterEach(() => { + cy.saveLocalStorage(); + }); + + after(() => { + cy.goToTaskList(); + cy.getTaskID(taskName).then(($taskID) => { + cy.deleteTask(taskName, $taskID); + }); + }); + + describe(`Testing "${labelName}"`, () => { + it('Registration of required users.', () => { + cy.userRegistration( + secondUser.firstName, + secondUser.lastName, + secondUserName, + secondUser.emailAddr, + secondUser.password, + ); + cy.logout(secondUserName); + cy.goToRegisterPage(); + cy.userRegistration( + thirdUser.firstName, + thirdUser.lastName, + thirdUserName, + thirdUser.emailAddr, + thirdUser.password, + ); + cy.logout(thirdUserName); + }); + + it('First user login. Create a task. Open the task. Assign to himself.', () => { + cy.login(); + cy.createAnnotationTask( + taskName, + labelName, + attrName, + textDefaultValue, + archiveName, + null, + advancedConfigurationParams, + ); + cy.openTask(taskName); + cy.assignTaskToUser(Cypress.env('user')); + cy.logout(); + }); + + it('Login the second, the third user. The task is missing.', () => { + cy.login(secondUserName, secondUser.password); + cy.contains('.cvat-item-task-name', taskName).should('not.exist'); + cy.logout(secondUserName); + cy.login(thirdUserName, thirdUser.password); + cy.contains('.cvat-item-task-name', taskName).should('not.exist'); + cy.logout(thirdUserName); + }); + + it('First user login. Assign the first job to the second user.', () => { + cy.login(); + cy.openTask(taskName); + cy.assignJobToUser(0, secondUserName); + cy.logout(); + }); + + it('Second user login. Open the task, open the job and annotates it.', () => { + cy.login(secondUserName, secondUser.password); + cy.openTaskJob(taskName); + cy.createRectangle(createRectangleShape2PointsSecond); + for (let i = 1; i < 4; i++) { + cy.createRectangle(createRectangleShape2Points); + cy.goToNextFrame(i); + } + }); + + it('Second user sends the job to review.', () => { + cy.server().route('POST', '/api/v1/server/logs').as('sendLogs'); + cy.interactMenu('Request a review'); + cy.contains('.cvat-modal-content-save-job', 'The job has unsaved annotations') + .should('exist') + .within(() => { + cy.contains('[type="button"]', 'OK').click(); + }); + cy.wait('@sendLogs').its('status').should('equal', 201); + cy.get('.cvat-request-review-dialog') + .should('exist') + .within(() => { + cy.get('.cvat-user-search-field').click(); + }); + cy.get('.ant-select-dropdown').within(() => { + cy.contains(new RegExp(`^${thirdUserName}`, 'g')).click(); + }); + cy.contains('.cvat-request-review-dialog', 'Reviewer:').within(() => { + cy.contains('[type="button"]', 'Submit').click(); + }); + cy.url().should('include', '/tasks'); + cy.contains('.cvat-task-details', taskName).should('exist'); + cy.checkJobStatus(0, 'validation', secondUserName, thirdUserName); // Check status, assignee, reviewer of the job + }); + + it('Second user opens the job again, switches to standard mode and tried to change anything and save changes. The request will be rejected with 403 code.', () => { + cy.openJob(); + cy.get('.cvat-workspace-selector').should('have.text', 'Review'); + cy.changeWorkspace('Standard', labelName); + cy.createPoint(createPointsShape); + cy.saveJob('PATCH', 403); + cy.get('.cvat-notification-notice-save-annotations-failed') + .should('exist') + .within(() => { + cy.get('[data-icon="close"]').click(); // Close the notice. + }); + cy.goToTaskList(); + cy.logout(secondUserName); + }); + + it('The third user opens the job. Review mode is opened automatically.', () => { + cy.login(thirdUserName, thirdUser.password); + cy.openTaskJob(taskName); + cy.get('.cvat-workspace-selector').should('have.text', 'Review'); + }); + + it('Use quick issues "Incorrect position". Issue will be created immediately.', () => { + cy.createIssueFromObject('#cvat_canvas_shape_1', 'Quick issue: incorrect position'); + cy.checkIssueLabel('Wrong position'); + }); + + it('Item submenu: "Quick issue ..." does not appear.', () => { + cy.get('#cvat_canvas_shape_2').trigger('mousemove').rightclick(); + cy.get('.cvat-canvas-context-menu') + .contains('.cvat-context-menu-item', 'Quick issue ...') + .should('not.exist'); + cy.get('.cvat-canvas-container').click(); // Close the context menu + }); + + it('Create different issues with a custom text.', () => { + cy.createIssueFromObject('#cvat_canvas_shape_2', 'Open an issue ...', customeIssueDescription); + cy.checkIssueLabel(customeIssueDescription); + }); + + it('Now item submenu: "Quick issue ..." appears and it contains several latest options.', () => { + cy.get('#cvat_canvas_shape_1').trigger('mousemove', { force: true }).rightclick({ force: true }); + cy.get('.cvat-canvas-context-menu') + .contains('.cvat-context-menu-item', 'Quick issue ...') + .should('exist') + .trigger('mousemove') + .trigger('mouseover'); + cy.get('[id="quick_issue_from_latest$Menu"]').within(() => { + cy.contains('.cvat-context-menu-item', new RegExp(`^${customeIssueDescription}$`, 'g')) + .should('exist') + .and('have.text', customeIssueDescription); + }); + }); + + it('Use one of items to create quick issue on another object on another frame. Issue has been created.', () => { + cy.goCheckFrameNumber(2); + cy.createIssueFromObject('#cvat_canvas_shape_4', 'Quick issue: incorrect attribute'); + cy.checkIssueLabel('Wrong attribute'); + cy.goCheckFrameNumber(0); // Back to first frame + }); + + it('Reload page. All the issue still exists.', () => { + cy.reload(); + cy.get('.cvat-canvas-container').should('exist'); + cy.checkIssueLabel(customeIssueDescription); + cy.checkIssueLabel('Wrong position'); + }); + + it('Use button on the left panel to create a couple of issues (in the first case draw a rectangle, in the second draw a point).', () => { + cy.createIssueFromControlButton(createIssueRectangle); + cy.createIssueFromControlButton(createIssuePoint); + }); + + it('Go to "Standard mode". Create a couple of objects. Save the job. Saving was successful.', () => { + cy.changeWorkspace('Standard', labelName); + cy.createPoint(createPointsShape); + cy.saveJob(); + cy.get('.cvat-notification-notice-save-annotations-failed').should('not.exist'); + }); + + it('Reject review. The third user was redirected to a task page. The job has status "annotation"', () => { + cy.interactMenu('Submit the review'); + cy.submitReview('Reject'); + cy.url().should('include', '/tasks'); + cy.contains('.cvat-task-details', taskName).should('exist'); + cy.checkJobStatus(0, 'annotation', secondUserName, thirdUserName); + }); + + it("Reopen the job. Change something there. Save work. That saving wasn't successful. The third user logout.", () => { + cy.openJob(); + cy.createPoint(createPointsShapeSecond); + cy.saveJob('PATCH', 403); + cy.get('.cvat-notification-notice-save-annotations-failed') + .should('exist') + .within(() => { + cy.get('[data-icon="close"]').click(); // Close the notice. + }); + cy.goToTaskList(); + cy.logout(thirdUserName); + }); + + it('The second user login. Opens the job again. All issues are visible.', () => { + cy.login(secondUserName, secondUser.password); + cy.openTaskJob(taskName); + cy.get('.cvat-workspace-selector').should('have.text', 'Standard'); + for (const j of [ + customeIssueDescription, + 'Wrong position', + createIssueRectangle.description, + createIssuePoint.description, + ]) { + cy.checkIssueLabel(j); + } + cy.goCheckFrameNumber(2); + cy.checkIssueLabel('Wrong attribute'); + cy.goCheckFrameNumber(0); + }); + + it('Go to "Issues" tab at right sidebar and select an issue.', () => { + cy.get('.cvat-objects-sidebar').within(() => { + cy.contains('Issues').click(); + }); + cy.get('.cvat-objects-sidebar-issue-item').then((sidebarIssueItems) => { + cy.get('.cvat-hidden-issue-label').then((issueLabels) => { + expect(sidebarIssueItems.length).to.be.equal(issueLabels.length); + }); + }); + }); + + it('Select an issue on sidebar. Issue indication has changed the color for highlighted issue', () => { + let index = 0; + cy.collectIssueRegionId().then(($issueRegionList) => { + cy.get('.cvat-objects-sidebar-issue-item').then((sidebarIssueItems) => { + for (let i = 0; i < sidebarIssueItems.length; i++) { + cy.get(sidebarIssueItems[i]).trigger('mousemove').trigger('mouseover'); + cy.get(`#cvat_canvas_issue_region_${$issueRegionList[index]}`).should( + 'have.attr', + 'fill', + 'url(#cvat_issue_region_pattern_2)', + ); + cy.get(sidebarIssueItems[i]).trigger('mouseout'); + cy.get(`#cvat_canvas_issue_region_${$issueRegionList[index]}`).should( + 'have.attr', + 'fill', + 'url(#cvat_issue_region_pattern_1)', + ); + index++; + } + }); + }); + }); + + it('Issue navigation. Navigation works and go only to frames with issues.', () => { + cy.get('.cvat-issues-sidebar-previous-frame').should('have.attr', 'style').and('contain', 'opacity: 0.5;'); // The element is not active + cy.get('.cvat-issues-sidebar-next-frame').click(); + cy.checkFrameNum(2); // Frame changed to 2 + cy.get('.cvat-issues-sidebar-next-frame').should('have.attr', 'style').and('contain', 'opacity: 0.5;'); // The element is not active + cy.get('.cvat-issues-sidebar-previous-frame').click(); + cy.checkFrameNum(0); // Frame changed to 0 + }); + + it('Hide all issues. All issues are hidden on all frames.', () => { + cy.get('.cvat-issues-sidebar-shown-issues').click(); + cy.get('.cvat-hidden-issue-label').should('not.exist'); + cy.get('.cvat-issues-sidebar-next-frame').click(); + cy.get('.cvat-hidden-issue-label').should('not.exist'); + cy.get('.cvat-issues-sidebar-previous-frame').click(); + }); + + it('Display all the issues again. Comment a couple of issues and resolve all them.', () => { + function resolveIssue() { + cy.collectIssueLabel().then((issueLabelList) => { + for (let label = 0; label < issueLabelList.length; label++) { + cy.resolveIssue(issueLabelList[label], 'Done'); + } + }); + } + + cy.get('.cvat-issues-sidebar-hidden-issues').click(); + cy.get('.cvat-hidden-issue-label').should('exist').and('have.length', 4); + cy.get('.cvat-issues-sidebar-next-frame').click(); + cy.get('.cvat-hidden-issue-label').should('exist').and('have.length', 1); + cy.get('.cvat-issues-sidebar-previous-frame').click(); + + resolveIssue(); + cy.checkIssueLabel('Done', 'resolved'); + cy.goCheckFrameNumber(2); + resolveIssue(); + cy.checkIssueLabel('Done', 'resolved'); + cy.goCheckFrameNumber(0); + }); + + it('Request a review again. Assign the third user again. The second user logout.', () => { + cy.interactMenu('Request a review'); + cy.contains('.cvat-request-review-dialog', 'Reviewer:').within(() => { + cy.get('.cvat-user-search-field').within(() => { + cy.get('input[type="search"]').should('have.value', thirdUserName); + }); + cy.contains('[type="button"]', 'Submit').click(); + }); + cy.logout(secondUserName); + }); + + it('The third user login, opens the job, goes to menu, "Submit review" => "Review next" => Assign the first user => Submit.', () => { + cy.login(thirdUserName, thirdUser.password); + cy.openTaskJob(taskName); + cy.interactMenu('Submit the review'); + cy.submitReview('Review next', Cypress.env('user')); + cy.get('.cvat-not-found').should('exist'); + }); + it('The third user logout. The first user login and opens the job, goes to menu, "Submit review" => Accept => Submit', () => { + cy.logout(thirdUserName); + cy.login(); + cy.openTaskJob(taskName); + cy.interactMenu('Submit the review'); + cy.submitReview('Accept'); + cy.url().should('include', '/tasks'); + cy.contains('.cvat-task-details', taskName).should('exist'); + cy.checkJobStatus(0, 'completed', secondUserName, Cypress.env('user')); + }); + + it("The first user can change annotations. The second users can't change annotations. For the third user the task is not visible.", () => { + cy.openJob(); + cy.createPoint(createPointsShapeThird); + cy.saveJob(); + cy.get('.cvat-notification-notice-save-annotations-failed').should('not.exist'); + cy.logout(); + cy.login(secondUserName, secondUser.password); + cy.openTaskJob(taskName); + cy.createPoint(createPointsShapeFourth); + cy.saveJob(); + cy.get('.cvat-notification-notice-save-annotations-failed').should('exist'); + cy.goToTaskList(); + cy.logout(secondUserName); + cy.login(thirdUserName, thirdUser.password); + cy.contains('strong', taskName).should('not.exist'); + cy.goToTaskList(); + cy.logout(thirdUserName); + }); + + it('The first user opens the job and presses "Renew the job".', () => { + cy.login(); + cy.openTaskJob(taskName); + cy.interactMenu('Renew the job'); + cy.get('.cvat-modal-content-renew-job').within(() => { + cy.contains('button', 'Continue').click(); + }); + cy.url().should('include', '/tasks'); + cy.contains('.cvat-task-details', taskName).should('exist'); + cy.checkJobStatus(0, 'annotation', secondUserName, Cypress.env('user')); + }); + + it('The first user opens the job and presses "Finish the job".', () => { + cy.openJob(); + cy.interactMenu('Finish the job'); + cy.get('.cvat-modal-content-finish-job').within(() => { + cy.contains('button', 'Continue').click(); + }); + cy.url().should('include', '/tasks'); + cy.contains('.cvat-task-details', taskName).should('exist'); + cy.checkJobStatus(0, 'completed', secondUserName, Cypress.env('user')); + }); + + it('In column "status" the job has question circle. The first user hover it, short statistics about reviews shown.', () => { + cy.get('.cvat-job-completed-color').within(() => { + cy.get('[aria-label="question-circle"]').trigger('mouseover'); + }); + let summary = []; + cy.get('.cvat-review-summary-description').within(() => { + cy.get('td').then(($td) => { + for (let i = 0; i < $td.length; i++) { + summary.push($td[i].outerText); + } + expect(Number(summary[1])).to.be.equal(3); // Reviews 3 + expect(Number(summary[5])).to.be.equal(0); // Unsolved issues 0 + expect(Number(summary[7])).to.be.equal(5); // Resolved issues 5 + }); + }); + }); + }); +}); diff --git a/tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js b/tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js index 4dacb700..4fc1ef3a 100644 --- a/tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js +++ b/tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js @@ -95,7 +95,7 @@ context('Multiple users. Assign task, job.', () => { it('First user login and assign the job to the third user. Logout', () => { cy.login(); cy.openTask(taskName); - cy.assignJobToUser(thirdUserName); + cy.assignJobToUser(0, thirdUserName); cy.logout(); }); it('Third user login. The task can be opened.', () => { diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index d15429bb..a90058e5 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -103,31 +103,35 @@ Cypress.Commands.add('openTask', (taskName) => { cy.get('.cvat-task-details').should('exist'); }); -Cypress.Commands.add('saveJob', () => { - cy.server().route('POST', '/api/v1/server/logs').as('sendLogs'); +Cypress.Commands.add('saveJob', (method = 'PATCH', status = 200) => { + cy.server().route(method, '/api/v1/jobs/**').as('saveJob'); cy.get('button').contains('Save').click({ force: true }); - cy.wait('@sendLogs').its('status').should('equal', 201); + cy.wait('@saveJob').its('status').should('equal', status); }); -Cypress.Commands.add('openJob', (jobNumber = 0) => { - let tdText = ''; - cy.get('.ant-table-tbody') +Cypress.Commands.add('getJobNum', (jobID) => { + cy.get('.cvat-task-jobs-table') .contains(/^0-/) - .parent() + .parents('.cvat-task-jobs-table-row') .find('td') .eq(0) .invoke('text') .then(($tdText) => { - tdText = Number($tdText.match(/\d+/g)) + jobNumber; - cy.get('.ant-table-tbody').contains('a', `Job #${tdText}`).click(); + return Number($tdText.match(/\d+/g)) + jobID; }); +}); + +Cypress.Commands.add('openJob', (jobID = 0) => { + 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'); }); -Cypress.Commands.add('openTaskJob', (taskName, jobNumber = 0) => { +Cypress.Commands.add('openTaskJob', (taskName, jobID = 0) => { cy.openTask(taskName); - cy.openJob(jobNumber); + cy.openJob(jobID); }); Cypress.Commands.add('createRectangle', (createRectangleParams) => { @@ -505,30 +509,6 @@ Cypress.Commands.add('goToRegisterPage', () => { cy.url().should('include', '/auth/register'); }); -Cypress.Commands.add('assignTaskToUser', (user) => { - cy.get('.cvat-task-details-user-block').within(() => { - cy.get('.cvat-user-search-field').click().type(user); - cy.wait(300); - }); - cy.get('.ant-select-dropdown') - .not('.ant-select-dropdown-hidden') - .within(() => { - cy.get(`.ant-select-item-option[title="${user}"]`).click(); - }); -}); - -Cypress.Commands.add('assignJobToUser', (user) => { - cy.get('.cvat-task-job-list').within(() => { - cy.get('.cvat-job-assignee-selector').click().type(user); - cy.wait(300); - }); - cy.get('.ant-select-dropdown') - .not('.ant-select-dropdown-hidden') - .within(() => { - cy.get(`.ant-select-item-option[title="${user}"]`).click(); - }); -}); - Cypress.Commands.add('getScaleValue', () => { cy.get('#cvat_canvas_background') .should('have.attr', 'style') @@ -567,7 +547,10 @@ Cypress.Commands.add('selectFilterValue', (clear, filterValue) => { Cypress.Commands.add('goCheckFrameNumber', (frameNum) => { cy.get('.cvat-player-frame-selector').within(() => { - cy.get('input[role="spinbutton"]').clear().type(`${frameNum}{Enter}`).should('have.value', frameNum); + cy.get('input[role="spinbutton"]') + .clear({ force: true }) + .type(`${frameNum}{Enter}`, { force: true }) + .should('have.value', frameNum); }); }); @@ -587,6 +570,13 @@ Cypress.Commands.add('goToPreviousFrame', (expectedFrameNum) => { cy.checkFrameNum(expectedFrameNum); }); +Cypress.Commands.add('interactMenu', (choice) => { + cy.contains('.cvat-annotation-header-button', 'Menu').click(); + cy.get('.cvat-annotation-menu').within(() => { + cy.contains(new RegExp(`^${choice}$`, 'g')).click(); + }); +}); + Cypress.Commands.add('closeNotification', (className) => { cy.get(className).find('span[aria-label="close"]').click(); cy.get(className).should('not.exist'); diff --git a/tests/cypress/support/commands_review_pipeline.js b/tests/cypress/support/commands_review_pipeline.js new file mode 100644 index 00000000..91425c35 --- /dev/null +++ b/tests/cypress/support/commands_review_pipeline.js @@ -0,0 +1,154 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +Cypress.Commands.add('assignTaskToUser', (user) => { + cy.get('.cvat-task-details-user-block').within(() => { + cy.get('.cvat-user-search-field').click(); + }); + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .contains(new RegExp(`^${user}$`, 'g')) + .click(); +}); + +Cypress.Commands.add('assignJobToUser', (jobID, user) => { + cy.getJobNum(jobID).then(($job) => { + cy.get('.cvat-task-jobs-table') + .contains('a', `Job #${$job}`) + .parents('.cvat-task-jobs-table-row') + .find('.cvat-job-assignee-selector') + .click(); + }); + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .contains(new RegExp(`^${user}$`, 'g')) + .click(); +}); + +Cypress.Commands.add('checkJobStatus', (jobID, status, assignee, reviewer) => { + cy.getJobNum(jobID).then(($job) => { + cy.get('.cvat-task-jobs-table') + .contains('a', `Job #${$job}`) + .parents('.cvat-task-jobs-table-row') + .within(() => { + cy.get('.cvat-job-item-status').should('have.text', status); + cy.get('.cvat-job-assignee-selector').within(() => { + cy.get('input[type="search"]').should('have.value', assignee); + }); + cy.get('.cvat-job-reviewer-selector').within(() => { + cy.get('input[type="search"]').should('have.value', reviewer); + }); + }); + }); +}); + +Cypress.Commands.add('collectIssueLabel', () => { + cy.document().then((doc) => { + return Array.from(doc.querySelectorAll('.cvat-hidden-issue-label')); + }); +}); + +Cypress.Commands.add('checkIssueLabel', (issueDescription, status = 'unsolved') => { + cy.collectIssueLabel().then((issueLabelList) => { + for (let i = 0; i < issueLabelList.length; i++) { + cy.get(issueLabelList[i]) + .invoke('text') + .then((issueText) => { + if (issueText === issueDescription) { + cy.get(issueLabelList[i]) + .should('exist') + .and('have.text', issueDescription) + .within(() => { + cy.get(`.cvat-hidden-issue-${status}-indicator`).should('exist'); // "unsolved" or "resolved" only. + }); + } + }); + } + }); +}); + +Cypress.Commands.add('collectIssueRegionId', () => { + let issueRegionIdList = []; + cy.document().then((doc) => { + const issueRegionList = Array.from(doc.querySelectorAll('.cvat_canvas_issue_region')); + for (let i = 0; i < issueRegionList.length; i++) { + issueRegionIdList.push(Number(issueRegionList[i].id.match(/\d+$/))); + } + return issueRegionIdList; + }); +}); + +Cypress.Commands.add('checkIssueRegion', (afterSave = false) => { + const sccSelectorIssueRegionId = '#cvat_canvas_issue_region_'; + cy.collectIssueRegionId().then((issueRegionIdList) => { + const maxId = Math.max(...issueRegionIdList); + if (!afterSave) { + cy.get(`${sccSelectorIssueRegionId}-${maxId}`).trigger('mousemove').should('exist').and('be.visible'); + } else { + cy.get(`${sccSelectorIssueRegionId}${maxId}`).trigger('mousemove').should('exist').and('be.visible'); + } + }); +}); + +Cypress.Commands.add('createIssueFromObject', (object, issueType, customeIssueDescription) => { + cy.get(object).trigger('mousemove').rightclick(); + cy.get('.cvat-canvas-context-menu').within(() => { + cy.contains('.cvat-context-menu-item', new RegExp(`^${issueType}$`, 'g')).click(); + }); + if (issueType === 'Open an issue ...') { + cy.get('.cvat-create-issue-dialog').within(() => { + cy.get('#issue_description').type(customeIssueDescription); + cy.get('[type="submit"]').click(); + }); + } else if (issueType === 'Quick issue ...') { + cy.get('[id="quick_issue_from_latest$Menu"]') + .should('be.visible') + .contains('.cvat-context-menu-item', new RegExp(`^${customeIssueDescription}$`, 'g')) + .click(); + } + cy.checkIssueRegion(); +}); + +Cypress.Commands.add('createIssueFromControlButton', (createIssueParams) => { + cy.get('.cvat-issue-control').click(); + if (createIssueParams.type === 'rectangle') { + cy.get('.cvat-canvas-container') + .trigger('mousedown', createIssueParams.firstX, createIssueParams.firstY, { button: 0 }) + .trigger('mousemove', createIssueParams.secondX, createIssueParams.secondY) + .trigger('mouseup'); + } else if (createIssueParams.type === 'point') { + cy.get('.cvat-canvas-container').click(createIssueParams.firstX, createIssueParams.firstY); + } + cy.get('.cvat-create-issue-dialog').within(() => { + cy.get('#issue_description').type(createIssueParams.description); + cy.get('[type="submit"]').click(); + }); + cy.checkIssueRegion(); +}); + +Cypress.Commands.add('resolveIssue', (issueLabel, resolveText) => { + cy.get(issueLabel).click(); + cy.get('.cvat-issue-dialog-input').type(resolveText); + cy.get('.cvat-issue-dialog-footer').within(() => { + cy.contains('button', 'Comment').click(); + cy.contains('button', 'Resolve').click(); + }); +}); + +Cypress.Commands.add('submitReview', (decision, user) => { + cy.get('.cvat-submit-review-dialog').within(() => { + cy.contains(new RegExp(`^${decision}$`, 'g')).click(); + if (decision === 'Review next') { + cy.server().route('GET', `/api/v1/users?search=${user}&limit=10`).as('searchUsers'); + cy.get('.cvat-user-search-field').within(() => { + cy.get('input[type="search"]').clear().type(`${user}`); + cy.wait('@searchUsers').its('status').should('equal', 200); + cy.get('input[type="search"]').type('{Enter}'); + }); + } + cy.contains('button', 'Submit').click(); + }); +}); diff --git a/tests/cypress/support/index.js b/tests/cypress/support/index.js index df1274b5..f05ba743 100644 --- a/tests/cypress/support/index.js +++ b/tests/cypress/support/index.js @@ -4,6 +4,7 @@ require('./commands'); require('./commands_projects'); +require('./commands_review_pipeline'); require('@cypress/code-coverage/support'); require('cypress-plugin-tab');