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

Näytetään tulevien päivien suunnitellut työvuorot työntekijän mobiilissa #6495

Merged
merged 7 commits into from
Mar 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 49 additions & 5 deletions frontend/src/e2e-test/pages/mobile/staff-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: LGPL-2.1-or-later

import { StaffAttendanceType } from 'lib-common/generated/api-types/attendance'
import { EmployeeId } from 'lib-common/generated/api-types/shared'
import LocalDate from 'lib-common/local-date'
import { UUID } from 'lib-common/types'

Expand All @@ -27,7 +28,8 @@ export class StaffAttendancePage {
departureTime: Element

#addNewExternalMemberButton: Element
#tabs: { present: Element; absent: Element }
#primaryTabs: { today: Element; planned: Element }
#todayTabs: { present: Element; absent: Element }
pinInput: Element

previousAttendancesPage: {
Expand Down Expand Up @@ -90,7 +92,11 @@ export class StaffAttendancePage {
this.#addNewExternalMemberButton = page.findByDataQa(
'add-external-member-btn'
)
this.#tabs = {
this.#primaryTabs = {
today: page.findByDataQa('today-tab'),
planned: page.findByDataQa('planned-tab')
}
this.#todayTabs = {
present: page.findByDataQa('present-tab'),
absent: page.findByDataQa('absent-tab')
}
Expand Down Expand Up @@ -211,7 +217,7 @@ export class StaffAttendancePage {
}

async assertPresentStaffCount(expected: number) {
await this.#tabs.present.assertTextEquals(`LÄSNÄ\n(${expected})`)
await this.#todayTabs.present.assertTextEquals(`LÄSNÄ\n(${expected})`)
}

