From 73487af964dfaa859b9325c557f5b865be2fcb03 Mon Sep 17 00:00:00 2001 From: Okechi Jones-Williams <55924431+O-Bots@users.noreply.github.com> Date: Sat, 4 Apr 2026 20:11:46 +0100 Subject: [PATCH] Changed POM/Fixture structure to use an app class to instantiate the page objects (#47) * Added Database checks to the onboarding flow * Added compatibility page setup Added more compatibility questions * Finished up the onboarding flow suite Added compatibility question tests and verifications Updated tests to cover Keywords and Headline changes recently made Updated tests to cover all of the big5 personality traits * . * Fix: Merge conflict * . * Fix: Added fix for None discriptive error issue #36 Updated signUp.spec.ts to use new fixture Updated Account information variable names Deleted "deleteUserFixture.ts" as it was incorporated into the "base.ts" file * Linting and Prettier * Minor cleaning * Organizing helper func * Added Google account to the Onboarding flow * Added account cleanup for google accounts * Started work on Sign-in tests Updated seedDatabase.ts to throw an error if the user already exists, to also add display names and usernames so they seedUser func acts like a normal basic user Some organising of the google auth code * Linting and Prettier * Added checks to the deleteUser func to check if the accout exists Added account deletion checks * Linting and Prettier * Added POM's for social and organisation page Updated settings POM * Formatting update, fixed homePage locator for signin * . * . * . * Coderabbitai fix's * Fix * Improve test utilities and stabilize onboarding flow tests * Changes requested * Seperated deletion tests from onboarding * . * Changed POM/Fixture structure to use an app class to instantiate the page objects * Apply suggestion from @MartinBraquet * Apply suggestion from @MartinBraquet * Delete .vscode/settings.json * Apply suggestion from @MartinBraquet * Apply suggestions from code review Co-authored-by: Martin Braquet * Apply suggestion from @MartinBraquet * Apply suggestion from @MartinBraquet * Linting and Prettier --------- Co-authored-by: MartinBraquet --- .coderabbit.yaml | 20 +- .../api/tests/unit/get-profiles.unit.test.ts | 2 +- docs/TESTING.md | 1 - tests/e2e/tsconfig.json | 1 + tests/e2e/utils/firebaseUtils.ts | 1 - tests/e2e/web/fixtures/base.ts | 60 +--- tests/e2e/web/fixtures/signInFixture.ts | 40 +-- tests/e2e/web/pages/app.ts | 75 +++++ .../web/pages/{AuthPage.ts => authPage.ts} | 0 tests/e2e/web/specs/onboardingFlow.spec.ts | 312 ++++++++---------- tests/e2e/web/specs/signIn.spec.ts | 57 +--- tests/e2e/web/specs/signUp.spec.ts | 35 +- tests/e2e/web/utils/testCleanupHelpers.ts | 64 ---- 13 files changed, 266 insertions(+), 402 deletions(-) create mode 100644 tests/e2e/web/pages/app.ts rename tests/e2e/web/pages/{AuthPage.ts => authPage.ts} (100%) delete mode 100644 tests/e2e/web/utils/testCleanupHelpers.ts diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 5f1a5f5a..65f5a936 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -17,7 +17,7 @@ reviews: enabled: true # Skip auto-review if PR title contains these keywords ignore_title_keywords: - - "WIP" + - 'WIP' # Don't auto-review draft PRs drafts: false # Only auto-review PRs targeting these branches @@ -57,17 +57,17 @@ reviews: # Exclude these paths from reviews (build artifacts and dependencies) path_filters: - - "!**/node_modules/**" # npm dependencies - - "!**/android/**" # Native Android build files - - "!**/ios/**" # Native iOS build files - - "!**/.expo/**" # Expo build cache - - "!**/.expo-shared/**" # Expo shared config - - "!**/dist/**" # Build output + - '!**/node_modules/**' # npm dependencies + - '!**/android/**' # Native Android build files + - '!**/ios/**' # Native iOS build files + - '!**/.expo/**' # Expo build cache + - '!**/.expo-shared/**' # Expo shared config + - '!**/dist/**' # Build output # Custom review instructions for specific file patterns path_instructions: # TypeScript/JavaScript files - main app code - - path: "**/*.{ts,tsx,js,jsx}" + - path: '**/*.{ts,tsx,js,jsx}' instructions: | General practices: - Summarize the changes clearly. @@ -78,7 +78,7 @@ reviews: - Flag any hardcoded strings; they should be in the constants file. - Check for edge cases like null values or empty arrays. - Suggest performance optimizations where appropriate. - + Mobile best practices: - Proper use of hooks (useRouter, useFonts, useAssets) - Accessibility: touch targets min 44x44, screen reader support @@ -105,7 +105,7 @@ reviews: const message = t('key', 'english string') ``` - - path: "tests/e2e/**/*.ts" + - path: 'tests/e2e/**/*.ts' instructions: | Playwright E2E test guidelines for this repo: - Page objects live in `tests/e2e/web/pages/`. Each class wraps one page/route, holds only `private readonly` Locators, and exposes action methods. diff --git a/backend/api/tests/unit/get-profiles.unit.test.ts b/backend/api/tests/unit/get-profiles.unit.test.ts index 2fdc7995..1e1e8139 100644 --- a/backend/api/tests/unit/get-profiles.unit.test.ts +++ b/backend/api/tests/unit/get-profiles.unit.test.ts @@ -316,7 +316,7 @@ describe('loadProfiles', () => { }) describe('when an error occurs', () => { - it('throw if there is no compatability', async () => { + it('throw if there is no compatibility', async () => { const props = { orderBy: 'compatibility_score', } diff --git a/docs/TESTING.md b/docs/TESTING.md index 7a71f563..e84955b2 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -348,7 +348,6 @@ jest.mock('path/to/module') * This creates an object containing all named exports from ./path/to/module */ import * as mockModule from 'path/to/module' - ;(mockModule.module as jest.Mock).mockResolvedValue(mockReturnValue) ``` diff --git a/tests/e2e/tsconfig.json b/tests/e2e/tsconfig.json index 6bcaa938..a4583100 100644 --- a/tests/e2e/tsconfig.json +++ b/tests/e2e/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "rootDir": "../../", "module": "commonjs", "jsx": "react-jsx", "moduleResolution": "node", diff --git a/tests/e2e/utils/firebaseUtils.ts b/tests/e2e/utils/firebaseUtils.ts index a7c8f0ef..a68d468c 100644 --- a/tests/e2e/utils/firebaseUtils.ts +++ b/tests/e2e/utils/firebaseUtils.ts @@ -1,5 +1,4 @@ import axios from 'axios' - import {config} from '../web/SPEC_CONFIG' export async function firebaseLoginEmailPassword( diff --git a/tests/e2e/web/fixtures/base.ts b/tests/e2e/web/fixtures/base.ts index fcd40d17..4fe2f1dd 100644 --- a/tests/e2e/web/fixtures/base.ts +++ b/tests/e2e/web/fixtures/base.ts @@ -1,29 +1,11 @@ import {test as base} from '@playwright/test' - -import {AuthPage} from '../pages/AuthPage' -import {ComatibilityPage} from '../pages/compatibilityPage' -import {HomePage} from '../pages/homePage' -import {OnboardingPage} from '../pages/onboardingPage' -import {OrganizationPage} from '../pages/organizationPage' -import {ProfilePage} from '../pages/profilePage' -import {SettingsPage} from '../pages/settingsPage' -import {SignUpPage} from '../pages/signUpPage' -import {SocialPage} from '../pages/socialPage' import {testAccounts, UserAccountInformation} from '../utils/accountInformation' import {deleteUser} from '../utils/deleteUser' import {getAuthAccountInfo} from '../utils/networkUtils' +import {App} from '../pages/app' export const test = base.extend<{ - homePage: HomePage - onboardingPage: OnboardingPage - signUpPage: SignUpPage - profilePage: ProfilePage - authPage: AuthPage - settingsPage: SettingsPage - socialPage: SocialPage - organizationPage: OrganizationPage - compatabilityPage: ComatibilityPage - cleanUpUsers: void + app: App onboardingAccount: UserAccountInformation fakerAccount: UserAccountInformation specAccount: UserAccountInformation @@ -62,41 +44,9 @@ export const test = base.extend<{ console.log('Cleaning up spec account...') await deleteUser('Email/Password', account) }, - onboardingPage: async ({page}, use) => { - const onboardingPage = new OnboardingPage(page) - await use(onboardingPage) - }, - homePage: async ({page}, use) => { - const homePage = new HomePage(page) - await use(homePage) - }, - signUpPage: async ({page}, use) => { - const signUpPage = new SignUpPage(page) - await use(signUpPage) - }, - authPage: async ({page}, use) => { - const authPage = new AuthPage(page) - await use(authPage) - }, - profilePage: async ({page}, use) => { - const profilePage = new ProfilePage(page) - await use(profilePage) - }, - compatabilityPage: async ({page}, use) => { - const compatibilityPage = new ComatibilityPage(page) - await use(compatibilityPage) - }, - settingsPage: async ({page}, use) => { - const settingsPage = new SettingsPage(page) - await use(settingsPage) - }, - socialPage: async ({page}, use) => { - const socialPage = new SocialPage(page) - await use(socialPage) - }, - organizationPage: async ({page}, use) => { - const organizationPage = new OrganizationPage(page) - await use(organizationPage) + app: async ({page}, use) => { + const appPage = new App(page) + await use(appPage) }, }) diff --git a/tests/e2e/web/fixtures/signInFixture.ts b/tests/e2e/web/fixtures/signInFixture.ts index 47c5b727..4167054f 100644 --- a/tests/e2e/web/fixtures/signInFixture.ts +++ b/tests/e2e/web/fixtures/signInFixture.ts @@ -1,47 +1,17 @@ import {test as base} from '@playwright/test' -import {AuthPage} from '../pages/AuthPage' -import {HomePage} from '../pages/homePage' +import {App} from '../pages/app' import {testAccounts, UserAccountInformation} from '../utils/accountInformation' -import { OnboardingPage } from '../pages/onboardingPage' -import { SignUpPage } from '../pages/signUpPage' -import { ProfilePage } from '../pages/profilePage' -import { SettingsPage } from '../pages/settingsPage' export const test = base.extend<{ - homePage: HomePage - onboardingPage: OnboardingPage - signUpPage: SignUpPage - profilePage: ProfilePage - settingsPage: SettingsPage - authPage: AuthPage + app: App dev_one_account: UserAccountInformation fakerAccount: UserAccountInformation googleAccountOne: UserAccountInformation googleAccountTwo: UserAccountInformation }>({ - homePage: async ({page}, use) => { - const homePage = new HomePage(page) - await use(homePage) - }, - onboardingPage: async ({page}, use) => { - const onboardingPage = new OnboardingPage(page) - await use(onboardingPage) - }, - signUpPage: async ({page}, use) => { - const signUpPage = new SignUpPage(page) - await use(signUpPage) - }, - profilePage: async ({page}, use) => { - const profilePage = new ProfilePage(page) - await use(profilePage) - }, - settingsPage: async ({page}, use) => { - const settingsPage = new SettingsPage(page) - await use(settingsPage) - }, - authPage: async ({page}, use) => { - const authPage = new AuthPage(page) - await use(authPage) + app: async ({page}, use) => { + const appPage = new App(page) + await use(appPage) }, dev_one_account: async ({}, use) => { const account = testAccounts.dev_one_account() diff --git a/tests/e2e/web/pages/app.ts b/tests/e2e/web/pages/app.ts new file mode 100644 index 00000000..d8ed56c3 --- /dev/null +++ b/tests/e2e/web/pages/app.ts @@ -0,0 +1,75 @@ +import {Page} from '@playwright/test' +import {UserAccountInformation} from '../utils/accountInformation' +import {AuthPage} from './authPage' +import {ComatibilityPage} from './compatibilityPage' +import {HomePage} from './homePage' +import {OnboardingPage} from './onboardingPage' +import {OrganizationPage} from './organizationPage' +import {ProfilePage} from './profilePage' +import {SettingsPage} from './settingsPage' +import {SignUpPage} from './signUpPage' +import {SocialPage} from './socialPage' + +export class App { + readonly auth: AuthPage + readonly compatibility: ComatibilityPage + readonly home: HomePage + readonly onboarding: OnboardingPage + readonly organization: OrganizationPage + readonly profile: ProfilePage + readonly settings: SettingsPage + readonly signUp: SignUpPage + readonly social: SocialPage + + constructor(public readonly page: Page) { + this.auth = new AuthPage(page) + this.compatibility = new ComatibilityPage(page) + this.home = new HomePage(page) + this.onboarding = new OnboardingPage(page) + this.organization = new OrganizationPage(page) + this.profile = new ProfilePage(page) + this.settings = new SettingsPage(page) + this.signUp = new SignUpPage(page) + this.social = new SocialPage(page) + } + + async deleteProfileFromSettings() { + await this.home.clickSettingsLink() + await this.settings.clickDeleteAccountButton() + await this.settings.fillDeleteAccountSurvey('Delete me') + await this.settings.clickDeleteAccountButton() + await this.home.verifyHomePageLinks() + } + + async skipOnboardingHeadToProfile(account: UserAccountInformation) { + await this.onboarding.clickSkipOnboardingButton() + await this.signUp.fillDisplayName(account.display_name) + await this.signUp.fillUsername(account.username) + await this.signUp.clickNextButton() + await this.signUp.clickNextButton() + await this.profile.clickCloseButton() + await this.onboarding.clickRefineProfileButton() + } + + async registerWithEmail(account: UserAccountInformation) { + await this.home.goToRegisterPage() + await this.auth.fillEmailField(account.email) + await this.auth.fillPasswordField(account.password) + await this.auth.clickSignUpWithEmailButton() + } + + async signinWithEmail(accountOrEmail: UserAccountInformation | string, password?: string) { + const email = typeof accountOrEmail === 'string' ? accountOrEmail : accountOrEmail.email + + const resolvedPassword = typeof accountOrEmail === 'string' ? password : accountOrEmail.password + + if (!email || !resolvedPassword) { + throw new Error('Provide either an `account` or `email` and `password`.') + } + + await this.home.goToSigninPage() + await this.auth.fillEmailField(email) + await this.auth.fillPasswordField(resolvedPassword) + await this.auth.clickSignInWithEmailButton() + } +} diff --git a/tests/e2e/web/pages/AuthPage.ts b/tests/e2e/web/pages/authPage.ts similarity index 100% rename from tests/e2e/web/pages/AuthPage.ts rename to tests/e2e/web/pages/authPage.ts diff --git a/tests/e2e/web/specs/onboardingFlow.spec.ts b/tests/e2e/web/specs/onboardingFlow.spec.ts index a433ee85..f26d490c 100644 --- a/tests/e2e/web/specs/onboardingFlow.spec.ts +++ b/tests/e2e/web/specs/onboardingFlow.spec.ts @@ -1,138 +1,133 @@ import {userInformationFromDb} from '../../utils/databaseUtils' import {expect, test} from '../fixtures/base' -import {registerWithEmail, skipOnboardingHeadToProfile} from '../utils/testCleanupHelpers' test.describe('when given valid input', () => { test('should successfully complete the onboarding flow with email', async ({ - homePage, - onboardingPage, - signUpPage, - authPage, - profilePage, + app, onboardingAccount, }) => { - await registerWithEmail(homePage, authPage, onboardingAccount) - await onboardingPage.clickContinueButton() //First continue - await onboardingPage.clickContinueButton() //Second continue - await onboardingPage.clickGetStartedButton() - await signUpPage.fillDisplayName(onboardingAccount.display_name) - await signUpPage.fillUsername(onboardingAccount.username) - await signUpPage.clickNextButton() - await signUpPage.chooseGender(onboardingAccount.gender) - await signUpPage.fillAge(onboardingAccount.age) - await signUpPage.fillHeight({ + await app.registerWithEmail(onboardingAccount) + await app.onboarding.clickContinueButton() //First continue + await app.onboarding.clickContinueButton() //Second continue + await app.onboarding.clickGetStartedButton() + await app.signUp.fillDisplayName(onboardingAccount.display_name) + await app.signUp.fillUsername(onboardingAccount.username) + await app.signUp.clickNextButton() + await app.signUp.chooseGender(onboardingAccount.gender) + await app.signUp.fillAge(onboardingAccount.age) + await app.signUp.fillHeight({ feet: onboardingAccount.height?.feet, inches: onboardingAccount.height?.inches, }) - await signUpPage.fillEthnicity(onboardingAccount.ethnicity_origin) - await signUpPage.fillHeadline(onboardingAccount.headline) - await signUpPage.fillKeywords(onboardingAccount.keywords) - await signUpPage.fillInterestedInConnectingWith(onboardingAccount.interested_in) - await signUpPage.fillAgeRangeInterest( + await app.signUp.fillEthnicity(onboardingAccount.ethnicity_origin) + await app.signUp.fillHeadline(onboardingAccount.headline) + await app.signUp.fillKeywords(onboardingAccount.keywords) + await app.signUp.fillInterestedInConnectingWith(onboardingAccount.interested_in) + await app.signUp.fillAgeRangeInterest( onboardingAccount.Interested_in_ages?.min, onboardingAccount.Interested_in_ages?.max, ) - await signUpPage.setConnectionType(onboardingAccount.connection_type) - await signUpPage.setRelationshipStatus(onboardingAccount.relationship_status) - await signUpPage.setRelationshipStyle(onboardingAccount.relationship_style) - await signUpPage.fillCurrentNumberOfChildren(onboardingAccount.number_of_kids) - await signUpPage.setWantChildrenExpectation(onboardingAccount.children_expectation) - await signUpPage.setInterests(onboardingAccount.interests) - await signUpPage.setCauses(onboardingAccount.causes) - await signUpPage.setHighestEducationLevel(onboardingAccount.education_level) - await signUpPage.fillUniversity(onboardingAccount.university) - await signUpPage.fillJobTitle(onboardingAccount.job_title) - await signUpPage.fillCompany(onboardingAccount.company) - await signUpPage.setWorkArea(onboardingAccount.work_area) - await signUpPage.setPoliticalBeliefs( + await app.signUp.setConnectionType(onboardingAccount.connection_type) + await app.signUp.setRelationshipStatus(onboardingAccount.relationship_status) + await app.signUp.setRelationshipStyle(onboardingAccount.relationship_style) + await app.signUp.fillCurrentNumberOfChildren(onboardingAccount.number_of_kids) + await app.signUp.setWantChildrenExpectation(onboardingAccount.children_expectation) + await app.signUp.setInterests(onboardingAccount.interests) + await app.signUp.setCauses(onboardingAccount.causes) + await app.signUp.setHighestEducationLevel(onboardingAccount.education_level) + await app.signUp.fillUniversity(onboardingAccount.university) + await app.signUp.fillJobTitle(onboardingAccount.job_title) + await app.signUp.fillCompany(onboardingAccount.company) + await app.signUp.setWorkArea(onboardingAccount.work_area) + await app.signUp.setPoliticalBeliefs( onboardingAccount.beliefs?.political?.belief, onboardingAccount.beliefs?.political?.details, ) - await signUpPage.setReligiousBeliefs( + await app.signUp.setReligiousBeliefs( onboardingAccount.beliefs?.religious?.belief, onboardingAccount.beliefs?.religious?.details, ) - await signUpPage.setPersonalityType(onboardingAccount.personality_type) - await signUpPage.setOpennessPersonalityValue( + await app.signUp.setPersonalityType(onboardingAccount.personality_type) + await app.signUp.setOpennessPersonalityValue( onboardingAccount.big_five_personality_traits?.openness, ) - await signUpPage.setAgreeablenessPersonalityValue( + await app.signUp.setAgreeablenessPersonalityValue( onboardingAccount.big_five_personality_traits?.agreeableness, ) - await signUpPage.setConscientiousnessPersonalityValue( + await app.signUp.setConscientiousnessPersonalityValue( onboardingAccount.big_five_personality_traits?.conscientiousness, ) - await signUpPage.setExtraversionPersonalityValue( + await app.signUp.setExtraversionPersonalityValue( onboardingAccount.big_five_personality_traits?.extraversion, ) - await signUpPage.setNeuroticismPersonalityValue( + await app.signUp.setNeuroticismPersonalityValue( onboardingAccount.big_five_personality_traits?.neuroticism, ) - await signUpPage.setDietType(onboardingAccount.diet) - await signUpPage.setIsSmoker(onboardingAccount.is_smoker) - await signUpPage.fillAlcoholPerMonth(onboardingAccount.alcohol_consumed_per_month) - await signUpPage.setLanguages(onboardingAccount.languages) - await signUpPage.addSocialMediaPlatform(onboardingAccount.social_media) - await signUpPage.fillBio(onboardingAccount.bio) - await signUpPage.clickNextButton() - await profilePage.clickCloseButton() - await onboardingPage.clickRefineProfileButton() - await profilePage.clickAnswerQuestionsButton() - const compatQuestionOne = await profilePage.answerCompatibilityQuestion( + await app.signUp.setDietType(onboardingAccount.diet) + await app.signUp.setIsSmoker(onboardingAccount.is_smoker) + await app.signUp.fillAlcoholPerMonth(onboardingAccount.alcohol_consumed_per_month) + await app.signUp.setLanguages(onboardingAccount.languages) + await app.signUp.addSocialMediaPlatform(onboardingAccount.social_media) + await app.signUp.fillBio(onboardingAccount.bio) + await app.signUp.clickNextButton() + await app.profile.clickCloseButton() + await app.onboarding.clickRefineProfileButton() + await app.profile.clickAnswerQuestionsButton() + const compatQuestionOne = await app.profile.answerCompatibilityQuestion( onboardingAccount.compatibility, ) - await profilePage.clickNextCompatibilityQuestionButton() - await profilePage.clickSkipCompatibilityQuestionButton() - await profilePage.clickSkipCompatibilityQuestionButton() + await app.profile.clickNextCompatibilityQuestionButton() + await app.profile.clickSkipCompatibilityQuestionButton() + await app.profile.clickSkipCompatibilityQuestionButton() - await profilePage.clickCloseButton() + await app.profile.clickCloseButton() //Verify information is correct - await profilePage.verifyDisplayName(onboardingAccount.display_name) - await profilePage.verifyHeadline(onboardingAccount.headline) - await profilePage.verifyKeywords(onboardingAccount.keywords) - await profilePage.verifyGenderLocationHeightAge( + await app.profile.verifyDisplayName(onboardingAccount.display_name) + await app.profile.verifyHeadline(onboardingAccount.headline) + await app.profile.verifyKeywords(onboardingAccount.keywords) + await app.profile.verifyGenderLocationHeightAge( onboardingAccount.gender, undefined, onboardingAccount.height?.feet, onboardingAccount.height?.inches, onboardingAccount.age, ) - await profilePage.verifySeeking( + await app.profile.verifySeeking( onboardingAccount.interested_in, onboardingAccount.Interested_in_ages?.min, onboardingAccount.Interested_in_ages?.max, onboardingAccount.connection_type, onboardingAccount.relationship_style, ) - await profilePage.verifyRelationshipStatus(onboardingAccount.relationship_status) - await profilePage.verifyCurrentNumberOfKids(onboardingAccount.number_of_kids) - await profilePage.verifyWantChildrenExpectation(onboardingAccount.children_expectation) - await profilePage.verifyInterests(onboardingAccount.interests) - await profilePage.verifyCauses(onboardingAccount.causes) - await profilePage.verifyEducationLevelAndUniversity( + await app.profile.verifyRelationshipStatus(onboardingAccount.relationship_status) + await app.profile.verifyCurrentNumberOfKids(onboardingAccount.number_of_kids) + await app.profile.verifyWantChildrenExpectation(onboardingAccount.children_expectation) + await app.profile.verifyInterests(onboardingAccount.interests) + await app.profile.verifyCauses(onboardingAccount.causes) + await app.profile.verifyEducationLevelAndUniversity( onboardingAccount.education_level, onboardingAccount.university, ) - await profilePage.verifyJobInformation(onboardingAccount.job_title, onboardingAccount.company) - await profilePage.verifyWorkArea(onboardingAccount.work_area) - await profilePage.verifyPoliticalBeliefs( + await app.profile.verifyJobInformation(onboardingAccount.job_title, onboardingAccount.company) + await app.profile.verifyWorkArea(onboardingAccount.work_area) + await app.profile.verifyPoliticalBeliefs( onboardingAccount.beliefs?.political?.belief, onboardingAccount.beliefs?.political?.details, ) - await profilePage.verifyReligiousBeliefs( + await app.profile.verifyReligiousBeliefs( onboardingAccount.beliefs?.religious?.belief, onboardingAccount.beliefs?.religious?.details, ) - await profilePage.verifyPersonalityType(onboardingAccount.personality_type) - await profilePage.verifyBigFivePersonalitySection(onboardingAccount.big_five_personality_traits) - await profilePage.verifyDiet(onboardingAccount.diet) - await profilePage.verifySmoker(onboardingAccount.is_smoker) - await profilePage.verifyDrinksPerMonth(onboardingAccount.alcohol_consumed_per_month) - await profilePage.verifyLanguages(onboardingAccount.languages) - await profilePage.verifySocialMedia(onboardingAccount.social_media) - await profilePage.verifyBio(onboardingAccount.bio) - await profilePage.verifyCompatibilityAnswers(compatQuestionOne) + await app.profile.verifyPersonalityType(onboardingAccount.personality_type) + await app.profile.verifyBigFivePersonalitySection(onboardingAccount.big_five_personality_traits) + await app.profile.verifyDiet(onboardingAccount.diet) + await app.profile.verifySmoker(onboardingAccount.is_smoker) + await app.profile.verifyDrinksPerMonth(onboardingAccount.alcohol_consumed_per_month) + await app.profile.verifyLanguages(onboardingAccount.languages) + await app.profile.verifySocialMedia(onboardingAccount.social_media) + await app.profile.verifyBio(onboardingAccount.bio) + await app.profile.verifyCompatibilityAnswers(compatQuestionOne) //Verify Database Information const dbInfo = await userInformationFromDb(onboardingAccount) @@ -219,26 +214,22 @@ test.describe('when given valid input', () => { }) test('should successfully complete the onboarding flow with google account', async ({ - homePage, - onboardingPage, - signUpPage, - authPage, - profilePage, + app, googleAccountOne, headless, }) => { test.skip(headless, 'Google popup auth test requires headed mode') - await homePage.goToRegisterPage() - await authPage.fillPasswordField('') //The test only passes when this is added...something is weird here - await authPage.signInToGoogleAccount( + await app.home.goToRegisterPage() + await app.auth.fillPasswordField('') //The test only passes when this is added...something is weird here + await app.auth.signInToGoogleAccount( googleAccountOne.email, googleAccountOne.display_name, googleAccountOne.username, ) - await skipOnboardingHeadToProfile(onboardingPage, signUpPage, profilePage, googleAccountOne) + await app.skipOnboardingHeadToProfile(googleAccountOne) //Verify displayed information is correct - await profilePage.verifyDisplayName(googleAccountOne.display_name) + await app.profile.verifyDisplayName(googleAccountOne.display_name) //Verify database info const dbInfo = await userInformationFromDb(googleAccountOne) @@ -247,19 +238,12 @@ test.describe('when given valid input', () => { await expect(dbInfo.user.username).toContain(googleAccountOne.username) }) - test('should successfully skip the onboarding flow', async ({ - homePage, - onboardingPage, - signUpPage, - authPage, - profilePage, - fakerAccount, - }) => { - await registerWithEmail(homePage, authPage, fakerAccount) - await skipOnboardingHeadToProfile(onboardingPage, signUpPage, profilePage, fakerAccount) + test('should successfully skip the onboarding flow', async ({app, fakerAccount}) => { + await app.registerWithEmail(fakerAccount) + await app.skipOnboardingHeadToProfile(fakerAccount) //Verify displayed information is correct - await profilePage.verifyDisplayName(fakerAccount.display_name) + await app.profile.verifyDisplayName(fakerAccount.display_name) //Verify database info const dbInfo = await userInformationFromDb(fakerAccount) @@ -269,27 +253,23 @@ test.describe('when given valid input', () => { }) test('should successfully enter optional information after completing flow', async ({ - homePage, - onboardingPage, - signUpPage, - authPage, - profilePage, + app, fakerAccount, }) => { - await registerWithEmail(homePage, authPage, fakerAccount) - await skipOnboardingHeadToProfile(onboardingPage, signUpPage, profilePage, fakerAccount) - await profilePage.clickEditProfileButton() - await signUpPage.chooseGender(fakerAccount.gender) - await signUpPage.fillAge(fakerAccount.age) - await signUpPage.fillHeight({ + await app.registerWithEmail(fakerAccount) + await app.skipOnboardingHeadToProfile(fakerAccount) + await app.profile.clickEditProfileButton() + await app.signUp.chooseGender(fakerAccount.gender) + await app.signUp.fillAge(fakerAccount.age) + await app.signUp.fillHeight({ feet: fakerAccount.height?.feet, inches: fakerAccount.height?.inches, }) - await signUpPage.saveProfileChanges() + await app.signUp.saveProfileChanges() //Verify displayed information is correct - await profilePage.verifyDisplayName(fakerAccount.display_name) - await profilePage.verifyGenderLocationHeightAge( + await app.profile.verifyDisplayName(fakerAccount.display_name) + await app.profile.verifyGenderLocationHeightAge( fakerAccount.gender, undefined, fakerAccount.height?.feet, @@ -308,31 +288,27 @@ test.describe('when given valid input', () => { }) test('should successfully use the start answering option', async ({ - homePage, - onboardingPage, - signUpPage, - authPage, - profilePage, + app, fakerAccount, onboardingAccount, }) => { - await registerWithEmail(homePage, authPage, fakerAccount) - await onboardingPage.clickSkipOnboardingButton() - await signUpPage.fillDisplayName(fakerAccount.display_name) - await signUpPage.fillUsername(fakerAccount.username) - await signUpPage.clickNextButton() - await signUpPage.clickNextButton() //Skip optional information - await profilePage.clickStartAnsweringButton() - const compatTwoQuestionOne = await profilePage.answerCompatibilityQuestion( + await app.registerWithEmail(fakerAccount) + await app.onboarding.clickSkipOnboardingButton() + await app.signUp.fillDisplayName(fakerAccount.display_name) + await app.signUp.fillUsername(fakerAccount.username) + await app.signUp.clickNextButton() + await app.signUp.clickNextButton() //Skip optional information + await app.profile.clickStartAnsweringButton() + const compatTwoQuestionOne = await app.profile.answerCompatibilityQuestion( onboardingAccount.compatibility, ) - await profilePage.clickNextCompatibilityQuestionButton() - await profilePage.clickCloseButton() - await onboardingPage.clickRefineProfileButton() + await app.profile.clickNextCompatibilityQuestionButton() + await app.profile.clickCloseButton() + await app.onboarding.clickRefineProfileButton() //Verify displayed information is correct - await profilePage.verifyDisplayName(fakerAccount.display_name) - await profilePage.verifyCompatibilityAnswers(compatTwoQuestionOne) + await app.profile.verifyDisplayName(fakerAccount.display_name) + await app.profile.verifyCompatibilityAnswers(compatTwoQuestionOne) //Verify database info const dbInfo = await userInformationFromDb(fakerAccount) @@ -342,29 +318,22 @@ test.describe('when given valid input', () => { }) test.describe('should successfully complete the onboarding flow after using the back button', () => { - test("the first time it's an option", async ({ - homePage, - authPage, - onboardingPage, - signUpPage, - profilePage, - fakerAccount, - }) => { - await registerWithEmail(homePage, authPage, fakerAccount) - await onboardingPage.clickContinueButton() - await onboardingPage.clickBackButton() - await onboardingPage.clickContinueButton() - await onboardingPage.clickContinueButton() - await onboardingPage.clickGetStartedButton() - await signUpPage.fillDisplayName(fakerAccount.display_name) - await signUpPage.fillUsername(fakerAccount.username) - await signUpPage.clickNextButton() - await signUpPage.clickNextButton() //Skip optional information - await profilePage.clickCloseButton() - await onboardingPage.clickRefineProfileButton() + test("the first time it's an option", async ({app, fakerAccount}) => { + await app.registerWithEmail(fakerAccount) + await app.onboarding.clickContinueButton() + await app.onboarding.clickBackButton() + await app.onboarding.clickContinueButton() + await app.onboarding.clickContinueButton() + await app.onboarding.clickGetStartedButton() + await app.signUp.fillDisplayName(fakerAccount.display_name) + await app.signUp.fillUsername(fakerAccount.username) + await app.signUp.clickNextButton() + await app.signUp.clickNextButton() //Skip optional information + await app.profile.clickCloseButton() + await app.onboarding.clickRefineProfileButton() //Verify displayed information is correct - await profilePage.verifyDisplayName(fakerAccount.display_name) + await app.profile.verifyDisplayName(fakerAccount.display_name) //Verify database info const dbInfo = await userInformationFromDb(fakerAccount) @@ -373,29 +342,22 @@ test.describe('when given valid input', () => { await expect(dbInfo.user.username).toContain(fakerAccount.username) }) - test("the second time it's an option", async ({ - homePage, - authPage, - onboardingPage, - signUpPage, - profilePage, - fakerAccount, - }) => { - await registerWithEmail(homePage, authPage, fakerAccount) - await onboardingPage.clickContinueButton() - await onboardingPage.clickContinueButton() - await onboardingPage.clickBackButton() - await onboardingPage.clickContinueButton() - await onboardingPage.clickGetStartedButton() - await signUpPage.fillDisplayName(fakerAccount.display_name) - await signUpPage.fillUsername(fakerAccount.username) - await signUpPage.clickNextButton() - await signUpPage.clickNextButton() //Skip optional information - await profilePage.clickCloseButton() - await onboardingPage.clickRefineProfileButton() + test("the second time it's an option", async ({app, fakerAccount}) => { + await app.registerWithEmail(fakerAccount) + await app.onboarding.clickContinueButton() + await app.onboarding.clickContinueButton() + await app.onboarding.clickBackButton() + await app.onboarding.clickContinueButton() + await app.onboarding.clickGetStartedButton() + await app.signUp.fillDisplayName(fakerAccount.display_name) + await app.signUp.fillUsername(fakerAccount.username) + await app.signUp.clickNextButton() + await app.signUp.clickNextButton() //Skip optional information + await app.profile.clickCloseButton() + await app.onboarding.clickRefineProfileButton() //Verify displayed information is correct - await profilePage.verifyDisplayName(fakerAccount.display_name) + await app.profile.verifyDisplayName(fakerAccount.display_name) //Verify database info const dbInfo = await userInformationFromDb(fakerAccount) diff --git a/tests/e2e/web/specs/signIn.spec.ts b/tests/e2e/web/specs/signIn.spec.ts index a2755816..2c602ac2 100644 --- a/tests/e2e/web/specs/signIn.spec.ts +++ b/tests/e2e/web/specs/signIn.spec.ts @@ -2,12 +2,6 @@ import {userInformationFromDb} from '../../utils/databaseUtils' import {seedUser} from '../../utils/seedDatabase' import {expect, test} from '../fixtures/signInFixture' import {testAccounts} from '../utils/accountInformation' -import { - deleteProfileFromSettings, - registerWithEmail, - signinWithEmail, - skipOnboardingHeadToProfile, -} from '../utils/testCleanupHelpers' //Seed the account test.beforeAll(async () => { @@ -26,30 +20,21 @@ test.beforeAll(async () => { }) test.describe('when given valid input', () => { - test('should be able to sign in to an available account', async ({ - homePage, - authPage, - dev_one_account, - }) => { - await signinWithEmail(homePage, authPage, dev_one_account) - await homePage.goToHomePage() - await homePage.verifySignedInHomePage(dev_one_account.display_name) + test('should be able to sign in to an available account', async ({app, dev_one_account}) => { + await app.signinWithEmail(dev_one_account) + await app.home.goToHomePage() + await app.home.verifySignedInHomePage(dev_one_account.display_name) }) test('should successfully delete an account created via email and password', async ({ - homePage, - onboardingPage, - signUpPage, - authPage, - profilePage, - settingsPage, + app, fakerAccount, }) => { - await registerWithEmail(homePage, authPage, fakerAccount) - await skipOnboardingHeadToProfile(onboardingPage, signUpPage, profilePage, fakerAccount) + await app.registerWithEmail(fakerAccount) + await app.skipOnboardingHeadToProfile(fakerAccount) //Verify displayed information is correct - await profilePage.verifyDisplayName(fakerAccount.display_name) + await app.profile.verifyDisplayName(fakerAccount.display_name) //Verify database info const dbInfo = await userInformationFromDb(fakerAccount) @@ -57,31 +42,26 @@ test.describe('when given valid input', () => { await expect(dbInfo.user.name).toContain(fakerAccount.display_name) await expect(dbInfo.user.username).toContain(fakerAccount.username) - await deleteProfileFromSettings(homePage, settingsPage) + await app.deleteProfileFromSettings() }) test('should successfully delete an account created via google auth', async ({ - homePage, - onboardingPage, - signUpPage, - authPage, - profilePage, - settingsPage, + app, googleAccountTwo, headless, }) => { test.skip(headless, 'Google popup auth test requires headed mode') - await homePage.goToRegisterPage() - await authPage.fillPasswordField('') //The test only passes when this is added...something is weird here - await authPage.signInToGoogleAccount( + await app.home.goToRegisterPage() + await app.auth.fillPasswordField('') //The test only passes when this is added...something is weird here + await app.auth.signInToGoogleAccount( googleAccountTwo.email, googleAccountTwo.display_name, googleAccountTwo.username, ) - await skipOnboardingHeadToProfile(onboardingPage, signUpPage, profilePage, googleAccountTwo) + await app.skipOnboardingHeadToProfile(googleAccountTwo) //Verify displayed information is correct - await profilePage.verifyDisplayName(googleAccountTwo.display_name) + await app.profile.verifyDisplayName(googleAccountTwo.display_name) //Verify database info const dbInfo = await userInformationFromDb(googleAccountTwo) @@ -89,18 +69,17 @@ test.describe('when given valid input', () => { await expect(dbInfo.user.name).toContain(googleAccountTwo.display_name) await expect(dbInfo.user.username).toContain(googleAccountTwo.username) - await deleteProfileFromSettings(homePage, settingsPage) + await app.deleteProfileFromSettings() }) }) test.describe('when given invalid input', () => { test('should not be able to sign in to an available account', async ({ - homePage, - authPage, + app, dev_one_account, page, }) => { - await signinWithEmail(homePage, authPage, dev_one_account.email, 'ThisPassword') + await app.signinWithEmail(dev_one_account.email, 'ThisPassword') await expect( page.getByText('Failed to sign in with your email and password', {exact: true}), ).toBeVisible() diff --git a/tests/e2e/web/specs/signUp.spec.ts b/tests/e2e/web/specs/signUp.spec.ts index 65f2afbe..53562129 100644 --- a/tests/e2e/web/specs/signUp.spec.ts +++ b/tests/e2e/web/specs/signUp.spec.ts @@ -1,5 +1,4 @@ import {expect, test} from '../fixtures/base' -import {registerWithEmail} from '../utils/testCleanupHelpers' // test.describe('when given valid input', () => { // test('placeholder', async () => {}) @@ -8,31 +7,25 @@ import {registerWithEmail} from '../utils/testCleanupHelpers' test.describe('when an error occurs', () => { test('should disable the button "Next" when the display name field is empty', async ({ specAccount, - homePage, - authPage, - onboardingPage, - signUpPage, + app, }) => { - await registerWithEmail(homePage, authPage, specAccount) - await onboardingPage.clickSkipOnboardingButton() - await signUpPage.fillDisplayName('') - await signUpPage.fillUsername(specAccount.username) - await signUpPage.verifyDisplayNameError() - await expect(signUpPage.nextButtonLocator).toBeDisabled() + await app.registerWithEmail(specAccount) + await app.onboarding.clickSkipOnboardingButton() + await app.signUp.fillDisplayName('') + await app.signUp.fillUsername(specAccount.username) + await app.signUp.verifyDisplayNameError() + await expect(app.signUp.nextButtonLocator).toBeDisabled() }) test('should disable the button "Next" when the username field is empty', async ({ specAccount, - homePage, - authPage, - onboardingPage, - signUpPage, + app, }) => { - await registerWithEmail(homePage, authPage, specAccount) - await onboardingPage.clickSkipOnboardingButton() - await signUpPage.fillDisplayName(specAccount.display_name) - await signUpPage.fillUsername('') - await signUpPage.verifyUsernameError() - await expect(signUpPage.nextButtonLocator).toBeDisabled() + await app.registerWithEmail(specAccount) + await app.onboarding.clickSkipOnboardingButton() + await app.signUp.fillDisplayName(specAccount.display_name) + await app.signUp.fillUsername('') + await app.signUp.verifyUsernameError() + await expect(app.signUp.nextButtonLocator).toBeDisabled() }) }) diff --git a/tests/e2e/web/utils/testCleanupHelpers.ts b/tests/e2e/web/utils/testCleanupHelpers.ts deleted file mode 100644 index c022028d..00000000 --- a/tests/e2e/web/utils/testCleanupHelpers.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {AuthPage} from '../pages/AuthPage' -import {HomePage} from '../pages/homePage' -import { OnboardingPage } from '../pages/onboardingPage' -import { ProfilePage } from '../pages/profilePage' -import { SettingsPage } from '../pages/settingsPage' -import { SignUpPage } from '../pages/signUpPage' -import {UserAccountInformation} from '../utils/accountInformation' - -export async function registerWithEmail( - homePage: HomePage, - authPage: AuthPage, - account: UserAccountInformation, -) { - await homePage.goToRegisterPage() - await authPage.fillEmailField(account.email) - await authPage.fillPasswordField(account.password) - await authPage.clickSignUpWithEmailButton() -} - -export async function signinWithEmail( - homePage: HomePage, - authPage: AuthPage, - accountOrEmail: UserAccountInformation | string, - password?: string, -) { - const email = typeof accountOrEmail === 'string' ? accountOrEmail : accountOrEmail.email - - const resolvedPassword = typeof accountOrEmail === 'string' ? password : accountOrEmail.password - - if (!email || !resolvedPassword) { - throw new Error('Provide either an `account` or `email` and `password`.') - } - - await homePage.goToSigninPage() - await authPage.fillEmailField(email) - await authPage.fillPasswordField(resolvedPassword) - await authPage.clickSignInWithEmailButton() -} - -export async function skipOnboardingHeadToProfile( - onboardingPage: OnboardingPage, - signUpPage: SignUpPage, - profilePage: ProfilePage, - account: UserAccountInformation, -) { - await onboardingPage.clickSkipOnboardingButton() - await signUpPage.fillDisplayName(account.display_name) - await signUpPage.fillUsername(account.username) - await signUpPage.clickNextButton() - await signUpPage.clickNextButton() - await profilePage.clickCloseButton() - await onboardingPage.clickRefineProfileButton() -} - -export async function deleteProfileFromSettings( - homePage: HomePage, - settingsPage: SettingsPage, -) { - await homePage.clickSettingsLink() - await settingsPage.clickDeleteAccountButton() - await settingsPage.fillDeleteAccountSurvey('Delete me') - await settingsPage.clickDeleteAccountButton() - await homePage.verifyHomePageLinks() -} \ No newline at end of file