Skip to content

Commit

Permalink
Fixed surface interval validation with unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jirkapok committed Dec 20, 2023
1 parent f1049b7 commit 1e1ba46
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import { PreferencesStore } from '../shared/preferencesStore';
import { Preferences } from '../shared/preferences';
import { DiveSchedules } from '../shared/dive.schedules';
import { ReloadDispatcher } from '../shared/reloadDispatcher';
import { Time } from 'scuba-physics';
import { SurfaceIntervalComponent } from '../surface-interval/surface-interval.component';
import { MaskitoModule } from '@maskito/angular';

export class SimpleDepthsPage {
constructor(private fixture: ComponentFixture<DepthsSimpleComponent>) { }
Expand All @@ -26,6 +29,10 @@ export class SimpleDepthsPage {
return this.fixture.debugElement.query(By.css('#duration')).nativeElement as HTMLInputElement;
}

public get surfaceIntervalInput(): HTMLInputElement {
return this.fixture.debugElement.query(By.css('#surfaceInterval')).nativeElement as HTMLInputElement;
}

public get applyMaxDurationButton(): HTMLButtonElement {
return this.fixture.debugElement.query(By.css('#btnApplyDuration')).nativeElement as HTMLButtonElement;
}
Expand All @@ -43,8 +50,8 @@ describe('Depths Simple Component', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [DepthsSimpleComponent],
imports: [ReactiveFormsModule],
declarations: [ DepthsSimpleComponent, SurfaceIntervalComponent ],
imports: [ ReactiveFormsModule, MaskitoModule ],
providers: [
WorkersFactoryCommon, PlannerService,
UnitConversion, InputControls, DiveSchedules,
Expand Down Expand Up @@ -73,8 +80,6 @@ describe('Depths Simple Component', () => {
expect(eventFired).toBeTruthy();
}));

// TODO test for surface interval validation

describe('Duration reloaded enforced by', () => {
it('Apply max NDL', inject([DiveSchedules, ReloadDispatcher],
(schedule: DiveSchedules, dispatcher: ReloadDispatcher) => {
Expand Down Expand Up @@ -123,4 +128,26 @@ describe('Depths Simple Component', () => {
expect(selected.depths.plannedDepthMeters).toBe(18);
}));
});

describe('Surface interval validation', () => {
it('Does apply valid value', inject([DiveSchedules],
(schedules: DiveSchedules) => {
schedules.add();
fixture.detectChanges();
simplePage.surfaceIntervalInput.value = '02:30';
simplePage.surfaceIntervalInput.dispatchEvent(new Event('input'));
expect(schedules.selected.surfaceInterval).toBe(Time.oneMinute * 150);
}));

it('Does not apply invalid value', inject([DiveSchedules],
(schedules: DiveSchedules) => {
schedules.add();
schedules.selected.surfaceInterval = Time.oneHour; // to switch from readonly
fixture.detectChanges();
simplePage.surfaceIntervalInput.value = '02:aa';
simplePage.surfaceIntervalInput.dispatchEvent(new Event('input'));
expect(schedules.selected.surfaceInterval).toEqual(Time.oneHour);
expect(component.simpleForm.invalid).toBeTruthy();
}));
});
});
23 changes: 20 additions & 3 deletions projects/planner/src/app/shared/ValidatorGroups.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable } from '@angular/core';
import { Validators, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
import { Validators, ValidatorFn, AbstractControl, ValidationErrors, FormControl } from '@angular/forms';
import { RangeConstants, UnitConversion } from './UnitConversion';
import { DateFormats } from './formaters';

@Injectable()
export class ValidatorGroups {
Expand Down Expand Up @@ -83,12 +84,28 @@ export class ValidatorGroups {
return [Validators.required, this.validateMinRmv, this.validateMaxRmv];
}

private get ranges(): RangeConstants {
return this.units.ranges;
}

public rangeFor(range: [number, number]): ValidatorFn[] {
return [Validators.required, Validators.min(range[0]), Validators.max(range[1])];
}

private get ranges(): RangeConstants {
return this.units.ranges;
public surfaceInterval(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const field = control as FormControl<string | null>;
const value = field?.value;
const parsed = DateFormats.parseToShortTime(value);

if(value !== null && (value.length === 0 || !parsed)) {
return {
surfaceInterval: true
};
}

return null;
};
}

// only these RMV methods needs direct access to the range without component reload
Expand Down
1 change: 0 additions & 1 deletion projects/planner/src/app/shared/formaters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ export class DateFormats {
return result;
}

// TODO add tests for surface interval formatting
/**
* Converts string in form "HH:MM" to number of seconds.
* Returns null, if value cant be parsed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { DiveSchedules } from '../shared/dive.schedules';
import { UnitConversion } from '../shared/UnitConversion';
import { ReloadDispatcher } from '../shared/reloadDispatcher';
import { InputControls } from '../shared/inputcontrols';
import { ValidatorGroups } from '../shared/ValidatorGroups';

describe('SurfaceIntervalComponent', () => {
let component: SurfaceIntervalComponent;
Expand All @@ -15,7 +16,7 @@ describe('SurfaceIntervalComponent', () => {
declarations: [SurfaceIntervalComponent],
providers: [
DiveSchedules, UnitConversion, DecimalPipe,
ReloadDispatcher, InputControls,
ReloadDispatcher, InputControls, ValidatorGroups
]
});
fixture = TestBed.createComponent(SurfaceIntervalComponent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ReloadDispatcher } from '../shared/reloadDispatcher';
import { Streamed } from '../shared/streamed';
import { DateFormats } from '../shared/formaters';
import { InputControls } from '../shared/inputcontrols';
import { ValidatorGroups } from '../shared/ValidatorGroups';

@Component({
selector: 'app-surface-interval',
Expand All @@ -26,14 +27,14 @@ export class SurfaceIntervalComponent extends Streamed implements OnInit {
private schedules: DiveSchedules,
private fb: NonNullableFormBuilder,
private dispatcher: ReloadDispatcher,
private inputs: InputControls) {
private inputs: InputControls,
private validators: ValidatorGroups) {
super();
}

public get surfaceIntervalInvalid(): boolean {
const surfaceControl = this.form.get(this.controlName) as AbstractControl;
const parsed = DateFormats.parseToShortTime(this.formControlValue);
return this.inputs.controlInValid(surfaceControl) || (!parsed && !this.schedules.selected.primary);
return this.inputs.controlInValid(surfaceControl);
}

public get surfaceReadOnly(): boolean {
Expand All @@ -44,10 +45,6 @@ export class SurfaceIntervalComponent extends Streamed implements OnInit {
return this.surfaceReadOnly ? 'First dive' : 'HH:MM';
}

public get isFirstDive(): boolean {
return this.schedules.selected.isFirst;
}

private get surfaceInterval(): string | null {
if(this.schedules.selected.primary) {
return null;
Expand All @@ -57,17 +54,14 @@ export class SurfaceIntervalComponent extends Streamed implements OnInit {
return DateFormats.formatShortTime(currentSeconds);
}

private get formControlValue(): string | null {
const field = this.form.get(this.controlName) as FormControl<string | null>;
return field?.value;
}

public ngOnInit(): void {
if(!this.form) {
this.form = new FormGroup([]);
this.form = this.fb.group([]);
}

const control = this.fb.control(this.surfaceInterval);
const control = this.fb.control(this.surfaceInterval, [
this.validators.surfaceInterval()
]);
this.form.addControl(this.controlName, control);

this.dispatcher.depthChanged$.pipe(takeUntil(this.unsubscribe$))
Expand Down Expand Up @@ -101,7 +95,8 @@ export class SurfaceIntervalComponent extends Streamed implements OnInit {
return;
}

this.setSurfaceInterval(this.formControlValue);
const field = this.form.get(this.controlName) as FormControl<string | null>;
this.setSurfaceInterval(field?.value);
}

private setSurfaceInterval(newValue?: string | null) {
Expand Down

0 comments on commit 1e1ba46

Please sign in to comment.