#Angular testing failed

120 messages · Page 1 of 1 (latest)

tender current
#

Hi i made a input component, but i keep getting errors when i run my test coverage:
Chrome Headless 109.0.5414.75 (Windows 10) InputFieldComponent should create FAILED
TypeError: Cannot read properties of undefined (reading 'setValidators')

I'm having more test errors, everything works just want the tests to succeed
component.html

<div class="form-row">
  <div class="form-label">
    <label for="{{id}}">{{label}}</label>
  </div>
  <div [class.field-error]="control.touched && control.invalid" class="form-field">
    <input [formControl]="control" id="{{id}}" maxlength="{{maxLength}}" name="{{id}}" type="text" >
    <div *ngIf="control.touched && control.errors?.['required']" class="inline-notification error">
      <span class="action-warning">{{label}} is verplicht.</span>
    </div>
    <div *ngIf="control.touched && control.errors?.['pattern']" class="inline-notification error">
      <span class="action-warning">{{label}} moet uit minstens 1 karakter bestaan.</span>
    </div>
  </div>
</div>

component.ts

import {Component, Input, OnInit} from '@angular/core';
import {FormControl, Validators} from '@angular/forms';

@Component({
  selector: 'lib-input-field',
  templateUrl: './input-field.component.html',
})
export class InputFieldComponent implements OnInit{

  @Input() control: FormControl;
  @Input() label: string;
  @Input() id: string;
  @Input() maxLength: number;
  @Input() required: boolean;


  ngOnInit() {
    if(this.required) this.control.setValidators(Validators.required)
    this.control.setValidators([Validators.maxLength(this.maxLength), Validators.minLength(1)])
  }
}
#

