Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
a0d1cc8
Trigger build
kris-szlapa Jan 29, 2025
5d90880
Merge branch 'main' into AEA-4755-trackeruserinfo-selectedrole-fix
seansteberisal Jan 29, 2025
dc07c51
Amend the confirmButtonText
kris-szlapa Jan 29, 2025
a949f51
Add the role to the rolesWithAccess array if it is not the currently …
kris-szlapa Jan 29, 2025
65102d7
Merge branch 'AEA-4755-trackeruserinfo-selectedrole-fix' of https://g…
kris-szlapa Jan 29, 2025
6975976
Add the role to rolesWithAccess array only if it is not the selectedR…
kris-szlapa Jan 29, 2025
c89fd0b
Remove the selected role from rolesWithAccess in the DynamoDB table
kris-szlapa Jan 29, 2025
5e78482
Fix the test that fetches and processes user info
kris-szlapa Jan 29, 2025
0454892
Ensure roles_with_access is defined before calling filter
kris-szlapa Jan 30, 2025
07d4a94
Add a function to fetch user roles with access from DynamoDB table
kris-szlapa Jan 30, 2025
e92177f
Restore selectedRole lambda
kris-szlapa Jan 30, 2025
03ea23d
Amend a comment
kris-szlapa Jan 30, 2025
40acd43
Add the fetchDynamoRolesWithAccess function and amend tests
kris-szlapa Jan 30, 2025
8baf970
Restructure unit tests
kris-szlapa Jan 30, 2025
c5ea6af
Improve the lambda description
kris-szlapa Jan 30, 2025
2145535
Remove unnecessary tests files
kris-szlapa Jan 30, 2025
29e4b99
Add unit tests for the selectedRole lambda handler
kris-szlapa Jan 30, 2025
e50e40b
Refactor mockGetUsernameFromEvent in the handler tests
kris-szlapa Jan 30, 2025
c58b286
Remove the empty line
kris-szlapa Jan 30, 2025
07b15df
Change snake_case to camelCase for DynamoDB variables and add log mes…
kris-szlapa Jan 30, 2025
6bf1cd8
Rename the selected role lambda
kris-szlapa Jan 30, 2025
157d76d
Amend the userSelectedRoleId extraction and add log messages
kris-szlapa Jan 30, 2025
020ba38
Update packages
kris-szlapa Jan 30, 2025
4afb632
Fix typo in a comment
kris-szlapa Jan 30, 2025
70bd67d
Add rolesWithAccess to the DynamoDB UpdateCommand
kris-szlapa Jan 31, 2025
9521470
Remove unnecessary log messages
kris-szlapa Jan 31, 2025
a6b8a85
Ensure last name is uppercase and first name is capitalized in the RB…
kris-szlapa Jan 31, 2025
81041a8
Add unit tests for the fetchDynamoRolesWithAccess function
kris-szlapa Jan 31, 2025
9f59cd0
Refactor tests for handler selectedRole lambda
kris-szlapa Jan 31, 2025
cd20905
Add tests for the updateDynamoTable function
kris-szlapa Jan 31, 2025
f15ebdb
Ammend the test commet
kris-szlapa Jan 31, 2025
02dc3a5
Update the REGRESSION_TESTS_REPO_TAG
kris-szlapa Feb 1, 2025
a514aa0
Add the userDetails to the DynamoDB table
kris-szlapa Feb 1, 2025
28e5d81
Ammend the UserInfo fetch log messages
kris-szlapa Feb 1, 2025
bc5a7d1
Ammend the test for UserInfo fetch log message
kris-szlapa Feb 1, 2025
2eeb411
Ammend tests for DynamoDB fetch log message
kris-szlapa Feb 1, 2025
96a61ce
Remove unnecessary comments
kris-szlapa Feb 1, 2025
7162ea3
Add the logic to swap currentlySelectedRole with the new selected role
kris-szlapa Feb 1, 2025
3bac2f5
Add uuid only if the selected role is not an empty object
kris-szlapa Feb 1, 2025
e75df5b
Make sure the currentlySelectedRole is added back before removing the…
kris-szlapa Feb 1, 2025
964852b
Add log messages for testing
kris-szlapa Feb 1, 2025
ad4876e
Fetch rolesWithAccess and currentlySelectedRole from DynamoDB with fe…
kris-szlapa Feb 2, 2025
9c06a2f
Move the previously selected role back into rolesWithAccess only if i…
kris-szlapa Feb 2, 2025
d25ff5d
Add tests for selectyourrole page
kris-szlapa Feb 2, 2025
1bec3b2
Change the confirmButton link to searchforaprescription
kris-szlapa Feb 2, 2025
bb05351
Clear from localStorage to ensure RBAC Banner is removed and update t…
kris-szlapa Feb 2, 2025
cda6c5b
Remove setBannerText to empty string
kris-szlapa Feb 2, 2025
0e481c8
Change the REGRESSION_TESTS_REPO_TAG to the branch name
kris-szlapa Feb 2, 2025
d3ed63b
Remove the firstName formatting in the RBAC banner
kris-szlapa Feb 3, 2025
bd67135
Change the REGRESSION_TESTS_REPO_TAG to v2.11.4
kris-szlapa Feb 3, 2025
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
3 changes: 1 addition & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 56 additions & 2 deletions packages/cpt-ui/__tests__/ChangeRolePage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ jest.mock("@/constants/ui-strings/CardStrings", () => {
"You are currently logged in at GREENE'S PHARMACY (ODS: FG419) with Health Professional Access Role.",
},
confirmButton: {
text: "Confirm and continue to find a prescription",
link: "tracker-presc-no",
text: "Continue to find a prescription",
link: "searchforaprescription",
},
alternativeMessage: "Alternatively, you can choose a new role below.",
organisation: "Organisation",
Expand Down Expand Up @@ -280,4 +280,58 @@ describe("ChangeRolePage", () => {
expect(mockPush).toHaveBeenCalledWith("/searchforaprescription")
})
})

