Merge branch 'develop' into mk/share_without_copying_

main
Maya 5 years ago
commit d52d5baab4

@ -30,23 +30,25 @@ 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 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}'
- 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
- 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'

@ -33,6 +33,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Exception: "Value must be a user instance" (<https://github.com/openvinotoolkit/cvat/pull/2441>)
- Reset zoom option doesn't work in tag annotation mode (<https://github.com/openvinotoolkit/cvat/pull/2443>)
- Canvas is busy error (<https://github.com/openvinotoolkit/cvat/pull/2437>)
- Projects view layout fix (<https://github.com/openvinotoolkit/cvat/pull/2503>)
- Fixed the tasks view (infinite loading) when it is impossible to get a preview of the task (<https://github.com/openvinotoolkit/cvat/pull/2504>)
- Empty frames navigation (<https://github.com/openvinotoolkit/cvat/pull/2505>)
### Security

@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.10.7",
"version": "1.10.9",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -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"

@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.10.7",
"version": "1.10.9",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
@ -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",

@ -93,7 +93,7 @@ export function getTasksAsync(query: TasksQuery): ThunkAction<Promise<void>, {},
}
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());

@ -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 (
<>
<Row type='flex' justify='center' align='middle'>
<Col className='cvat-projects-list' md={22} lg={18} xl={16} xxl={14}>
<Row gutter={[8, 8]}>
{projectInstances.map(
(instance: any): JSX.Element => (
<Col xs={8} sm={8} xl={6} key={instance.id}>
<ProjectItem projectInstance={instance} />
</Col>
),
)}
</Row>
{projectInstances.map(
(row: any[]): JSX.Element => (
<Row gutter={[8, 8]}>
{row.map((instance: any) => (
<Col span={6} key={instance.id}>
<ProjectItem projectInstance={instance} />
</Col>
))}
</Row>
),
)}
</Col>
</Row>
<Row type='flex' justify='center' align='middle'>

@ -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;
}
}

@ -446,9 +446,9 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
}
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);
}
}

@ -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
@ -431,17 +431,20 @@ 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:
return Response(data='data type not specified or has wrong value', status=status.HTTP_400_BAD_REQUEST)
elif data_type == 'chunk' or data_type == 'frame':
if not data_id:
return Response(data='number not specified', status=status.HTTP_400_BAD_REQUEST)
elif data_quality not in possible_quality_values:
return Response(data='wrong quality value', status=status.HTTP_400_BAD_REQUEST)
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:
raise NotFound(detail='Cannot find requested data for the task')
frame_provider = FrameProvider(db_task.data)
if data_type == 'chunk':
@ -473,7 +476,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 FileNotFoundError as ex:
msg = f"{ex.strerror} {ex.filename}"
slogger.task[pk].error(msg, exc_info=True)

@ -0,0 +1,3 @@
# Copyright (C) 2020 Intel Corporation
#
# SPDX-License-Identifier: MIT

@ -6,12 +6,13 @@
"defaultCommandTimeout": 25000,
"env": {
"user": "admin",
"email": "admin@localhost.company",
"password": "12qwaszx"
},
"testFiles": [
"auth_page.js",
"actions_tasks_objects/*",
"actions_users/*",
"actions_users/**/*",
"actions_projects/*",
"remove_users_tasks_projects.js"
]

