gparlakov / scuri

Automate Angular unit test and boilerplate with this schematic.
MIT License
67 stars 7 forks source link

Methods with parameters have no parameters on test #53

Closed gandadil closed 4 years ago

gandadil commented 4 years ago

Some methods have parameters but on spec created by SCuri no parameters is passed. Here is the original TS:

import { Component, OnInit, ViewEncapsulation, AfterViewInit, ViewChild, DoCheck, HostListener } from '@angular/core';
import { DocumentoTypes } from '../shared/model/documento.types';
import { LoginPainelConnectClient } from '../shared/model/login-painel-connect-client.model';
import { FormSimples } from '../../components/arquitetura/arquitetura-form/arquitetura-form.model';
import { AlertService } from '../../components/alert/_services';
import { Router, ActivatedRoute } from '@angular/router';
import { LoginPainelConnectClientService } from '../shared/services/login-painel-connect-client.service';
import { StringUtils } from '../../components/util/date/string-utils';
import { Cookie } from 'ng2-cookies';
import { EmpresasApiService } from '../../components/api-empresas/_service/api-empresas.service';
import { HttpParams } from '@angular/common/http';
import { BsModalService } from 'ngx-bootstrap';
import { ModalLoginEmpresaComponent } from './modal/modal-login-empresa/modal-login-empresa.component';
import { environment } from '../../../environments/environment';
import { AlertComponent } from '../../components/alert/_directives';

@Component({
  selector: 'app-login-painel-connect-client',
  templateUrl: './login-painel-connect-client.component.html',
  encapsulation: ViewEncapsulation.None,
  styleUrls: ['./login-painel-connect-client.component.scss', '../endless.css', '../endless-skin.css']
})
export class LoginPainelConnectClientComponent implements OnInit, AfterViewInit, FormSimples {

  login: LoginPainelConnectClient = new LoginPainelConnectClient();
  alertConfigs = this.alertService.configListStyle();
  documentoLogin = '';
  maxlength = 18;
  documentoDefault: DocumentoTypes = DocumentoTypes.CNPJ;
  documentos: Array<DocumentoTypes> = [this.documentoDefault, DocumentoTypes.CPF, DocumentoTypes.CAEPF, DocumentoTypes.CEI];
  documentoSelecionado = this.documentoDefault;
  placeHolderDocumento = 'CNPJ da sua empresa';

  isNotValidApelido = false;
  isNotValidDocumento = false;
  isNotValidLogin = false;
  isNotValidSenha = false;

  returnUrl: string;

  @ViewChild('alertaComponent')
  alertaComponent: AlertComponent;

  constructor(
    private readonly loginService: LoginPainelConnectClientService,
    private readonly alertService: AlertService,
    private readonly empresaService: EmpresasApiService,
    private readonly modalService: BsModalService,
    private readonly route: ActivatedRoute) { }

  ngOnInit() {
  }

  ngAfterViewInit(): void {
    this.alertaComponent.isPainelConnectClient = true;
    this.returnUrl = this.route.snapshot.queryParamMap.get('redirect');
  }

  @HostListener('window:keyup', ['$event'])
  keyEvent(event: KeyboardEvent) {
    if (event.keyCode === 13) {
      this.btnConfirmar();
    }
  }

  changeDocumentoSelecionado(documento: DocumentoTypes) {
    this.documentoSelecionado = documento;
    this.documentoLogin = '';
    this.getDocumentoPlaceHolder(this.documentoSelecionado)
  }

