Skip to content

Commit 4233619

Browse files
committed
Refactor reusable components and improve test utilities: extract clickCheckbox and optionChip helpers, streamline personality slider updates, and integrate missing data-testid attributes for better test targeting.
1 parent 77dc53d commit 4233619

11 files changed

Lines changed: 206 additions & 197 deletions

File tree

android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ android {
1111
applicationId "com.compassconnections.app"
1212
minSdkVersion rootProject.ext.minSdkVersion
1313
targetSdkVersion rootProject.ext.targetSdkVersion
14-
versionCode 141
15-
versionName "1.33.0"
14+
versionCode 142
15+
versionName "1.34.0"
1616
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1717
aaptOptions {
1818
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.

backend/api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@compass/api",
3-
"version": "1.53.0",
3+
"version": "1.54.0",
44
"private": true,
55
"description": "Backend API endpoints",
66
"main": "src/serve.ts",

playwright.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,6 @@ export default defineConfig({
6565
],
6666
timeout: 120000,
6767
expect: {
68-
timeout: 60000,
68+
timeout: 20000,
6969
},
7070
})

tests/e2e/web/pages/peoplePage.ts

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from 'common/choices'
1515

1616
import {MinMaxNumbers} from '../utils/accountInformation'
17+
import {optionChip} from '../utils/optionChip'
1718

1819
export type BackgroundFilter = {
1920
location?: string
@@ -94,6 +95,8 @@ export class PeoplePage {
9495
private readonly searchBox: Locator
9596
private readonly savedPeopleButton: Locator
9697
private readonly profileCount: Locator
98+
private readonly filtersButton: Locator
99+
private readonly filtersPanel: Locator
97100
private readonly resetFilters: Locator
98101
private readonly yourFiltersCheckbox: Locator
99102
private readonly incompleteProfilesCheckbox: Locator
@@ -130,6 +133,7 @@ export class PeoplePage {
130133
private readonly profileStar: Locator
131134
private readonly profileMessage: Locator
132135
private readonly messageInput: Locator
136+
private readonly messageSubmit: Locator
133137
private readonly profileName: Locator
134138
private readonly profileAgeGender: Locator
135139
private readonly profileSeeking: Locator
@@ -141,32 +145,34 @@ export class PeoplePage {
141145
this.searchBox = page.getByRole('textbox', {name: 'Search anything...'})
142146
this.savedPeopleButton = page.getByRole('button', {name: 'Saved People'})
143147
this.profileCount = page.getByTestId('people-profile-count')
148+
this.filtersButton = page.getByTestId('open-filters-button')
149+
this.filtersPanel = page.getByTestId('people-filters')
144150
this.resetFilters = page.getByRole('button', {name: 'Reset filters'})
145151
this.yourFiltersCheckbox = page.getByText('Your filters', {exact: true})
146152
this.incompleteProfilesCheckbox = page.getByText('Include incomplete profiles', {exact: true})
147-
this.connectionTypeDropdown = page.getByRole('button', {name: 'Any connection'})
148-
this.locationDropdown = page.getByRole('button', {name: 'Living anywhere'})
149-
this.ageRangeDropdown = page.getByRole('button', {name: 'Any age'})
150-
this.genderDropdown = page.getByRole('button', {name: 'Any gender'})
153+
this.connectionTypeDropdown = page.getByRole('button', {name: 'Connection'})
154+
this.locationDropdown = page.getByRole('button', {name: 'Location'})
155+
this.ageRangeDropdown = page.getByRole('button', {name: 'Age'})
156+
this.genderDropdown = page.getByRole('button', {name: 'Gender'})
151157
this.backgroundDropdown = page.getByRole('button', {name: 'Background'})
152-
this.backgroundLocation = page.getByRole('button', {name: 'Grew up anywhere'})
153-
this.backgroundEducation = page.getByText('Any education', {exact: true})
154-
this.backgroundWork = page.getByText('Any work', {exact: true})
158+
this.backgroundLocation = page.getByRole('button', {name: 'Grew up'})
159+
this.backgroundEducation = page.getByText('Education', {exact: true})
160+
this.backgroundWork = page.getByText('Work', {exact: true})
155161
this.lifestyleDropdown = page.getByRole('button', {name: 'Lifestyle'})
156-
this.lifestyleInterests = page.getByRole('button', {name: 'Any interests'})
157-
this.lifestyleCauses = page.getByRole('button', {name: 'Any causes'})
158-
this.lifestyleDiet = page.getByRole('button', {name: 'Any diet'})
159-
this.lifestyleAlcohol = page.getByRole('button', {name: 'Any drinks'})
162+
this.lifestyleInterests = page.getByRole('button', {name: 'Interests'})
163+
this.lifestyleCauses = page.getByRole('button', {name: 'Causes'})
164+
this.lifestyleDiet = page.getByRole('button', {name: 'Diet'})
165+
this.lifestyleAlcohol = page.getByRole('button', {name: 'Drinks'})
160166
this.lifestyleSmoker = page.getByTestId('lifestyle-smoker')
161-
this.lifestylePsychedelics = page.getByRole('button', {name: 'Any psychedelics'})
162-
this.lifestyleCannabis = page.getByRole('button', {name: 'Any cannabis'})
163-
this.lifestyleLanguages = page.getByRole('button', {name: 'Any language'})
167+
this.lifestylePsychedelics = page.getByRole('button', {name: 'Psychedelics'})
168+
this.lifestyleCannabis = page.getByRole('button', {name: 'Cannabis'})
169+
this.lifestyleLanguages = page.getByRole('button', {name: 'Language'})
164170
this.valuesAndBeliefsDropdown = page.getByRole('button', {name: 'Values & Beliefs'})
165-
this.valuesAndBeliefsPolitics = page.getByRole('button', {name: 'Any politics'})
166-
this.valuesAndBeliefsReligion = page.getByRole('button', {name: 'Any religion'})
171+
this.valuesAndBeliefsPolitics = page.getByRole('button', {name: 'Politics'})
172+
this.valuesAndBeliefsReligion = page.getByRole('button', {name: 'Religion'})
167173
this.personalityDropdown = page.getByRole('button', {name: 'Personality'})
168-
this.personalityMbti = page.getByRole('button', {name: 'Any MBTI'})
169-
this.personalityBigFive = page.getByRole('button', {name: 'Any Big 5'})
174+
this.personalityMbti = page.getByRole('button', {name: 'MBTI'})
175+
this.personalityBigFive = page.getByRole('button', {name: 'Big 5'})
170176
this.advancedDropdown = page.getByRole('button', {name: 'Advanced'})
171177
this.advancedActive = page.getByTestId('advanced-active')
172178
this.advancedPhotos = page.getByText('Photos', {exact: true})
@@ -177,6 +183,7 @@ export class PeoplePage {
177183
this.profileStar = page.getByTestId('star-profile-button')
178184
this.profileMessage = page.getByTestId('message-profile-button')
179185
this.messageInput = page.locator('.tiptap')
186+
this.messageSubmit = page.getByTestId('send-message-submit')
180187
this.profileName = page.getByTestId('people-profile-name')
181188
this.profileAgeGender = page.getByTestId('people-profile-age-gender')
182189
this.profileSeeking = page.getByTestId('people-profile-seeking')
@@ -249,11 +256,16 @@ export class PeoplePage {
249256
}
250257
}
251258

259+
/** See {@link optionChip} — filter options are chips, not clickable checkboxes. */
260+
optionChip(label: string): Locator {
261+
return optionChip(this.page, label)
262+
}
263+
252264
async selectOption(trigger: Locator, label: string) {
253265
await expect(trigger).toBeVisible()
254266
await trigger.click()
255267

256-
const option = this.page.getByLabel(label, {exact: true})
268+
const option = this.optionChip(label)
257269
await expect(option).toBeVisible()
258270
await option.click()
259271
}
@@ -274,22 +286,44 @@ export class PeoplePage {
274286
await this.page.waitForTimeout(1000)
275287
}
276288

289+
/**
290+
* The filters panel starts closed, and the "Filters" button toggles it — so clicking it
291+
* unconditionally would close an already-open panel. Everything below that reaches into the panel
292+
* calls this first; it is a no-op when the panel is already open.
293+
*/
294+
async openFilters() {
295+
if (await this.filtersPanel.isVisible()) return
296+
await expect(this.filtersButton).toBeVisible()
297+
await this.filtersButton.click()
298+
await expect(this.filtersPanel).toBeVisible()
299+
}
300+
301+
async closeFilters() {
302+
if (!(await this.filtersPanel.isVisible())) return
303+
await this.filtersButton.click()
304+
await expect(this.filtersPanel).not.toBeVisible()
305+
}
306+
277307
async resetFilter() {
308+
await this.openFilters()
278309
await expect(this.resetFilters).toBeVisible()
279310
await this.resetFilters.click()
280311
}
281312

282313
async setYourFilters() {
314+
await this.openFilters()
283315
await expect(this.yourFiltersCheckbox).toBeVisible()
284316
await this.yourFiltersCheckbox.click()
285317
}
286318

287319
async setIncludeIncompleteProfiles() {
320+
await this.openFilters()
288321
await expect(this.incompleteProfilesCheckbox).toBeVisible()
289322
await this.incompleteProfilesCheckbox.click()
290323
}
291324

292325
async setConnectionTypeFilter(connectionType: ConnectionTypeTuple) {
326+
await this.openFilters()
293327
await this.selectOption(this.connectionTypeDropdown, connectionType[0])
294328
// await expect(this.connectionTypeDropdown).toBeVisible()
295329
// await this.connectionTypeDropdown.click()
@@ -298,19 +332,22 @@ export class PeoplePage {
298332
}
299333

300334
async setLocationFilter(location: string) {
335+
await this.openFilters()
301336
await expect(this.locationDropdown).toBeVisible()
302337
await this.locationDropdown.click()
303338
await expect(this.page.getByRole('textbox', {name: 'Search city...'})).toBeVisible()
304339
await this.page.getByRole('textbox', {name: 'Search city...'}).fill(location)
305340
}
306341

307342
async setAgeRangeFilter(ageRange: MinMaxNumbers) {
343+
await this.openFilters()
308344
await expect(this.ageRangeDropdown).toBeVisible()
309345
await this.ageRangeDropdown.click()
310346
await this.sliderHelper(ageRange)
311347
}
312348

313349
async setGenderTypeFilter(genderType: InterestedInGenderTuple) {
350+
await this.openFilters()
314351
await this.selectOption(this.genderDropdown, genderType[0])
315352
// await expect(this.genderDropdown).toBeVisible()
316353
// await this.genderDropdown.click()
@@ -319,6 +356,7 @@ export class PeoplePage {
319356
}
320357

321358
async setBackgroundFilter(background: BackgroundFilter) {
359+
await this.openFilters()
322360
await expect(this.backgroundDropdown).toBeVisible()
323361
await this.backgroundDropdown.click()
324362
if (background.location) {
@@ -330,19 +368,22 @@ export class PeoplePage {
330368
if (background.education) {
331369
await expect(this.backgroundEducation).toBeVisible()
332370
await this.backgroundEducation.click()
333-
await expect(this.page.getByLabel(background.education[0], {exact: true})).toBeVisible()
334-
await this.page.getByLabel(background.education[0], {exact: true}).click()
371+
const education = this.optionChip(background.education[0])
372+
await expect(education).toBeVisible()
373+
await education.click()
335374
}
336375

337376
if (background.work) {
338377
await expect(this.backgroundWork).toBeVisible()
339378
await this.backgroundWork.click()
340-
await expect(this.page.getByLabel(background.work, {exact: true})).toBeVisible()
341-
await this.page.getByLabel(background.work, {exact: true}).click()
379+
const work = this.optionChip(background.work)
380+
await expect(work).toBeVisible()
381+
await work.click()
342382
}
343383
}
344384

345385
async setLifestyleFilter(lifestyle: LifestyleFilter) {
386+
await this.openFilters()
346387
await expect(this.lifestyleDropdown).toBeVisible()
347388
await this.lifestyleDropdown.click()
348389

@@ -370,6 +411,7 @@ export class PeoplePage {
370411
}
371412

372413
async setValuesAndBeliefsFilter(values: BeliefsFilter) {
414+
await this.openFilters()
373415
await expect(this.valuesAndBeliefsDropdown).toBeVisible()
374416
await this.valuesAndBeliefsDropdown.click()
375417

@@ -380,6 +422,7 @@ export class PeoplePage {
380422
}
381423

382424
async setPersonalityFilter(personality: PersonalityFilter) {
425+
await this.openFilters()
383426
await expect(this.personalityDropdown).toBeVisible()
384427
await this.personalityDropdown.click()
385428

@@ -421,6 +464,7 @@ export class PeoplePage {
421464
}
422465

423466
async setAdvancedFilter(advanced: AdvancedFilter) {
467+
await this.openFilters()
424468
await expect(this.advancedDropdown).toBeVisible()
425469
await this.advancedDropdown.click()
426470

@@ -436,6 +480,7 @@ export class PeoplePage {
436480
}
437481

438482
async setDisplayFilter(display: DisplayFilter) {
483+
await this.openFilters()
439484
await expect(this.displayDropdown).toBeVisible()
440485
await this.displayDropdown.click()
441486

@@ -489,13 +534,21 @@ export class PeoplePage {
489534
for (let i = 0; i < profiles.length; i++) {
490535
const profileName = await profiles[i].getByTestId('people-profile-name').textContent()
491536
if (profileName?.toLowerCase() === displayName.toLowerCase()) {
492-
await profiles[i].getByTestId('message-profile-button').click()
537+
const card = profiles[i]
538+
// Card actions are revealed on hover (opacity-0 until lg:group-hover), so hover the card
539+
// before reaching for the button rather than relying on the click's implicit hover.
540+
await card.hover()
541+
await card.getByTestId('message-profile-button').click()
493542
await expect(this.messageInput).toBeVisible()
494543
await this.messageInput.fill(message)
495-
await this.page.getByTestId('conversation-message-submit').click()
544+
// The modal composer passes `hideSubmitButton` and renders its own send control in a pinned
545+
// footer, so this is `send-message-submit` — not the `conversation-message-submit` used by
546+
// the in-conversation composer on the messages page.
547+
await this.messageSubmit.click()
496548
return
497549
}
498550
}
551+
throw new Error(`No profile card found for ${displayName}`)
499552
}
500553

501554
async verifySavedPerson(displayName: string | null | undefined) {

tests/e2e/web/pages/profilePage.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export class ProfilePage {
1818
private readonly disableProfileDropdownOption: Locator
1919
private readonly displayNameAndAgeSection: Locator
2020
private readonly genderLocationHightInInchesSection: Locator
21+
private readonly heightSection: Locator
2122
private readonly headlineSection: Locator
2223
private readonly keywordsSection: Locator
2324
private readonly politicalAboutSection: Locator
@@ -80,6 +81,7 @@ export class ProfilePage {
8081
this.genderLocationHightInInchesSection = page.getByTestId(
8182
'profile-gender-location-height-inches',
8283
)
84+
this.heightSection = page.getByTestId('profile-height')
8385
this.politicalAboutSection = page.getByTestId('profile-content')
8486
this.religiousAboutSection = page.getByTestId('profile-content')
8587
this.interestsAboutSection = page.getByTestId('profile-content')
@@ -341,10 +343,14 @@ export class ProfilePage {
341343
await expect(this.genderLocationHightInInchesSection).toContainText(location, {
342344
ignoreCase: true,
343345
})
344-
if (heightFeet) await expect(this.genderLocationHightInInchesSection).toContainText(heightFeet)
345-
if (heightInches)
346-
await expect(this.genderLocationHightInInchesSection).toContainText(heightInches)
347346
if (age) await expect(this.genderLocationHightInInchesSection).toContainText(age)
347+
// Height left this line and now lives in the Details rail (see ProfilePrimaryInfo), which
348+
// renders it as a single formatted string — `5' 10"` in imperial — rather than two fields.
349+
if (heightFeet || heightInches) {
350+
await expect(this.heightSection).toBeVisible()
351+
if (heightFeet) await expect(this.heightSection).toContainText(`${heightFeet}'`)
352+
if (heightInches) await expect(this.heightSection).toContainText(`${heightInches}"`)
353+
}
348354
}
349355

350356
async verifyEthnicityOrigin(origin: string) {

0 commit comments

Comments
 (0)