[Housekeeping] Organizing the test structure and files, adding fix for issue #36 (None descriptive error) (#38)

* 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: Added 'tsconfig-paths/register' to playwright config so it applies tsconfig/ts-node to test files and imported modules....there was a syntax error:

SyntaxError: tests/e2e/web/pages/homePage.ts: Unexpected token (2:0)

* .

* Updated ProfilePage: In some cases removed the use of "one shot grabs" using .textContent() to prevent flaky tests
Added test for entering profile information post signup flow

* Linting and Prettier

* Fix: Merge conflict

* Fix: Modified sortedAndFilteredAnswers to use "UseMemo" so that it doesnt run every time something changes

* .

* .

* Merged "verifyInterestedInConnectingWith" and "verifyRelationShipTypeAndInterest" into "verifySeeking" to match ui changes

* Added error message outlining the minimum character requirement for display names and usernames

* Updated displayName error message to show when editing the user profile post signup

* 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

* Linting and Prettier

---------

Co-authored-by: MartinBraquet <martin.braquet@gmail.com>
This commit is contained in:
Okechi Jones-Williams
2026-03-16 15:02:48 +00:00
committed by GitHub
parent c8735b8b01
commit cfeab0278c
14 changed files with 463 additions and 280 deletions

View File

@@ -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'

View File

@@ -1,4 +1,5 @@
import {defineConfig, devices} from '@playwright/test'
import 'tsconfig-paths/register'
import {execSync} from 'child_process'
import {config} from 'dotenv'

View File

@@ -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)

View File

@@ -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<CleanupFixtures>({
cleanupUsers: [
async ({}, use) => {
// Run all tests first
await use()
//then delete users
await deleteUser(config.USERS.SPEC.EMAIL, config.USERS.SPEC.PASSWORD)
},
{auto: true},
],
})

View File

@@ -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()
}

View File

@@ -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<string, number> | 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})
}
}

View File

@@ -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()
}
}

View File

@@ -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,
onboardingAccount.Interested_in_ages?.min,
onboardingAccount.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)
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,
onboardingAccount.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)
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,
onboardingAccount.height?.feet,
onboardingAccount.height?.inches,
onboardingAccount.age,
)
await profilePage.verifyInterestedInConnectingWith(
testAccount.interested_in,
testAccount.Interested_in_ages?.min,
testAccount.Interested_in_ages?.max,
await profilePage.verifySeeking(
onboardingAccount.interested_in,
onboardingAccount.Interested_in_ages?.min,
onboardingAccount.Interested_in_ages?.max,
onboardingAccount.connection_type,
onboardingAccount.relationship_style,
)
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)
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,
onboardingAccount.beliefs?.religious?.belief,
onboardingAccount.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)
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(', ') ?? []),
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(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)
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,12 +236,7 @@ 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()
@@ -232,6 +244,93 @@ 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)
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)

View File

@@ -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).*$/)
expect(page.url()).not.toContain('/signup')
test.describe('when given valid input', () => {
test('placeholder', async () => {})
})
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()
})
})

View File

@@ -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',
},
}
},
account_one: () => {
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_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)

View File

@@ -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 (

View File

@@ -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()
}

View File

@@ -220,7 +220,6 @@ Catches React errors and shows user-friendly message:
```tsx
import {ErrorBoundary} from 'web/components/error-boundary'
;<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
@@ -250,7 +249,6 @@ Keyboard users can skip to main content:
```tsx
import {SkipLink, MainContent} from 'web/components/skip-link'
;<>
<SkipLink />
<MainContent>...</MainContent>

View File

@@ -49,13 +49,21 @@ export const RequiredProfileUserForm = (props: {
const [step, setStep] = useState<number>(0)
const [loadingUsername, setLoadingUsername] = useState<boolean>(false)
const [isSubmitting, setIsSubmitting] = useState<boolean>(false)
const [errorUsername, setErrorUsername] = useState<string>('')
const [errorMessageUsername, setErrorUsername] = useState<string | null>(null)
const [errorMessageDisplayName, setErrorDisplayName] = useState<string | null>(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: {
<Input
disabled={false}
type="text"
minLength={3}
placeholder="Display name"
value={data.name || ''}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
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)
}}
/>
</Row>
{errorMessageDisplayName && (
<p className="text-error text-sm mt-1" data-testid="signup-display-name">
{errorMessageDisplayName}
</p>
)}
</Col>
)}
@@ -118,7 +138,13 @@ export const RequiredProfileUserForm = (props: {
placeholder="Username"
value={data.username || ''}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
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 && <LoadingIndicator className={'ml-2'} />}
@@ -131,7 +157,11 @@ export const RequiredProfileUserForm = (props: {
)}
</span>
}
{errorUsername && <span className="text-error text-sm">{errorUsername}</span>}
{errorMessageUsername && (
<span className="text-error text-sm" data-testid="signup-username">
{errorMessageUsername}
</span>
)}
</Col>
)}
@@ -154,7 +184,7 @@ export const RequiredProfileUserForm = (props: {
{onSubmit && (
<Row className={'justify-end'}>
<Button
disabled={isSubmitting}
disabled={isDisabled}
loading={isSubmitting}
onClick={async () => {
setIsSubmitting(true)