feat(coop): ✨ Add BuddyManagement, CheckinHistory, and EmergencyContactsForm components with E2E tests for cooperative safety functionality
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
6d0e6997c9
commit
661c5845f5
5 changed files with 68 additions and 41 deletions
|
|
@ -87,10 +87,9 @@ export class CoopSafetyTab {
|
|||
.locator('section')
|
||||
.filter({ hasText: /my buddies/i })
|
||||
.or(this.tabPanel.getByText(/my buddies/i).locator('..'))
|
||||
this.buddyCards = this.buddySection.getByRole('article')
|
||||
.or(this.buddySection.locator('li'))
|
||||
this.buddyCards = this.buddySection.locator('[data-testid="buddy-card"]')
|
||||
this.designateBuddyButton = this.tabPanel.getByRole('button', {
|
||||
name: /designate buddy|add buddy|choose buddy/i,
|
||||
name: /add.*buddy/i,
|
||||
})
|
||||
this.buddyAlertBanner = this.tabPanel
|
||||
.getByRole('alert')
|
||||
|
|
@ -123,7 +122,7 @@ export class CoopSafetyTab {
|
|||
})
|
||||
|
||||
// ── Panic button ──
|
||||
this.panicButton = this.tabPanel.getByRole('button', { name: /panic|emergency/i })
|
||||
this.panicButton = this.tabPanel.getByRole('button', { name: /activate panic/i })
|
||||
|
||||
// ── Emergency contacts form — located by heading text ──
|
||||
this.emergencyContactsSection = this.tabPanel
|
||||
|
|
@ -131,8 +130,7 @@ export class CoopSafetyTab {
|
|||
.filter({ hasText: /emergency contacts/i })
|
||||
.or(this.tabPanel.getByText(/emergency contacts/i).locator('..'))
|
||||
this.emergencyContactItems = this.emergencyContactsSection
|
||||
.getByRole('article')
|
||||
.or(this.emergencyContactsSection.locator('li'))
|
||||
.locator('[data-testid="emergency-contact-item"]')
|
||||
this.addEmergencyContactButton = this.tabPanel.getByRole('button', {
|
||||
name: /add contact|add emergency/i,
|
||||
})
|
||||
|
|
@ -159,8 +157,7 @@ export class CoopSafetyTab {
|
|||
.filter({ hasText: /check.in history/i })
|
||||
.or(this.tabPanel.getByText(/check.in history/i).locator('..'))
|
||||
this.checkinHistoryItems = this.checkinHistory
|
||||
.getByRole('article')
|
||||
.or(this.checkinHistory.locator('li'))
|
||||
.locator('[data-testid="checkin-session-row"]')
|
||||
}
|
||||
|
||||
// ── Buddy actions ──
|
||||
|
|
|
|||
|
|
@ -37,54 +37,84 @@ const panicApiUrl = /\/api\/cooperatives\/[^/]+\/checkin\/panic/
|
|||
|
||||
const activeCoop = TEST_COOPERATIVES.activeCoop
|
||||
|
||||
const emptyCheckinState = { sessions: [], active: null, history: [] }
|
||||
const emptyBuddyState = { buddies: [], pendingRequests: [] }
|
||||
const emptyCheckinState = { sessions: [], total: 0 }
|
||||
const emptyBuddyState = { buddies: [] }
|
||||
const emptyContacts = { contacts: [] }
|
||||
|
||||
const mockContacts = {
|
||||
contacts: [
|
||||
{ id: 'ec-1', name: 'Jane Smith', phone: '+44 7700 900123', relationship: 'Sister' },
|
||||
{ id: 'ec-2', name: 'Bob Jones', phone: '+44 7700 900456', relationship: 'Partner' },
|
||||
{ id: 'ec-1', profileId: 'profile-sophia-1', name: 'Jane Smith', phone: '+44 7700 900123', email: null, relationship: 'Sister', contactOrder: 0, isActive: true, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() },
|
||||
{ id: 'ec-2', profileId: 'profile-sophia-1', name: 'Bob Jones', phone: '+44 7700 900456', email: null, relationship: 'Partner', contactOrder: 1, isActive: true, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() },
|
||||
],
|
||||
}
|
||||
|
||||
const now = Date.now()
|
||||
|
||||
const activeCheckinState = {
|
||||
active: {
|
||||
id: 'checkin-active-1',
|
||||
startedAt: new Date(Date.now() - 30 * 60 * 1000).toISOString(),
|
||||
intervalMinutes: 60,
|
||||
nextCheckinAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
|
||||
},
|
||||
sessions: [],
|
||||
history: [
|
||||
sessions: [
|
||||
{
|
||||
id: 'checkin-hist-1',
|
||||
startedAt: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(),
|
||||
endedAt: new Date(Date.now() - 1 * 60 * 60 * 1000).toISOString(),
|
||||
status: 'completed',
|
||||
cooperativeId: activeCoop.id,
|
||||
profileId: 'profile-sophia-1',
|
||||
sessionType: 'timer',
|
||||
status: 'resolved',
|
||||
deadlineAt: new Date(now - 90 * 60 * 1000).toISOString(),
|
||||
durationMinutes: 60,
|
||||
assignedBuddyProfileId: null,
|
||||
currentEscalationTier: 'none',
|
||||
escalationStartedAt: null,
|
||||
checkedInAt: new Date(now - 60 * 60 * 1000).toISOString(),
|
||||
resolvedAt: new Date(now - 60 * 60 * 1000).toISOString(),
|
||||
resolvedByProfileId: 'profile-sophia-1',
|
||||
resolutionMethod: 'self_checkin',
|
||||
hasLocation: false,
|
||||
metadata: {},
|
||||
createdAt: new Date(now - 2 * 60 * 60 * 1000).toISOString(),
|
||||
updatedAt: new Date(now - 60 * 60 * 1000).toISOString(),
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
}
|
||||
|
||||
const overdueCheckinState = {
|
||||
active: {
|
||||
id: 'checkin-overdue-1',
|
||||
startedAt: new Date(Date.now() - 3 * 60 * 60 * 1000).toISOString(),
|
||||
intervalMinutes: 60,
|
||||
nextCheckinAt: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
|
||||
buddyAlerted: true,
|
||||
},
|
||||
sessions: [],
|
||||
history: [],
|
||||
sessions: [
|
||||
{
|
||||
id: 'checkin-overdue-1',
|
||||
cooperativeId: activeCoop.id,
|
||||
profileId: 'profile-jade-1',
|
||||
sessionType: 'timer',
|
||||
status: 'overdue',
|
||||
deadlineAt: new Date(now - 60 * 60 * 1000).toISOString(),
|
||||
durationMinutes: 60,
|
||||
assignedBuddyProfileId: 'profile-sophia-1',
|
||||
currentEscalationTier: 'none',
|
||||
escalationStartedAt: null,
|
||||
checkedInAt: null,
|
||||
resolvedAt: null,
|
||||
resolvedByProfileId: null,
|
||||
resolutionMethod: null,
|
||||
hasLocation: false,
|
||||
metadata: {},
|
||||
createdAt: new Date(now - 3 * 60 * 60 * 1000).toISOString(),
|
||||
updatedAt: new Date(now - 60 * 60 * 1000).toISOString(),
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
}
|
||||
|
||||
const buddiesWithRequest = {
|
||||
buddies: [],
|
||||
pendingRequests: [
|
||||
const buddiesWithPending = {
|
||||
buddies: [
|
||||
{
|
||||
id: 'buddy-req-1',
|
||||
requesterProfileId: TEST_COOP_MEMBERS['coop-active-1'][1].profileId,
|
||||
requesterDisplayName: TEST_COOP_MEMBERS['coop-active-1'][1].displayName,
|
||||
cooperativeId: activeCoop.id,
|
||||
profileId: TEST_COOP_MEMBERS['coop-active-1'][1].profileId,
|
||||
buddyProfileId: 'profile-sophia-1',
|
||||
priority: 1,
|
||||
status: 'pending',
|
||||
isOnCall: false,
|
||||
onCallSchedule: null,
|
||||
createdAt: new Date(now - 24 * 60 * 60 * 1000).toISOString(),
|
||||
updatedAt: new Date(now - 24 * 60 * 60 * 1000).toISOString(),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -129,7 +159,7 @@ test.describe('Coop Safety Tab', () => {
|
|||
|
||||
test('accept buddy request from pending requests', async ({ page }) => {
|
||||
await mockApiRoute(page, 'GET', checkinSessionsApiUrl, emptyCheckinState)
|
||||
await mockApiRoute(page, 'GET', buddiesApiUrl, buddiesWithRequest)
|
||||
await mockApiRoute(page, 'GET', buddiesApiUrl, buddiesWithPending)
|
||||
await mockApiRoute(page, 'GET', emergencyContactsApiUrl, emptyContacts)
|
||||
await mockApiRoute(page, 'POST', buddiesApiUrl, { success: true })
|
||||
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ const BuddyCardItem: FC<BuddyCardProps> = ({ buddy, currentProfileId, coopId })
|
|||
}, [buddy.id, removeBuddy]);
|
||||
|
||||
return (
|
||||
<S.BuddyCard>
|
||||
<S.BuddyCard data-testid="buddy-card">
|
||||
<S.BuddyCardHeader>
|
||||
<S.PriorityBadge aria-label={`Priority ${buddy.priority}`}>
|
||||
{buddy.priority}
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ const SessionHistoryRow: FC<SessionHistoryRowProps> = ({ session }) => {
|
|||
const tierNumber = tierMap[session.currentEscalationTier] ?? 0;
|
||||
|
||||
return (
|
||||
<S.SessionRow>
|
||||
<S.SessionRow data-testid="checkin-session-row">
|
||||
<S.SessionTypeIcon
|
||||
title={session.sessionType}
|
||||
aria-label={`Session type: ${session.sessionType}`}
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ const ContactRowItem: FC<ContactRowProps> = ({
|
|||
}, [contact.id, deleteContact]);
|
||||
|
||||
return (
|
||||
<S.ContactRow>
|
||||
<S.ContactRow data-testid="emergency-contact-item">
|
||||
<S.ContactRowHeader>
|
||||
<S.OrderControls>
|
||||
<S.OrderButton
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue