From 9e18612680b55f51b35c45f3f807f2a35cae38bb Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Mon, 10 Mar 2025 14:22:56 +0000 Subject: [PATCH 01/22] cherry 2 --- tests/cypress/e2e/features/consensus.js | 109 ++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 6 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 8d9814e40fe..62b25188b7f 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -1,17 +1,19 @@ +/* eslint-disable */ // Copyright (C) CVAT.ai Corporation // // SPDX-License-Identifier: MIT /// + context('Basic manipulations with consensus job replicas', () => { + const maxReplicas = 10; + const taskName = 'Test consensus'; + const labelName = 'test'; + const serverFiles = ['archive.zip']; + const replicas = 3; + const jobIDs = []; describe('Consensus task creation', () => { - const maxReplicas = 10; - const taskName = 'Test consensus'; - const labelName = 'test'; - const serverFiles = ['archive.zip']; - const replicas = 3; - before(() => { cy.visit('auth/login'); cy.login(); @@ -80,4 +82,99 @@ context('Basic manipulations with consensus job replicas', () => { }); }); }); + describe('Cosensus jobs merging', () => { + const shape = { + objectType: 'shape', + labelName, + frame: 0, + type: 'rectangle', + points: [250, 64, 491, 228], + occluded: false, + }; + it("Check new merge buttons exist and are visible. Trying to merge 'new' jobs should trigger errors", () => { + // Merge all consensus jobs in task + cy.mergeConsensusTask(400); + cy.get('.cvat-notification-notice-consensus-merge-task-failed') + .should('be.visible') + .invoke('text') + .should('include', 'Could not merge the task'); + cy.closeNotification('.cvat-notification-notice-consensus-merge-task-failed'); + + // Merge one consensus job + cy.mergeConsensusJob(400); + cy.get('.cvat-notification-notice-consensus-merge-task-failed') + .should('be.visible') + .invoke('text').should('include', 'Could not merge the job'); + cy.closeNotification('.cvat-notification-notice-consensus-merge-task-failed'); + }); + + it('Check consensus management page', () => { + const defaultQuorum = 50; + const defaultIoU = 40; + cy.contains('button', 'Actions').click(); + cy.contains('Consensus management').should('be.visible').click(); + cy.get('.cvat-consensus-management-inner').should('be.visible'); + // Save settings, confirm request is sent + let requestCount = 0; + cy.intercept('PATCH', 'api/consensus/settings/**', () => { + requestCount++; + }).as('settingsMeta'); + cy.contains('button', 'Save').click(); + cy.wait('@settingsMeta'); + cy.get('.ant-notification-notice-message') + .should('be.visible') + .invoke('text') + .should('eq', 'Settings have been updated'); + cy.closeNotification('.ant-notification-notice-closable'); + + // Forms and invalid saving + cy.get('#quorum').then(([$el]) => { + cy.wrap($el).invoke('val').should('eq', `${defaultQuorum}`); + cy.wrap($el).clear(); + }); + cy.get('.ant-form-item-explain-error').should('be.visible'); + cy.contains('button', 'Save').click(); + cy.get('#iouThreshold').then(([$el]) => { + cy.wrap($el).invoke('val').should('eq', `${defaultIoU}`); + cy.wrap($el).clear(); + }); + cy.get('.ant-form-item-explain-error').should('be.visible'); + cy.contains('button', 'Save').click(); + cy.then(() => { + expect(requestCount).to.equal(1); + }); + cy.get('.ant-notification-notice').should('not.exist'); + + // Go back to task page + cy.get('.ant-btn-default').should('be.visible').click(); + }); + + it('Create annotations and check that job replicas merge correctly', () => { + // Create annotations for job replicas + const shapes = []; + const delta = 50; + const consensusJobID = jobIDs[0]; + for ( + let i = 1, jobID = jobIDs[i], s = shape; + i <= replicas; + i++, jobID = jobIDs[i] + ) { + shapes.push(s); + cy.headlessCreateObjects([s], jobID); // only 'in progress' jobs can be merged + // cy.headlessUpdateJob(jobID, { state: 'in progress' }); + const points = translatePoints(s.points, delta, 'x'); + s = { ...s, points }; + } + // Should trigger merging + cy.mergeConsensusJob(); + cy.get('.cvat-notification-notice-consensus-merge-task-failed').should('not.exist'); + cy.get('.ant-notification-notice-info') + .should('be.visible') + .invoke('text') + .should('eq', `Consensus job #${consensusJobID} has been merged`); + + // const middle = Math.floor(shapes.length / 2); + // TODO: check consensus job annotation matches middle job annotation + }); + }); }); From a5335a2adf10629359f6d1d4f62e0c12d319e8c0 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Mon, 10 Mar 2025 14:23:48 +0000 Subject: [PATCH 02/22] cherry pick 3 --- tests/cypress/e2e/features/consensus.js | 54 +++++++++++++------ .../issue_8952_interpolation_impossible.js | 22 +------- tests/cypress/support/commands.js | 8 +++ tests/cypress/support/utils.js | 20 +++++++ 4 files changed, 69 insertions(+), 35 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 62b25188b7f..9ac710e0934 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -151,30 +151,54 @@ context('Basic manipulations with consensus job replicas', () => { it('Create annotations and check that job replicas merge correctly', () => { // Create annotations for job replicas - const shapes = []; const delta = 50; - const consensusJobID = jobIDs[0]; - for ( - let i = 1, jobID = jobIDs[i], s = shape; - i <= replicas; - i++, jobID = jobIDs[i] - ) { - shapes.push(s); - cy.headlessCreateObjects([s], jobID); // only 'in progress' jobs can be merged - // cy.headlessUpdateJob(jobID, { state: 'in progress' }); + const [consensusJobID, ...replicaJobIDs] = jobIDs; + for (let i = 0, s = shape; i < replicas; i++) { + cy.headlessCreateObjects([s], replicaJobIDs[i]); // only 'in progress' jobs can be merged + cy.headlessUpdateJob(replicaJobIDs[i], { state: 'in progress' }); const points = translatePoints(s.points, delta, 'x'); s = { ...s, points }; } - // Should trigger merging + // Merging of consensus job should go without errors in network and UI cy.mergeConsensusJob(); - cy.get('.cvat-notification-notice-consensus-merge-task-failed').should('not.exist'); - cy.get('.ant-notification-notice-info') + cy.get('.cvat-notification-notice-consensus-merge-job-failed').should('not.exist'); + cy.get('.ant-notification-notice-message') .should('be.visible') .invoke('text') .should('eq', `Consensus job #${consensusJobID} has been merged`); + cy.closeNotification('.ant-notification-notice-closable'); - // const middle = Math.floor(shapes.length / 2); - // TODO: check consensus job annotation matches middle job annotation + // Shapes in consensus job and a job replica in the middle should be equal + const middle = Math.floor(jobIDs.length / 2); + const consensusRect = {}; + cy.openJob(0, false).then(() => { + cy.get('.cvat_canvas_shape').trigger('mousemove'); + cy.get('.cvat_canvas_shape').then(($el) => { + consensusRect.x = $el.attr('x'); + consensusRect.y = $el.attr('y'); + consensusRect.width = $el.attr('width'); + consensusRect.height = $el.attr('height'); + }); + cy.get('#cvat_canvas_text_content').should('be.visible') + .invoke('text') + .should('include', `${labelName}`) + .and('include', 'consensus'); + }); + cy.go('back'); // go to previous page + // After returning to task page, consensus job should be 'completed' + cy.get('.cvat-job-item').first() + .find('.cvat-job-item-state').first() + .invoke('text') + .should('eq', 'completed'); + cy.contains('.cvat-job-item', `Job #${jobIDs[middle]}`).scrollIntoView(); + cy.openJob(middle, false).then(() => { + cy.get('.cvat_canvas_shape').then(($el) => { + expect($el.attr('x')).to.equal(consensusRect.x); + expect($el.attr('y')).to.equal(consensusRect.y); + expect($el.attr('width')).to.equal(consensusRect.width); + expect($el.attr('height')).to.equal(consensusRect.height); + }); + }); }); }); }); diff --git a/tests/cypress/e2e/issues_prs2/issue_8952_interpolation_impossible.js b/tests/cypress/e2e/issues_prs2/issue_8952_interpolation_impossible.js index 51a599672aa..352792431db 100644 --- a/tests/cypress/e2e/issues_prs2/issue_8952_interpolation_impossible.js +++ b/tests/cypress/e2e/issues_prs2/issue_8952_interpolation_impossible.js @@ -4,6 +4,8 @@ /// +import { translatePoints } from '../../support/utils'; + const taskName = '5frames'; const labelName = 'label'; const attrName = 'attr1'; @@ -27,26 +29,6 @@ const rect = [ 30 + 23, ]; -function translatePoints(points, delta, axis) { - if (axis === 'x') { - return [ - points[0] + delta, - points[1], - points[2] + delta, - points[3], - ]; - } - if (axis === 'y') { - return [ - points[0], - points[1] + delta, - points[2], - points[3] + delta, - ]; - } - return points; -} - context('Create any track, check if track works correctly after deleting some frames', () => { function readShapeCoords() { return cy.get('.cvat_canvas_shape').then(($shape) => ({ diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index b1db6c92be7..eaaccfd3287 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -450,6 +450,14 @@ Cypress.Commands.add('headlessCreateJob', (jobSpec) => { }); }); +Cypress.Commands.add('headlessUpdateJob', (jobID, updateJobParameters) => { + cy.window().then(async ($win) => { + const job = (await $win.cvat.jobs.get({ jobID }))[0]; + const result = await job.save(updateJobParameters); + return cy.wrap(result); + }); +}); + Cypress.Commands.add('openTask', (taskName, projectSubsetFieldValue) => { cy.contains('strong', new RegExp(`^${taskName}$`)) .parents('.cvat-tasks-list-item') diff --git a/tests/cypress/support/utils.js b/tests/cypress/support/utils.js index 441fae14497..12cd0f61ac2 100644 --- a/tests/cypress/support/utils.js +++ b/tests/cypress/support/utils.js @@ -21,3 +21,23 @@ export function decomposeMatrix(matrix) { const skewX = ((180 / Math.PI) * Math.atan2(px.y, px.x) - 90).toFixed(1); return skewX; } + +export function translatePoints(points, delta, axis) { + if (axis === 'x') { + return [ + points[0] + delta, + points[1], + points[2] + delta, + points[3], + ]; + } + if (axis === 'y') { + return [ + points[0], + points[1] + delta, + points[2], + points[3] + delta, + ]; + } + return points; +} From a5a93b55d325c56e25640ca1e04105382060f92e Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Sat, 8 Mar 2025 16:53:38 +0000 Subject: [PATCH 03/22] cherry -pick 3 --- tests/cypress/e2e/features/consensus.js | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 9ac710e0934..af45ba3fdd5 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -7,20 +7,13 @@ context('Basic manipulations with consensus job replicas', () => { - const maxReplicas = 10; - const taskName = 'Test consensus'; - const labelName = 'test'; - const serverFiles = ['archive.zip']; - const replicas = 3; const jobIDs = []; - describe('Consensus task creation', () => { - before(() => { - cy.visit('auth/login'); - cy.login(); - cy.get('.cvat-create-task-dropdown').click(); - cy.get('.cvat-create-task-button').should('be.visible').click(); - }); - + const labelName = 'test4r'; + const replicas = 4; + describe('Consensus job creation', () => { + const maxReplicas = 10; + const taskName = 'Test consensus'; + const serverFiles = ['archive.zip']; it('Check allowed number of replicas', () => { // Fill the fields to create the task cy.get('#name').type(taskName); @@ -82,6 +75,7 @@ context('Basic manipulations with consensus job replicas', () => { }); }); }); + describe('Cosensus jobs merging', () => { const shape = { objectType: 'shape', From 0941d58fbc23684f0db0d133859811ed76c4d4a5 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Sat, 8 Mar 2025 16:53:38 +0000 Subject: [PATCH 04/22] cherry -pick 4 --- .../consensus-settings-tab.tsx | 7 ++--- tests/cypress/e2e/features/consensus.js | 10 ++++++- tests/cypress/support/commands.js | 27 +++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/cvat-ui/src/components/consensus-management-page/consensus-settings-tab.tsx b/cvat-ui/src/components/consensus-management-page/consensus-settings-tab.tsx index 856529b3207..26023e18ae4 100644 --- a/cvat-ui/src/components/consensus-management-page/consensus-settings-tab.tsx +++ b/cvat-ui/src/components/consensus-management-page/consensus-settings-tab.tsx @@ -23,9 +23,10 @@ function ConsensusSettingsTab(props: Readonly): JSX.Element | null { } = props; const [form] = Form.useForm(); - const onSave = useCallback(async () => { - const values = await form.validateFields(); - setSettings(values); + const onSave = useCallback(() => { + form.validateFields().then((values) => { + setSettings(values); + }).catch(() => { /* do nothing */ }); }, [form, setSettings]); if (fetching) { diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index af45ba3fdd5..167a333bace 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -1,12 +1,18 @@ -/* eslint-disable */ // Copyright (C) CVAT.ai Corporation // // SPDX-License-Identifier: MIT /// +import { translatePoints } from '../../support/utils'; context('Basic manipulations with consensus job replicas', () => { + before(() => { + cy.visit('auth/login'); + cy.login(); + cy.get('.cvat-create-task-dropdown').click(); + cy.get('.cvat-create-task-button').should('be.visible').click(); + }); const jobIDs = []; const labelName = 'test4r'; const replicas = 4; @@ -68,8 +74,10 @@ context('Basic manipulations with consensus job replicas', () => { } cy.get('.cvat-job-item').then((jobItems) => { const sourceJobId = parseJobId(jobItems[0]); + jobIDs.push(sourceJobId); for (let i = 1; i <= replicas; i++) { const jobId = parseJobId(jobItems[i]); + jobIDs.push(jobId); expect(jobId).equals(sourceJobId + i); } }); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index eaaccfd3287..998ce2ebf07 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -1841,3 +1841,30 @@ Cypress.Commands.add('applyActionToSliders', (wrapper, slidersClassNames, action }); cy.get('.ant-tooltip').invoke('hide'); }); + +Cypress.Commands.add('mergeConsensusTask', (status = 202) => { + cy.intercept('POST', '/api/consensus/merges**').as('mergeJobs'); + + cy.get('.cvat-task-details-wrapper').should('be.visible'); + cy.contains('button', 'Actions').click(); + cy.contains('Merge consensus jobs').should('be.visible').click(); + cy.get('.cvat-modal-confirm-consensus-merge-task') + .contains('button', 'Merge') + .click(); + + cy.wait('@mergeJobs').its('response.statusCode').should('eq', status); +}); + +Cypress.Commands.add('mergeConsensusJob', (status = 202) => { + cy.intercept('POST', '/api/consensus/merges**').as('mergeJob'); + + cy.get('.cvat-job-item').first().within(() => { + cy.get('.anticon-more').first().click(); + }); + cy.get('.ant-dropdown-menu').contains('li', 'Merge consensus job').click(); + cy.get('.cvat-modal-confirm-consensus-merge-job') + .contains('button', 'Merge') + .click(); + + cy.wait('@mergeJob').its('response.statusCode').should('eq', status); +}); From 52adfc685cece2f4659c41f76550815f393bc9fc Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Mon, 10 Mar 2025 10:49:29 +0000 Subject: [PATCH 05/22] fix archiver path checks --- tests/cypress/plugins/createZipArchive/addPlugin.js | 6 +++--- .../plugins/createZipArchive/createZipArchiveCommand.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/cypress/plugins/createZipArchive/addPlugin.js b/tests/cypress/plugins/createZipArchive/addPlugin.js index 882b6c5c037..7ab93407ba0 100644 --- a/tests/cypress/plugins/createZipArchive/addPlugin.js +++ b/tests/cypress/plugins/createZipArchive/addPlugin.js @@ -10,9 +10,9 @@ const archiver = require('archiver'); const fs = require('fs-extra'); function createZipArchive(args) { - const { directoryToArchive } = args; + const { directoryToArchive, archivePath } = args; const { level } = args; - const output = fs.createWriteStream(args.arhivePath); + const output = fs.createWriteStream(archivePath); const archive = archiver('zip', { gzip: true, zlib: { level }, @@ -27,5 +27,5 @@ function createZipArchive(args) { archive.directory(`${directoryToArchive}/`, false); archive.finalize(); - return fs.pathExists(archive); + return fs.pathExists(archivePath); } diff --git a/tests/cypress/plugins/createZipArchive/createZipArchiveCommand.js b/tests/cypress/plugins/createZipArchive/createZipArchiveCommand.js index 5b27347906d..906ab689fbf 100644 --- a/tests/cypress/plugins/createZipArchive/createZipArchiveCommand.js +++ b/tests/cypress/plugins/createZipArchive/createZipArchiveCommand.js @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: MIT -Cypress.Commands.add('createZipArchive', (directoryToArchive, arhivePath, level = 9) => cy.task('createZipArchive', { +Cypress.Commands.add('createZipArchive', (directoryToArchive, archivePath, level = 9) => cy.task('createZipArchive', { directoryToArchive, - arhivePath, + archivePath, level, })); From 4ebad28c3ea38103e7a9dd91990b9eaf7bb9a008 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Mon, 10 Mar 2025 14:42:44 +0000 Subject: [PATCH 06/22] check tests pass, add eslint rules for fixed plugin --- tests/cypress/plugins/createZipArchive/addPlugin.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/cypress/plugins/createZipArchive/addPlugin.js b/tests/cypress/plugins/createZipArchive/addPlugin.js index 7ab93407ba0..a2f468e5bdb 100644 --- a/tests/cypress/plugins/createZipArchive/addPlugin.js +++ b/tests/cypress/plugins/createZipArchive/addPlugin.js @@ -2,11 +2,15 @@ // // SPDX-License-Identifier: MIT -// eslint-disable-next-line no-use-before-define +/* eslint-disable + import/no-extraneous-dependencies, + security/detect-non-literal-fs-filename, + no-use-before-define +*/ + exports.createZipArchive = createZipArchive; const archiver = require('archiver'); -// eslint-disable-next-line import/no-extraneous-dependencies const fs = require('fs-extra'); function createZipArchive(args) { From 62354914803c8c966ef662db65ff7664e06b7488 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Mon, 10 Mar 2025 14:52:55 +0000 Subject: [PATCH 07/22] minor refactor --- tests/cypress/e2e/features/consensus.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 167a333bace..873dafa6d78 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -7,15 +7,15 @@ import { translatePoints } from '../../support/utils'; context('Basic manipulations with consensus job replicas', () => { + const jobIDs = []; + const labelName = 'Consensus job merging'; + const replicas = 4; before(() => { cy.visit('auth/login'); cy.login(); cy.get('.cvat-create-task-dropdown').click(); cy.get('.cvat-create-task-button').should('be.visible').click(); }); - const jobIDs = []; - const labelName = 'test4r'; - const replicas = 4; describe('Consensus job creation', () => { const maxReplicas = 10; const taskName = 'Test consensus'; From 63c7f71e4466ef10afd8a0fe008129920c124f33 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Mon, 10 Mar 2025 18:10:34 +0000 Subject: [PATCH 08/22] decouple tasks from test cases + add serverFiles logic to createAnnotationTask --- tests/cypress/e2e/features/consensus.js | 57 ++++++++++++++++--------- tests/cypress/support/commands.js | 7 ++- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 873dafa6d78..05b2b77f6f9 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -7,9 +7,10 @@ import { translatePoints } from '../../support/utils'; context('Basic manipulations with consensus job replicas', () => { - const jobIDs = []; - const labelName = 'Consensus job merging'; + const labelName = 'Consensus'; const replicas = 4; + const taskName = 'Test consensus'; + const serverFiles = ['archive.zip']; before(() => { cy.visit('auth/login'); cy.login(); @@ -18,8 +19,6 @@ context('Basic manipulations with consensus job replicas', () => { }); describe('Consensus job creation', () => { const maxReplicas = 10; - const taskName = 'Test consensus'; - const serverFiles = ['archive.zip']; it('Check allowed number of replicas', () => { // Fill the fields to create the task cy.get('#name').type(taskName); @@ -65,22 +64,6 @@ context('Basic manipulations with consensus job replicas', () => { expect($el.text()).to.equal(`${replicas} Replicas`); cy.wrap($el).click(); }); - - // Check asc order of jobs - function parseJobId(jobItem) { - const jobItemText = jobItem.innerText; - const [start, stop] = [0, jobItemText.indexOf('\n')]; - return +(jobItemText.substring(start, stop).split('#')[1]); - } - cy.get('.cvat-job-item').then((jobItems) => { - const sourceJobId = parseJobId(jobItems[0]); - jobIDs.push(sourceJobId); - for (let i = 1; i <= replicas; i++) { - const jobId = parseJobId(jobItems[i]); - jobIDs.push(jobId); - expect(jobId).equals(sourceJobId + i); - } - }); }); }); @@ -93,6 +76,24 @@ context('Basic manipulations with consensus job replicas', () => { points: [250, 64, 491, 228], occluded: false, }; + const jobIDs = []; + + before(() => { + const attrName = labelName; + const advancedConfigurationParams = { consensusReplicas: replicas }; + const defaultArgs1 = ['Some default value for type Text', '', null]; + const deafultArgs2 = [false, false, '', 'success', 'Test', null]; + cy.goToTaskList(); + cy.createAnnotationTask( + taskName, labelName, attrName, + ...defaultArgs1, + advancedConfigurationParams, + ...deafultArgs2, + serverFiles, + ); + cy.openTask(taskName); + cy.get('.cvat-consensus-job-collapse').click(); + }); it("Check new merge buttons exist and are visible. Trying to merge 'new' jobs should trigger errors", () => { // Merge all consensus jobs in task cy.mergeConsensusTask(400); @@ -108,6 +109,22 @@ context('Basic manipulations with consensus job replicas', () => { .should('be.visible') .invoke('text').should('include', 'Could not merge the job'); cy.closeNotification('.cvat-notification-notice-consensus-merge-task-failed'); + + // Check asc order of jobs and save job ids + function parseJobId(jobItem) { + const jobItemText = jobItem.innerText; + const [start, stop] = [0, jobItemText.indexOf('\n')]; + return +(jobItemText.substring(start, stop).split('#')[1]); + } + cy.get('.cvat-job-item').then((jobItems) => { + const sourceJobId = parseJobId(jobItems[0]); + jobIDs.push(sourceJobId); + for (let i = 1; i <= replicas; i++) { + const jobId = parseJobId(jobItems[i]); + jobIDs.push(jobId); + expect(jobId).equals(sourceJobId + i); + } + }); }); it('Check consensus management page', () => { diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 998ce2ebf07..2c895967ceb 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -179,6 +179,7 @@ Cypress.Commands.add( expectedResult = 'success', projectSubsetFieldValue = 'Test', qualityConfigurationParams = null, + serverFiles = [], ) => { cy.url().then(() => { cy.get('.cvat-create-task-dropdown').click(); @@ -212,7 +213,11 @@ Cypress.Commands.add( cy.get('.cvat-project-subset-field').type(`${projectSubsetFieldValue}{Enter}`); cy.get('.cvat-constructor-viewer-new-item').should('not.exist'); } - cy.get('input[type="file"]').attachFile(image, { subjectType: 'drag-n-drop' }); + if (serverFiles.length > 0) { + cy.selectFilesFromShare(serverFiles); + } else { + cy.get('input[type="file"]').attachFile(image, { subjectType: 'drag-n-drop' }); + } if (advancedConfigurationParams) { cy.advancedConfiguration(advancedConfigurationParams); } From 701b406c88700d5871986f093bc29d00589e6a7b Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Mon, 10 Mar 2025 18:15:56 +0000 Subject: [PATCH 09/22] hoist constants, add a todo --- tests/cypress/e2e/features/consensus.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 05b2b77f6f9..a02310c57fb 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -11,6 +11,7 @@ context('Basic manipulations with consensus job replicas', () => { const replicas = 4; const taskName = 'Test consensus'; const serverFiles = ['archive.zip']; + before(() => { cy.visit('auth/login'); cy.login(); @@ -77,12 +78,13 @@ context('Basic manipulations with consensus job replicas', () => { occluded: false, }; const jobIDs = []; + const attrName = labelName; + const advancedConfigurationParams = { consensusReplicas: replicas }; + + const defaultArgs1 = ['Some default value for type Text', '', null]; + const deafultArgs2 = [false, false, '', 'success', 'Test', null]; before(() => { - const attrName = labelName; - const advancedConfigurationParams = { consensusReplicas: replicas }; - const defaultArgs1 = ['Some default value for type Text', '', null]; - const deafultArgs2 = [false, false, '', 'success', 'Test', null]; cy.goToTaskList(); cy.createAnnotationTask( taskName, labelName, attrName, @@ -90,7 +92,7 @@ context('Basic manipulations with consensus job replicas', () => { advancedConfigurationParams, ...deafultArgs2, serverFiles, - ); + ); // TODO: rewrite to headless call once the task consensus endpoint is finished cy.openTask(taskName); cy.get('.cvat-consensus-job-collapse').click(); }); From 7d2fe19f1a11436aaa8a6a725c4ffa2f93cd39ab Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Tue, 11 Mar 2025 21:17:01 +0000 Subject: [PATCH 10/22] refactor to merge consensus job by id --- tests/cypress/e2e/features/consensus.js | 47 ++++++++++++------------- tests/cypress/support/commands.js | 13 +++---- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index a02310c57fb..a0b233ee581 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -96,37 +96,36 @@ context('Basic manipulations with consensus job replicas', () => { cy.openTask(taskName); cy.get('.cvat-consensus-job-collapse').click(); }); + it("Check new merge buttons exist and are visible. Trying to merge 'new' jobs should trigger errors", () => { - // Merge all consensus jobs in task - cy.mergeConsensusTask(400); - cy.get('.cvat-notification-notice-consensus-merge-task-failed') - .should('be.visible') - .invoke('text') - .should('include', 'Could not merge the task'); - cy.closeNotification('.cvat-notification-notice-consensus-merge-task-failed'); + // Check asc order of jobs in drop-down + function parseJobId(jobItem) { + const jobItemText = jobItem.innerText; + const [start, stop] = [0, jobItemText.indexOf('\n')]; + return +(jobItemText.substring(start, stop).split('#')[1]); + } + cy.get('.cvat-job-item').each(([$el], i) => { + const jobID = parseJobId($el); + jobIDs.push(jobID); + expect(jobID).equals(jobIDs[0] + i); + }); // Merge one consensus job - cy.mergeConsensusJob(400); + cy.then(() => { + cy.mergeConsensusJob(jobIDs[0], 400); + }); cy.get('.cvat-notification-notice-consensus-merge-task-failed') .should('be.visible') .invoke('text').should('include', 'Could not merge the job'); cy.closeNotification('.cvat-notification-notice-consensus-merge-task-failed'); - // Check asc order of jobs and save job ids - function parseJobId(jobItem) { - const jobItemText = jobItem.innerText; - const [start, stop] = [0, jobItemText.indexOf('\n')]; - return +(jobItemText.substring(start, stop).split('#')[1]); - } - cy.get('.cvat-job-item').then((jobItems) => { - const sourceJobId = parseJobId(jobItems[0]); - jobIDs.push(sourceJobId); - for (let i = 1; i <= replicas; i++) { - const jobId = parseJobId(jobItems[i]); - jobIDs.push(jobId); - expect(jobId).equals(sourceJobId + i); - } - }); + // Merge all consensus jobs in task + cy.mergeConsensusTask(400); + cy.get('.cvat-notification-notice-consensus-merge-task-failed') + .should('be.visible') + .invoke('text') + .should('include', 'Could not merge the task'); + cy.closeNotification('.cvat-notification-notice-consensus-merge-task-failed'); }); it('Check consensus management page', () => { @@ -181,7 +180,7 @@ context('Basic manipulations with consensus job replicas', () => { s = { ...s, points }; } // Merging of consensus job should go without errors in network and UI - cy.mergeConsensusJob(); + cy.mergeConsensusJob(consensusJobID); cy.get('.cvat-notification-notice-consensus-merge-job-failed').should('not.exist'); cy.get('.ant-notification-notice-message') .should('be.visible') diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 2c895967ceb..593afe177f5 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -1848,7 +1848,7 @@ Cypress.Commands.add('applyActionToSliders', (wrapper, slidersClassNames, action }); Cypress.Commands.add('mergeConsensusTask', (status = 202) => { - cy.intercept('POST', '/api/consensus/merges**').as('mergeJobs'); + cy.intercept('POST', '/api/consensus/merges**').as('mergeTask'); cy.get('.cvat-task-details-wrapper').should('be.visible'); cy.contains('button', 'Actions').click(); @@ -1857,15 +1857,16 @@ Cypress.Commands.add('mergeConsensusTask', (status = 202) => { .contains('button', 'Merge') .click(); - cy.wait('@mergeJobs').its('response.statusCode').should('eq', status); + cy.wait('@mergeTask').its('response.statusCode').should('eq', status); }); -Cypress.Commands.add('mergeConsensusJob', (status = 202) => { +Cypress.Commands.add('mergeConsensusJob', (jobID, status = 202) => { cy.intercept('POST', '/api/consensus/merges**').as('mergeJob'); + cy.get('.cvat-job-item') + .filter(':has(.cvat-tag-consensus)') + .filter(`:contains("Job #${jobID}")`) + .find('.anticon-more').first().click(); - cy.get('.cvat-job-item').first().within(() => { - cy.get('.anticon-more').first().click(); - }); cy.get('.ant-dropdown-menu').contains('li', 'Merge consensus job').click(); cy.get('.cvat-modal-confirm-consensus-merge-job') .contains('button', 'Merge') From 7f4853bc882f86f4865d7af1dd498c6f24736012 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Tue, 11 Mar 2025 21:28:54 +0000 Subject: [PATCH 11/22] refactor functions, remove redundant logic --- tests/cypress/e2e/features/consensus.js | 40 ++++++++++--------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index a0b233ee581..574bdaecfdd 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -69,7 +69,7 @@ context('Basic manipulations with consensus job replicas', () => { }); describe('Cosensus jobs merging', () => { - const shape = { + const shape0 = { objectType: 'shape', labelName, frame: 0, @@ -135,10 +135,7 @@ context('Basic manipulations with consensus job replicas', () => { cy.contains('Consensus management').should('be.visible').click(); cy.get('.cvat-consensus-management-inner').should('be.visible'); // Save settings, confirm request is sent - let requestCount = 0; - cy.intercept('PATCH', 'api/consensus/settings/**', () => { - requestCount++; - }).as('settingsMeta'); + cy.intercept('PATCH', 'api/consensus/settings/**').as('settingsMeta'); cy.contains('button', 'Save').click(); cy.wait('@settingsMeta'); cy.get('.ant-notification-notice-message') @@ -148,21 +145,16 @@ context('Basic manipulations with consensus job replicas', () => { cy.closeNotification('.ant-notification-notice-closable'); // Forms and invalid saving - cy.get('#quorum').then(([$el]) => { - cy.wrap($el).invoke('val').should('eq', `${defaultQuorum}`); - cy.wrap($el).clear(); - }); - cy.get('.ant-form-item-explain-error').should('be.visible'); - cy.contains('button', 'Save').click(); - cy.get('#iouThreshold').then(([$el]) => { - cy.wrap($el).invoke('val').should('eq', `${defaultIoU}`); - cy.wrap($el).clear(); - }); - cy.get('.ant-form-item-explain-error').should('be.visible'); - cy.contains('button', 'Save').click(); - cy.then(() => { - expect(requestCount).to.equal(1); - }); + function checkFieldSaving(selector, defaultValue) { + cy.get(selector).then(([$el]) => { + cy.wrap($el).invoke('val').should('eq', `${defaultValue}`); + cy.wrap($el).clear(); + }); + cy.get('.ant-form-item-explain-error').should('be.visible'); + cy.contains('button', 'Save').click(); + } + checkFieldSaving('#quorum', defaultQuorum); + checkFieldSaving('#iouThreshold', defaultIoU); cy.get('.ant-notification-notice').should('not.exist'); // Go back to task page @@ -173,11 +165,11 @@ context('Basic manipulations with consensus job replicas', () => { // Create annotations for job replicas const delta = 50; const [consensusJobID, ...replicaJobIDs] = jobIDs; - for (let i = 0, s = shape; i < replicas; i++) { - cy.headlessCreateObjects([s], replicaJobIDs[i]); // only 'in progress' jobs can be merged + for (let i = 0, shape = shape0; i < replicas; i++) { + cy.headlessCreateObjects([shape], replicaJobIDs[i]); // only 'in progress' jobs can be merged cy.headlessUpdateJob(replicaJobIDs[i], { state: 'in progress' }); - const points = translatePoints(s.points, delta, 'x'); - s = { ...s, points }; + const points = translatePoints(shape.points, delta, 'x'); + shape = { ...shape, points }; } // Merging of consensus job should go without errors in network and UI cy.mergeConsensusJob(consensusJobID); From c9c9d0aed04268ddeb7cc1f20f31533bca62ff81 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Tue, 11 Mar 2025 22:10:17 +0000 Subject: [PATCH 12/22] add .cvat-back-btn class to GoBackButton component --- cvat-ui/src/components/common/go-back-button.tsx | 2 +- tests/cypress/e2e/features/consensus.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cvat-ui/src/components/common/go-back-button.tsx b/cvat-ui/src/components/common/go-back-button.tsx index 5b6f2a60e2b..8dd05471538 100644 --- a/cvat-ui/src/components/common/go-back-button.tsx +++ b/cvat-ui/src/components/common/go-back-button.tsx @@ -12,7 +12,7 @@ function GoBackButton(): JSX.Element { const goBack = useGoBack(); return ( <> - Back diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 574bdaecfdd..fabe777fc6b 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -92,7 +92,7 @@ context('Basic manipulations with consensus job replicas', () => { advancedConfigurationParams, ...deafultArgs2, serverFiles, - ); // TODO: rewrite to headless call once the task consensus endpoint is finished + ); // TODO: rewrite to headless call to task cy.openTask(taskName); cy.get('.cvat-consensus-job-collapse').click(); }); @@ -158,7 +158,7 @@ context('Basic manipulations with consensus job replicas', () => { cy.get('.ant-notification-notice').should('not.exist'); // Go back to task page - cy.get('.ant-btn-default').should('be.visible').click(); + cy.get('.cvat-back-btn').should('be.visible').click(); }); it('Create annotations and check that job replicas merge correctly', () => { From a883ebd452ea10d16f33fbef4a8b337bebb08705 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Wed, 12 Mar 2025 01:44:24 +0000 Subject: [PATCH 13/22] use headless function to create consensus tasks --- tests/cypress/e2e/features/consensus.js | 39 ++++++++++++------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index fabe777fc6b..963f8784314 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -8,25 +8,33 @@ import { translatePoints } from '../../support/utils'; context('Basic manipulations with consensus job replicas', () => { const labelName = 'Consensus'; - const replicas = 4; const taskName = 'Test consensus'; const serverFiles = ['archive.zip']; + const replicas = 4; + const taskSpec = { + name: taskName, + labels: [{ + name: labelName, + attributes: [], + type: 'any', + }], + }; + const dataSpec = { + server_files: serverFiles, + image_quality: 70, + }; + const extras = { consensus_replicas: replicas }; before(() => { cy.visit('auth/login'); cy.login(); + cy.headlessCreateTask(taskSpec, dataSpec, extras); cy.get('.cvat-create-task-dropdown').click(); cy.get('.cvat-create-task-button').should('be.visible').click(); }); describe('Consensus job creation', () => { const maxReplicas = 10; it('Check allowed number of replicas', () => { - // Fill the fields to create the task - cy.get('#name').type(taskName); - cy.addNewLabel({ name: labelName }); - cy.selectFilesFromShare(serverFiles); - cy.contains('[role="tab"]', 'My computer').click(); - cy.contains('Advanced configuration').click(); // 'Consensus Replicas' field cannot equal to 1 cy.get('#consensusReplicas').type(`{backspace}${1}`); @@ -49,8 +57,8 @@ context('Basic manipulations with consensus job replicas', () => { it('Check new consensus task has correct tags and drop-down with replicas', () => { // Create task with consensus - cy.get('#consensusReplicas').type(replicas); - cy.contains('button', 'Submit & Open').click(); + cy.goToTaskList(); + cy.openTask(taskName); cy.get('.cvat-task-details-wrapper').should('be.visible'); cy.get('.ant-notification-notice-error').should('not.exist'); // Check tags @@ -78,21 +86,10 @@ context('Basic manipulations with consensus job replicas', () => { occluded: false, }; const jobIDs = []; - const attrName = labelName; - const advancedConfigurationParams = { consensusReplicas: replicas }; - - const defaultArgs1 = ['Some default value for type Text', '', null]; - const deafultArgs2 = [false, false, '', 'success', 'Test', null]; before(() => { + cy.headlessCreateTask(taskSpec, dataSpec, extras); cy.goToTaskList(); - cy.createAnnotationTask( - taskName, labelName, attrName, - ...defaultArgs1, - advancedConfigurationParams, - ...deafultArgs2, - serverFiles, - ); // TODO: rewrite to headless call to task cy.openTask(taskName); cy.get('.cvat-consensus-job-collapse').click(); }); From 77e6187879c68139b75486de8206fb625b223c39 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Wed, 12 Mar 2025 12:09:47 +0000 Subject: [PATCH 14/22] remove comment Co-authored-by: Kirill Lakhov --- tests/cypress/e2e/features/consensus.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 963f8784314..1dddc9b2d36 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -56,7 +56,6 @@ context('Basic manipulations with consensus job replicas', () => { }); it('Check new consensus task has correct tags and drop-down with replicas', () => { - // Create task with consensus cy.goToTaskList(); cy.openTask(taskName); cy.get('.cvat-task-details-wrapper').should('be.visible'); From 028dae1a397202065bc71b825b1e911e3d80f1f5 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Wed, 12 Mar 2025 12:10:19 +0000 Subject: [PATCH 15/22] rename var Co-authored-by: Kirill Lakhov --- tests/cypress/e2e/features/consensus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 1dddc9b2d36..ee4f1fe9bca 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -76,7 +76,7 @@ context('Basic manipulations with consensus job replicas', () => { }); describe('Cosensus jobs merging', () => { - const shape0 = { + const baseShape = { objectType: 'shape', labelName, frame: 0, From a8f8e103e5f26b36d4dc72212caa534940bca944 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Wed, 12 Mar 2025 12:17:52 +0000 Subject: [PATCH 16/22] revert serverFiles logic from command --- tests/cypress/support/commands.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 593afe177f5..567b3fd15c6 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -179,7 +179,6 @@ Cypress.Commands.add( expectedResult = 'success', projectSubsetFieldValue = 'Test', qualityConfigurationParams = null, - serverFiles = [], ) => { cy.url().then(() => { cy.get('.cvat-create-task-dropdown').click(); @@ -213,11 +212,7 @@ Cypress.Commands.add( cy.get('.cvat-project-subset-field').type(`${projectSubsetFieldValue}{Enter}`); cy.get('.cvat-constructor-viewer-new-item').should('not.exist'); } - if (serverFiles.length > 0) { - cy.selectFilesFromShare(serverFiles); - } else { - cy.get('input[type="file"]').attachFile(image, { subjectType: 'drag-n-drop' }); - } + cy.get('input[type="file"]').attachFile(image, { subjectType: 'drag-n-drop' }); if (advancedConfigurationParams) { cy.advancedConfiguration(advancedConfigurationParams); } From d7ef56206b691fd3bee67668bb015886cbc4f0cd Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Wed, 12 Mar 2025 12:22:23 +0000 Subject: [PATCH 17/22] fix variable --- tests/cypress/e2e/features/consensus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index ee4f1fe9bca..255bcba3063 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -161,7 +161,7 @@ context('Basic manipulations with consensus job replicas', () => { // Create annotations for job replicas const delta = 50; const [consensusJobID, ...replicaJobIDs] = jobIDs; - for (let i = 0, shape = shape0; i < replicas; i++) { + for (let i = 0, shape = baseShape; i < replicas; i++) { cy.headlessCreateObjects([shape], replicaJobIDs[i]); // only 'in progress' jobs can be merged cy.headlessUpdateJob(replicaJobIDs[i], { state: 'in progress' }); const points = translatePoints(shape.points, delta, 'x'); From 343d091c47fa55609948a4e4a5c112327b2d60d0 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Wed, 12 Mar 2025 16:27:11 +0000 Subject: [PATCH 18/22] update consensus management page test according to fix #9178 --- tests/cypress/e2e/features/consensus.js | 37 ++++++++++++++++--------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 255bcba3063..417f09333b7 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -28,11 +28,13 @@ context('Basic manipulations with consensus job replicas', () => { before(() => { cy.visit('auth/login'); cy.login(); - cy.headlessCreateTask(taskSpec, dataSpec, extras); - cy.get('.cvat-create-task-dropdown').click(); - cy.get('.cvat-create-task-button').should('be.visible').click(); }); + describe('Consensus job creation', () => { + before(() => { + cy.get('.cvat-create-task-dropdown').click(); + cy.get('.cvat-create-task-button').should('be.visible').click(); + }); const maxReplicas = 10; it('Check allowed number of replicas', () => { cy.contains('Advanced configuration').click(); @@ -75,7 +77,7 @@ context('Basic manipulations with consensus job replicas', () => { }); }); - describe('Cosensus jobs merging', () => { + describe.only('Cosensus jobs merging', () => { const baseShape = { objectType: 'shape', labelName, @@ -87,10 +89,10 @@ context('Basic manipulations with consensus job replicas', () => { const jobIDs = []; before(() => { - cy.headlessCreateTask(taskSpec, dataSpec, extras); cy.goToTaskList(); cy.openTask(taskName); cy.get('.cvat-consensus-job-collapse').click(); + cy.headlessCreateTask(taskSpec, dataSpec, extras); }); it("Check new merge buttons exist and are visible. Trying to merge 'new' jobs should trigger errors", () => { @@ -124,7 +126,7 @@ context('Basic manipulations with consensus job replicas', () => { cy.closeNotification('.cvat-notification-notice-consensus-merge-task-failed'); }); - it('Check consensus management page', () => { + it.only('Check consensus management page', () => { const defaultQuorum = 50; const defaultIoU = 40; cy.contains('button', 'Actions').click(); @@ -141,16 +143,25 @@ context('Basic manipulations with consensus job replicas', () => { cy.closeNotification('.ant-notification-notice-closable'); // Forms and invalid saving - function checkFieldSaving(selector, defaultValue) { - cy.get(selector).then(([$el]) => { - cy.wrap($el).invoke('val').should('eq', `${defaultValue}`); - cy.wrap($el).clear(); + function checkFieldValue(selector, value) { + return cy.get(selector).then(([$el]) => { + cy.wrap($el).invoke('val').should('eq', `${value}`); + return cy.wrap($el); }); - cy.get('.ant-form-item-explain-error').should('be.visible'); + } + function attemptInvalidSaving(errorsCount) { + cy.get('.ant-form-item-explain-error').should('be.visible') + .should('have.length', errorsCount) + .each(($el) => { + cy.wrap($el).should('have.text', 'This field is required'); + }); cy.contains('button', 'Save').click(); + cy.closeNotification('.cvat-notification-save-consensus-settings-failed'); } - checkFieldSaving('#quorum', defaultQuorum); - checkFieldSaving('#iouThreshold', defaultIoU); + checkFieldValue('#quorum', defaultQuorum).clear(); + attemptInvalidSaving(1); + checkFieldValue('#iouThreshold', defaultIoU).clear(); + attemptInvalidSaving(2); cy.get('.ant-notification-notice').should('not.exist'); // Go back to task page From 368a72490bfc875ba3e2d27359ffd7b7afba7745 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Wed, 12 Mar 2025 17:57:21 +0000 Subject: [PATCH 19/22] Create two different tasks for both test suites --- tests/cypress/e2e/features/consensus.js | 39 ++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 417f09333b7..e193825b0ad 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -8,34 +8,48 @@ import { translatePoints } from '../../support/utils'; context('Basic manipulations with consensus job replicas', () => { const labelName = 'Consensus'; - const taskName = 'Test consensus'; const serverFiles = ['archive.zip']; const replicas = 4; - const taskSpec = { - name: taskName, + const dataSpec = { + server_files: serverFiles, + image_quality: 70, + }; + const extras = { consensus_replicas: replicas }; + + const taskNameCreate = 'Test consensus creation'; + const taskSpecCreate = { + name: taskNameCreate, labels: [{ name: labelName, attributes: [], type: 'any', }], }; - const dataSpec = { - server_files: serverFiles, - image_quality: 70, + const taskNameMerge = 'Test consensus merging'; + const taskSpecMerge = { + name: taskNameMerge, + labels: [{ + name: labelName, + attributes: [], + type: 'any', + }], }; - const extras = { consensus_replicas: replicas }; before(() => { cy.visit('auth/login'); cy.login(); + cy.headlessCreateTask(taskSpecCreate, dataSpec, extras); + cy.headlessCreateTask(taskSpecMerge, dataSpec, extras); }); describe('Consensus job creation', () => { + const maxReplicas = 10; + before(() => { cy.get('.cvat-create-task-dropdown').click(); cy.get('.cvat-create-task-button').should('be.visible').click(); }); - const maxReplicas = 10; + it('Check allowed number of replicas', () => { cy.contains('Advanced configuration').click(); // 'Consensus Replicas' field cannot equal to 1 @@ -59,7 +73,7 @@ context('Basic manipulations with consensus job replicas', () => { it('Check new consensus task has correct tags and drop-down with replicas', () => { cy.goToTaskList(); - cy.openTask(taskName); + cy.openTask(taskNameCreate); cy.get('.cvat-task-details-wrapper').should('be.visible'); cy.get('.ant-notification-notice-error').should('not.exist'); // Check tags @@ -77,7 +91,7 @@ context('Basic manipulations with consensus job replicas', () => { }); }); - describe.only('Cosensus jobs merging', () => { + describe('Cosensus jobs merging', () => { const baseShape = { objectType: 'shape', labelName, @@ -90,9 +104,8 @@ context('Basic manipulations with consensus job replicas', () => { before(() => { cy.goToTaskList(); - cy.openTask(taskName); + cy.openTask(taskNameMerge); cy.get('.cvat-consensus-job-collapse').click(); - cy.headlessCreateTask(taskSpec, dataSpec, extras); }); it("Check new merge buttons exist and are visible. Trying to merge 'new' jobs should trigger errors", () => { @@ -126,7 +139,7 @@ context('Basic manipulations with consensus job replicas', () => { cy.closeNotification('.cvat-notification-notice-consensus-merge-task-failed'); }); - it.only('Check consensus management page', () => { + it('Check consensus management page', () => { const defaultQuorum = 50; const defaultIoU = 40; cy.contains('button', 'Actions').click(); From 8d663622ef055895ba8e858b1ea425fde8951ebf Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Thu, 13 Mar 2025 11:23:31 +0000 Subject: [PATCH 20/22] Revert "Create two different tasks for both test suites" This reverts commit 368a72490bfc875ba3e2d27359ffd7b7afba7745. --- tests/cypress/e2e/features/consensus.js | 39 +++++++++---------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index e193825b0ad..417f09333b7 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -8,48 +8,34 @@ import { translatePoints } from '../../support/utils'; context('Basic manipulations with consensus job replicas', () => { const labelName = 'Consensus'; + const taskName = 'Test consensus'; const serverFiles = ['archive.zip']; const replicas = 4; - const dataSpec = { - server_files: serverFiles, - image_quality: 70, - }; - const extras = { consensus_replicas: replicas }; - - const taskNameCreate = 'Test consensus creation'; - const taskSpecCreate = { - name: taskNameCreate, + const taskSpec = { + name: taskName, labels: [{ name: labelName, attributes: [], type: 'any', }], }; - const taskNameMerge = 'Test consensus merging'; - const taskSpecMerge = { - name: taskNameMerge, - labels: [{ - name: labelName, - attributes: [], - type: 'any', - }], + const dataSpec = { + server_files: serverFiles, + image_quality: 70, }; + const extras = { consensus_replicas: replicas }; before(() => { cy.visit('auth/login'); cy.login(); - cy.headlessCreateTask(taskSpecCreate, dataSpec, extras); - cy.headlessCreateTask(taskSpecMerge, dataSpec, extras); }); describe('Consensus job creation', () => { - const maxReplicas = 10; - before(() => { cy.get('.cvat-create-task-dropdown').click(); cy.get('.cvat-create-task-button').should('be.visible').click(); }); - + const maxReplicas = 10; it('Check allowed number of replicas', () => { cy.contains('Advanced configuration').click(); // 'Consensus Replicas' field cannot equal to 1 @@ -73,7 +59,7 @@ context('Basic manipulations with consensus job replicas', () => { it('Check new consensus task has correct tags and drop-down with replicas', () => { cy.goToTaskList(); - cy.openTask(taskNameCreate); + cy.openTask(taskName); cy.get('.cvat-task-details-wrapper').should('be.visible'); cy.get('.ant-notification-notice-error').should('not.exist'); // Check tags @@ -91,7 +77,7 @@ context('Basic manipulations with consensus job replicas', () => { }); }); - describe('Cosensus jobs merging', () => { + describe.only('Cosensus jobs merging', () => { const baseShape = { objectType: 'shape', labelName, @@ -104,8 +90,9 @@ context('Basic manipulations with consensus job replicas', () => { before(() => { cy.goToTaskList(); - cy.openTask(taskNameMerge); + cy.openTask(taskName); cy.get('.cvat-consensus-job-collapse').click(); + cy.headlessCreateTask(taskSpec, dataSpec, extras); }); it("Check new merge buttons exist and are visible. Trying to merge 'new' jobs should trigger errors", () => { @@ -139,7 +126,7 @@ context('Basic manipulations with consensus job replicas', () => { cy.closeNotification('.cvat-notification-notice-consensus-merge-task-failed'); }); - it('Check consensus management page', () => { + it.only('Check consensus management page', () => { const defaultQuorum = 50; const defaultIoU = 40; cy.contains('button', 'Actions').click(); From 83aa23c78f4dbf9104816183d553807f804c6c26 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Thu, 13 Mar 2025 16:53:04 +0000 Subject: [PATCH 21/22] fix flaky form checks --- tests/cypress/e2e/features/consensus.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index 417f09333b7..c4a623cd9b8 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -40,17 +40,20 @@ context('Basic manipulations with consensus job replicas', () => { cy.contains('Advanced configuration').click(); // 'Consensus Replicas' field cannot equal to 1 cy.get('#consensusReplicas').type(`{backspace}${1}`); - cy.get('.ant-form-item-explain-error') + cy.get('.ant-form-item-has-error') .should('be.visible') - .invoke('text').should('eq', 'Value can not be equal to 1'); + .should('include.text', 'Value can not be equal to 1'); cy.contains('button', 'Submit & Continue').click(); cy.get('.ant-notification-notice-error').should('exist').and('be.visible'); cy.closeNotification('.ant-notification-notice-error'); // 'Consensus Replicas' field cannot be > 10 + cy.get('#consensusReplicas').clear(); + cy.get('.ant-form-item-has-error').should('not.exist'); cy.get('#consensusReplicas').type(`{backspace}${maxReplicas + 1}`); - cy.get('.ant-form-item-explain-error').should('be.visible') - .invoke('text').should('eq', `Value must be less than ${maxReplicas}`); + cy.get('.ant-form-item-has-error') + .should('be.visible') + .should('include.text', `Value must be less than ${maxReplicas}`); cy.contains('button', 'Submit & Continue').click(); cy.get('.ant-notification-notice-error').should('exist').and('be.visible'); cy.closeNotification('.ant-notification-notice-error'); From fcab0dfd1f527b592bcb8fa9c299ad08da4da4e3 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Thu, 13 Mar 2025 16:53:32 +0000 Subject: [PATCH 22/22] tasks use same spec and handled in separate cases --- tests/cypress/e2e/features/consensus.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/cypress/e2e/features/consensus.js b/tests/cypress/e2e/features/consensus.js index c4a623cd9b8..4049f5fe6b8 100644 --- a/tests/cypress/e2e/features/consensus.js +++ b/tests/cypress/e2e/features/consensus.js @@ -31,11 +31,15 @@ context('Basic manipulations with consensus job replicas', () => { }); describe('Consensus job creation', () => { + const maxReplicas = 10; + let consensusTaskID = null; before(() => { + cy.headlessCreateTask(taskSpec, dataSpec, extras).then(({ taskID }) => { + consensusTaskID = taskID; + }); cy.get('.cvat-create-task-dropdown').click(); cy.get('.cvat-create-task-button').should('be.visible').click(); }); - const maxReplicas = 10; it('Check allowed number of replicas', () => { cy.contains('Advanced configuration').click(); // 'Consensus Replicas' field cannot equal to 1 @@ -78,9 +82,13 @@ context('Basic manipulations with consensus job replicas', () => { cy.wrap($el).click(); }); }); + after(() => { + cy.headlessDeleteTask(consensusTaskID); + }); }); - describe.only('Cosensus jobs merging', () => { + describe('Cosensus jobs merging', () => { + let consensusTaskID = null; const baseShape = { objectType: 'shape', labelName, @@ -92,10 +100,12 @@ context('Basic manipulations with consensus job replicas', () => { const jobIDs = []; before(() => { + cy.headlessCreateTask(taskSpec, dataSpec, extras).then(({ taskID }) => { + consensusTaskID = taskID; + }); cy.goToTaskList(); cy.openTask(taskName); cy.get('.cvat-consensus-job-collapse').click(); - cy.headlessCreateTask(taskSpec, dataSpec, extras); }); it("Check new merge buttons exist and are visible. Trying to merge 'new' jobs should trigger errors", () => { @@ -129,7 +139,7 @@ context('Basic manipulations with consensus job replicas', () => { cy.closeNotification('.cvat-notification-notice-consensus-merge-task-failed'); }); - it.only('Check consensus management page', () => { + it('Check consensus management page', () => { const defaultQuorum = 50; const defaultIoU = 40; cy.contains('button', 'Actions').click(); @@ -176,7 +186,7 @@ context('Basic manipulations with consensus job replicas', () => { const delta = 50; const [consensusJobID, ...replicaJobIDs] = jobIDs; for (let i = 0, shape = baseShape; i < replicas; i++) { - cy.headlessCreateObjects([shape], replicaJobIDs[i]); // only 'in progress' jobs can be merged + cy.headlessCreateObjects([shape], jobIDs[i]); // only 'in progress' jobs can be merged cy.headlessUpdateJob(replicaJobIDs[i], { state: 'in progress' }); const points = translatePoints(shape.points, delta, 'x'); shape = { ...shape, points }; @@ -222,5 +232,8 @@ context('Basic manipulations with consensus job replicas', () => { }); }); }); + after(() => { + cy.headlessDeleteTask(consensusTaskID); + }); }); });