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');