mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-04-05 07:14:02 -04:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -17,7 +17,7 @@ reviews:
|
||||
enabled: true
|
||||
# Skip auto-review if PR title contains these keywords
|
||||
ignore_title_keywords:
|
||||
- "WIP"
|
||||
- 'WIP'
|
||||
# Don't auto-review draft PRs
|
||||
drafts: false
|
||||
# Only auto-review PRs targeting these branches
|
||||
@@ -57,17 +57,17 @@ reviews:
|
||||
|
||||
# Exclude these paths from reviews (build artifacts and dependencies)
|
||||
path_filters:
|
||||
- "!**/node_modules/**" # npm dependencies
|
||||
- "!**/android/**" # Native Android build files
|
||||
- "!**/ios/**" # Native iOS build files
|
||||
- "!**/.expo/**" # Expo build cache
|
||||
- "!**/.expo-shared/**" # Expo shared config
|
||||
- "!**/dist/**" # Build output
|
||||
- '!**/node_modules/**' # npm dependencies
|
||||
- '!**/android/**' # Native Android build files
|
||||
- '!**/ios/**' # Native iOS build files
|
||||
- '!**/.expo/**' # Expo build cache
|
||||
- '!**/.expo-shared/**' # Expo shared config
|
||||
- '!**/dist/**' # Build output
|
||||
|
||||
# Custom review instructions for specific file patterns
|
||||
path_instructions:
|
||||
# TypeScript/JavaScript files - main app code
|
||||
- path: "**/*.{ts,tsx,js,jsx}"
|
||||
- path: '**/*.{ts,tsx,js,jsx}'
|
||||
instructions: |
|
||||
General practices:
|
||||
- Summarize the changes clearly.
|
||||
@@ -78,7 +78,7 @@ reviews:
|
||||
- Flag any hardcoded strings; they should be in the constants file.
|
||||
- Check for edge cases like null values or empty arrays.
|
||||
- Suggest performance optimizations where appropriate.
|
||||
|
||||
|
||||
Mobile best practices:
|
||||
- Proper use of hooks (useRouter, useFonts, useAssets)
|
||||
- Accessibility: touch targets min 44x44, screen reader support
|
||||
@@ -105,7 +105,7 @@ reviews:
|
||||
const message = t('key', 'english string')
|
||||
```
|
||||
|
||||
- path: "tests/e2e/**/*.ts"
|
||||
- path: 'tests/e2e/**/*.ts'
|
||||
instructions: |
|
||||
Playwright E2E test guidelines for this repo:
|
||||
- Page objects live in `tests/e2e/web/pages/`. Each class wraps one page/route, holds only `private readonly` Locators, and exposes action methods.
|
||||
|
||||
@@ -316,7 +316,7 @@ describe('loadProfiles', () => {
|
||||
})
|
||||
|
||||
describe('when an error occurs', () => {
|
||||
it('throw if there is no compatability', async () => {
|
||||
it('throw if there is no compatibility', async () => {
|
||||
const props = {
|
||||
orderBy: 'compatibility_score',
|
||||
}
|
||||
|
||||
@@ -348,7 +348,6 @@ jest.mock('path/to/module')
|
||||
* This creates an object containing all named exports from ./path/to/module
|
||||
*/
|
||||
import * as mockModule from 'path/to/module'
|
||||
|
||||
;(mockModule.module as jest.Mock).mockResolvedValue(mockReturnValue)
|
||||
```
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": "../../",
|
||||
"module": "commonjs",
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import axios from 'axios'
|
||||
|
||||
import {config} from '../web/SPEC_CONFIG'
|
||||
|
||||
export async function firebaseLoginEmailPassword(
|
||||
|
||||
@@ -1,29 +1,11 @@
|
||||
import {test as base} from '@playwright/test'
|
||||
|
||||
import {AuthPage} from '../pages/AuthPage'
|
||||
import {ComatibilityPage} from '../pages/compatibilityPage'
|
||||
import {HomePage} from '../pages/homePage'
|
||||
import {OnboardingPage} from '../pages/onboardingPage'
|
||||
import {OrganizationPage} from '../pages/organizationPage'
|
||||
import {ProfilePage} from '../pages/profilePage'
|
||||
import {SettingsPage} from '../pages/settingsPage'
|
||||
import {SignUpPage} from '../pages/signUpPage'
|
||||
import {SocialPage} from '../pages/socialPage'
|
||||
import {testAccounts, UserAccountInformation} from '../utils/accountInformation'
|
||||
import {deleteUser} from '../utils/deleteUser'
|
||||
import {getAuthAccountInfo} from '../utils/networkUtils'
|
||||
import {App} from '../pages/app'
|
||||
|
||||
export const test = base.extend<{
|
||||
homePage: HomePage
|
||||
onboardingPage: OnboardingPage
|
||||
signUpPage: SignUpPage
|
||||
profilePage: ProfilePage
|
||||
authPage: AuthPage
|
||||
settingsPage: SettingsPage
|
||||
socialPage: SocialPage
|
||||
organizationPage: OrganizationPage
|
||||
compatabilityPage: ComatibilityPage
|
||||
cleanUpUsers: void
|
||||
app: App
|
||||
onboardingAccount: UserAccountInformation
|
||||
fakerAccount: UserAccountInformation
|
||||
specAccount: UserAccountInformation
|
||||
@@ -62,41 +44,9 @@ export const test = base.extend<{
|
||||
console.log('Cleaning up spec account...')
|
||||
await deleteUser('Email/Password', account)
|
||||
},
|
||||
onboardingPage: async ({page}, use) => {
|
||||
const onboardingPage = new OnboardingPage(page)
|
||||
await use(onboardingPage)
|
||||
},
|
||||
homePage: async ({page}, use) => {
|
||||
const homePage = new HomePage(page)
|
||||
await use(homePage)
|
||||
},
|
||||
signUpPage: async ({page}, use) => {
|
||||
const signUpPage = new SignUpPage(page)
|
||||
await use(signUpPage)
|
||||
},
|
||||
authPage: async ({page}, use) => {
|
||||
const authPage = new AuthPage(page)
|
||||
await use(authPage)
|
||||
},
|
||||
profilePage: async ({page}, use) => {
|
||||
const profilePage = new ProfilePage(page)
|
||||
await use(profilePage)
|
||||
},
|
||||
compatabilityPage: async ({page}, use) => {
|
||||
const compatibilityPage = new ComatibilityPage(page)
|
||||
await use(compatibilityPage)
|
||||
},
|
||||
settingsPage: async ({page}, use) => {
|
||||
const settingsPage = new SettingsPage(page)
|
||||
await use(settingsPage)
|
||||
},
|
||||
socialPage: async ({page}, use) => {
|
||||
const socialPage = new SocialPage(page)
|
||||
await use(socialPage)
|
||||
},
|
||||
organizationPage: async ({page}, use) => {
|
||||
const organizationPage = new OrganizationPage(page)
|
||||
await use(organizationPage)
|
||||
app: async ({page}, use) => {
|
||||
const appPage = new App(page)
|
||||
await use(appPage)
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,47 +1,17 @@
|
||||
import {test as base} from '@playwright/test'
|
||||
import {AuthPage} from '../pages/AuthPage'
|
||||
import {HomePage} from '../pages/homePage'
|
||||
import {App} from '../pages/app'
|
||||
import {testAccounts, UserAccountInformation} from '../utils/accountInformation'
|
||||
import { OnboardingPage } from '../pages/onboardingPage'
|
||||
import { SignUpPage } from '../pages/signUpPage'
|
||||
import { ProfilePage } from '../pages/profilePage'
|
||||
import { SettingsPage } from '../pages/settingsPage'
|
||||
|
||||
export const test = base.extend<{
|
||||
homePage: HomePage
|
||||
onboardingPage: OnboardingPage
|
||||
signUpPage: SignUpPage
|
||||
profilePage: ProfilePage
|
||||
settingsPage: SettingsPage
|
||||
authPage: AuthPage
|
||||
app: App
|
||||
dev_one_account: UserAccountInformation
|
||||
fakerAccount: UserAccountInformation
|
||||
googleAccountOne: UserAccountInformation
|
||||
googleAccountTwo: UserAccountInformation
|
||||
}>({
|
||||
homePage: async ({page}, use) => {
|
||||
const homePage = new HomePage(page)
|
||||
await use(homePage)
|
||||
},
|
||||
onboardingPage: async ({page}, use) => {
|
||||
const onboardingPage = new OnboardingPage(page)
|
||||
await use(onboardingPage)
|
||||
},
|
||||
signUpPage: async ({page}, use) => {
|
||||
const signUpPage = new SignUpPage(page)
|
||||
await use(signUpPage)
|
||||
},
|
||||
profilePage: async ({page}, use) => {
|
||||
const profilePage = new ProfilePage(page)
|
||||
await use(profilePage)
|
||||
},
|
||||
settingsPage: async ({page}, use) => {
|
||||
const settingsPage = new SettingsPage(page)
|
||||
await use(settingsPage)
|
||||
},
|
||||
authPage: async ({page}, use) => {
|
||||
const authPage = new AuthPage(page)
|
||||
await use(authPage)
|
||||
app: async ({page}, use) => {
|
||||
const appPage = new App(page)
|
||||
await use(appPage)
|
||||
},
|
||||
dev_one_account: async ({}, use) => {
|
||||
const account = testAccounts.dev_one_account()
|
||||
|
||||
75
tests/e2e/web/pages/app.ts
Normal file
75
tests/e2e/web/pages/app.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import {Page} from '@playwright/test'
|
||||
import {UserAccountInformation} from '../utils/accountInformation'
|
||||
import {AuthPage} from './authPage'
|
||||
import {ComatibilityPage} from './compatibilityPage'
|
||||
import {HomePage} from './homePage'
|
||||
import {OnboardingPage} from './onboardingPage'
|
||||
import {OrganizationPage} from './organizationPage'
|
||||
import {ProfilePage} from './profilePage'
|
||||
import {SettingsPage} from './settingsPage'
|
||||
import {SignUpPage} from './signUpPage'
|
||||
import {SocialPage} from './socialPage'
|
||||
|
||||
export class App {
|
||||
readonly auth: AuthPage
|
||||
readonly compatibility: ComatibilityPage
|
||||
readonly home: HomePage
|
||||
readonly onboarding: OnboardingPage
|
||||
readonly organization: OrganizationPage
|
||||
readonly profile: ProfilePage
|
||||
readonly settings: SettingsPage
|
||||
readonly signUp: SignUpPage
|
||||
readonly social: SocialPage
|
||||
|
||||
constructor(public readonly page: Page) {
|
||||
this.auth = new AuthPage(page)
|
||||
this.compatibility = new ComatibilityPage(page)
|
||||
this.home = new HomePage(page)
|
||||
this.onboarding = new OnboardingPage(page)
|
||||
this.organization = new OrganizationPage(page)
|
||||
this.profile = new ProfilePage(page)
|
||||
this.settings = new SettingsPage(page)
|
||||
this.signUp = new SignUpPage(page)
|
||||
this.social = new SocialPage(page)
|
||||
}
|
||||
|
||||
async deleteProfileFromSettings() {
|
||||
await this.home.clickSettingsLink()
|
||||
await this.settings.clickDeleteAccountButton()
|
||||
await this.settings.fillDeleteAccountSurvey('Delete me')
|
||||
await this.settings.clickDeleteAccountButton()
|
||||
await this.home.verifyHomePageLinks()
|
||||
}
|
||||
|
||||
async skipOnboardingHeadToProfile(account: UserAccountInformation) {
|
||||
await this.onboarding.clickSkipOnboardingButton()
|
||||
await this.signUp.fillDisplayName(account.display_name)
|
||||
await this.signUp.fillUsername(account.username)
|
||||
await this.signUp.clickNextButton()
|
||||
await this.signUp.clickNextButton()
|
||||
await this.profile.clickCloseButton()
|
||||
await this.onboarding.clickRefineProfileButton()
|
||||
}
|
||||
|
||||
async registerWithEmail(account: UserAccountInformation) {
|
||||
await this.home.goToRegisterPage()
|
||||
await this.auth.fillEmailField(account.email)
|
||||
await this.auth.fillPasswordField(account.password)
|
||||
await this.auth.clickSignUpWithEmailButton()
|
||||
}
|
||||
|
||||
async signinWithEmail(accountOrEmail: UserAccountInformation | string, password?: string) {
|
||||
const email = typeof accountOrEmail === 'string' ? accountOrEmail : accountOrEmail.email
|
||||
|
||||
const resolvedPassword = typeof accountOrEmail === 'string' ? password : accountOrEmail.password
|
||||
|
||||
if (!email || !resolvedPassword) {
|
||||
throw new Error('Provide either an `account` or `email` and `password`.')
|
||||
}
|
||||
|
||||
await this.home.goToSigninPage()
|
||||
await this.auth.fillEmailField(email)
|
||||
await this.auth.fillPasswordField(resolvedPassword)
|
||||
await this.auth.clickSignInWithEmailButton()
|
||||
}
|
||||
}
|
||||
@@ -1,138 +1,133 @@
|
||||
import {userInformationFromDb} from '../../utils/databaseUtils'
|
||||
import {expect, test} from '../fixtures/base'
|
||||
import {registerWithEmail, skipOnboardingHeadToProfile} from '../utils/testCleanupHelpers'
|
||||
|
||||
test.describe('when given valid input', () => {
|
||||
test('should successfully complete the onboarding flow with email', async ({
|
||||
homePage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
authPage,
|
||||
profilePage,
|
||||
app,
|
||||
onboardingAccount,
|
||||
}) => {
|
||||
await registerWithEmail(homePage, authPage, onboardingAccount)
|
||||
await onboardingPage.clickContinueButton() //First continue
|
||||
await onboardingPage.clickContinueButton() //Second continue
|
||||
await onboardingPage.clickGetStartedButton()
|
||||
await signUpPage.fillDisplayName(onboardingAccount.display_name)
|
||||
await signUpPage.fillUsername(onboardingAccount.username)
|
||||
await signUpPage.clickNextButton()
|
||||
await signUpPage.chooseGender(onboardingAccount.gender)
|
||||
await signUpPage.fillAge(onboardingAccount.age)
|
||||
await signUpPage.fillHeight({
|
||||
await app.registerWithEmail(onboardingAccount)
|
||||
await app.onboarding.clickContinueButton() //First continue
|
||||
await app.onboarding.clickContinueButton() //Second continue
|
||||
await app.onboarding.clickGetStartedButton()
|
||||
await app.signUp.fillDisplayName(onboardingAccount.display_name)
|
||||
await app.signUp.fillUsername(onboardingAccount.username)
|
||||
await app.signUp.clickNextButton()
|
||||
await app.signUp.chooseGender(onboardingAccount.gender)
|
||||
await app.signUp.fillAge(onboardingAccount.age)
|
||||
await app.signUp.fillHeight({
|
||||
feet: onboardingAccount.height?.feet,
|
||||
inches: onboardingAccount.height?.inches,
|
||||
})
|
||||
await signUpPage.fillEthnicity(onboardingAccount.ethnicity_origin)
|
||||
await signUpPage.fillHeadline(onboardingAccount.headline)
|
||||
await signUpPage.fillKeywords(onboardingAccount.keywords)
|
||||
await signUpPage.fillInterestedInConnectingWith(onboardingAccount.interested_in)
|
||||
await signUpPage.fillAgeRangeInterest(
|
||||
await app.signUp.fillEthnicity(onboardingAccount.ethnicity_origin)
|
||||
await app.signUp.fillHeadline(onboardingAccount.headline)
|
||||
await app.signUp.fillKeywords(onboardingAccount.keywords)
|
||||
await app.signUp.fillInterestedInConnectingWith(onboardingAccount.interested_in)
|
||||
await app.signUp.fillAgeRangeInterest(
|
||||
onboardingAccount.Interested_in_ages?.min,
|
||||
onboardingAccount.Interested_in_ages?.max,
|
||||
)
|
||||
await signUpPage.setConnectionType(onboardingAccount.connection_type)
|
||||
await signUpPage.setRelationshipStatus(onboardingAccount.relationship_status)
|
||||
await signUpPage.setRelationshipStyle(onboardingAccount.relationship_style)
|
||||
await signUpPage.fillCurrentNumberOfChildren(onboardingAccount.number_of_kids)
|
||||
await signUpPage.setWantChildrenExpectation(onboardingAccount.children_expectation)
|
||||
await signUpPage.setInterests(onboardingAccount.interests)
|
||||
await signUpPage.setCauses(onboardingAccount.causes)
|
||||
await signUpPage.setHighestEducationLevel(onboardingAccount.education_level)
|
||||
await signUpPage.fillUniversity(onboardingAccount.university)
|
||||
await signUpPage.fillJobTitle(onboardingAccount.job_title)
|
||||
await signUpPage.fillCompany(onboardingAccount.company)
|
||||
await signUpPage.setWorkArea(onboardingAccount.work_area)
|
||||
await signUpPage.setPoliticalBeliefs(
|
||||
await app.signUp.setConnectionType(onboardingAccount.connection_type)
|
||||
await app.signUp.setRelationshipStatus(onboardingAccount.relationship_status)
|
||||
await app.signUp.setRelationshipStyle(onboardingAccount.relationship_style)
|
||||
await app.signUp.fillCurrentNumberOfChildren(onboardingAccount.number_of_kids)
|
||||
await app.signUp.setWantChildrenExpectation(onboardingAccount.children_expectation)
|
||||
await app.signUp.setInterests(onboardingAccount.interests)
|
||||
await app.signUp.setCauses(onboardingAccount.causes)
|
||||
await app.signUp.setHighestEducationLevel(onboardingAccount.education_level)
|
||||
await app.signUp.fillUniversity(onboardingAccount.university)
|
||||
await app.signUp.fillJobTitle(onboardingAccount.job_title)
|
||||
await app.signUp.fillCompany(onboardingAccount.company)
|
||||
await app.signUp.setWorkArea(onboardingAccount.work_area)
|
||||
await app.signUp.setPoliticalBeliefs(
|
||||
onboardingAccount.beliefs?.political?.belief,
|
||||
onboardingAccount.beliefs?.political?.details,
|
||||
)
|
||||
await signUpPage.setReligiousBeliefs(
|
||||
await app.signUp.setReligiousBeliefs(
|
||||
onboardingAccount.beliefs?.religious?.belief,
|
||||
onboardingAccount.beliefs?.religious?.details,
|
||||
)
|
||||
await signUpPage.setPersonalityType(onboardingAccount.personality_type)
|
||||
await signUpPage.setOpennessPersonalityValue(
|
||||
await app.signUp.setPersonalityType(onboardingAccount.personality_type)
|
||||
await app.signUp.setOpennessPersonalityValue(
|
||||
onboardingAccount.big_five_personality_traits?.openness,
|
||||
)
|
||||
await signUpPage.setAgreeablenessPersonalityValue(
|
||||
await app.signUp.setAgreeablenessPersonalityValue(
|
||||
onboardingAccount.big_five_personality_traits?.agreeableness,
|
||||
)
|
||||
await signUpPage.setConscientiousnessPersonalityValue(
|
||||
await app.signUp.setConscientiousnessPersonalityValue(
|
||||
onboardingAccount.big_five_personality_traits?.conscientiousness,
|
||||
)
|
||||
await signUpPage.setExtraversionPersonalityValue(
|
||||
await app.signUp.setExtraversionPersonalityValue(
|
||||
onboardingAccount.big_five_personality_traits?.extraversion,
|
||||
)
|
||||
await signUpPage.setNeuroticismPersonalityValue(
|
||||
await app.signUp.setNeuroticismPersonalityValue(
|
||||
onboardingAccount.big_five_personality_traits?.neuroticism,
|
||||
)
|
||||
await signUpPage.setDietType(onboardingAccount.diet)
|
||||
await signUpPage.setIsSmoker(onboardingAccount.is_smoker)
|
||||
await signUpPage.fillAlcoholPerMonth(onboardingAccount.alcohol_consumed_per_month)
|
||||
await signUpPage.setLanguages(onboardingAccount.languages)
|
||||
await signUpPage.addSocialMediaPlatform(onboardingAccount.social_media)
|
||||
await signUpPage.fillBio(onboardingAccount.bio)
|
||||
await signUpPage.clickNextButton()
|
||||
await profilePage.clickCloseButton()
|
||||
await onboardingPage.clickRefineProfileButton()
|
||||
await profilePage.clickAnswerQuestionsButton()
|
||||
const compatQuestionOne = await profilePage.answerCompatibilityQuestion(
|
||||
await app.signUp.setDietType(onboardingAccount.diet)
|
||||
await app.signUp.setIsSmoker(onboardingAccount.is_smoker)
|
||||
await app.signUp.fillAlcoholPerMonth(onboardingAccount.alcohol_consumed_per_month)
|
||||
await app.signUp.setLanguages(onboardingAccount.languages)
|
||||
await app.signUp.addSocialMediaPlatform(onboardingAccount.social_media)
|
||||
await app.signUp.fillBio(onboardingAccount.bio)
|
||||
await app.signUp.clickNextButton()
|
||||
await app.profile.clickCloseButton()
|
||||
await app.onboarding.clickRefineProfileButton()
|
||||
await app.profile.clickAnswerQuestionsButton()
|
||||
const compatQuestionOne = await app.profile.answerCompatibilityQuestion(
|
||||
onboardingAccount.compatibility,
|
||||
)
|
||||
await profilePage.clickNextCompatibilityQuestionButton()
|
||||
await profilePage.clickSkipCompatibilityQuestionButton()
|
||||
await profilePage.clickSkipCompatibilityQuestionButton()
|
||||
await app.profile.clickNextCompatibilityQuestionButton()
|
||||
await app.profile.clickSkipCompatibilityQuestionButton()
|
||||
await app.profile.clickSkipCompatibilityQuestionButton()
|
||||
|
||||
await profilePage.clickCloseButton()
|
||||
await app.profile.clickCloseButton()
|
||||
|
||||
//Verify information is correct
|
||||
await profilePage.verifyDisplayName(onboardingAccount.display_name)
|
||||
await profilePage.verifyHeadline(onboardingAccount.headline)
|
||||
await profilePage.verifyKeywords(onboardingAccount.keywords)
|
||||
await profilePage.verifyGenderLocationHeightAge(
|
||||
await app.profile.verifyDisplayName(onboardingAccount.display_name)
|
||||
await app.profile.verifyHeadline(onboardingAccount.headline)
|
||||
await app.profile.verifyKeywords(onboardingAccount.keywords)
|
||||
await app.profile.verifyGenderLocationHeightAge(
|
||||
onboardingAccount.gender,
|
||||
undefined,
|
||||
onboardingAccount.height?.feet,
|
||||
onboardingAccount.height?.inches,
|
||||
onboardingAccount.age,
|
||||
)
|
||||
await profilePage.verifySeeking(
|
||||
await app.profile.verifySeeking(
|
||||
onboardingAccount.interested_in,
|
||||
onboardingAccount.Interested_in_ages?.min,
|
||||
onboardingAccount.Interested_in_ages?.max,
|
||||
onboardingAccount.connection_type,
|
||||
onboardingAccount.relationship_style,
|
||||
)
|
||||
await profilePage.verifyRelationshipStatus(onboardingAccount.relationship_status)
|
||||
await profilePage.verifyCurrentNumberOfKids(onboardingAccount.number_of_kids)
|
||||
await profilePage.verifyWantChildrenExpectation(onboardingAccount.children_expectation)
|
||||
await profilePage.verifyInterests(onboardingAccount.interests)
|
||||
await profilePage.verifyCauses(onboardingAccount.causes)
|
||||
await profilePage.verifyEducationLevelAndUniversity(
|
||||
await app.profile.verifyRelationshipStatus(onboardingAccount.relationship_status)
|
||||
await app.profile.verifyCurrentNumberOfKids(onboardingAccount.number_of_kids)
|
||||
await app.profile.verifyWantChildrenExpectation(onboardingAccount.children_expectation)
|
||||
await app.profile.verifyInterests(onboardingAccount.interests)
|
||||
await app.profile.verifyCauses(onboardingAccount.causes)
|
||||
await app.profile.verifyEducationLevelAndUniversity(
|
||||
onboardingAccount.education_level,
|
||||
onboardingAccount.university,
|
||||
)
|
||||
await profilePage.verifyJobInformation(onboardingAccount.job_title, onboardingAccount.company)
|
||||
await profilePage.verifyWorkArea(onboardingAccount.work_area)
|
||||
await profilePage.verifyPoliticalBeliefs(
|
||||
await app.profile.verifyJobInformation(onboardingAccount.job_title, onboardingAccount.company)
|
||||
await app.profile.verifyWorkArea(onboardingAccount.work_area)
|
||||
await app.profile.verifyPoliticalBeliefs(
|
||||
onboardingAccount.beliefs?.political?.belief,
|
||||
onboardingAccount.beliefs?.political?.details,
|
||||
)
|
||||
await profilePage.verifyReligiousBeliefs(
|
||||
await app.profile.verifyReligiousBeliefs(
|
||||
onboardingAccount.beliefs?.religious?.belief,
|
||||
onboardingAccount.beliefs?.religious?.details,
|
||||
)
|
||||
await profilePage.verifyPersonalityType(onboardingAccount.personality_type)
|
||||
await profilePage.verifyBigFivePersonalitySection(onboardingAccount.big_five_personality_traits)
|
||||
await profilePage.verifyDiet(onboardingAccount.diet)
|
||||
await profilePage.verifySmoker(onboardingAccount.is_smoker)
|
||||
await profilePage.verifyDrinksPerMonth(onboardingAccount.alcohol_consumed_per_month)
|
||||
await profilePage.verifyLanguages(onboardingAccount.languages)
|
||||
await profilePage.verifySocialMedia(onboardingAccount.social_media)
|
||||
await profilePage.verifyBio(onboardingAccount.bio)
|
||||
await profilePage.verifyCompatibilityAnswers(compatQuestionOne)
|
||||
await app.profile.verifyPersonalityType(onboardingAccount.personality_type)
|
||||
await app.profile.verifyBigFivePersonalitySection(onboardingAccount.big_five_personality_traits)
|
||||
await app.profile.verifyDiet(onboardingAccount.diet)
|
||||
await app.profile.verifySmoker(onboardingAccount.is_smoker)
|
||||
await app.profile.verifyDrinksPerMonth(onboardingAccount.alcohol_consumed_per_month)
|
||||
await app.profile.verifyLanguages(onboardingAccount.languages)
|
||||
await app.profile.verifySocialMedia(onboardingAccount.social_media)
|
||||
await app.profile.verifyBio(onboardingAccount.bio)
|
||||
await app.profile.verifyCompatibilityAnswers(compatQuestionOne)
|
||||
|
||||
//Verify Database Information
|
||||
const dbInfo = await userInformationFromDb(onboardingAccount)
|
||||
@@ -219,26 +214,22 @@ test.describe('when given valid input', () => {
|
||||
})
|
||||
|
||||
test('should successfully complete the onboarding flow with google account', async ({
|
||||
homePage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
authPage,
|
||||
profilePage,
|
||||
app,
|
||||
googleAccountOne,
|
||||
headless,
|
||||
}) => {
|
||||
test.skip(headless, 'Google popup auth test requires headed mode')
|
||||
await homePage.goToRegisterPage()
|
||||
await authPage.fillPasswordField('') //The test only passes when this is added...something is weird here
|
||||
await authPage.signInToGoogleAccount(
|
||||
await app.home.goToRegisterPage()
|
||||
await app.auth.fillPasswordField('') //The test only passes when this is added...something is weird here
|
||||
await app.auth.signInToGoogleAccount(
|
||||
googleAccountOne.email,
|
||||
googleAccountOne.display_name,
|
||||
googleAccountOne.username,
|
||||
)
|
||||
await skipOnboardingHeadToProfile(onboardingPage, signUpPage, profilePage, googleAccountOne)
|
||||
await app.skipOnboardingHeadToProfile(googleAccountOne)
|
||||
|
||||
//Verify displayed information is correct
|
||||
await profilePage.verifyDisplayName(googleAccountOne.display_name)
|
||||
await app.profile.verifyDisplayName(googleAccountOne.display_name)
|
||||
|
||||
//Verify database info
|
||||
const dbInfo = await userInformationFromDb(googleAccountOne)
|
||||
@@ -247,19 +238,12 @@ test.describe('when given valid input', () => {
|
||||
await expect(dbInfo.user.username).toContain(googleAccountOne.username)
|
||||
})
|
||||
|
||||
test('should successfully skip the onboarding flow', async ({
|
||||
homePage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
authPage,
|
||||
profilePage,
|
||||
fakerAccount,
|
||||
}) => {
|
||||
await registerWithEmail(homePage, authPage, fakerAccount)
|
||||
await skipOnboardingHeadToProfile(onboardingPage, signUpPage, profilePage, fakerAccount)
|
||||
test('should successfully skip the onboarding flow', async ({app, fakerAccount}) => {
|
||||
await app.registerWithEmail(fakerAccount)
|
||||
await app.skipOnboardingHeadToProfile(fakerAccount)
|
||||
|
||||
//Verify displayed information is correct
|
||||
await profilePage.verifyDisplayName(fakerAccount.display_name)
|
||||
await app.profile.verifyDisplayName(fakerAccount.display_name)
|
||||
|
||||
//Verify database info
|
||||
const dbInfo = await userInformationFromDb(fakerAccount)
|
||||
@@ -269,27 +253,23 @@ test.describe('when given valid input', () => {
|
||||
})
|
||||
|
||||
test('should successfully enter optional information after completing flow', async ({
|
||||
homePage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
authPage,
|
||||
profilePage,
|
||||
app,
|
||||
fakerAccount,
|
||||
}) => {
|
||||
await registerWithEmail(homePage, authPage, fakerAccount)
|
||||
await skipOnboardingHeadToProfile(onboardingPage, signUpPage, profilePage, fakerAccount)
|
||||
await profilePage.clickEditProfileButton()
|
||||
await signUpPage.chooseGender(fakerAccount.gender)
|
||||
await signUpPage.fillAge(fakerAccount.age)
|
||||
await signUpPage.fillHeight({
|
||||
await app.registerWithEmail(fakerAccount)
|
||||
await app.skipOnboardingHeadToProfile(fakerAccount)
|
||||
await app.profile.clickEditProfileButton()
|
||||
await app.signUp.chooseGender(fakerAccount.gender)
|
||||
await app.signUp.fillAge(fakerAccount.age)
|
||||
await app.signUp.fillHeight({
|
||||
feet: fakerAccount.height?.feet,
|
||||
inches: fakerAccount.height?.inches,
|
||||
})
|
||||
await signUpPage.saveProfileChanges()
|
||||
await app.signUp.saveProfileChanges()
|
||||
|
||||
//Verify displayed information is correct
|
||||
await profilePage.verifyDisplayName(fakerAccount.display_name)
|
||||
await profilePage.verifyGenderLocationHeightAge(
|
||||
await app.profile.verifyDisplayName(fakerAccount.display_name)
|
||||
await app.profile.verifyGenderLocationHeightAge(
|
||||
fakerAccount.gender,
|
||||
undefined,
|
||||
fakerAccount.height?.feet,
|
||||
@@ -308,31 +288,27 @@ test.describe('when given valid input', () => {
|
||||
})
|
||||
|
||||
test('should successfully use the start answering option', async ({
|
||||
homePage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
authPage,
|
||||
profilePage,
|
||||
app,
|
||||
fakerAccount,
|
||||
onboardingAccount,
|
||||
}) => {
|
||||
await registerWithEmail(homePage, authPage, fakerAccount)
|
||||
await onboardingPage.clickSkipOnboardingButton()
|
||||
await signUpPage.fillDisplayName(fakerAccount.display_name)
|
||||
await signUpPage.fillUsername(fakerAccount.username)
|
||||
await signUpPage.clickNextButton()
|
||||
await signUpPage.clickNextButton() //Skip optional information
|
||||
await profilePage.clickStartAnsweringButton()
|
||||
const compatTwoQuestionOne = await profilePage.answerCompatibilityQuestion(
|
||||
await app.registerWithEmail(fakerAccount)
|
||||
await app.onboarding.clickSkipOnboardingButton()
|
||||
await app.signUp.fillDisplayName(fakerAccount.display_name)
|
||||
await app.signUp.fillUsername(fakerAccount.username)
|
||||
await app.signUp.clickNextButton()
|
||||
await app.signUp.clickNextButton() //Skip optional information
|
||||
await app.profile.clickStartAnsweringButton()
|
||||
const compatTwoQuestionOne = await app.profile.answerCompatibilityQuestion(
|
||||
onboardingAccount.compatibility,
|
||||
)
|
||||
await profilePage.clickNextCompatibilityQuestionButton()
|
||||
await profilePage.clickCloseButton()
|
||||
await onboardingPage.clickRefineProfileButton()
|
||||
await app.profile.clickNextCompatibilityQuestionButton()
|
||||
await app.profile.clickCloseButton()
|
||||
await app.onboarding.clickRefineProfileButton()
|
||||
|
||||
//Verify displayed information is correct
|
||||
await profilePage.verifyDisplayName(fakerAccount.display_name)
|
||||
await profilePage.verifyCompatibilityAnswers(compatTwoQuestionOne)
|
||||
await app.profile.verifyDisplayName(fakerAccount.display_name)
|
||||
await app.profile.verifyCompatibilityAnswers(compatTwoQuestionOne)
|
||||
|
||||
//Verify database info
|
||||
const dbInfo = await userInformationFromDb(fakerAccount)
|
||||
@@ -342,29 +318,22 @@ test.describe('when given valid input', () => {
|
||||
})
|
||||
|
||||
test.describe('should successfully complete the onboarding flow after using the back button', () => {
|
||||
test("the first time it's an option", async ({
|
||||
homePage,
|
||||
authPage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
profilePage,
|
||||
fakerAccount,
|
||||
}) => {
|
||||
await registerWithEmail(homePage, authPage, fakerAccount)
|
||||
await onboardingPage.clickContinueButton()
|
||||
await onboardingPage.clickBackButton()
|
||||
await onboardingPage.clickContinueButton()
|
||||
await onboardingPage.clickContinueButton()
|
||||
await onboardingPage.clickGetStartedButton()
|
||||
await signUpPage.fillDisplayName(fakerAccount.display_name)
|
||||
await signUpPage.fillUsername(fakerAccount.username)
|
||||
await signUpPage.clickNextButton()
|
||||
await signUpPage.clickNextButton() //Skip optional information
|
||||
await profilePage.clickCloseButton()
|
||||
await onboardingPage.clickRefineProfileButton()
|
||||
test("the first time it's an option", async ({app, fakerAccount}) => {
|
||||
await app.registerWithEmail(fakerAccount)
|
||||
await app.onboarding.clickContinueButton()
|
||||
await app.onboarding.clickBackButton()
|
||||
await app.onboarding.clickContinueButton()
|
||||
await app.onboarding.clickContinueButton()
|
||||
await app.onboarding.clickGetStartedButton()
|
||||
await app.signUp.fillDisplayName(fakerAccount.display_name)
|
||||
await app.signUp.fillUsername(fakerAccount.username)
|
||||
await app.signUp.clickNextButton()
|
||||
await app.signUp.clickNextButton() //Skip optional information
|
||||
await app.profile.clickCloseButton()
|
||||
await app.onboarding.clickRefineProfileButton()
|
||||
|
||||
//Verify displayed information is correct
|
||||
await profilePage.verifyDisplayName(fakerAccount.display_name)
|
||||
await app.profile.verifyDisplayName(fakerAccount.display_name)
|
||||
|
||||
//Verify database info
|
||||
const dbInfo = await userInformationFromDb(fakerAccount)
|
||||
@@ -373,29 +342,22 @@ test.describe('when given valid input', () => {
|
||||
await expect(dbInfo.user.username).toContain(fakerAccount.username)
|
||||
})
|
||||
|
||||
test("the second time it's an option", async ({
|
||||
homePage,
|
||||
authPage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
profilePage,
|
||||
fakerAccount,
|
||||
}) => {
|
||||
await registerWithEmail(homePage, authPage, fakerAccount)
|
||||
await onboardingPage.clickContinueButton()
|
||||
await onboardingPage.clickContinueButton()
|
||||
await onboardingPage.clickBackButton()
|
||||
await onboardingPage.clickContinueButton()
|
||||
await onboardingPage.clickGetStartedButton()
|
||||
await signUpPage.fillDisplayName(fakerAccount.display_name)
|
||||
await signUpPage.fillUsername(fakerAccount.username)
|
||||
await signUpPage.clickNextButton()
|
||||
await signUpPage.clickNextButton() //Skip optional information
|
||||
await profilePage.clickCloseButton()
|
||||
await onboardingPage.clickRefineProfileButton()
|
||||
test("the second time it's an option", async ({app, fakerAccount}) => {
|
||||
await app.registerWithEmail(fakerAccount)
|
||||
await app.onboarding.clickContinueButton()
|
||||
await app.onboarding.clickContinueButton()
|
||||
await app.onboarding.clickBackButton()
|
||||
await app.onboarding.clickContinueButton()
|
||||
await app.onboarding.clickGetStartedButton()
|
||||
await app.signUp.fillDisplayName(fakerAccount.display_name)
|
||||
await app.signUp.fillUsername(fakerAccount.username)
|
||||
await app.signUp.clickNextButton()
|
||||
await app.signUp.clickNextButton() //Skip optional information
|
||||
await app.profile.clickCloseButton()
|
||||
await app.onboarding.clickRefineProfileButton()
|
||||
|
||||
//Verify displayed information is correct
|
||||
await profilePage.verifyDisplayName(fakerAccount.display_name)
|
||||
await app.profile.verifyDisplayName(fakerAccount.display_name)
|
||||
|
||||
//Verify database info
|
||||
const dbInfo = await userInformationFromDb(fakerAccount)
|
||||
|
||||
@@ -2,12 +2,6 @@ import {userInformationFromDb} from '../../utils/databaseUtils'
|
||||
import {seedUser} from '../../utils/seedDatabase'
|
||||
import {expect, test} from '../fixtures/signInFixture'
|
||||
import {testAccounts} from '../utils/accountInformation'
|
||||
import {
|
||||
deleteProfileFromSettings,
|
||||
registerWithEmail,
|
||||
signinWithEmail,
|
||||
skipOnboardingHeadToProfile,
|
||||
} from '../utils/testCleanupHelpers'
|
||||
|
||||
//Seed the account
|
||||
test.beforeAll(async () => {
|
||||
@@ -26,30 +20,21 @@ test.beforeAll(async () => {
|
||||
})
|
||||
|
||||
test.describe('when given valid input', () => {
|
||||
test('should be able to sign in to an available account', async ({
|
||||
homePage,
|
||||
authPage,
|
||||
dev_one_account,
|
||||
}) => {
|
||||
await signinWithEmail(homePage, authPage, dev_one_account)
|
||||
await homePage.goToHomePage()
|
||||
await homePage.verifySignedInHomePage(dev_one_account.display_name)
|
||||
test('should be able to sign in to an available account', async ({app, dev_one_account}) => {
|
||||
await app.signinWithEmail(dev_one_account)
|
||||
await app.home.goToHomePage()
|
||||
await app.home.verifySignedInHomePage(dev_one_account.display_name)
|
||||
})
|
||||
|
||||
test('should successfully delete an account created via email and password', async ({
|
||||
homePage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
authPage,
|
||||
profilePage,
|
||||
settingsPage,
|
||||
app,
|
||||
fakerAccount,
|
||||
}) => {
|
||||
await registerWithEmail(homePage, authPage, fakerAccount)
|
||||
await skipOnboardingHeadToProfile(onboardingPage, signUpPage, profilePage, fakerAccount)
|
||||
await app.registerWithEmail(fakerAccount)
|
||||
await app.skipOnboardingHeadToProfile(fakerAccount)
|
||||
|
||||
//Verify displayed information is correct
|
||||
await profilePage.verifyDisplayName(fakerAccount.display_name)
|
||||
await app.profile.verifyDisplayName(fakerAccount.display_name)
|
||||
|
||||
//Verify database info
|
||||
const dbInfo = await userInformationFromDb(fakerAccount)
|
||||
@@ -57,31 +42,26 @@ test.describe('when given valid input', () => {
|
||||
await expect(dbInfo.user.name).toContain(fakerAccount.display_name)
|
||||
await expect(dbInfo.user.username).toContain(fakerAccount.username)
|
||||
|
||||
await deleteProfileFromSettings(homePage, settingsPage)
|
||||
await app.deleteProfileFromSettings()
|
||||
})
|
||||
|
||||
test('should successfully delete an account created via google auth', async ({
|
||||
homePage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
authPage,
|
||||
profilePage,
|
||||
settingsPage,
|
||||
app,
|
||||
googleAccountTwo,
|
||||
headless,
|
||||
}) => {
|
||||
test.skip(headless, 'Google popup auth test requires headed mode')
|
||||
await homePage.goToRegisterPage()
|
||||
await authPage.fillPasswordField('') //The test only passes when this is added...something is weird here
|
||||
await authPage.signInToGoogleAccount(
|
||||
await app.home.goToRegisterPage()
|
||||
await app.auth.fillPasswordField('') //The test only passes when this is added...something is weird here
|
||||
await app.auth.signInToGoogleAccount(
|
||||
googleAccountTwo.email,
|
||||
googleAccountTwo.display_name,
|
||||
googleAccountTwo.username,
|
||||
)
|
||||
await skipOnboardingHeadToProfile(onboardingPage, signUpPage, profilePage, googleAccountTwo)
|
||||
await app.skipOnboardingHeadToProfile(googleAccountTwo)
|
||||
|
||||
//Verify displayed information is correct
|
||||
await profilePage.verifyDisplayName(googleAccountTwo.display_name)
|
||||
await app.profile.verifyDisplayName(googleAccountTwo.display_name)
|
||||
|
||||
//Verify database info
|
||||
const dbInfo = await userInformationFromDb(googleAccountTwo)
|
||||
@@ -89,18 +69,17 @@ test.describe('when given valid input', () => {
|
||||
await expect(dbInfo.user.name).toContain(googleAccountTwo.display_name)
|
||||
await expect(dbInfo.user.username).toContain(googleAccountTwo.username)
|
||||
|
||||
await deleteProfileFromSettings(homePage, settingsPage)
|
||||
await app.deleteProfileFromSettings()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('when given invalid input', () => {
|
||||
test('should not be able to sign in to an available account', async ({
|
||||
homePage,
|
||||
authPage,
|
||||
app,
|
||||
dev_one_account,
|
||||
page,
|
||||
}) => {
|
||||
await signinWithEmail(homePage, authPage, dev_one_account.email, 'ThisPassword')
|
||||
await app.signinWithEmail(dev_one_account.email, 'ThisPassword')
|
||||
await expect(
|
||||
page.getByText('Failed to sign in with your email and password', {exact: true}),
|
||||
).toBeVisible()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {expect, test} from '../fixtures/base'
|
||||
import {registerWithEmail} from '../utils/testCleanupHelpers'
|
||||
|
||||
// test.describe('when given valid input', () => {
|
||||
// test('placeholder', async () => {})
|
||||
@@ -8,31 +7,25 @@ import {registerWithEmail} from '../utils/testCleanupHelpers'
|
||||
test.describe('when an error occurs', () => {
|
||||
test('should disable the button "Next" when the display name field is empty', async ({
|
||||
specAccount,
|
||||
homePage,
|
||||
authPage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
app,
|
||||
}) => {
|
||||
await registerWithEmail(homePage, authPage, specAccount)
|
||||
await onboardingPage.clickSkipOnboardingButton()
|
||||
await signUpPage.fillDisplayName('')
|
||||
await signUpPage.fillUsername(specAccount.username)
|
||||
await signUpPage.verifyDisplayNameError()
|
||||
await expect(signUpPage.nextButtonLocator).toBeDisabled()
|
||||
await app.registerWithEmail(specAccount)
|
||||
await app.onboarding.clickSkipOnboardingButton()
|
||||
await app.signUp.fillDisplayName('')
|
||||
await app.signUp.fillUsername(specAccount.username)
|
||||
await app.signUp.verifyDisplayNameError()
|
||||
await expect(app.signUp.nextButtonLocator).toBeDisabled()
|
||||
})
|
||||
|
||||
test('should disable the button "Next" when the username field is empty', async ({
|
||||
specAccount,
|
||||
homePage,
|
||||
authPage,
|
||||
onboardingPage,
|
||||
signUpPage,
|
||||
app,
|
||||
}) => {
|
||||
await registerWithEmail(homePage, authPage, specAccount)
|
||||
await onboardingPage.clickSkipOnboardingButton()
|
||||
await signUpPage.fillDisplayName(specAccount.display_name)
|
||||
await signUpPage.fillUsername('')
|
||||
await signUpPage.verifyUsernameError()
|
||||
await expect(signUpPage.nextButtonLocator).toBeDisabled()
|
||||
await app.registerWithEmail(specAccount)
|
||||
await app.onboarding.clickSkipOnboardingButton()
|
||||
await app.signUp.fillDisplayName(specAccount.display_name)
|
||||
await app.signUp.fillUsername('')
|
||||
await app.signUp.verifyUsernameError()
|
||||
await expect(app.signUp.nextButtonLocator).toBeDisabled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import {AuthPage} from '../pages/AuthPage'
|
||||
import {HomePage} from '../pages/homePage'
|
||||
import { OnboardingPage } from '../pages/onboardingPage'
|
||||
import { ProfilePage } from '../pages/profilePage'
|
||||
import { SettingsPage } from '../pages/settingsPage'
|
||||
import { SignUpPage } from '../pages/signUpPage'
|
||||
import {UserAccountInformation} from '../utils/accountInformation'
|
||||
|
||||
export async function registerWithEmail(
|
||||
homePage: HomePage,
|
||||
authPage: AuthPage,
|
||||
account: UserAccountInformation,
|
||||
) {
|
||||
await homePage.goToRegisterPage()
|
||||
await authPage.fillEmailField(account.email)
|
||||
await authPage.fillPasswordField(account.password)
|
||||
await authPage.clickSignUpWithEmailButton()
|
||||
}
|
||||
|
||||
export async function signinWithEmail(
|
||||
homePage: HomePage,
|
||||
authPage: AuthPage,
|
||||
accountOrEmail: UserAccountInformation | string,
|
||||
password?: string,
|
||||
) {
|
||||
const email = typeof accountOrEmail === 'string' ? accountOrEmail : accountOrEmail.email
|
||||
|
||||
const resolvedPassword = typeof accountOrEmail === 'string' ? password : accountOrEmail.password
|
||||
|
||||
if (!email || !resolvedPassword) {
|
||||
throw new Error('Provide either an `account` or `email` and `password`.')
|
||||
}
|
||||
|
||||
await homePage.goToSigninPage()
|
||||
await authPage.fillEmailField(email)
|
||||
await authPage.fillPasswordField(resolvedPassword)
|
||||
await authPage.clickSignInWithEmailButton()
|
||||
}
|
||||
|
||||
export async function skipOnboardingHeadToProfile(
|
||||
onboardingPage: OnboardingPage,
|
||||
signUpPage: SignUpPage,
|
||||
profilePage: ProfilePage,
|
||||
account: UserAccountInformation,
|
||||
) {
|
||||
await onboardingPage.clickSkipOnboardingButton()
|
||||
await signUpPage.fillDisplayName(account.display_name)
|
||||
await signUpPage.fillUsername(account.username)
|
||||
await signUpPage.clickNextButton()
|
||||
await signUpPage.clickNextButton()
|
||||
await profilePage.clickCloseButton()
|
||||
await onboardingPage.clickRefineProfileButton()
|
||||
}
|
||||
|
||||
export async function deleteProfileFromSettings(
|
||||
homePage: HomePage,
|
||||
settingsPage: SettingsPage,
|
||||
) {
|
||||
await homePage.clickSettingsLink()
|
||||
await settingsPage.clickDeleteAccountButton()
|
||||
await settingsPage.fillDeleteAccountSurvey('Delete me')
|
||||
await settingsPage.clickDeleteAccountButton()
|
||||
await homePage.verifyHomePageLinks()
|
||||
}
|
||||
Reference in New Issue
Block a user