Linked Questions

Popular Questions

Unit test on files with Angular

Asked by At

So I'm kind of new with angular and web dev. in general so I don't really know what is accessible to me when it comes to unit test.

I've made a form that require a name and two files. If one of them is missing or doesn't respect the conditions associated with their respective fields, the user can't submit the form. In order to do this, I've created a form group called myForm that contains Validators for every form control(name, originalImage, modifiedImage)

I think that I figured how to test the name validity but I didn't find a way to test the validity of the files. Someone told me that I can mock files, but it is a bit complicated for my case. Here's a list of what I would like to test:

  1. The field of an image is not empty (required)
  2. The images have the good type of file (pattern)
  3. The images aren't empty
  4. My FileReader (in my onFileChange function) is working properly
  5. Somehow test my event (with a spy?)

Here's my code:

game-form.component.ts

 import { Component, OnInit} from '@angular/core';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';

                @Component({
                selector: 'app-game-form',
                templateUrl: './game-form.component.html',
                styleUrls: ['./game-form.component.scss']
                })
                export class GameFormComponent implements OnInit {

                myForm: FormGroup;

                constructor(private fb: FormBuilder) {}

                ngOnInit() {

                this.myForm = this.fb.group({
                    name: ['', [
                        Validators.required,
                        Validators.minLength(4),
                        Validators.maxLength(10),
                        Validators.pattern("[^ @]*")
                    ]],
                    originalImage: ['', [
                        Validators.required,
                        Validators.pattern("[^@]*(.bmp|.BMP)")
                    ]],
                    modifiedImage: ['', [
                        Validators.required,
                        Validators.pattern("[^@]*(.bmp|.BMP)")
                    ]],
                    });
                }

                public get name() {
                    return this.myForm.get('name');
                }
                public get originalImage() {
                    return this.myForm.get('originalImage');
                }
                public get modifiedImage() {
                    return this.myForm.get('modifiedImage');
                }

                public onFileChange(event:Event) {
                    const reader = new FileReader();
                    const files:FileList | null =  (event.target as HTMLInputElement).files;
                    const file:File | null = (files != null)?files[0]:null;
                    const idControl:string = (event.target != null)?(event.target as HTMLInputElement)["id"]:"";
                    reader.onload = () => 
                    {

                    switch(idControl)
                    {
                        case "originalImage":
                            this.myForm.patchValue({
                            originalImage : reader.result
                            });
                        break;
                        case "modifiedImage":
                            this.myForm.patchValue({
                            modifiedImage : reader.result
                            });
                        break;
                        }


                    };

                    if(file != null)
                    {
                    reader.readAsDataURL(file);

                    }

                }
                }

game-form.component.spec.ts

    import { async, ComponentFixture, TestBed} from "@angular/core/testing";
import { GameFormComponent } from "./game-form.component";
import { MatIconModule } from "@angular/material/icon";

import { ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule, MatInputModule } from '@angular/material';
import { AppComponent } from '../app.component';
import { RouterModule } from '@angular/router';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';


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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ 
        GameFormComponent, 
        AppComponent 
      ],
      imports:[
        RouterModule,
        ReactiveFormsModule,
        MatFormFieldModule,
        MatInputModule,
        BrowserAnimationsModule,
        MatIconModule 
      ]
    })
    .compileComponents();
  }));

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

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

  it('should not allow a form when empty', () => {
    expect(component.myForm.valid).toBeFalsy();
  });

  it('should accept the name in the form', () => {
    let name = component.myForm.controls['name'];

    // 4 <= number of characters <= 10 (correct)
    name.setValue("test");
    let errors = name.errors || {};
    expect(errors['required']).toBeFalsy();
    expect(errors['minlength']).toBeFalsy();
    expect(errors['maxlength']).toBeFalsy();
    expect(errors['pattern']).toBeFalsy();
  });

  it('should not accept the name in the form since too many characters are entered', () => {

    // 10 < number of characters (incorrect)
    let name = component.myForm.controls['name'];
    name.setValue("12345678900"); 
    let errors = name.errors || {};
    expect(errors['required']).toBeFalsy();
    expect(errors['minlength']).toBeFalsy();
    expect(errors['maxlength']).toBeTruthy();
    expect(errors['pattern']).toBeFalsy();
  });

  it('should not accept the name in the form since few characters are entered', () => {

    // 4 > number of characters (incorrect)
    let name = component.myForm.controls['name'];
    name.setValue("123"); 
    let errors = name.errors || {};
    expect(errors['required']).toBeFalsy();
    expect(errors['minlength']).toBeTruthy();
    expect(errors['maxlength']).toBeFalsy();
    expect(errors['pattern']).toBeFalsy();
  });

  it('should not accept the name in the form since spaces are entered', () => {
    // spaces (incorrect)
    let name = component.myForm.controls['name'];
    name.setValue("    1"); 
    let errors = name.errors || {};
    expect(errors['required']).toBeFalsy();
    expect(errors['minlength']).toBeFalsy();
    expect(errors['maxlength']).toBeFalsy();
    expect(errors['pattern']).toBeTruthy();
  });

  it('should not accept the name in the form since nothing is entered', () => {

    // nothing (incorrect)
    let name = component.myForm.controls['name'];
    name.setValue(""); 
    let errors = name.errors || {};
    expect(errors['required']).toBeTruthy(); 

  });


  it('should not accept the original image since she has not been choosen', () => {    
    // the user hasn't choose an original image yet
    let image = component.myForm.controls['originalImage'];
    let errors = image.errors || {};
    expect(errors['required']).toBeTruthy();
  });

  it('should not accept the modified image since she has not been choosen', () => {  

     // the user hasn't choose an modified image yet
    let image = component.myForm.controls['modifiedImage'];
    let errors = image.errors || {};
    expect(errors['required']).toBeTruthy();
  });

});

