Last active
June 25, 2019 18:51
-
-
Save jjrasche/1ba44469b07899b8b51c7c7da1639df3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Angular | |
import { Component, ViewChild } from "@angular/core"; | |
import { fakeAsync, TestModuleMetadata } from "@angular/core/testing"; | |
// Third-Party | |
import { of } from "rxjs"; | |
// Modules | |
import { ProposalHeaderModule } from "@vms/proposal/detail/proposal/proposal-header.module"; | |
// Services | |
import { BidTypeLookupService } from "@vms/services/bid-type/bid-type-lookup.service"; | |
import { GeorgeManageProposalLookupService } from "@vms/services/george/george-manage-proposal-lookup.service"; | |
import { PaymentTypeLookupService } from "@vms/services/proposal/payment-type-lookup.service"; | |
import { ProposalCertificationStatusLookupService } from "@vms/services/proposal/proposal-certification-status-lookup.service"; | |
import { ProposalDetailOwnersService } from "@vms/services/proposal/proposal-detail-owners.service"; | |
import { ProposalFiscalYearService } from "@vms/services/proposal/proposal-fiscal-year.service"; | |
import { ProposalStatusService } from "@vms/services/proposal/proposal-status.service"; | |
// Components / Directives | |
import { ProposalHeaderComponent } from "./proposal-header.component"; | |
// Models | |
import { BidType } from "@vms/shared/objects/bid-type"; | |
import { ApiResult } from "@vms/shared/objects/data-request"; | |
import { GeorgeOfficeDto } from "@vms/shared/objects/admin/george"; | |
import { HttpComponentTest } from "@vms/test/front-end-testing/http-component-test"; | |
import { PaymentType } from "@vms/shared/objects/payment-type"; | |
import { Proposal } from "@vms/proposal/objects/proposal"; | |
import { | |
getProposal, | |
getBidTypes, | |
getProposalManageGeorges, | |
getProposalOwners, | |
getFiscalYears, | |
getPaymentTypes, | |
getProposalStatuses, | |
getProposalCertStatuses, | |
getFailedSaveResponse, | |
getResponseOwners, | |
getChangeGeorgeResponse, | |
} from "@vms/proposal/detail/proposal/header/proposal-header.component.spec.data"; | |
import { ProposalStatusCodeEnum } from "@vms/shared/objects/proposal-status"; | |
import { TestInputType, InputTest } from "@vms/test/front-end-testing/test-objects"; | |
import { User } from "@vms/shared/objects/admin/user"; | |
// tslint:disable: max-line-length | |
// Host Component | |
@Component({ | |
selector: `test-host-component`, | |
template: ` | |
<vms-proposal-header [proposal]="proposal"> | |
</vms-proposal-header> | |
`, | |
}) | |
class HostComponent { | |
// create access to the component under test for use in test cases | |
@ViewChild(ProposalHeaderComponent) | |
public componentUnderTest: ProposalHeaderComponent; | |
public proposal: Proposal; | |
// get access to our components services that control access to global data in order to mock later | |
constructor( | |
public bidTypeService: BidTypeLookupService, | |
public georgeService: GeorgeManageProposalLookupService, | |
public ownerService: ProposalDetailOwnersService, | |
public fiscalYearService: ProposalFiscalYearService, | |
public paymentTypeService: PaymentTypeLookupService, | |
public proposalCertificationStatusService: ProposalCertificationStatusLookupService, | |
public proposalStatusService: ProposalStatusService | |
) {} | |
// allow test cases to set own data | |
public setupTestData(proposal: Proposal) { | |
this.proposal = proposal; | |
} | |
} | |
// this typing forces the implementation of inputs to list all values in the Input enum | |
export type FormInputKeys = { [key in keyof typeof Input]: any }; | |
export enum Input { | |
George = "George", | |
Owner = "Owner", | |
FiscalYear = "FiscalYear", | |
BidType = "BidType", | |
PaymentMethod = "PaymentMethod", | |
SaleName = "SaleName", | |
SaleNumber = "SaleNumber", | |
} | |
let specModule = ProposalHeaderModule.config() as TestModuleMetadata; | |
fdescribe("ProposalHeaderComponent", () => { | |
// testing abstraction used to simplify setup and access to helper methods | |
let base = new HttpComponentTest<ProposalHeaderComponent, FormInputKeys>(specModule, ProposalHeaderComponent, HostComponent); | |
base.settings = { | |
verifyAllCalls: false | |
}; | |
// header button interaction | |
it("When not in edit state in header edit button is cliked, then form is in edit state", fakeAsync(() => { | |
setupTest(getProposal(), false); | |
base.clickEditButton(); | |
expect(base.comp.editing).toEqual(true); | |
})); | |
it("When in edit state and header cancel button clicked, then form is not in edit state", fakeAsync(() => { | |
setupTest(); | |
base.clickCancelButton(); | |
expect(base.comp.editing).toEqual(false); | |
})); | |
it("When in edit state and form pristine, then header save button is disabled", fakeAsync(() => { | |
setupTest(); | |
base.expectDisabled(base.getFormSaveButton()); | |
})); | |
// Form Interactions | |
it("When form loads in read-mode, then the proposal object sets the form values", fakeAsync(() => { | |
let proposal = getProposal(); | |
setupTest(proposal, false, getBidTypes(), getProposalManageGeorges(), getProposalOwners(), getFiscalYears()); | |
base.compareReadOnlyFormValues(["18957", "Draft", "VMS 4.0", "Baraga Office", "Heym, Doug (HEYMDxxx)", proposal.fiscal_year.toString(), "Oral Auction", "Lump Sum", "Not Set", "Not Set", "Not Set", "Doug's second test sale", "11-999-18", "1", "Not Set", "Not Set", "Not Set"]); | |
})); | |
it("When form loads in edit-mode, then proposal object sets the form values", fakeAsync(() => { | |
let proposal = getProposal(); | |
proposal.is_fiscal_year_editable = true; | |
const fiscalYears = getFiscalYears(); | |
fiscalYears.data.push("2020"); | |
setupTest(proposal, true, getBidTypes(), getProposalManageGeorges(), getProposalOwners(), fiscalYears); | |
expect(base.getActualEditableFormValue()).toEqual(["Baraga Office", "Heym, Doug (HEYMDxxx)", proposal.fiscal_year.toString(), "Oral Auction", "Lump Sum", "Doug's second test sale", "999-18"]); | |
})); | |
it("When more than 1 fiscal year is available, then fiscal year field is editable", fakeAsync(() => { | |
let proposal = getProposal(); | |
proposal.is_fiscal_year_editable = true; | |
const fiscalYears = getFiscalYears(); | |
fiscalYears.data.push("2020"); | |
setupTest(proposal, true, getBidTypes(), getProposalManageGeorges(), getProposalOwners(), fiscalYears); | |
base.expectInputEditable(Input.FiscalYear); | |
})); | |
it("When 1 fiscal year is available, then fiscal year field is not editable", fakeAsync(() => { | |
setupTest(); | |
base.expectInputReadonly(Input.FiscalYear); | |
})); | |
it("When the prposal's fiscal year is not included in the drop down, then the fiscal year field is read only", fakeAsync(() => { | |
let proposal = getProposal(); | |
proposal.fiscal_year = 2018; // 2018 is not in the getFiscalYears data set | |
setupTest(proposal); | |
base.expectInputReadonly(Input.FiscalYear); | |
})); | |
it("When george is changed, then a request is made to update owners and sale number and owners and sale number are updated", fakeAsync(() => { | |
setupTest(); | |
base.flushAllRequests(); | |
base.setFormElement(Input.George, "380"); | |
base.expectDisabled(base.getFormElement(Input.SaleName)); | |
base.expectSingleRequest(base.verifyRequest(`/proposal/${base.hostComp.proposal.proposal_id}/get-sale-number-and-owners-after-george-change/380`, "GET"), getChangeGeorgeResponse()); | |
expect(getSaleNumberPrefix()).toEqual("12 -"); // for input prefix | |
expect(base.comp.formComponent.data.sale_num).toEqual("12-999-18"); // for input view value | |
// Saving state set correctly after response. Able to change inputs after save. | |
base.expectNotDisabled(base.getFormElement(Input.SaleName)); | |
expect(base.getInputFormControl(Input.Owner).value).toEqual(base.hostComp.proposal.owner); | |
const expectedOwnerOptions = getResponseOwners().filter(owner => owner.user_id !== 0).map(owner => owner.user_name); | |
const actual = base.getSelectOptionValues(base.getFormElement(Input.Owner), false); | |
expect(actual).toEqual(expectedOwnerOptions); | |
})); | |
// Validations | |
it("When editing sale number and saving, server side validation enforcing uniqueness shows validation error", fakeAsync(() => { | |
const proposal = getProposal(); | |
setupTest(); | |
// action | |
base.setFormElement(Input.SaleNumber, "12345"); | |
base.flushAllRequests(); | |
base.clickSaveButton(); | |
base.expectSingleRequest(base.verifyRequest(`/proposal/${proposal.proposal_id}`, "PUT"), getFailedSaveResponse()); | |
// expect | |
base.expectValidation(Input.SaleNumber); | |
base.fixForTimerInQueueError(); | |
})); | |
it("When users edits sale number, then unable to enter anything other than format xxx - xx", fakeAsync(() => { | |
setupTest(getProposal()); | |
// invalid input | |
base.setFormElement(Input.SaleNumber, "tester"); | |
expect(base.getFormElementValue(Input.SaleNumber)).toEqual(``); | |
// valid input, expect formatting | |
base.setFormElement(Input.SaleNumber, "12345"); | |
expect(base.getFormElementValue(Input.SaleNumber)).toEqual(`123-45`); | |
})); | |
it("When editing sale number, users are able to enter all 5 numbers or no numbers at all", fakeAsync(() => { | |
let proposal = { proposal_status_code: ProposalStatusCodeEnum.Draft, george_id: 280, sale_num_first_2: 11 } as Proposal; | |
setupTest(proposal); | |
// valid entry, 5 numbers | |
base.setAndValidateFormElement(Input.SaleNumber, "12345", "123-45"); | |
// invalid entry, validtion check should fail | |
base.setAndValidateFormElement(Input.SaleNumber, "123", "123-", true); | |
// valid entry, empty | |
base.setAndValidateFormElement(Input.SaleNumber, null, ""); | |
})); | |
// Permissions | |
it("When proposal is in Draft status and user has PROPOSAL_MANAGE permission, then can edit all fields", fakeAsync(() => { | |
base.giveUserGeorgePermissions(["PROPOSAL_MANAGE"]); | |
let proposal = { proposal_status_code: ProposalStatusCodeEnum.Draft, george_id: 280, sale_num_first_2: 11 } as Proposal; | |
setupTest(proposal); | |
base.expectFieldsToBeEditable([Input.George, Input.Owner, Input.BidType, Input.PaymentMethod, Input.SaleName, Input.SaleNumber]); | |
})); | |
it("When proposal is in Draft status and user has PROPOSAL_REVIEW permission, then can edit all fields", fakeAsync(() => { | |
base.giveUserGeorgePermissions(["PROPOSAL_REVIEW"]); | |
let proposal = { proposal_status_code: ProposalStatusCodeEnum.Draft, george_id: 280, sale_num_first_2: 11 } as Proposal; | |
setupTest(proposal); | |
base.expectFieldsToBeEditable([Input.George, Input.Owner, Input.BidType, Input.PaymentMethod, Input.SaleName, Input.SaleNumber]); | |
})); | |
it("When proposal status is between Draft and Reviewing and user has PROPOSAL_REVIEW permission, then can edit some fields", fakeAsync(() => { | |
base.giveUserGeorgePermissions(["PROPOSAL_REVIEW"]); | |
// spyOn((base.comp as any).proposalStatusService, "canEditProposal").and.returnValue(true); | |
let proposal = { proposal_status_code: ProposalStatusCodeEnum.Reviewing, george_id: 280, sale_num_first_2: 11 } as Proposal; | |
setupTest(proposal); | |
base.expectFieldsToBeEditable([Input.BidType, Input.PaymentMethod, Input.SaleName]); | |
})); | |
it("When proposal status is between Draft and Reviewing and user has PROPOSAL_MANAGE permission, then cannot edit anything", fakeAsync(() => { | |
base.giveUserGeorgePermissions(["PROPOSAL_MANAGE"]); | |
// spyOn((base.comp as any).proposalStatusService, "canEditProposal").and.returnValue(true); | |
let proposal = { proposal_status_code: ProposalStatusCodeEnum.Reviewing, george_id: 280, sale_num_first_2: 11 } as Proposal; | |
setupTest(proposal); | |
base.expectFieldsToBeEditable([]); | |
})); | |
function setupTest( | |
proposal: Proposal = getProposal(), | |
editing: boolean = true, | |
bidTypes: ApiResult<BidType[]> = getBidTypes(), | |
georges: ApiResult<GeorgeOfficeDto[]> = getProposalManageGeorges(), | |
proposalOwners: ApiResult<User[]> = getProposalOwners(), | |
fiscalYears: ApiResult<string[]> = getFiscalYears(), | |
paymentTypes: ApiResult<PaymentType[]> = getPaymentTypes(), | |
proposalStatuses: any = getProposalStatuses(), | |
proposalCertStatuses: any = getProposalCertStatuses()) { | |
// component setup | |
base.comp.isPristine = true; | |
base.comp.isValid = true; | |
base.comp.editing = editing; | |
base.hostComp.setupTestData(proposal); | |
// setup loadable data services | |
spyOn(base.hostComp.bidTypeService, "loadFunction").and.returnValue(of(bidTypes)); | |
spyOn(base.hostComp.georgeService, "loadFunction").and.returnValue(of(georges)); | |
spyOn(base.hostComp.ownerService, "loadFunction").and.returnValue(of(proposalOwners)); | |
spyOn(base.hostComp.fiscalYearService, "loadFunction").and.returnValue(of(fiscalYears)); | |
spyOn(base.hostComp.paymentTypeService, "loadFunction").and.returnValue(of(paymentTypes)); | |
spyOn(base.hostComp.proposalStatusService, "loadFunction").and.returnValue(of(proposalStatuses)); | |
spyOn(base.hostComp.proposalCertificationStatusService, "loadFunction").and.returnValue(of(proposalCertStatuses)); | |
// initialize inputs | |
base.formInputs = { | |
George: { type: TestInputType.Select, id: "george", controlName: "george_id" } as InputTest, | |
Owner: { type: TestInputType.Select, id: "owner", controlName: "owner" } as InputTest, | |
FiscalYear: { type: TestInputType.Select, id: "fiscal_year", controlName: "fiscal_year" } as InputTest, | |
BidType: { type: TestInputType.Select, id: "bid_type", controlName: "bid_type_code" } as InputTest, | |
PaymentMethod: { type: TestInputType.Select, id: "payment_method", controlName: "payment_type_code" } as InputTest, | |
SaleName: { type: TestInputType.Input, id: "sale_name", controlName: "sale_name" } as InputTest, | |
SaleNumber: { type: TestInputType.Input, id: "sale_number", controlName: "sale_num" } as InputTest, | |
}; | |
base.wait(); | |
base.formGroup = base.comp.formComponent.formGroup; | |
} | |
function getSaleNumberPrefix(): string { | |
const saleNumberInput = base.getFormElement(Input.SaleNumber); | |
const saleNumberAddOn = saleNumberInput.previousElementSibling; | |
return saleNumberAddOn.textContent; | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment