mseemann/angular2-mdl

View on GitHub
projects/core/src/lib/textfield/mdl-textfield.component.spec.ts

Summary

Maintainability
C
1 day
Test Coverage
import { TestBed, waitForAsync } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { Component } from "@angular/core";
import {
  DISABLE_NATIVE_VALIDITY_CHECKING,
  MdlTextFieldComponent,
} from "./mdl-textfield.component";
import { MdlButtonComponent } from "../button/mdl-button.component";
import { FormsModule } from "@angular/forms";
import { MdlTextFieldModule } from "./mdl-textfield.module";
import { MdlButtonModule } from "../button/mdl-button.module";

@Component({
  // eslint-disable-next-line
  selector: "test",
  template: "replaced by the test",
})
class MdlTestComponent {
  public text1 = "";

  public numberValue = 0;

  // eslint-disable-next-line @typescript-eslint/no-empty-function,@typescript-eslint/no-unused-vars
  public onBlur(event: FocusEvent) {}

  // eslint-disable-next-line @typescript-eslint/no-empty-function,@typescript-eslint/no-unused-vars
  public onFocus(event: FocusEvent) {}

  // eslint-disable-next-line @typescript-eslint/no-empty-function,@typescript-eslint/no-unused-vars
  public onKeyup(event: KeyboardEvent) {}
}

