diff --git a/CHANGELOG.md b/CHANGELOG.md index c1e56bda..e4395462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added basic projects implementation () - ### Changed - PATCH requests from cvat-core submit only changed fields () diff --git a/tests/cypress.json b/tests/cypress.json index 80523cb6..690735f6 100644 --- a/tests/cypress.json +++ b/tests/cypress.json @@ -8,5 +8,11 @@ "user": "admin", "password": "12qwaszx" }, - "testFiles": ["auth_page.js", "actions_tasks_objects/*", "actions_users/*", "remove_users_tasks.js"] + "testFiles": [ + "auth_page.js", + "actions_tasks_objects/*", + "actions_users/*", + "actions_projects/*", + "remove_users_tasks_projects.js" + ] } diff --git a/tests/cypress/integration/actions_projects/base_actions_project_task_user.js b/tests/cypress/integration/actions_projects/base_actions_project_task_user.js new file mode 100644 index 00000000..229f2313 --- /dev/null +++ b/tests/cypress/integration/actions_projects/base_actions_project_task_user.js @@ -0,0 +1,145 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { projectName } from '../../support/const_project'; + +const randomString = (isPassword) => { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + for (let i = 0; i <= 8; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return isPassword ? `${result}${Math.floor(Math.random() * 10)}` : result; +}; + +context('Base actions on the project', () => { + const labelName = `Base label for ${projectName}`; + const taskName = { + firstTask: `First task for ${projectName}`, + secondTask: `Second task for ${projectName}`, + }; + const attrName = `Attr for ${labelName}`; + const textDefaultValue = 'Some default value for type Text'; + const imagesCount = 1; + const imageFileName = `image_${taskName.firstTask.replace(/\s+/g, '_').toLowerCase()}`; + const width = 800; + const height = 800; + const posX = 10; + const posY = 10; + const color = 'white'; + const archiveName = `${imageFileName}.zip`; + const archivePath = `cypress/fixtures/${archiveName}`; + const imagesFolder = `cypress/fixtures/${imageFileName}`; + const directoryToArchive = imagesFolder; + const advancedConfigurationParams = false; + const forProject = true; + const attachToProject = { + yes: true, + no: false, + }; + const multiAttrParams = false; + const newLabelName1 = `First label ${projectName}`; + const newLabelName2 = `Second label ${projectName}`; + const newLabelName3 = `Third label ${projectName}`; + const newLabelName4 = `Fourth label ${projectName}`; + const firstName = `${randomString()}`; + const lastName = `${randomString()}`; + const userName = `${randomString()}`; + const emailAddr = `${userName}@local.local`; + const password = `${randomString(true)}`; + let projectID = ''; + + before(() => { + cy.openProject(projectName); + }); + + describe(`Testing "Base actions on the project"`, () => { + it('Add some labels to project.', () => { + cy.addNewLabel(newLabelName1); + cy.addNewLabel(newLabelName2); + cy.addNewLabel(newLabelName3); + cy.addNewLabel(newLabelName4); + }); + it('Create a first task for the project. Project field is completed with proper project name and labels editor is not accessible.', () => { + cy.imageGenerator(imagesFolder, imageFileName, width, height, color, posX, posY, labelName, imagesCount); + cy.createZipArchive(directoryToArchive, archivePath); + cy.createAnnotationTask( + taskName.firstTask, + labelName, + attrName, + textDefaultValue, + archiveName, + multiAttrParams, + advancedConfigurationParams, + forProject, + attachToProject.no, + projectName, + ); + }); + it('Create a second task from task list page and attach to the created project. Assign first user.', () => { + cy.goToTaskList(); + cy.createAnnotationTask( + taskName.secondTask, + labelName, + attrName, + textDefaultValue, + archiveName, + multiAttrParams, + advancedConfigurationParams, + forProject, + attachToProject.yes, + projectName, + ); + cy.goToProjectsList(); + cy.openProject(projectName); + cy.openTask(taskName.secondTask); + cy.assignTaskToUser(Cypress.env('user')); + }); + it('The task is successfully opened. No label editor on task page.', () => { + cy.goToProjectsList(); + cy.openProject(projectName); + cy.getProjectID(projectName).then(($projectID) => { + projectID = $projectID; + }); + cy.get('.cvat-tasks-list-item').then((countTasks) => { + // The number of created tasks is greater than zero + expect(countTasks.length).to.be.gt(0); + }); + cy.openTask(taskName.firstTask); + cy.get('.cvat-constructor-viewer').should('not.exist'); + }); + it('Logout first user, register second user, logout.', () => { + cy.logout(); + cy.goToRegisterPage(); + cy.userRegistration(firstName, lastName, userName, emailAddr, password); + cy.logout(userName); + }); + it('Login first user. Assing project to second user. Logout.', () => { + cy.login(); + cy.goToProjectsList(); + cy.openProject(projectName); + cy.assignProjectToUser(userName); + cy.logout(); + }); + it('Login second user. The project and first tasks available for that user. Logout.', () => { + cy.login(userName, password); + cy.goToProjectsList(); + cy.openProject(projectName); + cy.goToTaskList(); + cy.contains('strong', taskName.secondTask).should('not.exist'); + cy.openTask(taskName.firstTask); + cy.logout(userName); + }); + it('Delete the project. Deleted project not exist. Checking the availability of tasks.', () => { + cy.login(); + cy.goToProjectsList(); + cy.deleteProject(projectName, projectID); + cy.goToTaskList(); + cy.contains('strong', taskName.firstTask).should('not.exist'); + cy.contains('strong', taskName.secondTask).should('not.exist'); + }); + }); +}); diff --git a/tests/cypress/integration/actions_tasks_objects/case_3_task_start_stop_step_frame.js b/tests/cypress/integration/actions_tasks_objects/case_3_task_start_stop_step_frame.js index 70b11b76..c7aded98 100644 --- a/tests/cypress/integration/actions_tasks_objects/case_3_task_start_stop_step_frame.js +++ b/tests/cypress/integration/actions_tasks_objects/case_3_task_start_stop_step_frame.js @@ -34,7 +34,6 @@ context('Check if parameters "startFrame", "stopFrame", "frameStep" works as exp cy.login(); cy.imageGenerator(imagesFolder, imageFileName, width, height, color, posX, posY, labelName, imagesCount); cy.createZipArchive(directoryToArchive, archivePath); - cy.goToTaskList(); }); after(() => { diff --git a/tests/cypress/integration/actions_users/case_1_create_delete_task.js b/tests/cypress/integration/actions_users/case_1_create_delete_task.js index 8a2d496a..a959747c 100644 --- a/tests/cypress/integration/actions_users/case_1_create_delete_task.js +++ b/tests/cypress/integration/actions_users/case_1_create_delete_task.js @@ -28,7 +28,6 @@ context('Create and delete a annotation task', () => { cy.login(); cy.imageGenerator(imagesFolder, imageFileName, width, height, color, posX, posY, labelName, imagesCount); cy.createZipArchive(directoryToArchive, archivePath); - cy.goToTaskList(); }); describe(`Testing "${labelName}"`, () => { diff --git a/tests/cypress/integration/actions_users/case_2_register_user_change_pass.js b/tests/cypress/integration/actions_users/case_2_register_user_change_pass.js index 91b025c1..1eddfc9b 100644 --- a/tests/cypress/integration/actions_users/case_2_register_user_change_pass.js +++ b/tests/cypress/integration/actions_users/case_2_register_user_change_pass.js @@ -30,7 +30,6 @@ context('Register user, change password, login with new password', () => { describe(`Testing "Case ${caseId}"`, () => { it('Register user, change password', () => { cy.userRegistration(firstName, lastName, userName, emailAddr, password); - cy.url().should('include', '/tasks'); cy.get('.cvat-right-header') .find('.cvat-header-menu-dropdown') .should('have.text', userName) @@ -46,11 +45,9 @@ context('Register user, change password, login with new password', () => { }); it('Logout', () => { cy.logout(userName); - cy.url().should('include', '/auth/login'); }); it('Login with the new password', () => { cy.login(userName, newPassword); - cy.url().should('include', '/tasks'); }); }); }); diff --git a/tests/cypress/integration/actions_users/case_4_assign_taks_job_users.js b/tests/cypress/integration/actions_users/case_4_assign_taks_job_users.js index 26026366..41ffb3e4 100644 --- a/tests/cypress/integration/actions_users/case_4_assign_taks_job_users.js +++ b/tests/cypress/integration/actions_users/case_4_assign_taks_job_users.js @@ -39,7 +39,6 @@ context('Multiple users. Assign task, job.', () => { after(() => { cy.login(); - cy.goToTaskList(); cy.getTaskID(taskName).then(($taskID) => { cy.deleteTask(taskName, $taskID); }); @@ -57,9 +56,7 @@ context('Multiple users. Assign task, job.', () => { secondUser.emailAddr, secondUser.password, ); - cy.url().should('include', '/tasks'); cy.logout(secondUserName); - cy.url().should('include', '/auth/login'); }); it('Register third user and logout.', () => { cy.get('a[href="/auth/register"]').click(); @@ -71,14 +68,10 @@ context('Multiple users. Assign task, job.', () => { thirdUser.emailAddr, thirdUser.password, ); - cy.url().should('include', '/tasks'); cy.logout(thirdUserName); - cy.url().should('include', '/auth/login'); }); it('First user login and create a task', () => { cy.login(); - cy.url().should('include', '/tasks'); - cy.goToTaskList(); cy.imageGenerator(imagesFolder, imageFileName, width, height, color, posX, posY, labelName, imagesCount); cy.createZipArchive(directoryToArchive, archivePath); cy.createAnnotationTask(taskName, labelName, attrName, textDefaultValue, archiveName); @@ -93,23 +86,17 @@ context('Multiple users. Assign task, job.', () => { }); it('Second user login. The task can be opened. Logout', () => { cy.login(secondUserName, secondUser.password); - cy.url().should('include', '/tasks'); - cy.goToTaskList(); cy.contains('strong', taskName).should('exist'); cy.openTask(taskName); cy.logout(secondUserName); }); it('Third user login. The task not exist. Logout', () => { cy.login(thirdUserName, thirdUser.password); - cy.url().should('include', '/tasks'); - cy.goToTaskList(); cy.contains('strong', taskName).should('not.exist'); cy.logout(thirdUserName); }); it('First user login and assign the job to the third user. Logout', () => { cy.login(); - cy.url().should('include', '/tasks'); - cy.goToTaskList(); cy.openTask(taskName); cy.get('.cvat-task-job-list').within(() => { cy.get('.cvat-user-search-field').click({ force: true }); @@ -119,8 +106,6 @@ context('Multiple users. Assign task, job.', () => { }); it('Third user login. The task can be opened.', () => { cy.login(thirdUserName, thirdUser.password); - cy.url().should('include', '/tasks'); - cy.goToTaskList(); cy.contains('strong', taskName).should('exist'); cy.openTask(taskName); cy.logout(thirdUserName); diff --git a/tests/cypress/integration/actions_users/issue_1810_login_logout.js b/tests/cypress/integration/actions_users/issue_1810_login_logout.js index d2e8d01f..c7d64a04 100644 --- a/tests/cypress/integration/actions_users/issue_1810_login_logout.js +++ b/tests/cypress/integration/actions_users/issue_1810_login_logout.js @@ -14,11 +14,9 @@ context('When clicking on the Logout button, get the user session closed.', () = describe(`Testing issue "${issueId}"`, () => { it('Login', () => { cy.login(); - cy.url().should('include', '/tasks'); }); it('Logout', () => { cy.logout(); - cy.url().should('include', '/auth/login'); }); }); }); diff --git a/tests/cypress/integration/remove_users_tasks.js b/tests/cypress/integration/remove_users_tasks_projects.js similarity index 74% rename from tests/cypress/integration/remove_users_tasks.js rename to tests/cypress/integration/remove_users_tasks_projects.js index f6be5763..4aa2e0da 100644 --- a/tests/cypress/integration/remove_users_tasks.js +++ b/tests/cypress/integration/remove_users_tasks_projects.js @@ -61,4 +61,24 @@ describe('Delete users and tasks created during the test run.', () => { } }); }); + it('Get a list of projects and delete them all', () => { + cy.request({ + url: '/api/v1/projects?page_size=all', + headers: { + Authorization: `Token ${authKey}`, + }, + }).then(async (responce) => { + const responceResult = await responce['body']['results']; + for (let tasks of responceResult) { + let taskId = tasks['id']; + cy.request({ + method: 'DELETE', + url: `/api/v1/projects/${taskId}`, + headers: { + Authorization: `Token ${authKey}`, + }, + }); + } + }); + }); }); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 26203f01..340ce621 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -14,14 +14,15 @@ Cypress.Commands.add('login', (username = Cypress.env('user'), password = Cypres cy.get('[placeholder="Username"]').type(username); cy.get('[placeholder="Password"]').type(password); cy.get('[type="submit"]').click(); + cy.url().should('include', '/tasks'); }); Cypress.Commands.add('logout', (username = Cypress.env('user')) => { - cy.get('.cvat-right-header') - .find('.cvat-header-menu-dropdown') - .should('have.text', username) - .trigger('mouseover', { which: 1 }); + cy.get('.cvat-right-header').within(() => { + cy.get('.cvat-header-menu-dropdown').should('have.text', username).trigger('mouseover', { which: 1 }); + }); cy.get('.anticon-logout').click(); + cy.url().should('include', '/auth/login'); }); Cypress.Commands.add('userRegistration', (firstName, lastName, userName, emailAddr, password) => { @@ -32,6 +33,7 @@ Cypress.Commands.add('userRegistration', (firstName, lastName, userName, emailAd cy.get('#password1').type(password); cy.get('#password2').type(password); cy.get('.register-form-button').click(); + cy.url().should('include', '/tasks'); }); Cypress.Commands.add( @@ -44,40 +46,59 @@ Cypress.Commands.add( image = 'image.png', multiAttrParams, advancedConfigurationParams, + forProject = false, + attachToProject = false, + projectName, ) => { cy.get('#cvat-create-task-button').click({ force: true }); cy.url().should('include', '/tasks/create'); cy.get('[id="name"]').type(taksName); - cy.get('.cvat-constructor-viewer-new-item').click(); - cy.get('[placeholder="Label name"]').type(labelName); - cy.get('.cvat-new-attribute-button').click(); - cy.get('[placeholder="Name"]').type(attrName); - cy.get('div[title="Select"]').click(); - cy.get('li').contains('Text').click(); - cy.get('[placeholder="Default value"]').type(textDefaultValue); - if (multiAttrParams) { - cy.updateAttributes(multiAttrParams); + if (!forProject) { + cy.get('.cvat-constructor-viewer-new-item').click(); + cy.get('[placeholder="Label name"]').type(labelName); + cy.get('.cvat-new-attribute-button').click(); + cy.get('[placeholder="Name"]').type(attrName); + cy.get('div[title="Select"]').click(); + cy.get('li').contains('Text').click(); + cy.get('[placeholder="Default value"]').type(textDefaultValue); + if (multiAttrParams) { + cy.updateAttributes(multiAttrParams); + } + cy.contains('button', 'Done').click(); + } else { + if (attachToProject) { + cy.get('.cvat-project-search-field').click(); + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .contains(new RegExp(`^${projectName}$`, 'g')) + .click(); + } + cy.get('.cvat-project-search-field').within(() => { + cy.get('[type="text"]').should('have.value', projectName); + }); + cy.get('.cvat-constructor-viewer-new-item').should('not.exist'); } - cy.contains('button', 'Done').click(); cy.get('input[type="file"]').attachFile(image, { subjectType: 'drag-n-drop' }); if (advancedConfigurationParams) { cy.advancedConfiguration(advancedConfigurationParams); } cy.contains('button', 'Submit').click(); cy.contains('The task has been created'); - cy.get('[value="tasks"]').click(); - cy.url().should('include', '/tasks?page='); + if (!forProject) { + cy.goToTaskList(); + } else { + cy.goToProjectsList(); + } }, ); Cypress.Commands.add('openTask', (taskName) => { cy.contains('strong', taskName).parents('.cvat-tasks-list-item').contains('a', 'Open').click({ force: true }); + cy.get('.cvat-task-details').should('exist'); }); Cypress.Commands.add('saveJob', () => { - cy.get('button') - .contains('Save') - .click({ force: true }); + cy.get('button').contains('Save').click({ force: true }); }); Cypress.Commands.add('openJob', (jobNumber = 0) => { @@ -93,6 +114,7 @@ Cypress.Commands.add('openJob', (jobNumber = 0) => { cy.get('.ant-table-tbody').contains('a', `Job #${tdText}`).click(); }); cy.url().should('include', '/jobs'); + cy.get('.cvat-canvas-container').should('exist'); }); Cypress.Commands.add('openTaskJob', (taskName, jobNumber = 0) => { @@ -357,10 +379,12 @@ Cypress.Commands.add('removeAnnotations', () => { Cypress.Commands.add('goToTaskList', () => { cy.get('a[value="tasks"]').click(); + cy.url().should('include', '/tasks'); }); Cypress.Commands.add('addNewLabel', (newLabelName) => { let listCvatConstructorViewerItemText = []; + cy.get('.cvat-constructor-viewer').should('exist'); cy.document().then((doc) => { const labels = Array.from(doc.querySelectorAll('.cvat-constructor-viewer-item')); for (let i = 0; i < labels.length; i++) { @@ -383,3 +407,18 @@ Cypress.Commands.add('createTag', (labelName) => { cy.get('button').click(); }); }); + +Cypress.Commands.add('goToRegisterPage', () => { + cy.get('a[href="/auth/register"]').click(); + 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(); + }); + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .contains(new RegExp(`^${user}$`, 'g')) + .click(); +}); diff --git a/tests/cypress/support/commands_projects.js b/tests/cypress/support/commands_projects.js new file mode 100644 index 00000000..e1617314 --- /dev/null +++ b/tests/cypress/support/commands_projects.js @@ -0,0 +1,75 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +Cypress.Commands.add('goToProjectsList', () => { + cy.get('[value="projects"]').click(); + cy.url().should('include', '/projects'); +}); + +Cypress.Commands.add('createProjects', (projectName, labelName, attrName, textDefaultValue, multiAttrParams) => { + cy.get('#cvat-create-project-button').click(); + cy.get('#name').type(projectName); + cy.get('.cvat-constructor-viewer-new-item').click(); + cy.get('[placeholder="Label name"]').type(labelName); + cy.get('.cvat-new-attribute-button').click(); + cy.get('[placeholder="Name"]').type(attrName); + cy.get('div[title="Select"]').click(); + cy.get('li').contains('Text').click(); + cy.get('[placeholder="Default value"]').type(textDefaultValue); + if (multiAttrParams) { + cy.updateAttributes(multiAttrParams); + } + cy.contains('button', 'Done').click(); + cy.get('.cvat-create-project-content').within(() => { + cy.contains('Submit').click(); + }); + cy.contains('The project has been created').should('exist'); + cy.goToProjectsList(); +}); + +Cypress.Commands.add('openProject', (projectName) => { + cy.contains(projectName).click({ force: true }); + cy.get('.cvat-project-details').should('exist'); +}); + +Cypress.Commands.add('getProjectID', (projectName) => { + cy.contains('h4', projectName) + .parents('.cvat-project-details') + .within(() => { + cy.get('span') + .invoke('text') + .then((text) => { + return String(text.match(/#\d+/g)).replace(/[^\d]/g, ''); + }); + }); +}); + +Cypress.Commands.add('deleteProject', (projectName, projectID) => { + cy.contains(projectName) + .parents('.cvat-projects-project-item-card') + .within(() => { + cy.get('.cvat-porjects-project-item-description').within(() => { + cy.get('[type="button"]').trigger('mouseover'); + }); + }); + cy.get('.cvat-project-actions-menu').contains('Delete').click(); + cy.get('.ant-modal-content') + .should('contain', `The project ${projectID} will be deleted`) + .within(() => { + cy.contains('button', 'Delete').click(); + }); + cy.get('.cvat-projects-project-item-card').should('have.css', 'opacity', '0.5'); +}); + +Cypress.Commands.add('assignProjectToUser', (user) => { + cy.get('.cvat-project-details').within(() => { + cy.get('.cvat-user-search-field').click(); + }); + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .contains(new RegExp(`^${user}$`, 'g')) + .click(); +}); diff --git a/tests/cypress/support/const.js b/tests/cypress/support/const.js index 77589604..9c2c0838 100644 --- a/tests/cypress/support/const.js +++ b/tests/cypress/support/const.js @@ -9,7 +9,7 @@ export const taskName = `New annotation task for ${labelName}`; export const attrName = `Attr for ${labelName}`; export const textDefaultValue = 'Some default value for type Text'; export const imagesCount = 50; -export const imageFileName = `image_${labelName.replace(' ', '_').toLowerCase()}`; +export const imageFileName = `image_${labelName.replace(/\s+/g, '_').toLowerCase()}`; export const width = 800; export const height = 800; export const posX = 10; @@ -36,7 +36,6 @@ export const multiAttrParams = { it('Prepare to testing', () => { cy.visit('/'); cy.login(); - cy.goToTaskList(); cy.get('.cvat-tasks-page').should('exist'); let listItems = []; cy.document().then((doc) => { diff --git a/tests/cypress/support/const_project.js b/tests/cypress/support/const_project.js new file mode 100644 index 00000000..50f3163e --- /dev/null +++ b/tests/cypress/support/const_project.js @@ -0,0 +1,35 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +export const projectName = 'Main project'; +export const labelName = `Base label for ${projectName}`; +export const attrName = `Attr for ${labelName}`; +export const textDefaultValue = 'Some default value for type Text'; +export const multiAttrParams = { + additionalAttrName: `Attr 2`, + additionalValue: `Attr value 2`, + typeAttribute: 'Text', +}; + +it('Prepare to testing', () => { + cy.visit('/'); + cy.login(); + cy.goToProjectsList(); + cy.get('.cvat-projects-page').should('exist'); + let listItems = []; + cy.document().then((doc) => { + const collection = Array.from(doc.querySelectorAll('.cvat-projects-project-item-title')); + for (let i = 0; i < collection.length; i++) { + listItems.push(collection[i].innerText); + } + if (listItems.indexOf(projectName) === -1) { + cy.task('log', "A project doesn't exist. Creating."); + cy.createProjects(projectName, labelName, attrName, textDefaultValue, multiAttrParams); + } else { + cy.task('log', 'The project exist. Skipping creation.'); + } + }); +}); diff --git a/tests/cypress/support/index.js b/tests/cypress/support/index.js index a1d7757f..c0adf6df 100644 --- a/tests/cypress/support/index.js +++ b/tests/cypress/support/index.js @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT require('./commands'); +require('./commands_projects'); require('@cypress/code-coverage/support'); before(() => {