Skip to content

Commit

Permalink
Add tests for weak login credentials handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Gekkio committed Jan 15, 2025
1 parent 57eae73 commit 359ff63
Show file tree
Hide file tree
Showing 13 changed files with 432 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ export default React.memo(function LoginDetailsSection({
<>
<Label>{t.weakLoginCredentials}</Label>
<div>
{user.weakLoginUsername ? t.status.enabled : t.status.disabled}
{user.weakLoginUsername ? (
<span data-qa="weak-login-enabled">{t.status.enabled}</span>
) : (
<span data-qa="weak-login-disabled">{t.status.disabled}</span>
)}
<InlineInfoButton
onClick={toggleInfo}
aria-label={i18n.common.openExpandingInfo}
Expand All @@ -103,7 +107,7 @@ export default React.memo(function LoginDetailsSection({
<>
<Label>{t.weakLoginUsername}</Label>
<div>
{user.weakLoginUsername}
<span data-qa="username">{user.weakLoginUsername}</span>
<Gap horizontal size="xs" />
<InfoButton
onClick={toggleUsernameInfo}
Expand All @@ -122,6 +126,7 @@ export default React.memo(function LoginDetailsSection({
<div>
<div>********</div>
<Button
data-qa="update-password"
appearance="inline"
text={t.updatePassword}
icon={canEdit ? undefined : faLockAlt}
Expand All @@ -135,6 +140,7 @@ export default React.memo(function LoginDetailsSection({
<AlertBox message={t.unverifiedEmailWarning} />
)}
<Button
data-qa="activate-credentials"
disabled={!isEmailVerified}
text={t.activateCredentials}
icon={canEdit ? undefined : faLockAlt}
Expand All @@ -149,7 +155,7 @@ export default React.memo(function LoginDetailsSection({
{!!emailVerificationStatus.verifiedEmail && (
<>
{modalOpen && (
<WeakLoginFormModal
<WeakCredentialsFormModal
hasCredentials={!!user.weakLoginUsername}
username={
user.weakLoginUsername ??
Expand All @@ -167,6 +173,7 @@ export default React.memo(function LoginDetailsSection({
)}
{activationSuccessModalOpen && (
<BaseModal
data-qa="weak-credentials-modal"
type="success"
title={t.activationSuccess}
icon={faCheck}
Expand All @@ -175,6 +182,7 @@ export default React.memo(function LoginDetailsSection({
>
<ModalButtons $justifyContent="center">
<Button
data-qa="modal-okBtn"
primary
text={t.activationSuccessOk}
onClick={closeActivationSuccessModal}
Expand Down Expand Up @@ -215,7 +223,7 @@ const UsernameField = styled.input`
border: none;
`

const WeakLoginFormModal = React.memo(function WeakLoginFormModal({
const WeakCredentialsFormModal = React.memo(function WeakCredentialsFormModal({
hasCredentials,
username,
onSuccess,
Expand All @@ -238,6 +246,7 @@ const WeakLoginFormModal = React.memo(function WeakLoginFormModal({
const pattern = `.{${minLength},${maxLength}}`
return (
<MutateFormModal
data-qa="weak-credentials-modal"
title={t.weakLoginCredentials}
resolveLabel={
hasCredentials ? t.updatePassword : t.confirmActivateCredentials
Expand All @@ -258,6 +267,7 @@ const WeakLoginFormModal = React.memo(function WeakLoginFormModal({
<FixedSpaceColumn spacing="xs">
<Label htmlFor="username">{t.weakLoginUsername}</Label>
<UsernameField
data-qa="username"
id="username"
name="username"
type="email"
Expand All @@ -267,6 +277,7 @@ const WeakLoginFormModal = React.memo(function WeakLoginFormModal({
/>
<Label htmlFor="password">{t.password}</Label>
<InputFieldF
data-qa="password"
id="password"
name="password"
autoComplete="new-password"
Expand All @@ -279,6 +290,7 @@ const WeakLoginFormModal = React.memo(function WeakLoginFormModal({
/>
<Label htmlFor="confirm-password">{t.confirmPassword}</Label>
<InputFieldF
data-qa="confirm-password"
id="confirm-password"
name="confirm-password"
autoComplete="new-password"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ export default React.memo(function PersonalDetailsSection({
/>
{canEdit ? (
<MutateButton
data-qa="update-username"
text={t.personalDetails.detailsSection.updateUsername(
problem.email
)}
Expand All @@ -410,6 +411,7 @@ export default React.memo(function PersonalDetailsSection({
/>
) : (
<Button
data-qa="update-username"
text={t.personalDetails.detailsSection.updateUsername(
problem.email
)}
Expand Down
16 changes: 14 additions & 2 deletions frontend/src/e2e-test/dev-api/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ import {
postReservations,
postReservationsRaw,
upsertStaffOccupancyCoefficient,
upsertVtjDataset
upsertVtjDataset,
upsertWeakCredentials
} from '../generated/api-clients'
import {
Caretaker,
Expand Down Expand Up @@ -1285,11 +1286,22 @@ export class ClubTermBuilder extends FixtureBuilder<DevClubTerm> {
}

export class PersonBuilder extends FixtureBuilder<DevPerson> {
async saveAdult(opts: { updateMockVtjWithDependants?: DevPerson[] } = {}) {
async saveAdult(
opts: {
updateMockVtjWithDependants?: DevPerson[]
updateWeakCredentials?: { username: string; password: string }
} = {}
) {
await createPerson({ body: this.data, type: 'ADULT' })
if (opts.updateMockVtjWithDependants !== undefined) {
await this.updateMockVtj(opts.updateMockVtjWithDependants)
}
if (opts.updateWeakCredentials) {
await upsertWeakCredentials({
id: this.data.id,
body: opts.updateWeakCredentials
})
}
return this.data
}

Expand Down
25 changes: 25 additions & 0 deletions frontend/src/e2e-test/generated/api-clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ import { SpecialDiet } from 'lib-common/generated/api-types/specialdiet'
import { StaffAttendancePlanId } from './api-types'
import { StaffAttendanceRealtimeId } from 'lib-common/generated/api-types/shared'
import { StaffMemberAttendance } from 'lib-common/generated/api-types/attendance'
import { UpdateWeakLoginCredentialsRequest } from 'lib-common/generated/api-types/pis'
import { VoucherValueDecision } from './api-types'
import { VtjPersonSummary } from './api-types'
import { createUrlSearchParams } from 'lib-common/api'
Expand Down Expand Up @@ -2521,3 +2522,27 @@ export async function upsertVtjDataset(
throw new DevApiError(e)
}
}


/**
* Generated from fi.espoo.evaka.shared.dev.DevApi.upsertWeakCredentials
*/
export async function upsertWeakCredentials(
request: {
id: PersonId,
body: UpdateWeakLoginCredentialsRequest
},
options?: { mockedTime?: HelsinkiDateTime }
): Promise<void> {
try {
const { data: json } = await devClient.request<JsonOf<void>>({
url: uri`/citizen/${request.id}/weak-credentials`.toString(),
method: 'POST',
headers: { EvakaMockedTime: options?.mockedTime?.formatIso() },
data: request.body satisfies JsonCompatible<UpdateWeakLoginCredentialsRequest>
})
return json
} catch (e) {
throw new DevApiError(e)
}
}
34 changes: 33 additions & 1 deletion frontend/src/e2e-test/pages/citizen/citizen-personal-details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class CitizenPersonalDetailsSection extends Element {
sendVerificationCode: Element
verificationCodeField: TextInput
verifyEmail: Element
updateUsername: Element

constructor(element: Element) {
super(element)
Expand All @@ -57,6 +58,7 @@ export class CitizenPersonalDetailsSection extends Element {
element.findByDataQa('verification-code-field')
)
this.verifyEmail = element.findByDataQa('verify-email')
this.updateUsername = element.findByDataQa('update-username')
}

async checkMissingEmailWarningIsShown() {
Expand Down Expand Up @@ -133,7 +135,37 @@ export class CitizenPersonalDetailsSection extends Element {
}

export class LoginDetailsSection extends Element {
keycloakEmail = this.findByDataQa('keycloak-email')
keycloakEmail: Element
username: Element
activateCredentials: Element
weakLoginEnabled: Element
weakLoginDisabled: Element
updatePassword: Element

constructor(element: Element) {
super(element)
this.keycloakEmail = element.findByDataQa('keycloak-email')
this.username = element.findByDataQa('username')
this.activateCredentials = element.findByDataQa('activate-credentials')
this.weakLoginEnabled = element.findByDataQa('weak-login-enabled')
this.weakLoginDisabled = element.findByDataQa('weak-login-disabled')
this.updatePassword = element.findByDataQa('update-password')
}
}

export class WeakCredentialsModal extends Element {
username: Element
password: TextInput
confirmPassword: TextInput
ok: Element

constructor(page: Page) {
super(page.findByDataQa('weak-credentials-modal'))
this.username = this.findByDataQa('username')
this.password = new TextInput(this.findByDataQa('password'))
this.confirmPassword = new TextInput(this.findByDataQa('confirm-password'))
this.ok = this.findByDataQa('modal-okBtn')
}
}

export class CitizenNotificationSettingsSection extends Element {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
import HelsinkiDateTime from 'lib-common/helsinki-date-time'

import { Fixture } from '../../dev-api/fixtures'
import {
getSentEmails,
resetServiceState,
runJobs
} from '../../generated/api-clients'
import { resetServiceState, runJobs } from '../../generated/api-clients'
import { DevPerson } from '../../generated/api-types'
import CitizenHeader from '../../pages/citizen/citizen-header'
import CitizenPersonalDetailsPage from '../../pages/citizen/citizen-personal-details'
import { getVerificationCodeFromEmail } from '../../utils/email'
import { Page } from '../../utils/page'
import { enduserLogin } from '../../utils/user'

Expand Down Expand Up @@ -79,14 +76,3 @@ async function openPersonalDetailsPage(citizen: DevPerson) {
await header.selectTab('personal-details')
return new CitizenPersonalDetailsPage(page)
}

async function getVerificationCodeFromEmail(): Promise<string | null> {
const emails = await getSentEmails()
if (emails.length === 0) return null
expect(emails.length).toBe(1)
const email = emails[0]

const verificationCodeRegex = /[0-9]{8}/
const matches = verificationCodeRegex.exec(email.content.text)
return matches ? matches[0] : null
}
Loading

0 comments on commit 359ff63

Please sign in to comment.