Skip to content

Commit

Permalink
Merge branch 'master' into CHI-1994-unit_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenhand committed Nov 1, 2023
2 parents 2f25470 + a43d3de commit 62db6d2
Show file tree
Hide file tree
Showing 34 changed files with 506 additions and 259 deletions.
5 changes: 4 additions & 1 deletion e2e-tests/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import { Browser, BrowserContext, Page, chromium } from '@playwright/test';
import { logPageTelemetry } from './browser-logs';
import { getConfigValue } from './config';

export type SetupPageReturn = {
context: BrowserContext;
Expand Down Expand Up @@ -45,7 +46,9 @@ export const setupContextAndPage = async (browser: Browser): Promise<SetupPageRe
await waitForBrowser(browser);

console.log('Launching page');
const context = await browser.newContext();
const context = await browser.newContext({
storageState: getConfigValue('storageStatePath') as string,
});
const page = await context.newPage();

logPageTelemetry(page);
Expand Down
58 changes: 33 additions & 25 deletions e2e-tests/case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,35 @@
*/

// eslint-disable-next-line import/no-extraneous-dependencies
import { Page } from '@playwright/test';
import { delay } from './okta/sso-login';
import { expect, Page } from '@playwright/test';

export type CaseSectionForm<T = Record<string, string>> = {
sectionTypeId: 'note' | 'referral' | 'household' | 'perpetrator' | 'incident' | 'document';
items: T;
};

export const caseHome = (page: Page) => {
const caseHomeArea = page.locator('div.Twilio-CRMContainer');
// const caseHomeArea = page.locator('div.Twilio-CRMContainer');
const selectors = {
addSectionButton: (sectionTypeId: string) =>
caseHomeArea.locator(
page.locator(
`//button[@data-testid='Case-${
sectionTypeId.charAt(0).toUpperCase() + sectionTypeId.slice(1)
}-AddButton']`,
),
formInput: (itemId: string) => caseHomeArea.locator(`input#${itemId}`),
formSelect: (itemId: string) => caseHomeArea.locator(`select#${itemId}`),
formTextarea: (itemId: string) => caseHomeArea.locator(`textarea#${itemId}`),
saveCaseItemButton: caseHomeArea.locator(
`//button[@data-testid='Case-AddEditItemScreen-SaveItem']`,
),
saveCaseAndEndButton: caseHomeArea.locator(`//button[@data-testid='BottomBar-SaveCaseAndEnd']`),
getNewCaseId: caseHomeArea.locator(`//p[@data-testid='Case-DetailsHeaderCaseId']`),
formItem: (itemId: string) => page.locator(`#${itemId}`),
formInput: (itemId: string) => page.locator(`input#${itemId}`),
formSelect: (itemId: string) => page.locator(`select#${itemId}`),
formTextarea: (itemId: string) => page.locator(`textarea#${itemId}`),
saveCaseItemButton: page.locator(`//button[@data-testid='Case-AddEditItemScreen-SaveItem']`),
saveCaseAndEndButton: page.locator(`//button[@data-testid='BottomBar-SaveCaseAndEnd']`),
getNewCaseId: page.locator(`//p[@data-testid='Case-DetailsHeaderCaseId']`),
};

async function fillSectionForm({ items }: CaseSectionForm) {
for (let [itemId, value] of Object.entries(items)) {
await expect(selectors.formItem(itemId)).toBeVisible();
await expect(selectors.formItem(itemId)).toBeEnabled();
if (await selectors.formInput(itemId).count()) {
await selectors.formInput(itemId).fill(value);
} else if (await selectors.formSelect(itemId).count()) {
Expand All @@ -54,24 +54,32 @@ export const caseHome = (page: Page) => {
}
}

async function addCaseSection(sectionForm: CaseSectionForm) {
const addButton = selectors.addSectionButton(sectionForm.sectionTypeId);
await addButton.click();
async function addCaseSection(section: CaseSectionForm) {
const sectionId =
section.sectionTypeId.charAt(0).toUpperCase() + section.sectionTypeId.slice(1);
const newSectionButton = selectors.addSectionButton(sectionId);
await newSectionButton.waitFor({ state: 'visible' });
await expect(newSectionButton).toContainText(sectionId);
await newSectionButton.click();
await fillSectionForm(section);

await fillSectionForm(sectionForm);

const saveButton = selectors.saveCaseItemButton;
await saveButton.click();

/**
* Fix to addOfflineContact tests flakiness
* TODO: investigate root cause
*/
await delay(300);
const saveItemButton = selectors.saveCaseItemButton;
await expect(saveItemButton).toBeVisible();
await expect(saveItemButton).toBeEnabled();
const responsePromise = page.waitForResponse('**/cases/**');
await saveItemButton.click();
const response = await responsePromise;
expect(response.ok()).toBeTruthy();
}

async function saveCaseAndEnd() {
const responsesPromise = Promise.all([
page.waitForResponse('**/cases/**'),
page.waitForResponse('**/contacts/**'),
]);
await selectors.saveCaseAndEndButton.click();
const responses = await responsesPromise;
responses.forEach((response) => expect(response.ok()).toBeTruthy());
}

const { getNewCaseId } = selectors;
Expand Down
37 changes: 5 additions & 32 deletions e2e-tests/caseList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

// eslint-disable-next-line import/no-extraneous-dependencies
import { Page, expect } from '@playwright/test';
import { caseHome } from './case';

export type Filter =
| 'Status'
Expand Down Expand Up @@ -63,9 +64,6 @@ export const caseList = (page: Page) => {
caseEditButton: caseListPage.locator(`//button[@data-testid='Case-EditButton']`),

//Case Section view
formInput: (itemId: string) => caseListPage.locator(`input#${itemId}`),
formSelect: (itemId: string) => caseListPage.locator(`select#${itemId}`),
formTextarea: (itemId: string) => caseListPage.locator(`textarea#${itemId}`),
saveCaseItemButton: caseListPage.locator(
`//button[@data-testid='Case-AddEditItemScreen-SaveItem']`,
),
Expand Down Expand Up @@ -115,6 +113,7 @@ export const caseList = (page: Page) => {
await expect(openCaseButton).toContainText(/^OpenCase[0-9]+$/);
await openCaseButton.click();
console.log('Opened first case in the results');
return caseHome(page);
}

//Check print view
Expand All @@ -130,34 +129,6 @@ export const caseList = (page: Page) => {
console.log('Close Case Print');
}

async function fillSectionForm({ items }: CaseSectionForm) {
for (let [itemId, value] of Object.entries(items)) {
if (await selectors.formInput(itemId).count()) {
await selectors.formInput(itemId).fill(value);
} else if (await selectors.formSelect(itemId).count()) {
await selectors.formSelect(itemId).selectOption(value);
} else if (await selectors.formTextarea(itemId).count()) {
await selectors.formTextarea(itemId).fill(value);
} else throw new Error(`Control ${itemId} not found`);
}
}

//Add a section (and close)
async function addCaseSection(section: CaseSectionForm) {
const sectionId =
section.sectionTypeId.charAt(0).toUpperCase() + section.sectionTypeId.slice(1);
const newSectionButton = selectors.addSectionButton(sectionId);
await newSectionButton.waitFor({ state: 'visible' });
await expect(newSectionButton).toContainText(sectionId);
await newSectionButton.click();

await fillSectionForm(section);

const saveItemButton = selectors.saveCaseItemButton;
await saveItemButton.waitFor({ state: 'visible' });
await saveItemButton.click();
}

//Edit Case
async function editCase() {
const editCaseButton = selectors.caseEditButton;
Expand All @@ -178,7 +149,10 @@ export const caseList = (page: Page) => {
const updateCaseButton = selectors.updateCaseButton;
await updateCaseButton.waitFor({ state: 'visible' });
await expect(updateCaseButton).toContainText('Save');
const responsePromise = page.waitForResponse('**/cases/**');
await updateCaseButton.click();
await responsePromise;

console.log('Updated Case Summary');
}

Expand Down Expand Up @@ -209,7 +183,6 @@ export const caseList = (page: Page) => {
filterCases,
openFirstCaseButton,
viewClosePrintView,
addCaseSection,
editCase,
updateCaseSummary,
verifyCaseSummaryUpdated,
Expand Down
4 changes: 1 addition & 3 deletions e2e-tests/chatScripts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@ import { getConfigValue } from './config';

export const defaultScript: ChatStatement[] = [
botStatement(
'Welcome to the helpline. To help us better serve you, please answer the following three questions.',
'Welcome. To help us better serve you, please answer the following questions. Are you calling about yourself? Please answer Yes or No.',
),
botStatement('Are you calling about yourself? Please answer Yes or No.'),
callerStatement('yes'),
botStatement("Thank you. You can say 'prefer not to answer' (or type X) to any question."),
botStatement('How old are you?'),
callerStatement('10'),
botStatement('What is your gender?'),
Expand Down
8 changes: 7 additions & 1 deletion e2e-tests/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const skipDataUpdateEnvs = ['staging', 'production'];
const flexEnvs = ['development', 'staging', 'production'];

// This is kindof a hack to get the correct default remote webchat url and twilio account info for the local env
const localOverrideEnv = helplineEnv == 'local' ? 'development' : helplineEnv;
export const localOverrideEnv = helplineEnv == 'local' ? 'development' : helplineEnv;

export const config: Config = {};

Expand Down Expand Up @@ -127,6 +127,7 @@ const configOptions: ConfigOptions = {
twilioAccountSid: {
envKey: 'TWILIO_ACCOUNT_SID',
ssmPath: `/${localOverrideEnv}/twilio/${helplineShortCode.toUpperCase()}/account_sid`,
default: 'AC_FAKE_UI_TEST_ACCOUNT',
},
twilioAuthToken: {
envKey: 'TWILIO_AUTH_TOKEN',
Expand Down Expand Up @@ -186,6 +187,11 @@ const configOptions: ConfigOptions = {
envKey: 'TEST_NAME',
default: () => (getConfigValue('inLambda') ? 'login' : ''),
},

hrmRoot: {
envKey: 'HRM_ROOT',
default: '', // Default cannot be set up front due to the account sid might not calculated.
},
};

export const setConfigValue = (key: string, value: ConfigValue) => {
Expand Down
22 changes: 15 additions & 7 deletions e2e-tests/contactForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

// eslint-disable-next-line import/no-extraneous-dependencies
import { Page } from '@playwright/test';
import { expect, Page } from '@playwright/test';

export type Categories = Record<string, string[]>;

Expand Down Expand Up @@ -48,9 +48,13 @@ export function contactForm(page: Page) {

async function selectTab(tab: ContactFormTab<unknown>) {
const button = selectors.tabButton(tab);
await expect(button).toBeVisible();
await expect(button).toBeEnabled();
const responsePromise = page.waitForResponse('**/contacts/**');
if ((await button.getAttribute('aria-selected')) !== 'true') {
await button.click();
}
await responsePromise;
}

async function fillStandardTab({ id, items }: ContactFormTab) {
Expand All @@ -75,12 +79,11 @@ export function contactForm(page: Page) {
}

return {
selectChildCallType: async (allowSkip: boolean = false) => {
selectChildCallType: async () => {
const childCallTypeButton = selectors.childCallTypeButton();
if (!(await childCallTypeButton.isVisible({ timeout: 200 })) && allowSkip) {
return;
}
const responsePromise = page.waitForResponse('**/contacts/**');
await childCallTypeButton.click();
await responsePromise;
},
fill: async (tabs: ContactFormTab<any>[]) => {
for (const tab of tabs) {
Expand All @@ -98,9 +101,14 @@ export function contactForm(page: Page) {
await selectTab(tab);

if (saveAndAddToCase) {
const responsePromise = page.waitForResponse('**/connectToCase');
await selectors.saveAndAddToCaseButton.click();
await page.waitForResponse('**/connectToCase');
} else await selectors.saveContactButton.click();
await responsePromise;
} else {
const responsePromise = page.waitForResponse('**/contacts/**');
await selectors.saveContactButton.click();
await responsePromise;
}

await selectors.tabButton(tab).waitFor({ state: 'detached' });
},
Expand Down
33 changes: 22 additions & 11 deletions e2e-tests/global-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
/* eslint-disable import/no-extraneous-dependencies */
import { FullConfig } from '@playwright/test';
import { differenceInMilliseconds } from 'date-fns';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { oktaSsoLoginViaApi, oktaSsoLoginViaGui } from './okta/sso-login';
import { getConfigValue, initConfig } from './config';
import { oktaSsoLoginViaApi } from './okta/sso-login';
import { getConfigValue, initConfig, localOverrideEnv } from './config';
import { getSidForWorker } from './twilio/worker';
import { clearOfflineTask } from './hrm/clearOfflineTask';

async function globalSetup(config: FullConfig) {
const start = new Date();
Expand All @@ -31,19 +32,29 @@ async function globalSetup(config: FullConfig) {
}

await initConfig();
await oktaSsoLoginViaApi(
const flexToken = await oktaSsoLoginViaApi(
getConfigValue('baseURL') as string,
getConfigValue('oktaUsername') as string,
getConfigValue('oktaPassword') as string,
getConfigValue('twilioAccountSid') as string,
);
/* await oktaSsoLoginViaGui(
config,
getConfigValue('oktaUsername') ?? 'NOT SET',
getConfigValue('oktaPassword') ?? 'NOT SET',
);
*/

const workerSid = await getSidForWorker(getConfigValue('oktaUsername') as string);
if (workerSid) {
await clearOfflineTask(
(getConfigValue('hrmRoot') as string) ||
`https://hrm-${localOverrideEnv}.tl.techmatters.org/v0/accounts/${getConfigValue(
'twilioAccountSid',
)}`,
workerSid,
flexToken,
);
} else {
console.warn(
`Could not find worker with username ${getConfigValue(
'oktaUsername',
)} to clear out offline tasks.`,
);
}
process.env.ARTIFACT_PATH = config.projects[0].outputDir;
console.log(
'Global setup completed',
Expand Down
Loading

0 comments on commit 62db6d2

Please sign in to comment.