  transform() {
    let documento = this.documentoLogin;
    switch (this.documentoSelecionado) {
      case DocumentoTypes.CNPJ:
        documento = documento.replace(/^(\d{2})(\d)/, '$1.$2');
        documento = documento.replace(/^(\d{2})\.(\d{3})(\d)/, '$1.$2.$3');
        documento = documento.replace(/\.(\d{3})(\d)/, '.$1/$2');
        documento = documento.replace(/(\d{4})(\d)/, '$1-$2');
        break;
      case DocumentoTypes.CPF:
        documento = documento.replace(/(\d{3})(\d)/, '$1.$2');
        documento = documento.replace(/(\d{3})(\d)/, '$1.$2');
        documento = documento.replace(/(\d{3})(\d{1,2})$/, '$1-$2');
        break;
      case DocumentoTypes.CEI:
        documento = documento.replace(/(\d{2})(\d{3})(\d{5})(\d{2})/, '$1.$2.$3/$4'); // Mascara CEI (99.999.99999/99)
        break;
      case DocumentoTypes.CAEPF:
        documento = documento.replace(/(\d{3})(\d{3})(\d{3})(\d{3})(\d{1})/, '$1.$2.$3/$4-$5'); // 13 caracteres
        documento = documento.replace(/(\d{3})(\d{3})(\d{3})(\d{3})(\d{2})/, '$1.$2.$3/$4-$5'); // 14 caracteres - Mascara CAEPF (999.999.999/999-99)
        break;
    }
    this.documentoLogin = documento;
  }

  isValidForm(): boolean {
    let isValid = true;
    if (!this.login.apelido) {
      isValid = false;
      this.validarApelido();
    }
    if (!this.documentoLogin) {
      isValid = false;
      this.validarDocumento();
    }
    if (!this.login.login) {
      isValid = false;
      this.validarLogin();
    }
    if (!this.login.senha) {
      isValid = false;
      this.validarSenha();
    }
    return isValid;
  }

  btnCancelar(): void {
    throw new Error("Method not implemented.");
  }

  btnConfirmar(): void {
    if (this.isValidForm()) {
      this.alertService.clear();
      this.login.documento = StringUtils.replaceAll(this.documentoLogin, '', '[\/\\.-]');
      this.empresaService.getEmpresasByApelidoClienteAndDocumento(this.login.apelido, this.login.documento, null).subscribe(resourceEmpresas => {
        if (resourceEmpresas.content && resourceEmpresas.content.length > 1) {
          const initialState = {
            empresas: resourceEmpresas.content,
            login: this.login,
            alertConfigs: this.alertConfigs,
            returnUrl: this.returnUrl
          }
          this.modalService.show(ModalLoginEmpresaComponent, { initialState, ignoreBackdropClick: true, class: "modal-sm" })
        } else {
          this.loginService.obterToken(this.login, this.alertConfigs, this.returnUrl);
        }
      });
    }
  }

  validarApelido() {
    setTimeout(() => {
      if (!this.login.apelido) {
        this.isNotValidApelido = true;
      } else {
        this.isNotValidApelido = false;
      }

    }, 1);
  }

  validarDocumento() {
    this.transform();
    if (!this.documentoLogin) {
      this.isNotValidDocumento = true;
    } else {
      this.isNotValidDocumento = false;
    }
  }

  validarLogin() {
    setTimeout(() => {
      if (!this.login.login) {
        this.isNotValidLogin = true;
      } else {
        this.isNotValidLogin = false;
      }
    }, 1);
  }

  validarSenha() {
    setTimeout(() => {
      if (!this.login.senha) {
        this.isNotValidSenha = true;
      } else {
        this.isNotValidSenha = false;
      }
    }, 1);
  }

  public getDocumentoPlaceHolder(documento: DocumentoTypes) {

    if (documento === DocumentoTypes.CNPJ) {
      this.placeHolderDocumento = 'CNPJ da sua empresa';
      this.maxlength = 18;
    }

    if (documento === DocumentoTypes.CPF) {
      this.placeHolderDocumento = 'CPF da sua empresa';
      this.maxlength = 14;
    }

    if (documento === DocumentoTypes.CAEPF) {
      this.placeHolderDocumento = 'CAEPF da sua empresa';
      this.maxlength = 18;
    }

    if (documento === DocumentoTypes.CEI) {
      this.placeHolderDocumento = 'CEI da sua empresa';
      this.maxlength = 15;
    }

  }

}

and here is the SPEC, with errors on getDocumentoPlaceHolder, changeDocumentoSelecionado and keyEvent methods.

