Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular FormControl Jest test causing TypeError: Converting circular structure to JSON

Tags:

angular

jestjs

I get the following error when I run tests using Jest in an Angular project.

UnhandledPromiseRejectionWarning: TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'       
    |     property 'element' -> object with constructor 'Object'
    |     property 'publicProviders' -> object with constructor 'Object'
    |     property 'ɵNgNoValidate_65' -> object with constructor 'Object'
    --- property 'parent' closes the circle

I have taken things apart to find what is causing the error and it is pretty clear that it is caused by the forms in the components. If I remove the actual FormControls (form fields) from the FormGroup, then the tests run without problems. The same happens with several other forms, I have tried.

I understand what the error means, but not what is causing it in the FormControl. What could cause this error?

@Component({
  selector: 'app-edit-title-form',
  template: `
<form (ngSubmit)="onSubmit()" [formGroup]="form" novalidate>
    <input type="text" formControlName="title"> <!-- If this is removed then tests run -->
</form>
`
})
export class EditTitleFormComponent implements OnInit {
  @Input() title: string = '';
  @Output() onSave: EventEmitter<string> = new EventEmitter();

  public form!: FormGroup;

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit(): void {
    this.initForm();
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.title.currentValue) {
      this.form.controls.title.setValue(changes.title.currentValue);
      this.form.markAsPristine();
    }
  }

  get field() {
    return this.form.controls;
  }

  public onSubmit(): void {
    this.onSave.emit(title);
  }

  private initForm(): void {
    this.form = this.formBuilder.group({
      title: [this.title, []], // If this line is removed along with the html input field, then tests run 
    });
  }
}
describe('EditTitleFormComponent', () => {
  let component: EditTitleFormComponent;
  let fixture: ComponentFixture<EditTitleFormComponent>;

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

  beforeEach(() => {
    fixture = TestBed.createComponent(EditTitleFormComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
like image 650
nicolaib Avatar asked Aug 12 '20 12:08

nicolaib


3 Answers

I faced this problem when I ran jest against all my test suites, so it will pick up every spec.ts from the project.

Try to run jest against the single specific file which hangs onto this "Unhandled promise rejection", it will output the exact problem with that file. It could be basically anything (for me it was missing import in the spec.ts, sometimes a missing property value assignment, whatever).

like image 137
deejayy Avatar answered Nov 17 '22 00:11

deejayy


I ran into something similar that also looked to be related to FormControls. I resolved this by adding NoopAnimationsModule to my TestBed.configureTestingModule(). This may not be the exact module you need to import, but it got me past this error. You'll definitely want to make sure that all of the constructor injectables are being accounted for in your test.

In addition to making sure all injectables are provided, you'll also want to make sure any service mocks are in place. For instance, I ran into this error again when I had neglected to add the equivalent to the "fetchthings: jest.fn()" mock below:

{ provide: MyService, useValue: { fetchThings: jest.fn() } }
like image 3
skeize Avatar answered Nov 17 '22 00:11

skeize


Try using @Optional on your component constructor parameter.

constructor(@Optional private formBuilder: FormBuilder) {}

My similar error was firstly caused in our custom library tests by an injected ActivatedRoute. That was solved by importing RouterTestingModule in the TestBed.

 beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        imports: [RouterTestingModule, ReactiveFormsModule],
        declarations: [RadioComponent, TestComponent, FieldsetComponent, ErrorMessageComponent],
      }).compileComponents();
    })
  );

Then, the library tests run fine but the app tests showed the same error which was solved by putting @Optional in the custom library component constructor parameter.

Update

This error seems to occur on various occasions. My most recent was interpolating a property of undefined {{data.something}}. Fixed with {{data?.something}}. This is just one example.

The point is that this error hides the true cause. I will update when I find a way out of this.

like image 3
ginalx Avatar answered Nov 16 '22 23:11

ginalx