diff --git a/.github/workflows/cd-android-live-update.yml b/.github/workflows/cd-android-live-update.yml index 48ff11ee..292235ff 100644 --- a/.github/workflows/cd-android-live-update.yml +++ b/.github/workflows/cd-android-live-update.yml @@ -1,7 +1,7 @@ name: CD Android Live Update on: push: - branches: [ main, master ] + branches: [main, master] paths: - 'android/capawesome.json' - '.github/workflows/cd-android-live-update.yml' diff --git a/playwright.config.ts b/playwright.config.ts index cbdc6cf2..094ee5a4 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,4 +1,5 @@ import {defineConfig, devices} from '@playwright/test' +import 'tsconfig-paths/register' import {execSync} from 'child_process' import {config} from 'dotenv' diff --git a/tests/e2e/web/fixtures/base.ts b/tests/e2e/web/fixtures/base.ts index 277f5887..23ec87fc 100644 --- a/tests/e2e/web/fixtures/base.ts +++ b/tests/e2e/web/fixtures/base.ts @@ -6,7 +6,7 @@ import {HomePage} from '../pages/homePage' import {OnboardingPage} from '../pages/onboardingPage' import {ProfilePage} from '../pages/profilePage' import {SignUpPage} from '../pages/signUpPage' -import {onboarding, OnboardingUser} from '../utils/accountInformation' +import {testAccounts, UserAccountInformation} from '../utils/accountInformation' import {deleteUser} from '../utils/deleteUser' export const test = base.extend<{ @@ -17,21 +17,28 @@ export const test = base.extend<{ authPage: AuthPage compatabilityPage: ComatibilityPage cleanUpUsers: void - testAccount: OnboardingUser - fakerAccount: OnboardingUser + onboardingAccount: UserAccountInformation + fakerAccount: UserAccountInformation + specAccount: UserAccountInformation }>({ - testAccount: async ({}, use) => { - const account = onboarding.account_one() // email captured here + onboardingAccount: async ({}, use) => { + const account = testAccounts.account_all_info() // email captured here await use(account) console.log('Cleaning up onboarding 1 account...') await deleteUser(account.email, account.password) // same account, guaranteed }, fakerAccount: async ({}, use) => { - const account = onboarding.faker_account() // email captured here + const account = testAccounts.faker_account() // email captured here await use(account) console.log('Cleaning up faker account...') await deleteUser(account.email, account.password) // same account, guaranteed }, + specAccount: async ({}, use) => { + const account = testAccounts.spec_account() + await use(account) + console.log('Cleaning up spec account...') + await deleteUser(account.email, account.password) + }, onboardingPage: async ({page}, use) => { const onboardingPage = new OnboardingPage(page) await use(onboardingPage) diff --git a/tests/e2e/web/fixtures/deleteUserFixture.ts b/tests/e2e/web/fixtures/deleteUserFixture.ts deleted file mode 100644 index 583c4335..00000000 --- a/tests/e2e/web/fixtures/deleteUserFixture.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {test as base} from '@playwright/test' -import axios from 'axios' -import {config} from '../SPEC_CONFIG' - -// const baseUrl = 'http://localhost:9099/identitytoolkit.googleapis.com/v1'; - -async function deleteUser(email: string, password: string) { - try { - const login = await axios.post( - `${config.FIREBASE_URL.BASE}${config.FIREBASE_URL.SIGN_IN_PASSWORD}`, - { - email, - password, - returnSecureToken: true, - }, - ) - - await axios.post(`${config.FIREBASE_URL.BASE}${config.FIREBASE_URL.DELETE}`, { - idToken: login.data.idToken, - }) - } catch (err: any) { - // Skip deletion if user doesn't exist or other auth errors occur - if ( - err.response?.status === 400 || - err.response?.data?.error?.message?.includes('EMAIL_NOT_FOUND') - ) { - return - } - console.log(err) - } -} - -type CleanupFixtures = { - cleanupUsers: void -} - -export const test = base.extend({ - cleanupUsers: [ - async ({}, use) => { - // Run all tests first - await use() - - //then delete users - await deleteUser(config.USERS.SPEC.EMAIL, config.USERS.SPEC.PASSWORD) - }, - {auto: true}, - ], -}) diff --git a/tests/e2e/web/pages/AuthPage.ts b/tests/e2e/web/pages/AuthPage.ts index c3c56107..36b33ece 100644 --- a/tests/e2e/web/pages/AuthPage.ts +++ b/tests/e2e/web/pages/AuthPage.ts @@ -21,22 +21,27 @@ export class AuthPage { } async clickSignInLink() { + await expect(this.signInLink).toBeVisible() await this.signInLink.click() } async clickSignUpButton() { + await expect(this.signUpButton).toBeVisible() await this.signUpButton.click() } async clickSignInWithEmailButton() { + await expect(this.signInWithEmailButton).toBeVisible() await this.signInWithEmailButton.click() } async clickSignInWithGoogleButton() { + await expect(this.signInWithGoogleButton).toBeVisible() await this.signInWithGoogleButton.click() } async clickSignUpWithEmailButton() { + await expect(this.signUpWithEmailButton).toBeVisible() await this.signUpWithEmailButton.click() } diff --git a/tests/e2e/web/pages/profilePage.ts b/tests/e2e/web/pages/profilePage.ts index 1f999cc8..e6eef8cb 100644 --- a/tests/e2e/web/pages/profilePage.ts +++ b/tests/e2e/web/pages/profilePage.ts @@ -29,7 +29,6 @@ export class ProfilePage { private readonly dietAboutSection: Locator private readonly languagesAboutSection: Locator private readonly seekingAboutSection: Locator - private readonly relationshipTypeAboutSection: Locator private readonly relationshipStatusAboutSection: Locator private readonly educationAboutSection: Locator private readonly occupationAboutSection: Locator @@ -64,8 +63,8 @@ export class ProfilePage { private readonly profileCompatibilityExplanation: Locator constructor(public readonly page: Page) { - this.startAnsweringButton = page.getByRole('button', {}) - this.doThisLaterLink = page.getByRole('button', {}) + this.startAnsweringButton = page.getByRole('button', {name: 'Start answering'}) + this.doThisLaterLink = page.getByRole('button', {name: 'Do this later'}) this.closeButton = page.getByRole('button', {name: 'Close'}) this.shareButton = page.getByRole('button', {name: 'Share'}) this.editProfileButton = page.getByTestId('profile-edit') @@ -90,7 +89,6 @@ export class ProfilePage { this.dietAboutSection = page.getByTestId('profile-about-diet') this.languagesAboutSection = page.getByTestId('profile-about-languages') this.seekingAboutSection = page.getByTestId('profile-about-seeking') - this.relationshipTypeAboutSection = page.getByTestId('profile-about-seeking') this.relationshipStatusAboutSection = page.getByTestId('profile-about-relationship-status') this.educationAboutSection = page.getByTestId('profile-about-education') this.occupationAboutSection = page.getByTestId('profile-about-occupation') @@ -321,8 +319,8 @@ export class ProfilePage { async verifyDisplayName(displayName?: string) { await expect(this.displayNameAndAgeSection).toBeVisible() - const textContent = await this.displayNameAndAgeSection.textContent() - if (displayName) await expect(textContent?.toLowerCase()).toContain(displayName.toLowerCase()) + if (displayName) + await expect(this.displayNameAndAgeSection).toContainText(displayName, {ignoreCase: true}) } async verifyGenderLocationHeightAge( @@ -333,72 +331,73 @@ export class ProfilePage { age?: string, ) { await expect(this.genderLocationHightInInchesSection).toBeVisible() - const textContent = await this.genderLocationHightInInchesSection.textContent() - if (gender) await expect(textContent?.toLowerCase()).toContain(gender[0].toLowerCase()) - if (location) await expect(textContent?.toLowerCase()).toContain(location.toLowerCase()) - if (heightFeet) await expect(textContent?.toLowerCase()).toContain(heightFeet.toLowerCase()) - if (heightInches) await expect(textContent?.toLowerCase()).toContain(heightInches.toLowerCase()) - if (age) await expect(textContent?.toLowerCase()).toContain(age.toLowerCase()) + if (gender) + await expect(this.genderLocationHightInInchesSection).toContainText(gender[0], { + ignoreCase: true, + }) + if (location) + await expect(this.genderLocationHightInInchesSection).toContainText(location, { + ignoreCase: true, + }) + if (heightFeet) await expect(this.genderLocationHightInInchesSection).toContainText(heightFeet) + if (heightInches) + await expect(this.genderLocationHightInInchesSection).toContainText(heightInches) + if (age) await expect(this.genderLocationHightInInchesSection).toContainText(age) } async verifyEthnicityOrigin(origin: string) { await expect(this.ethnicityAboutSection).toBeVisible() - const textContent = await this.ethnicityAboutSection.textContent() - await expect(textContent?.toLowerCase()).toContain(origin.toLowerCase()) + await expect(this.ethnicityAboutSection).toContainText(origin, {ignoreCase: true}) } - async verifyInterestedInConnectingWith(gender?: string[], minAge?: string, maxAge?: string) { + async verifySeeking( + gender?: string[], + minAge?: string, + maxAge?: string, + type?: string[], + interest?: string[], + ) { await expect(this.seekingAboutSection).toBeVisible() - const textContent = await this.seekingAboutSection.textContent() - if (gender) await expect(textContent?.toLowerCase()).toContain(gender[0].toLowerCase()) - if (minAge) await expect(textContent?.toLowerCase()).toContain(minAge.toLowerCase()) - if (maxAge) await expect(textContent?.toLowerCase()).toContain(maxAge.toLowerCase()) - } - - async verifyRelationShipTypeAndInterest(type?: string[], interest?: string[]) { - await expect(this.relationshipTypeAboutSection).toBeVisible() - const textContent = await this.relationshipTypeAboutSection.textContent() - if (type) await expect(textContent?.toLowerCase()).toContain(type[0].toLowerCase()) - if (interest) await expect(textContent?.toLowerCase()).toContain(interest[0].toLowerCase()) + if (gender) await expect(this.seekingAboutSection).toContainText(gender[0], {ignoreCase: true}) + if (minAge) await expect(this.seekingAboutSection).toContainText(minAge) + if (maxAge) await expect(this.seekingAboutSection).toContainText(maxAge) + if (type) await expect(this.seekingAboutSection).toContainText(type[0], {ignoreCase: true}) + if (interest) + await expect(this.seekingAboutSection).toContainText(interest[0], {ignoreCase: true}) } async verifyRelationshipStatus(status: string[] | undefined) { if (!status) return await expect(this.relationshipStatusAboutSection).toBeVisible() - const textContent = await this.relationshipStatusAboutSection.textContent() - await expect(textContent?.toLowerCase()).toContain(status[0].toLowerCase()) + await expect(this.relationshipStatusAboutSection).toContainText(status[0], {ignoreCase: true}) } async verifyCurrentNumberOfKids(numberOfKids: string | undefined) { if (!numberOfKids) return await expect(this.hasKidsAboutSection).toBeVisible() - const textContent = await this.hasKidsAboutSection.textContent() - await expect(textContent?.toLowerCase()).toContain(numberOfKids.toLowerCase()) + await expect(this.hasKidsAboutSection).toContainText(numberOfKids) } async verifyWantChildrenExpectation(expectation: [string, number] | undefined) { if (!expectation) return const [label, _value] = expectation await expect(this.wantsKidsAboutSection).toBeVisible() - const textContent = await this.wantsKidsAboutSection.textContent() - await expect(textContent?.toLowerCase()).toContain(label.toLowerCase()) + await expect(this.wantsKidsAboutSection).toContainText(label, {ignoreCase: true}) } async verifyInterests(interest: string[] | undefined) { if (!interest || interest.length === 0) return await expect(this.interestsAboutSection).toBeVisible() - const textContent = await this.interestsAboutSection.textContent() for (let i = 0; i < interest.length; i++) { - await expect(textContent?.toLowerCase()).toContain(interest[i].toLowerCase()) + await expect(this.interestsAboutSection).toContainText(interest[i], {ignoreCase: true}) } } async verifyCauses(causes: string[] | undefined) { if (!causes || causes.length === 0) return await expect(this.causesAboutSection).toBeVisible() - const textContent = await this.causesAboutSection.textContent() for (let i = 0; i < causes.length; i++) { - await expect(textContent?.toLowerCase()).toContain(causes[i].toLowerCase()) + await expect(this.causesAboutSection).toContainText(causes[i], {ignoreCase: true}) } } @@ -412,86 +411,82 @@ export class ProfilePage { async verifyEducationLevelAndUniversity(educationLevel?: string[], university?: string) { await expect(this.educationAboutSection).toBeVisible() - const textContent = await this.educationAboutSection.textContent() if (educationLevel) - await expect(textContent?.toLowerCase()).toContain(educationLevel[0].toLowerCase()) - if (university) await expect(textContent?.toLowerCase()).toContain(university.toLowerCase()) + await expect(this.educationAboutSection).toContainText(educationLevel[0], {ignoreCase: true}) + if (university) + await expect(this.educationAboutSection).toContainText(university, {ignoreCase: true}) } async verifyJobInformation(jobTitle?: string, company?: string) { await expect(this.occupationAboutSection).toBeVisible() - const textContent = await this.occupationAboutSection.textContent() - if (jobTitle) await expect(textContent?.toLowerCase()).toContain(jobTitle.toLowerCase()) - if (company) await expect(textContent?.toLowerCase()).toContain(company.toLowerCase()) + if (jobTitle) + await expect(this.occupationAboutSection).toContainText(jobTitle, {ignoreCase: true}) + if (company) + await expect(this.occupationAboutSection).toContainText(company, {ignoreCase: true}) } async verifyPoliticalBeliefs(belief?: string[], details?: string) { await expect(this.politicalAboutSection).toBeVisible() - const textContent = await this.politicalAboutSection.textContent() - if (belief) await expect(textContent?.toLowerCase()).toContain(belief[0].toLowerCase()) - if (details) await expect(textContent?.toLowerCase()).toContain(details.toLowerCase()) + if (belief) + await expect(this.politicalAboutSection).toContainText(belief[0], {ignoreCase: true}) + if (details) await expect(this.politicalAboutSection).toContainText(details, {ignoreCase: true}) } async verifyReligiousBeliefs(belief?: string[], details?: string) { await expect(this.relegiousAboutSection).toBeVisible() - const textContent = await this.relegiousAboutSection.textContent() - if (belief) await expect(textContent?.toLowerCase()).toContain(belief[0].toLowerCase()) - if (details) await expect(textContent?.toLowerCase()).toContain(details.toLowerCase()) + if (belief) + await expect(this.relegiousAboutSection).toContainText(belief[0], {ignoreCase: true}) + if (details) await expect(this.relegiousAboutSection).toContainText(details, {ignoreCase: true}) } async verifyPersonalityType(personalityType: string | undefined) { if (!personalityType) return await expect(this.personalityAboutSection).toBeVisible() - const textContent = await this.personalityAboutSection.textContent() - await expect(textContent?.toLowerCase()).toContain(personalityType.toLowerCase()) + await expect(this.personalityAboutSection).toContainText(personalityType, {ignoreCase: true}) } async verifyBigFivePersonalitySection(personalityType: Record | undefined) { if (!personalityType) return await expect(this.bigFivePersonalityTraitsAboutSection).toBeVisible() - const textContent = await this.bigFivePersonalityTraitsAboutSection.textContent() for (const [key, value] of Object.entries(personalityType)) { - await expect(textContent?.toLowerCase()).toContain(key.toLowerCase()) - await expect(textContent?.toLowerCase()).toContain(String(value)) + await expect(this.bigFivePersonalityTraitsAboutSection).toContainText(key, {ignoreCase: true}) + await expect(this.bigFivePersonalityTraitsAboutSection).toContainText(String(value)) } } async verifyDiet(diet: string[] | undefined) { if (!diet) return await expect(this.dietAboutSection).toBeVisible() - const textContent = await this.dietAboutSection.textContent() - await expect(textContent?.toLowerCase()).toContain(diet[0].toLowerCase()) + await expect(this.dietAboutSection).toContainText(diet[0], {ignoreCase: true}) } async verifySmoker(smoker: boolean | undefined) { await expect(this.smokerAboutSection).toBeVisible() - const textContent = await this.smokerAboutSection.textContent() - if (smoker === true) await expect(textContent?.toLowerCase()).toContain('Smokes'.toLowerCase()) + if (smoker === true) + await expect(this.smokerAboutSection).toContainText('Smokes', {ignoreCase: true}) if (smoker === false) - await expect(textContent?.toLowerCase()).toContain("Doesn't smoke".toLowerCase()) + await expect(this.smokerAboutSection).toContainText("Doesn't smoke", {ignoreCase: true}) } async verifyDrinksPerMonth(drinks: string | undefined) { + if (!drinks) return await expect(this.drinkerAboutSection).toBeVisible() - const textContent = await this.drinkerAboutSection.textContent() - await expect(textContent?.toLowerCase()).toContain(drinks) + await expect(this.drinkerAboutSection).toContainText(drinks) } async verifyLanguages(languages: LanguageTuple[] | undefined) { if (!languages || languages.length === 0) return await expect(this.languagesAboutSection).toBeVisible() - const textContent = await this.languagesAboutSection.textContent() for (const language of languages) { - await expect(textContent?.toLowerCase()).toContain(language[0].toLowerCase()) + await expect(this.languagesAboutSection).toContainText(language[0], {ignoreCase: true}) } } async verifySocialMedia(socialMedia: Socials[] | undefined) { if (!socialMedia || socialMedia.length === 0) return await expect(this.socialMediaSection).toBeVisible() - const textContent = await this.socialMediaSection.textContent() for (const {urlOrUsername} of socialMedia) { - await expect(textContent?.toLowerCase()).toContain(urlOrUsername.toLowerCase()) + await expect(this.socialMediaSection).toContainText(urlOrUsername, {ignoreCase: true}) } } diff --git a/tests/e2e/web/pages/signUpPage.ts b/tests/e2e/web/pages/signUpPage.ts index e725d154..067167f4 100644 --- a/tests/e2e/web/pages/signUpPage.ts +++ b/tests/e2e/web/pages/signUpPage.ts @@ -45,7 +45,9 @@ export type Platforms = export class SignUpPage { private readonly displayNameField: Locator + private readonly displayNameError: Locator private readonly usernameField: Locator + private readonly usernameError: Locator private readonly nextButton: Locator private readonly bioField: Locator private readonly locationField: Locator @@ -101,7 +103,9 @@ export class SignUpPage { constructor(public readonly page: Page) { this.displayNameField = page.getByPlaceholder('Display name') + this.displayNameError = page.getByTestId('signup-display-name') this.usernameField = page.getByPlaceholder('Username') + this.usernameError = page.getByTestId('signup-username') this.nextButton = page.getByRole('button', {name: 'Next', exact: true}) this.bioField = page.locator('.tiptap') this.locationField = page.getByPlaceholder('Search city...') @@ -155,6 +159,10 @@ export class SignUpPage { this.saveButton = page.getByRole('button', {name: 'Save'}) } + get nextButtonLocator(): Locator { + return this.nextButton + } + async fillUsername(username: string) { await expect(this.usernameField).toBeVisible() await this.usernameField.fill(username) @@ -179,9 +187,9 @@ export class SignUpPage { async chooseGender(gender: GenderTuple | undefined) { if (!gender) return - await expect(this.page.locator(`span:has-text("${gender[0]}")`)).toBeVisible() - await this.page.locator(`span:has-text("${gender[0]}")`).click() - await expect(this.page.locator(`span:has-text("${gender[0]}")`)).toBeChecked() + await expect(this.page.locator(`:text-is("${gender[0]}")`)).toBeVisible() + await this.page.locator(`:text-is("${gender[0]}")`).click() + await expect(this.page.locator(`:text-is("${gender[0]}")`)).toBeChecked() } async fillAge(age: string | undefined) { @@ -716,4 +724,12 @@ export class SignUpPage { await expect(this.keywordsField).toBeVisible() await this.keywordsField.fill(keywords) } + + async verifyDisplayNameError() { + await expect(this.displayNameError).toBeVisible() + } + + async verifyUsernameError() { + await expect(this.usernameError).toBeVisible() + } } diff --git a/tests/e2e/web/specs/onboardingFlow.spec.ts b/tests/e2e/web/specs/onboardingFlow.spec.ts index a6aadb0e..a91a3c40 100644 --- a/tests/e2e/web/specs/onboardingFlow.spec.ts +++ b/tests/e2e/web/specs/onboardingFlow.spec.ts @@ -1,4 +1,5 @@ import {userInformationFromDb} from '../../utils/databaseUtils' +import {progressToRequiredForm} from '../utils/testCleanupHelpers' import {expect, test} from '../fixtures/base' test.describe('when given valid input', () => { @@ -8,82 +9,84 @@ test.describe('when given valid input', () => { signUpPage, authPage, profilePage, - testAccount, + onboardingAccount, }) => { console.log( - `Starting "should successfully complete the onboarding flow" with ${testAccount.username}`, + `Starting "should successfully complete the onboarding flow" with ${onboardingAccount.username}`, ) await homePage.gotToHomePage() await homePage.clickSignUpButton() - await authPage.fillEmailField(testAccount.email) - await authPage.fillPasswordField(testAccount.password) + await authPage.fillEmailField(onboardingAccount.email) + await authPage.fillPasswordField(onboardingAccount.password) await authPage.clickSignUpWithEmailButton() await onboardingPage.clickContinueButton() //First continue await onboardingPage.clickContinueButton() //Second continue await onboardingPage.clickGetStartedButton() - await signUpPage.fillDisplayName(testAccount.display_name) - await signUpPage.fillUsername(testAccount.username) + await signUpPage.fillDisplayName(onboardingAccount.display_name) + await signUpPage.fillUsername(onboardingAccount.username) await signUpPage.clickNextButton() - await signUpPage.chooseGender(testAccount.gender) - await signUpPage.fillAge(testAccount.age) + await signUpPage.chooseGender(onboardingAccount.gender) + await signUpPage.fillAge(onboardingAccount.age) await signUpPage.fillHeight({ - feet: testAccount.height?.feet, - inches: testAccount.height?.inches, + feet: onboardingAccount.height?.feet, + inches: onboardingAccount.height?.inches, }) - await signUpPage.fillEthnicity(testAccount.ethnicity_origin) - await signUpPage.fillHeadline(testAccount.headline) - await signUpPage.fillKeywords(testAccount.keywords) - await signUpPage.fillInterestedInConnectingWith(testAccount.interested_in) + 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( - testAccount.Interested_in_ages?.min, - testAccount.Interested_in_ages?.max, - ) - await signUpPage.setConnectionType(testAccount.connection_type) - await signUpPage.setRelationshipStatus(testAccount.relationship_status) - await signUpPage.setRelationshipStyle(testAccount.relationship_style) - await signUpPage.fillCurrentNumberOfChildren(testAccount.number_of_kids) - await signUpPage.setWantChildrenExpectation(testAccount.children_expectation) - await signUpPage.setInterests(testAccount.interests) - await signUpPage.setCauses(testAccount.causes) - await signUpPage.setHighestEducationLevel(testAccount.education_level) - await signUpPage.fillUniversity(testAccount.university) - await signUpPage.fillJobTitle(testAccount.job_title) - await signUpPage.fillCompany(testAccount.company) - await signUpPage.setWorkArea(testAccount.work_area) + 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( - testAccount.beliefs?.political?.belief, - testAccount.beliefs?.political?.details, + onboardingAccount.beliefs?.political?.belief, + onboardingAccount.beliefs?.political?.details, ) await signUpPage.setReligiousBeliefs( - testAccount.beliefs?.religious?.belief, - testAccount.beliefs?.religious?.details, + onboardingAccount.beliefs?.religious?.belief, + onboardingAccount.beliefs?.religious?.details, + ) + await signUpPage.setPersonalityType(onboardingAccount.personality_type) + await signUpPage.setOpennessPersonalityValue( + onboardingAccount.big_five_personality_traits?.openness, ) - await signUpPage.setPersonalityType(testAccount.personality_type) - await signUpPage.setOpennessPersonalityValue(testAccount.big_five_personality_traits?.openness) await signUpPage.setAgreeablenessPersonalityValue( - testAccount.big_five_personality_traits?.agreeableness, + onboardingAccount.big_five_personality_traits?.agreeableness, ) await signUpPage.setConscientiousnessPersonalityValue( - testAccount.big_five_personality_traits?.conscientiousness, + onboardingAccount.big_five_personality_traits?.conscientiousness, ) await signUpPage.setExtraversionPersonalityValue( - testAccount.big_five_personality_traits?.extraversion, + onboardingAccount.big_five_personality_traits?.extraversion, ) await signUpPage.setNeuroticismPersonalityValue( - testAccount.big_five_personality_traits?.neuroticism, - ) - await signUpPage.setDietType(testAccount.diet) - await signUpPage.setIsSmoker(testAccount.is_smoker) - await signUpPage.fillAlcoholPerMonth(testAccount.alcohol_consumed_per_month) - await signUpPage.setLanguages(testAccount.languages) - await signUpPage.addSocialMediaPlatform(testAccount.social_media) - await signUpPage.fillBio(testAccount.bio) + 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( - testAccount.compatibility, + onboardingAccount.compatibility, ) await profilePage.clickNextCompatibilityQuestionButton() await profilePage.clickSkipCompatibilityQuestionButton() @@ -92,119 +95,133 @@ test.describe('when given valid input', () => { await profilePage.clickCloseButton() //Verify information is correct - await profilePage.verifyDisplayName(testAccount.display_name) - await profilePage.verifyHeadline(testAccount.headline) - await profilePage.verifyKeywords(testAccount.keywords) + await profilePage.verifyDisplayName(onboardingAccount.display_name) + await profilePage.verifyHeadline(onboardingAccount.headline) + await profilePage.verifyKeywords(onboardingAccount.keywords) await profilePage.verifyGenderLocationHeightAge( - testAccount.gender, + onboardingAccount.gender, undefined, - testAccount.height?.feet, - testAccount.height?.inches, - testAccount.age, - ) - await profilePage.verifyInterestedInConnectingWith( - testAccount.interested_in, - testAccount.Interested_in_ages?.min, - testAccount.Interested_in_ages?.max, - ) - await profilePage.verifyRelationShipTypeAndInterest( - testAccount.connection_type, - testAccount.relationship_style, - ) - await profilePage.verifyRelationshipStatus(testAccount.relationship_status) - await profilePage.verifyCurrentNumberOfKids(testAccount.number_of_kids) - await profilePage.verifyWantChildrenExpectation(testAccount.children_expectation) - await profilePage.verifyInterests(testAccount.interests) - await profilePage.verifyCauses(testAccount.causes) + onboardingAccount.height?.feet, + onboardingAccount.height?.inches, + onboardingAccount.age, + ) + await profilePage.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( - testAccount.education_level, - testAccount.university, + onboardingAccount.education_level, + onboardingAccount.university, ) - await profilePage.verifyJobInformation(testAccount.job_title, testAccount.company) - await profilePage.verifyWorkArea(testAccount.work_area) + await profilePage.verifyJobInformation(onboardingAccount.job_title, onboardingAccount.company) + await profilePage.verifyWorkArea(onboardingAccount.work_area) await profilePage.verifyPoliticalBeliefs( - testAccount.beliefs?.political?.belief, - testAccount.beliefs?.political?.details, + onboardingAccount.beliefs?.political?.belief, + onboardingAccount.beliefs?.political?.details, ) await profilePage.verifyReligiousBeliefs( - testAccount.beliefs?.religious?.belief, - testAccount.beliefs?.religious?.details, - ) - await profilePage.verifyPersonalityType(testAccount.personality_type) - await profilePage.verifyBigFivePersonalitySection(testAccount.big_five_personality_traits) - await profilePage.verifyDiet(testAccount.diet) - await profilePage.verifySmoker(testAccount.is_smoker) - await profilePage.verifyDrinksPerMonth(testAccount.alcohol_consumed_per_month) - await profilePage.verifyLanguages(testAccount.languages) - await profilePage.verifySocialMedia(testAccount.social_media) - await profilePage.verifyBio(testAccount.bio) + 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) //Verify Database Information - const dbInfo = await userInformationFromDb(testAccount) + const dbInfo = await userInformationFromDb(onboardingAccount) console.log(dbInfo.profile) - await expect(dbInfo.user.name).toBe(testAccount.display_name) - await expect(dbInfo.user.username).toBe(testAccount.username) - await expect(dbInfo.profile.bio_text).toBe(testAccount.bio) - await expect(dbInfo.profile.gender).toEqual(testAccount.gender?.[1]) - await expect(dbInfo.profile.headline).toEqual(testAccount.headline) + await expect(dbInfo.user.name).toBe(onboardingAccount.display_name) + await expect(dbInfo.user.username).toBe(onboardingAccount.username) + await expect(dbInfo.profile.bio_text).toBe(onboardingAccount.bio) + await expect(dbInfo.profile.gender).toEqual(onboardingAccount.gender?.[1]) + await expect(dbInfo.profile.headline).toEqual(onboardingAccount.headline) await expect(dbInfo.profile.keywords).toEqual( - expect.arrayContaining(testAccount.keywords?.split(', ') ?? []), - ) - await expect(String(dbInfo.profile.age)).toEqual(testAccount.age) - await expect(dbInfo.profile.height_in_inches).toEqual(Number(testAccount.height?.feet) * 12) - await expect(dbInfo.profile.ethnicity).toContain(testAccount.ethnicity_origin?.[1]) - await expect(dbInfo.profile.pref_gender).toContain(testAccount.interested_in?.[1]) - await expect(String(dbInfo.profile.pref_age_min)).toContain(testAccount.Interested_in_ages?.min) - await expect(String(dbInfo.profile.pref_age_max)).toContain(testAccount.Interested_in_ages?.max) + expect.arrayContaining(onboardingAccount.keywords?.split(', ') ?? []), + ) + await expect(String(dbInfo.profile.age)).toEqual(onboardingAccount.age) + await expect(dbInfo.profile.height_in_inches).toEqual( + Number(onboardingAccount.height?.feet) * 12, + ) + await expect(dbInfo.profile.ethnicity).toContain(onboardingAccount.ethnicity_origin?.[1]) + await expect(dbInfo.profile.pref_gender).toContain(onboardingAccount.interested_in?.[1]) + await expect(String(dbInfo.profile.pref_age_min)).toContain( + onboardingAccount.Interested_in_ages?.min, + ) + await expect(String(dbInfo.profile.pref_age_max)).toContain( + onboardingAccount.Interested_in_ages?.max, + ) await expect(dbInfo.profile.pref_relation_styles).toContain( - `${testAccount.connection_type?.[1]}`.toLowerCase(), + `${onboardingAccount.connection_type?.[1]}`.toLowerCase(), + ) + await expect(dbInfo.profile.relationship_status).toContain( + onboardingAccount.relationship_status?.[1], + ) + await expect(dbInfo.profile.pref_romantic_styles).toContain( + onboardingAccount.relationship_style?.[1], + ) + await expect(dbInfo.profile.has_kids).toEqual(Number(onboardingAccount.number_of_kids)) + await expect(dbInfo.profile.wants_kids_strength).toEqual( + onboardingAccount.children_expectation?.[1], ) - await expect(dbInfo.profile.relationship_status).toContain(testAccount.relationship_status?.[1]) - await expect(dbInfo.profile.pref_romantic_styles).toContain(testAccount.relationship_style?.[1]) - await expect(dbInfo.profile.has_kids).toEqual(Number(testAccount.number_of_kids)) - await expect(dbInfo.profile.wants_kids_strength).toEqual(testAccount.children_expectation?.[1]) await expect(dbInfo.profile.education_level).toContain( - `${testAccount.education_level?.[1]}`.toLowerCase(), + `${onboardingAccount.education_level?.[1]}`.toLowerCase(), ) - await expect(dbInfo.profile.university).toContain(testAccount.university) - await expect(dbInfo.profile.occupation_title).toContain(testAccount.job_title) - await expect(dbInfo.profile.company).toContain(testAccount.company) + await expect(dbInfo.profile.university).toContain(onboardingAccount.university) + await expect(dbInfo.profile.occupation_title).toContain(onboardingAccount.job_title) + await expect(dbInfo.profile.company).toContain(onboardingAccount.company) await expect(dbInfo.profile.political_beliefs).toContain( - testAccount.beliefs?.political?.belief?.[1], + onboardingAccount.beliefs?.political?.belief?.[1], ) await expect(dbInfo.profile.political_details).toContain( - testAccount.beliefs?.political?.details, + onboardingAccount.beliefs?.political?.details, + ) + await expect(dbInfo.profile.religion).toContain( + onboardingAccount.beliefs?.religious?.belief?.[1], ) - await expect(dbInfo.profile.religion).toContain(testAccount.beliefs?.religious?.belief?.[1]) await expect(dbInfo.profile.religious_beliefs).toContain( - testAccount.beliefs?.religious?.details, + onboardingAccount.beliefs?.religious?.details, + ) + await expect(dbInfo.profile.mbti).toContain( + `${onboardingAccount.personality_type}`.toLowerCase(), ) - await expect(dbInfo.profile.mbti).toContain(`${testAccount.personality_type}`.toLowerCase()) await expect(dbInfo.profile.big5_openness).toEqual( - testAccount.big_five_personality_traits?.openness, + onboardingAccount.big_five_personality_traits?.openness, ) await expect(dbInfo.profile.big5_conscientiousness).toEqual( - testAccount.big_five_personality_traits?.conscientiousness, + onboardingAccount.big_five_personality_traits?.conscientiousness, ) await expect(dbInfo.profile.big5_extraversion).toEqual( - testAccount.big_five_personality_traits?.extraversion, + onboardingAccount.big_five_personality_traits?.extraversion, ) await expect(dbInfo.profile.big5_agreeableness).toEqual( - testAccount.big_five_personality_traits?.agreeableness, + onboardingAccount.big_five_personality_traits?.agreeableness, ) await expect(dbInfo.profile.big5_neuroticism).toEqual( - testAccount.big_five_personality_traits?.neuroticism, + onboardingAccount.big_five_personality_traits?.neuroticism, ) - await expect(dbInfo.profile.diet).toContain(testAccount.diet?.[1].toLowerCase()) - await expect(dbInfo.profile.is_smoker).toEqual(testAccount.is_smoker) + await expect(dbInfo.profile.diet).toContain(onboardingAccount.diet?.[1].toLowerCase()) + await expect(dbInfo.profile.is_smoker).toEqual(onboardingAccount.is_smoker) await expect(dbInfo.profile.languages).toHaveLength(2) await expect(dbInfo.profile.languages).toEqual( - expect.arrayContaining(testAccount.languages?.map(([_, l]) => l.toLowerCase()) ?? []), + expect.arrayContaining(onboardingAccount.languages?.map(([_, l]) => l.toLowerCase()) ?? []), ) await expect(String(dbInfo.profile.drinks_per_month)).toEqual( - testAccount.alcohol_consumed_per_month, + onboardingAccount.alcohol_consumed_per_month, ) }) @@ -219,19 +236,101 @@ test.describe('when given valid input', () => { console.log( `Starting "should successfully skip the onboarding flow" with ${fakerAccount.username}`, ) - await homePage.gotToHomePage() - await homePage.clickSignUpButton() - await authPage.fillEmailField(fakerAccount.email) - await authPage.fillPasswordField(fakerAccount.password) - await authPage.clickSignUpWithEmailButton() - await onboardingPage.clickSkipOnboardingButton() + await progressToRequiredForm(homePage, authPage, fakerAccount, onboardingPage) + 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() + + //Verify displayed information is correct + await profilePage.verifyDisplayName(fakerAccount.display_name) + + //Verify database info + const dbInfo = await userInformationFromDb(fakerAccount) + + await expect(dbInfo.user.name).toContain(fakerAccount.display_name) + await expect(dbInfo.user.username).toContain(fakerAccount.username) + }) + + test('should successfully enter optional information after completing flow', async ({ + homePage, + onboardingPage, + signUpPage, + authPage, + profilePage, + fakerAccount, + }) => { + console.log( + `Starting "should successfully enter optional information after completing flow" with ${fakerAccount.username}`, + ) + await progressToRequiredForm(homePage, authPage, fakerAccount, onboardingPage) 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() + await profilePage.clickEditProfileButton() + await signUpPage.chooseGender(fakerAccount.gender) + await signUpPage.fillAge(fakerAccount.age) + await signUpPage.fillHeight({ + feet: fakerAccount.height?.feet, + inches: fakerAccount.height?.inches, + }) + await signUpPage.saveProfileChanges() + + //Verify displayed information is correct + await profilePage.verifyDisplayName(fakerAccount.display_name) + await profilePage.verifyGenderLocationHeightAge( + fakerAccount.gender, + undefined, + fakerAccount.height?.feet, + fakerAccount.height?.inches, + fakerAccount.age, + ) + //Verify database info + const dbInfo = await userInformationFromDb(fakerAccount) + + await expect(dbInfo.user.name).toContain(fakerAccount.display_name) + await expect(dbInfo.user.username).toContain(fakerAccount.username) + await expect(dbInfo.profile.gender).toEqual(fakerAccount.gender?.[1]) + await expect(String(dbInfo.profile.age)).toEqual(fakerAccount.age) + await expect(dbInfo.profile.height_in_inches).toEqual(Number(fakerAccount.height?.feet) * 12) + }) + + test('should successfully use the start answering option', async ({ + homePage, + onboardingPage, + signUpPage, + authPage, + profilePage, + fakerAccount, + onboardingAccount, + }) => { + console.log( + `Starting "should successfully use the start answering option" with ${fakerAccount.username}`, + ) + await progressToRequiredForm(homePage, authPage, fakerAccount, onboardingPage) + 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( + onboardingAccount.compatibility, + ) + await profilePage.clickNextCompatibilityQuestionButton() + await profilePage.clickCloseButton() + await onboardingPage.clickRefineProfileButton() + + //Verify displayed information is correct + await profilePage.verifyDisplayName(fakerAccount.display_name) + await profilePage.verifyCompatibilityAnswers(compatTwoQuestionOne) + + //Verify database info const dbInfo = await userInformationFromDb(fakerAccount) await expect(dbInfo.user.name).toContain(fakerAccount.display_name) @@ -267,6 +366,10 @@ test.describe('when given valid input', () => { await profilePage.clickCloseButton() await onboardingPage.clickRefineProfileButton() + //Verify displayed information is correct + await profilePage.verifyDisplayName(fakerAccount.display_name) + + //Verify database info const dbInfo = await userInformationFromDb(fakerAccount) await expect(dbInfo.user.name).toContain(fakerAccount.display_name) @@ -292,6 +395,10 @@ test.describe('when given valid input', () => { await profilePage.clickCloseButton() await onboardingPage.clickRefineProfileButton() + //Verify displayed information is correct + await profilePage.verifyDisplayName(fakerAccount.display_name) + + //Verify database info const dbInfo = await userInformationFromDb(fakerAccount) await expect(dbInfo.user.name).toContain(fakerAccount.display_name) diff --git a/tests/e2e/web/specs/signUp.spec.ts b/tests/e2e/web/specs/signUp.spec.ts index 0434a27d..948f546d 100644 --- a/tests/e2e/web/specs/signUp.spec.ts +++ b/tests/e2e/web/specs/signUp.spec.ts @@ -1,22 +1,36 @@ -import {expect} from '@playwright/test' +import {expect, test} from '../fixtures/base' +import {progressToRequiredForm} from '../utils/testCleanupHelpers' -import {test} from '../fixtures/deleteUserFixture' -import {AuthPage} from '../pages/AuthPage' -import {config} from '../SPEC_CONFIG' - -test('user can sign up with email + password', async ({page}) => { - const auth = new AuthPage(page) - - await page.goto('/') - - await auth.clickSignUpButton() - - await auth.fillEmailField(config.USERS.SPEC.EMAIL) - await auth.fillPasswordField(config.USERS.SPEC.PASSWORD) - - await auth.clickSignUpWithEmailButton() - - await page.waitForURL(/^(?!.*\/signup).*$/) +test.describe('when given valid input', () => { + test('placeholder', async () => {}) +}) - expect(page.url()).not.toContain('/signup') +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, + }) => { + await progressToRequiredForm(homePage, authPage, specAccount, onboardingPage) + await signUpPage.fillDisplayName('') + await signUpPage.fillUsername(specAccount.username) + await signUpPage.verifyDisplayNameError() + await expect(signUpPage.nextButtonLocator).toBeDisabled() + }) + + test('should disable the button "Next" when the username field is empty', async ({ + specAccount, + homePage, + authPage, + onboardingPage, + signUpPage, + }) => { + await progressToRequiredForm(homePage, authPage, specAccount, onboardingPage) + await signUpPage.fillDisplayName(specAccount.display_name) + await signUpPage.fillUsername('') + await signUpPage.verifyUsernameError() + await expect(signUpPage.nextButtonLocator).toBeDisabled() + }) }) diff --git a/tests/e2e/web/utils/accountInformation.ts b/tests/e2e/web/utils/accountInformation.ts index 603ddcb4..a8bb3fdb 100644 --- a/tests/e2e/web/utils/accountInformation.ts +++ b/tests/e2e/web/utils/accountInformation.ts @@ -17,7 +17,7 @@ import {ImportanceTuple} from 'web/components/answers/answer-compatibility-quest import {Causes, ChildrenExpectation, Interests, Platforms} from '../pages/signUpPage' -export type OnboardingUser = { +export type UserAccountInformation = { email: string password: string display_name: string @@ -97,12 +97,15 @@ type FiveBigPersonalityTraits = { neuroticism?: number } -type OnboardingConfig = { - faker_account: () => OnboardingUser - account_one: () => OnboardingUser +type AccountConfig = { + faker_account: () => UserAccountInformation + spec_account: () => UserAccountInformation + dev_one_account: () => UserAccountInformation + dev_two_account: () => UserAccountInformation + account_all_info: () => UserAccountInformation } -export const onboarding: OnboardingConfig = { +export const testAccounts: AccountConfig = { // Use a function so email is unique per test call faker_account: () => { const id = crypto.randomUUID().slice(0, 6) @@ -111,14 +114,51 @@ export const onboarding: OnboardingConfig = { password: faker.internet.password(), display_name: faker.internet.displayName(), username: `user_${id}`, + gender: ['Man', 'male'], + age: '35', + height: { + feet: '5', + inches: '0', + centimeters: '152.4', + }, + } + }, + + spec_account: () => { + const id = crypto.randomUUID().slice(0, 6) + return { + email: `spec${id}@compass.com`, + password: 'compassConnections1!', + display_name: 'Spec.Compass', + username: `Spec.Connections_${id}`, + } + }, + + dev_one_account: () => { + const id = crypto.randomUUID().slice(0, 6) + return { + email: `dev_1_${id}@compass.com`, + password: 'dev_1Password', + display_name: 'Dev1.Compass', + username: `Dev1.Connections_${id}`, + } + }, + + dev_two_account: () => { + const id = crypto.randomUUID().slice(0, 6) + return { + email: 'dev_2@compass.com', + password: 'dev_2Password', + display_name: 'Dev2.Compass', + username: `Dev2.Connections_${id}`, } }, - account_one: () => { + account_all_info: () => { const id = crypto.randomUUID().slice(0, 6) return { // Use a non-real TLD like @test.compass to make it obvious these are test accounts and prevent accidental emails - email: `onboarding${id}@test.compass`, + email: `testAccounts${id}@test.compass`, password: 'CompassTest', display_name: 'Compass Onboarding', username: `TheGreatOnboarding_${id}`, // username max length is 25 (see /create-user) diff --git a/tests/e2e/web/utils/deleteUser.ts b/tests/e2e/web/utils/deleteUser.ts index eca1da27..96968b22 100644 --- a/tests/e2e/web/utils/deleteUser.ts +++ b/tests/e2e/web/utils/deleteUser.ts @@ -4,8 +4,8 @@ import {deleteAccount, firebaseLogin} from '../../utils/firebaseUtils' export async function deleteUser(email: string, password: string) { try { const loginInfo = await firebaseLogin(email, password) - await deleteFromDb(loginInfo.data.localId) await deleteAccount(loginInfo) + await deleteFromDb(loginInfo.data.localId) } catch (err: any) { // Skip deletion if user doesn't exist or other auth errors occur if ( diff --git a/tests/e2e/web/utils/testCleanupHelpers.ts b/tests/e2e/web/utils/testCleanupHelpers.ts new file mode 100644 index 00000000..1e11d794 --- /dev/null +++ b/tests/e2e/web/utils/testCleanupHelpers.ts @@ -0,0 +1,18 @@ +import {AuthPage} from '../pages/AuthPage' +import {HomePage} from '../pages/homePage' +import {OnboardingPage} from '../pages/onboardingPage' +import {UserAccountInformation} from '../utils/accountInformation' + +export async function progressToRequiredForm( + homePage: HomePage, + authPage: AuthPage, + account: UserAccountInformation, + onboardingPage: OnboardingPage, +) { + await homePage.gotToHomePage() + await homePage.clickSignUpButton() + await authPage.fillEmailField(account.email) + await authPage.fillPasswordField(account.password) + await authPage.clickSignUpWithEmailButton() + await onboardingPage.clickSkipOnboardingButton() +} diff --git a/web/README.md b/web/README.md index 3fa82f20..3e7d90a3 100644 --- a/web/README.md +++ b/web/README.md @@ -220,7 +220,6 @@ Catches React errors and shows user-friendly message: ```tsx import {ErrorBoundary} from 'web/components/error-boundary' - ; @@ -250,7 +249,6 @@ Keyboard users can skip to main content: ```tsx import {SkipLink, MainContent} from 'web/components/skip-link' - ;<> ... diff --git a/web/components/required-profile-form.tsx b/web/components/required-profile-form.tsx index 2f90092b..576c9569 100644 --- a/web/components/required-profile-form.tsx +++ b/web/components/required-profile-form.tsx @@ -49,13 +49,21 @@ export const RequiredProfileUserForm = (props: { const [step, setStep] = useState(0) const [loadingUsername, setLoadingUsername] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false) - const [errorUsername, setErrorUsername] = useState('') + const [errorMessageUsername, setErrorUsername] = useState(null) + const [errorMessageDisplayName, setErrorDisplayName] = useState(null) const t = useT() + const isDisabled = !!errorMessageDisplayName || !!errorMessageUsername || isSubmitting const updateUsername = async () => { let success = true setLoadingUsername(true) try { + if (data.username.length < 3) { + setErrorUsername('Minimum 3 characters required for usernames') + success = false + setLoadingUsername(false) + return success + } const { valid, message = undefined, @@ -94,13 +102,25 @@ export const RequiredProfileUserForm = (props: { ) => { - setData('name', e.target.value || '') + const value = e.target.value || '' + if (value.length < 3) { + setErrorDisplayName('Minimum 3 characters for display names') + } else { + setErrorDisplayName(null) + } + setData('name', value) }} /> + {errorMessageDisplayName && ( +

+ {errorMessageDisplayName} +

+ )} )} @@ -118,7 +138,13 @@ export const RequiredProfileUserForm = (props: { placeholder="Username" value={data.username || ''} onChange={(e: React.ChangeEvent) => { - setData('username', e.target.value || '') + const value = e.target.value || '' + if (value.length < 3) { + setErrorUsername('Minimum 3 characters required for usernames') + } else { + setErrorUsername(null) + } + setData('username', value) }} /> {loadingUsername && } @@ -131,7 +157,11 @@ export const RequiredProfileUserForm = (props: { )} } - {errorUsername && {errorUsername}} + {errorMessageUsername && ( + + {errorMessageUsername} + + )} )} @@ -154,7 +184,7 @@ export const RequiredProfileUserForm = (props: { {onSubmit && (