Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Form is not valid even after all form fields are valid

I m trying to solve one Angular form validation Hands on, for that i made the below form, this form is passing all the test cases except one, looks like the problem is with testing file(app.component.spec.ts) that is readable only working Demo of the handson shows form status as VALID. but it fails testing

I am stuck with this handson form last two days.Your help is cordially appreciated.

-------------------[read only]app.component.spec.ts----------

import { AppComponent } from './app.component';
import { ReactiveFormsModule, FormControl, AbstractControl, FormGroup } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { asNativeElements, DebugElement } from '@angular/core';

describe('AppComponent', () => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ReactiveFormsModule],
      declarations: [AppComponent]
    })
    .compileComponents();

    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
  }));

  // it('should create the component', () => {
  //   expect(component).toBeTruthy();
  // });

  it('form should be invalid when empty', () => {
    expect(component.contactForm.invalid).toBeTruthy();
  });

  it('form should be valid on entering required fields', () => {
    fixture.detectChanges(); // ngOninit()
    component.name.setValue('david');
    component.phone.setValue('9999999999');

    expect(component.contactForm.valid).toBeTruthy();
  });

  describe('#name', () => {
    let name: AbstractControl;

    beforeEach(() => {
      name = component.contactForm.controls['name'];
    });

    it('should be invalid if empty', () => {
      expect(name.invalid).toBeTruthy();
    });

    it('should be a "required" field', () => {
      expect(name.errors['required']).toBeTruthy();
    });

    it('should be valid if some value is present', fakeAsync(() => {
      name.setValue('david');
      expect(name.valid).toBeTruthy();
    }));
  });

  describe('#phone', () => {
    let phone: AbstractControl;
    beforeEach(() => {
      phone = component.contactForm.controls['phone'];
    });

    it('should be invalid if empty', () => {
      expect(phone.invalid).toBeTruthy();
    });

    it('should have "required" validation', () => {
      expect(phone.errors['required']).toBeTruthy();
    });

    it('should accept only numbers(pattern validation)', () => {
      phone.setValue('abc');
      expect(phone.errors['pattern']).toBeTruthy();
    });

    it('should have 10 digits(minlength & maxlength validation)', () => {
      phone.setValue('123');
      expect(phone.errors['minlength']).toBeTruthy();
      phone.setValue('12333333333');
      expect(phone.errors['maxlength']).toBeTruthy();
    });
  });

  describe('#address - zip', () => {
    let address;
    let zip;
    beforeEach(() => {
      address = component.contactForm.controls['address'] as FormGroup ;
      zip = address.controls['zip'] ;
      fixture.detectChanges(); // ngOnInit()
    });
    it('should be a number', fakeAsync(() => {
      zip.setValue('abc');
      expect(zip.errors['pattern']).toBeTruthy();
      zip.setValue('123456');
      fixture.detectChanges();
      expect(zip.valid).toBeTruthy();
    }));
    it('should have 6 digits exactly', () => {
      // enter 3 digits and check for minlength validation
      zip.setValue('123');
      expect(zip.errors['minlength']).toBeTruthy();

      // enter 7 digits and check for maxlength validation
      zip.setValue('1234567');
      fixture.detectChanges(); // update changes, angular will not do for you automatically
      expect(zip.errors['maxlength']).toBeTruthy();
    });
  });
});

Here is the test error

AppComponent
 ✖ form should be valid on entering required fields
   HeadlessChrome 75.0.3770 (Linux 0.0.0)
  Expected false to be truthy.
    at UserContext. (src/app/app.component.spec.ts:35:41)
    at ZoneDelegate.invoke (node_modules/zone.js/dist/zone-evergreen.js:359:1)
    at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke 
 (node_modules/zone.js/dist/zone-testing.js:308:1)
       at ZoneDelegate.invoke (node_modules/zone.js/dist/zone-evergreen.js:358:1)
like image 847
bali Avatar asked Oct 04 '19 05:10

bali


People also ask

What is dirty in angular form?

When the user changes the value in the watched field, the control is marked as "dirty" When the user blurs the form control element, the control is marked as "touched"

Is it good to let angular do form validation for You?

In other words, it's not always good enough to let Angular do the validating for you. You need to do it programmatically. One good use case for that is the "final pass" just before a user submits a form. In that case, you want to make sure that all the fields on the form are valid before sending everything to the downstream service.

What happens when input is invalid in ngform?

The ngForm.form.valid property does not change when a form's input is invalidated. The inputs invalidate correctly, but they aren't bubbled up to the form itself. As per the tutorial, the Save button should be disabled when any form control is invalidated and enabled when all controls are valid.

What is the use of formcontrol in angular?

An Angular form is a regular HTML form with few additional features. For each field (input, radio, select, etc.) in the form, we need an object of the FormControl class. The FormControl object gives information about that field. Its value, if the value is valid, and if it is not valid what are the validation errors, etc.

What is @angangular form validation?

Angular form validation provides us an easy to use form validation checks. However, it starts getting complex when the number of field grows and the validation rules become conditional.


1 Answers

Your test ist failing because zip has no value, but a value is required. Either remove the validation for the zip property or provide a value within your tests.

Removing the validation for zip within AppComponent would look like this

contactForm = new FormGroup({
        name: new FormControl(null, [ Validators.required,
                                    Validators.minLength(4),
                                    Validators.maxLength(10)]),
        phone: new FormControl(null,  [ Validators.required,
                                        Validators.pattern("^[0-9]*$"),        Validators.minLength(10),
                                        Validators.maxLength(10) ]),
        address: new FormGroup({
        street: new FormControl(null),
        city: new FormControl(null),
        zip: new FormControl(null)
        })
    });

Passing in a value for zip within your tests would look like this

    it('form should be valid on entering required fields', () => {
        fixture.detectChanges(); // ngOninit()
        component.name.setValue('david');
        component.phone.setValue('9999999999');
        component.zip.setValue('123456');

        expect(component.contactForm.valid).toBeTruthy();
      });

The second option only works if you have setters for these properties in your component. In your stackblitz there are only getters.

like image 128
Riscie Avatar answered Oct 17 '22 05:10

Riscie