Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/ui/src/app/mocks/profile.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const PROFILE_FORM: ProfileFormat[] = [
type: FormControlType.EMAIL_MULTIPLE,
validation: {
required: true,
max: '30',
},
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@
<mat-error *ngIf="getControl(formControlName).hasError('required')">
<span>The field is required</span>
</mat-error>
<mat-error *ngIf="getControl(formControlName).hasError('maxlength')">
<span
>The field must be a maximum of
{{ getControl(formControlName).getError('maxlength').requiredLength }}
characters.</span
>
</mat-error>
</mat-form-field>
</ng-template>

Expand All @@ -190,10 +197,12 @@
floatLabel="always"
class="profile-form-field"
[formGroup]="profileForm">
<input
<textarea
matInput
cdkTextareaAutosize
#autosize="cdkTextareaAutosize"
id="{{ formControlName }}-group"
[formControlName]="formControlName" />
[formControlName]="formControlName"></textarea>
<mat-hint *ngIf="description">{{ description }}</mat-hint>
<mat-error
*ngIf="getControl(formControlName).hasError('invalid_format')"
Expand All @@ -204,6 +213,13 @@
<mat-error *ngIf="getControl(formControlName).hasError('required')">
<span>The field is required</span>
</mat-error>
<mat-error *ngIf="getControl(formControlName).hasError('maxlength')">
<span
>The field must be a maximum of
{{ getControl(formControlName).getError('maxlength').requiredLength }}
characters.</span
>
</mat-error>
</mat-form-field>
</ng-template>

Expand All @@ -230,6 +246,13 @@
letters, numbers, &#64; and . (dot).</span
>
</mat-error>
<mat-error *ngIf="getControl(formControlName).hasError('maxlength')">
<span
>The field must be a maximum of
{{ getControl(formControlName).getError('maxlength').requiredLength }}
characters.</span
>
</mat-error>
</mat-form-field>
</ng-template>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ describe('ProfileFormComponent', () => {
} else if (item.type === FormControlType.SELECT_MULTIPLE) {
const select = fields[uiIndex].querySelector('mat-checkbox');
expect(select).toBeTruthy();
} else if (item.type === FormControlType.TEXTAREA) {
const input = fields[uiIndex]?.querySelector('textarea');
expect(input).toBeTruthy();
} else {
const input = fields[uiIndex]?.querySelector('input');
expect(input).toBeTruthy();
Expand Down Expand Up @@ -202,99 +205,87 @@ describe('ProfileFormComponent', () => {
}

if (
(item.type === FormControlType.TEXT ||
item.type === FormControlType.TEXTAREA) &&
item.validation?.required
item.type === FormControlType.TEXT ||
item.type === FormControlType.TEXTAREA ||
item.type === FormControlType.EMAIL_MULTIPLE
) {
describe('text or text-long', () => {
it('should have "required" error when field is not filled', () => {
const fields = compiled.querySelectorAll('.profile-form-field');
const uiIndex = index + 1; // as Profile name is at 0 position, the json items start from 1 i
const input: HTMLInputElement = fields[uiIndex].querySelector(
'input'
) as HTMLInputElement;
['', ' '].forEach(value => {
input.value = value;
input.dispatchEvent(new Event('input'));
component.getControl(index).markAsTouched();
fixture.detectChanges();

const error =
fields[uiIndex].querySelector('mat-error')?.textContent;

expect(error).toContain('The field is required');
});
});

it('should have "invalid_format" error when field does not satisfy validation rules', () => {
[
'very long value very long value very long value very long value very long value very long value very long value very long value very long value very long value',
'as\\\\\\\\\\""""""""',
].forEach(value => {
describe('text or text-long or email-multiple', () => {
if (item.validation?.required) {
it('should have "required" error when field is not filled', () => {
const fields = compiled.querySelectorAll('.profile-form-field');
const uiIndex = index + 1; // as Profile name is at 0 position, the json items start from 1 i
const input: HTMLInputElement = fields[uiIndex].querySelector(
'input'
const input = fields[uiIndex].querySelector(
'.mat-mdc-input-element'
) as HTMLInputElement;
input.value = value;
input.dispatchEvent(new Event('input'));
component.getControl(index).markAsTouched();
fixture.detectChanges();

const error = compiled.querySelector('mat-error')?.textContent;
expect(error).toContain(
'Please, check. “ and \\ are not allowed.'
);
['', ' '].forEach(value => {
input.value = value;
input.dispatchEvent(new Event('input'));
component.getControl(index).markAsTouched();
fixture.detectChanges();
const errors = fields[uiIndex].querySelectorAll('mat-error');
let hasError = false;
errors.forEach(error => {
if (error.textContent === 'The field is required') {
hasError = true;
}
});

expect(hasError).toBeTrue();
});
});
});
});
}
}

if (
item.type === FormControlType.EMAIL_MULTIPLE &&
item.validation?.required
) {
describe('text or text-long', () => {
it('should have "required" error when field is not filled', () => {
it('should have "invalid_format" error when field does not satisfy validation rules', () => {
const fields = compiled.querySelectorAll('.profile-form-field');
const uiIndex = index + 1; // as Profile name is at 0 position, the json items start from 1 i
const input: HTMLInputElement = fields[uiIndex].querySelector(
'input'
'.mat-mdc-input-element'
) as HTMLInputElement;
['', ' '].forEach(value => {
input.value = value;
input.dispatchEvent(new Event('input'));
component.getControl(index).markAsTouched();
fixture.detectChanges();

const error =
fields[uiIndex].querySelector('mat-error')?.textContent;

expect(error).toContain('The field is required');
input.value = 'as\\\\\\\\\\""""""""';
input.dispatchEvent(new Event('input'));
component.getControl(index).markAsTouched();
fixture.detectChanges();
const result =
item.type === FormControlType.EMAIL_MULTIPLE
? 'Please, check the email address. Valid e-mail can contain only latin letters, numbers, @ and . (dot).'
: 'Please, check. “ and \\ are not allowed.';
const errors = fields[uiIndex].querySelectorAll('mat-error');
let hasError = false;
errors.forEach(error => {
if (error.textContent === result) {
hasError = true;
}
});

expect(hasError).toBeTrue();
});

it('should have "invalid_format" error when field does not satisfy validation rules', () => {
[
'very long value very long value very long value very long value very long value very long value very long value very long value very long value very long value',
'as\\\\\\\\\\""""""""',
].forEach(value => {
if (item.validation?.max) {
it('should have "maxlength" error when field is exceeding max length', () => {
const fields = compiled.querySelectorAll('.profile-form-field');
const uiIndex = index + 1; // as Profile name is at 0 position, the json items start from 1 i
const input: HTMLInputElement = fields[uiIndex].querySelector(
'input'
'.mat-mdc-input-element'
) as HTMLInputElement;
input.value = value;
input.value =
'very long value very long value very long value very long value very long value very long value very long value very long value very long value very long value';
input.dispatchEvent(new Event('input'));
component.getControl(index).markAsTouched();
fixture.detectChanges();

const error = compiled.querySelector('mat-error')?.textContent;
expect(error).toContain(
'Please, check the email address. Valid e-mail can contain only latin letters, numbers, @ and . (dot).'
);
const errors = fields[uiIndex].querySelectorAll('mat-error');
let hasError = false;
errors.forEach(error => {
if (
error.textContent ===
`The field must be a maximum of ${item.validation?.max} characters.`
) {
hasError = true;
}
});
expect(hasError).toBeTrue();
});
});
}
});
}
});
Expand All @@ -318,7 +309,7 @@ describe('ProfileFormComponent', () => {
describe('Save button', () => {
it('should be enabled when required fields are present', () => {
component.nameControl.setValue('test');
component.getControl('0').setValue('test@test.test');
component.getControl('0').setValue('a@test.te;b@test.te, c@test.te');
component.getControl('1').setValue('test');
component.getControl('2').setValue('test');
component.getControl('3').setValue({ 0: true, 1: true, 2: true });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TextFieldModule } from '@angular/cdk/text-field';
import {
ChangeDetectionStrategy,
Component,
Expand All @@ -31,6 +32,7 @@ import {
FormGroup,
ReactiveFormsModule,
ValidatorFn,
Validators,
} from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { DeviceValidators } from '../../devices/components/device-form/device.validators';
Expand All @@ -54,6 +56,7 @@ import { ProfileValidators } from './profile.validators';
MatFormFieldModule,
MatSelectModule,
MatCheckboxModule,
TextFieldModule,
],
templateUrl: './profile-form.component.html',
styleUrl: './profile-form.component.scss',
Expand Down Expand Up @@ -109,15 +112,14 @@ export class ProfileFormComponent implements OnInit {
if (validation.required) {
validators.push(this.profileValidators.textRequired());
}
if (validation.max) {
validators.push(Validators.maxLength(Number(validation.max)));
}
if (type === FormControlType.EMAIL_MULTIPLE) {
validators.push(
this.profileValidators.emailStringFormat(Number(validation.max))
);
validators.push(this.profileValidators.emailStringFormat());
}
if (type === FormControlType.TEXT || type === FormControlType.TEXTAREA) {
validators.push(
this.profileValidators.textFormat(Number(validation.max))
);
validators.push(this.profileValidators.textFormat());
}
}
return validators;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { Profile } from '../../../model/profile';
@Injectable({ providedIn: 'root' })
export class ProfileValidators {
readonly MULTIPLE_EMAIL_FORMAT_REGEXP = new RegExp(
'^(([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)(\\s*;\\s*|\\s*$))*$',
'^(([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)(\\s*(;|,)\\s*|\\s*$))*$',
'i'
);

Expand Down Expand Up @@ -58,19 +58,18 @@ export class ProfileValidators {
return null;
}

public emailStringFormat(maxLength: number = 128): ValidatorFn {
return this.stringFormat(this.MULTIPLE_EMAIL_FORMAT_REGEXP, maxLength);
public emailStringFormat(): ValidatorFn {
return this.stringFormat(this.MULTIPLE_EMAIL_FORMAT_REGEXP);
}

public textFormat(maxLength: number = 128): ValidatorFn {
return this.stringFormat(this.STRING_FORMAT_REGEXP, maxLength);
public textFormat(): ValidatorFn {
return this.stringFormat(this.STRING_FORMAT_REGEXP);
}

private stringFormat(regExp: RegExp, maxLength: number = 28): ValidatorFn {
private stringFormat(regExp: RegExp): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value?.trim();
if (value) {
if (value.length > maxLength) return { invalid_format: true };
const result = regExp.test(value);
return !result ? { invalid_format: true } : null;
}
Expand Down