it("renders loading state when waiting for API response", async () => {
mockFetch.mockImplementation(() => new Promise(() => {}))
renderWithAuth()
expect(screen.getByText("Loading...")).toBeInTheDocument()
})

it("redirects when a single role is available", async () => {
(useRouter as jest.Mock).mockReturnValue({
push: jest.fn()
})

const mockUserInfo = {
roles_with_access: [{
role_name: "Pharmacist",
org_name: "Test Pharmacy",
org_code: "ORG123",
site_address: "123 Test St"
}],
roles_without_access: []
}

mockFetch.mockResolvedValue({
status: 200,
json: async () => ({userInfo: mockUserInfo})
})

renderWithAuth({isSignedIn: true, idToken: {toString: jest.fn().mockReturnValue("mock-id-token")}})

await waitFor(() => {
expect(useRouter().push).toHaveBeenCalledWith("/searchforaprescription")
})
})

it("does not fetch user roles if user is not signed in", async () => {
const mockFetch = jest.fn()
global.fetch = mockFetch

renderWithAuth({isSignedIn: false}) // Simulating a user who is not signed in

expect(mockFetch).not.toHaveBeenCalled()
})

it("displays an error when the API request fails", async () => {
mockFetch.mockRejectedValue(new Error("Failed to fetch user roles"))

renderWithAuth({isSignedIn: true, idToken: {toString: jest.fn().mockReturnValue("mock-id-token")}})

await waitFor(() => {
const errorSummary = screen.getByRole("heading", {name: "Error during role selection"})
expect(errorSummary).toBeInTheDocument()
expect(screen.getByText("Failed to fetch CPT user info")).toBeInTheDocument()
})
})
})
46 changes: 32 additions & 14 deletions packages/cpt-ui/__tests__/RBACBanner.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "@testing-library/jest-dom"
import { render, screen } from "@testing-library/react"
import { useRouter } from "next/navigation"
import {render, screen} from "@testing-library/react"
import {useRouter} from "next/navigation"
import React from "react"
import { JWT } from "aws-amplify/auth"
import {JWT} from "aws-amplify/auth"

