diff --git a/android/app/build.gradle b/android/app/build.gradle
index 1df981e8..972b49ce 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -11,8 +11,8 @@ android {
applicationId "com.compassconnections.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 141
- versionName "1.33.0"
+ versionCode 142
+ versionName "1.34.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
diff --git a/backend/api/package.json b/backend/api/package.json
index f722e1fe..a58c8e37 100644
--- a/backend/api/package.json
+++ b/backend/api/package.json
@@ -1,6 +1,6 @@
{
"name": "@compass/api",
- "version": "1.53.0",
+ "version": "1.54.0",
"private": true,
"description": "Backend API endpoints",
"main": "src/serve.ts",
diff --git a/playwright.config.ts b/playwright.config.ts
index 95359cfb..4558c51d 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -65,6 +65,6 @@ export default defineConfig({
],
timeout: 120000,
expect: {
- timeout: 60000,
+ timeout: 20000,
},
})
diff --git a/tests/e2e/web/pages/peoplePage.ts b/tests/e2e/web/pages/peoplePage.ts
index b2c5bb42..1f67d417 100644
--- a/tests/e2e/web/pages/peoplePage.ts
+++ b/tests/e2e/web/pages/peoplePage.ts
@@ -14,6 +14,7 @@ import {
} from 'common/choices'
import {MinMaxNumbers} from '../utils/accountInformation'
+import {optionChip} from '../utils/optionChip'
export type BackgroundFilter = {
location?: string
@@ -94,6 +95,8 @@ export class PeoplePage {
private readonly searchBox: Locator
private readonly savedPeopleButton: Locator
private readonly profileCount: Locator
+ private readonly filtersButton: Locator
+ private readonly filtersPanel: Locator
private readonly resetFilters: Locator
private readonly yourFiltersCheckbox: Locator
private readonly incompleteProfilesCheckbox: Locator
@@ -130,6 +133,7 @@ export class PeoplePage {
private readonly profileStar: Locator
private readonly profileMessage: Locator
private readonly messageInput: Locator
+ private readonly messageSubmit: Locator
private readonly profileName: Locator
private readonly profileAgeGender: Locator
private readonly profileSeeking: Locator
@@ -141,32 +145,34 @@ export class PeoplePage {
this.searchBox = page.getByRole('textbox', {name: 'Search anything...'})
this.savedPeopleButton = page.getByRole('button', {name: 'Saved People'})
this.profileCount = page.getByTestId('people-profile-count')
+ this.filtersButton = page.getByTestId('open-filters-button')
+ this.filtersPanel = page.getByTestId('people-filters')
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.connectionTypeDropdown = page.getByRole('button', {name: 'Connection'})
+ this.locationDropdown = page.getByRole('button', {name: 'Location'})
+ this.ageRangeDropdown = page.getByRole('button', {name: 'Age'})
+ this.genderDropdown = page.getByRole('button', {name: '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.backgroundLocation = page.getByRole('button', {name: 'Grew up'})
+ this.backgroundEducation = page.getByText('Education', {exact: true})
+ this.backgroundWork = page.getByText('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.lifestyleInterests = page.getByRole('button', {name: 'Interests'})
+ this.lifestyleCauses = page.getByRole('button', {name: 'Causes'})
+ this.lifestyleDiet = page.getByRole('button', {name: 'Diet'})
+ this.lifestyleAlcohol = page.getByRole('button', {name: '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.lifestylePsychedelics = page.getByRole('button', {name: 'Psychedelics'})
+ this.lifestyleCannabis = page.getByRole('button', {name: 'Cannabis'})
+ this.lifestyleLanguages = page.getByRole('button', {name: '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.valuesAndBeliefsPolitics = page.getByRole('button', {name: 'Politics'})
+ this.valuesAndBeliefsReligion = page.getByRole('button', {name: '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.personalityMbti = page.getByRole('button', {name: 'MBTI'})
+ this.personalityBigFive = page.getByRole('button', {name: 'Big 5'})
this.advancedDropdown = page.getByRole('button', {name: 'Advanced'})
this.advancedActive = page.getByTestId('advanced-active')
this.advancedPhotos = page.getByText('Photos', {exact: true})
@@ -177,6 +183,7 @@ export class PeoplePage {
this.profileStar = page.getByTestId('star-profile-button')
this.profileMessage = page.getByTestId('message-profile-button')
this.messageInput = page.locator('.tiptap')
+ this.messageSubmit = page.getByTestId('send-message-submit')
this.profileName = page.getByTestId('people-profile-name')
this.profileAgeGender = page.getByTestId('people-profile-age-gender')
this.profileSeeking = page.getByTestId('people-profile-seeking')
@@ -249,11 +256,16 @@ export class PeoplePage {
}
}
+ /** See {@link optionChip} — filter options are chips, not clickable checkboxes. */
+ optionChip(label: string): Locator {
+ return optionChip(this.page, label)
+ }
+
async selectOption(trigger: Locator, label: string) {
await expect(trigger).toBeVisible()
await trigger.click()
- const option = this.page.getByLabel(label, {exact: true})
+ const option = this.optionChip(label)
await expect(option).toBeVisible()
await option.click()
}
@@ -274,22 +286,44 @@ export class PeoplePage {
await this.page.waitForTimeout(1000)
}
+ /**
+ * The filters panel starts closed, and the "Filters" button toggles it — so clicking it
+ * unconditionally would close an already-open panel. Everything below that reaches into the panel
+ * calls this first; it is a no-op when the panel is already open.
+ */
+ async openFilters() {
+ if (await this.filtersPanel.isVisible()) return
+ await expect(this.filtersButton).toBeVisible()
+ await this.filtersButton.click()
+ await expect(this.filtersPanel).toBeVisible()
+ }
+
+ async closeFilters() {
+ if (!(await this.filtersPanel.isVisible())) return
+ await this.filtersButton.click()
+ await expect(this.filtersPanel).not.toBeVisible()
+ }
+
async resetFilter() {
+ await this.openFilters()
await expect(this.resetFilters).toBeVisible()
await this.resetFilters.click()
}
async setYourFilters() {
+ await this.openFilters()
await expect(this.yourFiltersCheckbox).toBeVisible()
await this.yourFiltersCheckbox.click()
}
async setIncludeIncompleteProfiles() {
+ await this.openFilters()
await expect(this.incompleteProfilesCheckbox).toBeVisible()
await this.incompleteProfilesCheckbox.click()
}
async setConnectionTypeFilter(connectionType: ConnectionTypeTuple) {
+ await this.openFilters()
await this.selectOption(this.connectionTypeDropdown, connectionType[0])
// await expect(this.connectionTypeDropdown).toBeVisible()
// await this.connectionTypeDropdown.click()
@@ -298,6 +332,7 @@ export class PeoplePage {
}
async setLocationFilter(location: string) {
+ await this.openFilters()
await expect(this.locationDropdown).toBeVisible()
await this.locationDropdown.click()
await expect(this.page.getByRole('textbox', {name: 'Search city...'})).toBeVisible()
@@ -305,12 +340,14 @@ export class PeoplePage {
}
async setAgeRangeFilter(ageRange: MinMaxNumbers) {
+ await this.openFilters()
await expect(this.ageRangeDropdown).toBeVisible()
await this.ageRangeDropdown.click()
await this.sliderHelper(ageRange)
}
async setGenderTypeFilter(genderType: InterestedInGenderTuple) {
+ await this.openFilters()
await this.selectOption(this.genderDropdown, genderType[0])
// await expect(this.genderDropdown).toBeVisible()
// await this.genderDropdown.click()
@@ -319,6 +356,7 @@ export class PeoplePage {
}
async setBackgroundFilter(background: BackgroundFilter) {
+ await this.openFilters()
await expect(this.backgroundDropdown).toBeVisible()
await this.backgroundDropdown.click()
if (background.location) {
@@ -330,19 +368,22 @@ export class PeoplePage {
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()
+ const education = this.optionChip(background.education[0])
+ await expect(education).toBeVisible()
+ await education.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()
+ const work = this.optionChip(background.work)
+ await expect(work).toBeVisible()
+ await work.click()
}
}
async setLifestyleFilter(lifestyle: LifestyleFilter) {
+ await this.openFilters()
await expect(this.lifestyleDropdown).toBeVisible()
await this.lifestyleDropdown.click()
@@ -370,6 +411,7 @@ export class PeoplePage {
}
async setValuesAndBeliefsFilter(values: BeliefsFilter) {
+ await this.openFilters()
await expect(this.valuesAndBeliefsDropdown).toBeVisible()
await this.valuesAndBeliefsDropdown.click()
@@ -380,6 +422,7 @@ export class PeoplePage {
}
async setPersonalityFilter(personality: PersonalityFilter) {
+ await this.openFilters()
await expect(this.personalityDropdown).toBeVisible()
await this.personalityDropdown.click()
@@ -421,6 +464,7 @@ export class PeoplePage {
}
async setAdvancedFilter(advanced: AdvancedFilter) {
+ await this.openFilters()
await expect(this.advancedDropdown).toBeVisible()
await this.advancedDropdown.click()
@@ -436,6 +480,7 @@ export class PeoplePage {
}
async setDisplayFilter(display: DisplayFilter) {
+ await this.openFilters()
await expect(this.displayDropdown).toBeVisible()
await this.displayDropdown.click()
@@ -489,13 +534,21 @@ export class PeoplePage {
for (let i = 0; i < profiles.length; i++) {
const profileName = await profiles[i].getByTestId('people-profile-name').textContent()
if (profileName?.toLowerCase() === displayName.toLowerCase()) {
- await profiles[i].getByTestId('message-profile-button').click()
+ const card = profiles[i]
+ // Card actions are revealed on hover (opacity-0 until lg:group-hover), so hover the card
+ // before reaching for the button rather than relying on the click's implicit hover.
+ await card.hover()
+ await card.getByTestId('message-profile-button').click()
await expect(this.messageInput).toBeVisible()
await this.messageInput.fill(message)
- await this.page.getByTestId('conversation-message-submit').click()
+ // The modal composer passes `hideSubmitButton` and renders its own send control in a pinned
+ // footer, so this is `send-message-submit` — not the `conversation-message-submit` used by
+ // the in-conversation composer on the messages page.
+ await this.messageSubmit.click()
return
}
}
+ throw new Error(`No profile card found for ${displayName}`)
}
async verifySavedPerson(displayName: string | null | undefined) {
diff --git a/tests/e2e/web/pages/profilePage.ts b/tests/e2e/web/pages/profilePage.ts
index 1d5b5d84..aad9281f 100644
--- a/tests/e2e/web/pages/profilePage.ts
+++ b/tests/e2e/web/pages/profilePage.ts
@@ -18,6 +18,7 @@ export class ProfilePage {
private readonly disableProfileDropdownOption: Locator
private readonly displayNameAndAgeSection: Locator
private readonly genderLocationHightInInchesSection: Locator
+ private readonly heightSection: Locator
private readonly headlineSection: Locator
private readonly keywordsSection: Locator
private readonly politicalAboutSection: Locator
@@ -80,6 +81,7 @@ export class ProfilePage {
this.genderLocationHightInInchesSection = page.getByTestId(
'profile-gender-location-height-inches',
)
+ this.heightSection = page.getByTestId('profile-height')
this.politicalAboutSection = page.getByTestId('profile-content')
this.religiousAboutSection = page.getByTestId('profile-content')
this.interestsAboutSection = page.getByTestId('profile-content')
@@ -341,10 +343,14 @@ export class ProfilePage {
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)
+ // Height left this line and now lives in the Details rail (see ProfilePrimaryInfo), which
+ // renders it as a single formatted string — `5' 10"` in imperial — rather than two fields.
+ if (heightFeet || heightInches) {
+ await expect(this.heightSection).toBeVisible()
+ if (heightFeet) await expect(this.heightSection).toContainText(`${heightFeet}'`)
+ if (heightInches) await expect(this.heightSection).toContainText(`${heightInches}"`)
+ }
}
async verifyEthnicityOrigin(origin: string) {
diff --git a/tests/e2e/web/pages/signUpPage.ts b/tests/e2e/web/pages/signUpPage.ts
index ce53777f..71085562 100644
--- a/tests/e2e/web/pages/signUpPage.ts
+++ b/tests/e2e/web/pages/signUpPage.ts
@@ -14,6 +14,8 @@ import {
ReligionTuple,
} from 'common/choices'
+import {clickCheckbox, optionChip, optionChipInput} from '../utils/optionChip'
+
export type ChildrenExpectation =
| ['Strongly against', 0]
| ['Against', 1]
@@ -107,7 +109,9 @@ export class SignUpPage {
this.usernameField = page.getByPlaceholder('Username')
this.usernameError = page.getByTestId('signup-username')
this.nextButton = page.getByRole('button', {name: 'Next', exact: true})
- this.bioField = page.locator('h3:has-text("Bio") ~ * .tiptap')
+ // Anchored on the editor itself rather than on markup around the "Bio" heading: the heading now
+ // sits inside its own `data-form-section` wrapper, so it no longer has the editor as a sibling.
+ this.bioField = page.getByTestId('signup-bio').locator('.tiptap')
this.locationField = page.getByPlaceholder('Search city...')
this.headlineField = page.getByTestId('headline')
this.keywordsField = page.getByTestId('keywords')
@@ -259,15 +263,15 @@ export class SignUpPage {
if (!interestedIn) return
if (interestedIn[0] === 'Men') {
await expect(this.interestedInMenCheckbox).toBeVisible()
- await this.interestedInMenCheckbox.click()
+ await clickCheckbox(this.interestedInMenCheckbox)
await expect(this.interestedInMenCheckbox).toBeChecked()
} else if (interestedIn[0] === 'Women') {
await expect(this.interestedInWomenCheckbox).toBeVisible()
- await this.interestedInWomenCheckbox.click()
+ await clickCheckbox(this.interestedInWomenCheckbox)
await expect(this.interestedInWomenCheckbox).toBeChecked()
} else {
await expect(this.interestedInOtherCheckbox).toBeVisible()
- await this.interestedInOtherCheckbox.click()
+ await clickCheckbox(this.interestedInOtherCheckbox)
await expect(this.interestedInOtherCheckbox).toBeChecked()
}
}
@@ -284,23 +288,23 @@ export class SignUpPage {
async setConnectionType(type: ConnectionTypeTuple | undefined) {
if (!type) return
- await expect(this.page.getByLabel(`${type[0]}`, {exact: true})).toBeVisible()
- await this.page.getByLabel(`${type[0]}`, {exact: true}).click()
- await expect(this.page.getByLabel(`${type[0]}`, {exact: true})).toBeChecked()
+ await expect(optionChip(this.page, type[0])).toBeVisible()
+ await optionChip(this.page, type[0]).click()
+ await expect(optionChipInput(this.page, type[0])).toBeChecked()
}
async setRelationshipStatus(status: RelationshipStatusTuple | undefined) {
if (!status) return
- await expect(this.page.getByLabel(`${status[0]}`, {exact: true})).toBeVisible()
- await this.page.getByLabel(`${status[0]}`, {exact: true}).click()
- await expect(this.page.getByLabel(`${status[0]}`, {exact: true})).toBeChecked()
+ await expect(optionChip(this.page, status[0])).toBeVisible()
+ await optionChip(this.page, status[0]).click()
+ await expect(optionChipInput(this.page, status[0])).toBeChecked()
}
async setRelationshipStyle(style: RelationshipStyleTuple | undefined) {
if (!style) return
- await expect(this.page.getByLabel(`${style[0]}`, {exact: true})).toBeVisible()
- await this.page.getByLabel(`${style[0]}`, {exact: true}).click()
- await expect(this.page.getByLabel(`${style[0]}`, {exact: true})).toBeChecked()
+ await expect(optionChip(this.page, style[0])).toBeVisible()
+ await optionChip(this.page, style[0]).click()
+ await expect(optionChipInput(this.page, style[0])).toBeChecked()
}
async fillCurrentNumberOfChildren(numberOfKids: string | undefined) {
@@ -344,7 +348,7 @@ export class SignUpPage {
if (isExisting) {
await expect(this.page.getByRole('checkbox', {name: `${interest[i]}`})).toBeVisible()
- await this.page.getByRole('checkbox', {name: `${interest[i]}`}).click()
+ await clickCheckbox(this.page.getByRole('checkbox', {name: `${interest[i]}`}))
} else {
await expect(this.addInterestsField).toBeVisible()
await expect(this.addInterestsButton).toBeVisible()
@@ -365,7 +369,7 @@ export class SignUpPage {
if (isExisting) {
await expect(this.page.getByRole('checkbox', {name: `${cause[i]}`})).toBeVisible()
- await this.page.getByRole('checkbox', {name: `${cause[i]}`}).click()
+ await clickCheckbox(this.page.getByRole('checkbox', {name: `${cause[i]}`}))
} else {
await expect(this.addCausesField).toBeVisible()
await expect(this.addCausesButton).toBeVisible()
@@ -406,12 +410,12 @@ export class SignUpPage {
if (!workArea || workArea?.length === 0) return
for (let i = 0; i < workArea.length; i++) {
- const checkbox = this.page.getByLabel(`${workArea[i]}`, {exact: true})
- const isExisting = (await checkbox.count()) > 0
+ const chip = optionChip(this.page, workArea[i])
+ const isExisting = (await chip.count()) > 0
if (isExisting) {
- await expect(this.page.getByLabel(`${workArea[i]}`, {exact: true})).toBeVisible()
- await this.page.getByLabel(`${workArea[i]}`, {exact: true}).click()
+ await expect(chip).toBeVisible()
+ await chip.click()
await this.page.waitForTimeout(500)
} else {
await expect(this.addWorkAreaField).toBeVisible()
@@ -420,8 +424,8 @@ export class SignUpPage {
await this.addWorkAreaButton.click()
await this.page.waitForTimeout(500)
}
- await expect(this.page.getByLabel(`${workArea[i]}`, {exact: true})).toBeVisible()
- await expect(this.page.getByLabel(`${workArea[i]}`, {exact: true})).toBeChecked()
+ await expect(optionChip(this.page, workArea[i])).toBeVisible()
+ await expect(optionChipInput(this.page, workArea[i])).toBeChecked()
}
}
@@ -431,7 +435,7 @@ export class SignUpPage {
) {
if (politicalBeliefs) {
await expect(this.page.getByRole('checkbox', {name: `${politicalBeliefs[0]}`})).toBeVisible()
- await this.page.getByRole('checkbox', {name: `${politicalBeliefs[0]}`}).click()
+ await clickCheckbox(this.page.getByRole('checkbox', {name: `${politicalBeliefs[0]}`}))
await expect(this.page.getByRole('checkbox', {name: `${politicalBeliefs[0]}`})).toBeChecked()
}
@@ -466,7 +470,7 @@ export class SignUpPage {
).toBeChecked()
} else {
await expect(this.page.getByRole('checkbox', {name: `${religiousBeliefs[0]}`})).toBeVisible()
- await this.page.getByRole('checkbox', {name: `${religiousBeliefs[0]}`}).click()
+ await clickCheckbox(this.page.getByRole('checkbox', {name: `${religiousBeliefs[0]}`}))
await expect(this.page.getByRole('checkbox', {name: `${religiousBeliefs[0]}`})).toBeChecked()
}
@@ -482,159 +486,71 @@ export class SignUpPage {
await expect(this.page.getByText(`${personalityType}`, {exact: true})).toBeChecked()
}
- async setOpennessPersonalityValue(value: number | undefined) {
+ /**
+ * Walk a Big Five slider to `value` with arrow keys.
+ *
+ * The value label reads "–", not a number, until the trait has been touched — the form
+ * deliberately distinguishes "unset" from 50 (see `Big5Slider` in optional-profile-form.tsx).
+ * Clicking the thumb doesn't clear that: Radix only emits onValueChange when the value actually
+ * changes, and the thumb already sits at the default. So nudge it with a key press first,
+ * otherwise `parseInt('–')` is NaN, every comparison below is false, and the trait is silently
+ * left unset — which then shows up much later as a missing Big Five section on the profile.
+ */
+ private async setBig5Value(slider: Locator, valueLabel: Locator, value: number | undefined) {
if (!value) return
- await expect(this.opennessPersonalitySlider).toBeVisible()
- await expect(this.opennessPersonalityValue).toBeVisible()
- await this.opennessPersonalitySlider.click()
- const originalValue = await this.opennessPersonalityValue.textContent()
- if (!originalValue) return
- if (parseInt(originalValue) < value) {
- while (true) {
- const actualValue = await this.opennessPersonalityValue.textContent()
- if (!actualValue) break
+ await expect(slider).toBeVisible()
+ await expect(valueLabel).toBeVisible()
+ await slider.click()
- if (parseInt(actualValue) >= value) {
- break
- }
- await this.page.keyboard.press('ArrowRight')
- }
- }
- if (parseInt(originalValue) > value) {
- while (true) {
- const actualValue = await this.opennessPersonalityValue.textContent()
- if (!actualValue) break
+ const readValue = async () => parseInt((await valueLabel.textContent()) ?? '')
+ if (isNaN(await readValue())) await slider.press('ArrowRight')
- if (parseInt(actualValue) <= value) {
- break
- }
- await this.page.keyboard.press('ArrowLeft')
+ const MAX_ITERATIONS = 200
+ for (let i = 0; ; i++) {
+ if (i > MAX_ITERATIONS) {
+ throw new Error(`Big Five slider did not reach ${value} within ${MAX_ITERATIONS} presses`)
}
+ const actual = await readValue()
+ if (isNaN(actual)) throw new Error('Big Five slider has no numeric value')
+ if (actual === value) return
+ await slider.press(actual < value ? 'ArrowRight' : 'ArrowLeft')
}
}
+ async setOpennessPersonalityValue(value: number | undefined) {
+ await this.setBig5Value(this.opennessPersonalitySlider, this.opennessPersonalityValue, value)
+ }
+
async setConscientiousnessPersonalityValue(value: number | undefined) {
- if (!value) return
- await expect(this.conscientiousnessPersonalitySlider).toBeVisible()
- await expect(this.conscientiousnessPersonalityValue).toBeVisible()
- await this.conscientiousnessPersonalitySlider.click()
- const originalValue = await this.conscientiousnessPersonalityValue.textContent()
- if (!originalValue) return
- if (parseInt(originalValue) < value) {
- while (true) {
- const actualValue = await this.conscientiousnessPersonalityValue.textContent()
- if (!actualValue) break
-
- if (parseInt(actualValue) >= value) {
- break
- }
- await this.page.keyboard.press('ArrowRight')
- }
- }
- if (parseInt(originalValue) > value) {
- while (true) {
- const actualValue = await this.conscientiousnessPersonalityValue.textContent()
- if (!actualValue) break
-
- if (parseInt(actualValue) <= value) {
- break
- }
- await this.page.keyboard.press('ArrowLeft')
- }
- }
+ await this.setBig5Value(
+ this.conscientiousnessPersonalitySlider,
+ this.conscientiousnessPersonalityValue,
+ value,
+ )
}
async setExtraversionPersonalityValue(value: number | undefined) {
- if (!value) return
- await expect(this.extraversionPersonalitySlider).toBeVisible()
- await expect(this.extraversionPersonalityValue).toBeVisible()
- await this.extraversionPersonalitySlider.click()
- const originalValue = await this.extraversionPersonalityValue.textContent()
- if (!originalValue) return
- if (parseInt(originalValue) < value) {
- while (true) {
- const actualValue = await this.extraversionPersonalityValue.textContent()
- if (!actualValue) break
-
- if (parseInt(actualValue) >= value) {
- break
- }
- await this.page.keyboard.press('ArrowRight')
- }
- }
- if (parseInt(originalValue) > value) {
- while (true) {
- const actualValue = await this.extraversionPersonalityValue.textContent()
- if (!actualValue) break
-
- if (parseInt(actualValue) <= value) {
- break
- }
- await this.page.keyboard.press('ArrowLeft')
- }
- }
+ await this.setBig5Value(
+ this.extraversionPersonalitySlider,
+ this.extraversionPersonalityValue,
+ value,
+ )
}
async setAgreeablenessPersonalityValue(value: number | undefined) {
- if (!value) return
- await expect(this.agreeablenessPersonalitySlider).toBeVisible()
- await expect(this.agreeablenessPersonalityValue).toBeVisible()
- await this.agreeablenessPersonalitySlider.click()
- const originalValue = await this.agreeablenessPersonalityValue.textContent()
- if (!originalValue) return
- if (parseInt(originalValue) < value) {
- while (true) {
- const actualValue = await this.agreeablenessPersonalityValue.textContent()
- if (!actualValue) break
-
- if (parseInt(actualValue) >= value) {
- break
- }
- await this.page.keyboard.press('ArrowRight')
- }
- }
- if (parseInt(originalValue) > value) {
- while (true) {
- const actualValue = await this.agreeablenessPersonalityValue.textContent()
- if (!actualValue) break
-
- if (parseInt(actualValue) <= value) {
- break
- }
- await this.page.keyboard.press('ArrowLeft')
- }
- }
+ await this.setBig5Value(
+ this.agreeablenessPersonalitySlider,
+ this.agreeablenessPersonalityValue,
+ value,
+ )
}
async setNeuroticismPersonalityValue(value: number | undefined) {
- if (!value) return
- await expect(this.neuroticismPersonalitySlider).toBeVisible()
- await expect(this.neuroticismPersonalityValue).toBeVisible()
- await this.neuroticismPersonalitySlider.click()
- const originalValue = await this.neuroticismPersonalityValue.textContent()
- if (!originalValue) return
- if (parseInt(originalValue) < value) {
- while (true) {
- const actualValue = await this.neuroticismPersonalityValue.textContent()
- if (!actualValue) break
-
- if (parseInt(actualValue) >= value) {
- break
- }
- await this.page.keyboard.press('ArrowRight')
- }
- }
- if (parseInt(originalValue) > value) {
- while (true) {
- const actualValue = await this.neuroticismPersonalityValue.textContent()
- if (!actualValue) break
-
- if (parseInt(actualValue) <= value) {
- break
- }
- await this.page.keyboard.press('ArrowLeft')
- }
- }
+ await this.setBig5Value(
+ this.neuroticismPersonalitySlider,
+ this.neuroticismPersonalityValue,
+ value,
+ )
}
async setDietType(dietType: DietTuple | undefined) {
@@ -645,7 +561,7 @@ export class SignUpPage {
await expect(this.page.locator('label').filter({hasText: 'Other'}).nth(4)).toBeChecked()
} else {
await expect(this.page.getByRole('checkbox', {name: `${dietType[0]}`})).toBeVisible()
- await this.page.getByRole('checkbox', {name: `${dietType[0]}`}).click()
+ await clickCheckbox(this.page.getByRole('checkbox', {name: `${dietType[0]}`}))
await expect(this.page.getByRole('checkbox', {name: `${dietType[0]}`})).toBeChecked()
}
}
@@ -670,10 +586,10 @@ export class SignUpPage {
async setLanguages(language: LanguageTuple[] | undefined) {
if (!language || language.length === 0) return
- await this.page.getByRole('checkbox', {name: `English`}).click()
+ await clickCheckbox(this.page.getByRole('checkbox', {name: `English`}))
for (let i = 0; i < language.length; i++) {
await expect(this.page.getByRole('checkbox', {name: `${language[i][0]}`})).toBeVisible()
- await this.page.getByRole('checkbox', {name: `${language[i][0]}`}).click()
+ await clickCheckbox(this.page.getByRole('checkbox', {name: `${language[i][0]}`}))
await expect(this.page.getByRole('checkbox', {name: `${language[i][0]}`})).toBeChecked()
}
}
diff --git a/tests/e2e/web/utils/optionChip.ts b/tests/e2e/web/utils/optionChip.ts
new file mode 100644
index 00000000..7a5c6ce3
--- /dev/null
+++ b/tests/e2e/web/utils/optionChip.ts
@@ -0,0 +1,31 @@
+import {Locator, Page} from '@playwright/test'
+
+/**
+ * Selectable options (connection type, gender, interests, education, ...) render as chips —
+ * `OptionChip` in web/components/multi-checkbox.tsx. The chip keeps a real ``
+ * for accessibility, but it is `sr-only` and the label's text span sits on top of it. Playwright
+ * still considers the 1px input "visible", so clicking it gets as far as the click and then fails
+ * with ` intercepts pointer events`.
+ *
+ * So: click the enclosing