diff --git a/.github/workflows/cd-api.yml b/.github/workflows/cd-api.yml index a5d9d8d0..60a80806 100644 --- a/.github/workflows/cd-api.yml +++ b/.github/workflows/cd-api.yml @@ -1,7 +1,7 @@ name: API Release on: push: - branches: [ main, master ] + branches: [main, master] paths: - 'backend/api/package.json' - '.github/workflows/cd-api.yml' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d57b393..41a35e36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: lint: diff --git a/backend/email/emails/new-endorsement.tsx b/backend/email/emails/new-endorsement.tsx index cc69f245..b6a65e14 100644 --- a/backend/email/emails/new-endorsement.tsx +++ b/backend/email/emails/new-endorsement.tsx @@ -1,16 +1,7 @@ -import { - Body, - Button, - Container, - Head, - Html, - Preview, - Section, - Text, -} from '@react-email/components' +import {Body, Button, Container, Head, Html, Preview, Section, Text} from '@react-email/components' import {DOMAIN} from 'common/envs/constants' import {type User} from 'common/user' -import { container, content, Footer, main} from 'email/utils' +import {container, content, Footer, main} from 'email/utils' import React from 'react' import {createT} from 'shared/locale' diff --git a/backend/email/emails/new-message.tsx b/backend/email/emails/new-message.tsx index f2a293f5..df9ca041 100644 --- a/backend/email/emails/new-message.tsx +++ b/backend/email/emails/new-message.tsx @@ -13,7 +13,7 @@ import {ANDROID_APP_URL} from 'common/constants' import {DOMAIN} from 'common/envs/constants' import {type ProfileRow} from 'common/profiles/profile' import {type User} from 'common/user' -import { container, content, Footer, main} from 'email/utils' +import {container, content, Footer, main} from 'email/utils' import React from 'react' import {createT} from 'shared/locale' diff --git a/common/src/choices.ts b/common/src/choices.ts index acc81247..35fcc5fc 100644 --- a/common/src/choices.ts +++ b/common/src/choices.ts @@ -339,3 +339,7 @@ export type SubstanceIntentionTuple = { export type SubstancePreferenceTuple = { [K in keyof typeof SUBSTANCE_PREFERENCE_CHOICES]: [K, (typeof SUBSTANCE_PREFERENCE_CHOICES)[K]] }[keyof typeof SUBSTANCE_PREFERENCE_CHOICES] + +export type LastActiveTuple = { + [K in keyof typeof LAST_ONLINE_CHOICES]: [K, (typeof LAST_ONLINE_CHOICES)[K]] +}[keyof typeof LAST_ONLINE_CHOICES] diff --git a/tests/e2e/backend/utils/userInformation.ts b/tests/e2e/backend/utils/userInformation.ts index e159f217..10e687b2 100644 --- a/tests/e2e/backend/utils/userInformation.ts +++ b/tests/e2e/backend/utils/userInformation.ts @@ -7,9 +7,13 @@ import { PSYCHEDELICS_CHOICES, RACE_CHOICES, RELATIONSHIP_CHOICES, + RELATIONSHIP_STATUS_CHOICES, + ROMANTIC_CHOICES, RELIGION_CHOICES, + LANGUAGE_CHOICES, SUBSTANCE_INTENTION_CHOICES, SUBSTANCE_PREFERENCE_CHOICES, + MBTI_CHOICES, } from 'common/choices' class UserAccountInformationForSeeding { @@ -30,8 +34,13 @@ class UserAccountInformationForSeeding { min: faker.number.int({min: 18, max: 27}), max: faker.number.int({min: 36, max: 68}), } - + has_kids = faker.number.int({min: 0, max: 5}) + wants_kids_strength = faker.number.int({min: 0, max: 4}) + is_smoker = faker.datatype.boolean() + relationship_status = Object.values(RELATIONSHIP_STATUS_CHOICES) pref_relation_styles = Object.values(RELATIONSHIP_CHOICES) + pref_romantic_styles = Object.values(ROMANTIC_CHOICES) + languages = Object.values(LANGUAGE_CHOICES) political_beliefs = Object.values(POLITICAL_CHOICES) religion = Object.values(RELIGION_CHOICES) diet = Object.values(DIET_CHOICES) @@ -42,6 +51,7 @@ class UserAccountInformationForSeeding { company = faker.company.name() occupation_title = faker.person.jobTitle() university = faker.company.name() + keywords = faker.lorem.word() cannabis = Object.values(CANNABIS_CHOICES) psychedelics = Object.values(PSYCHEDELICS_CHOICES) @@ -49,6 +59,12 @@ class UserAccountInformationForSeeding { cannabis_pref = Object.values(SUBSTANCE_PREFERENCE_CHOICES) psychedelics_intention = Object.values(SUBSTANCE_INTENTION_CHOICES) psychedelics_pref = Object.values(SUBSTANCE_PREFERENCE_CHOICES) + mbti = Object.values(MBTI_CHOICES) + big5_openness = faker.number.int({min: 0, max: 100}) + big5_conscientiousness = faker.number.int({min: 0, max: 100}) + big5_extraversion = faker.number.int({min: 0, max: 100}) + big5_agreeableness = faker.number.int({min: 0, max: 100}) + big5_neuroticism = faker.number.int({min: 0, max: 100}) randomElement(array: Array) { return array[Math.floor(Math.random() * array.length)].toLowerCase() diff --git a/tests/e2e/utils/firebaseUtils.ts b/tests/e2e/utils/firebaseUtils.ts index 96777d0b..c3dd114e 100644 --- a/tests/e2e/utils/firebaseUtils.ts +++ b/tests/e2e/utils/firebaseUtils.ts @@ -78,7 +78,10 @@ export async function deleteAccount(idToken: any) { * Check if a Firebase user exists by email * Returns userId if exists, undefined if not found */ -export async function firebaseUserExists(email: string, password: string): Promise { +export async function firebaseUserExists( + email: string, + password: string, +): Promise { try { const login = await firebaseLoginEmailPassword(email, password) return login.data.localId diff --git a/tests/e2e/utils/seedDatabase.ts b/tests/e2e/utils/seedDatabase.ts index 3da70fd3..035b3b34 100644 --- a/tests/e2e/utils/seedDatabase.ts +++ b/tests/e2e/utils/seedDatabase.ts @@ -1,3 +1,4 @@ +import {faker} from '@faker-js/faker' import {debug} from 'common/logger' import {PrivateUser} from 'common/user' import {getDefaultNotificationPreferences} from 'common/user-notification-preferences' @@ -37,13 +38,46 @@ export async function seedDbUser( }, ], } + const relationshipStyle = userInfo.randomElement(userInfo.pref_relation_styles) + let romanticStyle: string | null = null + if (relationshipStyle === 'relationship') { + romanticStyle = userInfo.randomElement(userInfo.pref_romantic_styles) + } + + const numberOfLanguages = faker.number.int({min: 1, max: 3}) + let languagesKnown = [] + + for (let i = 0; i < numberOfLanguages; i++) { + languagesKnown.push(userInfo.randomElement(userInfo.languages)) + } + + const keywords = faker.number.int({min: 1, max: 4}) + let profileKeywords = [] + for (let i = 0; i < keywords; i++) { + profileKeywords.push(userInfo.keywords) + } + const basicProfile = { user_id: userId, bio_length: userInfo.bio.length, bio: bio, age: userInfo.age, + gender: userInfo.randomElement(userInfo.gender), + ethnicity: [userInfo.randomElement(userInfo.ethnicity)], + height_in_inches: userInfo.height_in_inches, + pref_gender: [userInfo.randomElement(userInfo.pref_gender)], + pref_relation_styles: [relationshipStyle], + relationship_status: [userInfo.randomElement(userInfo.relationship_status)], + pref_romantic_styles: romanticStyle ? [romanticStyle] : [], + pref_age_min: userInfo.pref_age.min, + pref_age_max: userInfo.pref_age.max, born_in_location: userInfo.born_in_location, company: userInfo.company, + occupation_title: userInfo.occupation_title, + religion: [userInfo.randomElement(userInfo.religion)], + has_kids: userInfo.has_kids, + wants_kids_strength: userInfo.wants_kids_strength, + is_smoker: userInfo.is_smoker, } const mediumProfile = { @@ -51,17 +85,12 @@ export async function seedDbUser( drinks_per_month: userInfo.drinks_per_month, diet: [userInfo.randomElement(userInfo.diet)], education_level: userInfo.randomElement(userInfo.education_level), - ethnicity: [userInfo.randomElement(userInfo.ethnicity)], - gender: userInfo.randomElement(userInfo.gender), - height_in_inches: userInfo.height_in_inches, - pref_gender: [userInfo.randomElement(userInfo.pref_gender)], - pref_age_min: userInfo.pref_age.min, - pref_age_max: userInfo.pref_age.max, + languages: languagesKnown, + keywords: profileKeywords, } const fullProfile = { ...mediumProfile, - occupation_title: userInfo.occupation_title, cannabis: userInfo.randomElement(userInfo.cannabis), psychedelics: userInfo.randomElement(userInfo.psychedelics), cannabis_intention: [userInfo.randomElement(userInfo.cannabis_intention)], @@ -69,8 +98,12 @@ export async function seedDbUser( cannabis_pref: [userInfo.randomElement(userInfo.cannabis_pref)], psychedelics_pref: [userInfo.randomElement(userInfo.psychedelics_pref)], political_beliefs: [userInfo.randomElement(userInfo.political_beliefs)], - pref_relation_styles: [userInfo.randomElement(userInfo.pref_relation_styles)], - religion: [userInfo.randomElement(userInfo.religion)], + mbti: userInfo.randomElement(userInfo.mbti), + big5_openness: userInfo.big5_openness, + big5_conscientiousness: userInfo.big5_conscientiousness, + big5_extraversion: userInfo.big5_extraversion, + big5_agreeableness: userInfo.big5_agreeableness, + big5_neuroticism: userInfo.big5_neuroticism, } const profileData = diff --git a/tests/e2e/web/pages/app.ts b/tests/e2e/web/pages/app.ts index a5f25a1b..49048762 100644 --- a/tests/e2e/web/pages/app.ts +++ b/tests/e2e/web/pages/app.ts @@ -10,6 +10,8 @@ import {ProfilePage} from './profilePage' import {SettingsPage} from './settingsPage' import {SignUpPage} from './signUpPage' import {SocialPage} from './socialPage' +import {PeoplePage} from './peoplePage' +import {NotificationPage} from './notificationsPage' export class App { readonly auth: AuthPage @@ -21,6 +23,8 @@ export class App { readonly settings: SettingsPage readonly signUp: SignUpPage readonly social: SocialPage + readonly people: PeoplePage + readonly notifs: NotificationPage constructor(public readonly page: Page) { this.auth = new AuthPage(page) @@ -32,6 +36,8 @@ export class App { this.settings = new SettingsPage(page) this.signUp = new SignUpPage(page) this.social = new SocialPage(page) + this.people = new PeoplePage(page) + this.notifs = new NotificationPage(page) } async deleteProfileFromSettings() { diff --git a/tests/e2e/web/pages/authPage.ts b/tests/e2e/web/pages/authPage.ts index 7bccdf38..c4f82b94 100644 --- a/tests/e2e/web/pages/authPage.ts +++ b/tests/e2e/web/pages/authPage.ts @@ -55,8 +55,10 @@ export class AuthPage { await popup.getByLabel('Email').fill(email) if (display_name) await popup.getByLabel('Display name').fill(display_name) if (username) await popup.getByLabel('Screen name', {exact: true}).fill(username) - await popup.getByText('Sign in with Google.com', {exact: true}).click() - await popup.waitForEvent('close') + await Promise.all([ + popup.waitForEvent('close'), + popup.getByText('Sign in with Google.com', {exact: true}).click(), + ]) } async clickSignUpWithEmailButton() { diff --git a/tests/e2e/web/pages/notificationsPage.ts b/tests/e2e/web/pages/notificationsPage.ts new file mode 100644 index 00000000..90c75aa4 --- /dev/null +++ b/tests/e2e/web/pages/notificationsPage.ts @@ -0,0 +1,11 @@ +import {expect, Locator, Page} from '@playwright/test' + +export class NotificationPage { + private readonly notificationTab: Locator + private readonly settingsTab: Locator + + constructor(public readonly page: Page) { + this.notificationTab = page.getByTestId('notifications-tab') + this.settingsTab = page.getByTestId('settings-tab') + } +} diff --git a/tests/e2e/web/pages/peoplePage.ts b/tests/e2e/web/pages/peoplePage.ts new file mode 100644 index 00000000..8c620f4d --- /dev/null +++ b/tests/e2e/web/pages/peoplePage.ts @@ -0,0 +1,434 @@ +import {expect, Locator, Page} from '@playwright/test' +import { + ConnectionTypeTuple, + GenderTuple, + EducationTuple, + DietTuple, + PsychedelicsTuple, + CannabisTuple, + LanguageTuple, + PoliticalTuple, + ReligionTuple, + PersonalityKey, + LastActiveTuple, +} from 'common/choices' +import {MinMaxNumbers} from '../utils/accountInformation' + +export type BackgroundFilter = { + location?: string + education?: EducationTuple + work?: string +} + +export type LifestyleFilter = { + interest?: string + cause?: string + diet?: DietTuple + alcohol?: MinMaxNumbers + smoker?: string + psychedelics?: PsychedelicsTuple + cannabis?: CannabisTuple + language?: LanguageTuple +} + +export type BeliefsFilter = { + political?: PoliticalTuple + religious?: ReligionTuple +} + +export type PersonalityFilter = { + mbti?: PersonalityKey + bigFive?: BigFive +} + +type BigFive = { + openness?: MinMaxNumbers + conscientiousness?: MinMaxNumbers + extraversion?: MinMaxNumbers + agreeableness?: MinMaxNumbers + neuroticism?: MinMaxNumbers +} + +export type AdvancedFilter = { + lastActive?: LastActiveTuple + photos?: boolean +} + +export type DisplayFilter = { + cardSize?: 'Small' | 'Medium' | 'Large' + filters?: [string, boolean][] +} + +export type PeoplePageFilter = { + connectionFilter?: ConnectionTypeTuple + ageFilter?: MinMaxNumbers + genderFilter?: GenderTuple + backgroundFilter?: BackgroundFilter + lifestyleFilter?: LifestyleFilter + valuesAndBeliefsFilter?: BeliefsFilter + personalityFilter?: PersonalityFilter +} + +export class PeoplePage { + private readonly peopleHeading: Locator + private readonly searchBox: Locator + private readonly profileCount: Locator + private readonly resetFilters: Locator + private readonly yourFiltersCheckbox: Locator + private readonly incompleteProfilesCheckbox: Locator + private readonly connectionTypeDropdown: Locator + private readonly locationDropdown: Locator + private readonly ageRangeDropdown: Locator + private readonly genderDropdown: Locator + private readonly backgroundDropdown: Locator + private readonly backgroundLocation: Locator + private readonly backgroundEducation: Locator + private readonly backgroundWork: Locator + private readonly lifestyleDropdown: Locator + private readonly lifestyleInterests: Locator + private readonly lifestyleCauses: Locator + private readonly lifestyleDiet: Locator + private readonly lifestyleAlcohol: Locator + private readonly lifestyleSmoker: Locator + private readonly lifestylePsychedelics: Locator + private readonly lifestyleCannabis: Locator + private readonly lifestyleLanguages: Locator + private readonly valuesAndBeliefsDropdown: Locator + private readonly valuesAndBeliefsPolitics: Locator + private readonly valuesAndBeliefsReligion: Locator + private readonly personalityDropdown: Locator + private readonly personalityMbti: Locator + private readonly personalityBigFive: Locator + private readonly advancedDropdown: Locator + private readonly advancedActive: Locator + private readonly advancedPhotos: Locator + private readonly displayDropdown: Locator + private readonly profileGrid: Locator + private readonly profileResults: Locator + private readonly profileName: Locator + private readonly profileAgeGender: Locator + + constructor(public readonly page: Page) { + this.peopleHeading = page.getByRole('heading', {name: 'People'}) + this.searchBox = page.getByRole('textbox', {name: 'Search anything...'}) + this.profileCount = page.getByTestId('people-profile-count') + this.resetFilters = page.getByRole('button', {name: 'Reset filters'}) + this.yourFiltersCheckbox = page.getByText('Your filters', {exact: true}) + this.incompleteProfilesCheckbox = page.getByText('Include incomplete profiles', {exact: true}) + this.connectionTypeDropdown = page.getByRole('button', {name: 'Any connection'}) + this.locationDropdown = page.getByRole('button', {name: 'Living anywhere'}) + this.ageRangeDropdown = page.getByRole('button', {name: 'Any age'}) + this.genderDropdown = page.getByRole('button', {name: 'Any gender'}) + this.backgroundDropdown = page.getByRole('button', {name: 'Background'}) + this.backgroundLocation = page.getByRole('button', {name: 'Grew up anywhere'}) + this.backgroundEducation = page.getByText('Any education', {exact: true}) + this.backgroundWork = page.getByText('Any work', {exact: true}) + this.lifestyleDropdown = page.getByRole('button', {name: 'Lifestyle'}) + this.lifestyleInterests = page.getByRole('button', {name: 'Any interests'}) + this.lifestyleCauses = page.getByRole('button', {name: 'Any causes'}) + this.lifestyleDiet = page.getByRole('button', {name: 'Any diet'}) + this.lifestyleAlcohol = page.getByRole('button', {name: 'Any drinks'}) + this.lifestyleSmoker = page.getByTestId('lifestyle-smoker') + this.lifestylePsychedelics = page.getByRole('button', {name: 'Any psychedelics'}) + this.lifestyleCannabis = page.getByRole('button', {name: 'Any cannabis'}) + this.lifestyleLanguages = page.getByRole('button', {name: 'Any language'}) + this.valuesAndBeliefsDropdown = page.getByRole('button', {name: 'Values & Beliefs'}) + this.valuesAndBeliefsPolitics = page.getByRole('button', {name: 'Any politics'}) + this.valuesAndBeliefsReligion = page.getByRole('button', {name: 'Any religion'}) + this.personalityDropdown = page.getByRole('button', {name: 'Personality'}) + this.personalityMbti = page.getByRole('button', {name: 'Any MBTI'}) + this.personalityBigFive = page.getByRole('button', {name: 'Any Big 5'}) + this.advancedDropdown = page.getByRole('button', {name: 'Advanced'}) + this.advancedActive = page.getByTestId('advanced-active') + this.advancedPhotos = page.getByText('Photos', {exact: true}) + this.displayDropdown = page.getByRole('button', {name: 'Display'}) + this.profileGrid = page.getByTestId('people-profile-grid') + this.profileResults = page.getByTestId('people-profile-results') + this.profileName = page.getByTestId('people-profile-name') + this.profileAgeGender = page.getByTestId('people-profile-age-gender') + } + + get profileCountLocator(): Locator { + return this.profileCount + } + + async sliderHelper(range: MinMaxNumbers, locator?: Locator) { + let minSlider + let maxSlider + if (locator) { + minSlider = await locator.getByRole('slider', {name: 'Minimum'}) + maxSlider = await locator.getByRole('slider', {name: 'Maximum'}) + } else { + minSlider = await this.page.getByRole('slider', {name: 'Minimum'}) + maxSlider = await this.page.getByRole('slider', {name: 'Maximum'}) + } + + await expect(minSlider).toBeVisible() + await expect(maxSlider).toBeVisible() + if (range.min === null || range.max === null) return + + const minRange = Number(range.min) + const maxRange = Number(range.max) + const currentMinValue = Number(await minSlider.getAttribute('aria-valuenow')) + const currentMaxValue = Number(await maxSlider.getAttribute('aria-valuenow')) + + if (isNaN(currentMinValue) || isNaN(currentMaxValue)) return + + if (minRange > currentMinValue) { + await minSlider.click() + let iterations = 0 + const MAX_ITERATIONS = 100 + while (true) { + if (iterations++ > MAX_ITERATIONS) { + throw new Error(`Slider adjustment exceeded ${MAX_ITERATIONS} iterations`) + } + const changedMinValue = Number(await minSlider.getAttribute('aria-valuenow')) + + if (isNaN(changedMinValue)) break + if (minRange <= changedMinValue) break + await this.page.keyboard.press('ArrowRight') + } + } + + if (maxRange < currentMaxValue) { + await maxSlider.click() + let iterations = 0 + const MAX_ITERATIONS = 100 + while (true) { + if (iterations++ > MAX_ITERATIONS) { + throw new Error(`Slider adjustment exceeded ${MAX_ITERATIONS} iterations`) + } + const changedMaxValue = Number(await maxSlider.getAttribute('aria-valuenow')) + if (isNaN(changedMaxValue)) break + if (maxRange >= changedMaxValue) break + await this.page.keyboard.press('ArrowLeft') + } + } + } + + async selectOption(trigger: Locator, label: string) { + await expect(trigger).toBeVisible() + await trigger.click() + + const option = this.page.getByLabel(label, {exact: true}) + await expect(option).toBeVisible() + await option.click() + } + + async verifyPeoplePage() { + await expect(this.peopleHeading).toBeVisible() + } + + //Doesn't actually work, need to find out why + async useSearch(item: string) { + await expect(this.searchBox).toBeVisible() + await this.searchBox.click() + await this.searchBox.fill(item) + await this.page.keyboard.press('Enter') + } + + async resetFilter() { + await expect(this.resetFilters).toBeVisible() + await this.resetFilters.click() + } + + async setYourFilters() { + await expect(this.yourFiltersCheckbox).toBeVisible() + await this.yourFiltersCheckbox.click() + } + + async setIncludeIncompleteProfiles() { + await expect(this.incompleteProfilesCheckbox).toBeVisible() + await this.incompleteProfilesCheckbox.click() + } + + async setConnectionTypeFilter(connectionType: ConnectionTypeTuple) { + await this.selectOption(this.connectionTypeDropdown, connectionType[0]) + // await expect(this.connectionTypeDropdown).toBeVisible() + // await this.connectionTypeDropdown.click() + // await expect(this.page.getByLabel(connectionType[0])).toBeVisible() + // await this.page.getByLabel(connectionType[0]).click() + } + + async setLocationFilter(location: string) { + await expect(this.locationDropdown).toBeVisible() + await this.locationDropdown.click() + await expect(this.page.getByRole('textbox', {name: 'Search city...'})).toBeVisible() + await this.page.getByRole('textbox', {name: 'Search city...'}).fill(location) + } + + async setAgeRangeFilter(ageRange: MinMaxNumbers) { + await expect(this.ageRangeDropdown).toBeVisible() + await this.ageRangeDropdown.click() + await this.sliderHelper(ageRange) + } + + async setGenderTypeFilter(genderType: GenderTuple) { + await this.selectOption(this.genderDropdown, genderType[0]) + // await expect(this.genderDropdown).toBeVisible() + // await this.genderDropdown.click() + // await expect(this.page.getByLabel(genderType[0], {exact: true})).toBeVisible() + // await this.page.getByLabel(genderType[0], {exact: true}).click() + } + + async setBackgroundFilter(background: BackgroundFilter) { + await expect(this.backgroundDropdown).toBeVisible() + await this.backgroundDropdown.click() + if (background.location) { + await expect(this.backgroundLocation).toBeVisible() + await this.backgroundLocation.click() + await this.page.getByPlaceholder('Search city...', {exact: true}).fill(background.location) + } + + if (background.education) { + await expect(this.backgroundEducation).toBeVisible() + await this.backgroundEducation.click() + await expect(this.page.getByLabel(background.education[0], {exact: true})).toBeVisible() + await this.page.getByLabel(background.education[0], {exact: true}).click() + } + + if (background.work) { + await expect(this.backgroundWork).toBeVisible() + await this.backgroundWork.click() + await expect(this.page.getByLabel(background.work, {exact: true})).toBeVisible() + await this.page.getByLabel(background.work, {exact: true}).click() + } + } + + async setLifestyleFilter(lifestyle: LifestyleFilter) { + await expect(this.lifestyleDropdown).toBeVisible() + await this.lifestyleDropdown.click() + + if (lifestyle.interest) await this.selectOption(this.lifestyleInterests, lifestyle.interest) + if (lifestyle.cause) await this.selectOption(this.lifestyleCauses, lifestyle.cause) + if (lifestyle.diet) await this.selectOption(this.lifestyleDiet, lifestyle.diet[0]) + + if (lifestyle.alcohol) { + await expect(this.lifestyleAlcohol).toBeVisible() + await this.lifestyleAlcohol.click() + await this.sliderHelper(lifestyle.alcohol) + } + + if (lifestyle.smoker) { + await expect(this.lifestyleSmoker).toBeVisible() + await this.lifestyleSmoker.click() + await expect(this.page.getByText(lifestyle.smoker, {exact: true})).toBeVisible() + await this.page.getByText(lifestyle.smoker, {exact: true}).click() + } + + if (lifestyle.psychedelics) + await this.selectOption(this.lifestylePsychedelics, lifestyle.psychedelics[0]) + if (lifestyle.cannabis) await this.selectOption(this.lifestyleCannabis, lifestyle.cannabis[0]) + if (lifestyle.language) await this.selectOption(this.lifestyleLanguages, lifestyle.language[0]) + } + + async setValuesAndBeliefsFilter(values: BeliefsFilter) { + await expect(this.valuesAndBeliefsDropdown).toBeVisible() + await this.valuesAndBeliefsDropdown.click() + + if (values.political) + await this.selectOption(this.valuesAndBeliefsPolitics, values.political[0]) + if (values.religious) + await this.selectOption(this.valuesAndBeliefsReligion, values.religious[0]) + } + + async setPersonalityFilter(personality: PersonalityFilter) { + await expect(this.personalityDropdown).toBeVisible() + await this.personalityDropdown.click() + + if (personality.mbti) await this.selectOption(this.personalityMbti, personality.mbti) + + if (personality.bigFive) { + await this.personalityBigFive.click() + if (personality.bigFive?.openness) { + await this.sliderHelper( + personality.bigFive.openness, + this.page.getByTestId('big-five-openness'), + ) + } + if (personality.bigFive?.conscientiousness) { + await this.sliderHelper( + personality.bigFive.conscientiousness, + this.page.getByTestId('big-five-conscientiousness'), + ) + } + if (personality.bigFive?.extraversion) { + await this.sliderHelper( + personality.bigFive.extraversion, + this.page.getByTestId('big-five-extraversion'), + ) + } + if (personality.bigFive?.agreeableness) { + await this.sliderHelper( + personality.bigFive.agreeableness, + this.page.getByTestId('big-five-agreeableness'), + ) + } + if (personality.bigFive?.neuroticism) { + await this.sliderHelper( + personality.bigFive.neuroticism, + this.page.getByTestId('big-five-neuroticism'), + ) + } + } + } + + async setAdvancedFilter(advanced: AdvancedFilter) { + await expect(this.advancedDropdown).toBeVisible() + await this.advancedDropdown.click() + + if (advanced.lastActive) { + await this.advancedActive.click() + await this.page.getByRole('button', {name: `${advanced.lastActive[1]}`}).click() + } + + if (advanced.photos) { + await this.advancedPhotos.click() + await this.page.getByRole('checkbox', {name: 'Has photos'}).click() + } + } + + async setDisplayFilter(display: DisplayFilter) { + await expect(this.displayDropdown).toBeVisible() + await this.displayDropdown.click() + + if (display.cardSize) await this.page.getByRole('button', {name: `${display.cardSize}`}).click() + + if (!display.filters) return + if (display.filters?.length > 0) { + for (let i = 0; i < display.filters.length; i++) { + const filter = await this.page.getByRole('checkbox', {name: `${display.filters[i][0]}`}) + await expect(filter).toBeVisible() + const isChecked = await filter.isChecked() + + if (display.filters[i][1]) { + if (isChecked) continue + if (!isChecked) await filter.click() + } else if (!display.filters[i][1]) { + if (isChecked) await filter.click() + if (!isChecked) continue + } + } + } + } + + async getProfileInfo() { + await expect(this.profileGrid).toBeVisible() + const totalResults = await this.profileResults.count() + const chosenProfileNumber = Math.floor(Math.random() * totalResults) + const chosenProfile = await this.profileResults.nth(chosenProfileNumber) + const profileName = await chosenProfile.getByTestId('people-profile-name').textContent() + + if (!profileName) return + return { + name: profileName, + } + } + + async verifyNumberOfMatchingProfiles(count: number) { + await expect(this.profileCount).toBeVisible() + const test = await this.profileCount.textContent() + if (!test) return + expect(actual).toStrictEqual(expected) + } +} diff --git a/tests/e2e/web/specs/signIn.spec.ts b/tests/e2e/web/specs/signIn.spec.ts index 9501062f..537853fb 100644 --- a/tests/e2e/web/specs/signIn.spec.ts +++ b/tests/e2e/web/specs/signIn.spec.ts @@ -9,15 +9,31 @@ test.describe('when given valid input', () => { await app.home.goToHomePage() await app.home.verifySignedInHomePage(account.display_name) }) + + test('the profile count should update sucessfully when applying a filter', async ({ + app, + signedOutAccount: account, + }) => { + await app.signinWithEmail(account) + await app.home.clickPeopleLink() + await app.people.getProfileInfo() + const totalProfiles = await app.people.profileCountLocator.textContent() + await app.people.setConnectionTypeFilter(['Collaboration', 'collaboration']) + + const filterdProfiles = await app.people.profileCountLocator.textContent() + + if (!totalProfiles || !filterdProfiles) return + await expect(parseInt(totalProfiles)).not.toEqual(parseInt(filterdProfiles)) + }) }) test.describe('when given invalid input', () => { test('should not be able to sign in to an available account', async ({ app, - signedOutAccount, + signedOutAccount: account, page, }) => { - await app.signinWithEmail(signedOutAccount.email, 'ThisPassword', false) + await app.signinWithEmail(account.email, 'ThisPassword', false) await expect( page.getByText('Failed to sign in with your email and password', {exact: true}), ).toBeVisible() diff --git a/tests/e2e/web/utils/accountInformation.ts b/tests/e2e/web/utils/accountInformation.ts index bbb6b81f..2d2c4134 100644 --- a/tests/e2e/web/utils/accountInformation.ts +++ b/tests/e2e/web/utils/accountInformation.ts @@ -32,7 +32,7 @@ export type UserAccountInformation = { height?: Height ethnicity_origin?: EthnicityTuple interested_in?: InterestedInGenderTuple - Interested_in_ages?: InterestedInAges + Interested_in_ages?: MinMaxNumbers connection_type?: ConnectionTypeTuple relationship_status?: RelationshipStatusTuple relationship_style?: RelationshipStyleTuple @@ -62,7 +62,7 @@ type Height = { centimeters: string } -type InterestedInAges = { +export type MinMaxNumbers = { min: string max?: string } @@ -89,7 +89,7 @@ export type Socials = { urlOrUsername: string } -type FiveBigPersonalityTraits = { +export type FiveBigPersonalityTraits = { openness?: number conscientiousness?: number extraversion?: number diff --git a/web/components/filters/big5-filter.tsx b/web/components/filters/big5-filter.tsx index 169a1446..3932f447 100644 --- a/web/components/filters/big5-filter.tsx +++ b/web/components/filters/big5-filter.tsx @@ -70,7 +70,7 @@ export function Big5SliderRow(props: { const {label, minValue, maxValue, onChange} = props return ( -
+
{label} diff --git a/web/components/filters/filters.tsx b/web/components/filters/filters.tsx index 5953c6a4..cd120641 100644 --- a/web/components/filters/filters.tsx +++ b/web/components/filters/filters.tsx @@ -9,7 +9,12 @@ import {Profile} from 'common/profiles/profile' import {DisplayOptions} from 'common/profiles-rendering' import {nullifyDictValues, removeNullOrUndefinedProps, sampleDictByPrefix} from 'common/util/object' import {ReactNode, useState} from 'react' -import {Big5Filters, Big5FilterText, countBig5Filters, hasAnyBig5Filter,} from 'web/components/filters/big5-filter' +import { + Big5Filters, + Big5FilterText, + countBig5Filters, + hasAnyBig5Filter, +} from 'web/components/filters/big5-filter' import {CardSizeSelector} from 'web/components/filters/card-size-selector' import {DietFilter, DietFilterText} from 'web/components/filters/diet-filter' import {EducationFilter, EducationFilterText} from 'web/components/filters/education-filter' @@ -586,6 +591,7 @@ function Filters(props: { +