I don't know if it would help you (to help me), but I'm also postin my html file:

game-form.component.html

<form [formGroup]="myForm" (ngSubmit) ="onSubmit()">

    <mat-form-field class="example-full-width">
        <input matInput placeholder="Game name" [value]="" formControlName="name">

        <mat-error *ngIf="name.errors?.minlength && name.touched">
            The name entered should at least contain 4 letters
        </mat-error>

        <mat-error *ngIf="name.errors?.maxlength && name.touched">
            The name entered should at most contain 10 letters
        </mat-error>


        <mat-error *ngIf="name.errors?.pattern && name.touched">
            The name entered must contain spaces
        </mat-error>

    </mat-form-field>

    <br><br/>
    <br><br/>

    <mat-form-field class="example-full-width">

        <input matInput placeholder="Original image" disabled value="No file selected" name="fist_entry" id="first_entry">
        <div class="example-button-row">
            <button (click)="originalImageInput.click()" mat-icon-button >
                <mat-icon matSuffix>folder</mat-icon>
            </button>
        </div>

        <input hidden (change)="onFileChange($event)" #originalImageInput type="file" accept = ".bmp" id="originalImage"
            formControlName = "originalImage"
            onchange="document.getElementById('first_entry').value = this.value.split('\\').pop().split('/').pop()">
            <div class="error-message" *ngIf="originalImage.errors?.pattern">
                    The type of the file entered isn't ".bmp"
            </div>  

    </mat-form-field>

    <img [src]="this.myForm.get('originalImage').value" *ngIf="originalImage" class="image" id="originalImage" style="width:150px"> 

    <br><br/>
    <br><br/>
    <br><br/>
    <br><br/>

    <mat-form-field class="example-full-width">
        <input matInput placeholder="Modified Image" disabled value="No file selected" name="second_entry" id="second_entry"> 
        <div class="example-button-row">
            <button (click)="modifiedImageInput.click()" mat-icon-button >
                <mat-icon >folder</mat-icon>
            </button>
        </div>
        <input hidden (change)="onFileChange($event)" #modifiedImageInput type="file" accept= ".bmp" id="modifiedImage"
            formControlName = "modifiedImage"
            onchange="document.getElementById('second_entry').value = this.value.split('\\').pop().split('/').pop()">
            <div class="error-message" *ngIf="modifiedImage.errors?.pattern">
                    The type of the file entered isn't ".bmp"
            </div>  
    </mat-form-field>

    <img [src]="this.myForm.get('modifiedImage').value" *ngIf="modifiedImage" class="image" id="modifiedImage" style="width:150px"> 

    <br><br/>

      <div id="submitPosition">
        <button mat-button color="primary" type="submit" id="submit" [disabled]="myForm.invalid">Submit</button>
    </div>

</form> 

Thank you in advance for you help (or at least, your time), hoping that I find a solution and learn more about tests on Angular.

Related Questions