describe("Component: MdlTextField", () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [MdlTextFieldModule.forRoot(), MdlButtonModule, FormsModule],
      declarations: [MdlTestComponent],
    });
  });

  it("should add the css class mdl-textfield to the host element", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template:
          '<mdl-textfield type="text" label="Text..." ></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const tfEl: HTMLElement = fixture.nativeElement.children.item(0);
    expect(tfEl.classList.contains("mdl-textfield")).toBe(true);
  });

  it(
    "should support ngModel",
    waitForAsync(() => {
      TestBed.overrideComponent(MdlTestComponent, {
        set: {
          template:
            '<mdl-textfield type="text" label="Text..." [(ngModel)]="text1"></mdl-textfield>',
        },
      });
      const fixture = TestBed.createComponent(MdlTestComponent);
      fixture.detectChanges();
      fixture.whenStable().then(() => {
        const instance = fixture.componentInstance;
        const component = fixture.debugElement.query(
          By.directive(MdlTextFieldComponent)
        ).componentInstance;

        instance.text1 = "text1";
        fixture.detectChanges();
        fixture.whenStable().then(() => {
          expect(component.value).toEqual("text1");

          component.value = "text2";
          fixture.detectChanges();
          expect(instance.text1).toEqual("text2");
        });
      });
    })
  );

  it("should mark the component as focused and blured", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template:
          '<mdl-textfield type="text" label="Text..." [(ngModel)]="text1"></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const hostEl: HTMLElement = fixture.debugElement.query(
      By.directive(MdlTextFieldComponent)
    ).nativeElement;
    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;

    const evt = document.createEvent("HTMLEvents");
    evt.initEvent("focus", true, true);
    inputEl.dispatchEvent(evt);

    fixture.detectChanges();

    expect(hostEl.classList.contains("is-focused")).toBe(true);

    const evtBlur = document.createEvent("HTMLEvents");
    evtBlur.initEvent("blur", true, true);
    inputEl.dispatchEvent(evtBlur);

    fixture.detectChanges();
    expect(hostEl.classList.contains("is-focused")).toBe(false);
  });

  it(
    "should mark the component as invalid ngModel (pattern)",
    waitForAsync(() => {
      TestBed.overrideComponent(MdlTestComponent, {
        set: {
          template:
            '<mdl-textfield type="text" label="Text..." [(ngModel)]="text1" pattern="a"></mdl-textfield>',
        },
      });
      const fixture = TestBed.createComponent(MdlTestComponent);
      fixture.detectChanges();

      const hostEl: HTMLElement = fixture.debugElement.query(
        By.directive(MdlTextFieldComponent)
      ).nativeElement;
      const el: HTMLInputElement = fixture.debugElement.query(
        By.css("input")
      ).nativeElement;

      el.value = "b";
      fixture.detectChanges();

      fixture.whenStable().then(() => {
        expect(hostEl.classList.contains("is-invalid")).toBe(true);
      });
    })
  );

  it(
    "should mark the component as invalid ngModel (min)",
    waitForAsync(() => {
      TestBed.overrideComponent(MdlTestComponent, {
        set: {
          template:
            '<mdl-textfield type="number" label="Text..." [(ngModel)]="text1" min="2"></mdl-textfield>',
        },
      });
      const fixture = TestBed.createComponent(MdlTestComponent);
      fixture.detectChanges();

      const hostEl: HTMLElement = fixture.debugElement.query(
        By.directive(MdlTextFieldComponent)
      ).nativeElement;
      const el: HTMLInputElement = fixture.debugElement.query(
        By.css("input")
      ).nativeElement;

      el.value = "1";
      fixture.detectChanges();

      fixture.whenStable().then(() => {
        expect(hostEl.classList.contains("is-invalid")).toBe(true);
      });
    })
  );

  it(
    "should mark the component as invalid ngModel (max)",
    waitForAsync(() => {
      TestBed.overrideComponent(MdlTestComponent, {
        set: {
          template:
            '<mdl-textfield type="number" label="Text..." [(ngModel)]="text1" max="1"></mdl-textfield>',
        },
      });
      const fixture = TestBed.createComponent(MdlTestComponent);
      fixture.detectChanges();

      const hostEl: HTMLElement = fixture.debugElement.query(
        By.directive(MdlTextFieldComponent)
      ).nativeElement;
      const el: HTMLInputElement = fixture.debugElement.query(
        By.css("input")
      ).nativeElement;

      el.value = "2";
      fixture.detectChanges();

      fixture.whenStable().then(() => {
        expect(hostEl.classList.contains("is-invalid")).toBe(true);
      });
    })
  );

  it(
    "should mark the component as invalid ngModel (step)",
    waitForAsync(() => {
      TestBed.overrideComponent(MdlTestComponent, {
        set: {
          template:
            '<mdl-textfield type="number" label="Text..." [(ngModel)]="text1" min="0" step="1"></mdl-textfield>',
        },
      });
      const fixture = TestBed.createComponent(MdlTestComponent);
      fixture.detectChanges();

      const hostEl: HTMLElement = fixture.debugElement.query(
        By.directive(MdlTextFieldComponent)
      ).nativeElement;
      const el: HTMLInputElement = fixture.debugElement.query(
        By.css("input")
      ).nativeElement;

      el.value = "0.1";
      fixture.detectChanges();

      fixture.whenStable().then(() => {
        expect(hostEl.classList.contains("is-invalid")).toBe(true);
      });
    })
  );

  it("should create a textarea if row is specified", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template:
          '<mdl-textfield type="text" label="Text..." rows="3"></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const el = fixture.debugElement.query(By.css("textarea"));

    expect(el).toBeDefined();
  });

  it("should restrict the line count if maxrows is present", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template:
          '<mdl-textfield type="text" label="Text..." rows="3" [maxrows]="1"></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const el = fixture.debugElement.query(By.css("textarea")).nativeElement;

    el.value = "a";

    // eslint-disable-next-line
    const e = new Event("keydown") as any;
    e.keyCode = 13;

    spyOn(e, "preventDefault");

    el.dispatchEvent(e);

    expect(e.preventDefault).toHaveBeenCalled();
  });

  it("should not restrict the line count if maxrows is -1", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template:
          ' <mdl-textfield type="text" label="Text..." rows="3" [maxrows]="-1"></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const el = fixture.debugElement.query(By.css("textarea")).nativeElement;

    el.value = "a";
    // eslint-disable-next-line
    const e = new Event("keydown") as any;
    e.keyCode = 13;
    el.dispatchEvent(e);

    spyOn(e, "preventDefault");

    expect(e.preventDefault).not.toHaveBeenCalled();
  });

  it("should create an expandable textfield if icon is present", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: '<mdl-textfield type="text" icon="search"></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const el = fixture.debugElement.query(
      By.directive(MdlTextFieldComponent)
    ).nativeElement;

    expect(el.classList.contains("mdl-textfield--expandable")).toBe(true);
  });

  it("should activate the expandable if the icon button is clicked", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: '<mdl-textfield type="text" icon="search"></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const btnEl = fixture.debugElement.query(
      By.directive(MdlButtonComponent)
    ).nativeElement;
    btnEl.click();
    fixture.detectChanges();

    const el = fixture.debugElement.query(
      By.directive(MdlTextFieldComponent)
    ).nativeElement;
    expect(el.classList.contains("is-focused")).toBe(true);
  });

  it("should add name and id to the input element if provided", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template:
          '<mdl-textfield type="text" label="Text..." id="id-1" name="name-1"></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;

    expect(inputEl.name).toEqual("name-1", "name is not set");
    expect(inputEl.id).toEqual("id-1", "id is not set");
  });

  it("should autogenerate an id that must match the labels for-attribute", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template:
          '<mdl-textfield type="text" label="Text..." name="name-1"></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;

    const id = inputEl.id;

    expect(id).toBeDefined();

    const labelEl: HTMLLabelElement = fixture.debugElement.query(
      By.css("label")
    ).nativeElement;

    expect(labelEl.htmlFor).toBeDefined(id);
  });

  it("should pass autocomplete through to input", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template:
          '<mdl-textfield type="text" label="Name" autocomplete="name"></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;

    expect(inputEl.getAttribute("autocomplete")).toBe(
      "name",
      "The autocomplete attribute should pass to the input."
    );
  });

  it("should have native validity check", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: `
          <mdl-textfield
            type="text"
            pattern="^[a-z]+[a-z0-9._]+@[a-z]+\.[a-z.]{2,5}$"
            label="Text..." name="name-1">
        </mdl-textfield>'
      `,
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;
    inputEl.value = "this is not a valid email";

    fixture.detectChanges();

    const el = fixture.debugElement.query(
      By.directive(MdlTextFieldComponent)
    ).nativeElement;
    expect(el.classList.contains("is-invalid")).toBe(
      true,
      "textfield should have css is-invalid"
    );
  });

  it("should be possible to deactive native checking locally", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: `
          <mdl-textfield
            disableNativeValidityChecking
            type="text"
            pattern="^[a-z]+[a-z0-9._]+@[a-z]+\.[a-z.]{2,5}$"
            label="Text..." name="name-1">
        </mdl-textfield>'
      `,
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;
    inputEl.value = "this is not a valid email";

    fixture.detectChanges();

    const el = fixture.debugElement.query(
      By.directive(MdlTextFieldComponent)
    ).nativeElement;
    expect(el.classList.contains("is-invalid")).toBe(
      false,
      "textfield should not have css is-invalid"
    );
  });

  describe("globally deactivated native check", () => {
    beforeEach(() => {
      TestBed.configureTestingModule({
        imports: [MdlTextFieldModule, MdlButtonModule, FormsModule],
        declarations: [MdlTestComponent],
        providers: [
          { provide: DISABLE_NATIVE_VALIDITY_CHECKING, useValue: true },
        ],
      });
    });

    it("should be possible to deactive native checking globally", () => {
      TestBed.overrideComponent(MdlTestComponent, {
        set: {
          template: `
          <mdl-textfield
            type="text"
            pattern="^[a-z]+[a-z0-9._]+@[a-z]+\.[a-z.]{2,5}$"
            label="Text..." name="name-1">
        </mdl-textfield>'
      `,
        },
      });
      const fixture = TestBed.createComponent(MdlTestComponent);
      fixture.detectChanges();

      const inputEl: HTMLInputElement = fixture.debugElement.query(
        By.css("input")
      ).nativeElement;
      inputEl.value = "this is not a valid email";

      fixture.detectChanges();

      const el = fixture.debugElement.query(
        By.directive(MdlTextFieldComponent)
      ).nativeElement;
      expect(el.classList.contains("is-invalid")).toBe(
        false,
        "textfield should not have css is-invalid"
      );
    });
  });

  it("shoud support the autofocus attribute", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: `
          <mdl-textfield  type="text" autofocus></mdl-textfield>'
      `,
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;

    expect(inputEl.getAttribute("autofocus")).toBe(
      "",
      "the autofocus attribute should be set"
    );
  });

  it("should emit the blur and focus event", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: `
          <mdl-textfield  type="text" (focus)="onFocus($event)" (blur)="onBlur($event)"></mdl-textfield>'
      `,
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const component = fixture.componentInstance;

    spyOn(component, "onFocus");
    spyOn(component, "onBlur");

    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;
    inputEl.dispatchEvent(new Event("focus"));

    expect(component.onFocus).toHaveBeenCalled();

    inputEl.dispatchEvent(new Event("blur"));

    expect(component.onBlur).toHaveBeenCalled();
  });

  it("should be possible to set the focus programmatically", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: `
          <mdl-textfield  type="text"></mdl-textfield>'
      `,
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const textFieldDebugElement = fixture.debugElement.query(
      By.directive(MdlTextFieldComponent)
    );
    const textFieldComonent = textFieldDebugElement.componentInstance;
    const el = textFieldDebugElement.nativeElement;

    textFieldComonent.setFocus();

    fixture.detectChanges();

    expect(el.classList.contains("is-focused")).toBe(true);
  });

  it(
    "should be possible to disable the textinputfield",
    waitForAsync(() => {
      TestBed.overrideComponent(MdlTestComponent, {
        set: {
          template: `
          <mdl-textfield  type="text"></mdl-textfield>'
      `,
        },
      });
      const fixture = TestBed.createComponent(MdlTestComponent);

      const cbDebugElem = fixture.debugElement.query(
        By.directive(MdlTextFieldComponent)
      );

      cbDebugElem.componentInstance.setDisabledState(true);
      fixture.detectChanges();

      const textInputElement: HTMLElement = cbDebugElem.nativeElement;
      expect(textInputElement.classList.contains("is-disabled")).toBe(
        true,
        "should have css is-disabled"
      );

      fixture.whenStable().then(() => {
        const inputElement: HTMLInputElement = fixture.debugElement.query(
          By.css("input")
        ).nativeElement;
        expect(inputElement.getAttribute("disabled")).toBe(
          "",
          "the underlaying input element should be disbaled"
        );
      });
    })
  );

  it(
    "should keep type number if the input field is type number",
    waitForAsync(() => {
      TestBed.overrideComponent(MdlTestComponent, {
        set: {
          template: `
          <mdl-textfield  type="number" [(ngModel)]="numberValue"></mdl-textfield>'
      `,
        },
      });
      const fixture = TestBed.createComponent(MdlTestComponent);
      fixture.detectChanges();

      expect(typeof fixture.componentInstance.numberValue).toBe("number");

      const tfFieldComp = fixture.debugElement.query(
        By.directive(MdlTextFieldComponent)
      );
      const el: HTMLInputElement = fixture.debugElement.query(
        By.css("input")
      ).nativeElement;

      el.value = "1";
      // eslint-disable-next-line
      tfFieldComp.componentInstance.triggerChange({ target: el } as any);
      fixture.detectChanges();

      expect(tfFieldComp.componentInstance.value).toBe(1);
      expect(typeof tfFieldComp.componentInstance.value).toBe("number");

      el.value = "";
      // eslint-disable-next-line
      tfFieldComp.componentInstance.triggerChange({ target: el } as any);
      fixture.detectChanges();

      expect(tfFieldComp.componentInstance.value).toBe(null);
      expect(typeof tfFieldComp.componentInstance.value).toBe("object");
    })
  );

  it("should add the type text to the input field if no type is specified", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: "<mdl-textfield ></mdl-textfield>",
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const el: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;
    expect(el.type).toBe("text");
  });

  it("should set given tabindex value", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: '<mdl-textfield tabindex="-1"></mdl-textfield>',
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();
    const el: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;
    expect(el.getAttribute("tabindex")).toBe("-1");
  });

  it("should not set a default tabindex", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: "<mdl-textfield></mdl-textfield>",
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();
    const el: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;

    expect(el.getAttribute("tabindex")).toEqual(null);
  });

  it("shoud support the readonly attribute", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: `
          <mdl-textfield  type="text" readonly></mdl-textfield>'
      `,
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;

    expect(inputEl.readOnly).toBe(true, "the readonly attribute should be set");
  });

  it("shoud support the required attribute", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: `
          <mdl-textfield  type="text" [required]="true"></mdl-textfield>'
      `,
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;

    expect(inputEl.required).toBe(true, "the required attribute should be set");
  });

  it("shoud support the floating-label attribute", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: `
          <mdl-textfield  type="text" floating-label></mdl-textfield>'
      `,
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const tfEl = fixture.debugElement.query(
      By.css(".mdl-textfield--floating-label")
    );

    expect(tfEl).toBeDefined();
  });

  it("shoud support the maxlength attribute", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: `
          <mdl-textfield  type="text" [maxlength]="10"></mdl-textfield>'
      `,
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const inputEl: HTMLInputElement = fixture.debugElement.query(
      By.css("input")
    ).nativeElement;

    expect(inputEl.getAttribute("maxlength")).toBe(
      "10",
      "the maxlength attribute should be set"
    );
  });

  it("should emit the keyup event", () => {
    TestBed.overrideComponent(MdlTestComponent, {
      set: {
        template: `
          <mdl-textfield type="text" (keyup)="onKeyup()"></mdl-textfield>'
      `,
      },
    });
    const fixture = TestBed.createComponent(MdlTestComponent);
    fixture.detectChanges();

    const testComponent = fixture.componentInstance;
    spyOn(testComponent, "onKeyup");

    const debugElement = fixture.debugElement.query(By.css("input"));
    debugElement.triggerEventHandler("keyup", {});

    expect(testComponent.onKeyup).toHaveBeenCalled();
  });
});