import { LoginPainelConnectClientService } from '../shared/services/login-painel-connect-client.service';
import { AlertService } from '../../components/alert/_services';
import { EmpresasApiService } from '../../components/api-empresas/_service/api-empresas.service';
import { BsModalService } from 'ngx-bootstrap';
import { ActivatedRoute } from '@angular/router';
import { LoginPainelConnectClientComponent } from './login-painel-connect-client.component';
import { autoSpy } from 'src/auto-spy';

describe('LoginPainelConnectClientComponent', () => {
  it('when ngOnInit is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.ngOnInit();
    // assert
    // expect(c).toEqual
  });

  it('when ngAfterViewInit is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.ngAfterViewInit();
    // assert
    // expect(c).toEqual
  });

  it('when keyEvent is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.keyEvent();
    // assert
    // expect(c).toEqual
  });

  it('when changeDocumentoSelecionado is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.changeDocumentoSelecionado();
    // assert
    // expect(c).toEqual
  });

  it('when transform is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.transform();
    // assert
    // expect(c).toEqual
  });

  it('when isValidForm is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.isValidForm();
    // assert
    // expect(c).toEqual
  });

  it('when btnCancelar is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.btnCancelar();
    // assert
    // expect(c).toEqual
  });

  it('when btnConfirmar is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.btnConfirmar();
    // assert
    // expect(c).toEqual
  });

  it('when validarApelido is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.validarApelido();
    // assert
    // expect(c).toEqual
  });

  it('when validarDocumento is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.validarDocumento();
    // assert
    // expect(c).toEqual
  });

  it('when validarLogin is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.validarLogin();
    // assert
    // expect(c).toEqual
  });

  it('when validarSenha is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.validarSenha();
    // assert
    // expect(c).toEqual
  });

  it('when getDocumentoPlaceHolder is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.getDocumentoPlaceHolder();
    // assert
    // expect(c).toEqual
  });

});

function setup() {
  const loginService = autoSpy(LoginPainelConnectClientService);
const alertService = autoSpy(AlertService);
const empresaService = autoSpy(EmpresasApiService);
const modalService = autoSpy(BsModalService);
const route = autoSpy(ActivatedRoute);
  const builder = {
    loginService,
alertService,
empresaService,
modalService,
route,
    default() {
      return builder;
    },
    build() {
      return new LoginPainelConnectClientComponent(loginService,alertService,empresaService,modalService,route);
    }
  };

  return builder;
}
gandadil commented 4 years ago

I commented the lines with error on spec above and run the tests. I receive this message:

Error: src/auto-spy.ts(14,36): error TS1005: ';' expected. src/auto-spy.ts(14,89): error TS1128: Declaration or statement expected. src/auto-spy.ts(14,90): error TS1109: Expression expected. src/auto-spy.ts(14,91): error TS1109: Expression expected.

The line with error is:

type SpyOf<T> = T &
    Partial<{ [k in keyof T]: T[k] extends (...args: any[]) => any ? jasmine.Spy : T[k] }>;

I am using Angular 5, typescript 2.6.0, jasmine 3.4.0

gparlakov commented 4 years ago

It looks like 2 issues described here. Let me start by the second:

  1. Seems that you need to use the --legacy flag, see AutoSpy point. In short:

    • ng g scuri:autospy --for jest --legacy would generate a ts<2.8 jest compatible autoSpy type and function ng g scuri:autospy would generate a ts>2.8 jasmine compatible autoSpy type and function
    • replace the existing autospy.ts with this one, supporting older ts version
  2. Seems, you expect SCuri to place parameter(s) in the methods that need parameters. Correct me if I am wrong, please.

    it('when getDocumentoPlaceHolder is called it should', () => {
    // arrange
    const { build } = setup().default();
    const c = build();
    // act
    c.getDocumentoPlaceHolder(/* SCuri, add parameer here */);
    // assert
    // expect(c).toEqual
    });

How would that look like? And why would SCuri try to do that? SCuri's purpose is to help with boilerplate, not actually finish the logic of the unit test. What do you think?

gparlakov commented 4 years ago

@gandadil what do you think about my suggestion above?

gparlakov commented 4 years ago

@gandadil Closing this...