async openStaffPage(name: string) {
Expand All @@ -222,8 +228,12 @@ export class StaffAttendancePage {
await this.anyMemberPage.status.assertTextEquals(expected)
}

async selectPrimaryTab(tab: 'today' | 'planned') {
await this.#primaryTabs[tab].click()
}

async selectTab(tab: 'present' | 'absent') {
await this.#tabs[tab].click()
await this.#todayTabs[tab].click()
}

async goBackFromMemberPage() {
Expand Down Expand Up @@ -348,7 +358,7 @@ export class StaffAttendancePage {
)
}

async selectGroup(groupId: string) {
async selectArrivalGroup(groupId: string) {
await this.staffArrivalPage.groupSelect.selectOption(groupId)
}

Expand Down Expand Up @@ -418,3 +428,37 @@ export class StaffAttendanceEditPage {
await new AsyncButton(this.page.findByDataQa('confirm')).click()
}
}

export class PlannedAttendancesPage {
private page: Page

constructor(page: Page) {
this.page = page
}

getDateRow(date: LocalDate) {
return this.page.findByDataQa(`date-row-${date.formatIso()}`)
}

getExpandedDate(date: LocalDate) {
return this.page.findByDataQa(`expanded-date-${date.formatIso()}`)
}

getPresentEmployee(date: LocalDate, id: EmployeeId) {
return this.getExpandedDate(date).findByDataQa(`present-employee-${id}`)
}

getAbsentEmployee(date: LocalDate, id: EmployeeId) {
return this.getExpandedDate(date).findByDataQa(`absent-employee-${id}`)
}

getConfidenceWarning(date: LocalDate, id: EmployeeId) {
return this.getPresentEmployee(date, id).findByDataQa('confidence-warning')
}

async assertPresentCount(date: LocalDate, count: number) {
return this.getDateRow(date)
.findByDataQa('present-count')
.assertTextEquals(count.toString())
}
}
178 changes: 178 additions & 0 deletions frontend/src/e2e-test/specs/6_mobile/planned-staff-attendances.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// SPDX-FileCopyrightText: 2017-2025 City of Espoo
//
// SPDX-License-Identifier: LGPL-2.1-or-later

import HelsinkiDateTime from 'lib-common/helsinki-date-time'
import { randomId } from 'lib-common/id-type'
import LocalDate from 'lib-common/local-date'
import LocalTime from 'lib-common/local-time'

import { mobileViewport } from '../../browser'
import { testDaycare2, testDaycareGroup, Fixture } from '../../dev-api/fixtures'
import { resetServiceState } from '../../generated/api-clients'
import {
DevCareArea,
DevDaycareGroup,
DevEmployee
} from '../../generated/api-types'
import MobileNav from '../../pages/mobile/mobile-nav'
import {
PlannedAttendancesPage,
StaffAttendancePage
} from '../../pages/mobile/staff-page'
import { pairMobileDevice } from '../../utils/mobile'
import { Page } from '../../utils/page'

let page: Page
let nav: MobileNav
let staffPage: StaffAttendancePage
let plannedAttendancesPage: PlannedAttendancesPage

let careArea: DevCareArea
let aku: DevEmployee
let mikki: DevEmployee

const pin = '4242'

const today = LocalDate.of(2025, 3, 3) // Monday

const daycareGroup2Fixture: DevDaycareGroup = {
...testDaycareGroup,
id: randomId(),
name: 'Ryhmä 2'
}

beforeEach(async () => {
await resetServiceState()

careArea = await Fixture.careArea().save()
await Fixture.daycare({
...testDaycare2,
areaId: careArea.id,
enabledPilotFeatures: ['REALTIME_STAFF_ATTENDANCE']
}).save()

await Fixture.daycareGroup({
...testDaycareGroup,
daycareId: testDaycare2.id
}).save()
await Fixture.daycareGroup({
...daycareGroup2Fixture,
daycareId: testDaycare2.id
}).save()

aku = await Fixture.employee({
preferredFirstName: 'Aku',
firstName: 'Antero',
lastName: 'Ankka'
})
.staff(testDaycare2.id)
.withGroupAcl(testDaycareGroup.id)
.save()
mikki = await Fixture.employee({
firstName: 'Mikki',
lastName: 'Hiiri'
})
.staff(testDaycare2.id)
.withGroupAcl(testDaycareGroup.id)
.withGroupAcl(daycareGroup2Fixture.id)
.save()
await Fixture.employeePin({ userId: aku.id, pin }).save()
await Fixture.employeePin({ userId: mikki.id, pin }).save()
})

const initPages = async (mockedTime: HelsinkiDateTime) => {
page = await Page.open({
viewport: mobileViewport,
mockedTime
})
nav = new MobileNav(page)
staffPage = new StaffAttendancePage(page)
plannedAttendancesPage = new PlannedAttendancesPage(page)

const mobileSignupUrl = await pairMobileDevice(testDaycare2.id)
await page.goto(mobileSignupUrl)
await nav.staff.click()
}

describe('Planned staff attendances', () => {
test('shows who has planned attendances during next days', async () => {
const tuesday = today.addDays(1)
const wednesday = today.addDays(2)
const thursday = today.addDays(3)
const friday = today.addDays(4)

await Fixture.staffAttendancePlan({
employeeId: aku.id,
startTime: HelsinkiDateTime.fromLocal(tuesday, LocalTime.of(9, 0)),
endTime: HelsinkiDateTime.fromLocal(tuesday, LocalTime.of(17, 0))
}).save()
await Fixture.staffAttendancePlan({
employeeId: mikki.id,
startTime: HelsinkiDateTime.fromLocal(wednesday, LocalTime.of(9, 0)),
endTime: HelsinkiDateTime.fromLocal(wednesday, LocalTime.of(12, 0))
}).save()
await Fixture.staffAttendancePlan({
employeeId: mikki.id,
startTime: HelsinkiDateTime.fromLocal(wednesday, LocalTime.of(22, 0)),
endTime: HelsinkiDateTime.fromLocal(thursday, LocalTime.of(7, 0))
}).save()

await initPages(HelsinkiDateTime.fromLocal(today, LocalTime.of(6, 0)))
await staffPage.selectPrimaryTab('planned')
await plannedAttendancesPage.assertPresentCount(tuesday, 1)
await plannedAttendancesPage.assertPresentCount(wednesday, 1)
await plannedAttendancesPage.assertPresentCount(thursday, 1)
await plannedAttendancesPage.assertPresentCount(friday, 0)

await plannedAttendancesPage.getExpandedDate(tuesday).waitUntilHidden()
await plannedAttendancesPage.getDateRow(tuesday).click()
await plannedAttendancesPage.getExpandedDate(tuesday).waitUntilVisible()
await plannedAttendancesPage
.getPresentEmployee(tuesday, aku.id)
.assertText((s) => s.includes('Aku Ankka') && s.includes('09:00 - 17:00'))
await plannedAttendancesPage
.getConfidenceWarning(tuesday, aku.id)
.waitUntilHidden()
await plannedAttendancesPage
.getAbsentEmployee(tuesday, mikki.id)
.assertText((s) => s.includes('Mikki Hiiri'))

await plannedAttendancesPage.getDateRow(wednesday).click()
await plannedAttendancesPage.getExpandedDate(tuesday).waitUntilHidden()
await plannedAttendancesPage.getExpandedDate(wednesday).waitUntilVisible()
await plannedAttendancesPage
.getAbsentEmployee(wednesday, aku.id)
.waitUntilVisible()
await plannedAttendancesPage
.getPresentEmployee(wednesday, mikki.id)
.assertText((s) => s.includes('09:00 - 12:00,\n22:00 - →'))
await plannedAttendancesPage
.getConfidenceWarning(wednesday, mikki.id)
.assertText((s) => s.includes('Työvuoro voi olla toisessa ryhmässä'))

await plannedAttendancesPage.getDateRow(thursday).click()
await plannedAttendancesPage
.getPresentEmployee(thursday, mikki.id)
.assertText((s) => s.includes('→ - 07:00'))

// select group where only Mikki has been authorized
await nav.selectGroup(daycareGroup2Fixture.id)
await plannedAttendancesPage.assertPresentCount(tuesday, 0)
await plannedAttendancesPage.assertPresentCount(wednesday, 1)
await plannedAttendancesPage.assertPresentCount(thursday, 1)
await plannedAttendancesPage.assertPresentCount(friday, 0)

// On Tuesday Mikki is absent while Aku is neither present nor absent
await plannedAttendancesPage.getDateRow(tuesday).click()
await plannedAttendancesPage
.getAbsentEmployee(tuesday, mikki.id)
.waitUntilVisible()
await plannedAttendancesPage
.getPresentEmployee(tuesday, aku.id)
.waitUntilHidden()
await plannedAttendancesPage
.getAbsentEmployee(tuesday, aku.id)
.waitUntilHidden()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ describe('Realtime staff attendance page', () => {

// Within 30min from now so ok
await staffAttendancePage.setArrivalTime('12:30')
await staffAttendancePage.selectGroup(testDaycareGroup.id)
await staffAttendancePage.selectArrivalGroup(testDaycareGroup.id)
await staffAttendancePage.assertDoneButtonEnabled(true)

// 1min too far in the future
Expand Down Expand Up @@ -421,14 +421,14 @@ describe('Realtime staff attendance page', () => {

// Within 30min from planned start so ok, type required
await staffAttendancePage.setArrivalTime('07:30')
await staffAttendancePage.selectGroup(testDaycareGroup.id)
await staffAttendancePage.selectArrivalGroup(testDaycareGroup.id)
await staffAttendancePage.assertDoneButtonEnabled(false)
await staffAttendancePage.selectAttendanceType('JUSTIFIED_CHANGE')
await staffAttendancePage.assertDoneButtonEnabled(true)

// Within 5min from planned start so ok, type not required
await staffAttendancePage.setArrivalTime('07:55')
await staffAttendancePage.selectGroup(testDaycareGroup.id)
await staffAttendancePage.selectArrivalGroup(testDaycareGroup.id)
await staffAttendancePage.assertDoneButtonEnabled(true)

// Not ok because >+-30min from current time
Expand Down Expand Up @@ -466,12 +466,12 @@ describe('Realtime staff attendance page', () => {

// Within 5min from planned start so ok, type not required
await staffAttendancePage.setArrivalTime('08:00')
await staffAttendancePage.selectGroup(testDaycareGroup.id)
await staffAttendancePage.selectArrivalGroup(testDaycareGroup.id)
await staffAttendancePage.assertDoneButtonEnabled(true)

// More than 5min from planned start, type is not required but can be selected
await staffAttendancePage.setArrivalTime('08:15')
await staffAttendancePage.selectGroup(testDaycareGroup.id)
await staffAttendancePage.selectArrivalGroup(testDaycareGroup.id)
await staffAttendancePage.assertDoneButtonEnabled(true)
await staffAttendancePage.selectAttendanceType('JUSTIFIED_CHANGE')
await staffAttendancePage.assertDoneButtonEnabled(true)
Expand Down Expand Up @@ -665,7 +665,7 @@ describe('Realtime staff attendance page', () => {
await staffAttendancePage.clickStaffArrivedAndSetPin(pin)

await staffAttendancePage.setArrivalTime('15:00')
await staffAttendancePage.selectGroup(testDaycareGroup.id)
await staffAttendancePage.selectArrivalGroup(testDaycareGroup.id)
await staffAttendancePage.assertDoneButtonEnabled(true)
await staffAttendancePage.clickDoneButton()
await staffAttendancePage.assertAttendanceTimeTextShown(
Expand Down Expand Up @@ -693,7 +693,7 @@ describe('Realtime staff attendance page', () => {
await staffAttendancePage.clickStaffArrivedAndSetPin(pin)

await staffAttendancePage.setArrivalTime('12:04')
await staffAttendancePage.selectGroup(testDaycareGroup.id)
await staffAttendancePage.selectArrivalGroup(testDaycareGroup.id)
await staffAttendancePage.assertDoneButtonEnabled(true)
await staffAttendancePage.selectAttendanceType('TRAINING')
await staffAttendancePage.clickDoneButton()
Expand Down
Loading