From ca975072110f8e3a7a18d96df9d35fcbaa5e7ef7 Mon Sep 17 00:00:00 2001
From: Dmitry Kruchinin <33020454+dvkruchinin@users.noreply.github.com>
Date: Wed, 10 Mar 2021 22:48:52 +0300
Subject: [PATCH 1/8] Upload cypress screenshots folder as an artifact if the
tests failed with an error. (#2934)
* added upload cypress screenshots if a tests failed.
* Replace command to run cypress tests in publish_docker_images.yml
---
.github/workflows/main.yml | 6 ++++++
.github/workflows/publish_docker_images.yml | 9 ++++++++-
.github/workflows/schedule.yml | 6 ++++++
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index d136f0cc7e48..5d36ed29cd4a 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -38,6 +38,12 @@ jobs:
cd ./tests
npm ci
npx cypress run --headless --browser chrome
+ - name: Uploading cypress screenshots as an artifact
+ if: failure()
+ uses: actions/upload-artifact@v2
+ with:
+ name: cypress_screenshots
+ path: ${{ github.workspace }}/tests/cypress/screenshots
- name: Collect coverage data
env:
HOST_COVERAGE_DATA_DIR: ${{ github.workspace }}
diff --git a/.github/workflows/publish_docker_images.yml b/.github/workflows/publish_docker_images.yml
index 44aad7e9137b..4c7353ee2c4f 100644
--- a/.github/workflows/publish_docker_images.yml
+++ b/.github/workflows/publish_docker_images.yml
@@ -33,7 +33,14 @@ jobs:
run: |
cd ./tests
npm ci
- npx cypress run --headless --browser chrome
+ npm run cypress:run:chrome
+ - name: Uploading cypress screenshots as an artifact
+ if: failure()
+ uses: actions/upload-artifact@v2
+ with:
+ name: cypress_screenshots
+ path: ${{ github.workspace }}/tests/cypress/screenshots
+
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
diff --git a/.github/workflows/schedule.yml b/.github/workflows/schedule.yml
index ecce8547728e..90f53049f409 100644
--- a/.github/workflows/schedule.yml
+++ b/.github/workflows/schedule.yml
@@ -26,3 +26,9 @@ jobs:
cd ./tests
npm ci
npm run cypress:run:firefox
+ - name: Uploading cypress screenshots as an artifact
+ if: failure()
+ uses: actions/upload-artifact@v2
+ with:
+ name: cypress_screenshots
+ path: ${{ github.workspace }}/tests/cypress/screenshots
From 86eef84717fc901a1910a0c0290758b774835164 Mon Sep 17 00:00:00 2001
From: Dmitry Kruchinin <33020454+dvkruchinin@users.noreply.github.com>
Date: Fri, 12 Mar 2021 15:56:45 +0300
Subject: [PATCH 2/8] Rework Cypress test "Register user, change password,
login with new password". (#2933)
* Rework Cypress test "case 2".
* Add command to deleting a registered users.
* Tests adaptation.
* Update cypress command
* Test adaptation
* Apply comments.
---
.../base_actions_project_task_user.js | 24 +++++-------
.../case_39_issue_2572_rename_task.js | 1 +
.../case_28_review_pipeline_feature.js | 2 +
.../case_2_register_user_change_pass.js | 31 ++++++++--------
.../case_4_assign_taks_job_users.js | 1 +
.../issue_1599_ch_user_registration.js | 7 +++-
.../issue_1599_pl_user_registration.js | 7 +++-
tests/cypress/support/commands.js | 37 +++++++++++++++++++
8 files changed, 78 insertions(+), 32 deletions(-)
diff --git a/tests/cypress/integration/actions_projects/registration_involved/base_actions_project_task_user.js b/tests/cypress/integration/actions_projects/registration_involved/base_actions_project_task_user.js
index 847da80d8bf1..6d93a0f6c341 100644
--- a/tests/cypress/integration/actions_projects/registration_involved/base_actions_project_task_user.js
+++ b/tests/cypress/integration/actions_projects/registration_involved/base_actions_project_task_user.js
@@ -6,15 +6,6 @@
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 = {
@@ -45,11 +36,11 @@ context('Base actions on the project', () => {
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 firstName = 'Seconduser fm';
+ const lastName = 'Seconduser lm';
+ const userName = 'Seconduser';
const emailAddr = `${userName}@local.local`;
- const password = `${randomString(true)}`;
+ const password = 'GDrb41RguF!';
let projectID = '';
function getProjectID(projectName) {
@@ -65,6 +56,10 @@ context('Base actions on the project', () => {
cy.openProject(projectName);
});
+ after(() => {
+ cy.deletingRegisteredUsers([userName]);
+ });
+
describe(`Testing "Base actions on the project"`, () => {
it('Add some labels to project.', () => {
cy.addNewLabel(newLabelName1);
@@ -125,7 +120,7 @@ context('Base actions on the project', () => {
cy.userRegistration(firstName, lastName, userName, emailAddr, password);
cy.goToProjectsList();
// tries to create project
- const failProjectName = `${randomString()}`;
+ const failProjectName = 'failProject';
cy.createProjects(failProjectName, labelName, attrName, textDefaultValue, null, 'fail');
cy.closeNotification('.cvat-notification-notice-create-project-failed');
cy.goToProjectsList();
@@ -159,6 +154,7 @@ context('Base actions on the project', () => {
cy.goToTaskList();
cy.contains('strong', taskName.firstTask).should('not.exist');
cy.contains('strong', taskName.secondTask).should('not.exist');
+ cy.logout();
});
});
});
diff --git a/tests/cypress/integration/actions_tasks_objects/registration_involved/case_39_issue_2572_rename_task.js b/tests/cypress/integration/actions_tasks_objects/registration_involved/case_39_issue_2572_rename_task.js
index f88754998c46..289ebf440d92 100644
--- a/tests/cypress/integration/actions_tasks_objects/registration_involved/case_39_issue_2572_rename_task.js
+++ b/tests/cypress/integration/actions_tasks_objects/registration_involved/case_39_issue_2572_rename_task.js
@@ -47,6 +47,7 @@ context('Rename a task.', () => {
});
after(() => {
+ cy.deletingRegisteredUsers([secondUserName]);
cy.login();
cy.deleteTask(newNaskName);
});
diff --git a/tests/cypress/integration/actions_users/registration_involved/case_28_review_pipeline_feature.js b/tests/cypress/integration/actions_users/registration_involved/case_28_review_pipeline_feature.js
index 29c6c55416eb..1c2242bdd18d 100644
--- a/tests/cypress/integration/actions_users/registration_involved/case_28_review_pipeline_feature.js
+++ b/tests/cypress/integration/actions_users/registration_involved/case_28_review_pipeline_feature.js
@@ -130,6 +130,8 @@ context('Review pipeline feature', () => {
after(() => {
cy.goToTaskList();
cy.deleteTask(taskName);
+ cy.logout();
+ cy.deletingRegisteredUsers([secondUserName, thirdUserName]);
});
describe(`Testing "${labelName}"`, () => {
diff --git a/tests/cypress/integration/actions_users/registration_involved/case_2_register_user_change_pass.js b/tests/cypress/integration/actions_users/registration_involved/case_2_register_user_change_pass.js
index adf65255fda0..609755a98116 100644
--- a/tests/cypress/integration/actions_users/registration_involved/case_2_register_user_change_pass.js
+++ b/tests/cypress/integration/actions_users/registration_involved/case_2_register_user_change_pass.js
@@ -1,26 +1,19 @@
-// Copyright (C) 2020 Intel Corporation
+// Copyright (C) 2020-2021 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('Register user, change password, login with new password', () => {
const caseId = '2';
- const firstName = `${randomString()}`;
- const lastName = `${randomString()}`;
- const userName = `${randomString()}`;
+ const firstName = 'SecuserfmCaseTwo';
+ const lastName = 'SecuserlmCaseTwo';
+ const userName = 'SecuserCase2';
const emailAddr = `${userName}@local.local`;
- const password = `${randomString(true)}`;
- const newPassword = `${randomString(true)}`;
+ const password = 'GDrb41RguF!';
+ const incorrectCurrentPassword = 'gDrb41RguF!';
+ const newPassword = 'bYdOk8#eEd';
+ const secondNewPassword = 'ndTh48@yVY';
function changePassword(userName, password, newPassword) {
cy.get('.cvat-right-header')
@@ -41,6 +34,12 @@ context('Register user, change password, login with new password', () => {
cy.url().should('include', '/auth/register');
});
+ after(() => {
+ cy.get('.cvat-modal-change-password').find('[aria-label="Close"]').click();
+ cy.logout(userName);
+ cy.deletingRegisteredUsers([userName]);
+ });
+
describe(`Testing "Case ${caseId}"`, () => {
it('Register user, change password', () => {
cy.userRegistration(firstName, lastName, userName, emailAddr, password);
@@ -55,7 +54,7 @@ context('Register user, change password, login with new password', () => {
cy.login(userName, newPassword);
});
it('Change password with incorrect current password', () => {
- changePassword(userName, `${randomString(true)}`, newPassword);
+ changePassword(userName, incorrectCurrentPassword, secondNewPassword);
cy.get('.cvat-notification-notice-change-password-failed').should('exist');
});
});
diff --git a/tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js b/tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js
index e2be6d576f51..be9e5acfb0cd 100644
--- a/tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js
+++ b/tests/cypress/integration/actions_users/registration_involved/case_4_assign_taks_job_users.js
@@ -43,6 +43,7 @@ context('Multiple users. Assign task, job.', () => {
});
after(() => {
+ cy.deletingRegisteredUsers([secondUserName, thirdUserName]);
cy.login();
cy.deleteTask(taskName);
});
diff --git a/tests/cypress/integration/actions_users/registration_involved/issue_1599_ch_user_registration.js b/tests/cypress/integration/actions_users/registration_involved/issue_1599_ch_user_registration.js
index bbcb5db7ff19..37842cee3539 100644
--- a/tests/cypress/integration/actions_users/registration_involved/issue_1599_ch_user_registration.js
+++ b/tests/cypress/integration/actions_users/registration_involved/issue_1599_ch_user_registration.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2020 Intel Corporation
+// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@@ -16,6 +16,11 @@ context('Issue 1599 (Chinese alphabet).', () => {
cy.url().should('include', '/auth/register');
});
+ after(() => {
+ cy.logout(userName);
+ cy.deletingRegisteredUsers([userName]);
+ });
+
describe('User registration using the Chinese alphabet.', () => {
it('Filling in the placeholder "First name"', () => {
cy.get('[placeholder="First name"]').type(firstName).should('not.have.class', 'has-error');
diff --git a/tests/cypress/integration/actions_users/registration_involved/issue_1599_pl_user_registration.js b/tests/cypress/integration/actions_users/registration_involved/issue_1599_pl_user_registration.js
index 2ee411d9a504..e0605bf974e9 100644
--- a/tests/cypress/integration/actions_users/registration_involved/issue_1599_pl_user_registration.js
+++ b/tests/cypress/integration/actions_users/registration_involved/issue_1599_pl_user_registration.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2020 Intel Corporation
+// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@@ -16,6 +16,11 @@ context('Issue 1599 (Polish alphabet).', () => {
cy.url().should('include', '/auth/register');
});
+ after(() => {
+ cy.logout(userName);
+ cy.deletingRegisteredUsers([userName]);
+ });
+
describe('User registration using the Polish alphabet.', () => {
it('Filling in the placeholder "First name"', () => {
cy.get('[placeholder="First name"]').type(firstName).should('not.have.class', 'has-error');
diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js
index 0d73528ea36a..ae179d1a0dfe 100644
--- a/tests/cypress/support/commands.js
+++ b/tests/cypress/support/commands.js
@@ -41,6 +41,43 @@ Cypress.Commands.add('userRegistration', (firstName, lastName, userName, emailAd
}
});
+Cypress.Commands.add('deletingRegisteredUsers', (accountToDelete) => {
+ cy.request({
+ method: 'POST',
+ url: '/api/v1/auth/login',
+ body: {
+ username: Cypress.env('user'),
+ email: Cypress.env('email'),
+ password: Cypress.env('password'),
+ },
+ }).then((responce) => {
+ const authKey = responce['body']['key'];
+ cy.request({
+ url: '/api/v1/users?page_size=all',
+ headers: {
+ Authorization: `Token ${authKey}`,
+ },
+ }).then((responce) => {
+ const responceResult = responce['body']['results'];
+ for (const user of responceResult) {
+ const userId = user['id'];
+ const userName = user['username'];
+ for (const account of accountToDelete) {
+ if (userName === account) {
+ cy.request({
+ method: 'DELETE',
+ url: `/api/v1/users/${userId}`,
+ headers: {
+ Authorization: `Token ${authKey}`,
+ },
+ });
+ }
+ }
+ }
+ });
+ });
+});
+
Cypress.Commands.add(
'createAnnotationTask',
(
From d33daa1669925c42c789cf4b786fa9c4453337cb Mon Sep 17 00:00:00 2001
From: Nikita Manovich
Date: Mon, 15 Mar 2021 22:48:49 +0300
Subject: [PATCH 3/8] Fallback to libx264 encoder if libopenh264 isn't
available (#2875)
* Fallback to libx264 encoder if libopenh264 isn't available
* Unified ChunkWriters interfaces: quality setting from 0..100 range for both writers (#2895)
* Fix bandit warning.
Co-authored-by: Andrey Zhavoronkov
---
cvat/apps/engine/media_extractors.py | 57 +++++++++++++++++-----------
cvat/apps/engine/task.py | 33 ++++++++--------
2 files changed, 52 insertions(+), 38 deletions(-)
diff --git a/cvat/apps/engine/media_extractors.py b/cvat/apps/engine/media_extractors.py
index b72bf0cda297..0383341b528f 100644
--- a/cvat/apps/engine/media_extractors.py
+++ b/cvat/apps/engine/media_extractors.py
@@ -313,7 +313,8 @@ def get_image_size(self, i):
class IChunkWriter(ABC):
def __init__(self, quality, dimension=DimensionType.DIM_2D):
- self._image_quality = quality
+ # translate inversed range [1:100] to [0:51]
+ self._image_quality = round(51 * (100 - quality) / 99)
self._dimension = dimension
@staticmethod
@@ -373,12 +374,27 @@ def save_as_chunk(self, images, chunk_path):
return image_sizes
class Mpeg4ChunkWriter(IChunkWriter):
- def __init__(self, _):
- super().__init__(17)
+ def __init__(self, quality=67):
+ super().__init__(quality)
self._output_fps = 25
-
- @staticmethod
- def _create_av_container(path, w, h, rate, options, f='mp4'):
+ try:
+ codec = av.codec.Codec('libopenh264', 'w')
+ self._codec_name = codec.name
+ self._codec_opts = {
+ 'profile': 'constrained_baseline',
+ 'qmin': str(self._image_quality),
+ 'qmax': str(self._image_quality),
+ 'rc_mode': 'buffer',
+ }
+ except av.codec.codec.UnknownCodecError:
+ codec = av.codec.Codec('libx264', 'w')
+ self._codec_name = codec.name
+ self._codec_opts = {
+ "crf": str(self._image_quality),
+ "preset": "ultrafast",
+ }
+
+ def _create_av_container(self, path, w, h, rate, options, f='mp4'):
# x264 requires width and height must be divisible by 2 for yuv420p
if h % 2:
h += 1
@@ -386,7 +402,7 @@ def _create_av_container(path, w, h, rate, options, f='mp4'):
w += 1
container = av.open(path, 'w',format=f)
- video_stream = container.add_stream('libopenh264', rate=rate)
+ video_stream = container.add_stream(self._codec_name, rate=rate)
video_stream.pix_fmt = "yuv420p"
video_stream.width = w
video_stream.height = h
@@ -406,12 +422,7 @@ def save_as_chunk(self, images, chunk_path):
w=input_w,
h=input_h,
rate=self._output_fps,
- options={
- 'profile': 'constrained_baseline',
- 'qmin': str(self._image_quality),
- 'qmax': str(self._image_quality),
- 'rc_mode': 'buffer',
- },
+ options=self._codec_opts,
)
self._encode_images(images, output_container, output_v_stream)
@@ -434,10 +445,15 @@ def _encode_images(images, container, stream):
class Mpeg4CompressedChunkWriter(Mpeg4ChunkWriter):
def __init__(self, quality):
- # translate inversed range [1:100] to [0:51]
- self._image_quality = round(51 * (100 - quality) / 99)
- self._output_fps = 25
-
+ super().__init__(quality)
+ if self._codec_name == 'libx264':
+ self._codec_opts = {
+ 'profile': 'baseline',
+ 'coder': '0',
+ 'crf': str(self._image_quality),
+ 'wpredp': '0',
+ 'flags': '-loop',
+ }
def save_as_chunk(self, images, chunk_path):
if not images:
@@ -458,12 +474,7 @@ def save_as_chunk(self, images, chunk_path):
w=output_w,
h=output_h,
rate=self._output_fps,
- options={
- 'profile': 'constrained_baseline',
- 'qmin': str(self._image_quality),
- 'qmax': str(self._image_quality),
- 'rc_mode': 'buffer',
- },
+ options=self._codec_opts,
)
self._encode_images(images, output_container, output_v_stream)
diff --git a/cvat/apps/engine/task.py b/cvat/apps/engine/task.py
index 46a4bf9accdd..b54b3af95581 100644
--- a/cvat/apps/engine/task.py
+++ b/cvat/apps/engine/task.py
@@ -10,9 +10,9 @@
import rq
import shutil
from traceback import print_exception
-from urllib import error as urlerror
from urllib import parse as urlparse
from urllib import request as urlrequest
+import requests
from cvat.apps.engine.media_extractors import get_mime, MEDIA_TYPES, Mpeg4ChunkWriter, ZipChunkWriter, Mpeg4CompressedChunkWriter, ZipCompressedChunkWriter, ValidateDimension
from cvat.apps.engine.models import DataChoice, StorageMethodChoice, StorageChoice, RelatedFile
@@ -195,20 +195,16 @@ def _download_data(urls, upload_dir):
job.meta['status'] = '{} is being downloaded..'.format(url)
job.save_meta()
- req = urlrequest.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
- try:
- with urlrequest.urlopen(req) as fp, open(os.path.join(upload_dir, name), 'wb') as tfp:
- while True:
- block = fp.read(8192)
- if not block:
- break
- tfp.write(block)
- except urlerror.HTTPError as err:
- raise Exception("Failed to download " + url + ". " + str(err.code) + ' - ' + err.reason)
- except urlerror.URLError as err:
- raise Exception("Invalid URL: " + url + ". " + err.reason)
+ response = requests.get(url, stream=True)
+ if response.status_code == 200:
+ response.raw.decode_content = True
+ with open(os.path.join(upload_dir, name), 'wb') as output_file:
+ shutil.copyfileobj(response.raw, output_file)
+ else:
+ raise Exception("Failed to download " + url)
local_files[name] = True
+
return list(local_files.keys())
@transaction.atomic
@@ -298,13 +294,20 @@ def update_progress(progress):
update_progress.call_counter = (update_progress.call_counter + 1) % len(progress_animation)
compressed_chunk_writer_class = Mpeg4CompressedChunkWriter if db_data.compressed_chunk_type == DataChoice.VIDEO else ZipCompressedChunkWriter
- original_chunk_writer_class = Mpeg4ChunkWriter if db_data.original_chunk_type == DataChoice.VIDEO else ZipChunkWriter
+ if db_data.original_chunk_type == DataChoice.VIDEO:
+ original_chunk_writer_class = Mpeg4ChunkWriter
+ # Let's use QP=17 (that is 67 for 0-100 range) for the original chunks, which should be visually lossless or nearly so.
+ # A lower value will significantly increase the chunk size with a slight increase of quality.
+ original_quality = 67
+ else:
+ original_chunk_writer_class = ZipChunkWriter
+ original_quality = 100
kwargs = {}
if validate_dimension.dimension == DimensionType.DIM_3D:
kwargs["dimension"] = validate_dimension.dimension
compressed_chunk_writer = compressed_chunk_writer_class(db_data.image_quality, **kwargs)
- original_chunk_writer = original_chunk_writer_class(100)
+ original_chunk_writer = original_chunk_writer_class(original_quality)
# calculate chunk size if it isn't specified
if db_data.chunk_size is None:
From b42d4b0e3d5d5663c2bdc9de39fb2b760d0f36f2 Mon Sep 17 00:00:00 2001
From: Nikita Manovich
Date: Tue, 16 Mar 2021 09:41:34 +0300
Subject: [PATCH 4/8] fix: upgrade async-mutex from 0.3.0 to 0.3.1 (#2955)
Snyk has created this PR to upgrade async-mutex from 0.3.0 to 0.3.1.
See this package in npm:
https://www.npmjs.com/package/async-mutex
See this project in Snyk:
https://app.snyk.io/org/cvat/project/6457ad1f-89ea-4fc9-a983-bdb963e74086?utm_source=github&utm_medium=upgrade-pr
Co-authored-by: snyk-bot
---
cvat-data/package-lock.json | 6 +++---
cvat-data/package.json | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/cvat-data/package-lock.json b/cvat-data/package-lock.json
index 32dfd645e303..cd0c0979e02d 100644
--- a/cvat-data/package-lock.json
+++ b/cvat-data/package-lock.json
@@ -1208,9 +1208,9 @@
"dev": true
},
"async-mutex": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.0.tgz",
- "integrity": "sha512-6VIpUM7s37EMXvnO3TvujgaS6gx4yJby13BhxovMYSap7nrbS0gJ1UzGcjD+HElNSdTz/+IlAIqj7H48N0ZlyQ==",
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.1.tgz",
+ "integrity": "sha512-vRfQwcqBnJTLzVQo72Sf7KIUbcSUP5hNchx6udI1U6LuPQpfePgdjJzlCe76yFZ8pxlLjn9lwcl/Ya0TSOv0Tw==",
"requires": {
"tslib": "^2.1.0"
},
diff --git a/cvat-data/package.json b/cvat-data/package.json
index cd7926956148..2f7517667986 100644
--- a/cvat-data/package.json
+++ b/cvat-data/package.json
@@ -21,7 +21,7 @@
"worker-loader": "^2.0.0"
},
"dependencies": {
- "async-mutex": "^0.3.0",
+ "async-mutex": "^0.3.1",
"jszip": "3.6.0"
},
"scripts": {
From c9830d201e1cf78397c355ca6b68d9cdc7ea261d Mon Sep 17 00:00:00 2001
From: Dmitry Kruchinin <33020454+dvkruchinin@users.noreply.github.com>
Date: Tue, 16 Mar 2021 11:11:29 +0300
Subject: [PATCH 5/8] Cypress test. Canvas 3D functionality. Basic actions
(#2921)
* Add .pcd files to testing functional. Add cypress test.
* Add plugin to compare images.
* Add const file for testing canvas3d
* Add canvas3d folder to testing via cypress
* Add compare images to cypress.
* Some rework.
* Disable the test for Firefox.
* Disable the test in the cypress_cron_type.json (the record removed).
* Apply comments.
* Added click() as workaround for excluding wait()
* Try to click on the disabled workspace selectors.
For exluding check ant* class.
* Regenerate .pcd.
Add loop to wheel top/side/front
* Loop for wheel for perspective also.
---
tests/cypress.json | 1 +
.../assets/test_canvas3d.zip | Bin 0 -> 367257 bytes
...56_canvas3d_functionality_basic_actions.js | 179 ++++++++++++++++++
.../plugins/compareImages/addPlugin.js | 16 ++
.../compareImages/compareImagesCommand.js | 10 +
tests/cypress/plugins/index.js | 2 +
tests/cypress/support/commands.js | 1 +
tests/cypress/support/const_canvas3d.js | 40 ++++
8 files changed, 249 insertions(+)
create mode 100644 tests/cypress/integration/canvas3d_functionality/assets/test_canvas3d.zip
create mode 100644 tests/cypress/integration/canvas3d_functionality/case_56_canvas3d_functionality_basic_actions.js
create mode 100644 tests/cypress/plugins/compareImages/addPlugin.js
create mode 100644 tests/cypress/plugins/compareImages/compareImagesCommand.js
create mode 100644 tests/cypress/support/const_canvas3d.js
diff --git a/tests/cypress.json b/tests/cypress.json
index fa98e950252c..eb67a4ae14be 100644
--- a/tests/cypress.json
+++ b/tests/cypress.json
@@ -15,6 +15,7 @@
"actions_tasks_objects/**/*",
"actions_users/**/*",
"actions_projects/**/*",
+ "canvas3d_functionality/*",
"remove_users_tasks_projects.js"
]
}
diff --git a/tests/cypress/integration/canvas3d_functionality/assets/test_canvas3d.zip b/tests/cypress/integration/canvas3d_functionality/assets/test_canvas3d.zip
new file mode 100644
index 0000000000000000000000000000000000000000..c6defd2869c615989ef7e07b062f5b042bc098df
GIT binary patch
literal 367257
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