Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Henkilökunnan viestintä aloittaville lapsille #6309

Merged
merged 11 commits into from
Feb 7, 2025
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