diff --git a/tests/cypress.json b/tests/cypress.json
index fa98e950..eb67a4ae 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 00000000..c6defd28
Binary files /dev/null and b/tests/cypress/integration/canvas3d_functionality/assets/test_canvas3d.zip differ
diff --git a/tests/cypress/integration/canvas3d_functionality/case_56_canvas3d_functionality_basic_actions.js b/tests/cypress/integration/canvas3d_functionality/case_56_canvas3d_functionality_basic_actions.js
new file mode 100644
index 00000000..aa6aa212
--- /dev/null
+++ b/tests/cypress/integration/canvas3d_functionality/case_56_canvas3d_functionality_basic_actions.js
@@ -0,0 +1,179 @@
+// Copyright (C) 2021 Intel Corporation
+//
+// SPDX-License-Identifier: MIT
+
+///
+
+import { taskName } from '../../support/const_canvas3d';
+// Firefox does not yet support WebGL in headless mode: https://bugzilla.mozilla.org/show_bug.cgi?id=1375585 (disabled in the cypress_cron_type.json)
+context('Canvas 3D functionality. Basic actions.', () => {
+ const caseId = '56';
+ const screenshotsPath =
+ 'cypress/screenshots/canvas3d_functionality/case_56_canvas3d_functionality_basic_actions.js';
+
+ function compareImages(imgBefore, imgAfter) {
+ cy.compareImages(`${screenshotsPath}/${imgBefore}`, `${screenshotsPath}/${imgAfter}`).then((diffPercent) => {
+ expect(diffPercent).to.be.gt(0);
+ });
+ }
+
+ function testPerspectiveChangeOnKeyPress(key, screenshotNameBefore, screenshotNameAfter) {
+ cy.get('.cvat-canvas3d-perspective').trigger('mouseover').screenshot(screenshotNameBefore);
+ cy.get('body').type(`{alt}${key}`);
+ cy.get('.cvat-canvas3d-perspective').screenshot(screenshotNameAfter);
+ compareImages(`${screenshotNameBefore}.png`, `${screenshotNameAfter}.png`);
+ }
+
+ function testPerspectiveChangeOnArrowKeyPress(key, screenshotNameBefore, screenshotNameAfter) {
+ cy.get('.cvat-canvas3d-perspective').trigger('mouseover').screenshot(screenshotNameBefore);
+ cy.get('body').type(key);
+ cy.get('.cvat-canvas3d-perspective').screenshot(screenshotNameAfter);
+ compareImages(`${screenshotNameBefore}.png`, `${screenshotNameAfter}.png`);
+ }
+
+ function testPerspectiveChangeOnWheel(screenshotNameBefore, screenshotNameAfter) {
+ cy.get('.cvat-canvas3d-perspective').screenshot(screenshotNameBefore);
+ for (let i = 0; i < 5; i++) {
+ cy.get('.cvat-canvas3d-perspective').trigger('wheel', { deltaY: -200 });
+ }
+ cy.get('.cvat-canvas3d-perspective').screenshot(screenshotNameAfter);
+ compareImages(`${screenshotNameBefore}.png`, `${screenshotNameAfter}.png`);
+ }
+
+ function testTopSideFrontChangeOnWheel(element, deltaY, screenshotNameBefore, screenshotNameAfter) {
+ cy.get(element).screenshot(screenshotNameBefore);
+ for (let i = 0; i < 10; i++) {
+ cy.get(element).trigger('wheel', { deltaY: deltaY });
+ }
+ cy.get(element).screenshot(screenshotNameAfter);
+ compareImages(`${screenshotNameBefore}.png`, `${screenshotNameAfter}.png`);
+ }
+
+ before(() => {
+ cy.openTaskJob(taskName);
+ });
+
+ after(() => {
+ cy.goToTaskList();
+ cy.deleteTask(taskName);
+ });
+
+ describe(`Testing case "${caseId}"`, () => {
+ it('Check existing of elements.', () => {
+ cy.get('.cvat-canvas3d-perspective')
+ .should('exist')
+ .and('be.visible')
+ .within(() => {
+ cy.get('.cvat-canvas3d-perspective-arrow-directions')
+ .should('exist')
+ .and('be.visible')
+ .within(() => {
+ cy.get('[aria-label="arrow-up"]').should('exist').and('be.visible');
+ cy.get('[aria-label="arrow-left"]').should('exist').and('be.visible');
+ cy.get('[aria-label="arrow-down"]').should('exist').and('be.visible');
+ cy.get('[aria-label="arrow-right"]').should('exist').and('be.visible');
+ });
+ cy.get('.cvat-canvas3d-perspective-directions')
+ .should('exist')
+ .and('be.visible')
+ .within(() => {
+ cy.contains('button', 'U').should('exist').and('be.visible');
+ cy.contains('button', 'I').should('exist').and('be.visible');
+ cy.contains('button', 'O').should('exist').and('be.visible');
+ cy.contains('button', 'J').should('exist').and('be.visible');
+ cy.contains('button', 'K').should('exist').and('be.visible');
+ cy.contains('button', 'L').should('exist').and('be.visible');
+ });
+ });
+ cy.get('.cvat-canvas3d-topview').should('exist').and('be.visible');
+ cy.get('.cvat-canvas3d-sideview').should('exist').and('be.visible');
+ cy.get('.cvat-canvas3d-frontview').should('exist').and('be.visible');
+ cy.get('.cvat-canvas-controls-sidebar')
+ .should('exist')
+ .and('be.visible')
+ .within(() => {
+ cy.get('.cvat-move-control').should('exist').and('be.visible');
+ cy.get('.cvat-cursor-control').should('exist').and('be.visible');
+ cy.get('.cvat-draw-cuboid-control').should('exist').and('be.visible');
+ cy.get('[aria-label="camera"]').should('exist').and('be.visible');
+ });
+ });
+
+ it('Check workspace selector.', () => {
+ // Try to click on the disabled workspace selectors. The value of the selector should not changed.
+ cy.get('.cvat-workspace-selector').should('contain.text', 'Standard 3D').click();
+ for (const dropdownItems of [
+ '[title="Attribute annotation"]',
+ '[title="Tag annotation"]',
+ '[title="Review"]',
+ ]) {
+ cy.get('.cvat-workspace-selector-dropdown')
+ .not('.ant-select-dropdown-hidden')
+ .within(() => {
+ cy.get(dropdownItems).click();
+ });
+ cy.get('.cvat-workspace-selector').should('contain.text', 'Standard 3D');
+ }
+ });
+
+ it('Interaction with the frame change buttons.', () => {
+ cy.get('.cvat-player-last-button').click();
+ cy.checkFrameNum(2);
+ cy.get('.cvat-player-filename-wrapper').should('contain.text', 'generated_pcd_50000_points.pcd');
+ cy.get('.cvat-player-first-button').click();
+ cy.checkFrameNum(0);
+ cy.get('.cvat-player-filename-wrapper').should('contain.text', 'generated_pcd_100000_points.pcd');
+ cy.get('.cvat-player-forward-button').click();
+ cy.checkFrameNum(2);
+ cy.get('.cvat-player-filename-wrapper').should('contain.text', 'generated_pcd_50000_points.pcd');
+ cy.get('.cvat-player-backward-button').click();
+ cy.checkFrameNum(0);
+ cy.get('.cvat-player-filename-wrapper').should('contain.text', 'generated_pcd_100000_points.pcd');
+ cy.get('.cvat-player-next-button').click();
+ cy.checkFrameNum(1);
+ cy.get('.cvat-player-filename-wrapper').should('contain.text', 'generated_pcd_10000_points.pcd');
+ cy.get('.cvat-player-previous-button').click();
+ cy.checkFrameNum(0);
+ cy.get('.cvat-player-filename-wrapper').should('contain.text', 'generated_pcd_100000_points.pcd');
+ cy.get('.cvat-player-play-button').click();
+ cy.checkFrameNum(2);
+ cy.get('.cvat-player-filename-wrapper').should('contain.text', 'generated_pcd_50000_points.pcd');
+ cy.get('.cvat-player-first-button').click(); // Return to first frame
+ });
+
+ it('Testing perspective visual regressions.', () => {
+ testPerspectiveChangeOnWheel('perspective_before_wheel', 'perspective_after_wheel');
+ testPerspectiveChangeOnKeyPress('u', 'before_press_altU', 'after_press_altU');
+ testPerspectiveChangeOnKeyPress('o', 'before_press_altO', 'after_press_altO');
+ testPerspectiveChangeOnKeyPress('i', 'before_press_altI', 'after_press_altI');
+ testPerspectiveChangeOnKeyPress('k', 'before_press_altK', 'after_press_altK');
+ testPerspectiveChangeOnKeyPress('j', 'before_press_altJ', 'after_press_altJ');
+ testPerspectiveChangeOnKeyPress('l', 'before_press_altL', 'after_press_altL');
+ testPerspectiveChangeOnArrowKeyPress('{uparrow}', 'before_press_uparrow', 'after_press_uparrow');
+ testPerspectiveChangeOnArrowKeyPress('{downarrow}', 'before_press_downarrow', 'after_press_downarrow');
+ testPerspectiveChangeOnArrowKeyPress('{leftarrow}', 'before_press_leftarrow', 'after_press_leftarrow');
+ testPerspectiveChangeOnArrowKeyPress('{rightarrow}', 'before_press_rightarrow', 'after_press_rightarrow');
+ });
+
+ it('Testing top/side/front views visual regressions.', () => {
+ testTopSideFrontChangeOnWheel(
+ '.cvat-canvas3d-topview',
+ -1000,
+ 'topview_before_wheel',
+ 'topview_after_wheel',
+ );
+ testTopSideFrontChangeOnWheel(
+ '.cvat-canvas3d-sideview',
+ -1000,
+ 'sideview_before_wheel',
+ 'sideview_after_wheel',
+ );
+ testTopSideFrontChangeOnWheel(
+ '.cvat-canvas3d-frontview',
+ -1000,
+ 'frontview_before_wheel',
+ 'frontview_after_wheel',
+ );
+ });
+ });
+});
diff --git a/tests/cypress/plugins/compareImages/addPlugin.js b/tests/cypress/plugins/compareImages/addPlugin.js
new file mode 100644
index 00000000..3cb8529d
--- /dev/null
+++ b/tests/cypress/plugins/compareImages/addPlugin.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Intel Corporation
+//
+// SPDX-License-Identifier: MIT
+
+// eslint-disable-next-line no-undef
+exports.compareImages = compareImages;
+
+const Jimp = require('jimp');
+
+async function compareImages(args) {
+ const imgBase = await Jimp.read(args.imgBase);
+ const imgAfterChanges = await Jimp.read(args.imgAfterChanges);
+ const diff = Jimp.diff(imgBase, imgAfterChanges);
+
+ return diff.percent;
+}
diff --git a/tests/cypress/plugins/compareImages/compareImagesCommand.js b/tests/cypress/plugins/compareImages/compareImagesCommand.js
new file mode 100644
index 00000000..92c49f14
--- /dev/null
+++ b/tests/cypress/plugins/compareImages/compareImagesCommand.js
@@ -0,0 +1,10 @@
+// Copyright (C) 2021 Intel Corporation
+//
+// SPDX-License-Identifier: MIT
+
+Cypress.Commands.add('compareImages', function (imgBase, imgAfterChanges) {
+ return cy.task('compareImages', {
+ imgBase: imgBase,
+ imgAfterChanges: imgAfterChanges,
+ });
+});
diff --git a/tests/cypress/plugins/index.js b/tests/cypress/plugins/index.js
index b9748b5b..b529d9b6 100644
--- a/tests/cypress/plugins/index.js
+++ b/tests/cypress/plugins/index.js
@@ -6,12 +6,14 @@
const { imageGenerator } = require('../plugins/imageGenerator/addPlugin');
const { createZipArchive } = require('../plugins/createZipArchive/addPlugin');
+const { compareImages } = require('../plugins/compareImages/addPlugin');
const fs = require('fs');
module.exports = (on, config) => {
require('@cypress/code-coverage/task')(on, config);
on('task', { imageGenerator });
on('task', { createZipArchive });
+ on('task', { compareImages });
on('task', {
log(message) {
console.log(message);
diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js
index ae179d1a..d707c8eb 100644
--- a/tests/cypress/support/commands.js
+++ b/tests/cypress/support/commands.js
@@ -8,6 +8,7 @@ require('cypress-file-upload');
require('../plugins/imageGenerator/imageGeneratorCommand');
require('../plugins/createZipArchive/createZipArchiveCommand');
require('cypress-localstorage-commands');
+require('../plugins/compareImages/compareImagesCommand');
let selectedValueGlobal = '';
diff --git a/tests/cypress/support/const_canvas3d.js b/tests/cypress/support/const_canvas3d.js
new file mode 100644
index 00000000..2228893b
--- /dev/null
+++ b/tests/cypress/support/const_canvas3d.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Intel Corporation
+//
+// SPDX-License-Identifier: MIT
+
+///
+
+export const labelName = `points cloud`;
+export const taskName = `Canvas 3D functionality`;
+export const pcdPngZipArr = '../../cypress/integration/canvas3d_functionality/assets/test_canvas3d.zip';
+export const attrName = `Attr for ${labelName}`;
+export const textDefaultValue = 'Some default value for type Text';
+export const advancedConfigurationParams = false;
+export const multiAttrParams = false;
+
+it('Prepare to testing', () => {
+ cy.visit('/');
+ cy.login();
+ cy.get('.cvat-tasks-page').should('exist');
+ let listItems = [];
+ cy.document().then((doc) => {
+ const collection = Array.from(doc.querySelectorAll('.cvat-item-task-name'));
+ for (let i = 0; i < collection.length; i++) {
+ listItems.push(collection[i].innerText);
+ }
+ if (listItems.indexOf(taskName) === -1) {
+ cy.task('log', "A task doesn't exist. Creating.");
+ cy.createAnnotationTask(
+ taskName,
+ labelName,
+ attrName,
+ textDefaultValue,
+ pcdPngZipArr,
+ multiAttrParams,
+ advancedConfigurationParams,
+ );
+ } else {
+ cy.task('log', 'The task exist. Skipping creation.');
+ }
+ });
+});