From 953a9aa80e57834634df0915b5b943b08174a1c2 Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Fri, 13 Nov 2020 13:59:49 +0300 Subject: [PATCH 01/28] First step --- tests/docker-compose.email.yml | 8 ++++++++ tests/email_settings.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/docker-compose.email.yml create mode 100644 tests/email_settings.py diff --git a/tests/docker-compose.email.yml b/tests/docker-compose.email.yml new file mode 100644 index 00000000..452546f3 --- /dev/null +++ b/tests/docker-compose.email.yml @@ -0,0 +1,8 @@ +version: "3.3" + +services: + cvat: + volumes: + - ./email_settings.py:/home/django/email_settings.py:ro + environment: + DJANGO_SETTINGS_MODULE: email_settings diff --git a/tests/email_settings.py b/tests/email_settings.py new file mode 100644 index 00000000..71eeaa0a --- /dev/null +++ b/tests/email_settings.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +# Copyright (C) 2020 Intel Corporation +# +# SPDX-License-Identifier: MIT + +from cvat.settings.production import * + + +# https://github.com/pennersr/django-allauth +ACCOUNT_AUTHENTICATION_METHOD = 'username' +ACCOUNT_CONFIRM_EMAIL_ON_GET = True +ACCOUNT_EMAIL_REQUIRED = True +ACCOUNT_EMAIL_VERIFICATION = 'mandatory' + +# Email backend settings for Django +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' \ No newline at end of file From aa705417740ced3193646d1b2a82a2333e0b4e8f Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Fri, 13 Nov 2020 16:30:29 +0300 Subject: [PATCH 02/28] Added functionality for build CVAT with wmail support --- tests/__init__.py | 3 ++ tests/cypress.json | 1 + .../case_2_register_user_change_pass.js | 2 +- .../case_4_assign_taks_job_users.js | 2 +- .../check_email_verification_system.js | 39 +++++++++++++++++++ .../issue_1599_ch_user_registration.js | 22 +++++++---- .../issue_1599_pl_user_registration.js | 21 ++++++---- .../cypress/integration/remove_users_tasks.js | 1 + tests/docker-compose.email.yml | 4 +- 9 files changed, 75 insertions(+), 20 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/cypress/integration/actions_users/check_email_verification_system.js diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..f19d9bef --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2020 Intel Corporation +# +# SPDX-License-Identifier: MIT diff --git a/tests/cypress.json b/tests/cypress.json index 80523cb6..dd68e5fc 100644 --- a/tests/cypress.json +++ b/tests/cypress.json @@ -6,6 +6,7 @@ "defaultCommandTimeout": 25000, "env": { "user": "admin", + "email": "admin@localhost.company", "password": "12qwaszx" }, "testFiles": ["auth_page.js", "actions_tasks_objects/*", "actions_users/*", "remove_users_tasks.js"] 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..c0bb0a7a 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 @@ -13,7 +13,7 @@ const randomString = (isPassword) => { return isPassword ? `${result}${Math.floor(Math.random() * 10)}` : result; }; -context('Register user, change password, login with new password', () => { +context('Register user, change password, login with new password', { browser: 'chrome' }, () => { const caseId = '2'; const firstName = `${randomString()}`; const lastName = `${randomString()}`; 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 72a678b2..b11c3764 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 @@ -4,7 +4,7 @@ /// -context('Multiple users. Assign task, job.', () => { +context('Multiple users. Assign task, job.', { browser: 'chrome' }, () => { const caseId = '4'; const labelName = `Case ${caseId}`; const taskName = `New annotation task for ${labelName}`; diff --git a/tests/cypress/integration/actions_users/check_email_verification_system.js b/tests/cypress/integration/actions_users/check_email_verification_system.js new file mode 100644 index 00000000..8fc44378 --- /dev/null +++ b/tests/cypress/integration/actions_users/check_email_verification_system.js @@ -0,0 +1,39 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +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('Check email verification system', { browser: 'firefox' }, () => { + const caseId = 'Email verification system'; + const firstName = `${randomString()}`; + const lastName = `${randomString()}`; + const userName = `${randomString()}`; + const emailAddr = `${userName}@local.local`; + const password = `${randomString(true)}`; + + before(() => { + cy.visit('auth/register'); + cy.url().should('include', '/auth/register'); + }); + + describe(`Case: "${caseId}"`, () => { + it('Register user', () => { + cy.server().route('POST', '/api/v1/auth/register').as('userRegister'); + cy.userRegistration(firstName, lastName, userName, emailAddr, password); + cy.get('.ant-notification-topRight') + .contains(`We have sent an email with a confirmation link to ${emailAddr}.`) + .should('exist') + cy.wait('@userRegister').its('status').should('eq', 201); + }); + }); +}); diff --git a/tests/cypress/integration/actions_users/issue_1599_ch_user_registration.js b/tests/cypress/integration/actions_users/issue_1599_ch_user_registration.js index 08cda62c..92db957e 100644 --- a/tests/cypress/integration/actions_users/issue_1599_ch_user_registration.js +++ b/tests/cypress/integration/actions_users/issue_1599_ch_user_registration.js @@ -4,7 +4,14 @@ /// -context('Issue 1599 (Chinese alphabet).', () => { +context('Issue 1599 (Chinese alphabet).', { browser: 'chrome' }, () => { + + const firstName = '测试者' + const lastName = '测试' + const userName = 'Testuser_ch' + const email = 'Testuser_ch@local.local' + const password = 'Qwerty123!' + before(() => { cy.visit('auth/register'); cy.url().should('include', '/auth/register'); @@ -12,33 +19,32 @@ context('Issue 1599 (Chinese alphabet).', () => { describe('User registration using the Chinese alphabet.', () => { it('Filling in the placeholder "First name"', () => { - cy.get('[placeholder="First name"]').type('测试者').should('not.have.class', 'has-error'); + cy.get('[placeholder="First name"]').type(firstName).should('not.have.class', 'has-error'); }); it('Filling in the placeholder "Last name"', () => { - cy.get('[placeholder="Last name"]').type('测试').should('not.have.class', 'has-error'); + cy.get('[placeholder="Last name"]').type(lastName).should('not.have.class', 'has-error'); }); it('Filling in the placeholder "Username"', () => { - cy.get('[placeholder="Username"]').type('Testuser_ch'); + cy.get('[placeholder="Username"]').type(userName); }); it('Filling in the placeholder "Email address"', () => { - cy.get('[placeholder="Email address"]').type('Testuser_ch@local.local'); + cy.get('[placeholder="Email address"]').type(email); }); it('Filling in the placeholder "Password"', () => { - cy.get('[placeholder="Password"]').type('Qwerty123!'); + cy.get('[placeholder="Password"]').type(password); }); it('Filling in the placeholder "Confirm password"', () => { - cy.get('[placeholder="Confirm password"]').type('Qwerty123!'); + cy.get('[placeholder="Confirm password"]').type(password); }); it('Click to "Submit" button', () => { cy.get('[type="submit"]').click(); }); - it('Successful registration', () => { cy.url().should('include', '/tasks'); }); diff --git a/tests/cypress/integration/actions_users/issue_1599_pl_user_registration.js b/tests/cypress/integration/actions_users/issue_1599_pl_user_registration.js index 11a48a15..53b169f7 100644 --- a/tests/cypress/integration/actions_users/issue_1599_pl_user_registration.js +++ b/tests/cypress/integration/actions_users/issue_1599_pl_user_registration.js @@ -4,7 +4,14 @@ /// -context('Issue 1599 (Polish alphabet).', () => { +context('Issue 1599 (Polish alphabet).', { browser: 'chrome' }, () => { + + const firstName = 'Świętobor' + const lastName = 'Сzcić' + const userName = 'Testuser_pl' + const email = 'Testuser_pl@local.local' + const password = 'Qwerty123!' + before(() => { cy.visit('auth/register'); cy.url().should('include', '/auth/register'); @@ -12,27 +19,27 @@ context('Issue 1599 (Polish alphabet).', () => { describe('User registration using the Polish alphabet.', () => { it('Filling in the placeholder "First name"', () => { - cy.get('[placeholder="First name"]').type('Świętobor').should('not.have.class', 'has-error'); + cy.get('[placeholder="First name"]').type(firstName).should('not.have.class', 'has-error'); }); it('Filling in the placeholder "Last name"', () => { - cy.get('[placeholder="Last name"]').type('Сzcić').should('not.have.class', 'has-error'); + cy.get('[placeholder="Last name"]').type(lastName).should('not.have.class', 'has-error'); }); it('Filling in the placeholder "Username"', () => { - cy.get('[placeholder="Username"]').type('Testuser_pl'); + cy.get('[placeholder="Username"]').type(userName); }); it('Filling in the placeholder "Email address"', () => { - cy.get('[placeholder="Email address"]').type('Testuser_pl@local.local'); + cy.get('[placeholder="Email address"]').type(email); }); it('Filling in the placeholder "Password"', () => { - cy.get('[placeholder="Password"]').type('Qwerty123!'); + cy.get('[placeholder="Password"]').type(password); }); it('Filling in the placeholder "Confirm password"', () => { - cy.get('[placeholder="Confirm password"]').type('Qwerty123!'); + cy.get('[placeholder="Confirm password"]').type(password); }); it('Click to "Submit" button', () => { diff --git a/tests/cypress/integration/remove_users_tasks.js b/tests/cypress/integration/remove_users_tasks.js index f6be5763..e30bafdc 100644 --- a/tests/cypress/integration/remove_users_tasks.js +++ b/tests/cypress/integration/remove_users_tasks.js @@ -13,6 +13,7 @@ describe('Delete users and tasks created during the test run.', () => { url: '/api/v1/auth/login', body: { username: Cypress.env('user'), + email: Cypress.env('email'), password: Cypress.env('password'), }, }).then(async (responce) => { diff --git a/tests/docker-compose.email.yml b/tests/docker-compose.email.yml index 452546f3..962376a5 100644 --- a/tests/docker-compose.email.yml +++ b/tests/docker-compose.email.yml @@ -2,7 +2,5 @@ version: "3.3" services: cvat: - volumes: - - ./email_settings.py:/home/django/email_settings.py:ro environment: - DJANGO_SETTINGS_MODULE: email_settings + DJANGO_SETTINGS_MODULE: tests.email_settings From dcc8b3767606c74fe3c4ec46cd2d5d0c7ccbb894 Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Fri, 13 Nov 2020 17:46:33 +0300 Subject: [PATCH 03/28] Reconfigure .travis.yml --- .travis.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 547c9303..9b646231 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,20 +30,22 @@ before_install: - nvm install ${NODE_VERSION} before_script: - - docker-compose -f docker-compose.yml -f docker-compose.ci.yml build - chmod a+rwx ${HOST_COVERAGE_DATA_DIR} script: + - if [[ $TRAVIS_EVENT_TYPE == "cron" && $TRAVIS_BRANCH == "develop" ]]; + then + docker-compose -f docker-compose.yml -f ./tests/docker-compose.email.yml up -d --build; \ + docker exec -it cvat bash -ic 'python3 ~/manage.py migrate'; \ + docker exec -it cvat bash -ic "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"; \ + cd ./tests && npm run cypress:run:firefox; exit $?; + fi; + - docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d --build - docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'coverage run -a manage.py test cvat/apps utils/cli && mv .coverage ${CONTAINER_COVERAGE_DATA_DIR}' - docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'cd cvat-data && npm install && cd ../cvat-core && npm install && npm run test && mv ./reports/coverage/lcov.info ${CONTAINER_COVERAGE_DATA_DIR} && chmod a+rwx ${CONTAINER_COVERAGE_DATA_DIR}/lcov.info' - - docker-compose up -d - docker exec -it cvat bash -ic "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell" # End-to-end testing - cd ./tests && npm install && cd .. - - if [[ $TRAVIS_EVENT_TYPE == "cron" && $TRAVIS_BRANCH == "develop" ]]; - then - cd ./tests && npm run cypress:run:firefox && exit $?; - fi; - npm install && npm run coverage - docker-compose up -d --build - cd ./tests && npx cypress run --headless --browser chrome From d2d274984bc163096c43728417e0dda94764588a Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Fri, 13 Nov 2020 17:53:41 +0300 Subject: [PATCH 04/28] Fix .travis.yml --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9b646231..af0a4351 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,17 +38,16 @@ script: docker-compose -f docker-compose.yml -f ./tests/docker-compose.email.yml up -d --build; \ docker exec -it cvat bash -ic 'python3 ~/manage.py migrate'; \ docker exec -it cvat bash -ic "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"; \ - cd ./tests && npm run cypress:run:firefox; exit $?; + cd ./tests && npm install && npm run cypress:run:firefox; exit $?; fi; - docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d --build - docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'coverage run -a manage.py test cvat/apps utils/cli && mv .coverage ${CONTAINER_COVERAGE_DATA_DIR}' - docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'cd cvat-data && npm install && cd ../cvat-core && npm install && npm run test && mv ./reports/coverage/lcov.info ${CONTAINER_COVERAGE_DATA_DIR} && chmod a+rwx ${CONTAINER_COVERAGE_DATA_DIR}/lcov.info' - docker exec -it cvat bash -ic "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell" # End-to-end testing - - cd ./tests && npm install && cd .. - npm install && npm run coverage - docker-compose up -d --build - - cd ./tests && npx cypress run --headless --browser chrome + - cd ./tests && npm install && npx cypress run --headless --browser chrome - mv ./.nyc_output ../ && cd .. - npx nyc report --reporter=text-lcov >> ${HOST_COVERAGE_DATA_DIR}/lcov.info - docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'cd ${CONTAINER_COVERAGE_DATA_DIR} && coveralls-lcov -v -n lcov.info > ${CONTAINER_COVERAGE_DATA_DIR}/coverage.json' From 5aa5493abe295af4f78caf4eb4ade4d162b99ff6 Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Fri, 13 Nov 2020 17:58:45 +0300 Subject: [PATCH 05/28] Add EOF email_settings.py --- tests/email_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/email_settings.py b/tests/email_settings.py index 71eeaa0a..278b3399 100644 --- a/tests/email_settings.py +++ b/tests/email_settings.py @@ -14,4 +14,4 @@ ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_EMAIL_VERIFICATION = 'mandatory' # Email backend settings for Django -EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' \ No newline at end of file +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' From 6032a132b5309ab9083e8f77307903cbe0d5950a Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Fri, 13 Nov 2020 18:45:07 +0300 Subject: [PATCH 06/28] Fix .travis.yml --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index af0a4351..54c44b63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,9 +40,10 @@ script: docker exec -it cvat bash -ic "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"; \ cd ./tests && npm install && npm run cypress:run:firefox; exit $?; fi; - - docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d --build + - docker-compose -f docker-compose.yml -f docker-compose.ci.yml build - docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'coverage run -a manage.py test cvat/apps utils/cli && mv .coverage ${CONTAINER_COVERAGE_DATA_DIR}' - docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'cd cvat-data && npm install && cd ../cvat-core && npm install && npm run test && mv ./reports/coverage/lcov.info ${CONTAINER_COVERAGE_DATA_DIR} && chmod a+rwx ${CONTAINER_COVERAGE_DATA_DIR}/lcov.info' + - docker-compose up -d - docker exec -it cvat bash -ic "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell" # End-to-end testing - npm install && npm run coverage From 586e9c3f2eff0f9ec4a8b15d33147b074828e666 Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Fri, 13 Nov 2020 20:08:36 +0300 Subject: [PATCH 07/28] Try firefox --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 54c44b63..2732951b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,12 +33,12 @@ before_script: - chmod a+rwx ${HOST_COVERAGE_DATA_DIR} script: - - if [[ $TRAVIS_EVENT_TYPE == "cron" && $TRAVIS_BRANCH == "develop" ]]; + - if [[ $TRAVIS_EVENT_TYPE == "cron" || $TRAVIS_BRANCH == "dkru/cypress-test-check-email-verification" ]]; then - docker-compose -f docker-compose.yml -f ./tests/docker-compose.email.yml up -d --build; \ - docker exec -it cvat bash -ic 'python3 ~/manage.py migrate'; \ - docker exec -it cvat bash -ic "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"; \ - cd ./tests && npm install && npm run cypress:run:firefox; exit $?; + docker-compose -f docker-compose.yml -f ./tests/docker-compose.email.yml up -d --build; + docker exec -it cvat bash -ic 'python3 ~/manage.py migrate'; + docker exec -it cvat bash -ic "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"; + cd ./tests && npm install && npm run cypress:run:firefox; exit $?; fi; - docker-compose -f docker-compose.yml -f docker-compose.ci.yml build - docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'coverage run -a manage.py test cvat/apps utils/cli && mv .coverage ${CONTAINER_COVERAGE_DATA_DIR}' From 308afe5d75bba9e06d7bca46bb929e8c0c8e30d0 Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Sat, 14 Nov 2020 00:29:18 +0300 Subject: [PATCH 08/28] Minor fix for the test. --- .../actions_users/check_email_verification_system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cypress/integration/actions_users/check_email_verification_system.js b/tests/cypress/integration/actions_users/check_email_verification_system.js index 8fc44378..a4576c3c 100644 --- a/tests/cypress/integration/actions_users/check_email_verification_system.js +++ b/tests/cypress/integration/actions_users/check_email_verification_system.js @@ -27,7 +27,7 @@ context('Check email verification system', { browser: 'firefox' }, () => { }); describe(`Case: "${caseId}"`, () => { - it('Register user', () => { + it('Register user. Notification exist. The response status is successful.', () => { cy.server().route('POST', '/api/v1/auth/register').as('userRegister'); cy.userRegistration(firstName, lastName, userName, emailAddr, password); cy.get('.ant-notification-topRight') From 65b90f5fd8de03882494d783e23f6eb935ccce0b Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Sat, 14 Nov 2020 09:53:14 +0300 Subject: [PATCH 09/28] Minor fix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2732951b..9450e7ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ before_script: - chmod a+rwx ${HOST_COVERAGE_DATA_DIR} script: - - if [[ $TRAVIS_EVENT_TYPE == "cron" || $TRAVIS_BRANCH == "dkru/cypress-test-check-email-verification" ]]; + - if [[ $TRAVIS_EVENT_TYPE == "cron" && $TRAVIS_BRANCH == "develop" ]]; then docker-compose -f docker-compose.yml -f ./tests/docker-compose.email.yml up -d --build; docker exec -it cvat bash -ic 'python3 ~/manage.py migrate'; From 57833e203090f4432ddb88278cd4440ef54f62ca Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Thu, 19 Nov 2020 17:29:30 +0300 Subject: [PATCH 10/28] Rework test suite for CI. --- tests/cypress.json | 2 +- .../case_1_create_delete_task.js | 0 .../case_2_register_user_change_pass.js | 2 +- .../case_4_assign_taks_job_users.js | 2 +- .../issue_1599_ch_user_registration.js | 13 ++++++------- .../issue_1599_pl_user_registration.js | 13 ++++++------- .../check_email_verification_system.js | 6 +++--- tests/cypress_cron_type.json | 13 +++++++++++++ tests/package.json | 2 +- 9 files changed, 32 insertions(+), 21 deletions(-) rename tests/cypress/integration/{actions_users => actions_tasks_objects}/case_1_create_delete_task.js (100%) rename tests/cypress/integration/actions_users/{ => registration_involved}/case_2_register_user_change_pass.js (98%) rename tests/cypress/integration/actions_users/{ => registration_involved}/case_4_assign_taks_job_users.js (98%) rename tests/cypress/integration/actions_users/{ => registration_involved}/issue_1599_ch_user_registration.js (85%) rename tests/cypress/integration/actions_users/{ => registration_involved}/issue_1599_pl_user_registration.js (85%) rename tests/cypress/integration/{actions_users => email_system}/check_email_verification_system.js (86%) create mode 100644 tests/cypress_cron_type.json diff --git a/tests/cypress.json b/tests/cypress.json index dd68e5fc..24a6ba3f 100644 --- a/tests/cypress.json +++ b/tests/cypress.json @@ -9,5 +9,5 @@ "email": "admin@localhost.company", "password": "12qwaszx" }, - "testFiles": ["auth_page.js", "actions_tasks_objects/*", "actions_users/*", "remove_users_tasks.js"] + "testFiles": ["auth_page.js", "actions_tasks_objects/*", "actions_users/**/*", "remove_users_tasks.js"] } diff --git a/tests/cypress/integration/actions_users/case_1_create_delete_task.js b/tests/cypress/integration/actions_tasks_objects/case_1_create_delete_task.js similarity index 100% rename from tests/cypress/integration/actions_users/case_1_create_delete_task.js rename to tests/cypress/integration/actions_tasks_objects/case_1_create_delete_task.js diff --git a/tests/cypress/integration/actions_users/case_2_register_user_change_pass.js b/tests/cypress/integration/actions_users/registration_involved/case_2_register_user_change_pass.js similarity index 98% rename from tests/cypress/integration/actions_users/case_2_register_user_change_pass.js rename to tests/cypress/integration/actions_users/registration_involved/case_2_register_user_change_pass.js index c0bb0a7a..91b025c1 100644 --- a/tests/cypress/integration/actions_users/case_2_register_user_change_pass.js +++ b/tests/cypress/integration/actions_users/registration_involved/case_2_register_user_change_pass.js @@ -13,7 +13,7 @@ const randomString = (isPassword) => { return isPassword ? `${result}${Math.floor(Math.random() * 10)}` : result; }; -context('Register user, change password, login with new password', { browser: 'chrome' }, () => { +context('Register user, change password, login with new password', () => { const caseId = '2'; const firstName = `${randomString()}`; const lastName = `${randomString()}`; diff --git a/tests/cypress/integration/actions_users/case_4_assign_taks_job_users.js b/tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js similarity index 98% rename from tests/cypress/integration/actions_users/case_4_assign_taks_job_users.js rename to tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js index b11c3764..72a678b2 100644 --- a/tests/cypress/integration/actions_users/case_4_assign_taks_job_users.js +++ b/tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js @@ -4,7 +4,7 @@ /// -context('Multiple users. Assign task, job.', { browser: 'chrome' }, () => { +context('Multiple users. Assign task, job.', () => { const caseId = '4'; const labelName = `Case ${caseId}`; const taskName = `New annotation task for ${labelName}`; diff --git a/tests/cypress/integration/actions_users/issue_1599_ch_user_registration.js b/tests/cypress/integration/actions_users/registration_involved/issue_1599_ch_user_registration.js similarity index 85% rename from tests/cypress/integration/actions_users/issue_1599_ch_user_registration.js rename to tests/cypress/integration/actions_users/registration_involved/issue_1599_ch_user_registration.js index 92db957e..bbcb5db7 100644 --- a/tests/cypress/integration/actions_users/issue_1599_ch_user_registration.js +++ b/tests/cypress/integration/actions_users/registration_involved/issue_1599_ch_user_registration.js @@ -4,13 +4,12 @@ /// -context('Issue 1599 (Chinese alphabet).', { browser: 'chrome' }, () => { - - const firstName = '测试者' - const lastName = '测试' - const userName = 'Testuser_ch' - const email = 'Testuser_ch@local.local' - const password = 'Qwerty123!' +context('Issue 1599 (Chinese alphabet).', () => { + const firstName = '测试者'; + const lastName = '测试'; + const userName = 'Testuser_ch'; + const email = 'Testuser_ch@local.local'; + const password = 'Qwerty123!'; before(() => { cy.visit('auth/register'); diff --git a/tests/cypress/integration/actions_users/issue_1599_pl_user_registration.js b/tests/cypress/integration/actions_users/registration_involved/issue_1599_pl_user_registration.js similarity index 85% rename from tests/cypress/integration/actions_users/issue_1599_pl_user_registration.js rename to tests/cypress/integration/actions_users/registration_involved/issue_1599_pl_user_registration.js index 53b169f7..2ee411d9 100644 --- a/tests/cypress/integration/actions_users/issue_1599_pl_user_registration.js +++ b/tests/cypress/integration/actions_users/registration_involved/issue_1599_pl_user_registration.js @@ -4,13 +4,12 @@ /// -context('Issue 1599 (Polish alphabet).', { browser: 'chrome' }, () => { - - const firstName = 'Świętobor' - const lastName = 'Сzcić' - const userName = 'Testuser_pl' - const email = 'Testuser_pl@local.local' - const password = 'Qwerty123!' +context('Issue 1599 (Polish alphabet).', () => { + const firstName = 'Świętobor'; + const lastName = 'Сzcić'; + const userName = 'Testuser_pl'; + const email = 'Testuser_pl@local.local'; + const password = 'Qwerty123!'; before(() => { cy.visit('auth/register'); diff --git a/tests/cypress/integration/actions_users/check_email_verification_system.js b/tests/cypress/integration/email_system/check_email_verification_system.js similarity index 86% rename from tests/cypress/integration/actions_users/check_email_verification_system.js rename to tests/cypress/integration/email_system/check_email_verification_system.js index a4576c3c..8ff33b1b 100644 --- a/tests/cypress/integration/actions_users/check_email_verification_system.js +++ b/tests/cypress/integration/email_system/check_email_verification_system.js @@ -13,7 +13,7 @@ const randomString = (isPassword) => { return isPassword ? `${result}${Math.floor(Math.random() * 10)}` : result; }; -context('Check email verification system', { browser: 'firefox' }, () => { +context('Check email verification system', () => { const caseId = 'Email verification system'; const firstName = `${randomString()}`; const lastName = `${randomString()}`; @@ -31,8 +31,8 @@ context('Check email verification system', { browser: 'firefox' }, () => { cy.server().route('POST', '/api/v1/auth/register').as('userRegister'); cy.userRegistration(firstName, lastName, userName, emailAddr, password); cy.get('.ant-notification-topRight') - .contains(`We have sent an email with a confirmation link to ${emailAddr}.`) - .should('exist') + .contains(`We have sent an email with a confirmation link to ${emailAddr}.`) + .should('exist'); cy.wait('@userRegister').its('status').should('eq', 201); }); }); diff --git a/tests/cypress_cron_type.json b/tests/cypress_cron_type.json new file mode 100644 index 00000000..40a1b2b5 --- /dev/null +++ b/tests/cypress_cron_type.json @@ -0,0 +1,13 @@ +{ + "video": false, + "baseUrl": "http://localhost:8080", + "viewportWidth": 1300, + "viewportHeight": 960, + "defaultCommandTimeout": 25000, + "env": { + "user": "admin", + "email": "admin@localhost.company", + "password": "12qwaszx" + }, + "testFiles": ["auth_page.js", "actions_tasks_objects/*", "actions_users/*", "email_system/*", "remove_users_tasks.js"] +} diff --git a/tests/package.json b/tests/package.json index 2b38c570..746be871 100644 --- a/tests/package.json +++ b/tests/package.json @@ -1,7 +1,7 @@ { "scripts": { "cypress:run:chrome": "cypress run --env coverage=false --headless --browser chrome", - "cypress:run:firefox": "cypress run --env coverage=false --headless --browser firefox" + "cypress:run:firefox": "cypress run --env coverage=false --headless --browser firefox --config-file cypress_cron_type.json" }, "devDependencies": { "@cypress/code-coverage": "^3.8.1", From 5c21adcc4ed843e1ff38801ee7e0ba8b358fed97 Mon Sep 17 00:00:00 2001 From: Dmitriy Oparin Date: Wed, 25 Nov 2020 13:45:44 +0300 Subject: [PATCH 11/28] add test --- ..._2174_reset_zoom_in_tag_annotation_mode.js | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js diff --git a/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js b/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js new file mode 100644 index 00000000..73a237fc --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js @@ -0,0 +1,56 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName, labelName } from '../../support/const'; + +context('Reset zoom in tag annotation', () => { + const issueId = '2174'; + let scaleBefore = 0; + let scaleAfter = 0; + + before(() => { + cy.openTaskJob(taskName); + }); + + describe(`Testing issue "${issueId}"`, () => { + it('Uncheck reset zoom', () => { + cy.openSettings(); + cy.get('.ant-modal-content').within(() => { + cy.contains('Player').click(); + cy.get('.cvat-player-settings-reset-zoom-checkbox').within(() => { + cy.get('[type="checkbox"]').uncheck(); + }); + }); + cy.closeSettings(); + }); + + it('Go to tag annotation', () => { + cy.changeWorkspace('Tag annotation', labelName); + }); + + it('Change size background', () => { + cy.get('.cvat-canvas-container').trigger('wheel', { deltaY: 5 }); + }); + + it('Get scale from background', () => { + cy.get('#cvat_canvas_background') + .should('have.attr', 'style') + .then(($styles) => { + scaleBefore = Number($styles.match(/scale\((\d\.\d+)\)/m)[1]); + }); + }); + + it('Check scale background on next frame', () => { + cy.get('.cvat-player-next-button').click(); + cy.get('#cvat_canvas_background') + .should('have.attr', 'style') + .then(($styles) => { + scaleAfter = Number($styles.match(/scale\((\d\.\d+)\)/m)[1]); + cy.expect(scaleBefore).to.equal(scaleAfter); + }); + }); + }); +}); From 4913eb65a769c04b02b995595dda6aad2c101c02 Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Thu, 26 Nov 2020 10:33:44 +0300 Subject: [PATCH 12/28] Cypress test. Objects ordering feature. --- .../case_20_objects_ordering_feature.js | 83 +++++++++++++++++++ tests/cypress/support/commands.js | 12 ++- 2 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 tests/cypress/integration/actions_tasks_objects/case_20_objects_ordering_feature.js diff --git a/tests/cypress/integration/actions_tasks_objects/case_20_objects_ordering_feature.js b/tests/cypress/integration/actions_tasks_objects/case_20_objects_ordering_feature.js new file mode 100644 index 00000000..c1006ab2 --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/case_20_objects_ordering_feature.js @@ -0,0 +1,83 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName, labelName, attrName } from '../../support/const'; + +context('Objects ordering feature', () => { + const caseId = '20'; + + 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: createRectangleShape2Points.firstX + 300, + firstY: createRectangleShape2Points.firstY, + secondX: createRectangleShape2Points.secondX + 300, + secondY: createRectangleShape2Points.secondY, + }; + + function idAscent(arr) { + for (let i = 0; i < arr.length; i++) { + if (arr[i] > arr[i + 1]) { + return false; + } + } + return true; + } + + function checkSideBarItemOrdering(ordering) { + let cvatObjectsSidebarStateItemIdList = []; + cy.get('.cvat-objects-sidebar-state-item').then(($cvatObjectsSidebarStateItemId) => { + for (let i = 0; i < $cvatObjectsSidebarStateItemId.length; i++) { + cvatObjectsSidebarStateItemIdList.push(Number($cvatObjectsSidebarStateItemId[i].id.match(/\d+$/))); + } + if (ordering === 'ascent') { + expect(idAscent(cvatObjectsSidebarStateItemIdList)).to.be.true; //expected true to be true (ascent) + } else { + expect(idAscent(cvatObjectsSidebarStateItemIdList)).to.be.false; //expected false to be false (descent) + } + }); + } + + before(() => { + cy.openTaskJob(taskName); + }); + + describe(`Testing case "${caseId}"`, () => { + it('Create a couple of shapes.', () => { + cy.createRectangle(createRectangleShape2Points); + cy.createRectangle(createRectangleShape2PointsSecond); + checkSideBarItemOrdering('ascent'); + }); + + it('Sort object by "ID - descent".', () => { + cy.sidebarItemSortBy('ID - descent'); + checkSideBarItemOrdering('descent'); + }); + it('Sort objects by "Updated time". Change something in the first object. This object now in the top', () => { + cy.sidebarItemSortBy('Updated time'); + cy.get('#cvat_canvas_shape_1').trigger('mousemove').rightclick(); + cy.get('.cvat-canvas-context-menu').within(() => { + cy.contains('.cvat-objects-sidebar-state-item-collapse', 'Details').click(); + cy.contains('.cvat-object-item-attribute-wrapper', attrName).within(() => { + cy.get('.cvat-object-item-text-attribute').clear(); + }); + }); + cy.get('.cvat-canvas-container').click(); // Hide context menu + checkSideBarItemOrdering('ascent'); + }); + }); +}); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 26203f01..1cd56b48 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -75,9 +75,7 @@ Cypress.Commands.add('openTask', (taskName) => { }); 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) => { @@ -383,3 +381,11 @@ Cypress.Commands.add('createTag', (labelName) => { cy.get('button').click(); }); }); + +Cypress.Commands.add('sidebarItemSortBy', (sortBy) => { + cy.get('.cvat-objects-sidebar-ordering-selector').click(); + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .contains(new RegExp(`^${sortBy}$`, 'g')) + .click(); +}); From f9ac1a4bed17240c3603a7c004424fbee0a067a6 Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Thu, 26 Nov 2020 10:11:27 +0300 Subject: [PATCH 13/28] Cypress test. Filters functionality. --- .../case_18_filters_functionality.js | 217 ++++++++++++++++++ tests/cypress/support/commands.js | 62 ++++- 2 files changed, 273 insertions(+), 6 deletions(-) create mode 100644 tests/cypress/integration/actions_tasks_objects/case_18_filters_functionality.js diff --git a/tests/cypress/integration/actions_tasks_objects/case_18_filters_functionality.js b/tests/cypress/integration/actions_tasks_objects/case_18_filters_functionality.js new file mode 100644 index 00000000..a07f89e4 --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/case_18_filters_functionality.js @@ -0,0 +1,217 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName } from '../../support/const'; + +context('Filters functionality.', () => { + const caseId = '18'; + const labelPolygonShape = 'polygon shape'; + const additionalAttrsLabelPolygonShape = [ + { additionalAttrName: 'type', additionalValue: 'shape', typeAttribute: 'Text' }, + { additionalAttrName: 'count points', additionalValue: '3', typeAttribute: 'Text' }, + { additionalAttrName: 'polygon', additionalValue: 'True', typeAttribute: 'Checkbox' }, + ]; + const labelRectangleTrack2Points = 'rectangle track by 2 points'; + const additionalAttrsLabelRectangleTrack2Points = [ + { additionalAttrName: 'type', additionalValue: 'track', typeAttribute: 'Text' }, + { additionalAttrName: 'rectangle', additionalValue: 'True', typeAttribute: 'Checkbox' }, + { additionalAttrName: 'count points', additionalValue: '2', typeAttribute: 'Text' }, + ]; + const labelCuboidShape4Points = 'cuboid shape by 4 points'; + const additionalAttrsLabelCuboidShape4Points = [ + { additionalAttrName: 'type', additionalValue: 'shape', typeAttribute: 'Text' }, + { additionalAttrName: 'cuboid', additionalValue: 'True', typeAttribute: 'Checkbox' }, + { additionalAttrName: 'count points', additionalValue: '4', typeAttribute: 'Text' }, + ]; + const labelPolylinesShape = 'polylines shape'; + const additionalAttrsLabelPolylinesShape = [ + { additionalAttrName: 'type', additionalValue: 'shape', typeAttribute: 'Text' }, + { additionalAttrName: 'polylines', additionalValue: 'True', typeAttribute: 'Checkbox' }, + { additionalAttrName: 'count points', additionalValue: '3', typeAttribute: 'Text' }, + ]; + const labelPointsShape = 'points shape'; + const additionalAttrsLabelPointsShape = [ + { additionalAttrName: 'type', additionalValue: 'shape', typeAttribute: 'Text' }, + { additionalAttrName: 'points', additionalValue: 'True', typeAttribute: 'Checkbox' }, + { additionalAttrName: 'count points', additionalValue: '1', typeAttribute: 'Text' }, + ]; + const labelRectangleShape4Points = 'rectangle shape by 4 points'; + const additionalAttrsLabelRectangleShape4Points = [ + { additionalAttrName: 'type', additionalValue: 'shape', typeAttribute: 'Text' }, + { additionalAttrName: 'rectangle', additionalValue: 'True', typeAttribute: 'Checkbox' }, + { additionalAttrName: 'count points', additionalValue: '4', typeAttribute: 'Text' }, + ]; + const labelPolygonTrack = 'polygon track'; + const additionalAttrsLabelPolygonTrack = [ + { additionalAttrName: 'type', additionalValue: 'track', typeAttribute: 'Text' }, + { additionalAttrName: 'polygon', additionalValue: 'True', typeAttribute: 'Checkbox' }, + { additionalAttrName: 'count points', additionalValue: '4', typeAttribute: 'Text' }, + ]; + + const createPolygonShape = { + reDraw: false, + type: 'Shape', + labelName: labelPolygonShape, + pointsMap: [ + { x: 200, y: 200 }, + { x: 250, y: 200 }, + { x: 250, y: 240 }, + ], + complete: true, + numberOfPoints: null, + }; + const createRectangleTrack2Points = { + points: 'By 2 Points', + type: 'Track', + labelName: labelRectangleTrack2Points, + firstX: 260, + firstY: 200, + secondX: 360, + secondY: 250, + }; + const createCuboidShape4Points = { + points: 'By 4 Points', + type: 'Shape', + labelName: labelCuboidShape4Points, + firstX: 400, + firstY: 350, + secondX: 500, + secondY: 320, + thirdX: 500, + thirdY: 450, + fourthX: 400, + fourthY: 450, + }; + const createPolylinesShape = { + type: 'Shape', + labelName: labelPolylinesShape, + pointsMap: [ + { x: 600, y: 200 }, + { x: 650, y: 200 }, + { x: 650, y: 250 }, + ], + complete: true, + numberOfPoints: null, + }; + const createPointsShape = { + type: 'Shape', + labelName: labelPointsShape, + pointsMap: [{ x: 700, y: 200 }], + complete: true, + numberOfPoints: null, + }; + const createRectangleShape4Points = { + points: 'By 4 Points', + type: 'Shape', + labelName: labelRectangleShape4Points, + firstX: 550, + firstY: 350, + secondX: 650, + secondY: 350, + thirdX: 650, + thirdY: 450, + fourthX: 550, + fourthY: 450, + }; + const createPolygonTrack = { + reDraw: false, + type: 'Track', + labelName: labelPolygonTrack, + pointsMap: [ + { x: 700, y: 350 }, + { x: 850, y: 350 }, + { x: 850, y: 450 }, + { x: 700, y: 450 }, + ], + numberOfPoints: 4, + }; + + let cvatCanvasShapeList = []; + + function checkingFilterApplication(ids) { + for (let i = 0; i < cvatCanvasShapeList.length; i++) { + if (ids.indexOf(cvatCanvasShapeList[i]) > -1) { + cy.get(`#cvat_canvas_shape_${cvatCanvasShapeList[i]}`).should('exist'); + cy.get(`#cvat-objects-sidebar-state-item-${cvatCanvasShapeList[i]}`).should('exist'); + } else { + cy.get(`#cvat_canvas_shape_${cvatCanvasShapeList[i]}`).should('not.exist'); + cy.get(`#cvat-objects-sidebar-state-item-${cvatCanvasShapeList[i]}`).should('not.exist'); + } + } + } + + before(() => { + cy.openTask(taskName); + cy.addNewLabel(labelPolygonShape, additionalAttrsLabelPolygonShape); + cy.addNewLabel(labelRectangleTrack2Points, additionalAttrsLabelRectangleTrack2Points); + cy.addNewLabel(labelCuboidShape4Points, additionalAttrsLabelCuboidShape4Points); + cy.addNewLabel(labelPolylinesShape, additionalAttrsLabelPolylinesShape); + cy.addNewLabel(labelPointsShape, additionalAttrsLabelPointsShape); + cy.addNewLabel(labelRectangleShape4Points, additionalAttrsLabelRectangleShape4Points); + cy.addNewLabel(labelPolygonTrack, additionalAttrsLabelPolygonTrack); + cy.openJob(); + }); + + describe(`Testing case "${caseId}"`, () => { + it('Draw several objects (different shapes, tracks, labels)', () => { + cy.createPolygon(createPolygonShape); + cy.createRectangle(createRectangleTrack2Points); + cy.createCuboid(createCuboidShape4Points); + cy.createPolyline(createPolylinesShape); + cy.createPoint(createPointsShape); + cy.createRectangle(createRectangleShape4Points); + cy.createPolygon(createPolygonTrack); + cy.get('.cvat_canvas_shape').then(($cvatCanvasShapeList) => { + for (let i = 0; i < $cvatCanvasShapeList.length; i++) { + cvatCanvasShapeList.push(Number($cvatCanvasShapeList[i].id.match(/\d+$/))); + } + }); + }); + + it('Filter: shape=="cuboid". Only the cuboid exist.', () => { + cy.writeFilterValue(false, 'shape=="cuboid"'); // #cvat_canvas_shape_3, #cvat-objects-sidebar-state-item-3 + checkingFilterApplication([3]); + }); + it('Filter: shape=="polygon" | shape=="cuboid". Only the cuboid and polygon exist.', () => { + cy.writeFilterValue(true, 'shape=="polygon" | shape=="cuboid"'); // #cvat_canvas_shape_1,3,7, #cvat-objects-sidebar-state-item-1,3,7 + checkingFilterApplication([1, 3, 7]); + }); + it('Filter: type=="shape". Only the objects with shape type exist.', () => { + cy.writeFilterValue(true, 'type=="shape"'); // #cvat_canvas_shape_1,3-6, #cvat-objects-sidebar-state-item-1,3-6 + checkingFilterApplication([1, 3, 4, 5, 6]); + }); + it('Filter: label=="polygon shape". Only the polygon exist.', () => { + cy.writeFilterValue(true, `label=="${labelPolygonShape}"`); // #cvat_canvas_shape_1, #cvat-objects-sidebar-state-item-1 + checkingFilterApplication([1]); + }); + it('Filter: attr["count points"] == "4". Only the objects with same attr exist.', () => { + cy.writeFilterValue(true, 'attr["count points"] == "4"'); // #cvat_canvas_shape_3,6,7, #cvat-objects-sidebar-state-item-3,6,7 + checkingFilterApplication([3, 6, 7]); + }); + it('Filter: width >= height. All objects except polyline exist.', () => { + cy.writeFilterValue(true, 'width >= height'); // #cvat_canvas_shape_1-3,5-7, #cvat-objects-sidebar-state-item-1-3,5-7 + checkingFilterApplication([1, 2, 3, 5, 6, 7]); + }); + it('Filter: clientID == 7. Only the objects with same id exist (polygon track).', () => { + cy.writeFilterValue(true, 'clientID == 7'); // #cvat_canvas_shape_7, #cvat-objects-sidebar-state-item-7 + checkingFilterApplication([7]); + }); + it('Filter: (label=="polylines shape" & attr["polylines"]==true) | (label=="rectangle shape by 4 points" & width > 50). Only the objects polyline and rectangle exist.', () => { + cy.writeFilterValue( + true, + '(label=="polylines shape" & attr["polylines"]==true) | (label=="rectangle shape by 4 points" & width > 50)', + ); // #cvat_canvas_shape_4,6, #cvat-objects-sidebar-state-item-4,6 + checkingFilterApplication([4, 6]); + }); + it('Filter: (( label==["points shape"]) | (attr["type"]=="shape" & width > 50)) & (height > 50 & (clientID == serverID))). All objects not exist.', () => { + cy.writeFilterValue( + true, + '(( label==["points shape"]) | (attr["type"]=="shape" & width > 50)) & (height > 50 & (clientID == serverID)))', + ); + checkingFilterApplication([]); + }); + }); +}); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 340ce621..92e8de7b 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -299,11 +299,43 @@ Cypress.Commands.add('createCuboid', (createCuboidParams) => { }); Cypress.Commands.add('updateAttributes', (multiAttrParams) => { - cy.contains('button', 'Add an attribute').click(); - cy.get('[placeholder="Name"]').first().type(multiAttrParams.additionalAttrName); - cy.get('div[title="Select"]').first().click(); - cy.get('.ant-select-dropdown').last().contains(multiAttrParams.typeAttribute).click(); - cy.get('[placeholder="Default value"]').first().type(multiAttrParams.additionalValue); + let cvatAttributeInputsWrapperId = []; + cy.get('.cvat-new-attribute-button').click(); + cy.document().then((doc) => { + const cvatAttributeInputsWrapperList = Array.from(doc.querySelectorAll('.cvat-attribute-inputs-wrapper')); + for (let i = 0; i < cvatAttributeInputsWrapperList.length; i++) { + cvatAttributeInputsWrapperId.push(cvatAttributeInputsWrapperList[i].getAttribute('cvat-attribute-id')); + } + + const minId = Math.min(...cvatAttributeInputsWrapperId); + + cy.get(`[cvat-attribute-id="${minId}"]`).within(() => { + cy.get('.cvat-attribute-name-input').type(multiAttrParams.additionalAttrName); + cy.get('.cvat-attribute-type-input').click(); + }); + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .contains(new RegExp(`^${multiAttrParams.typeAttribute}$`, 'g')) + .click(); + + if (multiAttrParams.typeAttribute === 'Text' || multiAttrParams.typeAttribute === 'Number') { + cy.get(`[cvat-attribute-id="${minId}"]`).within(() => { + cy.get('.cvat-attribute-values-input').type(multiAttrParams.additionalValue); + }); + } else if (multiAttrParams.typeAttribute === 'Radio') { + cy.get(`[cvat-attribute-id="${minId}"]`).within(() => { + cy.get('.cvat-attribute-values-input').type(`${multiAttrParams.additionalValue}{Enter}`); + }); + } else if (multiAttrParams.typeAttribute === 'Checkbox') { + cy.get(`[cvat-attribute-id="${minId}"]`).within(() => { + cy.get('.cvat-attribute-values-input').click(); + }); + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .contains(new RegExp(`^.*${multiAttrParams.additionalValue}.*$`, 'g')) + .click(); + } + }); }); Cypress.Commands.add('createPolyline', (createPolylineParams) => { @@ -382,7 +414,7 @@ Cypress.Commands.add('goToTaskList', () => { cy.url().should('include', '/tasks'); }); -Cypress.Commands.add('addNewLabel', (newLabelName) => { +Cypress.Commands.add('addNewLabel', (newLabelName, additionalAttrs) => { let listCvatConstructorViewerItemText = []; cy.get('.cvat-constructor-viewer').should('exist'); cy.document().then((doc) => { @@ -393,6 +425,11 @@ Cypress.Commands.add('addNewLabel', (newLabelName) => { if (listCvatConstructorViewerItemText.indexOf(newLabelName) === -1) { cy.contains('button', 'Add label').click(); cy.get('[placeholder="Label name"]').type(newLabelName); + if (additionalAttrs) { + for (let i = 0; i < additionalAttrs.length; i++) { + cy.updateAttributes(additionalAttrs[i]); + } + } cy.contains('button', 'Done').click(); } }); @@ -422,3 +459,16 @@ Cypress.Commands.add('assignTaskToUser', (user) => { .contains(new RegExp(`^${user}$`, 'g')) .click(); }); + +Cypress.Commands.add('writeFilterValue', (clear, filterValue) => { + if (clear) { + cy.get('.cvat-annotations-filters-input').within(() => { + cy.get('.ant-select-selection__choice__remove').click(); + }); + } + cy.get('.cvat-annotations-filters-input') + .type(`${filterValue}{Enter}`) + .within(() => { + cy.get('.ant-select-selection__choice__content').should('have.text', filterValue); + }); +}); From b2e03a5f823a0d9a0ff82cb88183d2f124e7926a Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Thu, 26 Nov 2020 14:41:33 +0300 Subject: [PATCH 14/28] Minor fix. --- tests/cypress/support/commands.js | 4 +++- tests/cypress_cron_type.json | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 340ce621..eaf1c9ba 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -33,7 +33,9 @@ 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'); + if (Cypress.browser.family === 'chromium') { + cy.url().should('include', '/tasks'); + } }); Cypress.Commands.add( diff --git a/tests/cypress_cron_type.json b/tests/cypress_cron_type.json index 40a1b2b5..2033f6a3 100644 --- a/tests/cypress_cron_type.json +++ b/tests/cypress_cron_type.json @@ -9,5 +9,11 @@ "email": "admin@localhost.company", "password": "12qwaszx" }, - "testFiles": ["auth_page.js", "actions_tasks_objects/*", "actions_users/*", "email_system/*", "remove_users_tasks.js"] + "testFiles": [ + "auth_page.js", + "actions_tasks_objects/*", + "actions_users/*", + "email_system/*", + "remove_users_tasks_projects.js" + ] } From 4dc442e0bc15a9cea19141a80deff98331a6ae9c Mon Sep 17 00:00:00 2001 From: Dmitriy Oparin Date: Thu, 26 Nov 2020 15:40:33 +0300 Subject: [PATCH 15/28] improvements test --- ..._2174_reset_zoom_in_tag_annotation_mode.js | 86 +++++++++++++------ tests/cypress/support/commands.js | 8 ++ 2 files changed, 67 insertions(+), 27 deletions(-) diff --git a/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js b/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js index 73a237fc..471baa03 100644 --- a/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js +++ b/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js @@ -8,49 +8,81 @@ import { taskName, labelName } from '../../support/const'; context('Reset zoom in tag annotation', () => { const issueId = '2174'; - let scaleBefore = 0; - let scaleAfter = 0; + let scaleFirstFrame = 0; + let scaleSecondFrame = 0; + + function scaleFrame() { + cy.get('.cvat-canvas-container').trigger('wheel', { deltaY: 5 }); + }; + + function changeCheckboxResetZoom(value) { + cy.openSettings(); + cy.get('.ant-modal-content').within(() => { + cy.contains('Player').click(); + cy.get('.cvat-player-settings-reset-zoom-checkbox').within(() => { + if (value == "check") { + cy.get('[type="checkbox"]').check(); + } else if (value == "uncheck") { + cy.get('[type="checkbox"]').uncheck(); + }; + }); + }); + cy.closeSettings(); + }; + + function checkFrameNum(frameNum) { + cy.get('.cvat-player-frame-selector').within(() => { + cy.get('input[role="spinbutton"]').should('have.value', frameNum); + }); + }; before(() => { cy.openTaskJob(taskName); }); describe(`Testing issue "${issueId}"`, () => { - it('Uncheck reset zoom', () => { - cy.openSettings(); - cy.get('.ant-modal-content').within(() => { - cy.contains('Player').click(); - cy.get('.cvat-player-settings-reset-zoom-checkbox').within(() => { - cy.get('[type="checkbox"]').uncheck(); - }); - }); - cy.closeSettings(); + it('Set "reset zoom" to true', () => { + changeCheckboxResetZoom("check"); }); it('Go to tag annotation', () => { cy.changeWorkspace('Tag annotation', labelName); }); - it('Change size background', () => { - cy.get('.cvat-canvas-container').trigger('wheel', { deltaY: 5 }); + it('Scale frame', () => { + scaleFrame(); + cy.getScaleValue().then((value) => { + scaleFirstFrame = value; + }); }); - it('Get scale from background', () => { - cy.get('#cvat_canvas_background') - .should('have.attr', 'style') - .then(($styles) => { - scaleBefore = Number($styles.match(/scale\((\d\.\d+)\)/m)[1]); - }); + it('Go to next frame and check reset scale on second frame', () => { + cy.get('.cvat-player-next-button').click(); + checkFrameNum(1); + cy.getScaleValue().then((value) => { + scaleSecondFrame = value; + expect(scaleFirstFrame).to.not.equal(scaleSecondFrame); + }); }); - it('Check scale background on next frame', () => { - cy.get('.cvat-player-next-button').click(); - cy.get('#cvat_canvas_background') - .should('have.attr', 'style') - .then(($styles) => { - scaleAfter = Number($styles.match(/scale\((\d\.\d+)\)/m)[1]); - cy.expect(scaleBefore).to.equal(scaleAfter); - }); + it('Set "reset zoom" to false', () => { + changeCheckboxResetZoom("uncheck"); + }); + + it('Scale frame', () => { + scaleFrame(); + cy.getScaleValue().then((value) => { + scaleSecondFrame = value; + }); + }); + + it('Go to previous frame and check save scale on first frame', () => { + cy.get('.cvat-player-previous-button').click(); + checkFrameNum(0); + cy.getScaleValue().then((value) => { + scaleFirstFrame = value; + expect(scaleSecondFrame).to.equal(scaleFirstFrame); + }); }); }); }); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 340ce621..7e004088 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -422,3 +422,11 @@ Cypress.Commands.add('assignTaskToUser', (user) => { .contains(new RegExp(`^${user}$`, 'g')) .click(); }); + +Cypress.Commands.add('getScaleValue', () => { + cy.get('#cvat_canvas_background') + .should('have.attr', 'style') + .then(($styles) => { + return Number($styles.match(/scale\((\d\.\d+)\)/m)[1]); + }); +}); From 6f8a2539f9bae981dfe127402240c7eab7c48ca0 Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Thu, 26 Nov 2020 17:25:58 +0300 Subject: [PATCH 16/28] Reducing the number of labels and objects created --- .../case_18_filters_functionality.js | 137 +++++------------- 1 file changed, 34 insertions(+), 103 deletions(-) diff --git a/tests/cypress/integration/actions_tasks_objects/case_18_filters_functionality.js b/tests/cypress/integration/actions_tasks_objects/case_18_filters_functionality.js index a07f89e4..5853dc12 100644 --- a/tests/cypress/integration/actions_tasks_objects/case_18_filters_functionality.js +++ b/tests/cypress/integration/actions_tasks_objects/case_18_filters_functionality.js @@ -8,44 +8,14 @@ import { taskName } from '../../support/const'; context('Filters functionality.', () => { const caseId = '18'; - const labelPolygonShape = 'polygon shape'; - const additionalAttrsLabelPolygonShape = [ + const labelShape = 'shape 3 points'; + const additionalAttrsLabelShape = [ { additionalAttrName: 'type', additionalValue: 'shape', typeAttribute: 'Text' }, { additionalAttrName: 'count points', additionalValue: '3', typeAttribute: 'Text' }, { additionalAttrName: 'polygon', additionalValue: 'True', typeAttribute: 'Checkbox' }, ]; - const labelRectangleTrack2Points = 'rectangle track by 2 points'; - const additionalAttrsLabelRectangleTrack2Points = [ - { additionalAttrName: 'type', additionalValue: 'track', typeAttribute: 'Text' }, - { additionalAttrName: 'rectangle', additionalValue: 'True', typeAttribute: 'Checkbox' }, - { additionalAttrName: 'count points', additionalValue: '2', typeAttribute: 'Text' }, - ]; - const labelCuboidShape4Points = 'cuboid shape by 4 points'; - const additionalAttrsLabelCuboidShape4Points = [ - { additionalAttrName: 'type', additionalValue: 'shape', typeAttribute: 'Text' }, - { additionalAttrName: 'cuboid', additionalValue: 'True', typeAttribute: 'Checkbox' }, - { additionalAttrName: 'count points', additionalValue: '4', typeAttribute: 'Text' }, - ]; - const labelPolylinesShape = 'polylines shape'; - const additionalAttrsLabelPolylinesShape = [ - { additionalAttrName: 'type', additionalValue: 'shape', typeAttribute: 'Text' }, - { additionalAttrName: 'polylines', additionalValue: 'True', typeAttribute: 'Checkbox' }, - { additionalAttrName: 'count points', additionalValue: '3', typeAttribute: 'Text' }, - ]; - const labelPointsShape = 'points shape'; - const additionalAttrsLabelPointsShape = [ - { additionalAttrName: 'type', additionalValue: 'shape', typeAttribute: 'Text' }, - { additionalAttrName: 'points', additionalValue: 'True', typeAttribute: 'Checkbox' }, - { additionalAttrName: 'count points', additionalValue: '1', typeAttribute: 'Text' }, - ]; - const labelRectangleShape4Points = 'rectangle shape by 4 points'; - const additionalAttrsLabelRectangleShape4Points = [ - { additionalAttrName: 'type', additionalValue: 'shape', typeAttribute: 'Text' }, - { additionalAttrName: 'rectangle', additionalValue: 'True', typeAttribute: 'Checkbox' }, - { additionalAttrName: 'count points', additionalValue: '4', typeAttribute: 'Text' }, - ]; - const labelPolygonTrack = 'polygon track'; - const additionalAttrsLabelPolygonTrack = [ + const labelTrack = 'track 4 points'; + const additionalAttrsLabelTrack = [ { additionalAttrName: 'type', additionalValue: 'track', typeAttribute: 'Text' }, { additionalAttrName: 'polygon', additionalValue: 'True', typeAttribute: 'Checkbox' }, { additionalAttrName: 'count points', additionalValue: '4', typeAttribute: 'Text' }, @@ -54,7 +24,7 @@ context('Filters functionality.', () => { const createPolygonShape = { reDraw: false, type: 'Shape', - labelName: labelPolygonShape, + labelName: labelShape, pointsMap: [ { x: 200, y: 200 }, { x: 250, y: 200 }, @@ -66,47 +36,16 @@ context('Filters functionality.', () => { const createRectangleTrack2Points = { points: 'By 2 Points', type: 'Track', - labelName: labelRectangleTrack2Points, + labelName: labelTrack, firstX: 260, firstY: 200, secondX: 360, secondY: 250, }; - const createCuboidShape4Points = { - points: 'By 4 Points', - type: 'Shape', - labelName: labelCuboidShape4Points, - firstX: 400, - firstY: 350, - secondX: 500, - secondY: 320, - thirdX: 500, - thirdY: 450, - fourthX: 400, - fourthY: 450, - }; - const createPolylinesShape = { - type: 'Shape', - labelName: labelPolylinesShape, - pointsMap: [ - { x: 600, y: 200 }, - { x: 650, y: 200 }, - { x: 650, y: 250 }, - ], - complete: true, - numberOfPoints: null, - }; - const createPointsShape = { - type: 'Shape', - labelName: labelPointsShape, - pointsMap: [{ x: 700, y: 200 }], - complete: true, - numberOfPoints: null, - }; const createRectangleShape4Points = { points: 'By 4 Points', type: 'Shape', - labelName: labelRectangleShape4Points, + labelName: labelShape, firstX: 550, firstY: 350, secondX: 650, @@ -119,7 +58,7 @@ context('Filters functionality.', () => { const createPolygonTrack = { reDraw: false, type: 'Track', - labelName: labelPolygonTrack, + labelName: labelTrack, pointsMap: [ { x: 700, y: 350 }, { x: 850, y: 350 }, @@ -145,13 +84,8 @@ context('Filters functionality.', () => { before(() => { cy.openTask(taskName); - cy.addNewLabel(labelPolygonShape, additionalAttrsLabelPolygonShape); - cy.addNewLabel(labelRectangleTrack2Points, additionalAttrsLabelRectangleTrack2Points); - cy.addNewLabel(labelCuboidShape4Points, additionalAttrsLabelCuboidShape4Points); - cy.addNewLabel(labelPolylinesShape, additionalAttrsLabelPolylinesShape); - cy.addNewLabel(labelPointsShape, additionalAttrsLabelPointsShape); - cy.addNewLabel(labelRectangleShape4Points, additionalAttrsLabelRectangleShape4Points); - cy.addNewLabel(labelPolygonTrack, additionalAttrsLabelPolygonTrack); + cy.addNewLabel(labelShape, additionalAttrsLabelShape); + cy.addNewLabel(labelTrack, additionalAttrsLabelTrack); cy.openJob(); }); @@ -159,9 +93,6 @@ context('Filters functionality.', () => { it('Draw several objects (different shapes, tracks, labels)', () => { cy.createPolygon(createPolygonShape); cy.createRectangle(createRectangleTrack2Points); - cy.createCuboid(createCuboidShape4Points); - cy.createPolyline(createPolylinesShape); - cy.createPoint(createPointsShape); cy.createRectangle(createRectangleShape4Points); cy.createPolygon(createPolygonTrack); cy.get('.cvat_canvas_shape').then(($cvatCanvasShapeList) => { @@ -171,42 +102,42 @@ context('Filters functionality.', () => { }); }); - it('Filter: shape=="cuboid". Only the cuboid exist.', () => { - cy.writeFilterValue(false, 'shape=="cuboid"'); // #cvat_canvas_shape_3, #cvat-objects-sidebar-state-item-3 - checkingFilterApplication([3]); + it('Filter: shape=="polygon". Only the polygon exist.', () => { + cy.writeFilterValue(false, 'shape=="polygon"'); // #cvat_canvas_shape_1,4, #cvat-objects-sidebar-state-item-1,4 + checkingFilterApplication([1, 4]); }); - it('Filter: shape=="polygon" | shape=="cuboid". Only the cuboid and polygon exist.', () => { - cy.writeFilterValue(true, 'shape=="polygon" | shape=="cuboid"'); // #cvat_canvas_shape_1,3,7, #cvat-objects-sidebar-state-item-1,3,7 - checkingFilterApplication([1, 3, 7]); + it('Filter: shape=="polygon" | shape=="rectangle". Only the rectangle and polygon exist.', () => { + cy.writeFilterValue(true, 'shape=="polygon" | shape=="rectangle"'); // #cvat_canvas_shape_1,2,3,4, #cvat-objects-sidebar-state-item-1,2,3,4 + checkingFilterApplication([1, 2, 3, 4]); }); it('Filter: type=="shape". Only the objects with shape type exist.', () => { - cy.writeFilterValue(true, 'type=="shape"'); // #cvat_canvas_shape_1,3-6, #cvat-objects-sidebar-state-item-1,3-6 - checkingFilterApplication([1, 3, 4, 5, 6]); + cy.writeFilterValue(true, 'type=="shape"'); // #cvat_canvas_shape_1,3, #cvat-objects-sidebar-state-item-1,3 + checkingFilterApplication([1, 3]); }); - it('Filter: label=="polygon shape". Only the polygon exist.', () => { - cy.writeFilterValue(true, `label=="${labelPolygonShape}"`); // #cvat_canvas_shape_1, #cvat-objects-sidebar-state-item-1 - checkingFilterApplication([1]); + it('Filter: label=="track 4 points". Only the polygon exist.', () => { + cy.writeFilterValue(true, `label=="${labelTrack}"`); // #cvat_canvas_shape_2,4, #cvat-objects-sidebar-state-item-2,4 + checkingFilterApplication([2, 4]); }); it('Filter: attr["count points"] == "4". Only the objects with same attr exist.', () => { - cy.writeFilterValue(true, 'attr["count points"] == "4"'); // #cvat_canvas_shape_3,6,7, #cvat-objects-sidebar-state-item-3,6,7 - checkingFilterApplication([3, 6, 7]); + cy.writeFilterValue(true, 'attr["count points"] == "4"'); // #cvat_canvas_shape_2,4, #cvat-objects-sidebar-state-item-2,4 + checkingFilterApplication([2, 4]); }); - it('Filter: width >= height. All objects except polyline exist.', () => { - cy.writeFilterValue(true, 'width >= height'); // #cvat_canvas_shape_1-3,5-7, #cvat-objects-sidebar-state-item-1-3,5-7 - checkingFilterApplication([1, 2, 3, 5, 6, 7]); + it('Filter: width >= height. All objects exist.', () => { + cy.writeFilterValue(true, 'width >= height'); // #cvat_canvas_shape_1,2,3,4, #cvat-objects-sidebar-state-item-1,2,3,4 + checkingFilterApplication([1, 2, 3, 4]); }); - it('Filter: clientID == 7. Only the objects with same id exist (polygon track).', () => { - cy.writeFilterValue(true, 'clientID == 7'); // #cvat_canvas_shape_7, #cvat-objects-sidebar-state-item-7 - checkingFilterApplication([7]); + it('Filter: clientID == 4. Only the objects with same id exist (polygon track).', () => { + cy.writeFilterValue(true, 'clientID == 4'); // #cvat_canvas_shape_7, #cvat-objects-sidebar-state-item-4 + checkingFilterApplication([4]); }); - it('Filter: (label=="polylines shape" & attr["polylines"]==true) | (label=="rectangle shape by 4 points" & width > 50). Only the objects polyline and rectangle exist.', () => { + it('Filter: (label=="shape 3 points" & attr["polylines"]==true) | (label=="track 4 points" & width > 60). Only the objects polygon and rectangle exist.', () => { cy.writeFilterValue( true, - '(label=="polylines shape" & attr["polylines"]==true) | (label=="rectangle shape by 4 points" & width > 50)', - ); // #cvat_canvas_shape_4,6, #cvat-objects-sidebar-state-item-4,6 - checkingFilterApplication([4, 6]); + '(label=="shape 3 points" & attr["polylines"]==true) | (label=="track 4 points" & width > 60)', + ); // #cvat_canvas_shape_2,4, #cvat-objects-sidebar-state-item-2,4 + checkingFilterApplication([2, 4]); }); - it('Filter: (( label==["points shape"]) | (attr["type"]=="shape" & width > 50)) & (height > 50 & (clientID == serverID))). All objects not exist.', () => { + it('Filter: (( label==["shape 3 points"]) | (attr["type"]=="shape" & width > 50)) & (height > 50 & (clientID == serverID))). All objects not exist.', () => { cy.writeFilterValue( true, '(( label==["points shape"]) | (attr["type"]=="shape" & width > 50)) & (height > 50 & (clientID == serverID)))', From 56fbb02df5adf08a211e5bfcf202dccd6bf1d105 Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Fri, 27 Nov 2020 09:44:57 +0300 Subject: [PATCH 17/28] Cypress test. Canvas color feature. --- .../case_21_canvas_color_feature.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/cypress/integration/actions_tasks_objects/case_21_canvas_color_feature.js diff --git a/tests/cypress/integration/actions_tasks_objects/case_21_canvas_color_feature.js b/tests/cypress/integration/actions_tasks_objects/case_21_canvas_color_feature.js new file mode 100644 index 00000000..00b1a959 --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/case_21_canvas_color_feature.js @@ -0,0 +1,45 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName } from '../../support/const'; + +context('Canvas color feature', () => { + const caseId = '21'; + + before(() => { + cy.openTaskJob(taskName); + }); + + describe(`Testing case "${caseId}"`, () => { + it('Go to settings', () => { + cy.openSettings(); + }); + it('Change canvas background color. Color has been changed', () => { + cy.get('.cvat-player-settings-canvas-background').within(() => { + cy.get('button').click(); + }); + cy.get('.canvas-background-color-picker-popover') + .find('div[title]') + .then((colorPicker) => { + for (let i = 0; i < colorPicker.length; i++) { + cy.get(colorPicker[i]) + .click() + .should('have.css', 'background') + .then((colorPickerBgValue) => { + cy.get('.cvat-canvas-container') + .should('have.css', 'background-color') + .then((canvasBgColor) => { + //For each color change, compare the value with the css value background-color of .cvat-canvas-container + expect(String(colorPickerBgValue.match(/^.*\d+, \d+, \d+\)/))).to.be.equal( + canvasBgColor, + ); + }); + }); + } + }); + }); + }); +}); From acb53d952ffb56edacc39a5e6c9407b8868d2253 Mon Sep 17 00:00:00 2001 From: Dmitriy Oparin Date: Fri, 27 Nov 2020 10:29:04 +0300 Subject: [PATCH 18/28] added check scaleDefault --- .../issue_2174_reset_zoom_in_tag_annotation_mode.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js b/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js index 471baa03..febd82e4 100644 --- a/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js +++ b/tests/cypress/integration/actions_tasks_objects/issue_2174_reset_zoom_in_tag_annotation_mode.js @@ -10,6 +10,7 @@ context('Reset zoom in tag annotation', () => { const issueId = '2174'; let scaleFirstFrame = 0; let scaleSecondFrame = 0; + let scaleDefault = 0; function scaleFrame() { cy.get('.cvat-canvas-container').trigger('wheel', { deltaY: 5 }); @@ -50,6 +51,9 @@ context('Reset zoom in tag annotation', () => { }); it('Scale frame', () => { + cy.getScaleValue().then((value) => { + scaleDefault = value; + }); scaleFrame(); cy.getScaleValue().then((value) => { scaleFirstFrame = value; @@ -62,6 +66,7 @@ context('Reset zoom in tag annotation', () => { cy.getScaleValue().then((value) => { scaleSecondFrame = value; expect(scaleFirstFrame).to.not.equal(scaleSecondFrame); + expect(scaleDefault).to.equal(scaleSecondFrame); }); }); From 906c3c084624ac6699a9b0a36df9f810dcee4d6b Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Fri, 27 Nov 2020 11:30:10 +0300 Subject: [PATCH 19/28] Some fix --- .../actions_tasks_objects/case_21_canvas_color_feature.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/cypress/integration/actions_tasks_objects/case_21_canvas_color_feature.js b/tests/cypress/integration/actions_tasks_objects/case_21_canvas_color_feature.js index 00b1a959..3457c1d2 100644 --- a/tests/cypress/integration/actions_tasks_objects/case_21_canvas_color_feature.js +++ b/tests/cypress/integration/actions_tasks_objects/case_21_canvas_color_feature.js @@ -27,15 +27,13 @@ context('Canvas color feature', () => { for (let i = 0; i < colorPicker.length; i++) { cy.get(colorPicker[i]) .click() - .should('have.css', 'background') + .should('have.css', 'background-color') .then((colorPickerBgValue) => { cy.get('.cvat-canvas-container') .should('have.css', 'background-color') .then((canvasBgColor) => { //For each color change, compare the value with the css value background-color of .cvat-canvas-container - expect(String(colorPickerBgValue.match(/^.*\d+, \d+, \d+\)/))).to.be.equal( - canvasBgColor, - ); + expect(colorPickerBgValue).to.be.equal(canvasBgColor); }); }); } From a804bf74e29a0efb8ecd27e53b53a8598dee66d4 Mon Sep 17 00:00:00 2001 From: Kruchinin Date: Fri, 27 Nov 2020 14:15:51 +0300 Subject: [PATCH 20/28] Apply comments --- .../case_20_objects_ordering_feature.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/cypress/integration/actions_tasks_objects/case_20_objects_ordering_feature.js b/tests/cypress/integration/actions_tasks_objects/case_20_objects_ordering_feature.js index c1006ab2..0d0b426b 100644 --- a/tests/cypress/integration/actions_tasks_objects/case_20_objects_ordering_feature.js +++ b/tests/cypress/integration/actions_tasks_objects/case_20_objects_ordering_feature.js @@ -29,25 +29,19 @@ context('Objects ordering feature', () => { secondY: createRectangleShape2Points.secondY, }; - function idAscent(arr) { - for (let i = 0; i < arr.length; i++) { - if (arr[i] > arr[i + 1]) { - return false; - } - } - return true; - } - function checkSideBarItemOrdering(ordering) { let cvatObjectsSidebarStateItemIdList = []; cy.get('.cvat-objects-sidebar-state-item').then(($cvatObjectsSidebarStateItemId) => { for (let i = 0; i < $cvatObjectsSidebarStateItemId.length; i++) { cvatObjectsSidebarStateItemIdList.push(Number($cvatObjectsSidebarStateItemId[i].id.match(/\d+$/))); } + const idAscent = cvatObjectsSidebarStateItemIdList.reduce((previousValue, currentValue) => { + return previousValue > currentValue ? false : true; + }); if (ordering === 'ascent') { - expect(idAscent(cvatObjectsSidebarStateItemIdList)).to.be.true; //expected true to be true (ascent) + expect(idAscent).to.be.true; //expected true to be true (ascent) } else { - expect(idAscent(cvatObjectsSidebarStateItemIdList)).to.be.false; //expected false to be false (descent) + expect(idAscent).to.be.false; //expected false to be false (descent) } }); } From 441b1d41c2b601dd35b15de9d4ed1e20b09606e1 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Sat, 28 Nov 2020 02:41:22 +0000 Subject: [PATCH 21/28] fix: upgrade @types/react from 16.9.55 to 16.9.56 Snyk has created this PR to upgrade @types/react from 16.9.55 to 16.9.56. See this package in npm: https://www.npmjs.com/package/@types/react See this project in Snyk: https://app.snyk.io/org/cvat/project/c1f463ee-3776-44c4-b0fa-cd2254d0a094?utm_source=github&utm_medium=upgrade-pr --- cvat-ui/package-lock.json | 6 +++--- cvat-ui/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index 57d3d329..8635a3d2 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1213,9 +1213,9 @@ "dev": true }, "@types/react": { - "version": "16.9.55", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.55.tgz", - "integrity": "sha512-6KLe6lkILeRwyyy7yG9rULKJ0sXplUsl98MGoCfpteXf9sPWFWWMknDcsvubcpaTdBuxtsLF6HDUwdApZL/xIg==", + "version": "16.9.56", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.56.tgz", + "integrity": "sha512-gIkl4J44G/qxbuC6r2Xh+D3CGZpJ+NdWTItAPmZbR5mUS+JQ8Zvzpl0ea5qT/ZT3ZNTUcDKUVqV3xBE8wv/DyQ==", "requires": { "@types/prop-types": "*", "csstype": "^3.0.2" diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 1168766d..1df9761f 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -49,7 +49,7 @@ "dependencies": { "@types/lodash": "^4.14.165", "@types/platform": "^1.3.3", - "@types/react": "^16.9.55", + "@types/react": "^16.9.56", "@types/react-color": "^3.0.4", "@types/react-dom": "^16.9.9", "@types/react-redux": "^7.1.11", From 0c4f8882e1c4f709f8f9ad6ca7090b0d2d94e612 Mon Sep 17 00:00:00 2001 From: Dmitry Kalinin Date: Mon, 30 Nov 2020 13:16:23 +0300 Subject: [PATCH 22/28] Fixed projects layout --- .../components/projects-page/project-list.tsx | 31 +++++++++++++------ .../src/components/projects-page/styles.scss | 4 +-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/cvat-ui/src/components/projects-page/project-list.tsx b/cvat-ui/src/components/projects-page/project-list.tsx index 944e4faa..134286be 100644 --- a/cvat-ui/src/components/projects-page/project-list.tsx +++ b/cvat-ui/src/components/projects-page/project-list.tsx @@ -15,7 +15,7 @@ export default function ProjectListComponent(): JSX.Element { const dispatch = useDispatch(); const projectsCount = useSelector((state: CombinedState) => state.projects.count); const { page } = useSelector((state: CombinedState) => state.projects.gettingQuery); - const projectInstances = useSelector((state: CombinedState) => state.projects.current); + let projectInstances = useSelector((state: CombinedState) => state.projects.current); const gettingQuery = useSelector((state: CombinedState) => state.projects.gettingQuery); function changePage(p: number): void { @@ -27,19 +27,30 @@ export default function ProjectListComponent(): JSX.Element { ); } + projectInstances = projectInstances.reduce((rows, key, index) => { + if (index % 4 === 0) { + rows.push([key]); + } else { + rows[rows.length - 1].push(key); + } + return rows; + }, []); + return ( <> - - {projectInstances.map( - (instance: any): JSX.Element => ( - - - - ), - )} - + {projectInstances.map( + (row: any[]): JSX.Element => ( + + {row.map((instance: any) => ( + + + + ))} + + ), + )} diff --git a/cvat-ui/src/components/projects-page/styles.scss b/cvat-ui/src/components/projects-page/styles.scss index 9b113de4..44fb6615 100644 --- a/cvat-ui/src/components/projects-page/styles.scss +++ b/cvat-ui/src/components/projects-page/styles.scss @@ -109,11 +109,11 @@ .cvat-projects-project-item-card { .ant-empty { margin: $grid-unit-size; + height: $grid-unit-size * 16; } img { - height: 100%; - max-height: $grid-unit-size * 18; + height: $grid-unit-size * 18; object-fit: cover; } } From c61402522ed2ac8771a9bf2a185a79dae839074f Mon Sep 17 00:00:00 2001 From: Dmitry Kalinin Date: Mon, 30 Nov 2020 13:22:28 +0300 Subject: [PATCH 23/28] Added CHANGELOG and npm version --- CHANGELOG.md | 1 + cvat-ui/package-lock.json | 2 +- cvat-ui/package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4395462..987b5f11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Exception: "Value must be a user instance" () - Reset zoom option doesn't work in tag annotation mode () - Canvas is busy error () +- Projects view layout fix () ### Security diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index 57d3d329..5063901f 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.10.7", + "version": "1.10.8", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 1168766d..135d15f0 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.10.7", + "version": "1.10.8", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { From 3e336237ab030331c137f1533b26df826d9ae8b2 Mon Sep 17 00:00:00 2001 From: Andrey Zhavoronkov Date: Mon, 30 Nov 2020 14:30:18 +0300 Subject: [PATCH 24/28] fixed getTaskPreview exception handling, slightly improved the backend exception handling --- cvat-ui/src/actions/tasks-actions.ts | 2 +- cvat/apps/engine/views.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts index bf1ba2e4..349cd880 100644 --- a/cvat-ui/src/actions/tasks-actions.ts +++ b/cvat-ui/src/actions/tasks-actions.ts @@ -93,7 +93,7 @@ export function getTasksAsync(query: TasksQuery): ThunkAction, {}, } const array = Array.from(result); - const promises = array.map((task): string => (task as any).frames.preview().catch('')); + const promises = array.map((task): string => (task as any).frames.preview().catch(() => '')); dispatch(getInferenceStatusAsync()); diff --git a/cvat/apps/engine/views.py b/cvat/apps/engine/views.py index e8b14daf..aec2f88a 100644 --- a/cvat/apps/engine/views.py +++ b/cvat/apps/engine/views.py @@ -25,7 +25,7 @@ from drf_yasg.inspectors import CoreAPICompatInspector, NotHandled from drf_yasg.utils import swagger_auto_schema from rest_framework import mixins, serializers, status, viewsets from rest_framework.decorators import action -from rest_framework.exceptions import APIException +from rest_framework.exceptions import APIException, NotFound, ValidationError from rest_framework.permissions import SAFE_METHODS, IsAuthenticated from rest_framework.renderers import JSONRenderer from rest_framework.response import Response @@ -427,16 +427,19 @@ class TaskViewSet(auth.TaskGetQuerySetMixin, viewsets.ModelViewSet): possible_quality_values = ('compressed', 'original') if not data_type or data_type not in possible_data_type_values: - return Response(data='data type not specified or has wrong value', status=status.HTTP_400_BAD_REQUEST) + raise ValidationError(detail='Data type not specified or has wrong value') elif data_type == 'chunk' or data_type == 'frame': if not data_id: - return Response(data='number not specified', status=status.HTTP_400_BAD_REQUEST) + raise ValidationError(detail='Number not specified') elif data_quality not in possible_quality_values: - return Response(data='wrong quality value', status=status.HTTP_400_BAD_REQUEST) + raise ValidationError(detail='Wrong quality value') try: db_task = self.get_object() db_data = db_task.data + if not db_data: + raise NotFound(detail='Cannot find requested data for the task') + frame_provider = FrameProvider(db_task.data) if data_type == 'chunk': @@ -468,7 +471,7 @@ class TaskViewSet(auth.TaskGetQuerySetMixin, viewsets.ModelViewSet): else: return Response(data='unknown data type {}.'.format(data_type), status=status.HTTP_400_BAD_REQUEST) except APIException as e: - return Response(data=e.default_detail, status=e.status_code) + return Response(data=e.get_full_details(), status=e.status_code) except Exception as e: msg = 'cannot get requested data type: {}, number: {}, quality: {}'.format(data_type, data_id, data_quality) slogger.task[pk].error(msg, exc_info=True) From b40da653b38b58c2f8640ce75b1ddb6d0e03e97c Mon Sep 17 00:00:00 2001 From: Andrey Zhavoronkov Date: Mon, 30 Nov 2020 14:38:46 +0300 Subject: [PATCH 25/28] npm version++ --- cvat-ui/package-lock.json | 2 +- cvat-ui/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index 5063901f..9fd70cc3 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.10.8", + "version": "1.10.9", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 135d15f0..e555f248 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.10.8", + "version": "1.10.9", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { From 94a0c0d4837187e9f889f5c536a762b61841ae6c Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Mon, 30 Nov 2020 14:44:34 +0300 Subject: [PATCH 26/28] Fixed empty frames navigation --- cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx index 5a7a40fc..1d12a1db 100644 --- a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx +++ b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx @@ -446,9 +446,9 @@ class AnnotationTopBarContainer extends React.PureComponent { } private searchEmptyFrame(start: number, stop: number): void { - const { canvasInstance, jobInstance, searchAnnotations } = this.props; + const { canvasInstance, jobInstance, searchEmptyFrame } = this.props; if (canvasInstance.isAbleToChangeFrame()) { - searchAnnotations(jobInstance, start, stop); + searchEmptyFrame(jobInstance, start, stop); } } From f4eb7dfb8e217801310f079d2fd577e04df51f9b Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Mon, 30 Nov 2020 14:47:31 +0300 Subject: [PATCH 27/28] Updated changelog & version --- CHANGELOG.md | 1 + cvat-ui/package-lock.json | 2 +- cvat-ui/package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 987b5f11..28037cd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Reset zoom option doesn't work in tag annotation mode () - Canvas is busy error () - Projects view layout fix () +- Empty frames navigation () ### Security diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index 5063901f..9fd70cc3 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.10.8", + "version": "1.10.9", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 135d15f0..e555f248 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.10.8", + "version": "1.10.9", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { From b40863f1a30a754f10531ecd119ab2795998cb45 Mon Sep 17 00:00:00 2001 From: Andrey Zhavoronkov Date: Mon, 30 Nov 2020 14:40:34 +0300 Subject: [PATCH 28/28] updated changelog --- CHANGELOG.md | 1 + cvat/apps/engine/views.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 987b5f11..82897f74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Reset zoom option doesn't work in tag annotation mode () - Canvas is busy error () - Projects view layout fix () +- Fixed the tasks view (infinite loading) when it is impossible to get a preview of the task () ### Security diff --git a/cvat/apps/engine/views.py b/cvat/apps/engine/views.py index aec2f88a..e6d1745e 100644 --- a/cvat/apps/engine/views.py +++ b/cvat/apps/engine/views.py @@ -426,15 +426,15 @@ class TaskViewSet(auth.TaskGetQuerySetMixin, viewsets.ModelViewSet): possible_data_type_values = ('chunk', 'frame', 'preview') possible_quality_values = ('compressed', 'original') - if not data_type or data_type not in possible_data_type_values: - raise ValidationError(detail='Data type not specified or has wrong value') - elif data_type == 'chunk' or data_type == 'frame': - if not data_id: - raise ValidationError(detail='Number not specified') - elif data_quality not in possible_quality_values: - raise ValidationError(detail='Wrong quality value') - try: + if not data_type or data_type not in possible_data_type_values: + raise ValidationError(detail='Data type not specified or has wrong value') + elif data_type == 'chunk' or data_type == 'frame': + if not data_id: + raise ValidationError(detail='Number is not specified') + elif data_quality not in possible_quality_values: + raise ValidationError(detail='Wrong quality value') + db_task = self.get_object() db_data = db_task.data if not db_data: