qe-fe-automation
Version:
FE test automation framework using cypress
392 lines (378 loc) • 16.8 kB
text/typescript
import { createGuestDetails } from '@support-user/index';
import { UserModal, ProfileInterceptApis, TravelerModal } from '@profile-lib/index';
import { TripTileType, Timeouts, Random } from '@utility-lib/index';
import { SharedIntercepts } from '@intercepts-lib/shared_intercepts';
const selectors = {
// Selectors for contact info section
givenNameInput: 'input[formcontrolname="givenName"]',
familyNameInput: 'input[formcontrolname="familyName"]',
phoneInput: 'ta-phone-input[formcontrolname="mobilePhone"]',
workPhoneInput: 'ta-phone-input[formcontrolname="workPhoneNumber"]',
phoneValue: 'input.ta-phone-input__template',
personalEmailValue: 'input[formcontrolname="alternateEmail"]',
workEmailValue: 'input[formcontrolname="email"]',
loyaltyButton: '#mat-checkbox-1 > .mat-checkbox-layout > .mat-checkbox-inner-container',
loyaltyButtonStatus: 'ta-checkbox[formcontrolname="marketingCommEmailOptIn"] input',
saveFooterButton: '[data-testid="stickyPrimaryButton"]',
// Selectors for Delegate info section
asDelegateCheckbox: 'ta-checkbox[formcontrolname="iAmDelegateForCheckbox"]',
asDelegateInput: 'ta-user-group-simple[formcontrolname="iAmDelegateForUsers"] input',
asDelegateEntry:
'ta-user-group-simple[formcontrolname="iAmDelegateForUsers"] span.ta-user-tag-simple__name',
asDelegateRemove:
'ta-user-group-simple[formcontrolname="iAmDelegateForUsers"] .ta-user-tag-simple__remove',
setDelegateCheckbox: 'ta-checkbox[formcontrolname="myDelegatesCheckbox"]',
setDelegateInput: 'ta-user-group-simple[formcontrolname="myDelegatesUsers"] input',
setDelegateEntry:
'ta-user-group-simple[formcontrolname="myDelegatesUsers"] span.ta-user-tag-simple__name',
setDelegateRemove:
'ta-user-group-simple[formcontrolname="myDelegatesUsers"] .ta-user-tag-simple__remove',
searchDelgateResult: '.ta-user-search-simple-popover__item',
// Selectors for Assistant info section
searchAssistantInput:
'.ta-profile-assistant-info__search-wrapper > ta-user-search > .ta-user-search',
searchAssistantResult: '.ta-user-search-popover__item',
removeAssistant: '.ta-profile-assistant-info__cancel',
existingAssistantButtonNo: 'input[value="no"]',
existingAssistantButtonYes: 'input[value="yes"]',
newAssistantNameInput: '.ta-profile-assistant-info__form input[formcontrolname="name"]',
newAssistantEmailInput:
'.ta-profile-assistant-info__form input[formcontrolname="email"]',
newAssistantPhoneInput: '.ta-profile-assistant-info__form ta-form-wrap [type="tel"]',
addSecondAssistantButton:
'.ta-profile-contact-info__second-info > .ta-button > .ta-button__content',
// Selectors for Emergency Contact info section
emergencyNameInput:
'div[formgroupname="emergencyContact"] input[formcontrolname="name"]',
emergencyRelationshipDropdown: 'div[formgroupname="emergencyContact"] .ta-select',
emergencyRelationshipFriend: 'div[aria-label="Friend"]',
emergencyRelationshipValue: 'ta-select[formcontrolname="relation"] button',
emergencyEmailInput:
'div[formgroupname="emergencyContact"] input[formcontrolname="email"]',
emergencyPhoneInput:
'div[formgroupname="emergencyContact"] .ta-phone-input [type="tel"]'
};
type gender = 'MALE' | 'FEMALE';
type age = number;
export class ContactModal {
static visitPage(): void {
cy.allure().logStep('Visit profile page on contact info tab');
cy.visit('app/user2/profile?tab=contact');
}
static interceptApis(numRetries: number): void {
if (numRetries === 0) {
throw new Error(
`Could not find property '${'given_name'}' after multiple attempts`
);
}
SharedIntercepts.intercepts();
cy.allure().logStep('Visit profile page on contact info tab');
ContactModal.visitPage();
cy.wait('@waitForUserInfo').then((interception) => {
const response = interception.response;
if (response && Object.prototype.hasOwnProperty.call(response.body, 'given_name')) {
// Response has the expected property, proceed with the test
cy.wrap(response.body['given_name']).as('givenName');
} else {
// Response is missing the expected property, retry after 1 second with a new intercept
cy.log('Bad API response, retrying...');
cy.wait(Timeouts.SHORT_TIMEOUT_3_SEC.timeout);
this.interceptApis(numRetries - 1);
}
});
}
static getContactInfo(): void {
cy.wait('@waitForUserInfo').then((interception) => {
// Gets current contact information and saves them as aliases
const response = interception.response;
if (response) {
const properties = ['given_name', 'family_name', 'phone_number', 'email'];
properties.forEach((property) => {
cy.wrap(response.body)
.should('have.property', property)
.as(`org${property.charAt(0).toUpperCase()}${property.slice(1)}`);
});
}
});
}
static compareContactInfo(): void {
// Proper handling of the latest request needs unique alias each time
const userInfoInterceptAlias = 'waitForUserInfo_' + Random.randomString();
cy.intercept('GET', '/api/uaa/userinfo').as(userInfoInterceptAlias);
cy.reload();
cy.wait(`@${userInfoInterceptAlias}`).then((interception) => {
// Gets new contact information and saves them as new aliases
const response = interception.response;
function setAliasForProperty(propertyName: string) {
cy.wrap(response?.body)
.should('have.property', propertyName)
.as(`new${propertyName.charAt(0).toUpperCase()}${propertyName.slice(1)}`);
}
if (response) {
setAliasForProperty('given_name');
setAliasForProperty('family_name');
setAliasForProperty('phone_number');
setAliasForProperty('email');
}
});
// Confirms on the API level that the contact info has been updated
const properties = {
orgGiven_name: 'newGiven_name',
orgFamily_name: 'newFamily_name',
orgPhone_number: 'newPhone_number'
};
for (const [orgProp, newProp] of Object.entries(properties)) {
TravelerModal.compareAPIProperties(orgProp, newProp);
}
// Confirms that the UI is also displaying the updated contact info
TravelerModal.compareUIProperties(selectors.givenNameInput, properties.orgGiven_name);
TravelerModal.compareUIProperties(
selectors.familyNameInput,
properties.orgFamily_name
);
// Phone numbers requires unique comparisons
cy.get(selectors.phoneValue)
.first() // Checks mobile number field
.invoke('val')
.then((displayedPhoneNumber) => {
const phoneStr = displayedPhoneNumber?.toString().replace(/\s/g, '');
cy.get('@newPhone_number').then((value) => {
expect(phoneStr).to.equal(value);
});
});
cy.get(selectors.phoneValue)
.eq(1) // Checks work phone field
.invoke('val')
.then((displayedWorkNumber) => {
const phoneStr = displayedWorkNumber?.toString().replace(/\s/g, '');
cy.get('@newPhone_number').then((value) => {
expect(phoneStr).to.equal(value);
});
});
// Confirm personal email field is not blank (since there was none added before)
cy.get(selectors.personalEmailValue).invoke('val').should('not.be.empty');
// Confirms the work email address field is displaying (field cannot be edited; checks existing email is displaying)
cy.get(selectors.workEmailValue)
.invoke('val')
.then((displayedEmail) => {
cy.get('@newEmail').then((value) => {
expect(displayedEmail).to.equal(value);
});
});
// Confirms marketing email checkbox is now enabled (previously unchecked)
cy.get(selectors.loyaltyButtonStatus).should('have.attr', 'aria-checked', 'true');
}
static createNewContactInfo = (
age: age,
gender: gender,
travelerType: TripTileType
) => {
const {
givenName = '',
familyName = '',
phoneNumber = '',
email = ''
} = createGuestDetails(age, gender, travelerType);
// Trim the generated phone number down to 11 characters (with a '+')
const maxLength = 12;
cy.wrap(phoneNumber)
.invoke('substring', 0, maxLength)
.then((number) => {
const trimNumber = number;
// Adds and saves new contact information
UserModal.fillTravelerName(givenName, familyName);
UserModal.fillMobilePhoneNumber(trimNumber);
UserModal.fillWorkPhoneNumber(trimNumber);
UserModal.fillAltEmail(email);
cy.get(selectors.loyaltyButton).click(); // Checks marketing email box, which was previously not checked
cy.get(selectors.saveFooterButton).click();
cy.wait('@userInfoSave');
});
};
}
export class DelegateModal {
static interceptApis(numRetries: number): void {
if (numRetries === 0) {
throw new Error(
`Could not find property '${'delegatedUsers'}' after multiple attempts`
);
}
ProfileInterceptApis.interceptProfileApis();
cy.allure().logStep('Visit profile page on contact info tab');
ContactModal.visitPage();
cy.wait('@waitForDelegatesInfo').then((interception) => {
const response = interception.response;
if (
response &&
Object.prototype.hasOwnProperty.call(response.body, 'delegatedUsers')
) {
// Response has the expected property, proceed with the test
cy.wrap(response.body['delegatedUsers']).as('delegates');
} else {
// Response is missing the expected property, retry after 1 second with a new intercept
cy.log('Bad API response, retrying...');
cy.wait(Timeouts.SHORT_TIMEOUT_3_SEC.timeout);
this.interceptApis(numRetries - 1);
}
});
}
static addAsDelegate(delegate: string): void {
// Proper handling of the latest request needs unique alias each time
const delegatesInterceptAlias = 'waitForDelegatesInfoSave_' + Random.randomString();
cy.intercept('PUT', 'api/user/profile/delegates').as(delegatesInterceptAlias);
cy.get(selectors.asDelegateCheckbox).click();
cy.get(selectors.asDelegateInput).type(delegate);
cy.get(selectors.searchDelgateResult).click();
cy.get(selectors.saveFooterButton).click();
cy.wait(`@${delegatesInterceptAlias}`).then((interception) => {
// Confirms user has a saved delegate
const response = interception.response;
if (response) {
const properties = ['givenName', 'familyName'];
properties.forEach((property) => {
cy.wrap(response.body.delegatedUsers[0].user).should('have.property', property);
});
}
});
}
static verifyAsDelegate(delegate: string) {
cy.get(selectors.asDelegateEntry).should('contain.text', delegate);
}
static deleteAsDelegate() {
// Proper handling of the latest request needs unique alias each time
const delegatesInterceptAlias = 'waitForDelegatesInfoSave_' + Random.randomString();
cy.intercept('PUT', 'api/user/profile/delegates').as(delegatesInterceptAlias);
cy.get(selectors.asDelegateRemove).click();
cy.get(selectors.saveFooterButton).click();
cy.wait(`@${delegatesInterceptAlias}`).then((interception) => {
// Confirms user has no saved delegates
const response = interception.response;
if (response) {
const properties = ['user'];
properties.forEach((property) => {
cy.wrap(response.body.delegatedUsers).should('not.have.property', property);
});
}
});
cy.get(selectors.asDelegateEntry).should('not.exist');
}
static addSetDelegate(delegate: string) {
// Proper handling of the latest request needs unique alias each time
const delegatesInterceptAlias = 'waitForDelegatesInfoSave_' + Random.randomString();
cy.intercept('PUT', 'api/user/profile/delegates').as(delegatesInterceptAlias);
cy.get(selectors.setDelegateCheckbox).click();
cy.get(selectors.setDelegateInput).type(delegate);
cy.get(selectors.searchDelgateResult).click();
cy.get(selectors.saveFooterButton).click();
cy.wait(`@${delegatesInterceptAlias}`).then((interception) => {
// Confirms user has a set delegate
const response = interception.response;
console.log(response);
if (response) {
const properties = ['givenName'];
properties.forEach((property) => {
cy.wrap(response.body.delegates[0].user).should('have.property', property);
});
}
});
}
static verifySetDelegate(delegate: string): void {
cy.get(selectors.setDelegateEntry).should('contain.text', delegate);
}
}
export class AssistantModal {
static addExistingAssistant(assistant: string): void {
cy.allure().logStep('Adding existing assistant on contact info tab');
cy.get(selectors.searchAssistantInput).as('assistantInput');
cy.get('@assistantInput').scrollIntoView();
cy.get('@assistantInput').should('be.visible').click();
cy.get('@assistantInput').should('be.visible').type(assistant);
cy.get(selectors.searchAssistantResult).should('be.visible').click();
cy.get(selectors.saveFooterButton).click();
}
static removeAssistant(): void {
cy.allure().logStep('Removing assistant on contact info tab');
cy.get(selectors.removeAssistant).click();
cy.get(selectors.saveFooterButton).click();
}
static validateAssistant(
assistantName: string,
assistantEmail: string,
assistantPhone: string
): void {
cy.allure().logStep('Validating assistant on contact info tab');
cy.get(selectors.newAssistantNameInput).should('have.value', assistantName);
cy.get(selectors.newAssistantEmailInput).should('have.value', assistantEmail);
cy.get(selectors.newAssistantPhoneInput)
.invoke('val')
.then((phoneNumber) => {
const actualPhoneNumber = Cypress._.trim(
phoneNumber?.toString().replace(/\D/g, '').substring(1)
); // removes all non-numeric characters
expect(actualPhoneNumber).to.eq(assistantPhone);
});
}
static addNewAssistant(
assistantName: string,
assistantEmail: string,
assistantPhone: string
): void {
cy.allure().logStep('Adding new assistant on contact info tab');
cy.get(selectors.existingAssistantButtonNo).click();
cy.get(selectors.newAssistantNameInput).type(assistantName);
cy.get(selectors.newAssistantEmailInput).type(assistantEmail);
cy.get(selectors.newAssistantPhoneInput).type(assistantPhone);
cy.get(selectors.saveFooterButton).click();
}
static addSecondAssistant(assistant: string): void {
cy.allure().logStep('Adding second assistant on contact info tab');
cy.get(selectors.addSecondAssistantButton).click();
cy.get(selectors.searchAssistantInput).type(assistant);
cy.get(selectors.searchAssistantResult).click();
cy.get(selectors.saveFooterButton).click();
}
}
export class EmergencyContactModal {
static addEmergencyContact(
emergencyName: string,
emergencyEmail: string,
emergencyNumber: string
): void {
cy.allure().logStep('Adding emergency contact info on contact info tab');
cy.get(selectors.emergencyNameInput).type(emergencyName);
cy.get(selectors.emergencyRelationshipDropdown).click();
// Defaults to relationship type "Friend"
cy.get(selectors.emergencyRelationshipFriend).click();
cy.get(selectors.emergencyEmailInput).type(emergencyEmail);
cy.get(selectors.emergencyPhoneInput).type(emergencyNumber);
cy.get(selectors.saveFooterButton).click();
}
static verifyEmergencyContact(
emergencyName: string,
emergencyEmail: string,
emergencyNumber: string
) {
cy.allure().logStep('Verifying emergency contact info on contact info tab');
cy.get(selectors.emergencyNameInput).should('have.value', emergencyName);
cy.get(selectors.emergencyEmailInput).should('have.value', emergencyEmail);
cy.get(selectors.emergencyPhoneInput)
.invoke('val')
.then((displayedPhoneNumber) => {
const phoneStr = displayedPhoneNumber
?.toString()
.replace(/\s/g, '')
.replace(/^\+1/, '');
expect(phoneStr).to.equal(emergencyNumber);
});
// Verifies the default relationship type, friend
cy.get(selectors.emergencyRelationshipValue).should('have.value', 'FRIEND');
}
static clearEmergencyContact(): void {
cy.allure().logStep('Removing emergency contact info on contact info tab');
cy.get(selectors.emergencyNameInput).clear();
// Relationship dropdown cannot be cleared
cy.get(selectors.emergencyEmailInput).clear();
cy.get(selectors.emergencyPhoneInput).clear();
cy.get(selectors.saveFooterButton).click();
}
}