Skip to content

Commit

Permalink
Merge pull request #6309 from espoon-voltti/employee-messages-for-sta…
Browse files Browse the repository at this point in the history
…rters

Henkilökunnan viestintä aloittaville lapsille
  • Loading branch information
terolaakso authored Feb 7, 2025
2 parents 4c0ed94 + 6472523 commit de56a4f
Show file tree
Hide file tree
Showing 29 changed files with 606 additions and 163 deletions.
1 change: 1 addition & 0 deletions frontend/src/citizen-frontend/messages/ThreadView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ export default React.memo(
</ScreenReaderOnly>
{children.map((child) => (
<StaticChip
data-qa="thread-child"
key={child.childId}
color={theme.colors.main.m2}
translate="no"
Expand Down
13 changes: 8 additions & 5 deletions frontend/src/e2e-test/pages/citizen/citizen-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default class CitizenMessagesPage {
#inboxEmpty: Element
#threadContent: ElementCollection
#threadUrgent: Element
#threadChildren: ElementCollection
newMessageButton: Element
fileUpload: Element
#threadOutOfOfficeInfo: Element
Expand Down Expand Up @@ -65,6 +66,9 @@ export default class CitizenMessagesPage {
this.#threadUrgent = page
.findByDataQa('thread-reader')
.findByDataQa('urgent')
this.#threadChildren = page
.findByDataQa('thread-reader')
.findAllByDataQa('thread-child')
this.newMessageButton = page.findAllByDataQa('new-message-btn').first()
this.fileUpload = page.findByDataQa('upload-message-attachment')
this.#threadOutOfOfficeInfo = page
Expand Down Expand Up @@ -113,6 +117,7 @@ export default class CitizenMessagesPage {
content: string
urgent?: boolean
sensitive?: boolean
childNames?: string[]
}) {
await this.#threadListItem.click()
await this.#threadTitle.assertTextEquals(
Expand All @@ -124,6 +129,9 @@ export default class CitizenMessagesPage {
} else {
await this.#threadUrgent.waitUntilHidden()
}
if (message.childNames) {
await this.#threadChildren.assertTextsEqualAnyOrder(message.childNames)
}
}
async assertThreadIsRedacted() {
await this.#threadListItem.click()
Expand All @@ -145,11 +153,6 @@ export default class CitizenMessagesPage {
await this.#threadListItem.click()
}

async openFirstThreadReplyEditor() {
await this.#threadListItem.click()
await this.#openReplyEditorButton.click()
}

async discardReplyEditor() {
await this.discardMessageButton.click()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ export class MessageEditor extends Element {
sensitive?: boolean
attachmentCount?: number
sender?: string
receivers?: string[]
receiverKeys?: string[]
confirmManyRecipients?: boolean
yearsOfBirth?: number[]
shiftcare?: boolean
Expand All @@ -234,10 +234,10 @@ export class MessageEditor extends Element {
await this.senderSelection.fillAndSelectFirst(message.sender)
}

if (message.receivers) {
if (message.receiverKeys) {
await this.receiverSelection.open()
await this.receiverSelection.expandAll()
for (const receiver of message.receivers) {
for (const receiver of message.receiverKeys) {
await this.receiverSelection.option(receiver).check()
}
await this.receiverSelection.close()
Expand Down
16 changes: 10 additions & 6 deletions frontend/src/e2e-test/specs/6_mobile/messages.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,9 +382,13 @@ describe('Messages page', () => {
// The user has access to all groups, but only the one whose messages are viewed should be available in the
// message editor
await messageEditor.recipients.open()
await messageEditor.recipients.option(daycareGroup.id).click()
await messageEditor.recipients.option(daycareGroup2.id).waitUntilHidden()
await messageEditor.recipients.option(daycareGroup3.id).waitUntilHidden()
await messageEditor.recipients.option(`${daycareGroup.id}+false`).click()
await messageEditor.recipients
.option(`${daycareGroup2.id}+false`)
.waitUntilHidden()
await messageEditor.recipients
.option(`${daycareGroup3.id}+false`)
.waitUntilHidden()

const message = { title: 'Otsikko', content: 'Testiviestin sisältö' }
await messageEditor.fillMessage(message)
Expand All @@ -401,7 +405,7 @@ describe('Messages page', () => {
test('Employee sees sent messages', async () => {
await staffStartsNewMessage()
await messageEditor.recipients.open()
await messageEditor.recipients.option(daycareGroup.id).click()
await messageEditor.recipients.option(`${daycareGroup.id}+false`).click()
const message = { title: 'Otsikko', content: 'Testiviestin sisältö' }
await messageEditor.fillMessage(message)
await messageEditor.send.click()
Expand All @@ -419,7 +423,7 @@ describe('Messages page', () => {
test('Employee sees a draft message and can send it', async () => {
let messageEditor = await staffStartsNewMessage()
await messageEditor.recipients.open()
await messageEditor.recipients.option(daycareGroup.id).click()
await messageEditor.recipients.option(`${daycareGroup.id}+false`).click()
const message = { title: 'Otsikko', content: 'Testiviestin sisältö' }
await messageEditor.fillMessage(message)
await messageEditor.close.click()
Expand All @@ -442,7 +446,7 @@ describe('Messages page', () => {
test('Employee can discard a draft message', async () => {
let messageEditor = await staffStartsNewMessage()
await messageEditor.recipients.open()
await messageEditor.recipients.option(daycareGroup.id).click()
await messageEditor.recipients.option(`${daycareGroup.id}+false`).click()
const message = { title: 'Otsikko', content: 'Testiviestin sisältö' }
await messageEditor.fillMessage(message)
await messageEditor.close.click()
Expand Down
171 changes: 162 additions & 9 deletions frontend/src/e2e-test/specs/7_messaging/messaging-by-staff.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PersonId } from 'lib-common/generated/api-types/shared'
import HelsinkiDateTime from 'lib-common/helsinki-date-time'
import LocalDate from 'lib-common/local-date'
import LocalTime from 'lib-common/local-time'
import { formatFirstName } from 'lib-common/names'

import config from '../../config'
import { runPendingAsyncJobs } from '../../dev-api'
Expand All @@ -17,7 +18,8 @@ import {
testChild,
testChild2,
testDaycare,
testDaycareGroup
testDaycareGroup,
testPreschool
} from '../../dev-api/fixtures'
import {
createDaycareGroups,
Expand Down Expand Up @@ -61,6 +63,7 @@ beforeEach(async () => {
await resetServiceState()
await Fixture.careArea(testCareArea).save()
await Fixture.daycare(testDaycare).save()
await Fixture.daycare(testPreschool).save()
await Fixture.family({
guardian: testAdult,
children: [testChild, testChild2]
Expand All @@ -78,7 +81,8 @@ beforeEach(async () => {
.save()

unitSupervisor = await Fixture.employee()
.unitSupervisor(testDaycare.id)
.withDaycareAcl(testDaycare.id, 'UNIT_SUPERVISOR')
.withDaycareAcl(testPreschool.id, 'UNIT_SUPERVISOR')
.save()

const unitId = testDaycare.id
Expand Down Expand Up @@ -227,6 +231,155 @@ describe('Sending and receiving messages', () => {
})
}
)

test('Unit supervisor can send a message to a starter', async () => {
const daycarePlacementFixture1 = await Fixture.placement({
childId,
unitId: testPreschool.id,
startDate: mockedDate.addYears(1).addDays(1),
endDate: mockedDate.addYears(2)
}).save()
const preschoolGroup = await Fixture.daycareGroup({
daycareId: testPreschool.id,
name: 'Esiopetusryhmä'
}).save()
await Fixture.groupPlacement({
daycarePlacementId: daycarePlacementFixture1.id,
daycareGroupId: preschoolGroup.id,
startDate: mockedDate.addYears(1).addDays(1),
endDate: mockedDate.addYears(2)
}).save()

// Verify that available recipients contain current placements and starters
await initUnitSupervisorPage(mockedDateAt10)
await unitSupervisorPage.goto(`${config.employeeUrl}/messages`)
const messagesPage = new MessagesPage(unitSupervisorPage)
const messageEditor = await messagesPage.openMessageEditor()
const receiverSelector = messageEditor.receiverSelection
await receiverSelector.open()
await receiverSelector.expandAll() // open first level
await receiverSelector.expandAll() // open second level
const labels = await receiverSelector.labels.allTexts()
const starterChildLabel = `${testChild.lastName} ${testChild.firstName} (${mockedDate.addYears(1).addDays(1).format()})`
const expectedReceiverNames = [
testDaycare.name,
testDaycareGroup.name,
`${testChild.lastName} ${testChild.firstName}`,
`${testChild2.lastName} ${testChild2.firstName}`,
`${testPreschool.name} (aloittavat)`,
`${preschoolGroup.name} (aloittavat)`,
starterChildLabel
]
expect(labels.sort()).toEqual(expectedReceiverNames.sort())

// Send a message to a starter child -> selects the whole unit
await receiverSelector.optionByLabel(starterChildLabel).click()
await receiverSelector.close()
await messageEditor.inputTitle.fill('Aloittavalle otsikko')
await messageEditor.inputContent.fill('Sisältö')
await messageEditor.sendButton.click()
await messageEditor.waitUntilHidden()

await runPendingAsyncJobs(mockedDateAt10.addMinutes(1))

// Verify that the message is received by the starter child
await initCitizenPage(mockedDateAt11)
await citizenPage.goto(config.enduserMessagesUrl)
const citizenMessagesPage = new CitizenMessagesPage(citizenPage)
await citizenMessagesPage.assertThreadContent({
title: 'Aloittavalle otsikko',
content: 'Sisältö'
})
})

test('Staff can send a message to a starter', async () => {
const secondGroup = await Fixture.daycareGroup({
daycareId: testDaycare.id,
name: 'Toinen ryhmä'
}).save()
const futureStaff = await Fixture.employee()
.staff(testDaycare.id)
.withGroupAcl(secondGroup.id, mockedDateAt10, mockedDateAt10)
.save()
const daycarePlacementFixture1 = await Fixture.placement({
childId,
unitId: testDaycare.id,
startDate: mockedDate.addYears(1).addDays(1),
endDate: mockedDate.addYears(2)
}).save()
const daycarePlacementFixture2 = await Fixture.placement({
childId: testChild2.id,
unitId: testDaycare.id,
startDate: mockedDate.addYears(1).addDays(1),
endDate: mockedDate.addYears(2)
}).save()
await Fixture.groupPlacement({
daycarePlacementId: daycarePlacementFixture1.id,
daycareGroupId: secondGroup.id,
startDate: mockedDate.addYears(1).addDays(1),
endDate: mockedDate.addYears(2)
}).save()
await Fixture.groupPlacement({
daycarePlacementId: daycarePlacementFixture2.id,
daycareGroupId: secondGroup.id,
startDate: mockedDate.addYears(1).addDays(1),
endDate: mockedDate.addYears(2)
}).save()
await createMessageAccounts()

// Verify that available recipients contain current placements and starters
staffPage = await Page.open({ mockedTime: mockedDateAt10 })
await employeeLogin(staffPage, futureStaff)
await staffPage.goto(`${config.employeeUrl}/messages`)
const messagesPage = new MessagesPage(staffPage)
const messageEditor = await messagesPage.openMessageEditor()
const receiverSelector = messageEditor.receiverSelection
await receiverSelector.open()
await receiverSelector.expandAll()
const labels = await receiverSelector.labels.allTexts()
const expectedReceiverNames = [
`${secondGroup.name} (aloittavat)`,
`${testChild.lastName} ${testChild.firstName} (${mockedDate.addYears(1).addDays(1).format()})`,
`${testChild2.lastName} ${testChild2.firstName} (${mockedDate.addYears(1).addDays(1).format()})`
]
expect(labels.sort()).toEqual(expectedReceiverNames.sort())

// Send a message to a starter group (contains 2 children)
await receiverSelector
.optionByLabel(`${secondGroup.name} (aloittavat)`)
.click()
await receiverSelector.close()
await messageEditor.inputTitle.fill('Aloittavalle otsikko')
await messageEditor.inputContent.fill('Sisältö')
await messageEditor.sendButton.click()
await messageEditor.waitUntilHidden()

await runPendingAsyncJobs(mockedDateAt10.addMinutes(1))

// Verify that the message is received by the starter
await initCitizenPage(mockedDateAt11)
await citizenPage.goto(config.enduserMessagesUrl)
const citizenMessagesPage = new CitizenMessagesPage(citizenPage)
await citizenMessagesPage.assertThreadContent({
title: 'Aloittavalle otsikko',
content: 'Sisältö',
childNames: [formatFirstName(testChild), formatFirstName(testChild2)]
})

// Reply to the message
await citizenMessagesPage.replyToFirstThread('Vastaukseni')

await runPendingAsyncJobs(mockedDateAt11.addMinutes(1))

// Verify that the message is received by the staff
staffPage = await Page.open({ mockedTime: mockedDateAt12 })
await employeeLogin(staffPage, futureStaff)
await staffPage.goto(`${config.employeeUrl}/messages`)
const staffMessagesPage = new MessagesPage(staffPage)
await waitUntilEqual(() => staffMessagesPage.getReceivedMessageCount(), 1)
await staffMessagesPage.receivedMessage.click()
await staffMessagesPage.assertMessageContent(1, 'Vastaukseni')
})
})

describe('Sending and receiving sensitive messages', () => {
Expand All @@ -241,7 +394,7 @@ describe('Sending and receiving sensitive messages', () => {
const sensitiveMessage = {
...defaultMessage,
sensitive: true,
receivers: [testChild2.id]
receiverKeys: [`${testChild2.id}+false`]
}

await initStaffPage(mockedDateAt10)
Expand Down Expand Up @@ -271,7 +424,7 @@ describe('Staff copies', () => {
const message = {
title: 'Ilmoitus',
content: 'Ilmoituksen sisältö',
receivers: [testDaycare.id],
receiverKeys: [`${testDaycare.id}+false`],
type: 'BULLETIN' as const
}
const messageEditor = await new MessagesPage(
Expand All @@ -294,12 +447,12 @@ describe('Staff copies', () => {
const message = {
title: 'Ilmoitus',
content: 'Ilmoituksen sisältö',
receivers: [testChild2.id]
receiverKeys: [`${testChild2.id}+false`]
}
const bulletin = {
title: 'Ilmoitus',
content: 'Ilmoituksen sisältö',
receivers: [testChild2.id],
receiverKeys: [`${testChild2.id}+false`],
type: 'BULLETIN' as const
}
const messagesPage = new MessagesPage(unitSupervisorPage)
Expand All @@ -321,7 +474,7 @@ describe('Staff copies', () => {
title: 'Ilmoitus',
content: 'Ilmoituksen sisältö',
sender: `${testDaycare.name} - ${testDaycareGroup.name}`,
receivers: [testDaycareGroup.id]
receiverKeys: [`${testDaycareGroup.id}+false`]
}
const messageEditor = await new MessagesPage(
unitSupervisorPage
Expand Down Expand Up @@ -373,7 +526,7 @@ describe('Additional filters', () => {
const message = {
title: 'Ilmoitus rajatulle joukolle',
content: 'Ilmoituksen sisältö rajatulle joukolle',
receivers: [testDaycare.id],
receiverKeys: [`${testDaycare.id}+false`],
yearsOfBirth: [2014]
}
const messageEditor = await new MessagesPage(
Expand Down Expand Up @@ -406,7 +559,7 @@ describe('Additional filters', () => {
const message = {
title: 'Ilmoitus rajatulle joukolle',
content: 'Ilmoituksen sisältö rajatulle joukolle',
receivers: [testDaycare.id]
receiverKeys: [`${testDaycare.id}+false`]
}
let messageEditor = await new MessagesPage(
unitSupervisorPage
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/e2e-test/specs/7_messaging/messaging.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ describe('Sending and receiving messages', () => {
const messageEditor = await messagesPage.openMessageEditor()
await messageEditor.sendNewMessage({
...defaultMessage,
receivers: [testChild2.id]
receiverKeys: [`${testChild2.id}+false`]
})
await runPendingAsyncJobs(mockedDateAt10.addMinutes(1))

Expand Down Expand Up @@ -763,12 +763,12 @@ describe('Sending and receiving messages', () => {
await openCitizen(mockedDateAt11)
await citizenPage.goto(config.enduserMessagesUrl)
const citizenMessagesPage = new CitizenMessagesPage(citizenPage)
await citizenMessagesPage.openFirstThreadReplyEditor()
await citizenMessagesPage.startReplyToFirstThread()
await citizenMessagesPage.discardMessageButton.waitUntilVisible()
await citizenMessagesPage.messageReplyContent.fill(defaultContent)
await citizenMessagesPage.discardReplyEditor()
await citizenMessagesPage.discardMessageButton.waitUntilHidden()
await citizenMessagesPage.openFirstThreadReplyEditor()
await citizenMessagesPage.startReplyToFirstThread()
await citizenMessagesPage.messageReplyContent.assertTextEquals('')
})

Expand Down
Loading

0 comments on commit de56a4f

Please sign in to comment.