component.spec.ts

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { InputFieldComponent } from './input-field.component';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';

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


  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [
        InputFieldComponent,
      ],
      imports: [
        FormsModule,
        ReactiveFormsModule,
      ]
    })
    .compileComponents();
    fixture = TestBed.createComponent(InputFieldComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
patent geyser
#

So the error looks to be expected.

#

U have an input, but you didnt provide one.

#

So its undefined, yet u assume it's always available in the ngOnInit, so it fails.

tender current
#

euhh, so i need to provide that there is a <input>?

patent geyser
#

No, an @Input() for control

tender current
#

ohhh

patent geyser
#

Could try

component = fixture.componentInstance;
component.control = {setValidators: jasmine.createSpy() }
fixture.detectChanges();
tender current
#

ahhhh

patent geyser
#

ehhhh

tender current
#

so i make a spy object of the validators?

#

wel

#

no

#

not an object but mock them ?

patent geyser
#

I mean, u can do whatever u like as long as u ensure it doesnt crash.

tender current
#

its complaining and i don't understand, im sorry haha FE tests not my thing

tender current
#

doesnt seem right?

patent geyser
#

No it does not

#

component.control = {setValidators: jasmine.createSpy() } as any could work 😄

#

or component.control = new FormControl() if u want.

tender current
#

hmmm then it complains about cannot set properties of undefined setting 'control'?

#

I doesnt make sense to me tbh

half depot
tender current
#

That sounds interessting, but at the moment a bit out of scope I think, I saved the link, i'll read up on it

half depot
#

I should also warn you that you should most probably not write such a reusable component in the first place. It won't scale, and won't actually be reusable, because you will want to handle other kinds of errors, and other attributes on your input, and other layouts...
Look at angular material: even though they want to impose a very strict look and feel, they don't have such an input component. Instead, they allow wrapping an input. If the main goal is to handle errors in a standard way, look at ngx-valdemort.

patent geyser
tender current
#

Well I would need to read up on it since all this angulmar FE testing is new, gonna take longer then just fix this test i think

patent geyser
#

Anyway, its only a couple paragraphs to read, and only a couple of lines to add to your test file.

tender current
#

I'm already out of time so i guess why niot

patent geyser
#

What I said should work, but what @half depot said is better.

tender current
#

and i don't understand it

patent geyser
tender current
#

Lead dev said for the small components like input field / dropdown / radio button i could drop the tests

#

but still need to fix 2 others of the main components

#

sooo i'm still f'd

patent geyser
#

Goodluck.

tender current
#

Thanks and i do appriciatee the help

#

i'll read on the test host thing

#

it did sound interessting just a bit overstressed atm

patent geyser
#

Well, it would have solved the problem by now.

#

But anyway, if your lead wants to drop tests instead ... xD

tender current
#

yeah but changed f' all cause i still have test errors and i'm lowkey getting done with it

#

Lets say i'm not having fun writing FE testing, never have

patent geyser
#

Well, when people try to help u, it can help to not backout half way through it 😄

#

Anyway, take your time to read things, it's not easy so you make it harder on you.

tender current
#

Your right i'm sorry for that, im stressed and i havent seen it in any other of the tests on the project so i thought myabe not, but i think it might just be a good idea to come with other/better solutions

#

but most of the lines are still so confusing

patent geyser
#

Well, its a link shared to the angular docs

#

maybe you do want to give it a try 😄

tender current
#

i am

patent geyser
#

Happy to help.

#

So the idea is to create a component, just like any other, but thats only used in your tests.

#

The component doesnt do anything but render the component u want to test

#

and feed it the inputs.

tender current
#
<div class="form-row row-header steps-mobile" aria-hidden="true">
  <p>Stap * van *</p>
</div>

<div class="form-row row-header">
  <h2><span class="u-sr-only">Stap * van *</span> Lorem ipsum dolor sit</h2>
</div>

<div class="form-row">
  <div class="form-label">
    <label>Gesprek met</label>
  </div>
  <div class="form-field">
    <div class="multi-fields">
      <lib-radio-button [id]="'Bemiddelaar'" [name]="'bemiddelaar'" [options]="[{label: 'Bemiddelaar', value: 'Bemiddelaar'}]" ></lib-radio-button>
      <lib-radio-button [id]="'Bemiddelaar'" [name]="'andere'" [options]="[{label: 'Andere', value: 'Andere'}]" ></lib-radio-button>
    </div>
  </div>
</div>

<lib-input-field [control]="naam" [maxLength]="400" [label]="'Naam'" ></lib-input-field>

component .Ts

import {Component, Input, OnInit} from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';

@Component({
  selector: 'app-bij-wie',
  templateUrl: './bij-wie.component.html',
})
export class BijWieComponent implements OnInit{

  @Input() form: FormGroup;
  naam: FormControl;

  constructor() {
    // TODO document why this constructor is empty
  }

  ngOnInit(): void {
    this.naam = this.form.controls['naam'] as FormControl;
  }
}
#

this at the moment

patent geyser
#

bij wie 😄

#
@Component({
  template: '<app-bij-wie [naam]="control"></app-bij-wie>'
})
class TestHostComponent {
  control = new FormControl()
}
#

then use TestHostCOmponent in your test when u call CreateComponent

tender current
#

yeah its dutch 😉

patent geyser
tender current
#

thought so by your name haha

tender current
#

right?

patent geyser
#
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { InputFieldComponent } from './input-field.component'; // <== Replace import for BijWieComponent
import {FormsModule, ReactiveFormsModule} from '@angular/forms';

@Component({
  template: '<app-bij-wie [naam]="control"></app-bij-wie>'
})
class TestHostComponent {
  control = new FormControl()
}

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


  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [
        BijWieComponent,
        TestHostComponent
      ],
      imports: [
        FormsModule,
        ReactiveFormsModule,
      ]
    })
    .compileComponents();
    fixture = TestBed.createComponent(TestHostComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

That test should work

#

Or well, gotta fix the imports etc

#

On top.

#

(Sorry I just changed the code again, as I made a mistake)

tender current
#

still also getting issues but i'm confused

patent geyser
#

Ooooh

#

my bad

tender current
#

why is the component on top getting an [naam] input, think that should be form ?

patent geyser
#

let fixture: ComponentFixture<BijWieComponent>; => let fixture: ComponentFixture<TestHostComponent>;

patent geyser
#

Also let component: BijWieComponent; => let component: TestHostComponent;

#

control = new FormControl() => group = new FormGroup({naam: ''})

#

'<app-bij-wie [naam]="control"></app-bij-wie>' => '<app-bij-wie [form]="group"></app-bij-wie>'

tender current
#

yes oke oke

#

Oke so far i think i'm starting to get it but

#

and this is something i've come across before

#

but there i could as FormControl and fixed

patent geyser
#

Yeah, its been a while since I used angular so I might just have an old / incorrect syntax there.

tender current
#

this seems to solve it

patent geyser
#

Yeah looks good.

tender current
#

I don't get this either

#

I'm sorry man

#

i really appriciate the help

patent geyser
#

Yeah u need to include the components u need.

#

BijWie uses a component

#

u need to declare it in your test

tender current
#

and i need to declare it?

patent geyser
#

Yes

tender current
#

why could this happen?

#

I use the radio button in bijwie

#

bijwie is inside the wizard

patent geyser
#

What do u mean ?

#

its the same error as above

tender current
#
<div class="c-wizard">

  <div class="o-wrapper">
    <div class="c-wizard__header">
      <div class="c-wizard__header-info">
        <h1 class="c-wizard__header-info-title">Een gesprek inplannen</h1>
      </div>
      <div class="c-wizard__header-close">
        <a (click)="back()" class="action-close-after"><span>Sluit</span></a>
      </div>
    </div>
  </div>

  <lib-stepper-component (onSubmit)="submitForm()">
    <cdk-step label="Algemeen" [stepControl]="algemeenForm">
      <app-algemeen [form]="algemeenForm"></app-algemeen>
    </cdk-step>
    <cdk-step label="Bij wie" [stepControl]="bijWieForm">
      <app-bij-wie [formGroup]="bijWieForm" [form]="bijWieForm"></app-bij-wie>
    </cdk-step>
    <cdk-step label="Wanneer" [stepControl]="wanneerForm">
      <app-wanneer [form]="wanneerForm"></app-wanneer>
    </cdk-step>
    <cdk-step label="Overzicht">
      <app-overzicht></app-overzicht>
    </cdk-step>
  </lib-stepper-component>
</div>

Component.ts makes the forms

#

yeah

#

but it not being used in that

#

wizard

patent geyser
#

What does that wizard have to do with anything here

tender current
#

so the wizard should also have declaration of the components used within it?

patent geyser
#

Oh

#

different test

#

Got it

#

Well, if u include the BijWie, u need the checkbox

#

U can also tell angular to not complain on missing components

#

but that might do more harm than good.

tender current
#

i've added both components in wizard aswell now and it seems to be almost all green lights now

#

I didn't know that nested components would need to delcare the components used by the nested ones

#

if that makes sense