Skip to content

Commit 0836f94

Browse files
pietro-totaCianoDanilopagopa-github-bot
authored
feat: add settings menu (#849)
* feat: add settings menu * wip: setting page * wip: settings page * chore: revert perm * fix: text * Add alert title and description * wip * chore: restore condition * wip: setting menu * chore: add ff * feat: add ff * chore: use link component + eslint * chore: refactoring * chore: remove log * feat: popup messages (#850) * empty * feat: add popup + use trans for links * feat: popup menus * chore: dummy jwt token * feat: add info alert * chore: add translations * feat: add loader * chore: add TODO message * feat: add state changer + mock update action * feat: status chip calculation (#851) * feat: status chip calculation * chore: code refactoring * chore: code review * chore: service enabling data * chore: remove edit contacts button * chore: activation date calculation * fix: env variable name * chore: code optimization reduce to one modal instead of duplicating the same one make use of react component instead of function invocation * [CHK-4708] feat: service settings add PUT consent api invocation (#852) * feat(consent-api): add save consent API invocation on button click * revert local env file * feat(consent-api): fix ConsentEnum and Date usage * feat(consent-api): lint * feat(consent-api): update error title and description * chore: merge modification + code refactoring * [CHK-4708] feat: service settings add get api invocation (#853) * feat(consent-api): add GET service consents API invocation * feat(consent-api): update error title and description * Chk 4708 unit tests (#855) * feat(wip): test * chore: restore components * chore: add missing import + early return * chore: add error message * feat: wip tests * feat: test + page refactoring to help in test * fix: tests * chore: remove comment * chore: lint * fix: key stiles * trigger CR pipeline * chore: update can i use browser list * chore: add test for errors * [CHK-4708] feat(integration-test): add service settings page integration tests (#857) * feat(integration-test): add service settings page integration tests * feat(integration-test): remove only * feat(integration-test): add service settings page integration tests * feat(integration-test): remove only * feat(integration-test): refactor code * feat(integration-test): refactor code * feat: additional permission check to show components (#856) * feat: additional permission check to show components * chore: restore dummy value * chore: enable service modification keys only for admins * chore: code refactoring * chore: fix test * fix: test import * feat: add tests * trigger CR pipeline * feat: add tests for institution service * chore: remove unused import * chore: add tests for coverage --------- Co-authored-by: CianoDanilo <danilo.ciano@pagopa.it> --------- Co-authored-by: Pietro Tota <115724836+pietro-tota@users.noreply.github.com> --------- Co-authored-by: Pietro Tota <115724836+pietro-tota@users.noreply.github.com> Co-authored-by: pietro.tota <pietro.tota@pagopa.it> --------- Co-authored-by: CianoDanilo <danilo.ciano@pagopa.it> --------- Co-authored-by: CianoDanilo <danilo.ciano@pagopa.it> * Bump to version 1.46.0-1-CHK-4708-service-settings-section [skip ci] * fix: add org is ec and broker to check --------- Co-authored-by: danilo.ciano <danilo.ciano@pagopa.it> Co-authored-by: pagopa-github-bot <github-bot@pagopa.it>
1 parent 84eb167 commit 0836f94

File tree

25 files changed

+1395
-159
lines changed

25 files changed

+1395
-159
lines changed

.env.development.local

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,3 @@ REACT_APP_PAGOPA_HELP_EMAIL=assistenza@selfcare.it
3838
REACT_APP_OPERATOR_EMAIL_ADDRESSES='stefano.bafaro@pagopa.it;'
3939

4040
REACT_APP_JWT=DUMMY
41-
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { Page, test, expect } from '@playwright/test';
2+
import { changeToEcIPAUser, ORG } from './utils/e2eUtils';
3+
4+
const INSTITUTION_ID = ORG.EC_IPA.id;
5+
const GET_SERVICES_URL = `**/institutions/${INSTITUTION_ID}/services/consents`;
6+
7+
test.describe('Settings Page - Service Consents', () => {
8+
// eslint-disable-next-line functional/no-let
9+
let page: Page;
10+
11+
test.beforeAll(async ({ browser }) => {
12+
console.log('🚀 STARTING TEST FILE: settings.spec.ts');
13+
page = await browser.newPage({ storageState: undefined });
14+
});
15+
16+
test('should render the list of services correctly', async () => {
17+
await changeToEcIPAUser(page);
18+
await acceptCookieBanner(page);
19+
20+
console.log('🚀 STARTING TEST: Render the list of services');
21+
await page.getByTestId('settings-nav-test').click();
22+
await expect(page.getByRole('heading', { name: /Impostazioni/i })).toBeVisible();
23+
await expect(page.getByRole('alert')).toBeVisible();
24+
await expect(page.getByRole('heading', { name: /SEPA Request to Pay/})).toBeVisible();
25+
});
26+
27+
test('should open dialog, enable service (Opt-In) and disable service (Opt-in) or vice versa', async ({ page }) => {
28+
await changeToEcIPAUser(page);
29+
await acceptCookieBanner(page);
30+
31+
await page.getByTestId('settings-nav-test').click();
32+
33+
const btnEnable = page.getByTestId('settingCard-RTP-enableButton');
34+
const btnDisable = page.getByTestId('settingCard-RTP-disableButton');
35+
const eitherButton = btnEnable.or(btnDisable);
36+
await eitherButton.waitFor({ state: 'visible', timeout: 5000 });
37+
38+
if (await btnEnable.isVisible()) {
39+
console.log('🚀 STATE DETECTED: Service is OFF. Testing ENABLE flow.');
40+
await enableServiceFlow(page, "RTP");
41+
42+
console.log('🚀 Testing DISABLE flow.');
43+
await disableServiceFlow(page, "RTP");
44+
} else {
45+
console.log('🚀 STATE DETECTED: Service is ON. Testing DISABLE flow.');
46+
await disableServiceFlow(page, "RTP");
47+
48+
console.log('🚀 Testing ENABLE flow.');
49+
await enableServiceFlow(page, "RTP");
50+
}
51+
});
52+
53+
test('should close dialog when clicking cancel', async ({ page }) => {
54+
console.log('🚀 STARTING TEST: Should close dialog when clicking cancel.');
55+
await changeToEcIPAUser(page);
56+
await acceptCookieBanner(page);
57+
58+
await page.getByTestId('settings-nav-test').click();
59+
60+
const btnEnable = page.getByTestId('settingCard-RTP-enableButton');
61+
const btnDisable = page.getByTestId('settingCard-RTP-disableButton');
62+
const eitherButton = btnEnable.or(btnDisable);
63+
await eitherButton.waitFor({ state: 'visible', timeout: 5000 });
64+
65+
if (await btnEnable.isVisible()) {
66+
await btnEnable.click();
67+
} else {
68+
await btnDisable.click();
69+
}
70+
71+
const dialog = page.getByTestId('settingCard-RTP-dialog-message');
72+
await expect(dialog).toBeVisible();
73+
74+
// Click Cancel
75+
await page.getByTestId('settingCard-RTP-dialog-cancelButton').click();
76+
77+
await expect(dialog).toBeHidden();
78+
});
79+
80+
test('should show empty state if list is empty', async ({ page }) => {
81+
console.log('🚀 STARTING TEST: Should close dialog when clicking cancel.');
82+
await changeToEcIPAUser(page);
83+
await acceptCookieBanner(page);
84+
85+
await page.route(GET_SERVICES_URL, async (route) => {
86+
await route.fulfill({
87+
status: 200,
88+
body: JSON.stringify({ services: [] }),
89+
});
90+
});
91+
92+
await page.getByTestId('settings-nav-test').click();
93+
94+
await expect(page.getByTestId("settingsPage.emptyListError")).toBeVisible();
95+
});
96+
});
97+
98+
export const acceptCookieBanner = async (page: Page) => {
99+
const cookieButton = page.getByRole('button', { name: /accetta|accept/i });
100+
101+
try {
102+
await cookieButton.waitFor({ state: 'visible', timeout: 5000 });
103+
await cookieButton.click();
104+
console.log('🍪 Cookie banner accepted.');
105+
} catch (error) {
106+
console.log('🍪 Cookie banner not found or already accepted (Continuing test...)');
107+
}
108+
};
109+
110+
/**
111+
* Utility method that performs the "Enable Service" flow.
112+
* It handles the modal confirmation and asserts that the disabling button becomes visible afterwards.
113+
* @param page - The Playwright Page object
114+
* @param serviceId - The service id under test
115+
*/
116+
export const enableServiceFlow = async (page: Page, serviceId: string) => {
117+
const btnEnable = page.getByTestId(`settingCard-${serviceId}-enableButton`);
118+
const btnDisable = page.getByTestId(`settingCard-${serviceId}-disableButton`);
119+
const dialogBtnEnable = page.getByTestId(`settingCard-${serviceId}-dialog-enableButton`);
120+
121+
await btnEnable.click();
122+
await dialogBtnEnable.click();
123+
124+
await expect(page.getByText('In attivazione')).toBeVisible();
125+
await expect(btnDisable).toBeVisible();
126+
};
127+
128+
/**
129+
* Utility method that performs the "Disable Service" flow.
130+
* It handles the modal confirmation and asserts that the enabling button becomes visible afterwards.
131+
* @param page - The Playwright Page object
132+
* @param serviceId - The service id under test
133+
*/
134+
export const disableServiceFlow = async (page: Page, serviceId: string) => {
135+
const btnEnable = page.getByTestId(`settingCard-${serviceId}-enableButton`);
136+
const btnDisable = page.getByTestId(`settingCard-${serviceId}-disableButton`);
137+
const dialogBtnDisable = page.getByTestId(`settingCard-${serviceId}-dialog-disableButton`);
138+
139+
await btnDisable.click();
140+
await dialogBtnDisable.click();
141+
142+
await expect(page.getByText('In disattivazione')).toBeVisible();
143+
await expect(btnEnable).toBeVisible();
144+
};

integration-test/tests/utils/e2eUtils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export type ORG_TYPE = {
2424
export const ORG = {
2525
EC_DEMO_DIRECT: { id: '21079fe3-b5eb-4978-bef0-5588d11ab53f', taxCode: '99999000013' },
2626
PSP_DEMO_DIRECT: { id: '5b7130fb-dbe1-4e3b-b02b-2a9cfaf54602', taxCode: '99999000011' },
27+
EC_IPA: { id: '6b920f99-fc0d-4eb4-9f91-719a98ba48db', taxCode: '00067060947' },
2728
};
2829

2930
export async function login(
@@ -73,6 +74,10 @@ export async function changeToPspUser(page: Page, isOperator?: boolean) {
7374
await login(page, ORG.PSP_DEMO_DIRECT, isOperator);
7475
}
7576

77+
export async function changeToEcIPAUser(page: Page, isOperator?: boolean) {
78+
await login(page, ORG.EC_IPA, isOperator);
79+
}
80+
7681
export function getTodayDate() {
7782
const today = new Date();
7883
const dd = String(today.getDate()).padStart(2, '0');

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pagopa-selfcare-backoffice-frontend",
3-
"version": "1.46.0",
3+
"version": "1.46.0-1-CHK-4708-service-settings-section",
44
"homepage": "ui",
55
"private": true,
66
"scripts": {

src/App.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import routes from './routes';
6161
import { getMaintenanceMessage } from './services/maintenanceService';
6262
import { rewriteLinks } from './utils/onetrust-utils';
6363
import QuicksightDashboardPage from './pages/quicksight/QuicksightDashboardPage';
64+
import SettingsPage from './pages/settings/SettingsPage';
6465

6566
const SecuredRoutes = withLogin(
6667
withFeatureFlags(
@@ -350,6 +351,17 @@ const SecuredRoutes = withLogin(
350351
<QuicksightDashboardPage />
351352
</ProtectedRoute>
352353
</Route>
354+
355+
<Route path={routes.SETTINGS} exact={true}>
356+
<ProtectedRoute
357+
permission="settings"
358+
flagValue="settings-section"
359+
orgCheckCondition={(orgInfo) => orgInfo.types.isEcIPA}
360+
>
361+
<SettingsPage />
362+
</ProtectedRoute>
363+
</Route>
364+
353365

354366
<Route path="*">
355367
<Redirect to={routes.HOME} />

0 commit comments

Comments
 (0)