@ -0,0 +1,148 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
/// <reference types="cypress" />
import { taskName } from '../../support/const';
context('Filters functionality.', () => {
const caseId = '18';
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 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' },
];
const createPolygonShape = {
reDraw: false,
type: 'Shape',
labelName: labelShape,
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: labelTrack,
firstX: 260,
firstY: 200,
secondX: 360,
secondY: 250,
};
const createRectangleShape4Points = {
points: 'By 4 Points',
type: 'Shape',
labelName: labelShape,
firstX: 550,
firstY: 350,
secondX: 650,
secondY: 350,
thirdX: 650,
thirdY: 450,
fourthX: 550,
fourthY: 450,
};
const createPolygonTrack = {
reDraw: false,
type: 'Track',
labelName: labelTrack,
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(labelShape, additionalAttrsLabelShape);
cy.addNewLabel(labelTrack, additionalAttrsLabelTrack);
cy.openJob();
});
describe(`Testing case "${caseId}"`, () => {
it('Draw several objects (different shapes, tracks, labels)', () => {
cy.createPolygon(createPolygonShape);
cy.createRectangle(createRectangleTrack2Points);
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=="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=="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, #cvat-objects-sidebar-state-item-1,3
checkingFilterApplication([1, 3]);
});
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_2,4, #cvat-objects-sidebar-state-item-2,4
checkingFilterApplication([2, 4]);
});
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 == 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=="shape 3 points" & attr["polylines"]==true) | (label=="track 4 points" & width > 60). Only the objects polygon and rectangle exist.', () => {
cy.writeFilterValue(
true,
'(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==["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)))',
);
checkingFilterApplication([]);
});
});
});

@ -0,0 +1,77 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
/// <reference types="cypress" />
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 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).to.be.true; //expected true to be true (ascent)
} else {
expect(idAscent).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');
});
});
});

@ -0,0 +1,43 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
/// <reference types="cypress" />
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-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(colorPickerBgValue).to.be.equal(canvasBgColor);
});
});
}
});
});
});
});

@ -7,7 +7,6 @@
import { taskName, labelName } from '../../support/const';
context('Check error canvas is busy at resize element', () => {
const issueId = '1922';
const createRectangleShape2Points = {
points: 'By 2 Points',
@ -43,9 +42,10 @@ context('Check error canvas is busy at resize element', () => {
const secondY = createRectangleShape2Points.secondY;
cy.get('.cvat-canvas-container')
.trigger('mousemove', secondX - 10, secondY - 10) // activate second shape
.trigger('mousedown', secondX, secondY, {button: 0})
.trigger('mousedown', secondX, secondY, { button: 0 })
.trigger('mousemove', secondX + 100, secondY + 100)
.get('body').type('d') // go to previous frame
.get('body')
.type('d') // go to previous frame
.trigger('mouseup');
});

@ -0,0 +1,93 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
/// <reference types="cypress" />
import { taskName, labelName } from '../../support/const';
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 });
}
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('Set "reset zoom" to true', () => {
changeCheckboxResetZoom('check');
});
it('Go to tag annotation', () => {
cy.changeWorkspace('Tag annotation', labelName);
});
it('Scale frame', () => {
cy.getScaleValue().then((value) => {
scaleDefault = value;
});
scaleFrame();
cy.getScaleValue().then((value) => {
scaleFirstFrame = value;
});
});
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);
expect(scaleDefault).to.equal(scaleSecondFrame);
});
});
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);
});
});
});
});

@ -5,6 +5,12 @@
/// <reference types="cypress" />
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');
cy.url().should('include', '/auth/register');
@ -12,33 +18,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');
});

@ -5,6 +5,12 @@
/// <reference types="cypress" />
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');
cy.url().should('include', '/auth/register');
@ -12,27 +18,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', () => {

@ -0,0 +1,39 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
/// <reference types="cypress" />
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', () => {
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. 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')
.contains(`We have sent an email with a confirmation link to ${emailAddr}.`)
.should('exist');
cy.wait('@userRegister').its('status').should('eq', 201);
});
});
});

@ -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) => {

@ -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(
@ -299,11 +301,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 +416,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 +427,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();
}
});
@ -408,6 +447,14 @@ Cypress.Commands.add('createTag', (labelName) => {
});
});
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();
});
Cypress.Commands.add('goToRegisterPage', () => {
cy.get('a[href="/auth/register"]').click();
cy.url().should('include', '/auth/register');
@ -422,3 +469,24 @@ 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]);
});
});
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);
});
});

@ -0,0 +1,19 @@
{
"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_projects.js"
]
}

@ -0,0 +1,6 @@
version: '3.3'
services:
cvat:
environment:
DJANGO_SETTINGS_MODULE: tests.email_settings

@ -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'

@ -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",

Loading…
Cancel
Save