Improved UX of signing pages (#5295)

main
Kirill Lakhov 3 years ago committed by GitHub
parent e2d9860259
commit 7706eee504
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Mask tools are supported now (brush, eraser, polygon-plus, polygon-minus, returning masks - Mask tools are supported now (brush, eraser, polygon-plus, polygon-minus, returning masks
from online detectors & interactors) (<https://github.com/opencv/cvat/pull/4543>) from online detectors & interactors) (<https://github.com/opencv/cvat/pull/4543>)
- Added Webhooks (<https://github.com/opencv/cvat/pull/4863>) - Added Webhooks (<https://github.com/opencv/cvat/pull/4863>)
- Authentication with social accounts google & github (<https://github.com/opencv/cvat/pull/5147>, <https://github.com/opencv/cvat/pull/5181>) - Authentication with social accounts google & github (<https://github.com/opencv/cvat/pull/5147>, <https://github.com/opencv/cvat/pull/5181>, <https://github.com/opencv/cvat/pull/5295>)
- REST API tests to export job datasets & annotations and validate their structure (<https://github.com/opencv/cvat/pull/5160>) - REST API tests to export job datasets & annotations and validate their structure (<https://github.com/opencv/cvat/pull/5160>)
### Changed ### Changed

@ -1,6 +1,6 @@
{ {
"name": "cvat-ui", "name": "cvat-ui",
"version": "1.44.0", "version": "1.44.1",
"description": "CVAT single-page application", "description": "CVAT single-page application",
"main": "src/index.tsx", "main": "src/index.tsx",
"scripts": { "scripts": {

@ -1,4 +1,4 @@
<svg width="80" height="15" viewBox="0 0 80 15" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="64" height="12" viewBox="0 0 80 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.63399 15C2.37083 15 0 12.5333 0 7.48889C0 2.46667 2.34908 0 6.63399 0H10.6576V2.62222H6.63399C3.78464 2.62222 2.56659 4.06667 2.56659 7.48889C2.56659 10.9333 3.80639 12.3778 6.63399 12.3778H15.0638V15H6.63399Z" fill="white"/> <path d="M6.63399 15C2.37083 15 0 12.5333 0 7.48889C0 2.46667 2.34908 0 6.63399 0H10.6576V2.62222H6.63399C3.78464 2.62222 2.56659 4.06667 2.56659 7.48889C2.56659 10.9333 3.80639 12.3778 6.63399 12.3778H15.0638V15H6.63399Z" fill="white"/>
<path d="M22.412 0L27.2466 13.1004C27.7814 14.5415 28.8724 15 30.2842 15C31.7817 15 32.7657 14.476 33.3005 13.1004L38.3918 0H35.5895L30.9902 11.8559C30.8404 12.2052 30.6265 12.3581 30.2842 12.3581C29.942 12.3581 29.7067 12.2052 29.5783 11.8559L25.1929 0H22.412Z" fill="white"/> <path d="M22.412 0L27.2466 13.1004C27.7814 14.5415 28.8724 15 30.2842 15C31.7817 15 32.7657 14.476 33.3005 13.1004L38.3918 0H35.5895L30.9902 11.8559C30.8404 12.2052 30.6265 12.3581 30.2842 12.3581C29.942 12.3581 29.7067 12.2052 29.5783 11.8559L25.1929 0H22.412Z" fill="white"/>
<path d="M53.0989 3.1441C53.2487 2.79476 53.484 2.64192 53.8262 2.64192C54.1685 2.64192 54.4038 2.79476 54.5322 3.1441L58.9176 15H61.7199L56.8425 1.89956C56.3291 0.524017 55.3451 0 53.8476 0C52.3288 0 51.3448 0.524017 50.81 1.89956L45.74 15H48.521L53.0989 3.1441Z" fill="white"/> <path d="M53.0989 3.1441C53.2487 2.79476 53.484 2.64192 53.8262 2.64192C54.1685 2.64192 54.4038 2.79476 54.5322 3.1441L58.9176 15H61.7199L56.8425 1.89956C56.3291 0.524017 55.3451 0 53.8476 0C52.3288 0 51.3448 0.524017 50.81 1.89956L45.74 15H48.521L53.0989 3.1441Z" fill="white"/>

Before

Width:  |  Height:  |  Size: 981 B

After

Width:  |  Height:  |  Size: 981 B

@ -29,6 +29,12 @@
} }
} }
.anticon.cvat-logo-icon {
> svg {
transform: scale(0.7);
}
}
width: 50%; width: 50%;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
@ -49,12 +55,6 @@
} }
} }
.anticon.cvat-logo-icon {
> svg {
transform: scale(0.7);
}
}
.cvat-header-menu-user-dropdown { .cvat-header-menu-user-dropdown {
display: flex; display: flex;
align-items: center; align-items: center;

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React, { useCallback, useState } from 'react'; import React, { useState } from 'react';
import Form from 'antd/lib/form'; import Form from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
@ -15,6 +15,7 @@ import { Col, Row } from 'antd/lib/grid';
import Title from 'antd/lib/typography/Title'; import Title from 'antd/lib/typography/Title';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import CVATSigningInput, { CVATInputType } from 'components/signing-common/cvat-signing-input';
export interface LoginData { export interface LoginData {
credential: string; credential: string;
@ -35,9 +36,6 @@ function LoginFormComponent(props: Props): JSX.Element {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [credential, setCredential] = useState(''); const [credential, setCredential] = useState('');
const inputReset = useCallback((name: string):void => {
form.setFieldsValue({ [name]: '' });
}, [form]);
const forgotPasswordLink = ( const forgotPasswordLink = (
<Col className='cvat-credentials-link'> <Col className='cvat-credentials-link'>
<Text strong> <Text strong>
@ -97,23 +95,21 @@ function LoginFormComponent(props: Props): JSX.Element {
> >
<Input <Input
autoComplete='credential' autoComplete='credential'
placeholder='enter your email or username'
prefix={<Text>Email or username</Text>} prefix={<Text>Email or username</Text>}
suffix={( className={credential ? 'cvat-input-floating-label-above' : 'cvat-input-floating-label'}
credential && ( suffix={credential && (
<Icon <Icon
component={ClearIcon} component={ClearIcon}
onClick={() => { onClick={() => {
setCredential(''); setCredential('');
inputReset('credential'); form.setFieldsValue({ credential: '', password: '' });
}} }}
/> />
)
)} )}
onChange={(event) => { onChange={(event) => {
const { value } = event.target; const { value } = event.target;
setCredential(value); setCredential(value);
if (!value) inputReset('credential'); if (!value) form.setFieldsValue({ credential: '', password: '' });
}} }}
/> />
</Form.Item> </Form.Item>
@ -129,12 +125,11 @@ function LoginFormComponent(props: Props): JSX.Element {
}, },
]} ]}
> >
<Input.Password <CVATSigningInput
autoComplete='current-password' type={CVATInputType.PASSWORD}
placeholder='enter your password' id='password'
prefix={ placeholder='Password'
<Text>Password</Text> autoComplete='password'
}
/> />
</Form.Item> </Form.Item>
) )

@ -3,12 +3,10 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React, { useCallback, useState } from 'react'; import React, { useState } from 'react';
import Icon from '@ant-design/icons'; import Icon from '@ant-design/icons';
import Form, { RuleRender, RuleObject } from 'antd/lib/form'; import Form, { RuleRender, RuleObject } from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Input from 'antd/lib/input';
import Text from 'antd/lib/typography/Text';
import Checkbox from 'antd/lib/checkbox'; import Checkbox from 'antd/lib/checkbox';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { BackArrowIcon } from 'icons'; import { BackArrowIcon } from 'icons';
@ -17,7 +15,7 @@ import patterns from 'utils/validation-patterns';
import { UserAgreement } from 'reducers'; import { UserAgreement } from 'reducers';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import CVATSigningInput from 'components/signing-common/cvat-signing-input'; import CVATSigningInput, { CVATInputType } from 'components/signing-common/cvat-signing-input';
export interface UserConfirmation { export interface UserConfirmation {
name: string; name: string;
@ -104,9 +102,6 @@ function RegisterFormComponent(props: Props): JSX.Element {
const { fetching, onSubmit, userAgreements } = props; const { fetching, onSubmit, userAgreements } = props;
const [form] = Form.useForm(); const [form] = Form.useForm();
const [usernameEdited, setUsernameEdited] = useState(false); const [usernameEdited, setUsernameEdited] = useState(false);
const inputReset = useCallback((name: string):void => {
form.setFieldsValue({ [name]: '' });
}, [form]);
return ( return (
<div className={`cvat-register-form-wrapper ${userAgreements.length ? 'cvat-register-form-wrapper-extended' : ''}`}> <div className={`cvat-register-form-wrapper ${userAgreements.length ? 'cvat-register-form-wrapper-extended' : ''}`}>
<Row justify='space-between' className='cvat-credentials-navigation'> <Row justify='space-between' className='cvat-credentials-navigation'>
@ -146,9 +141,9 @@ function RegisterFormComponent(props: Props): JSX.Element {
> >
<CVATSigningInput <CVATSigningInput
id='firstName' id='firstName'
placeholder='enter your first name' placeholder='First name'
prefix='First name' autoComplete='given-name'
onReset={() => inputReset('firstName')} onReset={() => form.setFieldsValue({ firstName: '' })}
/> />
</Form.Item> </Form.Item>
</Col> </Col>
@ -166,9 +161,9 @@ function RegisterFormComponent(props: Props): JSX.Element {
> >
<CVATSigningInput <CVATSigningInput
id='lastName' id='lastName'
placeholder='enter your last name' placeholder='Last name'
prefix='Last name' autoComplete='family-name'
onReset={() => inputReset('lastName')} onReset={() => form.setFieldsValue({ lastName: '' })}
/> />
</Form.Item> </Form.Item>
</Col> </Col>
@ -190,9 +185,8 @@ function RegisterFormComponent(props: Props): JSX.Element {
<CVATSigningInput <CVATSigningInput
id='email' id='email'
autoComplete='email' autoComplete='email'
placeholder='enter your email' placeholder='Email'
prefix='Email' onReset={() => form.setFieldsValue({ email: '', username: '' })}
onReset={() => inputReset('email')}
onChange={(event) => { onChange={(event) => {
const { value } = event.target; const { value } = event.target;
if (!usernameEdited) { if (!usernameEdited) {
@ -217,9 +211,9 @@ function RegisterFormComponent(props: Props): JSX.Element {
> >
<CVATSigningInput <CVATSigningInput
id='username' id='username'
placeholder='enter your username' placeholder='Username'
prefix='Username' autoComplete='username'
onReset={() => inputReset('username')} onReset={() => form.setFieldsValue({ username: '' })}
onChange={() => setUsernameEdited(true)} onChange={() => setUsernameEdited(true)}
/> />
</Form.Item> </Form.Item>
@ -233,12 +227,13 @@ function RegisterFormComponent(props: Props): JSX.Element {
}, validatePassword, }, validatePassword,
]} ]}
> >
<Input.Password <CVATSigningInput
placeholder='enter your password' type={CVATInputType.PASSWORD}
prefix={<Text>Password</Text>} id='password1'
placeholder='Password'
autoComplete='new-password'
/> />
</Form.Item> </Form.Item>
{userAgreements.map((userAgreement: UserAgreement): JSX.Element => ( {userAgreements.map((userAgreement: UserAgreement): JSX.Element => (
<Form.Item <Form.Item
className='cvat-agreements-form-item' className='cvat-agreements-form-item'

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React, { useCallback } from 'react'; import React from 'react';
import Form from 'antd/lib/form'; import Form from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Icon from '@ant-design/icons'; import Icon from '@ant-design/icons';
@ -28,9 +28,6 @@ function ResetPasswordFormComponent({ fetching, onSubmit }: Props): JSX.Element
const location = useLocation(); const location = useLocation();
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
const defaultCredential = params.get('credential'); const defaultCredential = params.get('credential');
const inputReset = useCallback((name: string):void => {
form.setFieldsValue({ [name]: '' });
}, [form]);
return ( return (
<div className='cvat-password-reset-form-wrapper'> <div className='cvat-password-reset-form-wrapper'>
<Row justify='space-between' className='cvat-credentials-navigation'> <Row justify='space-between' className='cvat-credentials-navigation'>
@ -74,9 +71,8 @@ function ResetPasswordFormComponent({ fetching, onSubmit }: Props): JSX.Element
> >
<CVATSigningInput <CVATSigningInput
autoComplete='email' autoComplete='email'
placeholder='enter your email' placeholder='Email'
prefix='Email' onReset={() => form.setFieldsValue({ email: '' })}
onReset={() => inputReset('email')}
/> />
</Form.Item> </Form.Item>
<Row> <Row>

@ -1,50 +1,65 @@
// Copyright (C) 2022 CVAT.ai Corporation // Copyright (C) 2022 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import Icon from '@ant-design/icons'; import Icon from '@ant-design/icons';
import Text from 'antd/lib/typography/Text';
import { ClearIcon } from 'icons'; import { ClearIcon } from 'icons';
import { Input } from 'antd'; import { Input } from 'antd';
import Text from 'antd/lib/typography/Text';
interface SocialAccountLinkProps { interface SocialAccountLinkProps {
id?: string; id?: string;
prefix: string;
autoComplete?: string; autoComplete?: string;
placeholder: string; placeholder: string;
value?: string; value?: string;
onReset: () => void; type?: CVATInputType;
onReset?: () => void;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
} }
export enum CVATInputType {
TEXT = 'text',
PASSWORD = 'passord',
}
function CVATSigningInput(props: SocialAccountLinkProps): JSX.Element { function CVATSigningInput(props: SocialAccountLinkProps): JSX.Element {
const { const {
id, prefix, autoComplete, onReset, placeholder, value, onChange, id, autoComplete, type, onReset, placeholder, value, onChange,
} = props; } = props;
const [valueNonEmpty, setValueNonEmpty] = useState(false); const [valueNonEmpty, setValueNonEmpty] = useState(false);
useEffect((): void => {
setValueNonEmpty(!!value);
}, [value]);
if (type === CVATInputType.PASSWORD) {
return (
<Input.Password
value={value}
autoComplete={autoComplete}
className={valueNonEmpty ? 'cvat-input-floating-label-above' : 'cvat-input-floating-label'}
prefix={<Text>{placeholder}</Text>}
id={id}
onChange={onChange}
/>
);
}
return ( return (
<Input <Input
value={value} value={value}
autoComplete={autoComplete} autoComplete={autoComplete}
placeholder={placeholder} className={valueNonEmpty ? 'cvat-input-floating-label-above' : 'cvat-input-floating-label'}
prefix={<Text>{prefix}</Text>} prefix={<Text>{placeholder}</Text>}
id={id} id={id}
suffix={( suffix={valueNonEmpty && (
valueNonEmpty ? ( <Icon
<Icon component={ClearIcon}
component={ClearIcon} onClick={() => {
onClick={() => { setValueNonEmpty(false);
setValueNonEmpty(false); if (onReset) onReset();
onReset(); }}
}} />
/>
) : null
)} )}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => { onChange={onChange}
const { value: inputValue } = event.target;
setValueNonEmpty(!!inputValue);
if (onChange) onChange(event);
}}
/> />
); );
} }

@ -59,11 +59,23 @@ function SignInLayout(props: SignInLayoutComponentProps): JSX.Element {
xl: { span: 8 }, xl: { span: 8 },
xxl: { span: 10 }, xxl: { span: 10 },
}; };
const logoSizes = {
xs: { span: 21 },
sm: { span: 21 },
md: { span: 21 },
lg: { span: 21 },
xl: { span: 21 },
xxl: { span: 22 },
};
return ( return (
<Layout> <Layout>
<SVGSigningBackground className='cvat-signing-background' /> <SVGSigningBackground className='cvat-signing-background' />
<Header className='cvat-signing-header'> <Header className='cvat-signing-header'>
<Icon className='cvat-logo-icon' component={CVATMinimalisticLogo} /> <Row justify='center' align='middle'>
<Col {...logoSizes}>
<Icon className='cvat-logo-icon' component={CVATMinimalisticLogo} />
</Col>
</Row>
</Header> </Header>
<Layout className='cvat-signing-layout'> <Layout className='cvat-signing-layout'>
<Content> <Content>

@ -11,8 +11,10 @@ $error-color: #ff4d4f;
$action-button-color-1: black; $action-button-color-1: black;
$action-button-color-2: gray; $action-button-color-2: gray;
$action-button-color-3: #d4d4d4; $action-button-color-3: #d4d4d4;
$placeholder-color: #c5bfbf;
$base-transition: all 0.8s ease; $base-transition: all 0.8s ease;
$input-transition: all 0.3s ease;
$social-google-background: #4286f5; $social-google-background: #4286f5;
@ -36,7 +38,13 @@ $social-google-background: #4286f5;
.cvat-signing-header { .cvat-signing-header {
background: transparent; background: transparent;
position: absolute; position: fixed;
padding: $grid-unit-size*2 0 0 0;
width: 100%;
svg {
transform: none;
}
} }
.cvat-signing-background { .cvat-signing-background {
@ -60,6 +68,31 @@ $social-google-background: #4286f5;
} }
} }
.cvat-input-floating-label {
.ant-input-prefix {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 1;
font-size: 20px;
transition: $input-transition;
transform-origin: left top;
.ant-typography {
color: $placeholder-color;
}
}
}
.cvat-input-floating-label-above {
@extend .cvat-input-floating-label;
.ant-input-prefix {
transition: $input-transition;
transform: translateY(-100%) scale(0.7);
}
}
.cvat-credentials-form-item { .cvat-credentials-form-item {
.ant-input-affix-wrapper { .ant-input-affix-wrapper {
border: none; border: none;
@ -69,12 +102,9 @@ $social-google-background: #4286f5;
transition: $base-transition; transition: $base-transition;
} }
.ant-input-prefix {
position: absolute;
z-index: 1;
}
.ant-input-affix-wrapper-focused { .ant-input-affix-wrapper-focused {
@extend .cvat-input-floating-label-above;
box-shadow: 0 2px 0 0 black; box-shadow: 0 2px 0 0 black;
} }
@ -94,11 +124,8 @@ $social-google-background: #4286f5;
} }
} }
.ant-typography { .cvat-signing-input-not-empty {
text-transform: uppercase; @extend .cvat-input-floating-label;
color: $border-color-1;
font-size: 12px;
letter-spacing: 1px;
} }
} }
@ -322,8 +349,8 @@ $social-google-background: #4286f5;
} }
letter-spacing: 1px; letter-spacing: 1px;
padding: 0 $grid-unit-size*4 !important;
margin-bottom: $grid-unit-size; margin-bottom: $grid-unit-size;
padding: $grid-unit-size $grid-unit-size*4;
display: flex; display: flex;
} }

@ -12,8 +12,8 @@ context('When clicking on the Logout button, get the user session closed.', () =
let taskId; let taskId;
function login(credential, password) { function login(credential, password) {
cy.get('[placeholder="enter your email or username"]').clear().type(credential); cy.get('#credential').clear().type(credential);
cy.get('[placeholder="enter your password"]').clear().type(password); cy.get('#password').clear().type(password);
cy.get('[type="submit"]').click(); cy.get('[type="submit"]').click();
} }

@ -25,23 +25,23 @@ context('Issue 1599 (Chinese alphabet).', () => {
describe('User registration using the Chinese alphabet.', () => { describe('User registration using the Chinese alphabet.', () => {
it('Filling in the placeholder "First name"', () => { it('Filling in the placeholder "First name"', () => {
cy.get('[placeholder="enter your first name"]').type(firstName).should('not.have.class', 'has-error'); cy.get('#firstName').type(firstName).should('not.have.class', 'has-error');
}); });
it('Filling in the placeholder "Last name"', () => { it('Filling in the placeholder "Last name"', () => {
cy.get('[placeholder="enter your last name"]').type(lastName).should('not.have.class', 'has-error'); cy.get('#lastName').type(lastName).should('not.have.class', 'has-error');
}); });
it('Filling in the placeholder "Username"', () => { it('Filling in the placeholder "Username"', () => {
cy.get('[placeholder="enter your username"]').type(userName); cy.get('#username').type(userName);
}); });
it('Filling in the placeholder "Email address"', () => { it('Filling in the placeholder "Email address"', () => {
cy.get('[placeholder="enter your email"]').type(email); cy.get('#email').type(email);
}); });
it('Filling in the placeholder "Password"', () => { it('Filling in the placeholder "Password"', () => {
cy.get('[placeholder="enter your password"]').type(password); cy.get('#password1').type(password);
}); });
it('Click to "Submit" button', () => { it('Click to "Submit" button', () => {

@ -25,23 +25,23 @@ context('Issue 1599 (Polish alphabet).', () => {
describe('User registration using the Polish alphabet.', () => { describe('User registration using the Polish alphabet.', () => {
it('Filling in the placeholder "First name"', () => { it('Filling in the placeholder "First name"', () => {
cy.get('[placeholder="enter your first name"]').type(firstName).should('not.have.class', 'has-error'); cy.get('#firstName').type(firstName).should('not.have.class', 'has-error');
}); });
it('Filling in the placeholder "Last name"', () => { it('Filling in the placeholder "Last name"', () => {
cy.get('[placeholder="enter your last name"]').type(lastName).should('not.have.class', 'has-error'); cy.get('#lastName').type(lastName).should('not.have.class', 'has-error');
}); });
it('Filling in the placeholder "Username"', () => { it('Filling in the placeholder "Username"', () => {
cy.get('[placeholder="enter your username"]').type(userName); cy.get('#username').type(userName);
}); });
it('Filling in the placeholder "Email address"', () => { it('Filling in the placeholder "Email address"', () => {
cy.get('[placeholder="enter your email"]').type(email); cy.get('#email').type(email);
}); });
it('Filling in the placeholder "Password"', () => { it('Filling in the placeholder "Password"', () => {
cy.get('[placeholder="enter your password"]').type(password); cy.get('#password1').type(password);
}); });
it('Click to "Submit" button', () => { it('Click to "Submit" button', () => {

@ -20,8 +20,8 @@ require('cy-verify-downloads').addCustomCommand();
let selectedValueGlobal = ''; let selectedValueGlobal = '';
Cypress.Commands.add('login', (username = Cypress.env('user'), password = Cypress.env('password'), page = 'tasks') => { Cypress.Commands.add('login', (username = Cypress.env('user'), password = Cypress.env('password'), page = 'tasks') => {
cy.get('[placeholder="enter your email or username"]').type(username); cy.get('#credential').type(username);
cy.get('[placeholder="enter your password"]').type(password); cy.get('#password').type(password);
cy.get('.cvat-credentials-action-button').click(); cy.get('.cvat-credentials-action-button').click();
cy.url().should('contain', `/${page}`); cy.url().should('contain', `/${page}`);
cy.document().then((doc) => { cy.document().then((doc) => {
@ -48,7 +48,7 @@ Cypress.Commands.add('userRegistration', (firstName, lastName, userName, emailAd
cy.get('#lastName').type(lastName); cy.get('#lastName').type(lastName);
cy.get('#username').type(userName); cy.get('#username').type(userName);
cy.get('#email').type(emailAddr); cy.get('#email').type(emailAddr);
cy.get('#password').type(password); cy.get('#password1').type(password);
cy.get('.cvat-credentials-action-button').click(); cy.get('.cvat-credentials-action-button').click();
if (Cypress.browser.family === 'chromium') { if (Cypress.browser.family === 'chromium') {
cy.url().should('include', '/tasks'); cy.url().should('include', '/tasks');

Loading…
Cancel
Save