diff --git a/playwright-e2e/config/config.ts b/playwright-e2e/config/config.ts new file mode 100644 index 0000000000..2d25282050 --- /dev/null +++ b/playwright-e2e/config/config.ts @@ -0,0 +1,38 @@ +import path from 'path'; +import dotenv from 'dotenv'; +import { env } from 'process'; + +dotenv.config({ path: path.resolve(__dirname, '../.env') }); + +type STUDY = 'SINGULAR' | 'RGP'; + +interface ENV { + EMAIL: number | undefined; + PASSWORD: number | undefined; + BASE_URI: string | undefined; +} + +export type Config = Required; + +const getConfig = (study: STUDY): ENV => { + return { + EMAIL: undefined, + PASSWORD: undefined, + BASE_URI: env.BASE_URI + }; +}; + +/** + * Throwing error if any study env is undefined + * @param {STUDY} study + */ +const verifyEnv = (study: STUDY): void => { + const studyEnv = Object.entries(env).filter((key) => key.indexOf(study) > -1); + for (const [key, value] of studyEnv) { + if (value === undefined) { + throw new Error(`Missing ${key} in .env`); + } + } +}; + +export default verifyEnv; diff --git a/playwright-e2e/data/user.ts b/playwright-e2e/data/user.ts new file mode 100644 index 0000000000..8c6b99af73 --- /dev/null +++ b/playwright-e2e/data/user.ts @@ -0,0 +1,4 @@ +export interface User { + email: string | undefined; + password: string | undefined; +} diff --git a/playwright-e2e/fixtures/singular-fixture.ts b/playwright-e2e/fixtures/singular-fixture.ts index a20c3adf9e..d22850d682 100644 --- a/playwright-e2e/fixtures/singular-fixture.ts +++ b/playwright-e2e/fixtures/singular-fixture.ts @@ -32,7 +32,10 @@ const fixture = base.extend({ const homePage = await launchHomePage(page); await homePage.signUp(); await new PreScreeningPage(page).enterInformationAboutYourself(); - await auth.createAccountWithEmailAlias(page, { email: SINGULAR_USER_EMAIL, password: SINGULAR_USER_PASSWORD }); + process.env.SINGULAR_USER_EMAIL_ALIAS = await auth.createAccountWithEmailAlias(page, { + email: SINGULAR_USER_EMAIL, + password: SINGULAR_USER_PASSWORD + }); const dashboardPage = new DashboardPage(page); await dashboardPage.waitForReady(); await use(dashboardPage); diff --git a/playwright-e2e/pages/page-base.ts b/playwright-e2e/pages/page-base.ts index ac261ff00a..615d9f29ec 100644 --- a/playwright-e2e/pages/page-base.ts +++ b/playwright-e2e/pages/page-base.ts @@ -12,11 +12,16 @@ export default abstract class PageBase implements PageInterface { protected readonly baseUrl: string; protected constructor(page: Page, baseURL: string) { + if (baseURL == null) { + throw Error(`Invalid baseURL=${baseURL}`); + } this.page = page; this.baseUrl = baseURL; } - abstract waitForReady(): Promise; + async waitForReady(): Promise { + await waitForNoSpinner(this.page); + } /** * Return "Back" button locator diff --git a/playwright-e2e/pages/singular/dashboard-page.ts b/playwright-e2e/pages/singular/dashboard-page.ts index 84fc9a0453..a85319ee95 100644 --- a/playwright-e2e/pages/singular/dashboard-page.ts +++ b/playwright-e2e/pages/singular/dashboard-page.ts @@ -11,6 +11,7 @@ export default class DashboardPage extends SingularPage { } async waitForReady(): Promise { + await super.waitForReady(); await expect(this.page.locator('h1.title')).toHaveText('My Dashboard'); await expect(this.page.locator('h1.title')).toBeVisible(); await expect(this.getViewFamilyEnrollmentMessageButton()).toBeVisible(); diff --git a/playwright-e2e/pages/singular/navbar.ts b/playwright-e2e/pages/singular/navbar.ts index 38e2e76fc4..4a8211f1ab 100644 --- a/playwright-e2e/pages/singular/navbar.ts +++ b/playwright-e2e/pages/singular/navbar.ts @@ -10,7 +10,8 @@ export const NavSelectors = { FAQs: '.header__nav >> text=FAQs', ForResearchers: '.header__nav >> text=For Researchers', ForClinicians: '.header__nav >> text=For Clinicians', - SignMeUp: '.header__nav >> text=Sign me up!' + SignMeUp: '.header__nav >> text=Sign me up!', + MyDashboard: '.header__nav >> text=My Dashboard' }; export async function goToAboutUs(page: Page): Promise { @@ -34,13 +35,3 @@ export async function goToPath(page: Page, path?: string): Promise { return await goToPath(page); } - -export async function signMeUp(page: Page): Promise { - const progressSpinner = 'mat-spinner[role="progressbar"]'; - await Promise.all([page.locator(NavSelectors.SignMeUp).click(), page.locator(progressSpinner).waitFor({ state: 'visible' })]); - await page.locator(progressSpinner).waitFor({ state: 'detached', timeout: 30 * 1000 }); -} - -export async function clickLogin(page: Page): Promise { - await page.locator(NavSelectors.Login).click(); -} diff --git a/playwright-e2e/pages/singular/singular-page.ts b/playwright-e2e/pages/singular/singular-page.ts index d8eec100e1..9e4db199c9 100644 --- a/playwright-e2e/pages/singular/singular-page.ts +++ b/playwright-e2e/pages/singular/singular-page.ts @@ -1,15 +1,22 @@ import { Page } from '@playwright/test'; +import { User } from 'data/user'; import PageBase from 'pages/page-base'; +import { SingularUser } from 'pages/singular/singular-user'; +import { generateEmailAlias } from 'utils/faker-utils'; /** * Project Singular base page. */ export abstract class SingularPage extends PageBase { + private user: User; + protected constructor(page: Page) { - const { SINGULAR_BASE_URL } = process.env; - if (SINGULAR_BASE_URL == null) { - throw Error(`Invalid Singular base URL: process.env.SINGULAR_BASE_URL=${SINGULAR_BASE_URL}`); + const { SINGULAR_BASE_URL, SINGULAR_USER_EMAIL, SINGULAR_USER_PASSWORD } = process.env; + if (SINGULAR_USER_EMAIL == null || SINGULAR_USER_PASSWORD == null || SINGULAR_BASE_URL == null) { + throw Error('Invalid parameters: Email and Password are undefined or null.'); } super(page, SINGULAR_BASE_URL); + const emailAlias = generateEmailAlias(SINGULAR_USER_EMAIL); + this.user = new SingularUser(emailAlias, SINGULAR_USER_PASSWORD); } } diff --git a/playwright-e2e/pages/singular/singular-user.ts b/playwright-e2e/pages/singular/singular-user.ts new file mode 100644 index 0000000000..01feb09c77 --- /dev/null +++ b/playwright-e2e/pages/singular/singular-user.ts @@ -0,0 +1,22 @@ +import { Page } from '@playwright/test'; +import { fillInEmailPassword } from 'authentication/auth-base'; +import { User } from 'data/user'; + +export class SingularUser implements User { + email: string; + password: string; + + constructor(email: string, password: string) { + this.email = email; + this.password = password; + } +/* + async login(page: Page, opts: { email?: string; password?: string } = {}): Promise { + const { email = this.email, password = this.password } = opts; + await page.locator('.header button[data-ddp-test="signInButton"]:has-text("Log In")').click(); + await fillInEmailPassword(page, { + email, + password + }); + } */ +} diff --git a/playwright-e2e/tests/singular/account-login.spec.ts b/playwright-e2e/tests/singular/account-login.spec.ts new file mode 100644 index 0000000000..c77ff286aa --- /dev/null +++ b/playwright-e2e/tests/singular/account-login.spec.ts @@ -0,0 +1,37 @@ +import { expect } from '@playwright/test'; +import { login } from 'authentication/auth-singular'; +import { test } from 'fixtures/singular-fixture'; +import Modal from 'lib/component/modal'; +import { assertActivityHeader } from 'utils/assertion-helper'; +import { NavSelectors } from 'pages/singular/navbar'; + +test('new enrollment can continue after log out @visual @singular', async ({ page, dashboardPage }) => { + await dashboardPage.logOut(); + + await login(page, { email: process.env.SINGULAR_USER_EMAIL_ALIAS, password: process.env.SINGULAR_USER_PASSWORD }); + await dashboardPage.waitForReady(); + + const enrollMyselfButton = await page.locator('//*[contains(@class,"controls")]/button[./*[contains(@class,"next-container")]]').nth(0).screenshot(); + expect(enrollMyselfButton).toMatchSnapshot('enroll-myself-button.png'); + + const enrollMyChildButton = await page.locator('//*[contains(@class,"controls")]/button[./*[contains(@class,"next-container")]]').nth(1).screenshot(); + expect(enrollMyChildButton).toMatchSnapshot('enroll-my-child-button.png'); + + const enrollMyAdultDependentButton = await page.locator('//*[contains(@class,"controls")]/button[./*[contains(@class,"next-container")]]').nth(2).screenshot(); + expect(enrollMyAdultDependentButton).toMatchSnapshot('enroll-my-adult-dependent-button.png'); + + // Click Enroll Myself button + await dashboardPage.enrollMyself(); + await assertActivityHeader(page, 'Enroll myself'); + + // Go back to Dashboard + await page.locator(NavSelectors.MyDashboard).click(); + await dashboardPage.waitForReady(); + + // Click button "View Family Enrollment Message" + await page.locator('button.enrollment-message-btn').click(); + + const modal = new Modal(page); + await expect(modal.toLocator()).toBeVisible(); + expect(await modal.toLocator().screenshot()).toMatchSnapshot('family-enrollment-message-modal.png'); +});