import RBACBanner from "@/components/RBACBanner"

Expand All @@ -19,10 +19,10 @@ jest.mock("@/constants/ui-strings/RBACBannerStrings", () => {
LOCUM_NAME: "Locum pharmacy"
}

return { RBAC_BANNER_STRINGS }
return {RBAC_BANNER_STRINGS}
})

const { RBAC_BANNER_STRINGS } = require("@/constants/ui-strings/RBACBannerStrings")
const {RBAC_BANNER_STRINGS} = require("@/constants/ui-strings/RBACBannerStrings")

// Mock `next/navigation`
jest.mock("next/navigation", () => ({
Expand All @@ -46,8 +46,8 @@ jest.mock("@/context/AccessProvider", () => {
org_name: "org name"
},
userDetails: {
given_name: "Jane",
family_name: "Doe"
given_name: "JaNe",
family_name: "DoE"
},
setUserDetails: jest.fn(),
setSelectedRole: jest.fn(),
Expand All @@ -57,7 +57,7 @@ jest.mock("@/context/AccessProvider", () => {
const useAccess = () => React.useContext(MockAccessContext)

const __setMockContextValue = (newValue: any) => {
mockContextValue = { ...mockContextValue, ...newValue }
mockContextValue = {...mockContextValue, ...newValue}
// Reassign the context’s defaultValue so subsequent consumers get new values
MockAccessContext._currentValue = mockContextValue
MockAccessContext._currentValue2 = mockContextValue
Expand All @@ -70,7 +70,7 @@ jest.mock("@/context/AccessProvider", () => {
__setMockContextValue
}
})
const { __setMockContextValue } = require("@/context/AccessProvider")
const {__setMockContextValue} = require("@/context/AccessProvider")

// Mock an AuthContext
const AuthContext = React.createContext<any>(null)
Expand All @@ -90,7 +90,7 @@ const defaultAuthContext = {
}

export const renderWithAuth = (authOverrides = {}) => {
const authValue = { ...defaultAuthContext, ...authOverrides }
const authValue = {...defaultAuthContext, ...authOverrides}

return render(
<AuthContext.Provider value={authValue}>
Expand All @@ -112,7 +112,7 @@ describe("RBACBanner", () => {
org_name: "org name"
},
userDetails: {
family_name: "Doe",
family_name: "DoE",
given_name: "Jane"
}
})
Expand Down Expand Up @@ -145,7 +145,7 @@ describe("RBACBanner", () => {
expect(bannerText).toBeInTheDocument()

// Check that placeholders are properly replaced
const expectedText = `CONFIDENTIAL: PERSONAL PATIENT DATA accessed by Doe, Jane - Role Name - org name (ODS: deadbeef)`
const expectedText = `CONFIDENTIAL: PERSONAL PATIENT DATA accessed by DOE, Jane - Role Name - org name (ODS: deadbeef)`
expect(bannerText).toHaveTextContent(expectedText)
})

Expand All @@ -165,7 +165,7 @@ describe("RBACBanner", () => {
const bannerText = screen.getByTestId("rbac-banner-text")
// Locum pharmacy name should appear
expect(bannerText).toHaveTextContent(
`CONFIDENTIAL: PERSONAL PATIENT DATA accessed by Doe, Jane - Role Name - Locum pharmacy (ODS: FFFFF)`
`CONFIDENTIAL: PERSONAL PATIENT DATA accessed by DOE, Jane - Role Name - Locum pharmacy (ODS: FFFFF)`
)
})

Expand Down Expand Up @@ -198,7 +198,25 @@ describe("RBACBanner", () => {
const bannerText = screen.getByTestId("rbac-banner-text")
// Notice fallback values: NO_ROLE_NAME, NO_ORG_NAME, NO_ODS_CODE
expect(bannerText).toHaveTextContent(
`CONFIDENTIAL: PERSONAL PATIENT DATA accessed by Doe, Jane - NO_ROLE_NAME - NO_ORG_NAME (ODS: NO_ODS_CODE)`
`CONFIDENTIAL: PERSONAL PATIENT DATA accessed by DOE, Jane - NO_ROLE_NAME - NO_ORG_NAME (ODS: NO_ODS_CODE)`
)
})

it("should fallback to NO_ORG_NAME if org_name is missing", () => {
__setMockContextValue({
selectedRole: {
role_name: "Role Name",
role_id: "role-id",
org_code: "deadbeef"
// org_name is missing
}
})

renderWithAuth()

const bannerText = screen.getByTestId("rbac-banner-text")
expect(bannerText).toHaveTextContent(
`CONFIDENTIAL: PERSONAL PATIENT DATA accessed by DOE, Jane - Role Name - NO_ORG_NAME (ODS: deadbeef)`
)
})

Expand Down
25 changes: 23 additions & 2 deletions packages/cpt-ui/__tests__/SelectYourRolePage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ jest.mock("@/constants/ui-strings/CardStrings", () => {
"You are currently logged in at GREENE'S PHARMACY (ODS: FG419) with Health Professional Access Role.",
},
confirmButton: {
text: "Confirm and continue to find a prescription",
link: "tracker-presc-no",
text: "Continue to find a prescription",
link: "searchforaprescription",
},
alternativeMessage: "Alternatively, you can choose a new role below.",
organisation: "Organisation",
Expand Down Expand Up @@ -321,4 +321,25 @@ describe("SelectYourRolePage", () => {
expect(useRouter().push).toHaveBeenCalledWith("/searchforaprescription")
})
})

it("does not fetch user roles if user is not signed in", async () => {
const mockFetch = jest.fn()
global.fetch = mockFetch

renderWithAuth({isSignedIn: false}) // Simulating a user who is not signed in

expect(mockFetch).not.toHaveBeenCalled()
})

it("displays an error when the API request fails", async () => {
mockFetch.mockRejectedValue(new Error("Failed to fetch user roles"))

renderWithAuth({isSignedIn: true, idToken: {toString: jest.fn().mockReturnValue("mock-id-token")}})

await waitFor(() => {
const errorSummary = screen.getByRole("heading", {name: "Error during role selection"})
expect(errorSummary).toBeInTheDocument()
expect(screen.getByText("Failed to fetch CPT user info")).toBeInTheDocument()
})
})
})
2 changes: 1 addition & 1 deletion packages/cpt-ui/__tests__/YourSelectedRole.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jest.mock("@/constants/ui-strings/YourSelectedRoleStrings", () => {
roleLabel: "Role",
orgLabel: "Organisation",
changeLinkText: "Change",
confirmButtonText: "Confirm and continue to find a prescription",
confirmButtonText: "Continue to find a prescription",
noODSCode: "NO ODS CODE",
noRoleName: "NO ROLE NAME",
noOrgName: "NO ORG NAME"
Expand Down
6 changes: 3 additions & 3 deletions packages/cpt-ui/app/confirmrole/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'
import React from "react";
import React from "react"

import { Button, Card, Container, Col, InsetText, Row } from "nhsuk-react-components";
import {Button, Card, Container, Col, InsetText, Row} from "nhsuk-react-components"
export default function Page() {
return (
<main className="nhsuk-main-wrapper">
Expand All @@ -26,7 +26,7 @@ export default function Page() {
</p>
</InsetText>
<Button>
Confirm and continue to find a prescription
Continue to find a prescription
</Button>
<p>Alternatively, you can choose a new role below.</p>

Expand Down
53 changes: 26 additions & 27 deletions packages/cpt-ui/app/logout/page.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,53 @@
'use client'
import React, { useContext, useEffect } from "react";
import { Container } from "nhsuk-react-components"
import Link from "next/link";
import React, {useContext, useEffect} from "react"
import {Container} from "nhsuk-react-components"
import Link from "next/link"

import { AuthContext } from "@/context/AuthProvider";
import { useAccess } from "@/context/AccessProvider"
import EpsSpinner from "@/components/EpsSpinner";
import { EpsLogoutStrings } from "@/constants/ui-strings/EpsLogoutPageStrings";
import {AuthContext} from "@/context/AuthProvider"
import {useAccess} from "@/context/AccessProvider"
import EpsSpinner from "@/components/EpsSpinner"
import {EpsLogoutStrings} from "@/constants/ui-strings/EpsLogoutPageStrings"

export default function LogoutPage() {

const auth = useContext(AuthContext);
const { clear } = useAccess();
const auth = useContext(AuthContext)
const {clear} = useAccess()

// Log out on page load
useEffect(() => {
const signOut = async () => {
console.log("Signing out", auth);
console.log("Signing out", auth)

await auth?.cognitoSignOut()

await auth?.cognitoSignOut();
console.log("Signed out: ", auth);
// Ensure user details & roles are cleared from local storage
clear()
console.log("Signed out and cleared session data")
}

if (auth?.isSignedIn) {
signOut();
clear();
signOut()
} else {
console.log("Cannot sign out - not signed in");
console.log("Cannot sign out - not signed in")
clear() // Clear data even if not signed in
}
}, [auth, clear]);
}, [auth, clear])

return (
<main id="main-content" className="nhsuk-main-wrapper">
<Container>
{auth?.isSignedIn ? (
{!auth?.isSignedIn ? (
<>
<h1>{EpsLogoutStrings.loading}</h1>
<EpsSpinner />
<h1>{EpsLogoutStrings.title}</h1>
<p>{EpsLogoutStrings.body}</p>
<Link href="/login">{EpsLogoutStrings.login_link}</Link>
</>
) : (
<>
<h1>{EpsLogoutStrings.title}</h1>
<div>{EpsLogoutStrings.body}</div>
<p />
<Link href="/login">
{EpsLogoutStrings.login_link}
</Link>
<h1>{EpsLogoutStrings.loading}</h1>
<EpsSpinner />
</>
)}
</Container>
</main>
);
)
}
21 changes: 14 additions & 7 deletions packages/cpt-ui/components/EpsRoleSelectionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,20 @@ export default function RoleSelectionPage({contentText}: RoleSelectionPageProps)
const rolesWithAccess = userInfo.roles_with_access || []
const rolesWithoutAccess = userInfo.roles_without_access || []

const selectedRole = userInfo?.currently_selected_role
? {
...userInfo?.currently_selected_role,
uuid: `selected_role_0`
}
: undefined
// Check if the user info object and currently_selected_role exist
const selectedRole =
userInfo?.currently_selected_role && Object.keys(userInfo.currently_selected_role).length > 0
? {
// If currently_selected_role is not empty, spread its properties
...userInfo.currently_selected_role,
// Add uuid only if the selected role is not an empty object
uuid: `selected_role_0`
}
// If currently_selected_role is an empty object `{}`, set selectedRole to undefined
: undefined

console.log("Selected role:", selectedRole)
setSelectedRole(selectedRole)

// Populate the EPS card props
setRolesWithAccess(
Expand All @@ -158,7 +166,6 @@ export default function RoleSelectionPage({contentText}: RoleSelectionPageProps)
}))
)

setSelectedRole(selectedRole)
setNoAccess(rolesWithAccess.length === 0)
setSingleAccess(rolesWithAccess.length === 1)

Expand Down
Loading