mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-05-30 03:37:12 -04:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -213,7 +213,7 @@ export const MBTI_CHOICES = {
|
||||
ESTP: 'estp',
|
||||
ESFJ: 'esfj',
|
||||
ESFP: 'esfp',
|
||||
}
|
||||
} as const
|
||||
|
||||
// MBTI type name mapping
|
||||
export const MBTI_TYPE_NAMES: Record<string, string> = {
|
||||
|
||||
33
tests/e2e/utils/contextManager.ts
Normal file
33
tests/e2e/utils/contextManager.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import {Browser} from '@playwright/test'
|
||||
|
||||
import {App} from '../web/pages/app'
|
||||
|
||||
export class ContextManager {
|
||||
private contexts: Map<string, App> = new Map()
|
||||
|
||||
constructor(private browser: Browser) {}
|
||||
|
||||
async createContext(customName?: string): Promise<App> {
|
||||
const name = customName ?? crypto.randomUUID().slice(0, 6)
|
||||
const existing = this.contexts.get(name)
|
||||
// Return the existing one instead of closing it?
|
||||
if (existing) await existing.page.context().close()
|
||||
|
||||
const context = await this.browser.newContext()
|
||||
const page = await context.newPage()
|
||||
const app = new App(page)
|
||||
this.contexts.set(name, app)
|
||||
return app
|
||||
}
|
||||
|
||||
getContext(name: string): App | undefined {
|
||||
return this.contexts.get(name)
|
||||
}
|
||||
|
||||
async closeAll(): Promise<void> {
|
||||
for (const app of this.contexts.values()) {
|
||||
await app.page.context().close()
|
||||
}
|
||||
this.contexts.clear()
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,36 @@ export async function findUser(idToken: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendVerificationEmail(idToken: string) {
|
||||
await axios.post(`${config.FIREBASE_URL.BASE}${config.FIREBASE_URL.SEND_EMAIL_VERIFICATION}`, {
|
||||
requestType: 'VERIFY_EMAIL',
|
||||
idToken,
|
||||
})
|
||||
}
|
||||
export async function getOobCode(oobCodes: any[], email: string) {
|
||||
return oobCodes.find((item) => item.email.toLowerCase() === email.toLowerCase())?.oobCode
|
||||
}
|
||||
|
||||
export async function verifyEmail(email: string, password: string) {
|
||||
try {
|
||||
const loginInfo = await firebaseLoginEmailPassword(email, password)
|
||||
await sendVerificationEmail(loginInfo.data.idToken)
|
||||
const oobResponse = await axios.get(`${config.FIREBASE_URL.FIREBASE_EMULATOR_API}`)
|
||||
const oobCode = await getOobCode(oobResponse.data.oobCodes, email)
|
||||
if (!oobCode) throw new Error(`No verification OOB code found for email: ${email}`)
|
||||
|
||||
const response = await axios.post(
|
||||
`${config.FIREBASE_URL.BASE}${config.FIREBASE_URL.CONFIRM_EMAIL_VERIFICATION}`,
|
||||
{
|
||||
oobCode,
|
||||
},
|
||||
)
|
||||
} catch (err: any) {
|
||||
console.log(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
export async function firebaseSignUp(email: string, password: string) {
|
||||
try {
|
||||
const response = await axios.post(`${config.FIREBASE_URL.BASE}${config.FIREBASE_URL.SIGNUP}`, {
|
||||
|
||||
@@ -3,7 +3,13 @@ import {createSomeNotifications} from 'shared/create-notification'
|
||||
import {createSupabaseDirectClient} from 'shared/supabase/init'
|
||||
import {insert} from 'shared/supabase/utils'
|
||||
|
||||
import {seedUser} from './seedDatabase'
|
||||
import {
|
||||
seedUser,
|
||||
TEST_USER_DISPLAY_NAME,
|
||||
TEST_USER_EMAIL,
|
||||
TEST_USER_PASSWORD,
|
||||
TEST_USER_USERNAME,
|
||||
} from './seedDatabase'
|
||||
|
||||
async function seedCompatibilityPrompts(userId: string | null = null) {
|
||||
// Need some prompts to prevent the onboarding from stopping once it reaches them (just after profile creation)
|
||||
@@ -59,6 +65,15 @@ type ProfileType = 'basic' | 'medium' | 'full'
|
||||
}
|
||||
}
|
||||
|
||||
// Used in some tests that require interaction with a permanent user
|
||||
await seedUser(
|
||||
TEST_USER_EMAIL,
|
||||
TEST_USER_PASSWORD,
|
||||
'full',
|
||||
TEST_USER_DISPLAY_NAME,
|
||||
TEST_USER_USERNAME,
|
||||
)
|
||||
|
||||
await seedCompatibilityPrompts()
|
||||
await seedNotifications()
|
||||
|
||||
|
||||
@@ -9,7 +9,12 @@ import {insert} from 'shared/supabase/utils'
|
||||
import {getUser} from 'shared/utils'
|
||||
|
||||
import UserAccountInformationForSeeding from '../backend/utils/userInformation'
|
||||
import {firebaseSignUp} from './firebaseUtils'
|
||||
import {firebaseSignUp, verifyEmail} from './firebaseUtils'
|
||||
|
||||
export const TEST_USER_EMAIL = 'user@compass.test'
|
||||
export const TEST_USER_PASSWORD = 'pass'
|
||||
export const TEST_USER_DISPLAY_NAME = 'Test User'
|
||||
export const TEST_USER_USERNAME = 'TestUser'
|
||||
|
||||
/**
|
||||
* Function used to populate the database with profiles.
|
||||
@@ -151,6 +156,7 @@ export async function seedUser(
|
||||
profileType?: string | undefined,
|
||||
displayName?: string | undefined,
|
||||
userName?: string | undefined,
|
||||
verifyUserEmail?: boolean,
|
||||
) {
|
||||
const userInfo = new UserAccountInformationForSeeding()
|
||||
if (email) userInfo.email = email
|
||||
@@ -162,4 +168,5 @@ export async function seedUser(
|
||||
// Fall back to the pre-generated faker id when Firebase is unreachable
|
||||
const created = await seedDbUser(userInfo, profileType ?? 'full')
|
||||
if (created) debug('User created in Supabase:', userInfo.email)
|
||||
if (verifyUserEmail) await verifyEmail(userInfo.email, userInfo.password)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@ export const config = {
|
||||
BASE_URL: 'http://localhost:3000',
|
||||
FIREBASE_URL: {
|
||||
BASE: 'http://localhost:9099/identitytoolkit.googleapis.com/v1',
|
||||
FIREBASE_EMULATOR_API: 'http://localhost:9099/emulator/v1/projects/compass-57c3c/oobCodes',
|
||||
SIGNUP: '/accounts:signUp?key=fake-api-key',
|
||||
SIGN_IN_PASSWORD: '/accounts:signInWithPassword?key=fake-api-key',
|
||||
ACCOUNT_LOOKUP: '/accounts:lookup?key=fake-api-key',
|
||||
DELETE: '/accounts:delete?key=fake-api-key',
|
||||
SEND_EMAIL_VERIFICATION: '/accounts:sendOobCode?key=fake-api-key',
|
||||
CONFIRM_EMAIL_VERIFICATION: '/accounts:update?key=fake-api-key',
|
||||
},
|
||||
USERS: {
|
||||
DEV_1: {
|
||||
|
||||
@@ -7,7 +7,9 @@ import {deleteUser} from '../utils/deleteUser'
|
||||
|
||||
export const test = base.extend<{
|
||||
app: App
|
||||
dev_one_account: UserAccountInformation
|
||||
devOneAccount: UserAccountInformation
|
||||
devTwoAccount: UserAccountInformation
|
||||
specAccount: UserAccountInformation
|
||||
fakerAccount: UserAccountInformation
|
||||
googleAccountOne: UserAccountInformation
|
||||
googleAccountTwo: UserAccountInformation
|
||||
@@ -17,6 +19,7 @@ export const test = base.extend<{
|
||||
app: async ({page}, use) => {
|
||||
const appPage = new App(page)
|
||||
await use(appPage)
|
||||
await appPage.contextManager?.closeAll()
|
||||
},
|
||||
signedInAccount: async ({app}: {app: App}, use) => {
|
||||
const account = testAccounts.faker_account()
|
||||
@@ -26,6 +29,7 @@ export const test = base.extend<{
|
||||
undefined,
|
||||
account.display_name,
|
||||
account.username,
|
||||
true,
|
||||
)
|
||||
await app.signinWithEmail(account)
|
||||
await use(account)
|
||||
@@ -39,6 +43,46 @@ export const test = base.extend<{
|
||||
undefined,
|
||||
account.display_name,
|
||||
account.username,
|
||||
true,
|
||||
)
|
||||
await use(account)
|
||||
await deleteUser('Email/Password', account)
|
||||
},
|
||||
devOneAccount: async ({}, use) => {
|
||||
const account = testAccounts.dev_one_account()
|
||||
await seedUser(
|
||||
account.email,
|
||||
account.password,
|
||||
undefined,
|
||||
account.display_name,
|
||||
account.username,
|
||||
true,
|
||||
)
|
||||
await use(account)
|
||||
await deleteUser('Email/Password', account)
|
||||
},
|
||||
devTwoAccount: async ({}, use) => {
|
||||
const account = testAccounts.dev_two_account()
|
||||
await seedUser(
|
||||
account.email,
|
||||
account.password,
|
||||
undefined,
|
||||
account.display_name,
|
||||
account.username,
|
||||
true,
|
||||
)
|
||||
await use(account)
|
||||
await deleteUser('Email/Password', account)
|
||||
},
|
||||
specAccount: async ({}, use) => {
|
||||
const account = testAccounts.spec_account()
|
||||
await seedUser(
|
||||
account.email,
|
||||
account.password,
|
||||
undefined,
|
||||
account.display_name,
|
||||
account.username,
|
||||
true,
|
||||
)
|
||||
await use(account)
|
||||
await deleteUser('Email/Password', account)
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import {Page} from '@playwright/test'
|
||||
import {BrowserContext, Page} from '@playwright/test'
|
||||
|
||||
import {ContextManager} from '../../utils/contextManager'
|
||||
import {UserAccountInformation} from '../utils/accountInformation'
|
||||
import {AuthPage} from './authPage'
|
||||
import {CompatibilityPage} from './compatibilityPage'
|
||||
import {HomePage} from './homePage'
|
||||
import {MessagesPage} from './messagesPage'
|
||||
import {NotificationPage} from './notificationsPage'
|
||||
import {OnboardingPage} from './onboardingPage'
|
||||
import {OrganizationPage} from './organizationPage'
|
||||
import {PeoplePage} from './peoplePage'
|
||||
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
|
||||
@@ -25,6 +27,9 @@ export class App {
|
||||
readonly social: SocialPage
|
||||
readonly people: PeoplePage
|
||||
readonly notifs: NotificationPage
|
||||
readonly messages: MessagesPage
|
||||
readonly contextManager: ContextManager
|
||||
readonly context: BrowserContext
|
||||
|
||||
constructor(public readonly page: Page) {
|
||||
this.auth = new AuthPage(page)
|
||||
@@ -38,6 +43,12 @@ export class App {
|
||||
this.social = new SocialPage(page)
|
||||
this.people = new PeoplePage(page)
|
||||
this.notifs = new NotificationPage(page)
|
||||
this.messages = new MessagesPage(page)
|
||||
this.context = page.context()
|
||||
|
||||
const browser = page.context().browser()
|
||||
if (!browser) throw new Error('Could not get Browser from page.context().browser()')
|
||||
this.contextManager = new ContextManager(browser)
|
||||
}
|
||||
|
||||
async deleteProfileFromSettings() {
|
||||
|
||||
108
tests/e2e/web/pages/messagesPage.ts
Normal file
108
tests/e2e/web/pages/messagesPage.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import {expect, Locator, Page} from '@playwright/test'
|
||||
import {sleep} from 'common/util/time'
|
||||
|
||||
export class MessagesPage {
|
||||
private readonly messagesPageHeader: Locator
|
||||
private readonly messagesTable: Locator
|
||||
private readonly messagesRow: Locator
|
||||
private readonly messagesUsername: Locator
|
||||
private readonly messagesTimestamp: Locator
|
||||
private readonly newMessageButton: Locator
|
||||
private readonly newMessageSearchUsers: Locator
|
||||
private readonly newMessageSearchResults: Locator
|
||||
private readonly newMessageSearchCreateButton: Locator
|
||||
private readonly newMessageStart: Locator
|
||||
private readonly messageInput: Locator
|
||||
private readonly messageSubmit: Locator
|
||||
private readonly conversation: Locator
|
||||
private readonly conversationMessage: Locator
|
||||
|
||||
constructor(public readonly page: Page) {
|
||||
this.messagesPageHeader = page.getByRole('heading', {name: 'Messages'})
|
||||
this.messagesTable = page.getByTestId('messages-table')
|
||||
this.messagesRow = page.getByTestId('messages-row')
|
||||
this.messagesUsername = page.getByTestId('messages-username')
|
||||
this.messagesTimestamp = page.getByTestId('messages-timestamp')
|
||||
this.newMessageButton = page.getByRole('button', {name: 'New Message'})
|
||||
this.newMessageSearchUsers = page.getByRole('textbox', {name: 'Search users...'})
|
||||
this.newMessageSearchResults = page.getByTestId('search-results')
|
||||
this.newMessageSearchCreateButton = page.getByRole('button', {name: 'Create'})
|
||||
this.newMessageStart = page.getByText('No messages yet.', {exact: true})
|
||||
this.messageInput = page.locator('.tiptap')
|
||||
this.messageSubmit = page.getByTestId('conversation-message-submit')
|
||||
this.conversation = page.getByTestId('conversation')
|
||||
this.conversationMessage = page.getByTestId('conversation-message')
|
||||
}
|
||||
|
||||
async verifyMessagesPage() {
|
||||
await expect(this.messagesPageHeader).toBeVisible()
|
||||
}
|
||||
|
||||
async createNewMessage(username: string[]) {
|
||||
await expect(this.newMessageButton).toBeVisible()
|
||||
await this.newMessageButton.click()
|
||||
await expect(this.newMessageSearchUsers).toBeVisible()
|
||||
for (let i = 0; i < username.length; i++) {
|
||||
await this.newMessageSearchUsers.fill(username[i])
|
||||
await sleep(1000)
|
||||
await expect(this.newMessageSearchResults).toBeVisible()
|
||||
const results = await this.newMessageSearchResults
|
||||
.getByTestId('search-results-username')
|
||||
.all()
|
||||
const targetUser = username[i].toLowerCase()
|
||||
for (let j = 0; j < results.length; j++) {
|
||||
const usernameResults = (await results[j].textContent())?.toLowerCase()
|
||||
if (usernameResults === targetUser) {
|
||||
await results[j].click()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await expect(this.newMessageSearchCreateButton).toBeVisible()
|
||||
await this.newMessageSearchCreateButton.click()
|
||||
}
|
||||
|
||||
async sendMessage(message: string) {
|
||||
await expect(this.messageInput).toBeVisible()
|
||||
await this.messageInput.fill(message)
|
||||
await expect(this.messageSubmit).toBeVisible()
|
||||
await this.messageSubmit.click()
|
||||
const verified = await this.verifyMessage(message)
|
||||
if (!verified)
|
||||
throw new Error(`Message "${message}" was not found in conversation after sending`)
|
||||
}
|
||||
|
||||
async findMessageConversation(displayName: string) {
|
||||
await expect(this.messagesTable).toBeVisible()
|
||||
await this.page.waitForTimeout(1000)
|
||||
const doMessagesExist = (await this.messagesRow.count()) > 0
|
||||
if (doMessagesExist) {
|
||||
const messages = await this.messagesRow.getByTestId('messages-username').all()
|
||||
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
await expect(messages[i]).toBeVisible()
|
||||
const messageFromUser = await messages[i].textContent()
|
||||
if (messageFromUser?.toLowerCase() === displayName.toLowerCase()) await messages[i].click()
|
||||
}
|
||||
} else {
|
||||
throw new Error('There are no messages on this account')
|
||||
}
|
||||
}
|
||||
|
||||
async verifyMessage(messageContent: string) {
|
||||
await expect(this.conversation).toBeVisible()
|
||||
await sleep(1000)
|
||||
const messageCount = (await this.conversationMessage.count()) > 0
|
||||
if (messageCount) {
|
||||
const messages = await this.conversationMessage.all()
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = await messages[i].textContent()
|
||||
if (message?.toLowerCase() === messageContent.toLowerCase()) return true
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
throw new Error('There are no messages in this conversation')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import {expect, Locator, Page} from '@playwright/test'
|
||||
import {
|
||||
ConnectionTypeTuple,
|
||||
GenderTuple,
|
||||
EducationTuple,
|
||||
DietTuple,
|
||||
PsychedelicsTuple,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
ReligionTuple,
|
||||
PersonalityKey,
|
||||
LastActiveTuple,
|
||||
InterestedInGenderTuple,
|
||||
} from 'common/choices'
|
||||
import {MinMaxNumbers} from '../utils/accountInformation'
|
||||
|
||||
@@ -25,7 +25,7 @@ export type LifestyleFilter = {
|
||||
cause?: string
|
||||
diet?: DietTuple
|
||||
alcohol?: MinMaxNumbers
|
||||
smoker?: string
|
||||
smoker?: 'Yes' | 'No' | 'Either'
|
||||
psychedelics?: PsychedelicsTuple
|
||||
cannabis?: CannabisTuple
|
||||
language?: LanguageTuple
|
||||
@@ -56,13 +56,30 @@ export type AdvancedFilter = {
|
||||
|
||||
export type DisplayFilter = {
|
||||
cardSize?: 'Small' | 'Medium' | 'Large'
|
||||
filters?: [string, boolean][]
|
||||
filters?: {
|
||||
Gender?: boolean
|
||||
City?: boolean
|
||||
Age?: boolean
|
||||
Headline?: boolean
|
||||
Keywords?: boolean
|
||||
'What they seek'?: boolean
|
||||
Work?: boolean
|
||||
Interests?: boolean
|
||||
Causes?: boolean
|
||||
Diet?: boolean
|
||||
Smoking?: boolean
|
||||
Drinks?: boolean
|
||||
MBTI?: boolean
|
||||
Languages?: boolean
|
||||
Bio?: boolean
|
||||
'Profile photo'?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export type PeoplePageFilter = {
|
||||
connectionFilter?: ConnectionTypeTuple
|
||||
ageFilter?: MinMaxNumbers
|
||||
genderFilter?: GenderTuple
|
||||
genderFilter?: InterestedInGenderTuple
|
||||
backgroundFilter?: BackgroundFilter
|
||||
lifestyleFilter?: LifestyleFilter
|
||||
valuesAndBeliefsFilter?: BeliefsFilter
|
||||
@@ -71,7 +88,10 @@ export type PeoplePageFilter = {
|
||||
|
||||
export class PeoplePage {
|
||||
private readonly peopleHeading: Locator
|
||||
private readonly savedPeopleHeading: Locator
|
||||
private readonly savedPeopleList: Locator
|
||||
private readonly searchBox: Locator
|
||||
private readonly savedPeopleButton: Locator
|
||||
private readonly profileCount: Locator
|
||||
private readonly resetFilters: Locator
|
||||
private readonly yourFiltersCheckbox: Locator
|
||||
@@ -105,12 +125,20 @@ export class PeoplePage {
|
||||
private readonly displayDropdown: Locator
|
||||
private readonly profileGrid: Locator
|
||||
private readonly profileResults: Locator
|
||||
private readonly profileHide: Locator
|
||||
private readonly profileStar: Locator
|
||||
private readonly profileMessage: Locator
|
||||
private readonly messageInput: Locator
|
||||
private readonly profileName: Locator
|
||||
private readonly profileAgeGender: Locator
|
||||
private readonly profileSeeking: Locator
|
||||
|
||||
constructor(public readonly page: Page) {
|
||||
this.peopleHeading = page.getByRole('heading', {name: 'People'})
|
||||
this.savedPeopleHeading = page.getByRole('heading', {name: 'Saved People'})
|
||||
this.savedPeopleList = page.getByTestId('saved-person')
|
||||
this.searchBox = page.getByRole('textbox', {name: 'Search anything...'})
|
||||
this.savedPeopleButton = page.getByRole('button', {name: 'Saved People'})
|
||||
this.profileCount = page.getByTestId('people-profile-count')
|
||||
this.resetFilters = page.getByRole('button', {name: 'Reset filters'})
|
||||
this.yourFiltersCheckbox = page.getByText('Your filters', {exact: true})
|
||||
@@ -144,13 +172,27 @@ export class PeoplePage {
|
||||
this.displayDropdown = page.getByRole('button', {name: 'Display'})
|
||||
this.profileGrid = page.getByTestId('people-profile-grid')
|
||||
this.profileResults = page.getByTestId('people-profile-results')
|
||||
this.profileHide = page.getByTestId('hide-profile-button')
|
||||
this.profileStar = page.getByTestId('star-profile-button')
|
||||
this.profileMessage = page.getByTestId('message-profile-button')
|
||||
this.messageInput = page.locator('.tiptap')
|
||||
this.profileName = page.getByTestId('people-profile-name')
|
||||
this.profileAgeGender = page.getByTestId('people-profile-age-gender')
|
||||
this.profileSeeking = page.getByTestId('people-profile-seeking')
|
||||
}
|
||||
|
||||
get profileCountLocator(): Locator {
|
||||
return this.profileCount
|
||||
}
|
||||
get profileNameLocator(): Locator {
|
||||
return this.profileName
|
||||
}
|
||||
get profileAgeGenderLocator(): Locator {
|
||||
return this.profileAgeGender
|
||||
}
|
||||
get profileSeekingLocator(): Locator {
|
||||
return this.profileSeeking
|
||||
}
|
||||
|
||||
async sliderHelper(range: MinMaxNumbers, locator?: Locator) {
|
||||
let minSlider
|
||||
@@ -219,12 +261,16 @@ export class PeoplePage {
|
||||
await expect(this.peopleHeading).toBeVisible()
|
||||
}
|
||||
|
||||
//Doesn't actually work, need to find out why
|
||||
async clickSavedPeopleButton() {
|
||||
await expect(this.savedPeopleButton).toBeVisible()
|
||||
await this.savedPeopleButton.click()
|
||||
}
|
||||
|
||||
async useSearch(item: string) {
|
||||
await expect(this.searchBox).toBeVisible()
|
||||
await this.searchBox.click()
|
||||
await this.searchBox.fill(item)
|
||||
await this.page.keyboard.press('Enter')
|
||||
await this.page.waitForTimeout(1000)
|
||||
}
|
||||
|
||||
async resetFilter() {
|
||||
@@ -263,7 +309,7 @@ export class PeoplePage {
|
||||
await this.sliderHelper(ageRange)
|
||||
}
|
||||
|
||||
async setGenderTypeFilter(genderType: GenderTuple) {
|
||||
async setGenderTypeFilter(genderType: InterestedInGenderTuple) {
|
||||
await this.selectOption(this.genderDropdown, genderType[0])
|
||||
// await expect(this.genderDropdown).toBeVisible()
|
||||
// await this.genderDropdown.click()
|
||||
@@ -395,19 +441,14 @@ export class PeoplePage {
|
||||
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]}`})
|
||||
if (display.filters) {
|
||||
for (const [name, shouldBeChecked] of Object.entries(display.filters)) {
|
||||
const filter = await this.page.getByRole('checkbox', {name, exact: true})
|
||||
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
|
||||
}
|
||||
if (shouldBeChecked && !isChecked) await filter.click()
|
||||
if (!shouldBeChecked && isChecked) await filter.click()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -415,20 +456,77 @@ export class PeoplePage {
|
||||
async getProfileInfo() {
|
||||
await expect(this.profileGrid).toBeVisible()
|
||||
const totalResults = await this.profileResults.count()
|
||||
if (totalResults === 0) throw Error('No profiles found')
|
||||
const chosenProfileNumber = Math.floor(Math.random() * totalResults)
|
||||
const chosenProfile = await this.profileResults.nth(chosenProfileNumber)
|
||||
const profileName = await chosenProfile.getByTestId('people-profile-name').textContent()
|
||||
const ageGender = await chosenProfile.getByTestId('people-profile-age-gender').textContent()
|
||||
const seekingInfo = await chosenProfile.getByTestId('people-profile-seeking').textContent()
|
||||
const hideProfile = await chosenProfile.getByTestId('hide-profile-button')
|
||||
const starProfile = await chosenProfile.getByTestId('star-profile-button')
|
||||
const messageProfile = await chosenProfile.getByTestId('message-profile-button')
|
||||
|
||||
if (!profileName) return
|
||||
return {
|
||||
name: profileName,
|
||||
profile: chosenProfile ?? '',
|
||||
name: profileName ?? '',
|
||||
ageGender: ageGender ?? '',
|
||||
seeking: seekingInfo ?? '',
|
||||
hide: hideProfile ?? '',
|
||||
star: starProfile ?? '',
|
||||
message: messageProfile ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
async verifyNumberOfMatchingProfiles(count: number) {
|
||||
await expect(this.profileCount).toBeVisible()
|
||||
const actualCount = await this.profileCount.textContent()
|
||||
if (!actualCount) return
|
||||
expect(parseInt(actualCount)).toStrictEqual(count)
|
||||
async verifyProfileCount(totalProfiles: string) {
|
||||
const exists = (await this.profileCountLocator.count()) > 0
|
||||
|
||||
if (exists) {
|
||||
const filterdProfiles = await this.profileCountLocator.textContent()
|
||||
|
||||
if (!totalProfiles || !filterdProfiles) return
|
||||
await expect(parseInt(totalProfiles)).not.toEqual(parseInt(filterdProfiles))
|
||||
} else {
|
||||
const noProfilesFound = await this.page.getByText('No profiles found.', {exact: true})
|
||||
await expect(noProfilesFound).toBeVisible()
|
||||
}
|
||||
}
|
||||
|
||||
async selectProfile(displayName: string) {
|
||||
await expect(this.profileGrid).toBeVisible()
|
||||
await this.profileName.getByText(displayName).click()
|
||||
}
|
||||
|
||||
async messageProfile(displayName: string, message: string) {
|
||||
await expect(this.profileGrid).toBeVisible()
|
||||
const profiles = await this.profileResults.all()
|
||||
|
||||
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()
|
||||
await expect(this.messageInput).toBeVisible()
|
||||
await this.messageInput.fill(message)
|
||||
await this.page.getByTestId('conversation-message-submit').click()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async verifySavedPerson(displayName: string) {
|
||||
await expect(this.savedPeopleHeading).toBeVisible()
|
||||
await this.page.waitForTimeout(1000)
|
||||
const isThereSavedPeople = (await this.savedPeopleList.count()) > 0
|
||||
|
||||
if (isThereSavedPeople) {
|
||||
const listOfPeople = await this.savedPeopleList.all()
|
||||
for (let i = 0; i < listOfPeople.length; i++) {
|
||||
await expect(listOfPeople[i]).toBeVisible()
|
||||
const profileName = await listOfPeople[i].textContent()
|
||||
if (profileName?.toLowerCase() === displayName.toLowerCase()) return true
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
throw new Error('There are no profiles in the saved people list')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,7 +504,6 @@ export class ProfilePage {
|
||||
|
||||
async verifyKeywords(keywords: string | undefined) {
|
||||
if (!keywords) return
|
||||
console.log(this.keywordsSection.textContent())
|
||||
const keywordsArr = keywords.split(', ')
|
||||
await expect(this.keywordsSection).toBeVisible()
|
||||
for (const word of keywordsArr) {
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
import axios from 'axios'
|
||||
import {config} from '../SPEC_CONFIG'
|
||||
import {
|
||||
firebaseLoginEmailPassword,
|
||||
getOobCode,
|
||||
getUserId,
|
||||
sendVerificationEmail,
|
||||
} from '../../utils/firebaseUtils'
|
||||
|
||||
async function setup() {
|
||||
const results = await axios.post(`${config.FIREBASE_URL.BASE}${config.FIREBASE_URL.SIGNUP}`, {
|
||||
email: 'trial_test@email.com',
|
||||
password: 'trialTestPassword',
|
||||
returnSecureToken: true,
|
||||
})
|
||||
const loginInfo = await firebaseLoginEmailPassword('AnotherTest@email.com', 'Password')
|
||||
await sendVerificationEmail(loginInfo.data.idToken)
|
||||
const oobResponse = await axios.get(`${config.FIREBASE_URL.FIREBASE_EMULATOR_API}`)
|
||||
const oobCode = await getOobCode(oobResponse.data.oobCodes, 'AnotherTest@email.com')
|
||||
console.log(oobCode)
|
||||
|
||||
console.log('Auth created: ', 'trial_test@email.com')
|
||||
console.log('Id: ', results.data.localId)
|
||||
const response = await axios.post(
|
||||
`${config.FIREBASE_URL.BASE}${config.FIREBASE_URL.CONFIRM_EMAIL_VERIFICATION}`,
|
||||
{
|
||||
oobCode,
|
||||
},
|
||||
)
|
||||
console.log(response)
|
||||
console.log(response.status)
|
||||
}
|
||||
|
||||
setup()
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {sleep} from 'common/util/time'
|
||||
|
||||
import {TEST_USER_DISPLAY_NAME} from '../../utils/seedDatabase'
|
||||
import {expect, test} from '../fixtures/signInFixture'
|
||||
|
||||
test.describe('when given valid input', () => {
|
||||
@@ -10,27 +13,277 @@ test.describe('when given valid input', () => {
|
||||
await app.home.verifySignedInHomePage(account.display_name)
|
||||
})
|
||||
|
||||
test('the profile count should update successfully when applying a filter', async ({
|
||||
app,
|
||||
signedOutAccount: account,
|
||||
}) => {
|
||||
test('should be able to save/favorite people', async ({app, signedOutAccount: account}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
await app.people.getProfileInfo()
|
||||
const profile = await app.people.getProfileInfo()
|
||||
await expect(profile.star).toBeVisible()
|
||||
await profile.star.click()
|
||||
await sleep(1000)
|
||||
await app.people.clickSavedPeopleButton()
|
||||
await app.people.verifySavedPerson(profile.name)
|
||||
})
|
||||
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
expect(totalProfiles).toBeTruthy()
|
||||
const totalCount = Number.parseInt(totalProfiles!, 10)
|
||||
expect(Number.isNaN(totalCount)).toBe(false)
|
||||
test.describe('the applied filter should', () => {
|
||||
test('update the profile count', async ({app, signedOutAccount: account}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
|
||||
await app.people.setConnectionTypeFilter(['Collaboration', 'collaboration'])
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
await app.people.setConnectionTypeFilter(['Collaboration', 'collaboration'])
|
||||
await app.people.setDisplayFilter({cardSize: 'Large'})
|
||||
const filteredProfiles = await app.people.profileCountLocator.textContent()
|
||||
|
||||
// The count updates asynchronously after the filter is applied, so poll until it changes.
|
||||
await expect
|
||||
.poll(async () =>
|
||||
Number.parseInt((await app.people.profileCountLocator.textContent())!, 10),
|
||||
await expect(totalProfiles).not.toBeNull()
|
||||
await expect(filteredProfiles).not.toBeNull()
|
||||
console.log(totalProfiles)
|
||||
console.log(filteredProfiles)
|
||||
await expect(Number(totalProfiles?.split(' ')[0])).not.toEqual(
|
||||
Number(filteredProfiles?.split(' ')[0]),
|
||||
)
|
||||
.not.toEqual(totalCount)
|
||||
|
||||
const results = await app.people.getProfileInfo()
|
||||
if (!results) return
|
||||
await expect(results.seeking).toContain('Collaboration')
|
||||
})
|
||||
|
||||
/**
|
||||
* Test fails due to ui not updating
|
||||
* works fine manually
|
||||
*/
|
||||
test.skip('show profiles with the correct age', async ({app, signedOutAccount: account}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
await app.people.setDisplayFilter({filters: {Age: true}})
|
||||
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
const profileResults = await app.people.getProfileInfo()
|
||||
const profileAge = parseInt(profileResults.ageGender.match(/\d+/)?.[0] ?? '0')
|
||||
const age = profileAge <= 60 ? profileAge : 60
|
||||
console.log(profileResults, age)
|
||||
|
||||
await app.people.setAgeRangeFilter({min: String(age), max: String(age)})
|
||||
|
||||
const filterdProfiles = await app.people.profileCountLocator.textContent()
|
||||
|
||||
if (!totalProfiles || !filterdProfiles) return
|
||||
await expect(parseInt(totalProfiles)).not.toEqual(parseInt(filterdProfiles))
|
||||
})
|
||||
|
||||
test('show profiles with the correct gender', async ({app, signedOutAccount: account}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
await app.people.setGenderTypeFilter(['Woman', 'female'])
|
||||
await app.people.setDisplayFilter({cardSize: 'Large'})
|
||||
if (!totalProfiles) return
|
||||
await app.people.verifyProfileCount(totalProfiles)
|
||||
})
|
||||
|
||||
test('show profiles with the correct education level', async ({
|
||||
app,
|
||||
signedOutAccount: account,
|
||||
}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
await app.people.setBackgroundFilter({education: ['College', 'some-college']})
|
||||
await app.people.setDisplayFilter({cardSize: 'Large'})
|
||||
if (!totalProfiles) return
|
||||
await app.people.verifyProfileCount(totalProfiles)
|
||||
})
|
||||
|
||||
test('show profiles with the correct diet', async ({app, signedOutAccount: account}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
await app.people.setLifestyleFilter({diet: ['Vegetarian', 'veg']})
|
||||
await app.people.setDisplayFilter({cardSize: 'Large'})
|
||||
if (!totalProfiles) return
|
||||
await app.people.verifyProfileCount(totalProfiles)
|
||||
})
|
||||
|
||||
test('show profiles with the correct smoking preference', async ({
|
||||
app,
|
||||
signedOutAccount: account,
|
||||
}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
await app.people.setLifestyleFilter({smoker: 'Yes'})
|
||||
await app.people.setDisplayFilter({cardSize: 'Large'})
|
||||
if (!totalProfiles) return
|
||||
await app.people.verifyProfileCount(totalProfiles)
|
||||
})
|
||||
|
||||
test('show profiles with the correct psychedelics preference', async ({
|
||||
app,
|
||||
signedOutAccount: account,
|
||||
}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
await app.people.setLifestyleFilter({psychedelics: ['Regularly (weekly+)', 'regularly']})
|
||||
await app.people.setDisplayFilter({cardSize: 'Large'})
|
||||
if (!totalProfiles) return
|
||||
await app.people.verifyProfileCount(totalProfiles)
|
||||
})
|
||||
|
||||
test('show profiles with the correct cannabis preference', async ({
|
||||
app,
|
||||
signedOutAccount: account,
|
||||
}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
await app.people.setLifestyleFilter({
|
||||
cannabis: ['Occasionally (a few times a year)', 'occasionally'],
|
||||
})
|
||||
await app.people.setDisplayFilter({cardSize: 'Large'})
|
||||
if (!totalProfiles) return
|
||||
await app.people.verifyProfileCount(totalProfiles)
|
||||
})
|
||||
|
||||
test('show profiles with the correct political preference', async ({
|
||||
app,
|
||||
signedOutAccount: account,
|
||||
}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
await app.people.setValuesAndBeliefsFilter({political: ['Progressive', 'progressive']})
|
||||
await app.people.setDisplayFilter({cardSize: 'Large'})
|
||||
if (!totalProfiles) return
|
||||
await app.people.verifyProfileCount(totalProfiles)
|
||||
})
|
||||
|
||||
test('show profiles with the correct religion preference', async ({
|
||||
app,
|
||||
signedOutAccount: account,
|
||||
}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
|
||||
const totalProfiles = await app.people.profileCountLocator.textContent()
|
||||
await app.people.setValuesAndBeliefsFilter({religious: ['Jewish', 'jewish']})
|
||||
await app.people.setDisplayFilter({cardSize: 'Large'})
|
||||
if (!totalProfiles) return
|
||||
await app.people.verifyProfileCount(totalProfiles)
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('the hide profile feature', () => {
|
||||
test('should correctly hide a profile', async ({app, signedOutAccount: account}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
await app.people.useSearch(TEST_USER_DISPLAY_NAME)
|
||||
await sleep(1000)
|
||||
const results = await app.people.getProfileInfo()
|
||||
console.log(results)
|
||||
const hideProfileButton = await results.profile.getByRole('button', {
|
||||
name: 'Hide this profile',
|
||||
})
|
||||
await expect(hideProfileButton).toBeVisible()
|
||||
await hideProfileButton.click()
|
||||
await expect(
|
||||
app.page.getByText(`You won't see ${results.name} in your search results anymore.`),
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should be reversible using undo', async ({app, signedOutAccount: account}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
const results = await app.people.getProfileInfo()
|
||||
if (!results) return
|
||||
const hideProfileButton = await results.profile.getByRole('button', {
|
||||
name: 'Hide this profile',
|
||||
})
|
||||
await expect(hideProfileButton).toBeVisible()
|
||||
await hideProfileButton.click()
|
||||
const hideProfileMessage = await app.page.getByText(
|
||||
`You won't see ${results.name} in your search results anymore.`,
|
||||
)
|
||||
await expect(hideProfileMessage).toBeVisible()
|
||||
await app.people.page.getByRole('button', {name: 'Undo'}).click()
|
||||
await expect(hideProfileMessage).not.toBeVisible()
|
||||
const profile = await app.people.page.getByRole('heading', {name: `${results.name}`})
|
||||
await expect(profile).toBeVisible()
|
||||
})
|
||||
|
||||
test('should be reversible using manage hidden profiles feature in settings', async ({
|
||||
app,
|
||||
signedOutAccount: account,
|
||||
}) => {
|
||||
await app.signinWithEmail(account)
|
||||
await app.home.clickPeopleLink()
|
||||
const results = await app.people.getProfileInfo()
|
||||
if (!results) return
|
||||
const hideProfileButton = await results.profile.getByRole('button', {
|
||||
name: 'Hide this profile',
|
||||
})
|
||||
await expect(hideProfileButton).toBeVisible()
|
||||
await hideProfileButton.click()
|
||||
const hideProfileMessage = await app.page.getByText(
|
||||
`You won't see ${results.name} in your search results anymore.`,
|
||||
)
|
||||
await expect(hideProfileMessage).toBeVisible()
|
||||
await app.home.clickSettingsLink()
|
||||
await app.settings.clickManageHiddenProfilesButton()
|
||||
await app.settings.verifyHiddenProfiles([results.name])
|
||||
await app.settings.unhideProfiles(results.name)
|
||||
await app.settings.clickCloseButton()
|
||||
await app.home.clickPeopleLink()
|
||||
const profile = await app.people.page.getByRole('heading', {name: `${results.name}`})
|
||||
await expect(profile).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('a verified account should', () => {
|
||||
const message = 'This is a message'
|
||||
test('be able to send a message from the messages page', async ({
|
||||
app,
|
||||
signedInAccount: sender,
|
||||
signedOutAccount: receiver,
|
||||
}) => {
|
||||
const receiverApp = await app.contextManager.createContext()
|
||||
await receiverApp.signinWithEmail(receiver)
|
||||
|
||||
await app.home.clickMessagesLink()
|
||||
await app.messages.createNewMessage([receiver.display_name])
|
||||
await app.messages.sendMessage(message)
|
||||
|
||||
await receiverApp.home.clickMessagesLink()
|
||||
await receiverApp.messages.findMessageConversation(sender.display_name)
|
||||
await receiverApp.messages.verifyMessage(message)
|
||||
})
|
||||
|
||||
test('be able to send a message from the people page', async ({
|
||||
app,
|
||||
signedInAccount: sender,
|
||||
signedOutAccount: receiver,
|
||||
}) => {
|
||||
const receiverApp = await app.contextManager.createContext()
|
||||
await receiverApp.signinWithEmail(receiver)
|
||||
|
||||
// To pass the min character limit for message intro (250 chars)
|
||||
const longMessage = message.repeat(20)
|
||||
|
||||
await app.home.clickPeopleLink()
|
||||
await app.people.useSearch(receiver.display_name)
|
||||
await app.people.messageProfile(receiver.display_name, longMessage)
|
||||
await app.messages.verifyMessage(longMessage)
|
||||
|
||||
await receiverApp.home.clickMessagesLink()
|
||||
await receiverApp.messages.findMessageConversation(sender.display_name)
|
||||
await receiverApp.messages.verifyMessage(longMessage)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -28,4 +28,4 @@ test.describe('when an error occurs', () => {
|
||||
await app.signUp.verifyUsernameError()
|
||||
await expect(app.signUp.nextButtonLocator).toBeDisabled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -130,6 +130,7 @@ export function ChatMessageItem(props: {
|
||||
onTouchStart={() => startLongPress(chat.id)}
|
||||
onTouchEnd={cancelLongPress}
|
||||
onTouchCancel={cancelLongPress}
|
||||
data-testid="conversation-message"
|
||||
>
|
||||
<Content
|
||||
size={'sm'}
|
||||
|
||||
@@ -227,6 +227,7 @@ export function CommentInputTextArea(props: {
|
||||
)}
|
||||
{!isSubmitting && !isDisabled && submit && commentTypes.includes('comment') && (
|
||||
<button
|
||||
data-testid="conversation-message-submit"
|
||||
className="text-ink-500 hover:text-ink-700 active:bg-ink-300 disabled:text-ink-300 px-4 transition-colors"
|
||||
disabled={isDisabled || !editor || editor.isEmpty}
|
||||
onClick={() => submit('comment')}
|
||||
|
||||
@@ -232,7 +232,10 @@ function Filters(props: {
|
||||
const [noMinAge, noMaxAge] = getNoMinMaxAge(filters.pref_age_min, filters.pref_age_max)
|
||||
|
||||
return (
|
||||
<Col className="mb-[calc(var(--filter-offset)+env(safe-area-inset-bottom))] mt-[calc(var(--filter-offset)+env(safe-area-inset-top))] pt-3 pb-1">
|
||||
<Col
|
||||
className="mb-[calc(var(--filter-offset)+env(safe-area-inset-bottom))] mt-[calc(var(--filter-offset)+env(safe-area-inset-top))] pt-3 pb-1"
|
||||
data-testid="people-filters"
|
||||
>
|
||||
<SelectedFiltersSummary
|
||||
filters={filters}
|
||||
locationFilterProps={locationFilterProps}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import {ReactNode} from 'react'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
|
||||
export function IconWithInfo(props: {text?: string; icon: ReactNode; children?: ReactNode}) {
|
||||
const {text, icon, children} = props
|
||||
export function IconWithInfo(props: {
|
||||
text?: string
|
||||
icon: ReactNode
|
||||
children?: ReactNode
|
||||
testid?: string
|
||||
}) {
|
||||
const {text, icon, children, testid} = props
|
||||
return (
|
||||
<Row className="items-center gap-1" style={{gap: '5px'}}>
|
||||
<Row
|
||||
className="items-center gap-1"
|
||||
style={{gap: '5px'}}
|
||||
{...(testid ? {'data-testid': testid} : {})}
|
||||
>
|
||||
<div className="mt-0.5" style={{width: '14px', height: '14px'}}>
|
||||
{icon}
|
||||
</div>
|
||||
|
||||
@@ -1005,7 +1005,7 @@ export const OptionalProfileUserForm = (props: {
|
||||
{t('profile.optional.mbti', 'MBTI Personality Type')}
|
||||
</label>
|
||||
<ChoicesToggleGroup
|
||||
currentChoice={profile['mbti'] ?? ''}
|
||||
currentChoice={profile['mbti'] as any}
|
||||
choicesMap={MBTI_CHOICES}
|
||||
setChoice={(c) => setProfile('mbti', c)}
|
||||
className="grid grid-cols-4 xs:grid-cols-8"
|
||||
|
||||
@@ -431,7 +431,10 @@ function ProfilePreview(props: {
|
||||
/>
|
||||
)}
|
||||
{user && (
|
||||
<div className={clsx(cardSize !== 'large' && 'hidden sm:flex')}>
|
||||
<div
|
||||
className={clsx(cardSize !== 'large' && 'hidden sm:flex')}
|
||||
data-testid="message-profile-button"
|
||||
>
|
||||
<SendMessageButton
|
||||
toUser={user}
|
||||
profile={profile}
|
||||
@@ -506,6 +509,7 @@ function ProfilePreview(props: {
|
||||
)}
|
||||
{showSeeking !== false && seekingText && (
|
||||
<IconWithInfo
|
||||
testid="people-profile-seeking"
|
||||
text={seekingText}
|
||||
icon={<PiMagnifyingGlassBold className="h-4 w-4 " />}
|
||||
/>
|
||||
|
||||
@@ -230,7 +230,7 @@ function StarModal(props: {
|
||||
</p>
|
||||
<Col className={clsx('divide-y divide-canvas-200 w-full pr-2', SCROLLABLE_MODAL_CLASS)}>
|
||||
{visibleUsers.map((u) => (
|
||||
<div key={u.id} className="py-3 first:pt-0 last:pb-0">
|
||||
<div key={u.id} className="py-3 first:pt-0 last:pb-0" data-testid="saved-person">
|
||||
<div className="bg-canvas-0 border border-canvas-200 rounded-xl p-3 transition-all hover:border-primary-300 hover:shadow-sm">
|
||||
<Row className="items-center justify-between gap-3">
|
||||
<Link className="flex-1 group" href={'/' + u.username}>
|
||||
@@ -243,7 +243,10 @@ function StarModal(props: {
|
||||
/>
|
||||
</div>
|
||||
<Col className="flex-1">
|
||||
<div className="font-medium text-ink-900 group-hover:text-primary-600 transition-colors">
|
||||
<div
|
||||
className="font-medium text-ink-900 group-hover:text-primary-600 transition-colors"
|
||||
data-testid="saved-person-display-name"
|
||||
>
|
||||
{u.name}
|
||||
</div>
|
||||
<div className="text-ink-500 text-sm">@{u.username}</div>
|
||||
@@ -251,6 +254,7 @@ function StarModal(props: {
|
||||
</Row>
|
||||
</Link>
|
||||
<button
|
||||
data-testid="remove-saved-person"
|
||||
onClick={() => {
|
||||
// Optimistically remove the user from the list
|
||||
setRemovingIds((prev) => new Set(prev).add(u.id))
|
||||
|
||||
@@ -94,6 +94,7 @@ export function SelectUsers(props: {
|
||||
'relative inline-block w-full overflow-y-scroll text-right',
|
||||
queryReady && 'h-56',
|
||||
)}
|
||||
data-testid="search-results"
|
||||
>
|
||||
{() => (
|
||||
<Transition
|
||||
@@ -112,7 +113,7 @@ export function SelectUsers(props: {
|
||||
>
|
||||
<div className="py-1">
|
||||
{filteredUsers.map((user) => (
|
||||
<Menu.Item key={user.id}>
|
||||
<Menu.Item key={user.id} data-testid="search-results-username">
|
||||
{({active}) => (
|
||||
<button
|
||||
className={clsx(
|
||||
|
||||
@@ -76,6 +76,7 @@ export function HideProfileButton(props: HideProfileButtonProps) {
|
||||
noTap
|
||||
>
|
||||
<button
|
||||
data-testid="hide-profile-button"
|
||||
className={clsx(
|
||||
'relative inline-flex items-center justify-center border bg-canvas-50 border-canvas-300 text-ink-300 hover:border-primary-400 hover:bg-primary-50 rounded-lg h-7 w-7',
|
||||
className,
|
||||
|
||||
@@ -43,6 +43,7 @@ export const StarButton = (props: {
|
||||
|
||||
const button = (
|
||||
<button
|
||||
data-testid="star-profile-button"
|
||||
className={clsx(
|
||||
'border border-canvas-200',
|
||||
buttonClass('xs', 'none'),
|
||||
|
||||
@@ -388,6 +388,7 @@ export const PrivateChat = (props: {
|
||||
style={{
|
||||
transform: isSafari ? 'translate3d(0, 0, 0)' : 'none',
|
||||
}}
|
||||
data-testid="conversation"
|
||||
>
|
||||
<div
|
||||
className="relative px-1 pb-4 pt-1 transition-all duration-100"
|
||||
|
||||
@@ -64,7 +64,7 @@ export function MessagesContent(props: {currentUser: User}) {
|
||||
</h1>
|
||||
<NewMessageButton />
|
||||
</Row>
|
||||
<Col className={'w-full overflow-hidden gap-2'}>
|
||||
<Col className={'w-full overflow-hidden gap-2'} data-testid="messages-table">
|
||||
{channels && channels.length === 0 && (
|
||||
<div className="bg-canvas-50 border border-canvas-200 rounded-xl p-8 text-center mt-4">
|
||||
<p className="text-ink-500 text-sm mb-1">
|
||||
@@ -116,6 +116,7 @@ export const MessageChannelRow = (props: {
|
||||
className={
|
||||
'items-center gap-3 bg-canvas-50 border border-canvas-200 rounded-xl p-3 transition-all hover:border-primary-300 hover:shadow-sm hover:bg-canvas-100'
|
||||
}
|
||||
data-testid="messages-row"
|
||||
>
|
||||
{otherUsers && otherUsers.length > 0 ? (
|
||||
<MultipleOrSingleAvatars
|
||||
@@ -135,6 +136,7 @@ export const MessageChannelRow = (props: {
|
||||
className={
|
||||
'font-medium text-ink-900 text-sm group-hover:text-primary-600 transition-colors'
|
||||
}
|
||||
data-testid="messages-username"
|
||||
>
|
||||
{otherUsers && otherUsers.length > 0 ? (
|
||||
<span>
|
||||
@@ -162,7 +164,7 @@ export const MessageChannelRow = (props: {
|
||||
)}
|
||||
{isBanned && <BannedBadge />}
|
||||
</span>
|
||||
<span className={'text-ink-300 text-xs'}>
|
||||
<span className={'text-ink-300 text-xs'} data-testid="messages-timestamp">
|
||||
{lastMessage && <RelativeTimestamp time={lastMessage.createdTime} />}
|
||||
</span>
|
||||
</Row>
|
||||
|
||||
Reference in New Issue
Block a user