Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,18 @@ describe('administrative consent filters', () => {
consentId: ' consent-1 ',
subjectId: ' admin ',
serviceId: ' dpdp-portal ',
purposeId: ' purpose-1 ',
propertyKey: ' dataCategory ',
propertyValue: ' personal ',
}),
).toEqual({
state: 'ACTIVE',
consentId: 'consent-1',
subjectId: 'admin',
serviceId: 'dpdp-portal',
purposeId: 'purpose-1',
propertyKey: 'dataCategory',
propertyValue: 'personal',
})
})

Expand All @@ -67,31 +73,61 @@ describe('administrative consent filters', () => {
expect(getAdminConsentFilters(new URLSearchParams('subjectId=admin')).subjectId).toBe('admin')
})

it('offers only the subject and service advanced filters', () => {
it('searches by User ID directly from the main row', () => {
const onFilterChange = vi.fn()
renderFilters(EMPTY_ADMIN_CONSENT_FILTERS, onFilterChange)

expect(screen.getByPlaceholderText('Search by consent ID')).toBeInTheDocument()

const subjectId = screen.getByRole('textbox', { name: 'User ID' })
expect(subjectId).toBeEnabled()

fireEvent.change(subjectId, { target: { value: ' admin ' } })
fireEvent.keyDown(subjectId, { key: 'Enter' })

expect(onFilterChange).toHaveBeenCalledWith({
state: 'All',
consentId: '',
subjectId: 'admin',
serviceId: '',
purposeId: '',
propertyKey: '',
propertyValue: '',
})
})

it('offers service, purpose and a consent-property filter in advanced filters', () => {
const onFilterChange = vi.fn()
renderFilters(EMPTY_ADMIN_CONSENT_FILTERS, onFilterChange)

fireEvent.click(screen.getByRole('button', { name: 'Advanced filters' }))

const subjectId = screen.getByRole('textbox', { name: 'User' })
const serviceId = screen.getByRole('textbox', { name: 'Service' })
expect(subjectId).toBeEnabled()
const purposeId = screen.getByRole('textbox', { name: 'Purpose' })
const propertyKey = screen.getByRole('textbox', { name: 'Key' })
const propertyValue = screen.getByRole('textbox', { name: 'Value' })
expect(serviceId).toBeEnabled()
expect(purposeId).toBeEnabled()
expect(propertyKey).toBeEnabled()
expect(propertyValue).toBeEnabled()
expect(screen.queryByRole('textbox', { name: 'User ID' })).not.toBeInTheDocument()
Comment thread
hasithakn marked this conversation as resolved.
Outdated
expect(screen.queryByRole('textbox', { name: /element/i })).not.toBeInTheDocument()
expect(screen.queryByRole('textbox', { name: /group/i })).not.toBeInTheDocument()
expect(screen.queryByRole('textbox', { name: /purpose/i })).not.toBeInTheDocument()

fireEvent.change(subjectId, { target: { value: ' admin ' } })
fireEvent.change(serviceId, { target: { value: 'dpdp-portal' } })
fireEvent.change(purposeId, { target: { value: ' purpose-1 ' } })
fireEvent.change(propertyKey, { target: { value: ' dataCategory ' } })
fireEvent.change(propertyValue, { target: { value: ' personal ' } })
fireEvent.click(screen.getByRole('button', { name: 'Apply' }))

expect(onFilterChange).toHaveBeenCalledWith({
state: 'All',
consentId: '',
subjectId: 'admin',
subjectId: '',
serviceId: 'dpdp-portal',
purposeId: 'purpose-1',
propertyKey: 'dataCategory',
propertyValue: 'personal',
})
})

Expand All @@ -113,15 +149,22 @@ describe('administrative consent filters', () => {
renderFilters({ ...EMPTY_ADMIN_CONSENT_FILTERS, consentId: 'consent-123' })

const advancedFiltersButton = screen.getByRole('button', { name: 'Advanced filters' })
const subjectId = screen.getByRole('textbox', { name: 'User ID' })
const stateSelect = screen.getByRole('combobox', { name: 'State' })
expect(advancedFiltersButton).toBeDisabled()
expect(subjectId).toBeDisabled()
expect(stateSelect).toHaveAttribute('aria-disabled', 'true')

fireEvent.mouseOver(advancedFiltersButton.parentElement as HTMLElement)
expect(
await screen.findByText('Remove the Consent ID filter to use advanced filters.'),
).toBeInTheDocument()

fireEvent.mouseOver(subjectId.closest('[aria-label]') as HTMLElement)
expect(
await screen.findByText('Remove the Consent ID filter to use the User ID filter.'),
).toBeInTheDocument()

fireEvent.mouseOver(stateSelect.closest('[aria-label]') as HTMLElement)
expect(
await screen.findByText('Remove the Consent ID filter to use the state filter.'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,42 @@ describe('AdminConsentRegistryPage', () => {
expect(Object.fromEntries(url.searchParams)).toEqual({ limit: '10' })
})

it('renders purpose names when the BFF has expanded list rows with detail', async () => {
vi.stubGlobal('fetch', fetchMock)
fetchMock.mockResolvedValue({
ok: true,
status: 200,
json: async () => ({
totalResults: 1,
links: [],
Consents: [
{
id: 'db0759de-c098-4f44-b78d-6718226db8b2',
subjectId: 'admin',
serviceId: 'dpdp-portal-spike',
state: 'ACTIVE',
timestamp: 1785833928316,
purposes: [
{
id: 'purpose-1',
name: 'marketing-spike',
type: 'CONSENT',
versionId: 'version-1',
version: '1.0.0',
elements: [],
},
],
},
],
}),
})

renderAdminPage()

expect(await screen.findByText('marketing-spike')).toBeInTheDocument()
expect(screen.getByRole('columnheader', { name: 'Purposes' })).toBeInTheDocument()
})

it('pages forward with the after cursor taken from links', async () => {
vi.stubGlobal('fetch', fetchMock)
fetchMock.mockResolvedValue({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import { afterEach, describe, expect, it, vi } from 'vitest'
import {
buildConsentPropertyFilter,
fetchAdminConsentByID,
fetchAdminConsents,
revokeAdminConsent,
Expand Down Expand Up @@ -77,6 +78,32 @@ describe('administrative consent API', () => {
})
})

it('passes purposeId and a custom property filter to the BFF', async () => {
mockJSONResponse({ totalResults: 0, links: [], Consents: [] })

await fetchAdminConsents({
limit: 10,
purposeId: 'purpose-1',
filter: 'properties.dataCategory eq "personal"',
})

const [requestUrl] = fetchMock.mock.calls[0] ?? []
expect(Object.fromEntries(new URL(String(requestUrl)).searchParams)).toEqual({
limit: '10',
purposeId: 'purpose-1',
filter: 'properties.dataCategory eq "personal"',
})
})

it('builds a properties.<key> eq "<value>" filter only when both key and value are set', () => {
expect(buildConsentPropertyFilter('dataCategory', 'personal')).toBe(
'properties.dataCategory eq "personal"',
)
expect(buildConsentPropertyFilter(' region ', ' EU ')).toBe('properties.region eq "EU"')
expect(buildConsentPropertyFilter('dataCategory', '')).toBeUndefined()
expect(buildConsentPropertyFilter('', 'personal')).toBeUndefined()
})

it('reads next and previous cursors out of the returned links', async () => {
const links = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ describe('AppSidebar', () => {
<CssBaseline />
<I18nextProvider i18n={i18n}>
<MemoryRouter initialEntries={['/consents']}>
<TestAuthorizationProvider scopes={Object.values(PORTAL_SCOPES)}>
<TestAuthorizationProvider
scopes={Object.values(PORTAL_SCOPES)}
hideSelfConsentsForAdmins={false}
>
<Routes>
<Route
path="*"
Expand All @@ -63,14 +66,14 @@ describe('AppSidebar', () => {

expect(screen.getByRole('complementary')).toBeInTheDocument()
expect(screen.getByRole('navigation')).toBeInTheDocument()
expect(screen.getByText('Consent')).toBeInTheDocument()
expect(screen.getByText('Consents')).toBeInTheDocument()
expect(screen.getByText('/consents')).toBeInTheDocument()
const navigationText = screen.getByRole('navigation').textContent ?? ''
expect(navigationText.indexOf('Administration')).toBeLessThan(
navigationText.indexOf('Definitions'),
)

fireEvent.click(screen.getByText('Pending Consents'))
fireEvent.click(screen.getByText('My Pending Consents'))

expect(screen.getByText('/consents?state=PENDING')).toBeInTheDocument()

Expand All @@ -93,7 +96,7 @@ describe('AppSidebar', () => {
)

expect(screen.getByText('Purposes')).toBeInTheDocument()
expect(screen.queryByText('Consent')).not.toBeInTheDocument()
expect(screen.queryByText('Consents')).not.toBeInTheDocument()
expect(screen.queryByText('Dashboard')).not.toBeInTheDocument()
expect(screen.queryByText('Elements')).not.toBeInTheDocument()
expect(screen.queryByText('Administration')).not.toBeInTheDocument()
Expand Down Expand Up @@ -123,10 +126,52 @@ describe('AppSidebar', () => {
)

expect(screen.getByText('Administration')).toBeInTheDocument()
expect(screen.getByText('Consents')).toBeInTheDocument()
expect(screen.queryByText('Consent')).not.toBeInTheDocument()
expect(screen.queryByText('All Consents')).not.toBeInTheDocument()
expect(screen.getByText('All Consents')).toBeInTheDocument()
expect(screen.queryByText('Consents')).not.toBeInTheDocument()
expect(screen.queryByText('My Consents')).not.toBeInTheDocument()
expect(screen.queryByText('Dashboard')).not.toBeInTheDocument()
expect(screen.getByText('/administration/consents')).toBeInTheDocument()
})

it('hides self-service consents for admins when hideSelfConsentsForAdmins is true', () => {
render(
<OxygenUIThemeProvider theme={AcrylicOrangeTheme}>
<I18nextProvider i18n={i18n}>
<MemoryRouter initialEntries={['/administration/consents']}>
<TestAuthorizationProvider
scopes={[PORTAL_SCOPES.CONSENTS_READ_SELF, PORTAL_SCOPES.CONSENTS_READ_ANY]}
hideSelfConsentsForAdmins
>
<AppSidebar collapsed={false} />
</TestAuthorizationProvider>
</MemoryRouter>
</I18nextProvider>
</OxygenUIThemeProvider>,
)

expect(screen.getByText('All Consents')).toBeInTheDocument()
expect(screen.queryByText('My Consents')).not.toBeInTheDocument()
expect(screen.queryByText('My Pending Consents')).not.toBeInTheDocument()
expect(screen.queryByText('Consents')).not.toBeInTheDocument()
})

it('still shows self-service consents for a non-admin even when hideSelfConsentsForAdmins is true', () => {
render(
<OxygenUIThemeProvider theme={AcrylicOrangeTheme}>
<I18nextProvider i18n={i18n}>
<MemoryRouter initialEntries={['/consents']}>
<TestAuthorizationProvider
scopes={[PORTAL_SCOPES.CONSENTS_READ_SELF]}
hideSelfConsentsForAdmins
>
<AppSidebar collapsed={false} />
</TestAuthorizationProvider>
</MemoryRouter>
</I18nextProvider>
</OxygenUIThemeProvider>,
)

expect(screen.getByText('My Consents')).toBeInTheDocument()
expect(screen.getByText('My Pending Consents')).toBeInTheDocument()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,26 @@ describe('ConsentDetailsPage content', () => {
expect(screen.getByText('dpdp-portal')).toBeInTheDocument()
})

it('renders consent properties in a key/value table', async () => {
renderConsentDetailsPage('ACTIVE', Object.values(PORTAL_SCOPES), {
properties: { dataCategory: 'financial', region: 'EU' },
})

expect(await screen.findByRole('heading', { name: 'Properties' })).toBeInTheDocument()
expect(screen.getByText('dataCategory')).toBeInTheDocument()
expect(screen.getByText('financial')).toBeInTheDocument()
expect(screen.getByText('region')).toBeInTheDocument()
expect(screen.getByText('EU')).toBeInTheDocument()
})

it('shows an empty state when a consent has no properties', async () => {
renderConsentDetailsPage('ACTIVE')

expect(
await screen.findByText('No properties are associated with this consent.'),
).toBeInTheDocument()
})

it('lists authorizations by username with their state', async () => {
renderConsentDetailsPage('ACTIVE')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* under the License.
*/

import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { AcrylicOrangeTheme, CssBaseline, OxygenUIThemeProvider } from '@wso2/oxygen-ui'
import { I18nextProvider } from 'react-i18next'
Expand Down Expand Up @@ -115,7 +115,7 @@ describe('ConsentRegistryPage', () => {

renderConsentRegistryPage(createQueryClient())

expect(await screen.findByRole('heading', { name: 'All Consents' })).toBeInTheDocument()
expect(await screen.findByRole('heading', { name: 'My Consents' })).toBeInTheDocument()
expect(screen.getByLabelText('Consent filters')).toBeInTheDocument()
expect(screen.getByPlaceholderText('Search by service')).toBeInTheDocument()
expect(screen.getByRole('combobox', { name: 'State' })).toBeInTheDocument()
Expand Down Expand Up @@ -227,6 +227,19 @@ describe('ConsentRegistryPage', () => {
})
})

it('shows the pending title and breadcrumb when filtered to pending consents', async () => {
mockConsentSearch([])

renderConsentRegistryPage(createQueryClient(), '/consents?state=PENDING')

expect(await screen.findByRole('heading', { name: 'My Pending Consents' })).toBeInTheDocument()
const breadcrumbs = screen.getByRole('navigation', { name: 'Breadcrumb' })
expect(within(breadcrumbs).getByText('My Pending Consents')).toHaveAttribute(
'aria-current',
'page',
)
})

it('ignores the removed CREATED status in the URL', async () => {
mockConsentSearch([])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ describe('current-user API', () => {
userId: 'user-1',
organizationId: 'org-1',
scopes: [PORTAL_SCOPES.CONSENTS_READ_SELF],
hideSelfConsentsForAdmins: true,
})

await expect(fetchCurrentUser()).resolves.toEqual({
userId: 'user-1',
organizationId: 'org-1',
scopes: [PORTAL_SCOPES.CONSENTS_READ_SELF],
hideSelfConsentsForAdmins: true,
})
expect(apiClientMocks.apiRequest).toHaveBeenCalledWith('/me', { method: 'GET' })
})
Expand All @@ -51,6 +53,17 @@ describe('current-user API', () => {
userId: 'user-1',
organizationId: '',
scopes: ['portal:unknown'],
hideSelfConsentsForAdmins: true,
})

await expect(fetchCurrentUser()).rejects.toThrow('invalid current-user response')
})

it('rejects a response missing the hideSelfConsentsForAdmins flag', async () => {
apiClientMocks.apiRequest.mockResolvedValue({
userId: 'user-1',
organizationId: 'org-1',
scopes: [PORTAL_SCOPES.CONSENTS_READ_SELF],
})

await expect(fetchCurrentUser()).rejects.toThrow('invalid current-user response')
Expand Down
Loading